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, µsecond);
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 µsecond);
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