1 /*
2 processors.c
3 Copyright (C) 2010-2021 the SQLAlchemy authors and contributors <see AUTHORS file>
4 Copyright (C) 2010-2011 Gaetan de Menten gdementen@gmail.com
5 
6 This module is part of SQLAlchemy and is released under
7 the MIT License: https://www.opensource.org/licenses/mit-license.php
8 */
9 
10 #include <Python.h>
11 #include <datetime.h>
12 
13 #define MODULE_NAME "cprocessors"
14 #define MODULE_DOC "Module containing C versions of data processing functions."
15 
16 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
17 typedef int Py_ssize_t;
18 #define PY_SSIZE_T_MAX INT_MAX
19 #define PY_SSIZE_T_MIN INT_MIN
20 #endif
21 
22 static PyObject *
int_to_boolean(PyObject * self,PyObject * arg)23 int_to_boolean(PyObject *self, PyObject *arg)
24 {
25     int l = 0;
26     PyObject *res;
27 
28     if (arg == Py_None)
29         Py_RETURN_NONE;
30 
31     l = PyObject_IsTrue(arg);
32     if (l == 0) {
33         res = Py_False;
34     } else if (l == 1) {
35         res = Py_True;
36     } else {
37         return NULL;
38     }
39 
40     Py_INCREF(res);
41     return res;
42 }
43 
44 static PyObject *
to_str(PyObject * self,PyObject * arg)45 to_str(PyObject *self, PyObject *arg)
46 {
47     if (arg == Py_None)
48         Py_RETURN_NONE;
49 
50     return PyObject_Str(arg);
51 }
52 
53 static PyObject *
to_float(PyObject * self,PyObject * arg)54 to_float(PyObject *self, PyObject *arg)
55 {
56     if (arg == Py_None)
57         Py_RETURN_NONE;
58 
59     return PyNumber_Float(arg);
60 }
61 
62 static PyObject *
str_to_datetime(PyObject * self,PyObject * arg)63 str_to_datetime(PyObject *self, PyObject *arg)
64 {
65 #if PY_MAJOR_VERSION >= 3
66     PyObject *bytes;
67     PyObject *err_bytes;
68 #endif
69     const char *str;
70     int numparsed;
71     unsigned int year, month, day, hour, minute, second, microsecond = 0;
72     PyObject *err_repr;
73 
74     if (arg == Py_None)
75         Py_RETURN_NONE;
76 
77 #if PY_MAJOR_VERSION >= 3
78     bytes = PyUnicode_AsASCIIString(arg);
79     if (bytes == NULL)
80         str = NULL;
81     else
82         str = PyBytes_AS_STRING(bytes);
83 #else
84     str = PyString_AsString(arg);
85 #endif
86     if (str == NULL) {
87         err_repr = PyObject_Repr(arg);
88         if (err_repr == NULL)
89             return NULL;
90 #if PY_MAJOR_VERSION >= 3
91         err_bytes = PyUnicode_AsASCIIString(err_repr);
92         if (err_bytes == NULL)
93             return NULL;
94         PyErr_Format(
95                 PyExc_ValueError,
96                 "Couldn't parse datetime string '%.200s' "
97                 "- value is not a string.",
98                 PyBytes_AS_STRING(err_bytes));
99         Py_DECREF(err_bytes);
100 #else
101         PyErr_Format(
102                 PyExc_ValueError,
103                 "Couldn't parse datetime string '%.200s' "
104                 "- value is not a string.",
105                 PyString_AsString(err_repr));
106 #endif
107         Py_DECREF(err_repr);
108         return NULL;
109     }
110 
111     /* microseconds are optional */
112     /*
113     TODO: this is slightly less picky than the Python version which would
114     not accept "2000-01-01 00:00:00.". I don't know which is better, but they
115     should be coherent.
116     */
117     numparsed = sscanf(str, "%4u-%2u-%2u %2u:%2u:%2u.%6u", &year, &month, &day,
118                        &hour, &minute, &second, &microsecond);
119 #if PY_MAJOR_VERSION >= 3
120     Py_DECREF(bytes);
121 #endif
122     if (numparsed < 6) {
123         err_repr = PyObject_Repr(arg);
124         if (err_repr == NULL)
125             return NULL;
126 #if PY_MAJOR_VERSION >= 3
127         err_bytes = PyUnicode_AsASCIIString(err_repr);
128         if (err_bytes == NULL)
129             return NULL;
130         PyErr_Format(
131                 PyExc_ValueError,
132                 "Couldn't parse datetime string: %.200s",
133                 PyBytes_AS_STRING(err_bytes));
134         Py_DECREF(err_bytes);
135 #else
136         PyErr_Format(
137                 PyExc_ValueError,
138                 "Couldn't parse datetime string: %.200s",
139                 PyString_AsString(err_repr));
140 #endif
141         Py_DECREF(err_repr);
142         return NULL;
143     }
144     return PyDateTime_FromDateAndTime(year, month, day,
145                                       hour, minute, second, microsecond);
146 }
147 
148 static PyObject *
str_to_time(PyObject * self,PyObject * arg)149 str_to_time(PyObject *self, PyObject *arg)
150 {
151 #if PY_MAJOR_VERSION >= 3
152     PyObject *bytes;
153     PyObject *err_bytes;
154 #endif
155     const char *str;
156     int numparsed;
157     unsigned int hour, minute, second, microsecond = 0;
158     PyObject *err_repr;
159 
160     if (arg == Py_None)
161         Py_RETURN_NONE;
162 
163 #if PY_MAJOR_VERSION >= 3
164     bytes = PyUnicode_AsASCIIString(arg);
165     if (bytes == NULL)
166         str = NULL;
167     else
168         str = PyBytes_AS_STRING(bytes);
169 #else
170     str = PyString_AsString(arg);
171 #endif
172     if (str == NULL) {
173         err_repr = PyObject_Repr(arg);
174         if (err_repr == NULL)
175             return NULL;
176 
177 #if PY_MAJOR_VERSION >= 3
178         err_bytes = PyUnicode_AsASCIIString(err_repr);
179         if (err_bytes == NULL)
180             return NULL;
181         PyErr_Format(
182                 PyExc_ValueError,
183                 "Couldn't parse time string '%.200s' - value is not a string.",
184                 PyBytes_AS_STRING(err_bytes));
185         Py_DECREF(err_bytes);
186 #else
187         PyErr_Format(
188                 PyExc_ValueError,
189                 "Couldn't parse time string '%.200s' - value is not a string.",
190                 PyString_AsString(err_repr));
191 #endif
192         Py_DECREF(err_repr);
193         return NULL;
194     }
195 
196     /* microseconds are optional */
197     /*
198     TODO: this is slightly less picky than the Python version which would
199     not accept "00:00:00.". I don't know which is better, but they should be
200     coherent.
201     */
202     numparsed = sscanf(str, "%2u:%2u:%2u.%6u", &hour, &minute, &second,
203                        &microsecond);
204 #if PY_MAJOR_VERSION >= 3
205     Py_DECREF(bytes);
206 #endif
207     if (numparsed < 3) {
208         err_repr = PyObject_Repr(arg);
209         if (err_repr == NULL)
210             return NULL;
211 #if PY_MAJOR_VERSION >= 3
212         err_bytes = PyUnicode_AsASCIIString(err_repr);
213         if (err_bytes == NULL)
214             return NULL;
215         PyErr_Format(
216                 PyExc_ValueError,
217                 "Couldn't parse time string: %.200s",
218                 PyBytes_AS_STRING(err_bytes));
219         Py_DECREF(err_bytes);
220 #else
221         PyErr_Format(
222                 PyExc_ValueError,
223                 "Couldn't parse time string: %.200s",
224                 PyString_AsString(err_repr));
225 #endif
226         Py_DECREF(err_repr);
227         return NULL;
228     }
229     return PyTime_FromTime(hour, minute, second, microsecond);
230 }
231 
232 static PyObject *
str_to_date(PyObject * self,PyObject * arg)233 str_to_date(PyObject *self, PyObject *arg)
234 {
235 #if PY_MAJOR_VERSION >= 3
236     PyObject *bytes;
237     PyObject *err_bytes;
238 #endif
239     const char *str;
240     int numparsed;
241     unsigned int year, month, day;
242     PyObject *err_repr;
243 
244     if (arg == Py_None)
245         Py_RETURN_NONE;
246 
247 #if PY_MAJOR_VERSION >= 3
248     bytes = PyUnicode_AsASCIIString(arg);
249     if (bytes == NULL)
250         str = NULL;
251     else
252         str = PyBytes_AS_STRING(bytes);
253 #else
254     str = PyString_AsString(arg);
255 #endif
256     if (str == NULL) {
257         err_repr = PyObject_Repr(arg);
258         if (err_repr == NULL)
259             return NULL;
260 #if PY_MAJOR_VERSION >= 3
261         err_bytes = PyUnicode_AsASCIIString(err_repr);
262         if (err_bytes == NULL)
263             return NULL;
264         PyErr_Format(
265                 PyExc_ValueError,
266                 "Couldn't parse date string '%.200s' - value is not a string.",
267                 PyBytes_AS_STRING(err_bytes));
268         Py_DECREF(err_bytes);
269 #else
270         PyErr_Format(
271                 PyExc_ValueError,
272                 "Couldn't parse date string '%.200s' - value is not a string.",
273                 PyString_AsString(err_repr));
274 #endif
275         Py_DECREF(err_repr);
276         return NULL;
277     }
278 
279     numparsed = sscanf(str, "%4u-%2u-%2u", &year, &month, &day);
280 #if PY_MAJOR_VERSION >= 3
281     Py_DECREF(bytes);
282 #endif
283     if (numparsed != 3) {
284         err_repr = PyObject_Repr(arg);
285         if (err_repr == NULL)
286             return NULL;
287 #if PY_MAJOR_VERSION >= 3
288         err_bytes = PyUnicode_AsASCIIString(err_repr);
289         if (err_bytes == NULL)
290             return NULL;
291         PyErr_Format(
292                 PyExc_ValueError,
293                 "Couldn't parse date string: %.200s",
294                 PyBytes_AS_STRING(err_bytes));
295         Py_DECREF(err_bytes);
296 #else
297         PyErr_Format(
298                 PyExc_ValueError,
299                 "Couldn't parse date string: %.200s",
300                 PyString_AsString(err_repr));
301 #endif
302         Py_DECREF(err_repr);
303         return NULL;
304     }
305     return PyDate_FromDate(year, month, day);
306 }
307 
308 
309 /***********
310  * Structs *
311  ***********/
312 
313 typedef struct {
314     PyObject_HEAD
315     PyObject *encoding;
316     PyObject *errors;
317 } UnicodeResultProcessor;
318 
319 typedef struct {
320     PyObject_HEAD
321     PyObject *type;
322     PyObject *format;
323 } DecimalResultProcessor;
324 
325 
326 
327 /**************************
328  * UnicodeResultProcessor *
329  **************************/
330 
331 static int
UnicodeResultProcessor_init(UnicodeResultProcessor * self,PyObject * args,PyObject * kwds)332 UnicodeResultProcessor_init(UnicodeResultProcessor *self, PyObject *args,
333                             PyObject *kwds)
334 {
335     PyObject *encoding, *errors = NULL;
336     static char *kwlist[] = {"encoding", "errors", NULL};
337 
338 #if PY_MAJOR_VERSION >= 3
339     if (!PyArg_ParseTupleAndKeywords(args, kwds, "U|U:__init__", kwlist,
340                                      &encoding, &errors))
341         return -1;
342 #else
343     if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|S:__init__", kwlist,
344                                      &encoding, &errors))
345         return -1;
346 #endif
347 
348 #if PY_MAJOR_VERSION >= 3
349     encoding = PyUnicode_AsASCIIString(encoding);
350 #else
351     Py_INCREF(encoding);
352 #endif
353     self->encoding = encoding;
354 
355     if (errors) {
356 #if PY_MAJOR_VERSION >= 3
357         errors = PyUnicode_AsASCIIString(errors);
358 #else
359         Py_INCREF(errors);
360 #endif
361     } else {
362 #if PY_MAJOR_VERSION >= 3
363         errors = PyBytes_FromString("strict");
364 #else
365         errors = PyString_FromString("strict");
366 #endif
367         if (errors == NULL)
368             return -1;
369     }
370     self->errors = errors;
371 
372     return 0;
373 }
374 
375 static PyObject *
UnicodeResultProcessor_process(UnicodeResultProcessor * self,PyObject * value)376 UnicodeResultProcessor_process(UnicodeResultProcessor *self, PyObject *value)
377 {
378     const char *encoding, *errors;
379     char *str;
380     Py_ssize_t len;
381 
382     if (value == Py_None)
383         Py_RETURN_NONE;
384 
385 #if PY_MAJOR_VERSION >= 3
386     if (PyBytes_AsStringAndSize(value, &str, &len))
387         return NULL;
388 
389     encoding = PyBytes_AS_STRING(self->encoding);
390     errors = PyBytes_AS_STRING(self->errors);
391 #else
392     if (PyString_AsStringAndSize(value, &str, &len))
393         return NULL;
394 
395     encoding = PyString_AS_STRING(self->encoding);
396     errors = PyString_AS_STRING(self->errors);
397 #endif
398 
399     return PyUnicode_Decode(str, len, encoding, errors);
400 }
401 
402 static PyObject *
UnicodeResultProcessor_conditional_process(UnicodeResultProcessor * self,PyObject * value)403 UnicodeResultProcessor_conditional_process(UnicodeResultProcessor *self, PyObject *value)
404 {
405     const char *encoding, *errors;
406     char *str;
407     Py_ssize_t len;
408 
409     if (value == Py_None)
410         Py_RETURN_NONE;
411 
412 #if PY_MAJOR_VERSION >= 3
413     if (PyUnicode_Check(value) == 1) {
414         Py_INCREF(value);
415         return value;
416     }
417 
418     if (PyBytes_AsStringAndSize(value, &str, &len))
419         return NULL;
420 
421     encoding = PyBytes_AS_STRING(self->encoding);
422     errors = PyBytes_AS_STRING(self->errors);
423 #else
424 
425     if (PyUnicode_Check(value) == 1) {
426         Py_INCREF(value);
427         return value;
428     }
429 
430     if (PyString_AsStringAndSize(value, &str, &len))
431         return NULL;
432 
433 
434     encoding = PyString_AS_STRING(self->encoding);
435     errors = PyString_AS_STRING(self->errors);
436 #endif
437 
438     return PyUnicode_Decode(str, len, encoding, errors);
439 }
440 
441 static void
UnicodeResultProcessor_dealloc(UnicodeResultProcessor * self)442 UnicodeResultProcessor_dealloc(UnicodeResultProcessor *self)
443 {
444     Py_XDECREF(self->encoding);
445     Py_XDECREF(self->errors);
446 #if PY_MAJOR_VERSION >= 3
447     Py_TYPE(self)->tp_free((PyObject*)self);
448 #else
449     self->ob_type->tp_free((PyObject*)self);
450 #endif
451 }
452 
453 static PyMethodDef UnicodeResultProcessor_methods[] = {
454     {"process", (PyCFunction)UnicodeResultProcessor_process, METH_O,
455      "The value processor itself."},
456     {"conditional_process", (PyCFunction)UnicodeResultProcessor_conditional_process, METH_O,
457      "Conditional version of the value processor."},
458     {NULL}  /* Sentinel */
459 };
460 
461 static PyTypeObject UnicodeResultProcessorType = {
462     PyVarObject_HEAD_INIT(NULL, 0)
463     "sqlalchemy.cprocessors.UnicodeResultProcessor",        /* tp_name */
464     sizeof(UnicodeResultProcessor),             /* tp_basicsize */
465     0,                                          /* tp_itemsize */
466     (destructor)UnicodeResultProcessor_dealloc, /* tp_dealloc */
467     0,                                          /* tp_print */
468     0,                                          /* tp_getattr */
469     0,                                          /* tp_setattr */
470     0,                                          /* tp_compare */
471     0,                                          /* tp_repr */
472     0,                                          /* tp_as_number */
473     0,                                          /* tp_as_sequence */
474     0,                                          /* tp_as_mapping */
475     0,                                          /* tp_hash  */
476     0,                                          /* tp_call */
477     0,                                          /* tp_str */
478     0,                                          /* tp_getattro */
479     0,                                          /* tp_setattro */
480     0,                                          /* tp_as_buffer */
481     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
482     "UnicodeResultProcessor objects",           /* tp_doc */
483     0,                                          /* tp_traverse */
484     0,                                          /* tp_clear */
485     0,                                          /* tp_richcompare */
486     0,                                          /* tp_weaklistoffset */
487     0,                                          /* tp_iter */
488     0,                                          /* tp_iternext */
489     UnicodeResultProcessor_methods,             /* tp_methods */
490     0,                                          /* tp_members */
491     0,                                          /* tp_getset */
492     0,                                          /* tp_base */
493     0,                                          /* tp_dict */
494     0,                                          /* tp_descr_get */
495     0,                                          /* tp_descr_set */
496     0,                                          /* tp_dictoffset */
497     (initproc)UnicodeResultProcessor_init,      /* tp_init */
498     0,                                          /* tp_alloc */
499     0,                                          /* tp_new */
500 };
501 
502 /**************************
503  * DecimalResultProcessor *
504  **************************/
505 
506 static int
DecimalResultProcessor_init(DecimalResultProcessor * self,PyObject * args,PyObject * kwds)507 DecimalResultProcessor_init(DecimalResultProcessor *self, PyObject *args,
508                             PyObject *kwds)
509 {
510     PyObject *type, *format;
511 
512 #if PY_MAJOR_VERSION >= 3
513     if (!PyArg_ParseTuple(args, "OU", &type, &format))
514 #else
515     if (!PyArg_ParseTuple(args, "OS", &type, &format))
516 #endif
517         return -1;
518 
519     Py_INCREF(type);
520     self->type = type;
521 
522     Py_INCREF(format);
523     self->format = format;
524 
525     return 0;
526 }
527 
528 static PyObject *
DecimalResultProcessor_process(DecimalResultProcessor * self,PyObject * value)529 DecimalResultProcessor_process(DecimalResultProcessor *self, PyObject *value)
530 {
531     PyObject *str, *result, *args;
532 
533     if (value == Py_None)
534         Py_RETURN_NONE;
535 
536     /* Decimal does not accept float values directly */
537     /* SQLite can also give us an integer here (see [ticket:2432]) */
538     /* XXX: starting with Python 3.1, we could use Decimal.from_float(f),
539                  but the result wouldn't be the same */
540 
541     args = PyTuple_Pack(1, value);
542     if (args == NULL)
543         return NULL;
544 
545 #if PY_MAJOR_VERSION >= 3
546     str = PyUnicode_Format(self->format, args);
547 #else
548     str = PyString_Format(self->format, args);
549 #endif
550 
551     Py_DECREF(args);
552     if (str == NULL)
553         return NULL;
554 
555     result = PyObject_CallFunctionObjArgs(self->type, str, NULL);
556     Py_DECREF(str);
557     return result;
558 }
559 
560 static void
DecimalResultProcessor_dealloc(DecimalResultProcessor * self)561 DecimalResultProcessor_dealloc(DecimalResultProcessor *self)
562 {
563     Py_XDECREF(self->type);
564     Py_XDECREF(self->format);
565 #if PY_MAJOR_VERSION >= 3
566     Py_TYPE(self)->tp_free((PyObject*)self);
567 #else
568     self->ob_type->tp_free((PyObject*)self);
569 #endif
570 }
571 
572 static PyMethodDef DecimalResultProcessor_methods[] = {
573     {"process", (PyCFunction)DecimalResultProcessor_process, METH_O,
574      "The value processor itself."},
575     {NULL}  /* Sentinel */
576 };
577 
578 static PyTypeObject DecimalResultProcessorType = {
579     PyVarObject_HEAD_INIT(NULL, 0)
580     "sqlalchemy.DecimalResultProcessor",        /* tp_name */
581     sizeof(DecimalResultProcessor),             /* tp_basicsize */
582     0,                                          /* tp_itemsize */
583     (destructor)DecimalResultProcessor_dealloc, /* tp_dealloc */
584     0,                                          /* tp_print */
585     0,                                          /* tp_getattr */
586     0,                                          /* tp_setattr */
587     0,                                          /* tp_compare */
588     0,                                          /* tp_repr */
589     0,                                          /* tp_as_number */
590     0,                                          /* tp_as_sequence */
591     0,                                          /* tp_as_mapping */
592     0,                                          /* tp_hash  */
593     0,                                          /* tp_call */
594     0,                                          /* tp_str */
595     0,                                          /* tp_getattro */
596     0,                                          /* tp_setattro */
597     0,                                          /* tp_as_buffer */
598     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,   /* tp_flags */
599     "DecimalResultProcessor objects",           /* tp_doc */
600     0,                                          /* tp_traverse */
601     0,                                          /* tp_clear */
602     0,                                          /* tp_richcompare */
603     0,                                          /* tp_weaklistoffset */
604     0,                                          /* tp_iter */
605     0,                                          /* tp_iternext */
606     DecimalResultProcessor_methods,             /* tp_methods */
607     0,                                          /* tp_members */
608     0,                                          /* tp_getset */
609     0,                                          /* tp_base */
610     0,                                          /* tp_dict */
611     0,                                          /* tp_descr_get */
612     0,                                          /* tp_descr_set */
613     0,                                          /* tp_dictoffset */
614     (initproc)DecimalResultProcessor_init,      /* tp_init */
615     0,                                          /* tp_alloc */
616     0,                                          /* tp_new */
617 };
618 
619 static PyMethodDef module_methods[] = {
620     {"int_to_boolean", int_to_boolean, METH_O,
621      "Convert an integer to a boolean."},
622     {"to_str", to_str, METH_O,
623      "Convert any value to its string representation."},
624     {"to_float", to_float, METH_O,
625      "Convert any value to its floating point representation."},
626     {"str_to_datetime", str_to_datetime, METH_O,
627      "Convert an ISO string to a datetime.datetime object."},
628     {"str_to_time", str_to_time, METH_O,
629      "Convert an ISO string to a datetime.time object."},
630     {"str_to_date", str_to_date, METH_O,
631      "Convert an ISO string to a datetime.date object."},
632     {NULL, NULL, 0, NULL}        /* Sentinel */
633 };
634 
635 #ifndef PyMODINIT_FUNC  /* declarations for DLL import/export */
636 #define PyMODINIT_FUNC void
637 #endif
638 
639 
640 #if PY_MAJOR_VERSION >= 3
641 
642 static struct PyModuleDef module_def = {
643     PyModuleDef_HEAD_INIT,
644     MODULE_NAME,
645     MODULE_DOC,
646     -1,
647     module_methods
648 };
649 
650 #define INITERROR return NULL
651 
652 PyMODINIT_FUNC
PyInit_cprocessors(void)653 PyInit_cprocessors(void)
654 
655 #else
656 
657 #define INITERROR return
658 
659 PyMODINIT_FUNC
660 initcprocessors(void)
661 
662 #endif
663 
664 {
665     PyObject *m;
666 
667     UnicodeResultProcessorType.tp_new = PyType_GenericNew;
668     if (PyType_Ready(&UnicodeResultProcessorType) < 0)
669         INITERROR;
670 
671     DecimalResultProcessorType.tp_new = PyType_GenericNew;
672     if (PyType_Ready(&DecimalResultProcessorType) < 0)
673         INITERROR;
674 
675 #if PY_MAJOR_VERSION >= 3
676     m = PyModule_Create(&module_def);
677 #else
678     m = Py_InitModule3(MODULE_NAME, module_methods, MODULE_DOC);
679 #endif
680     if (m == NULL)
681         INITERROR;
682 
683     PyDateTime_IMPORT;
684 
685     Py_INCREF(&UnicodeResultProcessorType);
686     PyModule_AddObject(m, "UnicodeResultProcessor",
687                        (PyObject *)&UnicodeResultProcessorType);
688 
689     Py_INCREF(&DecimalResultProcessorType);
690     PyModule_AddObject(m, "DecimalResultProcessor",
691                        (PyObject *)&DecimalResultProcessorType);
692 
693 #if PY_MAJOR_VERSION >= 3
694     return m;
695 #endif
696 }
697