LCOV - code coverage report
Current view: top level - sql/backends/monet5/UDF/pyapi3 - type_conversion3.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 35 87 40.2 %
Date: 2021-10-13 02:24:04 Functions: 7 34 20.6 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : #include "monetdb_config.h"
      10             : #include "type_conversion.h"
      11             : #include "unicode.h"
      12             : 
      13             : #include <longintrepr.h>
      14             : 
      15             : #define PyInt_Check PyLong_Check
      16             : #define PyString_CheckExact PyUnicode_CheckExact
      17             : 
      18          15 : bool pyapi3_string_copy(const char *source, char *dest, size_t max_size, bool allow_unicode)
      19             : {
      20             :         size_t i;
      21         152 :         for (i = 0; i < max_size; i++) {
      22         152 :                 dest[i] = source[i];
      23         152 :                 if (dest[i] == 0)
      24             :                         return TRUE;
      25         137 :                 if (!allow_unicode && source[i] & 0x80)
      26             :                         return FALSE;
      27             :         }
      28           0 :         dest[max_size] = '\0';
      29           0 :         return TRUE;
      30             : }
      31             : 
      32             : #ifdef HAVE_HGE
      33           2 : int hge_to_string(char *str, hge x)
      34             : {
      35           2 :         size_t len = 256; /* assume str is large enough */
      36           2 :         hgeToStr(&str, &len, &x, false);
      37           2 :         return TRUE;
      38             : }
      39             : 
      40           0 : PyObject *PyLong_FromHge(hge h)
      41             : {
      42             :         PyLongObject *z;
      43             :         size_t size = 0;
      44           0 :         hge shift = h >= 0 ? h : -h;
      45             :         hge prev = shift;
      46             :         int i;
      47           0 :         while (shift > 0) {
      48           0 :                 size++;
      49           0 :                 shift = shift >> PyLong_SHIFT;
      50             :         }
      51           0 :         z = _PyLong_New(size);
      52           0 :         for (i = size - 1; i >= 0; i--) {
      53           0 :                 digit result = (digit)(prev >> (PyLong_SHIFT * i));
      54           0 :                 prev = prev - ((prev >> (PyLong_SHIFT * i)) << (PyLong_SHIFT * i));
      55           0 :                 z->ob_digit[i] = result;
      56             :         }
      57           0 :         if (h < 0)
      58           0 :                 Py_SIZE(z) = -(Py_SIZE(z));
      59           0 :         return (PyObject *)z;
      60             : }
      61             : #endif
      62             : 
      63          17 : size_t pyobject_get_size(PyObject *obj)
      64             : {
      65             :         size_t size = 256;
      66             : 
      67          17 :         if (PyByteArray_CheckExact(obj)) {
      68           0 :                 size = Py_SIZE(obj); // Normal strings are 1 byte per character
      69          17 :         } else if (PyUnicode_CheckExact(obj)) {
      70          15 :                 size = Py_SIZE(obj) * 4; // UTF32 is 4 bytes per character
      71             :         }
      72          17 :         return size;
      73             : }
      74             : 
      75             : 
      76           0 : str pyobject_to_blob(PyObject **ptr, size_t maxsize, blob **value) {
      77             :         size_t size;
      78             :         char* bytes_data;
      79             :         PyObject *obj;
      80             :         str msg = MAL_SUCCEED;
      81           0 :         if (ptr == NULL || *ptr == NULL) {
      82           0 :                 msg = createException(MAL, "pyapi3.eval", "Invalid PyObject.");
      83           0 :                 goto wrapup;
      84             :         }
      85             :         obj = *ptr;
      86             : 
      87             :         (void)maxsize;
      88           0 :         if (PyByteArray_CheckExact(obj)) {
      89           0 :                 size = PyByteArray_Size(obj);
      90           0 :                 bytes_data = ((PyByteArrayObject *)obj)->ob_bytes;
      91             :         } else {
      92           0 :                 msg = createException(
      93             :                         MAL, "pyapi3.eval",
      94             :                         "Unrecognized Python object. Could not convert to blob.\n");
      95           0 :                 goto wrapup;
      96             :         }
      97             : 
      98           0 :         *value = GDKmalloc(sizeof(blob) + size + 1);
      99           0 :         if (!*value) {
     100           0 :                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(HY013) MAL_MALLOC_FAIL);
     101           0 :                 goto wrapup;
     102             :         }
     103           0 :         (*value)->nitems = size;
     104           0 :         memcpy((*value)->data, bytes_data, size);
     105           0 : wrapup:
     106           0 :         return msg;
     107             : }
     108             : 
     109          17 : str pyobject_to_str(PyObject **ptr, size_t maxsize, str *value)
     110             : {
     111             :         PyObject *obj;
     112             :         str msg = MAL_SUCCEED;
     113             :         str utf8_string = NULL;
     114             :         size_t len = 0;
     115             : 
     116          17 :         if (ptr == NULL || *ptr == NULL) {
     117           0 :                 msg = createException(MAL, "pyapi3.eval", SQLSTATE(PY000) "Invalid PyObject.");
     118           0 :                 goto wrapup;
     119             :         }
     120          17 :         obj = *ptr;
     121             : 
     122          17 :         utf8_string = *value;
     123          17 :         if (!utf8_string) {
     124           7 :                 utf8_string = (str)malloc(len = (pyobject_get_size(obj) + 1));
     125           7 :                 if (!utf8_string) {
     126           0 :                         msg = createException(MAL, "pyapi3.eval",
     127             :                                                                   SQLSTATE(HY013) MAL_MALLOC_FAIL "python string");
     128           0 :                         goto wrapup;
     129             :                 }
     130           7 :                 *value = utf8_string;
     131             :         } else {
     132             :                 len = maxsize;
     133             :         }
     134             : 
     135          17 :         if (PyByteArray_CheckExact(obj)) {
     136           0 :                 char *str = ((PyByteArrayObject *)obj)->ob_bytes;
     137           0 :                 if (!pyapi3_string_copy(str, utf8_string, len-1, false)) {
     138           0 :                         msg = createException(MAL, "pyapi3.eval",
     139             :                                   SQLSTATE(PY000) "Invalid string encoding used. Please return "
     140             :                                                   "a regular ASCII string, or a Numpy_Unicode "
     141             :                                                   "object.\n");
     142           0 :                         goto wrapup;
     143             :                 }
     144          17 :         } else if (PyUnicode_CheckExact(obj)) {
     145          15 :                 const char *str = PyUnicode_AsUTF8(obj);
     146          15 :                 if (!pyapi3_string_copy(str, utf8_string, len-1, true)) {
     147           0 :                         msg = createException(MAL, "pyapi3.eval",
     148             :                                   SQLSTATE(PY000) "Invalid string encoding used. Please return "
     149             :                                                   "a regular ASCII string, or a Numpy_Unicode "
     150             :                                                   "object.\n");
     151           0 :                         goto wrapup;
     152             :                 }
     153           2 :         } else if (PyBool_Check(obj) || PyLong_Check(obj) || PyInt_Check(obj) ||
     154           2 :                            PyFloat_Check(obj)) {
     155             : #ifdef HAVE_HGE
     156             :                 hge h;
     157           2 :                 pyobject_to_hge(&obj, 0, &h);
     158           2 :                 hge_to_string(utf8_string, h);
     159             : #else
     160             :                 lng h;
     161             :                 pyobject_to_lng(&obj, 0, &h);
     162             :                 snprintf(utf8_string, utf8string_minlength, LLFMT, h);
     163             : #endif
     164             :         } else {
     165           0 :                 msg = createException(
     166             :                         MAL, "pyapi3.eval",
     167             :                         SQLSTATE(PY000) "Unrecognized Python object. Could not convert to NPY_UNICODE.\n");
     168           0 :                 goto wrapup;
     169             :         }
     170          17 : wrapup:
     171          17 :         return msg;
     172             : }
     173             : 
     174             : #define STRING_TO_NUMBER_FACTORY(tpe)                                          \
     175             :         str str_to_##tpe(const char *ptr, size_t maxsize, tpe *value)              \
     176             :         {                                                                          \
     177             :                 size_t len = sizeof(tpe);                                              \
     178             :                 char buf[256];                                                         \
     179             :                 if (maxsize > 0) {                                                     \
     180             :                         if (maxsize >= sizeof(buf))                                        \
     181             :                                 maxsize = sizeof(buf) - 1;                                     \
     182             :                         strncpy(buf, ptr, maxsize);                                        \
     183             :                         buf[maxsize] = 0;                                                  \
     184             :                         if (strlen(buf) >= sizeof(buf) - 1)                                \
     185             :                                 return GDKstrdup("string too long to convert.");               \
     186             :                         ptr = buf;                                                         \
     187             :                 }                                                                      \
     188             :                 if (BATatoms[TYPE_##tpe].atomFromStr(ptr, &len, (void **)&value, false) < 0) \
     189             :                         return GDKstrdup("Error converting string.");                      \
     190             :                 return MAL_SUCCEED;                                                    \
     191             :         }
     192             : 
     193             : #define PY_TO_(type, inttpe)                                            \
     194             : str pyobject_to_##type(PyObject **pyobj, size_t maxsize, type *value)   \
     195             : {                                                                       \
     196             :         PyObject *ptr = *pyobj;                                         \
     197             :         str retval = MAL_SUCCEED;                                               \
     198             :         (void) maxsize;                                                 \
     199             :         if (PyLong_CheckExact(ptr)) {                                   \
     200             :                 PyLongObject *p = (PyLongObject*) ptr;                          \
     201             :                 inttpe h = 0;                                                   \
     202             :                 inttpe prev = 0;                                                \
     203             :                 Py_ssize_t i = Py_SIZE(p);                                              \
     204             :                 int sign = i < 0 ? -1 : 1;                                   \
     205             :                 i *= sign;                                                      \
     206             :                 while (--i >= 0) {                                           \
     207             :                         prev = h; (void)prev;                                   \
     208             :                         h = (h << PyLong_SHIFT) + p->ob_digit[i];                      \
     209             :                         if ((h >> PyLong_SHIFT) != prev) {                                \
     210             :                                 return GDKstrdup("Overflow when converting value.");  \
     211             :                         }                                                               \
     212             :                 }                                                               \
     213             :                 *value = (type)(h * sign);                                      \
     214             :         } else if (PyBool_Check(ptr)) {                                 \
     215             :                 *value = ptr == Py_True ? (type) 1 : (type) 0;                  \
     216             :         } else if (PyFloat_CheckExact(ptr)) {                           \
     217             :                 *value = isnan(((PyFloatObject*)ptr)->ob_fval) ? type##_nil : (type) ((PyFloatObject*)ptr)->ob_fval; \
     218             :         } else if (PyUnicode_CheckExact(ptr)) {                         \
     219             :                 return str_to_##type(PyUnicode_AsUTF8(ptr), 0, value);          \
     220             :         }  else if (PyByteArray_CheckExact(ptr)) {                              \
     221             :                 return str_to_##type(((PyByteArrayObject*)ptr)->ob_bytes, 0, value); \
     222             :         }  else if (ptr == Py_None) {                                   \
     223             :                 *value = type##_nil;                                            \
     224             :         }                                                                       \
     225             :         return retval;                                                  \
     226             : }
     227             : 
     228             : #define CONVERSION_FUNCTION_FACTORY(tpe, inttpe)            \
     229             :         STRING_TO_NUMBER_FACTORY(tpe)                   \
     230             :         str unicode_to_##tpe(Py_UNICODE *ptr, size_t maxsize, tpe *value)   \
     231             :         {                                   \
     232             :                 char utf8[1024];                        \
     233             :         if (maxsize == 0)                       \
     234             :                         maxsize = utf32_strlen(ptr);                \
     235             :         if (maxsize > 255)                      \
     236             :                         maxsize = 255;                      \
     237             :                 unicode_to_utf8(0, maxsize, utf8, ptr);             \
     238             :                 return str_to_##tpe(utf8, 0, value);                \
     239             :         }                                   \
     240             :         PY_TO_(tpe, inttpe);
     241             : 
     242           0 : CONVERSION_FUNCTION_FACTORY(bte, bte)
     243           0 : CONVERSION_FUNCTION_FACTORY(oid, oid)
     244           0 : CONVERSION_FUNCTION_FACTORY(bit, bit)
     245           0 : CONVERSION_FUNCTION_FACTORY(sht, sht)
     246         368 : CONVERSION_FUNCTION_FACTORY(int, int)
     247           0 : CONVERSION_FUNCTION_FACTORY(lng, lng)
     248           0 : CONVERSION_FUNCTION_FACTORY(flt, lng)
     249             : 
     250             : #ifdef HAVE_HGE
     251           4 : CONVERSION_FUNCTION_FACTORY(hge, hge)
     252          12 : CONVERSION_FUNCTION_FACTORY(dbl, hge)
     253             : #else
     254             : CONVERSION_FUNCTION_FACTORY(dbl, lng)
     255             : #endif
     256             : 
     257           0 : void _typeconversion_init(void) { _import_array(); }

Generated by: LCOV version 1.14