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 : Py_UNICODE, 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 Py_UNICODE \
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 : }
|