LCOV - code coverage report
Current view: top level - sql/backends/monet5/UDF/pyapi3 - convert_loops.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 3 12 25.0 %
Date: 2024-11-14 20:04:02 Functions: 1 1 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             : /*
      14             :  * M. Raasveldt
      15             :  * Conversion loops used to convert from BAT <> NumPy Array
      16             :  * these are in a separate header because they are used in multiple places
      17             :  */
      18             : 
      19             : #define BAT_TO_NP_CREATE_ALWAYS(bat, nptpe)                                    \
      20             :         do {                                                                       \
      21             :                 vararray = PyArray_Arange(0, (double)bat->batCount, 1, nptpe);         \
      22             :         } while(0)                                                                 \
      23             : 
      24             : #define BAT_TO_NP(bat, mtpe, nptpe)                                            \
      25             :         do {                                                                       \
      26             :                 BATiter bi = bat_iterator(bat);                                        \
      27             :                 if (copy) {                                                            \
      28             :                         vararray = PyArray_EMPTY(1, elements, nptpe, 0);                   \
      29             :                         memcpy(PyArray_DATA((PyArrayObject *)vararray), bi.base,           \
      30             :                                 sizeof(mtpe) * (t_end - t_start));                             \
      31             :                 } else {                                                               \
      32             :                         vararray =                                                         \
      33             :                                 PyArray_New(&PyArray_Type, 1, elements, nptpe, NULL,           \
      34             :                                                         &((mtpe *)bi.base)[t_start], 0,                    \
      35             :                                                         NPY_ARRAY_CARRAY || !NPY_ARRAY_WRITEABLE, NULL);   \
      36             :                 }                                                                      \
      37             :                 bat_iterator_end(&bi);                                                 \
      38             :         } while(0)
      39             : 
      40             : // This #define creates a new BAT with the internal data and mask from a Numpy
      41             : // array, without copying the data
      42             : // 'bat' is a BAT* pointer, which will contain the new BAT. TYPE_'mtpe' is the
      43             : // BAT type, and 'batstore' is the heap storage type of the BAT (this should be
      44             : // STORE_CMEM or STORE_SHARED)
      45             : #define nancheck_flt(bat)                                                      \
      46             :         do {                                                                       \
      47             :                 for (iu = 0; iu < ret->count; iu++) {                                  \
      48             :                         if (isnan(((flt *)data)[index_offset * ret->count + iu])) {        \
      49             :                                 ((flt *)data)[index_offset * ret->count + iu] = flt_nil;       \
      50             :                                 bat->tnil = true;                                              \
      51             :                         }                                                                  \
      52             :                 }                                                                      \
      53             :                 bat->tnonil = !bat->tnil;                                              \
      54             :         } while (0)
      55             : #define nancheck_dbl(bat)                                                      \
      56             :         do {                                                                       \
      57             :                 for (iu = 0; iu < ret->count; iu++) {                                  \
      58             :                         if (isnan(((dbl *)data)[index_offset * ret->count + iu])) {        \
      59             :                                 ((dbl *)data)[index_offset * ret->count + iu] = dbl_nil;       \
      60             :                                 bat->tnil = true;                                              \
      61             :                         }                                                                  \
      62             :                 }                                                                      \
      63             :                 bat->tnonil = !bat->tnil;                                              \
      64             :         } while (0)
      65             : #define nancheck_bit(bat) ((void)0)
      66             : #define nancheck_bte(bat) ((void)0)
      67             : #define nancheck_sht(bat) ((void)0)
      68             : #define nancheck_int(bat) ((void)0)
      69             : #define nancheck_lng(bat) ((void)0)
      70             : #define nancheck_hge(bat) ((void)0) /* not used if no HAVE_HGE */
      71             : #define nancheck_oid(bat) ((void)0)
      72             : #define nancheck_date(bat) ((void)0)
      73             : #define nancheck_daytime(bat) ((void)0)
      74             : #define nancheck_timestamp(bat) ((void)0)
      75             : #define CREATE_BAT_ZEROCOPY(bat, mtpe, batstore)                               \
      76             :         {                                                                          \
      77             :                 bat = COLnew(seqbase, TYPE_void, 0, TRANSIENT);                        \
      78             :                 if (bat == NULL) {                                                     \
      79             :                         msg = createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "Cannot create column");     \
      80             :                         goto wrapup;                                                       \
      81             :                 }                                                                      \
      82             :                 bat->ttype = TYPE_##mtpe;                                              \
      83             :                 bat->twidth = ATOMsize(TYPE_##mtpe);                                   \
      84             :                 bat->tshift = ATOMelmshift(b->twidth);                                 \
      85             :                 bat->tnil = false;                                                     \
      86             :                 bat->tnonil = true;                                                    \
      87             :                 bat->tkey = false;                                                     \
      88             :                 bat->tsorted = false;                                                  \
      89             :                 bat->trevsorted = false;                                               \
      90             :                 /*Change nil values to the proper values, if they exist*/              \
      91             :                 if (mask != NULL) {                                                    \
      92             :                         for (iu = 0; iu < ret->count; iu++) {                              \
      93             :                                 if (mask[index_offset * ret->count + iu] == TRUE) {            \
      94             :                                         (*(mtpe *)(&data[(index_offset * ret->count + iu) *        \
      95             :                                                                          ret->memory_size])) = mtpe##_nil;         \
      96             :                                         bat->tnil = true;                                          \
      97             :                                 }                                                              \
      98             :                         }                                                                  \
      99             :                         bat->tnonil = !bat->tnil;                                          \
     100             :                 } else {                                                               \
     101             :                         bat->tnil = false;                                                 \
     102             :                         bat->tnonil = false;                                               \
     103             :                         nancheck_##mtpe(bat);                                              \
     104             :                 }                                                                      \
     105             :                 assert(bat->theap->base == NULL);                                      \
     106             :                 bat->theap->base =                                                     \
     107             :                         &data[(index_offset * ret->count) * ret->memory_size];             \
     108             :                 bat->theap->free = ret->count * ret->memory_size;                      \
     109             :                 bat->theap->dirty = true;                                              \
     110             :                 /*If index_offset > 0, we are mapping part of a multidimensional */    \
     111             :                 /* array.*/                                                            \
     112             :                 /*The entire array will be cleared when the part with index_offset=0 */\
     113             :                 /* is freed*/                                                          \
     114             :                 /*So we set this part of the mapping to 'NOWN'*/                       \
     115             :                 if (index_offset > 0) {                                                \
     116             :                         bat->theap->size = bat->theap->free;                               \
     117             :                         bat->theap->storage = STORE_NOWN;                                  \
     118             :                 } else {                                                               \
     119             :                         bat->theap->size = ret->array_size ? ret->array_size : bat->theap->free; \
     120             :                         bat->theap->storage = batstore;                                    \
     121             :                 }                                                                      \
     122             :                 bat->theap->newstorage = STORE_MEM;                                    \
     123             :                 bat->batCount = (BUN)ret->count;                                       \
     124             :                 bat->batCapacity = (BUN)ret->count;                                    \
     125             :                 bat->batCopiedtodisk = false;                                          \
     126             :                 /*Take over the data from the numpy array*/                            \
     127             :                 if (ret->numpy_array != NULL)                                          \
     128             :                         PyArray_CLEARFLAGS((PyArrayObject *)ret->numpy_array,              \
     129             :                                                            NPY_ARRAY_OWNDATA);                             \
     130             :         }
     131             : 
     132             : // This #define converts a Numpy Array to a BAT by copying the internal data to
     133             : // the BAT. It assumes the BAT 'bat' is already created with the proper size.
     134             : // This should only be used with integer data that can be cast. It assumes the
     135             : // Numpy Array has an internal array of type 'mtpe_from', and the BAT has an
     136             : // internal array of type 'mtpe_to'.
     137             : // it then does the cast by simply doing BAT[i] = (mtpe_to)
     138             : // ((mtpe_from*)NUMPY_ARRAY[i]), which only works if both mtpe_to and mtpe_from
     139             : // are integers
     140             : #define NP_COL_BAT_LOOP(bat, mtpe_to, mtpe_from, index)                        \
     141             :         {                                                                          \
     142             :                 if (mask == NULL) {                                                    \
     143             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     144             :                                 ((mtpe_to *)Tloc(bat, 0))[index + iu] = (mtpe_to)(             \
     145             :                                         *(mtpe_from *)(&data[(index_offset * ret->count + iu) *    \
     146             :                                                                                  ret->memory_size]));                  \
     147             :                         }                                                                  \
     148             :                 } else {                                                               \
     149             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     150             :                                 if (mask[index_offset * ret->count + iu] == TRUE) {            \
     151             :                                         bat->tnil = true;                                          \
     152             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = mtpe_to##_nil;     \
     153             :                                 } else {                                                       \
     154             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = (mtpe_to)(*(       \
     155             :                                                 mtpe_from *)(&data[(index_offset * ret->count + iu) *  \
     156             :                                                                                    ret->memory_size]));                \
     157             :                                 }                                                              \
     158             :                         }                                                                  \
     159             :                 }                                                                      \
     160             :         }
     161             : #define NP_COL_BAT_LOOPF(bat, mtpe_to, mtpe_from, index)                       \
     162             :         {                                                                          \
     163             :                 if (mask == NULL) {                                                    \
     164             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     165             :                                 if (isnan((                                                    \
     166             :                                                 (mtpe_from *)data)[index_offset * ret->count + iu])) { \
     167             :                                         bat->tnil = true;                                          \
     168             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = mtpe_to##_nil;     \
     169             :                                 } else {                                                       \
     170             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = (mtpe_to)(         \
     171             :                                                 (mtpe_from *)data)[index_offset * ret->count + iu];    \
     172             :                                 }                                                              \
     173             :                         }                                                                  \
     174             :                 } else {                                                               \
     175             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     176             :                                 if (mask[index_offset * ret->count + iu] == TRUE ||            \
     177             :                                         isnan((                                                    \
     178             :                                                 (mtpe_from *)data)[index_offset * ret->count + iu])) { \
     179             :                                         bat->tnil = true;                                          \
     180             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = mtpe_to##_nil;     \
     181             :                                 } else {                                                       \
     182             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = (mtpe_to)(*(       \
     183             :                                                 mtpe_from *)(&data[(index_offset * ret->count + iu) *  \
     184             :                                                                                    ret->memory_size]));                \
     185             :                                 }                                                              \
     186             :                         }                                                                  \
     187             :                 }                                                                      \
     188             :         }
     189             : 
     190             : // This #define converts a Numpy Array to a BAT by copying the internal data to
     191             : // the BAT. It converts the data from the Numpy Array to the BAT using a
     192             : // function
     193             : // This function has to have the prototype 'bool function(void *data, size_t
     194             : // memory_size, mtpe_to *resulting_value)', and either return False (if
     195             : // conversion fails)
     196             : //  or write the value into the 'resulting_value' pointer. This is used
     197             : //  convertring strings/unicodes/python objects to numeric values.
     198             : #define NP_COL_BAT_LOOP_FUNC(bat, mtpe_to, func, ptrtpe, index)                \
     199             :         {                                                                          \
     200             :                 mtpe_to value;                                                         \
     201             :                 if (mask == NULL) {                                                    \
     202             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     203             :                                 msg = func((ptrtpe *)&data[(index_offset * ret->count + iu) *  \
     204             :                                                                                    ret->memory_size],                  \
     205             :                                                    ret->memory_size, &value);                          \
     206             :                                 if (msg != MAL_SUCCEED) {                                      \
     207             :                                         goto wrapup;                                               \
     208             :                                 }                                                              \
     209             :                                 ((mtpe_to *)Tloc(bat, 0))[index + iu] = value;                 \
     210             :                                 if (!bat->tnil)                                                \
     211             :                                         bat->tnil = is_##mtpe_to##_nil(value);                     \
     212             :                         }                                                                  \
     213             :                 } else {                                                               \
     214             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     215             :                                 if (mask[index_offset * ret->count + iu] == TRUE) {            \
     216             :                                         bat->tnil = true;                                          \
     217             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = mtpe_to##_nil;     \
     218             :                                 } else {                                                       \
     219             :                                         msg = func(                                                \
     220             :                                                 (ptrtpe *)&data[(index_offset * ret->count + iu) *     \
     221             :                                                                                 ret->memory_size],                     \
     222             :                                                 ret->memory_size, &value);                             \
     223             :                                         if (msg != MAL_SUCCEED) {                                  \
     224             :                                                 goto wrapup;                                           \
     225             :                                         }                                                          \
     226             :                                         ((mtpe_to *)Tloc(bat, 0))[index + iu] = value;             \
     227             :                                         if (!bat->tnil)                                            \
     228             :                                                 bat->tnil = is_##mtpe_to##_nil(value);                 \
     229             :                                 }                                                              \
     230             :                         }                                                                  \
     231             :                 }                                                                      \
     232             :         }
     233             : 
     234             : 
     235             : static gdk_return
     236     1200093 : convert_and_append(BAT* b, const char* text, bool force) {
     237     1200093 :         if (b->ttype == TYPE_str) {
     238     1200093 :                 return BUNappend(b, text, force);
     239           0 :         } else if (text == str_nil) {
     240           0 :                 return BUNappend(b, BATatoms[b->ttype].atomNull, force);
     241             :         } else {
     242           0 :                 void* element = NULL;
     243           0 :                 size_t len = 0;
     244           0 :                 gdk_return ret;
     245             : 
     246           0 :                 if (BATatoms[b->ttype].atomFromStr(text, &len, &element, false) < 0)
     247             :                         return GDK_FAIL;
     248           0 :                 ret = BUNappend(b, element, force);
     249           0 :                 GDKfree(element);
     250           0 :                 return ret;
     251             :         }
     252             : }
     253             : 
     254             : // This #define is for converting a numeric numpy array into a string BAT.
     255             : // 'conv' is a function that turns a numeric value of type 'mtpe' to a char*
     256             : // array.
     257             : #define NP_COL_BAT_STR_LOOP(bat, mtpe, fmt)                                    \
     258             :         if (mask == NULL) {                                                        \
     259             :                 for (iu = 0; iu < ret->count; iu++) {                                  \
     260             :                         snprintf(utf8_string, utf8string_minlength, fmt,                   \
     261             :                                          *((mtpe *)&data[(index_offset * ret->count + iu) *        \
     262             :                                                                          ret->memory_size]));                      \
     263             :                         if (convert_and_append(bat, utf8_string, false) != GDK_SUCCEED) {           \
     264             :                                 msg =                                                          \
     265             :                                         createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "BUNappend failed.\n"); \
     266             :                                 goto wrapup;                                                   \
     267             :                         }                                                                  \
     268             :                 }                                                                      \
     269             :         } else {                                                                   \
     270             :                 for (iu = 0; iu < ret->count; iu++) {                                  \
     271             :                         if (mask[index_offset * ret->count + iu] == TRUE) {                \
     272             :                                 bat->tnil = true;                                              \
     273             :                                 if (convert_and_append(bat, str_nil, false) != GDK_SUCCEED) {  \
     274             :                                         msg = createException(MAL, "pyapi3.eval",                  \
     275             :                                                                                   SQLSTATE(PY000) "BUNappend failed.\n");              \
     276             :                                         goto wrapup;                                               \
     277             :                                 }                                                              \
     278             :                         } else {                                                           \
     279             :                                 snprintf(utf8_string, utf8string_minlength, fmt,               \
     280             :                                                  *((mtpe *)&data[(index_offset * ret->count + iu) *    \
     281             :                                                                                  ret->memory_size]));                  \
     282             :                                 if (convert_and_append(bat, utf8_string, false) != GDK_SUCCEED) {       \
     283             :                                         msg = createException(MAL, "pyapi3.eval",                  \
     284             :                                                                                   SQLSTATE(PY000) "BUNappend failed.\n");              \
     285             :                                         goto wrapup;                                               \
     286             :                                 }                                                              \
     287             :                         }                                                                  \
     288             :                 }                                                                      \
     289             :         }
     290             : 
     291             : #define NP_INSERT_BAT(bat, mtpe, index)                                        \
     292             :         {                                                                          \
     293             :                 switch (ret->result_type) {                                            \
     294             :                         case NPY_BOOL:                                                     \
     295             :                                 NP_COL_BAT_LOOP(bat, mtpe, char, index);                       \
     296             :                                 break;                                                         \
     297             :                         case NPY_BYTE:                                                     \
     298             :                                 NP_COL_BAT_LOOP(bat, mtpe, char, index);                       \
     299             :                                 break;                                                         \
     300             :                         case NPY_SHORT:                                                    \
     301             :                                 NP_COL_BAT_LOOP(bat, mtpe, short, index);                      \
     302             :                                 break;                                                         \
     303             :                         case NPY_INT:                                                      \
     304             :                                 NP_COL_BAT_LOOP(bat, mtpe, int, index);                        \
     305             :                                 break;                                                         \
     306             :                         case NPY_LONG:                                                     \
     307             :                                 NP_COL_BAT_LOOP(bat, mtpe, long, index);                       \
     308             :                                 break;                                                         \
     309             :                         case NPY_LONGLONG:                                                 \
     310             :                                 NP_COL_BAT_LOOP(bat, mtpe, long long, index);                  \
     311             :                                 break;                                                         \
     312             :                         case NPY_UBYTE:                                                    \
     313             :                                 NP_COL_BAT_LOOP(bat, mtpe, unsigned char, index);              \
     314             :                                 break;                                                         \
     315             :                         case NPY_USHORT:                                                   \
     316             :                                 NP_COL_BAT_LOOP(bat, mtpe, unsigned short, index);             \
     317             :                                 break;                                                         \
     318             :                         case NPY_UINT:                                                     \
     319             :                                 NP_COL_BAT_LOOP(bat, mtpe, unsigned int, index);               \
     320             :                                 break;                                                         \
     321             :                         case NPY_ULONG:                                                    \
     322             :                                 NP_COL_BAT_LOOP(bat, mtpe, unsigned long, index);              \
     323             :                                 break;                                                         \
     324             :                         case NPY_ULONGLONG:                                                \
     325             :                                 NP_COL_BAT_LOOP(bat, mtpe, unsigned long long, index);         \
     326             :                                 break;                                                         \
     327             :                         case NPY_FLOAT16:                                                  \
     328             :                         case NPY_FLOAT:                                                    \
     329             :                                 NP_COL_BAT_LOOPF(bat, mtpe, float, index);                     \
     330             :                                 break;                                                         \
     331             :                         case NPY_DOUBLE:                                                   \
     332             :                                 NP_COL_BAT_LOOPF(bat, mtpe, double, index);                    \
     333             :                                 break;                                                         \
     334             :                         case NPY_LONGDOUBLE:                                               \
     335             :                                 NP_COL_BAT_LOOPF(bat, mtpe, long double, index);               \
     336             :                                 break;                                                         \
     337             :                         case NPY_STRING:                                                   \
     338             :                                 NP_COL_BAT_LOOP_FUNC(bat, mtpe, str_to_##mtpe, char, index);   \
     339             :                                 break;                                                         \
     340             :                         case NPY_UNICODE:                                                  \
     341             :                                 NP_COL_BAT_LOOP_FUNC(bat, mtpe, unicode_to_##mtpe,             \
     342             :                                                                          wchar_t, index);                       \
     343             :                                 break;                                                         \
     344             :                         case NPY_OBJECT:                                                   \
     345             :                                 NP_COL_BAT_LOOP_FUNC(bat, mtpe, pyobject_to_##mtpe,            \
     346             :                                                                          PyObject *, index);                       \
     347             :                                 break;                                                         \
     348             :                         default:                                                           \
     349             :                                 msg = createException(                                         \
     350             :                                         MAL, "pyapi3.eval",                                        \
     351             :                                         SQLSTATE(PY000) "Unrecognized type. Could not convert to %s.\n",           \
     352             :                                         BatType_Format(TYPE_##mtpe));                              \
     353             :                                 goto wrapup;                                                   \
     354             :                 }                                                                      \
     355             :                 bat->tnonil = !bat->tnil;                                              \
     356             :         }
     357             : 
     358             : #define NP_INSERT_STRING_BAT(b)                                                \
     359             :         switch (ret->result_type) {                                                \
     360             :                 case NPY_BOOL:                                                         \
     361             :                         NP_COL_BAT_STR_LOOP(b, bit, "%hhd");                               \
     362             :                         break;                                                             \
     363             :                 case NPY_BYTE:                                                         \
     364             :                         NP_COL_BAT_STR_LOOP(b, bte, "%hhd");                               \
     365             :                         break;                                                             \
     366             :                 case NPY_SHORT:                                                        \
     367             :                         NP_COL_BAT_STR_LOOP(b, sht, "%hd");                                \
     368             :                         break;                                                             \
     369             :                 case NPY_INT:                                                          \
     370             :                         NP_COL_BAT_STR_LOOP(b, int, "%d");                                 \
     371             :                         break;                                                             \
     372             :                 case NPY_LONG:                                                         \
     373             :                         NP_COL_BAT_STR_LOOP(b, long, "%ld");                               \
     374             :                         break;                                                             \
     375             :                 case NPY_LONGLONG:                                                     \
     376             :                         NP_COL_BAT_STR_LOOP(b, lng, LLFMT);                                \
     377             :                         break;                                                             \
     378             :                 case NPY_UBYTE:                                                        \
     379             :                         NP_COL_BAT_STR_LOOP(b, unsigned char, "%hhu");                     \
     380             :                         break;                                                             \
     381             :                 case NPY_USHORT:                                                       \
     382             :                         NP_COL_BAT_STR_LOOP(b, unsigned short, "%hu");                     \
     383             :                         break;                                                             \
     384             :                 case NPY_UINT:                                                         \
     385             :                         NP_COL_BAT_STR_LOOP(b, unsigned int, "%u");                        \
     386             :                         break;                                                             \
     387             :                 case NPY_ULONG:                                                        \
     388             :                         NP_COL_BAT_STR_LOOP(b, unsigned long, "%lu");                      \
     389             :                         break;                                                             \
     390             :                 case NPY_ULONGLONG:                                                    \
     391             :                         NP_COL_BAT_STR_LOOP(b, ulng, ULLFMT);                              \
     392             :                         break;                                                             \
     393             :                 case NPY_FLOAT16:                                                      \
     394             :                 case NPY_FLOAT:                                                        \
     395             :                         NP_COL_BAT_STR_LOOP(b, flt, "%f");                                 \
     396             :                         break;                                                             \
     397             :                 case NPY_DOUBLE:                                                       \
     398             :                 case NPY_LONGDOUBLE:                                                   \
     399             :                         NP_COL_BAT_STR_LOOP(b, dbl, "%lf");                                \
     400             :                         break;                                                             \
     401             :                 case NPY_STRING:                                                       \
     402             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     403             :                                 if (mask != NULL &&                                            \
     404             :                                         (mask[index_offset * ret->count + iu]) == TRUE) {          \
     405             :                                         b->tnil = true;                                            \
     406             :                                         if (convert_and_append(b, str_nil, false) != GDK_SUCCEED) {         \
     407             :                                                 msg = createException(MAL, "pyapi3.eval",              \
     408             :                                                                                           SQLSTATE(PY000) "BUNappend failed.\n");          \
     409             :                                                 goto wrapup;                                           \
     410             :                                         }                                                          \
     411             :                                 } else {                                                       \
     412             :                                         if (!pyapi3_string_copy(&data[(index_offset * ret->count + iu) *  \
     413             :                                                                                    ret->memory_size],                  \
     414             :                                                                          utf8_string, ret->memory_size, false)) {  \
     415             :                                                 msg = createException(MAL, "pyapi3.eval",              \
     416             :                                                                                           SQLSTATE(PY000) "Invalid string encoding used. " \
     417             :                                                                                           "Please return a regular ASCII " \
     418             :                                                                                           "string, or a Numpy_Unicode "    \
     419             :                                                                                           "object.\n");                    \
     420             :                                                 goto wrapup;                                           \
     421             :                                         }                                                          \
     422             :                                         if (convert_and_append(b, utf8_string, false) != GDK_SUCCEED) {     \
     423             :                                                 msg = createException(MAL, "pyapi3.eval",              \
     424             :                                                                                           SQLSTATE(PY000) "BUNappend failed.\n");          \
     425             :                                                 goto wrapup;                                           \
     426             :                                         }                                                          \
     427             :                                 }                                                              \
     428             :                         }                                                                  \
     429             :                         break;                                                             \
     430             :                 case NPY_UNICODE:                                                      \
     431             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     432             :                                 if (mask != NULL &&                                            \
     433             :                                         (mask[index_offset * ret->count + iu]) == TRUE) {          \
     434             :                                         b->tnil = true;                                            \
     435             :                                         if (convert_and_append(b, str_nil, false) != GDK_SUCCEED) {         \
     436             :                                                 msg = createException(MAL, "pyapi3.eval",              \
     437             :                                                                                           SQLSTATE(PY000) "BUNappend failed.\n");          \
     438             :                                                 goto wrapup;                                           \
     439             :                                         }                                                          \
     440             :                                 } else {                                                       \
     441             :                                         utf32_to_utf8(                                             \
     442             :                                                 0, ret->memory_size / 4, utf8_string,                  \
     443             :                                                 (const wchar_t                                      \
     444             :                                                          *)(&data[(index_offset * ret->count + iu) *       \
     445             :                                                                           ret->memory_size]));                     \
     446             :                                         if (convert_and_append(b, utf8_string, false) != GDK_SUCCEED) {     \
     447             :                                                 msg = createException(MAL, "pyapi3.eval",              \
     448             :                                                                                           SQLSTATE(PY000) "BUNappend failed.\n");          \
     449             :                                                 goto wrapup;                                           \
     450             :                                         }                                                          \
     451             :                                 }                                                              \
     452             :                         }                                                                  \
     453             :                         break;                                                             \
     454             :                 case NPY_OBJECT: {                                                     \
     455             :                         /* The resulting array is an array of pointers to various python   \
     456             :                          * objects */                                                      \
     457             :                         /* Because the python objects can be of any size, we need to       \
     458             :                          * allocate a different size utf8_string for every object */       \
     459             :                         /* we will first loop over all the objects to get the maximum size \
     460             :                          * needed, so we only need to do one allocation */                 \
     461             :                         size_t utf8_size = utf8string_minlength;                           \
     462             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     463             :                                 size_t size = utf8string_minlength;                            \
     464             :                                 PyObject *obj;                                                 \
     465             :                                 if (mask != NULL &&                                            \
     466             :                                         (mask[index_offset * ret->count + iu]) == TRUE)            \
     467             :                                         continue;                                                  \
     468             :                                 obj = *((PyObject **)&data[(index_offset * ret->count + iu) *  \
     469             :                                                                                    ret->memory_size]);                 \
     470             :                                 size = pyobject_get_size(obj);                                 \
     471             :                                 if (size > utf8_size)                                          \
     472             :                                         utf8_size = size;                                          \
     473             :                         }                                                                  \
     474             :                         utf8_string = GDKzalloc(utf8_size);                                \
     475             :                         if (utf8_string == NULL) {                      \
     476             :                                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL); \
     477             :                                 goto wrapup;                            \
     478             :                         }                                               \
     479             :                         for (iu = 0; iu < ret->count; iu++) {                              \
     480             :                                 if (mask != NULL &&                                            \
     481             :                                         (mask[index_offset * ret->count + iu]) == TRUE) {          \
     482             :                                         b->tnil = true;                                            \
     483             :                                         if (convert_and_append(b, str_nil, false) != GDK_SUCCEED) {         \
     484             :                                                 msg = createException(MAL, "pyapi3.eval",              \
     485             :                                                                                           SQLSTATE(PY000) "BUNappend failed.\n");          \
     486             :                                                 goto wrapup;                                           \
     487             :                                         }                                                          \
     488             :                                 } else {                                                       \
     489             :                                         /* we try to handle as many types as possible */           \
     490             :                                         msg = pyobject_to_str(                                                          \
     491             :                                                 ((PyObject **)&data[(index_offset * ret->count + iu) * \
     492             :                                                                                         ret->memory_size]),                \
     493             :                                                 utf8_size, &utf8_string);                              \
     494             :                                         if (msg != MAL_SUCCEED)                                    \
     495             :                                                 goto wrapup;                                           \
     496             :                                         if (convert_and_append(b, utf8_string, false) != GDK_SUCCEED) {     \
     497             :                                                 msg = createException(MAL, "pyapi3.eval",              \
     498             :                                                                                           SQLSTATE(PY000) "BUNappend failed.\n");          \
     499             :                                                 goto wrapup;                                           \
     500             :                                         }                                                          \
     501             :                                 }                                                              \
     502             :                         }                                                                  \
     503             :                         break;                                                             \
     504             :                 }                                                                      \
     505             :                 default:                                                               \
     506             :                         msg = createException(                                             \
     507             :                                 MAL, "pyapi3.eval",                                            \
     508             :                                 SQLSTATE(PY000) "Unrecognized type. Could not convert to NPY_UNICODE.\n");     \
     509             :                         goto wrapup;                                                       \
     510             :         }                                                                          \
     511             :         b->tnonil = !b->tnil;
     512             : 
     513             : #ifdef HAVE_HGE
     514             : #define NOT_HGE(mtpe) TYPE_##mtpe != TYPE_hge
     515             : #else
     516             : #define NOT_HGE(mtpe) true
     517             : #endif
     518             : 
     519             : #define NP_CREATE_EMPTY_BAT(bat, mtpe)                                     \
     520             :         {                                                                      \
     521             :                 bat = COLnew(seqbase, TYPE_##mtpe, (BUN)ret->count, TRANSIENT);    \
     522             :                 if (bat == NULL) {                                                 \
     523             :                         msg = createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "Cannot create column"); \
     524             :                         goto wrapup;                                                   \
     525             :                 }                                                                  \
     526             :                 bat->tkey = false;                                                 \
     527             :                 bat->tsorted = false;                                              \
     528             :                 bat->trevsorted = false;                                           \
     529             :                 bat->tnil = false;                                                 \
     530             :                 bat->tnonil = true;                                                \
     531             :                 BATsetcount(bat, (BUN)ret->count);                                 \
     532             :         }
     533             : 
     534             : // This very big #define combines all the previous #defines for one big #define
     535             : // that is responsible for converting a Numpy array (described in the PyReturn
     536             : // object 'ret')
     537             : // to a BAT of type 'mtpe'. This should only be used for numeric BATs (but can
     538             : // be used for any Numpy Array). The resulting BAT will be stored in 'bat'.
     539             : #define NP_CREATE_BAT(bat, mtpe)                                               \
     540             :         {                                                                          \
     541             :                 bool *mask = NULL;                                                     \
     542             :                 char *data = NULL;                                                     \
     543             :                 if (ret->mask_data != NULL) {                                          \
     544             :                         mask = (bool *)ret->mask_data;                                     \
     545             :                 }                                                                      \
     546             :                 if (ret->array_data == NULL) {                                         \
     547             :                         msg = createException(MAL, "pyapi3.eval",                          \
     548             :                                 SQLSTATE(PY000) "No return value stored in the structure.\n"); \
     549             :                         goto wrapup;                                                       \
     550             :                 }                                                                      \
     551             :                 data = (char *)ret->array_data;                                        \
     552             :                 if (!copy && ret->count > 0 &&                                         \
     553             :                         TYPE_##mtpe == PyType_ToBat(ret->result_type) &&                   \
     554             :                         (ret->count * ret->memory_size < BUN_MAX) &&                       \
     555             :                         (ret->numpy_array == NULL ||                                       \
     556             :                          PyArray_FLAGS((PyArrayObject *)ret->numpy_array) &                \
     557             :                                  NPY_ARRAY_OWNDATA)) {                                         \
     558             :                         /*We can only create a direct map if the numpy array type and      \
     559             :                          * target BAT type*/                                               \
     560             :                         /*are identical, otherwise we have to do a conversion.*/           \
     561             :                         if (ret->numpy_array == NULL) {                                    \
     562             :                                 CREATE_BAT_ZEROCOPY(bat, mtpe, STORE_MMAPABS);                 \
     563             :                                 ret->array_data = NULL;                                        \
     564             :                         } else {                                                           \
     565             :                                 CREATE_BAT_ZEROCOPY(bat, mtpe, STORE_CMEM);                    \
     566             :                         }                                                                  \
     567             :                 } else {                                                               \
     568             :                         bat = COLnew(seqbase, TYPE_##mtpe, (BUN)ret->count, TRANSIENT);    \
     569             :                         if (bat == NULL) {                                                 \
     570             :                                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "Cannot create column"); \
     571             :                                 goto wrapup;                                                   \
     572             :                         }                                                                  \
     573             :                         bat->tkey = false;                                                 \
     574             :                         bat->tsorted = false;                                              \
     575             :                         bat->trevsorted = false;                                           \
     576             :                         NP_INSERT_BAT(bat, mtpe, 0);                                       \
     577             :                         if (!mask) {                                                       \
     578             :                                 bat->tnil = false;                                             \
     579             :                                 bat->tnonil = false;                                           \
     580             :                         }                                                                  \
     581             :                         BATsetcount(bat, (BUN)ret->count);                                 \
     582             :                 }                                                                      \
     583             :         }

Generated by: LCOV version 1.14