LCOV - code coverage report
Current view: top level - sql/backends/monet5/UDF/pyapi3 - conversion3.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 493 772 63.9 %
Date: 2024-12-19 23:10:26 Functions: 16 16 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : #include "conversion.h"
      15             : #include "convert_loops.h"
      16             : #include "pytypes.h"
      17             : #include "type_conversion.h"
      18             : #include "unicode.h"
      19             : 
      20             : //! Wrapper to get eclass of SQL type
      21             : int GetSQLType(sql_subtype *sql_subtype);
      22             : 
      23             : static bool IsBlobType(int type)
      24             : {
      25             :         return type == TYPE_blob;
      26             : }
      27             : 
      28             : static bool IsVoidType(int type)
      29             : {
      30             :         return type == TYPE_void;
      31             : }
      32             : 
      33          85 : PyObject *PyArrayObject_FromScalar(PyInput *inp, char **return_message)
      34             : {
      35          85 :         PyObject *vararray = NULL;
      36          85 :         char *msg = NULL;
      37          85 :         assert(inp->scalar); // input has to be a scalar
      38             : 
      39          85 :         switch (inp->bat_type) {
      40           0 :                 case TYPE_void:
      41             : #if SIZEOF_OID == SIZEOF_INT
      42             :                         vararray = PyArray_Arange(0, 1, 1, NPY_UINT);
      43             : #else
      44           0 :                         vararray = PyArray_Arange(0, 1, 1, NPY_ULONGLONG);
      45             : #endif
      46           0 :                         break;
      47           0 :                 case TYPE_oid:
      48           0 :                         vararray = PyLong_FromLong((long)(*(oid *)inp->dataptr));
      49           0 :                         break;
      50           0 :                 case TYPE_bit:
      51           0 :                         vararray = PyLong_FromLong((long)(*(bit *)inp->dataptr));
      52           0 :                         break;
      53           0 :                 case TYPE_bte:
      54           0 :                         vararray = PyLong_FromLong((long)(*(bte *)inp->dataptr));
      55           0 :                         break;
      56           0 :                 case TYPE_sht:
      57           0 :                         vararray = PyLong_FromLong((long)(*(sht *)inp->dataptr));
      58           0 :                         break;
      59          63 :                 case TYPE_int:
      60          63 :                         vararray = PyLong_FromLong((long)(*(int *)inp->dataptr));
      61          63 :                         break;
      62           0 :                 case TYPE_lng:
      63           0 :                         vararray = PyLong_FromLongLong((*(lng *)inp->dataptr));
      64           0 :                         break;
      65           0 :                 case TYPE_flt:
      66           0 :                         vararray = PyFloat_FromDouble((double)(*(flt *)inp->dataptr));
      67           0 :                         break;
      68           1 :                 case TYPE_dbl:
      69           1 :                         vararray = PyFloat_FromDouble((double)(*(dbl *)inp->dataptr));
      70           1 :                         break;
      71             : #ifdef HAVE_HGE
      72           0 :                 case TYPE_hge:
      73           0 :                         vararray = PyLong_FromHge(*((hge *)inp->dataptr));
      74           0 :                         break;
      75             : #endif
      76           1 :                 case TYPE_date: {
      77           1 :                                 USE_DATETIME_API;
      78           1 :                                 date dt = *(date *)inp->dataptr;
      79           1 :                                 vararray = PyDate_FromDate(date_year(dt), date_month(dt), date_day(dt));
      80             :                                 /* error checking */
      81           1 :                                 break;
      82             :                 }
      83           1 :                 case TYPE_daytime: {
      84           1 :                                 USE_DATETIME_API;
      85           1 :                                 daytime dt = *(daytime *)inp->dataptr;
      86           1 :                                 vararray = PyTime_FromTime(daytime_hour(dt), daytime_min(dt), daytime_sec(dt), daytime_usec(dt));
      87             :                                 /* error checking */
      88           1 :                                 break;
      89             :                 }
      90           1 :                 case TYPE_timestamp: {
      91           1 :                                 USE_DATETIME_API;
      92           1 :                                 timestamp ts = *(timestamp *)inp->dataptr;
      93           1 :                                 date dt = timestamp_date(ts);
      94           1 :                                 daytime dtm = timestamp_daytime(ts);
      95           1 :                                 vararray = PyDateTime_FromDateAndTime(date_year(dt), date_month(dt), date_day(dt), daytime_hour(dtm), daytime_min(dtm), daytime_sec(dtm), daytime_usec(dtm));
      96             :                                 /* error checking */
      97           1 :                                 break;
      98             :                 }
      99          18 :                 case TYPE_str:
     100          18 :                         vararray = PyUnicode_FromString(*((char **)inp->dataptr));
     101          18 :                         break;
     102           0 :                 default:
     103           0 :                         msg = createException(MAL, "pyapi3.eval",
     104             :                                                                   SQLSTATE(PY000) "Unsupported scalar type %i.", inp->bat_type);
     105           0 :                         goto wrapup;
     106             :         }
     107          85 :         if (vararray == NULL) {
     108           0 :                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "Something went wrong "
     109             :                                                                                                  "converting the MonetDB "
     110             :                                                                                                  "scalar to a Python scalar.");
     111           0 :                 goto wrapup;
     112             :         }
     113          85 : wrapup:
     114          85 :         *return_message = msg;
     115          85 :         return vararray;
     116             : }
     117             : 
     118             : PyObject *
     119         339 : PyMaskedArray_FromBAT(PyInput *inp, size_t t_start, size_t t_end, char **return_message, bool copy)
     120             : {
     121         339 :         BAT *b;
     122         339 :         char *msg;
     123         339 :         PyObject *vararray = PyArrayObject_FromBAT(inp, t_start, t_end, return_message, copy);
     124             : 
     125         339 :         if (vararray == NULL)
     126             :                 return NULL;
     127         339 :         b = inp->bat;
     128             :         // To deal with null values, we use the numpy masked array structure
     129             :         // The masked array structure is an object with two arrays of equal size, a
     130             :         // data array and a mask array
     131             :         // The mask array is a boolean array that has the value 'True' when the
     132             :         // element is NULL, and 'False' otherwise
     133             :         // if we know for sure that the BAT has no NULL values, we can skip the construction
     134             :         // of this masked array. Otherwise, we create it.
     135         339 :         MT_lock_set(&b->theaplock);
     136         339 :         bool bnonil = b->tnonil;
     137         339 :         MT_lock_unset(&b->theaplock);
     138         339 :         if (!bnonil) {
     139          39 :                 PyObject *mod = PyImport_ImportModule("numpy.ma");
     140          39 :                 PyObject *mafunc = PyObject_GetAttrString( mod, "masked_array");
     141          39 :                 PyObject *nullmask = PyNullMask_FromBAT(b, t_start, t_end);
     142             : 
     143          39 :                 if (!nullmask) {
     144           0 :                         Py_DECREF(vararray);
     145           0 :                         Py_DECREF(mafunc);
     146           0 :                         Py_DECREF(mod);
     147           0 :                         msg = createException(MAL, "pyapi3.eval", "Failed to create mask for some reason");
     148           0 :                         goto wrapup;
     149          39 :                 } else if (nullmask == Py_None) {
     150          15 :                         Py_DECREF(nullmask);
     151             :                 } else {
     152          24 :                         PyObject *maargs = PyTuple_New(2);
     153          24 :                         PyTuple_SetItem(maargs, 0, vararray);
     154          24 :                         PyTuple_SetItem(maargs, 1, nullmask);
     155             : 
     156             :                         // Now we will actually construct the mask by calling the masked
     157             :                         // array constructor
     158          24 :                         PyObject *mask = PyObject_CallObject(mafunc, maargs);
     159          24 :                         Py_DECREF(maargs);
     160          24 :                         if (!mask) {
     161           0 :                                 Py_DECREF(mafunc);
     162           0 :                                 Py_DECREF(mod);
     163           0 :                                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "Failed to create mask");
     164           0 :                                 goto wrapup;
     165             :                         }
     166             :                         vararray = mask;
     167             :                 }
     168          39 :                 Py_DECREF(mafunc);
     169          39 :                 Py_DECREF(mod);
     170             :         }
     171             :         return vararray;
     172           0 : wrapup:
     173           0 :         *return_message = msg;
     174           0 :         return NULL;
     175             : }
     176             : 
     177             : PyObject *
     178         339 : PyArrayObject_FromBAT(PyInput *inp, size_t t_start, size_t t_end, char **return_message, bool copy)
     179             : {
     180             :         // This variable will hold the converted Python object
     181         339 :         PyObject *vararray = NULL;
     182         339 :         char *msg;
     183         339 :         size_t j = 0;
     184         339 :         BUN p = 0, q = 0;
     185         339 :         BATiter li;
     186         339 :         BAT *b = inp->bat;
     187         339 :         npy_intp elements[1] = {t_end - t_start};
     188             : 
     189         339 :         assert(!inp->scalar); // input has to be a BAT
     190             : 
     191         339 :         if (!b) {
     192             :                 // No BAT was found, we can't do anything in this case
     193           0 :                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL " bat missing");
     194           0 :                 goto wrapup;
     195             :         }
     196             : 
     197         665 :         if (!IsVoidType(inp->bat_type) && !IsBlobType(inp->bat_type) &&
     198         651 :                 (!IsStandardBATType(inp->bat_type) ||
     199         325 :                  ConvertableSQLType(inp->sql_subtype))) { // if the sql type is set, we
     200             :                                                                                                   // have to do some conversion
     201           4 :                 if (inp->scalar) {
     202             :                         // FIXME: scalar SQL types
     203           0 :                         msg = createException(
     204             :                                 MAL, "pyapi3.eval",
     205             :                                 SQLSTATE(0A000) "Scalar SQL types haven't been implemented yet... sorry");
     206           0 :                         goto wrapup;
     207             :                 } else {
     208           4 :                         BAT *ret_bat = NULL;
     209           4 :                         msg = ConvertFromSQLType(b, inp->sql_subtype, &ret_bat, &inp->bat_type);
     210           4 :                         if (msg != MAL_SUCCEED) {
     211           0 :                                 freeException(msg);
     212           0 :                                 msg = createException(MAL, "pyapi3.eval",
     213             :                                                                           SQLSTATE(PY000) "Failed to convert BAT.");
     214           0 :                                 goto wrapup;
     215             :                         }
     216           4 :                         b = ret_bat;
     217             :                 }
     218             :         }
     219             : 
     220         339 :         if (IsBlobType(inp->bat_type)) {
     221           2 :                 PyObject **data;
     222           2 :                 li = bat_iterator(b);
     223           2 :                 vararray = PyArray_EMPTY(1, elements, NPY_OBJECT, 0);
     224           2 :                 data = PyArray_DATA((PyArrayObject *)vararray);
     225           8 :                 BATloop(b, p, q)
     226             :                 {
     227           6 :                         const blob *t = (const blob *)BUNtvar(li, p);
     228           6 :                         if (t->nitems == ~(size_t)0) {
     229           0 :                                 data[p] = Py_None;
     230           0 :                                 Py_INCREF(Py_None);
     231             :                         } else {
     232           6 :                                 data[p] = PyByteArray_FromStringAndSize((char *) t->data, t->nitems);
     233             :                         }
     234             :                 }
     235           2 :                 bat_iterator_end(&li);
     236             :         } else {
     237         337 :                 switch (inp->bat_type) {
     238          11 :                         case TYPE_void:
     239             : #if SIZEOF_OID == SIZEOF_INT
     240             :                                 BAT_TO_NP_CREATE_ALWAYS(b, NPY_UINT);
     241             : #else
     242          11 :                                 BAT_TO_NP_CREATE_ALWAYS(b, NPY_ULONGLONG);
     243             : #endif
     244          11 :                                 break;
     245          19 :                         case TYPE_oid:
     246             : #if SIZEOF_OID == SIZEOF_INT
     247             :                                 BAT_TO_NP(b, oid, NPY_UINT32);
     248             : #else
     249          19 :                                 BAT_TO_NP(b, oid, NPY_UINT64);
     250             : #endif
     251          19 :                                 break;
     252           1 :                         case TYPE_bit:
     253           1 :                                 BAT_TO_NP(b, bit, NPY_INT8);
     254           1 :                                 break;
     255           3 :                         case TYPE_bte:
     256           3 :                                 BAT_TO_NP(b, bte, NPY_INT8);
     257           3 :                                 break;
     258           1 :                         case TYPE_sht:
     259           1 :                                 BAT_TO_NP(b, sht, NPY_INT16);
     260           1 :                                 break;
     261         271 :                         case TYPE_int:
     262         271 :                                 BAT_TO_NP(b, int, NPY_INT32);
     263         271 :                                 break;
     264           1 :                         case TYPE_lng:
     265           1 :                                 BAT_TO_NP(b, lng, NPY_INT64);
     266           1 :                                 break;
     267           0 :                         case TYPE_flt:
     268           0 :                                 BAT_TO_NP(b, flt, NPY_FLOAT32);
     269           0 :                                 break;
     270          16 :                         case TYPE_dbl:
     271          16 :                                 BAT_TO_NP(b, dbl, NPY_FLOAT64);
     272          16 :                                 break;
     273           2 :                         case TYPE_date: {
     274           2 :                                 li = bat_iterator(b);
     275             : 
     276           2 :                                 USE_DATETIME_API;
     277           2 :                                 vararray = PyArray_EMPTY(1, elements, NPY_OBJECT, 0);
     278             :                                 {
     279           2 :                                         PyObject **data = ((PyObject **)PyArray_DATA((PyArrayObject *)vararray));
     280             :                                         // PyObject *obj;
     281           2 :                                         j = 0;
     282           6 :                                         BATloop(b, p, q)
     283             :                                         {
     284           4 :                                                 date dt = *(const date*)BUNtail(li, p);
     285           4 :                                                 if (is_date_nil(dt))
     286           0 :                                                         dt = date_create(1, 1, 1);
     287           4 :                                                 data[j++] = PyDate_FromDate(date_year(dt), date_month(dt), date_day(dt));
     288             :                                         }
     289             :                                 }
     290           2 :                                 bat_iterator_end(&li);
     291           2 :                                 break;
     292             :                         }
     293           1 :                         case TYPE_daytime: {
     294           1 :                                 li = bat_iterator(b);
     295             : 
     296           1 :                                 USE_DATETIME_API;
     297           1 :                                 vararray = PyArray_EMPTY(1, elements, NPY_OBJECT, 0);
     298             :                                 {
     299           1 :                                         PyObject **data = ((PyObject **)PyArray_DATA((PyArrayObject *)vararray));
     300             :                                         // PyObject *obj;
     301           1 :                                         j = 0;
     302           2 :                                         BATloop(b, p, q)
     303             :                                         {
     304           1 :                                                 daytime dt = *(const daytime*)BUNtail(li, p);
     305           1 :                                                 if (is_daytime_nil(dt))
     306           0 :                                                         dt = daytime_create(0, 0, 0, 0);
     307           1 :                                                 data[j++] = PyTime_FromTime(daytime_hour(dt),
     308             :                                                                                                         daytime_min(dt),
     309             :                                                                                                         daytime_sec(dt),
     310             :                                                                                                         daytime_usec(dt));
     311             :                                         }
     312             :                                 }
     313           1 :                                 bat_iterator_end(&li);
     314           1 :                                 break;
     315             :                         }
     316           2 :                         case TYPE_timestamp: {
     317           2 :                                 li = bat_iterator(b);
     318             : 
     319           2 :                                 USE_DATETIME_API;
     320           2 :                                 vararray = PyArray_EMPTY(1, elements, NPY_OBJECT, 0);
     321             :                                 {
     322           2 :                                         PyObject **data = ((PyObject **)PyArray_DATA((PyArrayObject *)vararray));
     323             :                                         // PyObject *obj;
     324           2 :                                         j = 0;
     325           5 :                                         BATloop(b, p, q)
     326             :                                         {
     327           3 :                                                 const timestamp ts = *(const timestamp*)BUNtail(li, p);
     328           3 :                                                 const date dt = is_timestamp_nil(ts) ? date_create(1, 1, 1) : timestamp_date(ts);
     329           3 :                                                 const daytime dtm = is_timestamp_nil(ts) ? daytime_create(0, 0, 0, 0) : timestamp_daytime(ts);
     330             : 
     331           3 :                                                 data[j++] = PyDateTime_FromDateAndTime(date_year(dt), date_month(dt), date_day(dt), daytime_hour(dtm), daytime_min(dtm), daytime_sec(dtm), daytime_usec(dtm));
     332             :                                         }
     333             :                                 }
     334           2 :                                 bat_iterator_end(&li);
     335           2 :                                 break;
     336             :                         }
     337             : 
     338           9 :                         case TYPE_str: {
     339           9 :                                 bool unicode = false;
     340           9 :                                 li = bat_iterator(b);
     341             :                                 // create a NPY_OBJECT array object
     342           9 :                                 vararray = PyArray_New(&PyArray_Type, 1, elements, NPY_OBJECT,
     343             :                                                                            NULL, NULL, 0, 0, NULL);
     344             : 
     345     1100037 :                                 BATloop(b, p, q)
     346             :                                 {
     347     1100028 :                                         const char *t = (const char *)BUNtvar(li, p);
     348     6589029 :                                         for (; *t != 0; t++) {
     349     5489001 :                                                 if (*t & 0x80) {
     350             :                                                         unicode = true;
     351             :                                                         break;
     352             :                                                 }
     353             :                                         }
     354     1100028 :                                         if (unicode) {
     355             :                                                 break;
     356             :                                         }
     357             :                                 }
     358             : 
     359             :                                 {
     360           9 :                                         PyObject **data = ((PyObject **)PyArray_DATA((PyArrayObject *)vararray));
     361           9 :                                         PyObject *obj;
     362           9 :                                         j = 0;
     363           9 :                                         if (unicode) {
     364           0 :                                                 if (GDK_ELIMDOUBLES(b->tvheap)) {
     365           0 :                                                         PyObject **pyptrs = GDKzalloc(b->tvheap->free * sizeof(PyObject *));
     366           0 :                                                         if (!pyptrs) {
     367           0 :                                                                 bat_iterator_end(&li);
     368           0 :                                                                 msg = createException(MAL, "pyapi3.eval",
     369             :                                                                                                           SQLSTATE(HY013) MAL_MALLOC_FAIL
     370             :                                                                                                           " PyObject strings.");
     371           0 :                                                                 goto wrapup;
     372             :                                                         }
     373           0 :                                                         BATloop(b, p, q)
     374             :                                                         {
     375           0 :                                                                 const char *t = (const char *)BUNtvar(li, p);
     376           0 :                                                                 ptrdiff_t offset = t - b->tvheap->base;
     377           0 :                                                                 if (!pyptrs[offset]) {
     378           0 :                                                                         if (strNil(t)) {
     379             :                                                                                 // str_nil isn't a valid UTF-8 character
     380             :                                                                                 // (it's 0x80), so we can't decode it as
     381             :                                                                                 // UTF-8 (it will throw an error)
     382           0 :                                                                                 pyptrs[offset] = PyUnicode_FromString("-");
     383             :                                                                         } else {
     384             :                                                                                 // otherwise we can just decode the
     385             :                                                                                 // string as UTF-8
     386           0 :                                                                                 pyptrs[offset] = PyUnicode_FromString(t);
     387             :                                                                         }
     388           0 :                                                                         if (!pyptrs[offset]) {
     389           0 :                                                                                 bat_iterator_end(&li);
     390           0 :                                                                                 msg = createException(
     391             :                                                                                         MAL, "pyapi3.eval",
     392             :                                                                                         SQLSTATE(PY000) "Failed to create string.");
     393           0 :                                                                                 goto wrapup;
     394             :                                                                         }
     395             :                                                                 } else {
     396           0 :                                                                         Py_INCREF(pyptrs[offset]);
     397             :                                                                 }
     398           0 :                                                                 data[j++] = pyptrs[offset];
     399             :                                                         }
     400           0 :                                                         GDKfree(pyptrs);
     401             :                                                 } else {
     402           0 :                                                         BATloop(b, p, q)
     403             :                                                         {
     404           0 :                                                                 const char *t = (const char *)BUNtvar(li, p);
     405           0 :                                                                 if (strNil(t)) {
     406             :                                                                         // str_nil isn't a valid UTF-8 character
     407             :                                                                         // (it's 0x80), so we can't decode it as
     408             :                                                                         // UTF-8 (it will throw an error)
     409           0 :                                                                         obj = PyUnicode_FromString("-");
     410             :                                                                 } else {
     411             :                                                                         // otherwise we can just decode the string
     412             :                                                                         // as UTF-8
     413           0 :                                                                         obj = PyUnicode_FromString(t);
     414             :                                                                 }
     415             : 
     416           0 :                                                                 if (obj == NULL) {
     417           0 :                                                                         bat_iterator_end(&li);
     418           0 :                                                                         msg = createException(
     419             :                                                                                 MAL, "pyapi3.eval",
     420             :                                                                                 SQLSTATE(PY000) "Failed to create string.");
     421           0 :                                                                         goto wrapup;
     422             :                                                                 }
     423           0 :                                                                 data[j++] = obj;
     424             :                                                         }
     425             :                                                 }
     426             :                                         } else {
     427             :                                                 /* special case where we exploit the
     428             :                                                  * duplicate-eliminated string heap */
     429           9 :                                                 if (GDK_ELIMDOUBLES(b->tvheap)) {
     430           8 :                                                         PyObject **pyptrs = GDKzalloc(b->tvheap->free * sizeof(PyObject *));
     431           8 :                                                         if (!pyptrs) {
     432           0 :                                                                 bat_iterator_end(&li);
     433           0 :                                                                 msg = createException(MAL, "pyapi3.eval",
     434             :                                                                                                           SQLSTATE(HY013) MAL_MALLOC_FAIL
     435             :                                                                                                           " PyObject strings.");
     436           0 :                                                                 goto wrapup;
     437             :                                                         }
     438     1000036 :                                                         BATloop(b, p, q)
     439             :                                                         {
     440     1000028 :                                                                 const char *t = (const char *)BUNtvar(li, p);
     441     1000028 :                                                                 ptrdiff_t offset = t - b->tvheap->base;
     442     1000028 :                                                                 if (!pyptrs[offset]) {
     443          21 :                                                                         pyptrs[offset] = PyUnicode_FromString(t);
     444             :                                                                 } else {
     445     1000007 :                                                                         Py_INCREF(pyptrs[offset]);
     446             :                                                                 }
     447     1000028 :                                                                 data[j++] = pyptrs[offset];
     448             :                                                         }
     449           8 :                                                         GDKfree(pyptrs);
     450             :                                                 } else {
     451      100001 :                                                         BATloop(b, p, q)
     452             :                                                         {
     453      100000 :                                                                 const char *t = (const char *)BUNtvar(li, p);
     454      100000 :                                                                 obj = PyUnicode_FromString(t);
     455      100000 :                                                                 if (obj == NULL) {
     456           0 :                                                                         bat_iterator_end(&li);
     457           0 :                                                                         msg = createException(
     458             :                                                                                 MAL, "pyapi3.eval",
     459             :                                                                                 SQLSTATE(PY000) "Failed to create string.");
     460           0 :                                                                         goto wrapup;
     461             :                                                                 }
     462      100000 :                                                                 data[j++] = obj;
     463             :                                                         }
     464             :                                                 }
     465             :                                         }
     466             :                                 }
     467           9 :                                 bat_iterator_end(&li);
     468           9 :                                 break;
     469             :                         }
     470             : #ifdef HAVE_HGE
     471           0 :                         case TYPE_hge: {
     472             :                                 // create a NPY_FLOAT64 array to hold the huge type
     473           0 :                                 vararray = PyArray_New(&PyArray_Type, 1,
     474           0 :                                                                            (npy_intp[1]){t_end - t_start},
     475             :                                                                            NPY_FLOAT64, NULL, NULL, 0, 0, NULL);
     476             : 
     477           0 :                                 j = 0;
     478           0 :                                 npy_float64 *data = (npy_float64 *)PyArray_DATA((PyArrayObject *)vararray);
     479           0 :                                 BATiter bi = bat_iterator(b);
     480           0 :                                 const hge *vals = (const hge *) bi.base;
     481           0 :                                 BATloop(b, p, q) {
     482           0 :                                         data[j++] = (npy_float64)vals[p];
     483             :                                 }
     484           0 :                                 bat_iterator_end(&bi);
     485           0 :                                 break;
     486             :                         }
     487             : #endif
     488           0 :                         default:
     489           0 :                                 if (!inp->sql_subtype || !inp->sql_subtype->type) {
     490           0 :                                         msg = createException(MAL, "pyapi3.eval",
     491             :                                                                                   SQLSTATE(PY000) "unknown argument type");
     492             :                                 } else {
     493           0 :                                         msg = createException(MAL, "pyapi3.eval",
     494             :                                                                                   SQLSTATE(PY000) "Unsupported SQL Type: %s",
     495             :                                                                                   inp->sql_subtype->type->base.name);
     496             :                                 }
     497           0 :                                 goto wrapup;
     498             :                 }
     499             :         }
     500             : 
     501         339 :         if (vararray == NULL) {
     502           0 :                 msg = createException(MAL, "pyapi3.eval",
     503             :                                                           SQLSTATE(PY000) "Failed to convert BAT to Numpy array.");
     504           0 :                 goto wrapup;
     505             :         }
     506         339 :         if (b != inp->bat)
     507           4 :                 inp->conv_bat = b;           /* still in use, free later */
     508             :         return vararray;
     509           0 : wrapup:
     510           0 :         *return_message = msg;
     511           0 :         if (b != inp->bat)
     512           0 :                 BBPunfix(b->batCacheid);
     513             :         return NULL;
     514             : }
     515             : 
     516             : #define CreateNullMask(tpe)                                                    \
     517             :         {                                                                          \
     518             :                 tpe *bat_ptr = (tpe *)b->theap->base + b->tbaseoff;                    \
     519             :                 for (j = 0; j < count; j++) {                                          \
     520             :                         mask_data[j] = is_##tpe##_nil(bat_ptr[j]);                         \
     521             :                         found_nil |= mask_data[j];                                         \
     522             :                 }                                                                      \
     523             :         }
     524             : 
     525             : PyObject *
     526          39 : PyNullMask_FromBAT(BAT *b, size_t t_start, size_t t_end)
     527             : {
     528             :         // We will now construct the Masked array, we start by setting everything to
     529             :         // False
     530          39 :         size_t count = t_end - t_start;
     531          39 :         npy_intp elements[1] = {count};
     532          39 :         PyArrayObject *nullmask = (PyArrayObject *)PyArray_EMPTY(1, elements, NPY_BOOL, 0);
     533          39 :         const void *nil = ATOMnilptr(b->ttype);
     534          39 :         size_t j;
     535          39 :         bool found_nil = false;
     536          39 :         BATiter bi = bat_iterator(b);
     537          39 :         bool *mask_data = (bool *)PyArray_DATA(nullmask);
     538             : 
     539          77 :         switch (ATOMbasetype(getBatType(b->ttype))) {
     540           0 :                 case TYPE_bit:
     541           0 :                         CreateNullMask(bit);
     542             :                         break;
     543           0 :                 case TYPE_bte:
     544           0 :                         CreateNullMask(bte);
     545             :                         break;
     546           1 :                 case TYPE_sht:
     547      100001 :                         CreateNullMask(sht);
     548             :                         break;
     549          32 :                 case TYPE_int:
     550      435079 :                         CreateNullMask(int);
     551             :                         break;
     552           2 :                 case TYPE_lng:
     553      100004 :                         CreateNullMask(lng);
     554             :                         break;
     555           0 :                 case TYPE_flt:
     556           0 :                         CreateNullMask(flt);
     557             :                         break;
     558           4 :                 case TYPE_dbl:
     559      200018 :                         CreateNullMask(dbl);
     560             :                         break;
     561             : #ifdef HAVE_HGE
     562           0 :                 case TYPE_hge:
     563           0 :                         CreateNullMask(hge);
     564             :                         break;
     565             : #endif
     566           0 :                 default: {
     567           0 :                         int (*atomcmp)(const void *, const void *) = ATOMcompare(b->ttype);
     568           0 :                         for (j = 0; j < count; j++) {
     569           0 :                                 mask_data[j] = (*atomcmp)(BUNtail(bi, (BUN)(j)), nil) == 0;
     570           0 :                                 found_nil |= mask_data[j];
     571             :                         }
     572             :                 }
     573             :         }
     574          39 :         bat_iterator_end(&bi);
     575             : 
     576          39 :         if (!found_nil) {
     577          15 :                 Py_DECREF(nullmask);
     578          15 :                 Py_RETURN_NONE;
     579             :         }
     580             : 
     581             :         return (PyObject *)nullmask;
     582             : }
     583             : 
     584             : PyObject *
     585          41 : PyDict_CheckForConversion(PyObject *pResult, int expected_columns, char **retcol_names, char **return_message)
     586             : {
     587          41 :         char *msg = MAL_SUCCEED;
     588          41 :         PyObject *result = PyList_New(expected_columns);
     589          41 :         int i;
     590             : 
     591         181 :         for (i = 0; i < expected_columns; i++) {
     592         102 :                 PyObject *object = PyDict_GetItemString(pResult, retcol_names[i]);
     593         102 :                 if (object == NULL) {
     594           2 :                         msg = createException(MAL, "pyapi3.eval",
     595             :                                                                 SQLSTATE(PY000) "Expected a return value with name \"%s\", but "
     596             :                                                                 "this key was not present in the dictionary.",
     597             :                                                                 retcol_names[i]);
     598           2 :                         goto wrapup;
     599             :                 }
     600         100 :                 Py_INCREF(object);
     601         100 :                 object = PyObject_CheckForConversion(object, 1, NULL, return_message);
     602         100 :                 if (object == NULL) {
     603           1 :                         msg = createException(
     604             :                                 MAL, "pyapi3.eval",
     605             :                                 SQLSTATE(PY000) "Error converting dict return value \"%s\": %s",
     606             :                                 retcol_names[i], getExceptionMessage(*return_message));
     607           1 :                         freeException(*return_message);
     608           1 :                         goto wrapup;
     609             :                 }
     610          99 :                 if (PyList_CheckExact(object)) {
     611          99 :                         PyObject *item = PyList_GetItem(object, 0);
     612          99 :                         PyList_SetItem(result, i, item);
     613          99 :                         Py_INCREF(item);
     614          99 :                         Py_DECREF(object);
     615             :                 } else {
     616           0 :                         if (object)
     617           0 :                                 Py_DECREF(object);
     618           0 :                         msg = createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "Why is this not a list?");
     619           0 :                         goto wrapup;
     620             :                 }
     621             :         }
     622          38 :         Py_DECREF(pResult);
     623          38 :         return result;
     624           3 : wrapup:
     625           3 :         *return_message = msg;
     626           3 :         Py_DECREF(result);
     627           3 :         Py_DECREF(pResult);
     628           3 :         return NULL;
     629             : }
     630             : 
     631             : PyObject *
     632         212 : PyObject_CheckForConversion(PyObject *pResult, int expected_columns, int *actual_columns, char **return_message)
     633             : {
     634         212 :         char *msg;
     635         212 :         int columns = 0;
     636             : 
     637         212 :         if (pResult) {
     638         212 :                 PyObject *pColO = NULL;
     639         212 :                 if (PyType_IsPandasDataFrame(pResult)) {
     640             :                         // the result object is a Pandas data frame
     641             :                         // we can convert the pandas data frame to a numpy array by simply
     642             :                         // accessing the "values" field (as pandas dataframes are numpy
     643             :                         // arrays internally)
     644           0 :                         PyObject *nResult = PyObject_GetAttrString(pResult, "values");
     645           0 :                         Py_DECREF(pResult);
     646           0 :                         if (nResult == NULL) {
     647           0 :                                 msg = createException(MAL, "pyapi3.eval",
     648             :                                                                           SQLSTATE(PY000) "Invalid Pandas data frame.");
     649           0 :                                 goto wrapup;
     650             :                         }
     651             :                         // we transpose the values field so it's aligned correctly for our
     652             :                         // purposes
     653           0 :                         pResult = PyObject_GetAttrString(nResult, "T");
     654           0 :                         Py_DECREF(nResult);
     655           0 :                         if (pResult == NULL) {
     656           0 :                                 msg = createException(MAL, "pyapi3.eval",
     657             :                                                                           SQLSTATE(PY000) "Invalid Pandas data frame.");
     658           0 :                                 goto wrapup;
     659             :                         }
     660             :                 }
     661             : 
     662         212 :                 if (PyType_IsPyScalar(pResult)) { // check if the return object is a scalar
     663          68 :                         if (expected_columns == 1 || expected_columns <= 0) {
     664             :                                 // if we only expect a single return value, we can accept
     665             :                                 // scalars by converting it into an array holding an array
     666             :                                 // holding the element (i.e. [[pResult]])
     667          68 :                                 PyObject *list = PyList_New(1);
     668          68 :                                 PyList_SetItem(list, 0, pResult);
     669          68 :                                 pResult = list;
     670             : 
     671          68 :                                 list = PyList_New(1);
     672          68 :                                 PyList_SetItem(list, 0, pResult);
     673          68 :                                 pResult = list;
     674             : 
     675          68 :                                 columns = 1;
     676             :                         } else {
     677             :                                 // the result object is a scalar, yet we expect more than one
     678             :                                 // return value. We can only convert the result into a list with
     679             :                                 // a single element, so the output is necessarily wrong.
     680           0 :                                 msg = createException(
     681             :                                         MAL, "pyapi3.eval",
     682             :                                         SQLSTATE(PY000) "A single scalar was returned, yet we expect a list of %d "
     683             :                                         "columns. We can only convert a single scalar into a "
     684             :                                         "single column, thus the result is invalid.",
     685             :                                         expected_columns);
     686           0 :                                 goto wrapup;
     687             :                         }
     688             :                 } else {
     689             :                         // if it is not a scalar, we check if it is a single array
     690         144 :                         bool IsSingleArray = true;
     691         144 :                         PyObject *data = pResult, *ndata = NULL;
     692         144 :                         if (PyType_IsNumpyMaskedArray(data)) {
     693          30 :                                 ndata = PyObject_GetAttrString(pResult, "data");
     694          30 :                                 if (ndata == NULL) {
     695           0 :                                         msg = createException(MAL, "pyapi3.eval",
     696             :                                                                                   SQLSTATE(PY000) "Invalid masked array.");
     697           0 :                                         goto wrapup;
     698             :                                 }
     699             :                                 data = ndata;
     700             :                         }
     701         144 :                         if (PyType_IsNumpyArray(data)) {
     702         105 :                                 if (PyArray_NDIM((PyArrayObject *)data) != 1) {
     703             :                                         IsSingleArray = false;
     704         105 :                                 } else if (PyArray_SIZE((PyArrayObject *)data) == 0) {
     705             :                                         IsSingleArray = true;
     706             :                                 } else {
     707         103 :                                         pColO = PyArray_GETITEM( (PyArrayObject *)data, PyArray_GETPTR1((PyArrayObject *)data, 0));
     708         103 :                                         IsSingleArray = PyType_IsPyScalar(pColO);
     709         103 :                                         Py_DECREF(pColO);
     710             :                                 }
     711          39 :                         } else if (PyList_Check(data)) {
     712          39 :                                 if (PyList_Size(data) == 0) {
     713             :                                         IsSingleArray = true;
     714             :                                 } else {
     715          39 :                                         pColO = PyList_GetItem(data, 0);
     716          39 :                                         IsSingleArray = PyType_IsPyScalar(pColO);
     717             :                                 }
     718           0 :                         } else if (!PyType_IsNumpyMaskedArray(data)) {
     719             :                                 // it is neither a python array, numpy array or numpy masked
     720             :                                 // array, thus the result is unsupported! Throw an exception!
     721           0 :                                 msg = createException(
     722             :                                         MAL, "pyapi3.eval",
     723             :                                         SQLSTATE(PY000) "Unsupported result object. Expected either a list, "
     724             :                                         "dictionary, a numpy array, a numpy masked array or a "
     725             :                                         "pandas data frame, but received an object of type \"%s\"",
     726             :                                         /* leaks */
     727             :                                         PyUnicode_AsUTF8(PyObject_Str(PyObject_Type(data))));
     728           0 :                                 goto wrapup;
     729             :                         }
     730             : 
     731         142 :                         if (IsSingleArray) {
     732         136 :                                 if (expected_columns == 1 || expected_columns <= 0) {
     733             :                                         // if we only expect a single return value, we can accept a
     734             :                                         // single array by converting it into an array holding an
     735             :                                         // array holding the element (i.e. [pResult])
     736         136 :                                         PyObject *list = PyList_New(1);
     737         136 :                                         PyList_SetItem(list, 0, pResult);
     738         136 :                                         pResult = list;
     739             : 
     740         136 :                                         columns = 1;
     741             :                                 } else {
     742             :                                         // the result object is a single array, yet we expect more
     743             :                                         // than one return value. We can only convert the result
     744             :                                         // into a list with a single array, so the output is
     745             :                                         // necessarily wrong.
     746           0 :                                         msg = createException(MAL, "pyapi3.eval",
     747             :                                                                                   SQLSTATE(PY000) "A single array was returned, yet we "
     748             :                                                                                   "expect a list of %d columns. The "
     749             :                                                                                   "result is invalid.",
     750             :                                                                                   expected_columns);
     751           0 :                                         goto wrapup;
     752             :                                 }
     753             :                         } else {
     754             :                                 // the return value is an array of arrays, all we need to do is
     755             :                                 // check if it is the correct size
     756           8 :                                 int results = 0;
     757           8 :                                 if (PyList_Check(data))
     758           8 :                                         results = (int)PyList_Size(data);
     759             :                                 else
     760           0 :                                         results = (int)PyArray_DIMS((PyArrayObject *)data)[0];
     761           8 :                                 columns = results;
     762           8 :                                 if (results != expected_columns && expected_columns > 0) {
     763             :                                         // wrong return size, we expect pci->retc arrays
     764           1 :                                         msg = createException(MAL, "pyapi3.eval",
     765             :                                                                                   SQLSTATE(PY000) "An array of size %d was returned, "
     766             :                                                                                   "yet we expect a list of %d columns. "
     767             :                                                                                   "The result is invalid.",
     768             :                                                                                   results, expected_columns);
     769           1 :                                         goto wrapup;
     770             :                                 }
     771             :                         }
     772         143 :                         if (ndata)
     773          30 :                                 Py_DECREF(ndata);
     774             :                 }
     775             :         } else {
     776           0 :                 msg = createException(
     777             :                         MAL, "pyapi3.eval",
     778             :                         SQLSTATE(PY000) "Invalid result object. No result object could be generated.");
     779           0 :                 goto wrapup;
     780             :         }
     781             : 
     782         211 :         if (actual_columns != NULL)
     783           0 :                 *actual_columns = columns;
     784             :         return pResult;
     785           1 : wrapup:
     786           1 :         if (actual_columns != NULL)
     787           0 :                 *actual_columns = columns;
     788           1 :         *return_message = msg;
     789           1 :         return NULL;
     790             : }
     791             : 
     792             : #ifndef NUMPY_CORE_INCLUDE_NUMPY_NPY_2_COMPAT_H_
     793             : #define PyDataType_ELSIZE(x) (x)->elsize
     794             : #endif
     795             : 
     796             : str
     797         254 : PyObject_GetReturnValues(PyObject *obj, PyReturn *ret)
     798             : {
     799         254 :         PyObject *pMask = NULL;
     800         254 :         str msg = MAL_SUCCEED;
     801             :         // If it isn't we need to convert pColO to the expected Numpy Array type
     802         254 :         ret->numpy_array = PyArray_FromAny( obj, NULL, 1, 1, NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST, NULL);
     803         254 :         if (ret->numpy_array == NULL) {
     804           0 :                 msg = createException(
     805             :                         MAL, "pyapi3.eval",
     806             :                         SQLSTATE(PY000) "Could not create a Numpy array from the return type.\n");
     807           0 :                 goto wrapup;
     808             :         }
     809             : 
     810         254 :         ret->result_type = PyArray_DESCR((PyArrayObject *)ret->numpy_array)->type_num; // We read the result type from the resulting array
     811         254 :         ret->memory_size = PyDataType_ELSIZE(PyArray_DESCR((PyArrayObject *)ret->numpy_array));
     812         254 :         ret->count = PyArray_DIMS((PyArrayObject *)ret->numpy_array)[0];
     813         254 :         ret->array_data = PyArray_DATA((PyArrayObject *)ret->numpy_array);
     814         254 :         ret->mask_data = NULL;
     815         254 :         ret->numpy_mask = NULL;
     816             :         // If pColO is a Masked array, we convert the mask to a NPY_BOOL numpy array
     817         254 :         if (PyObject_HasAttrString(obj, "mask")) {
     818          30 :                 pMask = PyObject_GetAttrString(obj, "mask");
     819          30 :                 if (pMask != NULL) {
     820          30 :                         ret->numpy_mask = PyArray_FromAny(pMask, PyArray_DescrFromType(NPY_BOOL), 1, 1, NPY_ARRAY_CARRAY, NULL);
     821          30 :                         Py_DECREF(pMask);
     822          30 :                         if (ret->numpy_mask == NULL || PyArray_DIMS((PyArrayObject *)ret->numpy_mask)[0] != (int)ret->count) {
     823           0 :                                 PyErr_Clear();
     824           0 :                                 if (ret->numpy_mask)
     825           0 :                                         Py_DECREF(ret->numpy_mask);
     826           0 :                                 ret->numpy_mask = NULL;
     827             :                         }
     828             :                 }
     829             :         }
     830         254 :         if (ret->numpy_mask != NULL)
     831          30 :                 ret->mask_data = PyArray_DATA((PyArrayObject *)ret->numpy_mask);
     832         224 : wrapup:
     833         254 :         return msg;
     834             : }
     835             : 
     836             : bool
     837         150 : PyObject_PreprocessObject(PyObject *pResult, PyReturn *pyreturn_values, int column_count, char **return_message)
     838             : {
     839         150 :         int i;
     840         150 :         char *msg;
     841         363 :         for (i = 0; i < column_count; i++) {
     842             :                 // Refers to the current Numpy mask (if it exists)
     843         213 :                 PyObject *pMask = NULL;
     844             :                 // Refers to the current Numpy array
     845         213 :                 PyObject *pColO = NULL;
     846             :                 // This is the PyReturn header information for the current return value,
     847             :                 // we will fill this now
     848         213 :                 PyReturn *ret = &pyreturn_values[i];
     849             : 
     850         213 :                 ret->multidimensional = false;
     851             :                 // There are three possibilities (we have ensured this right after
     852             :                 // executing the Python call by calling PyObject_CheckForConversion)
     853             :                 // 1: The top level result object is a PyList or Numpy Array containing
     854             :                 // pci->retc Numpy Arrays
     855             :                 // 2: The top level result object is a (pci->retc x N) dimensional Numpy
     856             :                 // Array [Multidimensional]
     857             :                 // 3: The top level result object is a (pci->retc x N) dimensional Numpy
     858             :                 // Masked Array [Multidimensional]
     859         213 :                 if (PyList_Check(pResult)) {
     860             :                         // If it is a PyList, we simply get the i'th Numpy array from the
     861             :                         // PyList
     862         213 :                         pColO = PyList_GetItem(pResult, i);
     863         213 :                         Py_INCREF(pColO);
     864             :                 } else {
     865             :                         // If it isn't, the result object is either a Nump Masked Array or a
     866             :                         // Numpy Array
     867           0 :                         PyObject *data = pResult;
     868           0 :                         if (PyType_IsNumpyMaskedArray(data)) {
     869           0 :                                 assert(0);
     870             :                                 data = PyObject_GetAttrString( pResult, "data");
     871             :                                 // If it is a Masked array, the data is stored in the masked_array.data attribute
     872             :                                 pMask = PyObject_GetAttrString(pResult, "mask");
     873             :                         }
     874             : 
     875             :                         // We can either have a multidimensional numpy array, or a single
     876             :                         // dimensional numpy array
     877           0 :                         if (PyArray_NDIM((PyArrayObject *)data) != 1) {
     878             :                                 // If it is a multidimensional numpy array, we have to convert
     879             :                                 // the i'th dimension to a NUMPY array object
     880           0 :                                 ret->multidimensional = true;
     881           0 :                                 ret->result_type = PyArray_DESCR((PyArrayObject *)data)->type_num;
     882             :                         } else {
     883             :                                 // If it is a single dimensional Numpy array, we get the i'th
     884             :                                 // Numpy array from the Numpy Array
     885           0 :                                 pColO = PyArray_GETITEM((PyArrayObject *)data, PyArray_GETPTR1((PyArrayObject *)data, i));
     886             :                         }
     887             :                 }
     888             : 
     889             :                 // Now we have to do some preprocessing on the data
     890         213 :                 if (ret->multidimensional) {
     891           0 :                         assert(pColO==NULL);
     892             :                         // If it is a multidimensional Numpy array, we don't need to do any
     893             :                         // conversion, we can just do some pointers
     894           0 :                         ret->count = PyArray_DIMS((PyArrayObject *)pResult)[1];
     895           0 :                         ret->numpy_array = pResult;
     896           0 :                         ret->numpy_mask = pMask;
     897           0 :                         ret->array_data = PyArray_DATA((PyArrayObject *)ret->numpy_array);
     898           0 :                         if (ret->numpy_mask != NULL)
     899             :                                 ret->mask_data = PyArray_DATA((PyArrayObject *)ret->numpy_mask);
     900           0 :                         ret->memory_size = PyDataType_ELSIZE(PyArray_DESCR((PyArrayObject *)ret->numpy_array));
     901             :                 } else {
     902         213 :                         msg = PyObject_GetReturnValues(pColO, ret);
     903         213 :                         Py_DECREF(pColO);
     904         213 :                         if (msg != MAL_SUCCEED) {
     905           0 :                                 goto wrapup;
     906             :                         }
     907             :                 }
     908             :         }
     909             :         return true;
     910           0 : wrapup:
     911           0 :         *return_message = msg;
     912           0 :         return false;
     913             : }
     914             : 
     915             : BAT *
     916         213 : PyObject_ConvertToBAT(PyReturn *ret, sql_subtype *type, int bat_type, int i, oid seqbase, char **return_message, bool copy)
     917             : {
     918         213 :         BAT *b = NULL;
     919         213 :         size_t index_offset = 0;
     920         213 :         char *msg;
     921         213 :         size_t iu;
     922             : 
     923         213 :         if (ret->multidimensional)
     924           0 :                 index_offset = i;
     925             : 
     926         213 :         switch (GetSQLType(type)) {
     927           3 :                 case EC_TIMESTAMP:
     928           3 :                         bat_type = TYPE_timestamp;
     929           3 :                         break;
     930           3 :                 case EC_TIME:
     931           3 :                         bat_type = TYPE_daytime;
     932           3 :                         break;
     933           4 :                 case EC_DATE:
     934           4 :                         bat_type = TYPE_date;
     935           4 :                         break;
     936           4 :                 case EC_DEC:
     937           4 :                         bat_type = TYPE_dbl;
     938           4 :                         break;
     939             :                 default:
     940             :                         break;
     941             :         }
     942             : 
     943         213 :         if (IsBlobType(bat_type)) {
     944           2 :                 bool *mask = NULL;
     945           2 :                 char *data = NULL;
     946           2 :                 blob *ele_blob;
     947           2 :                 size_t blob_fixed_size = ret->memory_size;
     948             : 
     949           2 :                 PyObject *pickle_module = NULL, *pickle = NULL;
     950           2 :                 bool gstate = 0;
     951             : 
     952           2 :                 if (ret->result_type == NPY_OBJECT) {
     953             :                         // Python objects, we may need to pickle them, so we
     954             :                         // may execute Python code, we have to obtain the GIL
     955           0 :                         gstate = Python_ObtainGIL();
     956           0 :                         pickle_module = PyImport_ImportModule("pickle");
     957           0 :                         if (pickle_module == NULL) {
     958           0 :                                 msg = createException(MAL, "pyapi3.eval",
     959             :                                                                           SQLSTATE(PY000) "Can't load pickle module to pickle python object to blob");
     960           0 :                                 Python_ReleaseGIL(gstate);
     961           0 :                                 goto wrapup;
     962             :                         }
     963             :                         blob_fixed_size = 0; // Size depends on the objects
     964             :                 }
     965             : 
     966           2 :                 if (ret->mask_data != NULL) {
     967             :                         mask = (bool *)ret->mask_data;
     968             :                 }
     969           2 :                 if (ret->array_data == NULL) {
     970           0 :                         msg = createException(MAL, "pyapi3.eval",
     971             :                                                                   SQLSTATE(PY000) "No return value stored in the structure.");
     972           0 :                         if (ret->result_type == NPY_OBJECT) {
     973           0 :                                 Py_XDECREF(pickle_module);
     974           0 :                                 Python_ReleaseGIL(gstate);
     975             :                         }
     976           0 :                         goto wrapup;
     977             :                 }
     978           2 :                 data = (char *)ret->array_data;
     979           2 :                 data += (index_offset * ret->count) * ret->memory_size;
     980           2 :                 b = COLnew(seqbase, TYPE_blob, (BUN)ret->count, TRANSIENT);
     981           2 :                 if (b == NULL) {
     982           0 :                         if (ret->result_type == NPY_OBJECT) {
     983           0 :                                 Py_XDECREF(pickle_module);
     984           0 :                                 Python_ReleaseGIL(gstate);
     985             :                         }
     986           0 :                         msg = createException(MAL, "pyapi3.eval", GDK_EXCEPTION);
     987           0 :                         goto wrapup;
     988             :                 }
     989           2 :                 b->tnil = false;
     990           2 :                 b->tnonil = true;
     991           2 :                 b->tkey = false;
     992           2 :                 b->tsorted = false;
     993           2 :                 b->trevsorted = false;
     994           8 :                 for (iu = 0; iu < ret->count; iu++) {
     995             : 
     996           6 :                         char* memcpy_data;
     997           6 :                         size_t blob_len = 0;
     998             : 
     999           6 :                         if (ret->result_type == NPY_OBJECT) {
    1000           0 :                                 PyObject *object = *((PyObject **)&data[0]);
    1001           0 :                                 if (PyByteArray_Check(object)) {
    1002           0 :                                         memcpy_data = PyByteArray_AsString(object);
    1003           0 :                                         blob_len = pyobject_get_size(object);
    1004             :                                 } else {
    1005           0 :                                         pickle = PyObject_CallMethod(pickle_module, "dumps", "O", object);
    1006           0 :                                         if (pickle == NULL) {
    1007           0 :                                                 msg = createException(MAL, "pyapi3.eval",
    1008             :                                                                                           SQLSTATE(PY000) "Can't pickle object to blob");
    1009           0 :                                                 Py_XDECREF(pickle_module);
    1010           0 :                                                 Python_ReleaseGIL(gstate);
    1011           0 :                                                 goto wrapup;
    1012             :                                         }
    1013           0 :                                         memcpy_data = PyBytes_AsString(pickle);
    1014           0 :                                         blob_len = pyobject_get_size(pickle);
    1015           0 :                                         Py_XDECREF(pickle);
    1016             :                                 }
    1017           0 :                                 if (memcpy_data == NULL) {
    1018           0 :                                         msg = createException(MAL, "pyapi3.eval",
    1019             :                                                                                   SQLSTATE(PY000) "Can't get blob pickled object as char*");
    1020           0 :                                         Py_XDECREF(pickle_module);
    1021           0 :                                         Python_ReleaseGIL(gstate);
    1022           0 :                                         goto wrapup;
    1023             :                                 }
    1024             :                         } else {
    1025             :                                 memcpy_data = data;
    1026             :                         }
    1027             : 
    1028           6 :                         if (mask && mask[iu]) {
    1029           0 :                                 ele_blob = (blob *)GDKmalloc(offsetof(blob, data));
    1030           0 :                                 ele_blob->nitems = ~(size_t)0;
    1031             :                         } else {
    1032           6 :                                 if (blob_fixed_size > 0) {
    1033           6 :                                         blob_len = blob_fixed_size;
    1034             :                                 }
    1035           6 :                                 ele_blob = GDKmalloc(blobsize(blob_len));
    1036           6 :                                 ele_blob->nitems = blob_len;
    1037           6 :                                 memcpy(ele_blob->data, memcpy_data, blob_len);
    1038             :                         }
    1039           6 :                         if (BUNappend(b, ele_blob, false) != GDK_SUCCEED) {
    1040           0 :                                 if (ret->result_type == NPY_OBJECT) {
    1041           0 :                                         Py_XDECREF(pickle_module);
    1042           0 :                                         Python_ReleaseGIL(gstate);
    1043             :                                 }
    1044           0 :                                 BBPunfix(b->batCacheid);
    1045           0 :                                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1046           0 :                                 goto wrapup;
    1047             :                         }
    1048           6 :                         GDKfree(ele_blob);
    1049           6 :                         data += ret->memory_size;
    1050             : 
    1051             :                 }
    1052             : 
    1053             :                 // We are done, we can release the GIL
    1054           2 :                 if (ret->result_type == NPY_OBJECT) {
    1055           0 :                         Py_XDECREF(pickle_module);
    1056           0 :                         Python_ReleaseGIL(gstate);
    1057             :                 }
    1058             : 
    1059           2 :                 BATsetcount(b, (BUN)ret->count);
    1060             :         } else {
    1061         211 :                 switch (bat_type) {
    1062           0 :                         case TYPE_void:
    1063           0 :                                 NP_CREATE_EMPTY_BAT(b, oid);
    1064           0 :                                 break;
    1065           9 :                         case TYPE_bit:
    1066          18 :                                 NP_CREATE_BAT(b, bit);
    1067             :                                 break;
    1068           1 :                         case TYPE_bte:
    1069         101 :                                 NP_CREATE_BAT(b, bte);
    1070             :                                 break;
    1071           2 :                         case TYPE_sht:
    1072      100003 :                                 NP_CREATE_BAT(b, sht);
    1073             :                                 break;
    1074         136 :                         case TYPE_int:
    1075    20470561 :                                 NP_CREATE_BAT(b, int);
    1076             :                                 break;
    1077           0 :                         case TYPE_oid:
    1078           0 :                                 NP_CREATE_BAT(b, oid);
    1079             :                                 break;
    1080           2 :                         case TYPE_lng:
    1081           4 :                                 NP_CREATE_BAT(b, lng);
    1082             :                                 break;
    1083           0 :                         case TYPE_flt:
    1084           0 :                                 NP_CREATE_BAT(b, flt);
    1085             :                                 break;
    1086          30 :                         case TYPE_dbl:
    1087     1700171 :                                 NP_CREATE_BAT(b, dbl);
    1088             :                                 break;
    1089             : #ifdef HAVE_HGE
    1090           0 :                         case TYPE_hge:
    1091           0 :                                 NP_CREATE_BAT(b, hge);
    1092             :                                 break;
    1093             : #endif
    1094           4 :                         case TYPE_date:
    1095          10 :                                 NP_CREATE_BAT(b, date);
    1096             :                                 break;
    1097           3 :                         case TYPE_daytime:
    1098           6 :                                 NP_CREATE_BAT(b, daytime);
    1099             :                                 break;
    1100           3 :                         case TYPE_timestamp:
    1101           6 :                                 NP_CREATE_BAT(b, timestamp);
    1102             :                                 break;
    1103          21 :                         case TYPE_str: {
    1104          21 :                                 bool *mask = NULL;
    1105          21 :                                 char *data = NULL;
    1106          21 :                                 char *utf8_string = NULL;
    1107          21 :                                 if (ret->mask_data != NULL) {
    1108             :                                         mask = (bool *)ret->mask_data;
    1109             :                                 }
    1110          21 :                                 if (ret->array_data == NULL) {
    1111           0 :                                         msg = createException(
    1112             :                                                 MAL, "pyapi3.eval",
    1113             :                                                 SQLSTATE(PY000) "No return value stored in the structure.  n");
    1114           0 :                                         goto wrapup;
    1115             :                                 }
    1116          21 :                                 data = (char *)ret->array_data;
    1117             : 
    1118          21 :                                 if (ret->result_type != NPY_OBJECT) {
    1119          15 :                                         utf8_string = GDKzalloc(utf8string_minlength + ret->memory_size + 1);
    1120          15 :                                         utf8_string[utf8string_minlength + ret->memory_size] = '\0';
    1121             :                                 }
    1122             : 
    1123          21 :                                 b = COLnew(seqbase, TYPE_str, (BUN)ret->count, TRANSIENT);
    1124          21 :                                 if (b == NULL) {
    1125           0 :                                         GDKfree(utf8_string);
    1126           0 :                                         msg = createException(MAL, "pyapi3.eval", GDK_EXCEPTION);
    1127           0 :                                         goto wrapup;
    1128             :                                 }
    1129          21 :                                 b->tnil = false;
    1130          21 :                                 b->tnonil = true;
    1131          21 :                                 b->tkey = false;
    1132          21 :                                 b->tsorted = false;
    1133          21 :                                 b->trevsorted = false;
    1134     1300085 :                                 NP_INSERT_STRING_BAT(b);
    1135          21 :                                 GDKfree(utf8_string);
    1136          21 :                                 BATsetcount(b, (BUN)ret->count);
    1137          21 :                                 break;
    1138             :                         }
    1139           0 :                         default:
    1140           0 :                                 msg = createException(MAL, "pyapi3.eval",
    1141             :                                                                           SQLSTATE(PY000) "Unrecognized BAT type %s.\n",
    1142             :                                                                           BatType_Format(bat_type));
    1143           0 :                                 goto wrapup;
    1144             :                 }
    1145             :         }
    1146         213 :         if (ConvertableSQLType(type)) {
    1147           4 :                 BAT *result;
    1148           4 :                 msg = ConvertToSQLType(NULL, b, type, &result, &bat_type);
    1149           4 :                 BBPunfix(b->batCacheid);
    1150           4 :                 if (msg != MAL_SUCCEED) {
    1151           0 :                         goto wrapup;
    1152             :                 }
    1153           4 :                 b = result;
    1154             :         }
    1155             : 
    1156             :         return b;
    1157             : 
    1158           0 :   wrapup:
    1159           0 :         *return_message = msg;
    1160           0 :         return NULL;
    1161             : }
    1162             : 
    1163         538 : bit ConvertableSQLType(sql_subtype *sql_subtype)
    1164             : {
    1165         538 :         switch (GetSQLType(sql_subtype)) {
    1166             :                 case EC_DEC:
    1167             :                         return 1;
    1168             :         }
    1169         531 :         return 0;
    1170             : }
    1171             : 
    1172         751 : int GetSQLType(sql_subtype *sql_subtype)
    1173             : {
    1174         751 :         if (!sql_subtype)
    1175             :                 return -1;
    1176         535 :         if (!sql_subtype->type)
    1177             :                 return -1;
    1178         535 :         return (int) sql_subtype->type->eclass;
    1179             : }
    1180             : 
    1181             : str
    1182           4 : ConvertFromSQLType(BAT *b, sql_subtype *sql_subtype, BAT **ret_bat, int *ret_type)
    1183             : {
    1184           4 :         str res = MAL_SUCCEED;
    1185           4 :         int conv_type;
    1186             : 
    1187           4 :         assert(sql_subtype);
    1188           4 :         assert(sql_subtype->type);
    1189             : 
    1190           4 :         switch (sql_subtype->type->eclass) {
    1191             :                 case EC_DEC:
    1192             :                         conv_type = TYPE_dbl;
    1193             :                         break;
    1194             :                 default:
    1195           1 :                         conv_type = TYPE_str;
    1196             :         }
    1197             : 
    1198           4 :         if (conv_type == TYPE_str) {
    1199           1 :                 BUN p = 0, q = 0;
    1200           1 :                 char *result = NULL;
    1201           1 :                 size_t length = 0;
    1202           1 :                 ssize_t (*strConversion)(str *, size_t *, const void *, bool) =
    1203           1 :                         BATatoms[b->ttype].atomToStr;
    1204           1 :                 *ret_bat = COLnew(0, TYPE_str, 0, TRANSIENT);
    1205           1 :                 *ret_type = conv_type;
    1206           1 :                 if (!(*ret_bat)) {
    1207           0 :                         return createException(MAL, "pyapi3.eval",
    1208             :                                                                    SQLSTATE(HY013) MAL_MALLOC_FAIL " string conversion BAT.");
    1209             :                 }
    1210           1 :                 BATiter li = bat_iterator(b);
    1211           2 :                 BATloop(b, p, q)
    1212             :                 {
    1213           1 :                         const void *element = (const void*)BUNtail(li, p);
    1214           1 :                         if (strConversion(&result, &length, element, false) < 0) {
    1215           0 :                                 bat_iterator_end(&li);
    1216           0 :                                 BBPunfix((*ret_bat)->batCacheid);
    1217           0 :                                 return createException(MAL, "pyapi3.eval",
    1218             :                                                                            SQLSTATE(PY000) "Failed to convert element to string.");
    1219             :                         }
    1220           1 :                         if (BUNappend(*ret_bat, result, false) != GDK_SUCCEED) {
    1221           0 :                                 bat_iterator_end(&li);
    1222           0 :                                 BBPunfix((*ret_bat)->batCacheid);
    1223           0 :                                 throw(MAL, "pyapi3.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
    1224             :                         }
    1225             :                 }
    1226           1 :                 bat_iterator_end(&li);
    1227           1 :                 if (result) {
    1228           1 :                         GDKfree(result);
    1229             :                 }
    1230           1 :                 return res;
    1231           3 :         } else if (conv_type == TYPE_dbl) {
    1232           3 :                 int bat_type = ATOMstorage(b->ttype);
    1233           3 :                 int hpos = sql_subtype->scale;
    1234           3 :                 bat result = 0;
    1235             :                 // decimal values can be stored in various numeric fields, so check the
    1236             :                 // numeric field and convert the one it's actually stored in
    1237           3 :                 switch (bat_type) {
    1238           0 :                         case TYPE_bte:
    1239           0 :                                 res = batbte_dec2_dbl(&result, &hpos, &b->batCacheid, NULL);
    1240           0 :                                 break;
    1241           0 :                         case TYPE_sht:
    1242           0 :                                 res = batsht_dec2_dbl(&result, &hpos, &b->batCacheid, NULL);
    1243           0 :                                 break;
    1244           0 :                         case TYPE_int:
    1245           0 :                                 res = batint_dec2_dbl(&result, &hpos, &b->batCacheid, NULL);
    1246           0 :                                 break;
    1247           3 :                         case TYPE_lng:
    1248           3 :                                 res = batlng_dec2_dbl(&result, &hpos, &b->batCacheid, NULL);
    1249           3 :                                 break;
    1250             : #ifdef HAVE_HGE
    1251           0 :                         case TYPE_hge:
    1252           0 :                                 res = bathge_dec2_dbl(&result, &hpos, &b->batCacheid, NULL);
    1253           0 :                                 break;
    1254             : #endif
    1255           0 :                         default:
    1256           0 :                                 return createException(MAL, "pyapi3.eval",
    1257             :                                                                            "Unsupported decimal storage type.");
    1258             :                 }
    1259           3 :                 if (res == MAL_SUCCEED) {
    1260           3 :                         *ret_bat = BATdescriptor(result);
    1261           3 :                         BBPrelease(result);
    1262           3 :                         *ret_type = TYPE_dbl;
    1263             :                 } else {
    1264           0 :                         *ret_bat = NULL;
    1265             :                 }
    1266           3 :                 return res;
    1267             :         }
    1268             :         return createException(MAL, "pyapi3.eval", "Unrecognized conv type.");
    1269             : }
    1270             : 
    1271           4 : str ConvertToSQLType(Client cntxt, BAT *b, sql_subtype *sql_subtype,
    1272             :                                          BAT **ret_bat, int *ret_type)
    1273             : {
    1274           4 :         str res = MAL_SUCCEED;
    1275           4 :         bat result_bat = 0;
    1276           4 :         int digits = sql_subtype->digits;
    1277           4 :         int scale = sql_subtype->scale;
    1278           4 :         (void)cntxt;
    1279             : 
    1280           4 :         assert(sql_subtype);
    1281           4 :         assert(sql_subtype->type);
    1282             : 
    1283           4 :         switch (sql_subtype->type->eclass) {
    1284           0 :                 case EC_TIMESTAMP:
    1285           0 :                         res = batstr_2time_timestamp(&result_bat, &b->batCacheid, NULL, &digits);
    1286           0 :                         break;
    1287           0 :                 case EC_TIME:
    1288           0 :                         res = batstr_2time_daytime(&result_bat, &b->batCacheid, NULL, &digits);
    1289           0 :                         break;
    1290           0 :                 case EC_DATE:
    1291           0 :                         if ((*ret_bat = BATconvert(b, NULL, TYPE_date, 0, 0, 0)) == NULL)
    1292           0 :                                 throw(MAL, "pyapi3.eval", GDK_EXCEPTION);
    1293           0 :                         *ret_type = TYPE_date;
    1294           0 :                         return MAL_SUCCEED;
    1295           4 :                 case EC_DEC:
    1296           4 :                         res = batdbl_num2dec_lng(&result_bat, &b->batCacheid, NULL,
    1297             :                                                                          &digits, &scale);
    1298           4 :                         break;
    1299           0 :                 default:
    1300           0 :                         return createException(
    1301             :                                 MAL, "pyapi3.eval",
    1302             :                                 "Convert To SQL Type: Unrecognized SQL type %s (%d).",
    1303             :                                 sql_subtype->type->base.name, (int) sql_subtype->type->eclass);
    1304             :         }
    1305           4 :         if (res == MAL_SUCCEED) {
    1306           4 :                 *ret_bat = BATdescriptor(result_bat);
    1307           4 :                 BBPrelease(result_bat);
    1308           4 :                 *ret_type = (*ret_bat)->ttype;
    1309             :         }
    1310             : 
    1311             :         return res;
    1312             : }
    1313             : 
    1314         224 : ssize_t PyType_Size(PyObject *obj)
    1315             : {
    1316         224 :         if (PyType_IsPyScalar(obj)) {
    1317             :                 return 1;
    1318             :         }
    1319          62 :         if (PyArray_Check(obj)) {
    1320          57 :                 return PyArray_Size(obj);
    1321             :         }
    1322           5 :         if (PyList_Check(obj)) {
    1323           5 :                 return Py_SIZE(obj);
    1324             :         }
    1325             :         return -1;
    1326             : }
    1327             : 
    1328         326 : bit IsStandardBATType(int type)
    1329             : {
    1330         326 :         switch (type) {
    1331             :                 case TYPE_bit:
    1332             :                 case TYPE_bte:
    1333             :                 case TYPE_sht:
    1334             :                 case TYPE_int:
    1335             :                 case TYPE_oid:
    1336             :                 case TYPE_lng:
    1337             :                 case TYPE_flt:
    1338             :                 case TYPE_dbl:
    1339             : #ifdef HAVE_HGE
    1340             :                 case TYPE_hge:
    1341             : #endif
    1342             :                 case TYPE_date:
    1343             :                 case TYPE_daytime:
    1344             :                 case TYPE_timestamp:
    1345             :                 case TYPE_str:
    1346             :                         return 1;
    1347           1 :                 default:
    1348           1 :                         return 0;
    1349             :         }
    1350             : }
    1351             : 
    1352          10 : static void conversion_import_array(void) { _import_array(); }
    1353             : 
    1354          10 : str _conversion_init(void)
    1355             : {
    1356          10 :         str msg = MAL_SUCCEED;
    1357          10 :         conversion_import_array();
    1358             : 
    1359          10 :         return msg;
    1360             : }

Generated by: LCOV version 1.14