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

Generated by: LCOV version 1.14