Changeset: bb647b65e1c9 for MonetDB
URL: http://dev.monetdb.org/hg/MonetDB?cmd=changeset;node=bb647b65e1c9
Modified Files:
sql/backends/monet5/Tests/pyapi09.sql
sql/backends/monet5/Tests/pyapi21.sql
sql/backends/monet5/UDF/pyapi/conversion.c
Branch: default
Log Message:
Add support for blob/sqlblob conversions to Python UDFs.
diffs (truncated from 1793 to 300 lines):
diff --git a/sql/backends/monet5/Tests/pyapi09.sql
b/sql/backends/monet5/Tests/pyapi09.sql
--- a/sql/backends/monet5/Tests/pyapi09.sql
+++ b/sql/backends/monet5/Tests/pyapi09.sql
@@ -40,7 +40,7 @@ DROP TABLE pyapi09multiplication;
#Third test: Store python object in table and load it in later function
-CREATE FUNCTION pyapi09create() returns TABLE(s STRING)
+CREATE FUNCTION pyapi09create() returns TABLE(s BLOB)
language P
{
import pickle
@@ -58,7 +58,7 @@ language P
import pickle
res = _conn.execute('SELECT s FROM pyapi09objects;')
array = pickle.loads(res['s'][0])
- print array
+ print(array)
return array[:10]
};
diff --git a/sql/backends/monet5/Tests/pyapi21.sql
b/sql/backends/monet5/Tests/pyapi21.sql
--- a/sql/backends/monet5/Tests/pyapi21.sql
+++ b/sql/backends/monet5/Tests/pyapi21.sql
@@ -2,7 +2,7 @@
START TRANSACTION;
-CREATE FUNCTION pyapi21_create() returns TABLE(s STRING)
+CREATE FUNCTION pyapi21_create() returns TABLE(s BLOB)
language P
{
import pickle
@@ -18,12 +18,12 @@ language P
return result
};
-CREATE FUNCTION pyapi21_load(objects STRING) returns BOOLEAN
+CREATE FUNCTION pyapi21_load(objects BLOB) returns BOOLEAN
LANGUAGE P
{
import pickle
for x in [pickle.loads(y) for y in objects]:
- print str(type(x)) + ": " + str(x)
+ print(str(type(x)) + ": " + str(x))
return True
};
diff --git a/sql/backends/monet5/UDF/pyapi/conversion.c
b/sql/backends/monet5/UDF/pyapi/conversion.c
--- a/sql/backends/monet5/UDF/pyapi/conversion.c
+++ b/sql/backends/monet5/UDF/pyapi/conversion.c
@@ -4,6 +4,7 @@
#include "pytypes.h"
#include "type_conversion.h"
#include "unicode.h"
+#include "blob.h"
#ifndef HAVE_EMBEDDED
#include "gdk_interprocess.h"
#endif
@@ -23,899 +24,960 @@ CREATE_SQL_FUNCTION_PTR(str,batdbl_num2d
//! Wrapper to get eclass of SQL type
int GetSQLType(sql_subtype *sql_subtype);
+static bool IsBlobType(int type) {
+ return type == TYPE_blob || type == TYPE_sqlblob;
+}
+
PyObject *PyArrayObject_FromScalar(PyInput* inp, char **return_message)
{
- PyObject *vararray = NULL;
- char *msg = NULL;
- assert(inp->scalar); //input has to be a scalar
+ PyObject *vararray = NULL;
+ char *msg = NULL;
+ assert(inp->scalar); //input has to be a scalar
- switch(inp->bat_type)
- {
- case TYPE_bit:
- vararray = PyInt_FromLong((long)(*(bit*)inp->dataptr));
- break;
- case TYPE_bte:
- vararray = PyInt_FromLong((long)(*(bte*)inp->dataptr));
- break;
- case TYPE_sht:
- vararray = PyInt_FromLong((long)(*(sht*)inp->dataptr));
- break;
- case TYPE_int:
- vararray = PyInt_FromLong((long)(*(int*)inp->dataptr));
- break;
- case TYPE_lng:
- vararray = PyLong_FromLongLong((*(lng*)inp->dataptr));
- break;
- case TYPE_flt:
- vararray = PyFloat_FromDouble((double)(*(flt*)inp->dataptr));
- break;
- case TYPE_dbl:
- vararray = PyFloat_FromDouble((double)(*(dbl*)inp->dataptr));
- break;
+ switch(inp->bat_type)
+ {
+ case TYPE_bit:
+ vararray = PyInt_FromLong((long)(*(bit*)inp->dataptr));
+ break;
+ case TYPE_bte:
+ vararray = PyInt_FromLong((long)(*(bte*)inp->dataptr));
+ break;
+ case TYPE_sht:
+ vararray = PyInt_FromLong((long)(*(sht*)inp->dataptr));
+ break;
+ case TYPE_int:
+ vararray = PyInt_FromLong((long)(*(int*)inp->dataptr));
+ break;
+ case TYPE_lng:
+ vararray = PyLong_FromLongLong((*(lng*)inp->dataptr));
+ break;
+ case TYPE_flt:
+ vararray =
PyFloat_FromDouble((double)(*(flt*)inp->dataptr));
+ break;
+ case TYPE_dbl:
+ vararray =
PyFloat_FromDouble((double)(*(dbl*)inp->dataptr));
+ break;
#ifdef HAVE_HGE
- case TYPE_hge:
- vararray = PyLong_FromHge(*((hge *) inp->dataptr));
- break;
+ case TYPE_hge:
+ vararray = PyLong_FromHge(*((hge *) inp->dataptr));
+ break;
#endif
- case TYPE_str:
- vararray = PyUnicode_FromString(*((char**) inp->dataptr));
- break;
- default:
- msg = createException(MAL, "pyapi.eval", "Unsupported scalar type
%i.", inp->bat_type);
- goto wrapup;
- }
- if (vararray == NULL)
- {
- msg = createException(MAL, "pyapi.eval", "Something went wrong
converting the MonetDB scalar to a Python scalar.");
- goto wrapup;
- }
+ case TYPE_str:
+ vararray = PyUnicode_FromString(*((char**)
inp->dataptr));
+ break;
+ default:
+ msg = createException(MAL, "pyapi.eval", "Unsupported
scalar type %i.", inp->bat_type);
+ goto wrapup;
+ }
+ if (vararray == NULL)
+ {
+ msg = createException(MAL, "pyapi.eval", "Something went wrong
converting the MonetDB scalar to a Python scalar.");
+ goto wrapup;
+ }
wrapup:
- *return_message = msg;
- return vararray;
+ *return_message = msg;
+ return vararray;
}
PyObject *PyMaskedArray_FromBAT(PyInput *inp, size_t t_start, size_t t_end,
char **return_message, bool copy)
{
- BAT *b = inp->bat;
- char *msg;
- PyObject *vararray;
+ BAT *b = inp->bat;
+ char *msg;
+ PyObject *vararray;
- vararray = PyArrayObject_FromBAT(inp, t_start, t_end, return_message,
copy);
- if (vararray == NULL) {
- return NULL;
- }
- // To deal with null values, we use the numpy masked array structure
- // The masked array structure is an object with two arrays of equal size,
a data array and a mask array
- // The mask array is a boolean array that has the value 'True' when the
element is NULL, and 'False' otherwise
- // If the BAT has Null values, we construct this masked array
- if (!(b->tnil == 0 && b->tnonil == 1))
- {
- PyObject *mask;
- PyObject *mafunc =
PyObject_GetAttrString(PyImport_Import(PyString_FromString("numpy.ma")),
"masked_array");
- PyObject *maargs;
- PyObject *nullmask = PyNullMask_FromBAT(b, t_start, t_end);
+ vararray = PyArrayObject_FromBAT(inp, t_start, t_end, return_message,
copy);
+ if (vararray == NULL) {
+ return NULL;
+ }
+ // To deal with null values, we use the numpy masked array structure
+ // The masked array structure is an object with two arrays of equal
size, a data array and a mask array
+ // The mask array is a boolean array that has the value 'True' when the
element is NULL, and 'False' otherwise
+ // If the BAT has Null values, we construct this masked array
+ if (!(b->tnil == 0 && b->tnonil == 1))
+ {
+ PyObject *mask;
+ PyObject *mafunc =
PyObject_GetAttrString(PyImport_Import(PyString_FromString("numpy.ma")),
"masked_array");
+ PyObject *maargs;
+ PyObject *nullmask = PyNullMask_FromBAT(b, t_start, t_end);
- if (nullmask == Py_None) {
- maargs = PyTuple_New(1);
- PyTuple_SetItem(maargs, 0, vararray);
- } else {
- maargs = PyTuple_New(2);
- PyTuple_SetItem(maargs, 0, vararray);
- PyTuple_SetItem(maargs, 1, (PyObject*) nullmask);
- }
+ if (nullmask == Py_None) {
+ maargs = PyTuple_New(1);
+ PyTuple_SetItem(maargs, 0, vararray);
+ } else {
+ maargs = PyTuple_New(2);
+ PyTuple_SetItem(maargs, 0, vararray);
+ PyTuple_SetItem(maargs, 1, (PyObject*) nullmask);
+ }
- // Now we will actually construct the mask by calling the masked array
constructor
- mask = PyObject_CallObject(mafunc, maargs);
- if (!mask) {
- msg = createException(MAL, "pyapi.eval", "Failed to create mask");
- goto wrapup;
- }
- Py_DECREF(maargs);
- Py_DECREF(mafunc);
+ // Now we will actually construct the mask by calling the
masked array constructor
+ mask = PyObject_CallObject(mafunc, maargs);
+ if (!mask) {
+ msg = createException(MAL, "pyapi.eval", "Failed to
create mask");
+ goto wrapup;
+ }
+ Py_DECREF(maargs);
+ Py_DECREF(mafunc);
- vararray = mask;
- }
- return vararray;
+ vararray = mask;
+ }
+ return vararray;
wrapup:
- *return_message = msg;
- return NULL;
+ *return_message = msg;
+ return NULL;
}
PyObject *PyArrayObject_FromBAT(PyInput *inp, size_t t_start, size_t t_end,
char **return_message, bool copy)
{
- // This variable will hold the converted Python object
- PyObject *vararray = NULL;
- char *msg;
- size_t j = 0;
- BUN p = 0, q = 0;
- BATiter li;
- BAT *b = inp->bat;
- npy_intp elements[1] = { t_end-t_start };
+ // This variable will hold the converted Python object
+ PyObject *vararray = NULL;
+ char *msg;
+ size_t j = 0;
+ BUN p = 0, q = 0;
+ BATiter li;
+ BAT *b = inp->bat;
+ npy_intp elements[1] = { t_end-t_start };
- assert(!inp->scalar); //input has to be a BAT
+ assert(!inp->scalar); //input has to be a BAT
- if (!b) {
- // No BAT was found, we can't do anything in this case
- msg = createException(MAL, "pyapi.eval", MAL_MALLOC_FAIL" bat.");
- goto wrapup;
- }
+ if (!b) {
+ // No BAT was found, we can't do anything in this case
+ msg = createException(MAL, "pyapi.eval", MAL_MALLOC_FAIL"
bat.");
+ goto wrapup;
+ }
- if (!IsStandardBATType(inp->bat_type) ||
ConvertableSQLType(inp->sql_subtype)) { // if the sql type is set, we have to
do some conversion
- if (inp->scalar) {
- // todo: scalar SQL types
- msg = createException(MAL, "pyapi.eval", "Scalar SQL types haven't
been implemented yet... sorry");
- goto wrapup;
- } else {
- BAT *ret_bat = NULL;
- msg = ConvertFromSQLType(inp->bat, inp->sql_subtype, &ret_bat,
&inp->bat_type);
- if (msg != MAL_SUCCEED) {
- msg = createException(MAL, "pyapi.eval", "Failed to convert
BAT.");
- goto wrapup;
- }
- BBPunfix(inp->bat->batCacheid);
- inp->bat = ret_bat;
- }
- }
+ if (!IsBlobType(inp->bat_type) && (!IsStandardBATType(inp->bat_type) ||
ConvertableSQLType(inp->sql_subtype))) { // if the sql type is set, we have to
do some conversion
+ if (inp->scalar) {
+ // FIXME: scalar SQL types
+ msg = createException(MAL, "pyapi.eval", "Scalar SQL
types haven't been implemented yet... sorry");
+ goto wrapup;
_______________________________________________
checkin-list mailing list
[email protected]
https://www.monetdb.org/mailman/listinfo/checkin-list