1 /*
2 * Copyright (c) 2014, 2021, Oracle and/or its affiliates.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0, as
6 * published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation. The authors of MySQL hereby grant you an
12 * additional permission to link the program and your derivative works
13 * with the separately licensed software that they have included with
14 * MySQL.
15 *
16 * Without limiting anything contained in the foregoing, this file,
17 * which is part of MySQL Connector/Python, is also subject to the
18 * Universal FOSS Exception, version 1.0, a copy of which can be found at
19 * http://oss.oracle.com/licenses/universal-foss-exception.
20 *
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
24 * See the GNU General Public License, version 2.0, for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software Foundation, Inc.,
28 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <time.h>
34
35 #include <Python.h>
36 #include <datetime.h>
37
38 #ifdef MS_WINDOWS
39 #include <windows.h>
40 #define strtok_r strtok_s
41 #endif
42 #include <mysql.h>
43
44 #include "mysql_connector.h"
45 #include "exceptions.h"
46 #include "mysql_capi.h"
47 #include "mysql_capi_conversion.h"
48
49 extern PyObject *MySQLError;
50 extern PyObject *MySQLInterfaceError;
51
52 // Forward declarations
53 PyObject* MySQL_connected(MySQL *self);
54
55 // Handy Macros
56 #define CHECK_SESSION(session) if (session == NULL) {\
57 raise_with_string(PyUnicode_FromString("MySQL session not available."),\
58 NULL); return 0; }
59
60 #define IS_CONNECTED(cnx) \
61 if ((PyObject*)MySQL_connected(cnx) == Py_False) {\
62 raise_with_session(&cnx->session, MySQLInterfaceError); return 0; }
63
64 // Constants and defaults
65 #define CONNECTION_TIMEOUT 13
66 #define VERSION_OFFSET_MAJOR 10000
67 #define VERSION_OFFSET_MINOR 100
68
69 /**
70 Helper function printing a string as hexadecimal.
71 */
72 void
print_buffer(const char * buffer,unsigned long size)73 print_buffer(const char *buffer, unsigned long size)
74 {
75 unsigned int i;
76 for (i=0; i < size; i++)
77 {
78 printf("%02x ", (unsigned char)buffer[i]);
79 }
80 printf("\n");
81 }
82
83 /**
84 Convert an Python object to a bytes type.
85
86 Convert the given PyObject using the given character set
87 to either a PyString_Type or PyBytes_Type for Python v3.
88
89 The PyObject value must be either a PyUnicode_Type or
90 PyBytes_Type, or PyString_Type for Python v2.
91
92 @param charset name of the character set to use
93 @param value PyObject to convert
94
95 @return PyObject
96 @retval PyObject OK
97 @retval NULL Exception
98 */
99 PyObject*
str_to_bytes(const char * charset,PyObject * value)100 str_to_bytes(const char* charset, PyObject *value)
101 {
102 PyObject *bytes;
103
104 // Unicode strings for Python v2 and v3
105 if (PyUnicode_Check(value))
106 {
107 bytes = PyUnicode_AsEncodedString(value, charset, NULL);
108 if (!bytes)
109 {
110 return NULL;
111 }
112 return bytes;
113 }
114 else if (PyBytes_Check(value))
115 {
116 return value;
117 }
118 else
119 {
120 PyErr_SetString(PyExc_TypeError, "Argument must be str or bytes");
121 return NULL;
122 }
123
124 return NULL;
125 }
126
127 /**
128 Get Python character name based on MySQL character name
129 */
130 static char*
python_characterset_name(const char * mysql_name)131 python_characterset_name(const char* mysql_name)
132 {
133 if (!mysql_name) {
134 return "latin1"; // MySQL default
135 }
136
137 if ((strcmp(mysql_name, "utf8mb4") == 0) ^
138 (strcmp(mysql_name, "utf8mb3") == 0)){
139 return "utf8";
140 }
141
142 return (char*)mysql_name;
143 }
144
145 /**
146 Get the character set name from the current MySQL session.
147
148 Get the character set name from the current MySQL session.
149 Some MySQL character sets have no equivalent names in
150 Python. When this is the case, a name for usable by Python
151 will be returned.
152 For example, 'utf8mb4' MySQL character set name will be
153 returned as 'utf8'.
154
155 @param session MySQL database connection
156
157 @return Character set name
158 @retval const char* OK
159 @retval NULL Exception
160 */
161 static const char*
my2py_charset_name(MYSQL * session)162 my2py_charset_name(MYSQL *session)
163 {
164 const char *name;
165
166 if (!session)
167 {
168 return NULL;
169 }
170
171 name= mysql_character_set_name(session);
172 return python_characterset_name(name);
173 }
174
175 /**
176 Fetch column information using the MySQL result.
177
178 Fetch the column information using the given MySQL result
179 and the number of fields.
180 The returned PyObject is a PyList which consists of
181 PyTuple objects.
182
183 @param result a MySQL result
184 @param num_fields number of fields/columns
185
186 @return PyList of PyTuple objects
187 @retval PyList OK
188 @retval NULL Exception
189 */
190 static PyObject*
fetch_fields(MYSQL_RES * result,unsigned int num_fields,MY_CHARSET_INFO * cs,unsigned int use_unicode)191 fetch_fields(MYSQL_RES *result, unsigned int num_fields, MY_CHARSET_INFO *cs,
192 unsigned int use_unicode)
193 {
194 PyObject *fields= NULL;
195 PyObject *field= NULL;
196 PyObject *decoded= NULL;
197 MYSQL_FIELD *myfs;
198 unsigned int i;
199 char *charset= python_characterset_name(cs->csname);
200
201 fields = PyList_New(0);
202
203 if (!result)
204 {
205 Py_RETURN_NONE;
206 }
207
208 Py_BEGIN_ALLOW_THREADS
209 myfs = mysql_fetch_fields(result);
210 Py_END_ALLOW_THREADS
211
212 for (i = 0; i < num_fields; i++)
213 {
214 field = PyTuple_New(11);
215
216 decoded= mytopy_string(myfs[i].catalog,
217 myfs[i].type,
218 45,
219 myfs[i].catalog_length,
220 charset,
221 use_unicode);
222 if (NULL == decoded) return NULL; // decode error
223 PyTuple_SET_ITEM(field, 0, decoded);
224
225 decoded= mytopy_string(myfs[i].db,
226 myfs[i].type,
227 45,
228 myfs[i].db_length,
229 charset,
230 use_unicode);
231 if (NULL == decoded) return NULL; // decode error
232 PyTuple_SET_ITEM(field, 1, decoded);
233
234 decoded= mytopy_string(myfs[i].table,
235 myfs[i].type,
236 45,
237 myfs[i].table_length,
238 charset,
239 use_unicode);
240 if (NULL == decoded) return NULL; // decode error
241 PyTuple_SET_ITEM(field, 2, decoded);
242
243 decoded= mytopy_string(myfs[i].org_table,
244 myfs[i].type,
245 45,
246 myfs[i].org_table_length,
247 charset,
248 use_unicode);
249 if (NULL == decoded) return NULL; // decode error
250 PyTuple_SET_ITEM(field, 3, decoded);
251
252 decoded= mytopy_string(myfs[i].name,
253 myfs[i].type,
254 45,
255 myfs[i].name_length,
256 charset,
257 use_unicode);
258 if (NULL == decoded) return NULL; // decode error
259 PyTuple_SET_ITEM(field, 4, decoded);
260
261 decoded= mytopy_string(myfs[i].org_name,
262 myfs[i].type,
263 45,
264 myfs[i].org_name_length,
265 charset,
266 use_unicode);
267 if (NULL == decoded) return NULL; // decode error
268 PyTuple_SET_ITEM(field, 5, decoded);
269
270 PyTuple_SET_ITEM(field, 6, PyLong_FromLong(myfs[i].charsetnr));
271 PyTuple_SET_ITEM(field, 7, PyLong_FromLong(myfs[i].max_length));
272 PyTuple_SET_ITEM(field, 8, PyLong_FromLong(myfs[i].type));
273 PyTuple_SET_ITEM(field, 9, PyLong_FromLong(myfs[i].flags));
274 PyTuple_SET_ITEM(field, 10, PyLong_FromLong(myfs[i].decimals));
275 PyList_Append(fields, field);
276 Py_DECREF(field);
277 }
278
279 return fields;
280 }
281
282 /**
283 MySQL instance destructor function.
284
285 MySQL instance destructor freeing result (if any) and
286 closing the connection.
287
288 @param self MySQL instance
289 */
290 void
MySQL_dealloc(MySQL * self)291 MySQL_dealloc(MySQL *self)
292 {
293 if (self) {
294 MySQL_free_result(self);
295 mysql_close(&self->session);
296
297 Py_DECREF(self->charset_name);
298 Py_DECREF(self->auth_plugin);
299 Py_DECREF(self->plugin_dir);
300
301 Py_TYPE(self)->tp_free((PyObject*)self);
302 }
303 }
304
305 /**
306 MySQL instance creation function.
307
308 MySQL instance creation function. It allocates the new
309 MySQL instance and sets default values private members.
310
311 @param type type of object being created
312 @param args positional arguments
313 @param kwargs keyword arguments
314
315 @return Instance of MySQL
316 @retval PyObject OK
317 @retval NULL Exception
318 */
319 PyObject *
MySQL_new(PyTypeObject * type,PyObject * args,PyObject * kwds)320 MySQL_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
321 {
322 MySQL *self;
323
324 self = (MySQL *)type->tp_alloc(type, 0);
325
326 if (self == NULL)
327 {
328 return NULL;
329 }
330 self->result_num_fields= 0;
331 self->buffered= Py_False;
332 self->raw= Py_False;
333 self->raw_as_string= Py_False;
334 self->buffered_at_connect= Py_False;
335 self->raw_at_connect= Py_False;
336 self->charset_name= PyUnicode_FromString("latin1");
337 self->connected= 0;
338 self->have_result_set= Py_False;
339 self->connection_timeout= CONNECTION_TIMEOUT;
340 self->result= NULL;
341 self->fields= NULL;
342 self->use_unicode= 1;
343 self->auth_plugin= PyUnicode_FromString("mysql_native_password");
344 self->plugin_dir= PyUnicode_FromString(".");
345 self->converter_str_fallback = Py_False;
346
347 return (PyObject *)self;
348 }
349
350 /**
351 MySQL instance initialization function.
352
353 MySQL instance initialization function. It handles the
354 connection arguments passed as positional or keyword
355 arguments.
356
357 Not all connection arguments are used with the initialization
358 function. List of arguments which can be used:
359 buffered, raw, charset_name,
360 connection_timeout, use_unicode,
361 auth_plugin
362
363 Other connection argument are used when actually connecting
364 with the MySQL server using the MySQL_connect() function.
365
366 @param self MySQL instance
367 @param args positional arguments
368 @param kwargs keyword arguments
369
370 @return Instance of MySQL
371 @retval PyObject OK
372 @retval NULL Exception
373 */
374 int
MySQL_init(MySQL * self,PyObject * args,PyObject * kwds)375 MySQL_init(MySQL *self, PyObject *args, PyObject *kwds)
376 {
377 PyObject *charset_name= NULL, *use_unicode= NULL, *auth_plugin= NULL, *plugin_dir= NULL, *tmp, *con_timeout= NULL;
378
379 static char *kwlist[]=
380 {
381 "buffered", "raw", "charset_name",
382 "connection_timeout", "use_unicode",
383 "auth_plugin", "plugin_dir",
384 NULL
385 };
386
387 PyDateTime_IMPORT;
388
389 // Initialization expect -1 when parsing arguments failed
390 if (!PyArg_ParseTupleAndKeywords(args, kwds,
391 "|O!O!O!O!O!O!O!", kwlist,
392 &PyBool_Type, &self->buffered_at_connect,
393 &PyBool_Type, &self->raw_at_connect,
394 &PyUnicode_Type, &charset_name,
395 &PyLong_Type, &con_timeout,
396 &PyBool_Type, &use_unicode,
397 &PyUnicode_Type, &auth_plugin,
398 &PyUnicode_Type, &plugin_dir))
399 return -1;
400
401 if (self->buffered_at_connect)
402 {
403 self->buffered= self->buffered_at_connect;
404 }
405
406 if (self->raw_at_connect)
407 {
408 self->raw= self->raw_at_connect;
409 }
410
411 if (use_unicode && use_unicode == Py_False)
412 {
413 self->use_unicode= 0;
414 }
415
416 if (charset_name) {
417 Py_DECREF(self->charset_name);
418 self->charset_name = charset_name;
419 Py_INCREF(self->charset_name);
420 }
421
422 if (auth_plugin)
423 {
424 if (strcmp(PyUnicode_AsUTF8(auth_plugin), "") == 0)
425 {
426 auth_plugin= Py_None;
427 }
428 if (auth_plugin != Py_None)
429 {
430 tmp= self->auth_plugin;
431 Py_INCREF(auth_plugin);
432 self->auth_plugin= auth_plugin;
433 Py_XDECREF(tmp);
434 }
435 }
436
437 if (plugin_dir)
438 {
439 Py_DECREF(self->plugin_dir);
440 self->plugin_dir = plugin_dir;
441 Py_INCREF(self->plugin_dir);
442 }
443
444 if (con_timeout)
445 {
446 self->connection_timeout= (unsigned int)PyLong_AsUnsignedLong(con_timeout);
447 }
448
449 return 0;
450 }
451
452 /**
453 Reset stored result.
454
455 Reset the stored result for this MySQL instance.
456
457 @param self MySQL instance
458
459 @return None
460 @retval Py_None OK
461 */
462 PyObject *
MySQL_reset_result(MySQL * self)463 MySQL_reset_result(MySQL *self)
464 {
465 self->result= NULL;
466
467 Py_XDECREF(self->fields);
468
469 self->fields= NULL;
470 self->have_result_set= Py_False;
471
472 Py_RETURN_NONE;
473 }
474
475 /**
476 Free stored result.
477
478 Free the stored result for this MySQL instance.
479 MySQL_reset_result() is called to reset the result in the
480 MySQL instance.
481
482 Note that if there is a result it is being consumed if
483 needed. If the statement that was executed returned
484 multiple results, it will loop over all sets and consume.
485
486 @param self MySQL instance
487
488 @return None
489 @retval Py_None OK
490 */
491 PyObject *
MySQL_free_result(MySQL * self)492 MySQL_free_result(MySQL *self)
493 {
494 if (self->result)
495 {
496 Py_BEGIN_ALLOW_THREADS
497 mysql_free_result(self->result);
498 Py_END_ALLOW_THREADS
499 }
500
501 MySQL_reset_result(self);
502
503 Py_RETURN_NONE;
504 }
505
506 /**
507 Consume the result.
508
509 Consume the stored result for this MySQL instance by
510 fetching all rows. MySQL_free_result() is called to reset
511 the result in the MySQL instance.
512
513 Note that if there is a result it is being consumed if
514 needed. If the statement that was executed returned
515 multiple results, it will loop over all sets and consume.
516
517 @param self MySQL instance
518
519 @return None
520 @retval Py_None OK
521 */
522 PyObject *
MySQL_consume_result(MySQL * self)523 MySQL_consume_result(MySQL *self)
524 {
525 int res = 0;
526 if (self->result)
527 {
528 Py_BEGIN_ALLOW_THREADS
529 while (mysql_fetch_row(self->result))
530 {
531 res++;
532 }
533 Py_END_ALLOW_THREADS
534 }
535
536 MySQL_free_result(self);
537
538 Py_RETURN_NONE;
539 }
540
541 /**
542 Toggle and return whether results are buffered (stored) or not.
543
544 Return whether the MySQL instance is buffering or storing
545 the results. If a boolean value is specified in the arguments
546 then the instance will be reconfigured.
547
548 Raises TypeError when the value is not PyBool_type.
549
550 @param self MySQL instance
551 @param args optional boolean type to toggle buffering
552
553 @return Boolean Object Py_True or Py_False
554 @retval PyBool_type OK
555 @retval NULL Exception
556 */
557 PyObject *
MySQL_buffered(MySQL * self,PyObject * args)558 MySQL_buffered(MySQL *self, PyObject *args)
559 {
560 PyObject *value = NULL;
561
562 if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &value))
563 {
564 return NULL;
565 }
566
567 if (value)
568 {
569 if (value == Py_True)
570 {
571 self->buffered= Py_True;
572 }
573 else
574 {
575 self->buffered= Py_False;
576 }
577 }
578
579 if (self->buffered == Py_True)
580 {
581 Py_RETURN_TRUE;
582 }
583 else
584 {
585 Py_RETURN_FALSE;
586 }
587 }
588
589 /**
590 Toggle and return whether results are converted to Python types or not.
591
592 Return whether the MySQL instance will return the rows
593 as-is, meaning not converted to Python types. If a boolean value
594 is specified in the arguments then the instance will be
595 reconfigured.
596
597 @param self MySQL instance
598 @param args optional boolean type to toggle raw
599
600 @return Boolean Object Py_True or Py_False
601 @retval PyBool_type OK
602 @retval NULL Exception
603 */
604 PyObject*
MySQL_raw(MySQL * self,PyObject * args)605 MySQL_raw(MySQL *self, PyObject *args)
606 {
607 PyObject *value = NULL;
608
609 if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &value)) {
610 return NULL;
611 }
612
613 if (value)
614 {
615 if (value == Py_True)
616 {
617 self->raw= Py_True;
618 } else {
619 self->raw= Py_False;
620 }
621 }
622
623 if (self->raw == Py_True)
624 {
625 Py_RETURN_TRUE;
626 }
627 else
628 {
629 Py_RETURN_FALSE;
630 }
631 }
632
633 /**
634 Toggle and return whether Unicode strings will be returned.
635
636 Return whether the MySQL instance will return non-binary
637 strings as Unicode. If a boolean value is specified in the
638 arguments then the instance will be
639
640 @param self MySQL instance
641 @param args optional boolean type to toggle unicode
642
643 @return Boolean Object Py_True or Py_False
644 @retval PyBool_type OK
645 @retval NULL Exception
646 */
647 PyObject*
MySQL_use_unicode(MySQL * self,PyObject * args)648 MySQL_use_unicode(MySQL *self, PyObject *args)
649 {
650 PyObject *value = NULL;
651
652 if (!PyArg_ParseTuple(args, "|O!", &PyBool_Type, &value))
653 {
654 return NULL;
655 }
656
657 if (value)
658 {
659 if (value == Py_True)
660 {
661 self->use_unicode= 1;
662 }
663 else
664 {
665 self->use_unicode= 0;
666 }
667 }
668
669 if (self->use_unicode)
670 {
671 Py_RETURN_TRUE;
672 }
673 else
674 {
675 Py_RETURN_FALSE;
676 }
677 }
678
679 /**
680 Return number of rows changed by the last statement.
681
682 Return number of rows changed by the last statement.
683
684 @param self MySQL instance
685
686 @return PyInt_Type or PyNone
687 @retval PyInt_Type OK
688 @retval PyNone no active session
689 */
690 PyObject*
MySQL_st_affected_rows(MySQL * self)691 MySQL_st_affected_rows(MySQL *self)
692 {
693 if (&self->session)
694 {
695 return PyLong_FromUnsignedLongLong((&self->session)->affected_rows);
696 }
697
698 Py_RETURN_NONE;
699 }
700
701 /**
702 Return client flags of the current session.
703
704 Return client flags of the current session.
705
706 @param self MySQL instance
707
708 @return PyInt_Type or PyNone
709 @retval PyInt_Type OK
710 @retval PyNone no active session
711 */
712 PyObject*
MySQL_st_client_flag(MySQL * self)713 MySQL_st_client_flag(MySQL *self)
714 {
715 if (&self->session)
716 {
717 return PyLong_FromLong((&self->session)->client_flag);
718 }
719
720 Py_RETURN_NONE;
721 }
722
723 /**
724 Return field count of the current session.
725
726 Return field count of the current session.
727
728 @param self MySQL instance
729
730 @return PyInt_Type or PyNone
731 @retval PyInt_Type OK
732 @retval PyNone no active session
733 */
734 PyObject*
MySQL_st_field_count(MySQL * self)735 MySQL_st_field_count(MySQL *self)
736 {
737 if (&self->session)
738 {
739 return PyLong_FromLong((&self->session)->field_count);
740 }
741
742 Py_RETURN_NONE;
743 }
744
745 /**
746 Return insert ID.
747
748 Return insert ID.
749
750 @param self MySQL instance
751
752 @return PyInt_Type or PyNone
753 @retval PyInt_Type OK
754 @retval PyNone no active session
755 */
756 PyObject*
MySQL_st_insert_id(MySQL * self)757 MySQL_st_insert_id(MySQL *self)
758 {
759 if (&self->session)
760 {
761 return PyLong_FromUnsignedLongLong((&self->session)->insert_id);
762 }
763
764 Py_RETURN_NONE;
765 }
766
767 /**
768 Return server capabilities.
769
770 Return server capabilities.
771
772 @param self MySQL instance
773
774 @return PyInt_Type or PyNone
775 @retval PyInt_Type OK
776 @retval PyNone no active session
777 */
778 PyObject*
MySQL_st_server_capabilities(MySQL * self)779 MySQL_st_server_capabilities(MySQL *self)
780 {
781 if (&self->session)
782 {
783 return PyLong_FromLong((&self->session)->server_capabilities);
784 }
785
786 Py_RETURN_NONE;
787 }
788
789 /**
790 Return server status flag.
791
792 Return server status flag.
793
794 @param self MySQL instance
795
796 @return PyInt_Type or PyNone
797 @retval PyInt_Type OK
798 @retval PyNone no active session
799 */
800 PyObject*
MySQL_st_server_status(MySQL * self)801 MySQL_st_server_status(MySQL *self)
802 {
803 if (&self->session)
804 {
805 return PyLong_FromLong((&self->session)->server_status);
806 }
807
808 Py_RETURN_NONE;
809 }
810
811 /**
812 Return warning count.
813
814 Return warning count.
815
816 @param self MySQL instance
817
818 @return PyInt_Type or PyNone
819 @retval PyInt_Type OK
820 @retval PyNone no active session
821 */
822 PyObject*
MySQL_st_warning_count(MySQL * self)823 MySQL_st_warning_count(MySQL *self)
824 {
825 if (&self->session)
826 {
827 return PyLong_FromLong((&self->session)->warning_count);
828 }
829
830 Py_RETURN_NONE;
831 }
832
833 /**
834 Return whether MySQL instance is connected or not.
835
836 Return whether the MySQL instance is connected or not.
837
838 This function uses the mysql_ping() C API function.
839
840 @param self MySQL instance
841
842 @return Boolean Object Py_True or Py_False
843 @retval PyBool_type OK
844 */
845 PyObject*
MySQL_connected(MySQL * self)846 MySQL_connected(MySQL *self)
847 {
848 if (!self->connected)
849 {
850 Py_RETURN_FALSE;
851 }
852
853 self->connected= 1;
854 Py_RETURN_TRUE;
855 }
856
857 /**
858 Toggle autocommit.
859
860 Toggle autocommit to be on or off.
861
862 Raises ValueError when mode is not a PyBool_type (Py_True or
863 Py_False).
864
865 @param self MySQL instance
866
867 @return PyNone or NULL
868 @retval PyNone OK
869 @retval NULL Exception
870 */
871 PyObject*
MySQL_autocommit(MySQL * self,PyObject * mode)872 MySQL_autocommit(MySQL *self, PyObject *mode)
873 {
874 int res= 0;
875 int new_mode= 0;
876
877 if (Py_TYPE(mode) == &PyBool_Type)
878 {
879 new_mode= (mode == Py_True) ? 1 : 0;
880
881 res= (int)mysql_autocommit(&self->session, new_mode);
882 if (res == -1 && mysql_errno(&self->session))
883 {
884 raise_with_session(&self->session, NULL);
885 return NULL;
886 }
887 Py_RETURN_NONE;
888 }
889
890 PyErr_SetString(PyExc_ValueError, "mode must be boolean");
891 return NULL;
892 }
893
894 /**
895 Change user and set new default database.
896
897 Change user and set new default database using the positional
898 and keyword arguments.
899 Arguments can be user, password or database.
900
901 @param self MySQL instance
902 @param args positional arguments
903 @param kwargs keyword arguments
904
905 @return Boolean Object Py_True or Py_False
906 @retval PyNone OK
907 @retval NULL Exception
908 */
909 PyObject*
MySQL_change_user(MySQL * self,PyObject * args,PyObject * kwds)910 MySQL_change_user(MySQL *self, PyObject *args, PyObject *kwds)
911 {
912 char *user= NULL, *database= NULL;
913 char *password= NULL, *password1=NULL, *password2= NULL, *password3= NULL;
914 char *oci_config_file = NULL;
915 unsigned int mfa_factor1= 1, mfa_factor2= 2, mfa_factor3= 3;
916 int res;
917 int option_set_res= 0;
918 static char *kwlist[]= {"user", "password", "database",
919 "password1", "password2", "password3",
920 "oci_config_file", NULL};
921 #if MYSQL_VERSION_ID >= 80001
922 bool abool;
923 #else
924 my_bool abool;
925 #endif
926
927 IS_CONNECTED(self);
928
929 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzzzzzz", kwlist,
930 &user, &password, &database,
931 &password1, &password2, &password3,
932 &oci_config_file
933 ))
934 {
935 return NULL;
936 }
937
938 if (strcmp(PyUnicode_AsUTF8(self->auth_plugin), "mysql_clear_password") == 0)
939 {
940 abool= 1;
941 mysql_options(&self->session, MYSQL_ENABLE_CLEARTEXT_PLUGIN, (char*)&abool);
942 }
943
944 #if MYSQL_VERSION_ID >= 80027
945 // Multi Factor Authentication: 1-factor password
946 if (password1 && strlen(password1) > 0)
947 {
948 mysql_options4(&self->session, MYSQL_OPT_USER_PASSWORD, &mfa_factor1, password1);
949 }
950
951 // Multi Factor Authentication: 2-factor password
952 if (password2 && strlen(password2) > 0)
953 {
954 mysql_options4(&self->session, MYSQL_OPT_USER_PASSWORD, &mfa_factor2, password2);
955 }
956
957 // Multi Factor Authentication: 3-factor password
958 if (password3 && strlen(password3) > 0)
959 {
960 mysql_options4(&self->session, MYSQL_OPT_USER_PASSWORD, &mfa_factor3, password3);
961 }
962 #endif
963
964 if (oci_config_file != NULL) {
965 /* load oci client authentication plugin if required */
966 struct st_mysql_client_plugin *oci_iam_plugin =
967 mysql_client_find_plugin(&self->session, "authentication_oci_client",
968 MYSQL_CLIENT_AUTHENTICATION_PLUGIN);
969 if (!oci_iam_plugin){
970 raise_with_string(
971 PyUnicode_FromString("The OCI IAM PLUGIN could not be loaded."),
972 NULL);
973 return NULL;
974 }
975 /* set oci-config-file in plugin */
976 if (mysql_plugin_options(
977 oci_iam_plugin, "oci-config-file", oci_config_file)){
978 raise_with_string(
979 PyUnicode_FromFormat("Invalid oci-config-file: %s",
980 oci_config_file), NULL);
981 return NULL;
982 }
983 }
984
985 Py_BEGIN_ALLOW_THREADS
986 res= mysql_change_user(&self->session, user, password, database);
987 Py_END_ALLOW_THREADS
988 if (res)
989 {
990 raise_with_session(&self->session, NULL);
991 return NULL;
992 }
993
994 Py_RETURN_NONE;
995 }
996
997 /**
998 Return the default character set name for the current session.
999
1000 Return the default character set name for the current session.
1001
1002 Raises ValueError when mode is not a PyBool_type (Py_True or
1003 Py_False).
1004
1005 @param self MySQL instance
1006
1007 @return MySQL character set name.
1008 @retval PyUnicode Python v3
1009 */
1010 PyObject*
MySQL_character_set_name(MySQL * self)1011 MySQL_character_set_name(MySQL *self)
1012 {
1013 const char *name;
1014
1015 IS_CONNECTED(self);
1016
1017 Py_BEGIN_ALLOW_THREADS
1018 name= mysql_character_set_name(&self->session);
1019 Py_END_ALLOW_THREADS
1020
1021 return PyUnicode_FromString(name);
1022 }
1023
1024 /**
1025 Set the default character set for the current session.
1026
1027 Set the default character set for the current session. The
1028 only arg allowed is a PyString_Type which has contains the
1029 character set name.
1030
1031 Raises TypeError when the argument is not a PyString_type.
1032
1033 @param self MySQL instance
1034
1035 @return MySQL character set name.
1036 @retval None OK
1037 @retval NULL Exception.
1038 */
1039 PyObject*
MySQL_set_character_set(MySQL * self,PyObject * args)1040 MySQL_set_character_set(MySQL *self, PyObject *args)
1041 {
1042 PyObject* value;
1043 int res;
1044
1045 if (!PyArg_ParseTuple(args, "O!", &PyUnicode_Type, &value))
1046 {
1047 return NULL;
1048 }
1049
1050 IS_CONNECTED(self);
1051
1052 Py_BEGIN_ALLOW_THREADS
1053 res= mysql_set_character_set(&self->session, PyUnicode_AsUTF8(value));
1054 Py_END_ALLOW_THREADS
1055
1056 if (res)
1057 {
1058 raise_with_session(&self->session, NULL);
1059 return NULL;
1060 }
1061
1062 Py_DECREF(self->charset_name);
1063 self->charset_name= value;
1064 Py_INCREF(self->charset_name);
1065
1066 Py_RETURN_NONE;
1067 }
1068
1069 /**
1070 Set the local_infile_in_path for the current session.
1071
1072 Set the local_infile_in_path for the current session. The
1073 directory from where a load data is allowed when allow_local_infile is
1074 dissabled.
1075
1076 Raises TypeError when the argument is not a PyString_type.
1077
1078 @param self MySQL instance
1079 @param args allow_local_infile_in_path
1080
1081 @return int
1082 @retval 0 Zero for success.
1083 */
1084 PyObject*
MySQL_set_load_data_local_infile_option(MySQL * self,PyObject * args)1085 MySQL_set_load_data_local_infile_option(MySQL *self, PyObject *args)
1086 {
1087 PyObject* value;
1088 int res;
1089
1090 if (!PyArg_ParseTuple(args, "O!", &PyUnicode_Type, &value))
1091 {
1092 return NULL;
1093 }
1094
1095 IS_CONNECTED(self);
1096
1097 Py_BEGIN_ALLOW_THREADS
1098
1099 res= mysql_options(&self->session, MYSQL_OPT_LOAD_DATA_LOCAL_DIR , PyUnicode_AsUTF8(value));
1100
1101 Py_END_ALLOW_THREADS
1102
1103 if (res)
1104 {
1105 raise_with_session(&self->session, NULL);
1106 return NULL;
1107 }
1108
1109 Py_RETURN_NONE;
1110 }
1111
1112 /**
1113 Commit the current transaction.
1114
1115 Commit the current transaction.
1116
1117 Raises MySQLInterfaceError on errors.
1118
1119 @param self MySQL instance
1120
1121 @return PyNone or NULL.
1122 @retval PyNone OK
1123 @retval NULL Exception.
1124 */
1125 PyObject*
MySQL_commit(MySQL * self)1126 MySQL_commit(MySQL *self)
1127 {
1128 int res = 0;
1129
1130 IS_CONNECTED(self);
1131
1132 res= mysql_commit(&self->session);
1133 if (res)
1134 {
1135 raise_with_session(&self->session, NULL);
1136 return NULL;
1137 }
1138
1139 Py_RETURN_NONE;
1140 }
1141
1142 /**
1143 Connect with a MySQL server.
1144
1145 Connect with a MySQL server using the positional and keyword
1146 arguments.
1147 Note that some connection argument are first passed to the
1148 MySQL instance using the MySQL_init() function. The other
1149 connection argument can be passed to MySQL_connect().
1150
1151 MySQL_connect() will first try to reset results and close
1152 the open connection. It will then use mysql_init() C API
1153 function to allocate the MYSQL structure. mysql_options()
1154 is then used to configure the connection. Finally,
1155 mysql_real_connect() is called.
1156
1157 Raises TypeError when one of the arguements is of an invalid
1158 type.
1159
1160 @param self MySQL instance
1161 @param args positional arguments
1162 @param kwargs keyword arguments
1163
1164 @return Boolean Object Py_True or Py_False
1165 @retval PyNone OK
1166 @retval NULL Exception
1167 */
1168 PyObject*
MySQL_connect(MySQL * self,PyObject * args,PyObject * kwds)1169 MySQL_connect(MySQL *self, PyObject *args, PyObject *kwds)
1170 {
1171 char *host= NULL, *user= NULL, *database= NULL, *unix_socket= NULL;
1172 char *oci_config_file= NULL;
1173 char *load_data_local_dir= NULL;
1174 char *ssl_ca= NULL, *ssl_cert= NULL, *ssl_key= NULL,
1175 *ssl_cipher_suites= NULL, *tls_versions= NULL,
1176 *tls_cipher_suites= NULL;
1177 PyObject *charset_name= NULL, *compress= NULL, *ssl_verify_cert= NULL,
1178 *ssl_verify_identity= NULL, *ssl_disabled= NULL,
1179 *conn_attrs= NULL, *key= NULL, *value= NULL;
1180 const char *auth_plugin, *plugin_dir;
1181 unsigned long client_flags= 0;
1182 unsigned int port= 3306, tmp_uint;
1183 int local_infile= -1;
1184 unsigned int protocol= 0;
1185 Py_ssize_t pos= 0;
1186 #if MYSQL_VERSION_ID >= 50711
1187 unsigned int ssl_mode;
1188 #endif
1189 #if MYSQL_VERSION_ID >= 80001
1190 bool abool;
1191 bool ssl_enabled= 0;
1192 #else
1193 my_bool abool;
1194 my_bool ssl_enabled= 0;
1195 #endif
1196 char *password= NULL, *password1= NULL, *password2= NULL, *password3= NULL;
1197 unsigned int mfa_factor1= 1, mfa_factor2= 2, mfa_factor3= 3;
1198 MYSQL *res;
1199
1200 static char *kwlist[]=
1201 {
1202 "host", "user", "password", "password1", "password2", "password3", "database",
1203 "port", "unix_socket", "client_flags", "ssl_ca", "ssl_cert", "ssl_key",
1204 "ssl_cipher_suites", "tls_versions", "tls_cipher_suites", "ssl_verify_cert",
1205 "ssl_verify_identity", "ssl_disabled", "compress", "conn_attrs",
1206 "local_infile", "load_data_local_dir", "oci_config_file",
1207 NULL
1208 };
1209
1210 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|zzzzzzzkzkzzzzzzO!O!O!O!O!izz",
1211 kwlist,
1212 &host,
1213 &user,
1214 &password,
1215 &password1,
1216 &password2,
1217 &password3,
1218 &database,
1219 &port,
1220 &unix_socket,
1221 &client_flags,
1222 &ssl_ca,
1223 &ssl_cert,
1224 &ssl_key,
1225 &ssl_cipher_suites,
1226 &tls_versions,
1227 &tls_cipher_suites,
1228 &PyBool_Type, &ssl_verify_cert,
1229 &PyBool_Type, &ssl_verify_identity,
1230 &PyBool_Type, &ssl_disabled,
1231 &PyBool_Type, &compress,
1232 &PyDict_Type, &conn_attrs,
1233 &local_infile,
1234 &load_data_local_dir,
1235 &oci_config_file))
1236 {
1237 return NULL;
1238 }
1239
1240 Py_BEGIN_ALLOW_THREADS
1241 if (self->connected)
1242 {
1243 self->connected= 0;
1244 mysql_close(&self->session);
1245 }
1246
1247 mysql_init(&self->session);
1248 Py_END_ALLOW_THREADS
1249
1250 if (local_infile == 1) {
1251 unsigned int accept= 1;
1252 mysql_options(&self->session, MYSQL_OPT_LOCAL_INFILE, &accept);
1253
1254 } else if (local_infile == 0 && load_data_local_dir != NULL) {
1255 if (load_data_local_dir != NULL){
1256 mysql_options(&self->session, MYSQL_OPT_LOAD_DATA_LOCAL_DIR,
1257 load_data_local_dir);
1258 }
1259
1260 } else {
1261 unsigned int denied= 0;
1262 mysql_options(&self->session, MYSQL_OPT_LOCAL_INFILE, &denied);
1263
1264 }
1265
1266 if (client_flags & CLIENT_LOCAL_FILES && (local_infile != 1)){
1267 client_flags= client_flags & ~CLIENT_LOCAL_FILES;
1268 }
1269
1270 #ifdef MS_WINDOWS
1271 if (NULL == host)
1272 {
1273 // if host is NULL, we try with named pipe
1274 mysql_options(&self->session, MYSQL_OPT_NAMED_PIPE, 0);
1275 protocol= MYSQL_PROTOCOL_PIPE;
1276 }
1277 #else
1278 if (unix_socket)
1279 {
1280 // Unix Sockets are only used when unix_socket is given.
1281 // 'localhost' is not a special case, and it use TCP protocol
1282 protocol= MYSQL_PROTOCOL_SOCKET;
1283 host= NULL;
1284 }
1285 #endif
1286 else
1287 {
1288 protocol= MYSQL_PROTOCOL_TCP;
1289 }
1290
1291 charset_name= PyUnicode_AsASCIIString(self->charset_name);
1292 if (NULL == charset_name)
1293 {
1294 return NULL;
1295 }
1296
1297 plugin_dir= PyUnicode_AsUTF8(self->plugin_dir);
1298 mysql_options(&self->session, MYSQL_PLUGIN_DIR, plugin_dir);
1299 mysql_options(&self->session, MYSQL_OPT_PROTOCOL, (char*)&protocol);
1300 mysql_options(&self->session, MYSQL_SET_CHARSET_NAME,
1301 PyBytes_AsString(charset_name));
1302
1303 Py_DECREF(charset_name);
1304
1305 tmp_uint= self->connection_timeout;
1306 mysql_options(&self->session, MYSQL_OPT_CONNECT_TIMEOUT, (char*)&tmp_uint);
1307
1308 if (ssl_disabled != NULL && (PyBool_Check(ssl_disabled) && ssl_disabled == Py_False)) {
1309 ssl_enabled= 1;
1310 client_flags |= CLIENT_SSL;
1311 if (ssl_verify_cert && ssl_verify_cert == Py_True)
1312 {
1313 #if MYSQL_VERSION_ID >= 50711
1314 if (ssl_verify_identity && ssl_verify_identity == Py_True) {
1315 ssl_mode= SSL_MODE_VERIFY_IDENTITY;
1316 mysql_options(&self->session, MYSQL_OPT_SSL_MODE, &ssl_mode);
1317 }
1318 #else
1319 abool= 1;
1320 #if MYSQL_VERSION_ID > 50703
1321 mysql_options(&self->session, MYSQL_OPT_SSL_ENFORCE, (char*)&abool);
1322 #endif
1323 mysql_options(&self->session,
1324 MYSQL_OPT_SSL_VERIFY_SERVER_CERT, (char*)&abool);
1325 #endif
1326 #if MYSQL_VERSION_ID >= 50711
1327 } else if (ssl_verify_identity && ssl_verify_identity == Py_True) {
1328 ssl_mode= SSL_MODE_VERIFY_IDENTITY;
1329 mysql_options(&self->session, MYSQL_OPT_SSL_MODE, &ssl_mode);
1330 #endif
1331 } else {
1332 ssl_ca= NULL;
1333 }
1334 mysql_ssl_set(&self->session, ssl_key, ssl_cert, ssl_ca, NULL, NULL);
1335 char logout[100];
1336 if (tls_versions != NULL)
1337 {
1338 mysql_options(&self->session,
1339 MYSQL_OPT_TLS_VERSION, tls_versions);
1340 }
1341 if (ssl_cipher_suites != NULL)
1342 {
1343 mysql_options(&self->session,
1344 MYSQL_OPT_SSL_CIPHER, ssl_cipher_suites);
1345 }
1346 if (tls_cipher_suites != NULL)
1347 {
1348 mysql_options(&self->session,
1349 MYSQL_OPT_TLS_CIPHERSUITES, tls_cipher_suites);
1350 }
1351 } else {
1352 // Make sure to not enforce SSL
1353 #if MYSQL_VERSION_ID > 50703 && MYSQL_VERSION_ID < 50711
1354 {
1355 abool= 0;
1356 mysql_options(&self->session, MYSQL_OPT_SSL_ENFORCE, (char*)&abool);
1357 }
1358 #endif
1359 #if MYSQL_VERSION_ID >= 50711
1360 {
1361 ssl_mode= SSL_MODE_DISABLED;
1362 mysql_options(&self->session, MYSQL_OPT_SSL_MODE, &ssl_mode);
1363 }
1364 #endif
1365 }
1366
1367 if (PyUnicode_Check(self->auth_plugin)) {
1368 auth_plugin= PyUnicode_AsUTF8(self->auth_plugin);
1369 mysql_options(&self->session, MYSQL_DEFAULT_AUTH, auth_plugin);
1370 if (strcmp(auth_plugin, "sha256_password") == 0 && !ssl_enabled)
1371 {
1372 PyObject *exc_type= MySQLInterfaceError;
1373 PyObject *err_no= PyLong_FromLong(2002);
1374 PyObject *err_msg= PyUnicode_FromString("sha256_password requires SSL");
1375 PyObject *err_obj= NULL;
1376 err_obj= PyObject_CallFunctionObjArgs(exc_type, err_msg, NULL);
1377 PyObject_SetAttr(err_obj, PyUnicode_FromString("sqlstate"), Py_None);
1378 PyObject_SetAttr(err_obj, PyUnicode_FromString("errno"), err_no);
1379 PyObject_SetAttr(err_obj, PyUnicode_FromString("msg"), err_msg);
1380 PyErr_SetObject(exc_type, err_obj);
1381 Py_XDECREF(exc_type);
1382 Py_XDECREF(err_no);
1383 Py_XDECREF(err_msg);
1384 return NULL;
1385 }
1386
1387 if (strcmp(auth_plugin, "mysql_clear_password") == 0)
1388 {
1389 abool= 1;
1390 mysql_options(&self->session, MYSQL_ENABLE_CLEARTEXT_PLUGIN,
1391 (char*)&abool);
1392 }
1393 }
1394
1395 if (database && strlen(database) == 0)
1396 {
1397 database= NULL;
1398 }
1399
1400 if (!database)
1401 {
1402 client_flags= client_flags & ~CLIENT_CONNECT_WITH_DB;
1403 }
1404
1405 if (conn_attrs != NULL)
1406 {
1407 while (PyDict_Next(conn_attrs, &pos, &key, &value)) {
1408 const char* attr_name;
1409 PyObject *str_name = PyObject_Str(key);
1410 if (!str_name)
1411 {
1412 printf("Unable to get attribute name\n");
1413 }
1414 attr_name= PyUnicode_AsUTF8AndSize(str_name, NULL);
1415 const char* attr_value;
1416 PyObject *str_value = PyObject_Str(value);
1417 if (!str_value)
1418 {
1419 printf("Unable to get attribute value\n");
1420 }
1421 attr_value= PyUnicode_AsUTF8AndSize(str_value, NULL);
1422 mysql_options4(&self->session, MYSQL_OPT_CONNECT_ATTR_ADD, attr_name, attr_value);
1423
1424 Py_DECREF(str_name);
1425 Py_DECREF(str_value);
1426 }
1427 }
1428
1429 #if MYSQL_VERSION_ID >= 80027
1430 // Multi Factor Authentication: 1-factor password
1431 if (password1 && strlen(password1) > 0)
1432 {
1433 mysql_options4(&self->session, MYSQL_OPT_USER_PASSWORD, &mfa_factor1, password1);
1434 }
1435
1436 // Multi Factor Authentication: 2-factor password
1437 if (password2 && strlen(password2) > 0)
1438 {
1439 mysql_options4(&self->session, MYSQL_OPT_USER_PASSWORD, &mfa_factor2, password2);
1440 }
1441
1442 // Multi Factor Authentication: 3-factor password
1443 if (password3 && strlen(password3) > 0)
1444 {
1445 mysql_options4(&self->session, MYSQL_OPT_USER_PASSWORD, &mfa_factor3, password3);
1446 }
1447 #endif
1448
1449 if (oci_config_file != NULL) {
1450 /* load oci client authentication plugin if required */
1451 struct st_mysql_client_plugin *oci_iam_plugin =
1452 mysql_client_find_plugin(&self->session, "authentication_oci_client",
1453 MYSQL_CLIENT_AUTHENTICATION_PLUGIN);
1454 if (!oci_iam_plugin){
1455 raise_with_string(
1456 PyUnicode_FromString(
1457 "The OCI authentication plugin could not be loaded."),
1458 NULL);
1459 return NULL;
1460 }
1461 /* set oci-config-file in plugin */
1462 if (mysql_plugin_options(
1463 oci_iam_plugin, "oci-config-file", oci_config_file)){
1464 raise_with_string(
1465 PyUnicode_FromFormat("Invalid oci-config-file: %s",
1466 oci_config_file), NULL);
1467 return NULL;
1468 }
1469 }
1470
1471 Py_BEGIN_ALLOW_THREADS
1472 res= mysql_real_connect(&self->session, host, user, password, database,
1473 port, unix_socket, client_flags);
1474 Py_END_ALLOW_THREADS
1475
1476 if (!res)
1477 {
1478 raise_with_session(&self->session, NULL);
1479 return NULL;
1480 }
1481
1482 self->connected= 1;
1483
1484 Py_RETURN_NONE;
1485 }
1486
1487 /**
1488 Close the MySQL connection.
1489
1490 Close the MySQL connection.
1491
1492 @param self MySQL instance
1493
1494 @return PyNone.
1495 @retval PyNone OK
1496 */
1497 PyObject*
MySQL_close(MySQL * self)1498 MySQL_close(MySQL *self)
1499 {
1500 if (self->connected)
1501 {
1502 self->connected= 0;
1503 Py_BEGIN_ALLOW_THREADS
1504 mysql_close(&self->session);
1505 Py_END_ALLOW_THREADS
1506 }
1507
1508 Py_RETURN_NONE;
1509 }
1510
1511 /**
1512 Create a legal SQL string that you can use in an SQL statement.
1513
1514 Create a legal SQL string that you can use in an SQL statement
1515 using the mysql_escape_string() C API function.
1516
1517 Raises TypeError if value is not a PyUnicode_Type,
1518 PyBytes_Type or, for Python v2, PyString_Type.
1519 Raises MySQLError when the string could not be escaped.
1520
1521 @param self MySQL instance
1522 @param value the string to escape
1523
1524 @return PyObject with the escaped string.
1525 @retval PyBytes Python v3
1526 @retval NULL Exception.
1527 */
1528 PyObject*
MySQL_escape_string(MySQL * self,PyObject * value)1529 MySQL_escape_string(MySQL *self, PyObject *value)
1530 {
1531 PyObject *to= NULL, *from= NULL;
1532 char *from_str, *to_str;
1533 Py_ssize_t from_size= 0;
1534 Py_ssize_t escaped_size= 0;
1535 const char *charset;
1536
1537 IS_CONNECTED(self);
1538
1539 charset= my2py_charset_name(&self->session);
1540
1541 if (PyUnicode_Check(value))
1542 {
1543 if (strcmp(charset, "binary") == 0)
1544 {
1545 charset = "utf8";
1546 }
1547 from= PyUnicode_AsEncodedString(value, charset, NULL);
1548 if (!from)
1549 {
1550 return NULL;
1551 }
1552 from_size= PyBytes_Size(from);
1553 from_str= PyBytes_AsString(from);
1554 }
1555 else if (PyBytes_Check(value))
1556 {
1557 from_size= PyBytes_Size(value);
1558 from_str= PyBytes_AsString(value);
1559 }
1560 else
1561 {
1562 PyErr_SetString(PyExc_TypeError, "Argument must be str or bytes");
1563 return NULL;
1564 }
1565
1566 to= PyBytes_FromStringAndSize(NULL, from_size * 2 + 1);
1567 to_str= PyBytes_AsString(to);
1568
1569 escaped_size= (Py_ssize_t)mysql_real_escape_string(&self->session, to_str,
1570 from_str,
1571 (unsigned long)from_size);
1572
1573 _PyBytes_Resize(&to, escaped_size);
1574 Py_XDECREF(from);
1575
1576 if (!to)
1577 {
1578 PyErr_SetString(MySQLError, "Failed escaping string.");
1579 return NULL;
1580 }
1581
1582 return to;
1583 }
1584
1585 /**
1586 Get information about the default character set.
1587
1588 Get information about the default character set for
1589 the current MySQL session. The returned dictionary
1590 has the keys number, name, csname, comment, dir,
1591 mbminlen and mbmaxlen.
1592
1593 Raises TypeError if value is not a PyUnicode_Type,
1594 PyBytes_Type or, for Python v2, PyString_Type.
1595 Raises MySQLError when the string could not be escaped.
1596
1597 @param self MySQL instance
1598 @param value the string to escape
1599
1600 @return A mapping with information as key/value pairs.
1601 @retval PyDict Python v3
1602 @retval NULL Exception.
1603 */
1604 PyObject*
MySQL_get_character_set_info(MySQL * self)1605 MySQL_get_character_set_info(MySQL *self)
1606 {
1607 MY_CHARSET_INFO cs;
1608 PyObject *cs_info;
1609
1610 IS_CONNECTED(self);
1611
1612 Py_BEGIN_ALLOW_THREADS
1613 mysql_get_character_set_info(&self->session, &cs);
1614 Py_END_ALLOW_THREADS
1615
1616 cs_info= PyDict_New();
1617 PyDict_SetItemString(cs_info, "number", PyLong_FromLong(cs.number));
1618 PyDict_SetItemString(cs_info, "name",
1619 PyUnicode_FromStringAndSize(cs.name, strlen(cs.name)));
1620 PyDict_SetItemString(cs_info, "csname",
1621 PyUnicode_FromStringAndSize(cs.csname,
1622 strlen(cs.csname)));
1623
1624 PyDict_SetItemString(cs_info, "comment",
1625 PyUnicode_FromStringAndSize(cs.comment,
1626 strlen(cs.comment)));
1627
1628 if (cs.dir)
1629 {
1630 PyDict_SetItemString(cs_info, "dir",
1631 PyUnicode_FromStringAndSize(cs.dir,
1632 strlen(cs.dir)));
1633 }
1634
1635 PyDict_SetItemString(cs_info, "mbminlen", PyLong_FromLong(cs.mbminlen));
1636 PyDict_SetItemString(cs_info, "mbmaxlen", PyLong_FromLong(cs.mbmaxlen));
1637
1638 return cs_info;
1639 }
1640
1641 /**
1642 Get MySQL client library version as string.
1643
1644 @param self MySQL instance
1645
1646 @return MySQL client version as string.
1647 @retval PyUnicode Python v3
1648 */
1649 PyObject*
MySQL_get_client_info(MySQL * self)1650 MySQL_get_client_info(MySQL *self)
1651 {
1652 const char *name;
1653
1654 Py_BEGIN_ALLOW_THREADS
1655 name= mysql_get_client_info();
1656 Py_END_ALLOW_THREADS
1657
1658 return PyUnicode_FromString(name);
1659 }
1660
1661 /**
1662 Get MySQL client library version as tuple.
1663
1664 @param self MySQL instance
1665
1666 @return MySQL version as sequence of integers.
1667 @retval PyTuple OK
1668 */
1669 PyObject*
MySQL_get_client_version(MySQL * self)1670 MySQL_get_client_version(MySQL *self)
1671 {
1672 unsigned long ver;
1673 PyObject *version;
1674
1675 Py_BEGIN_ALLOW_THREADS
1676 ver= mysql_get_client_version();
1677 Py_END_ALLOW_THREADS
1678
1679 version= PyTuple_New(3);
1680 // ver has format XYYZZ: X=major, YY=minor, ZZ=sub-version
1681 PyTuple_SET_ITEM(version, 0, PyLong_FromLong(ver / VERSION_OFFSET_MAJOR));
1682 PyTuple_SET_ITEM(version, 1,
1683 PyLong_FromLong((ver / VERSION_OFFSET_MINOR) \
1684 % VERSION_OFFSET_MINOR));
1685 PyTuple_SET_ITEM(version, 2, PyLong_FromLong(ver % VERSION_OFFSET_MINOR));
1686 return version;
1687 }
1688
1689 /**
1690 Get description of the type of connection in use.
1691
1692 @param self MySQL instance
1693
1694 @return Connection description as string.
1695 @retval PyUnicode Python v3
1696 */
1697 PyObject*
MySQL_get_host_info(MySQL * self)1698 MySQL_get_host_info(MySQL *self)
1699 {
1700 const char *host;
1701
1702 IS_CONNECTED(self);
1703
1704 Py_BEGIN_ALLOW_THREADS
1705 host= mysql_get_host_info(&self->session);
1706 Py_END_ALLOW_THREADS
1707
1708 return PyUnicode_FromString(host);
1709 }
1710
1711 /**
1712 Get protocol version used by current connection.
1713
1714 @param self MySQL instance
1715
1716 @return MySQL server version as string.
1717 @retval PyInt OK
1718 @retval NULL Exception
1719 */
1720 PyObject*
MySQL_get_proto_info(MySQL * self)1721 MySQL_get_proto_info(MySQL *self)
1722 {
1723 unsigned int proto;
1724
1725 IS_CONNECTED(self);
1726
1727 Py_BEGIN_ALLOW_THREADS
1728 proto= mysql_get_proto_info(&self->session);
1729 Py_END_ALLOW_THREADS
1730
1731 return PyLong_FromLong(proto);
1732 }
1733
1734 /**
1735 Get MySQL server version as string.
1736
1737 @param self MySQL instance
1738
1739 @return MySQL server version as string.
1740 @retval PyUnicode Python v3
1741 */
1742 PyObject*
MySQL_get_server_info(MySQL * self)1743 MySQL_get_server_info(MySQL *self)
1744 {
1745 const char *name;
1746
1747 IS_CONNECTED(self);
1748
1749 Py_BEGIN_ALLOW_THREADS
1750 name= mysql_get_server_info(&self->session);
1751 Py_END_ALLOW_THREADS
1752
1753 return PyUnicode_FromString(name);
1754 }
1755
1756 /**
1757 Get MySQL server version as tuple.
1758
1759 @param self MySQL instance
1760
1761 @return MySQL version as sequence of integers.
1762 @retval PyTuple OK
1763 */
1764 PyObject*
MySQL_get_server_version(MySQL * self)1765 MySQL_get_server_version(MySQL *self)
1766 {
1767 unsigned long ver;
1768 PyObject *version;
1769
1770 IS_CONNECTED(self);
1771
1772 Py_BEGIN_ALLOW_THREADS
1773 ver= mysql_get_server_version(&self->session);
1774 Py_END_ALLOW_THREADS
1775
1776 version= PyTuple_New(3);
1777 PyTuple_SET_ITEM(version, 0, PyLong_FromLong(ver / VERSION_OFFSET_MAJOR));
1778 PyTuple_SET_ITEM(version, 1,
1779 PyLong_FromLong((ver / VERSION_OFFSET_MINOR) \
1780 % VERSION_OFFSET_MINOR));
1781 PyTuple_SET_ITEM(version, 2, PyLong_FromLong(ver % VERSION_OFFSET_MINOR));
1782 return version;
1783 }
1784
1785 /**
1786 Get SSL cipher used for the current connection.
1787
1788 @param self MySQL instance
1789
1790 @return SSL cipher as string.
1791 @retval PyUnicode Python v3
1792 */
1793 PyObject*
MySQL_get_ssl_cipher(MySQL * self)1794 MySQL_get_ssl_cipher(MySQL *self)
1795 {
1796 const char *name;
1797
1798 IS_CONNECTED(self);
1799
1800 name= mysql_get_ssl_cipher(&self->session);
1801 if (name == NULL)
1802 {
1803 Py_RETURN_NONE;
1804 }
1805 return PyUnicode_FromString(name);
1806 }
1807
1808 /**
1809 Encode string in hexadecimal format.
1810
1811 Encode value in hexadecimal format and wrap it inside
1812 X''. For example, "spam" becomes X'68616d'.
1813
1814 @param self MySQL instance
1815 @param value string to encode
1816
1817 @return Encoded string prefixed with X and quoted.
1818 @retval PyBytes Python v3
1819 */
1820 PyObject*
MySQL_hex_string(MySQL * self,PyObject * value)1821 MySQL_hex_string(MySQL *self, PyObject *value)
1822 {
1823 PyObject *to, *from, *result= NULL;
1824 char *from_str, *to_str;
1825 Py_ssize_t from_size= 0;
1826 Py_ssize_t hexed_size= 0;
1827 const char *charset;
1828
1829 charset= my2py_charset_name(&self->session);
1830 from= str_to_bytes(charset, value);
1831 if (!from)
1832 {
1833 return NULL;
1834 }
1835
1836 from_size= PyBytes_Size(from);
1837 to= PyBytes_FromStringAndSize(NULL, from_size * 2 + 1);
1838 if (!to)
1839 {
1840 return NULL;
1841 }
1842 to_str= PyBytes_AsString(to);
1843 from_str= PyBytes_AsString(from);
1844
1845 Py_BEGIN_ALLOW_THREADS
1846 hexed_size= (Py_ssize_t)mysql_hex_string(to_str, from_str,
1847 (unsigned long)from_size);
1848 Py_END_ALLOW_THREADS
1849
1850 _PyBytes_Resize(&to, hexed_size);
1851
1852 result= PyBytes_FromString("X'");
1853 PyBytes_Concat(&result, to);
1854 PyBytes_Concat(&result, PyBytes_FromString("'"));
1855
1856 return result;
1857 }
1858
1859 /**
1860 Get the ID generated by AUTO_INCREMENT column.
1861
1862 Get the ID generated by the AUTO_INCREMENT column by the
1863 previous executed query.
1864
1865 @param self MySQL instance
1866
1867 @return ID generated by AUTO_INCREMENT.
1868 @retval PyInt OK
1869 @retval NULL Exception
1870 */
1871 PyObject*
MySQL_insert_id(MySQL * self)1872 MySQL_insert_id(MySQL *self)
1873 {
1874 my_ulonglong id;
1875
1876 CHECK_SESSION(self);
1877
1878 // if there was an error, result is undefined, better check:
1879 if (mysql_errno(&self->session))
1880 {
1881 raise_with_session(&self->session, NULL);
1882 return NULL;
1883 }
1884
1885 Py_BEGIN_ALLOW_THREADS
1886 id= mysql_insert_id(&self->session);
1887 Py_END_ALLOW_THREADS
1888
1889 return PyLong_FromUnsignedLongLong(id);
1890 }
1891
1892 /**
1893 Check whether connection is working.
1894
1895 Check whether connection to the MySQL is working.
1896
1897 @param self MySQL instance
1898
1899 @return Boolean Object Py_True or Py_False
1900 @retval Py_True connection available
1901 @retval Py_False connection not available
1902 */
1903 PyObject*
MySQL_ping(MySQL * self)1904 MySQL_ping(MySQL *self)
1905 {
1906 int res;
1907
1908 if (!self->connected)
1909 {
1910 Py_RETURN_FALSE;
1911 }
1912
1913 res= mysql_ping(&self->session);
1914
1915 if (!res)
1916 {
1917 Py_RETURN_TRUE;
1918 }
1919
1920 Py_RETURN_FALSE;
1921 }
1922
1923 /**
1924 Convert Python values to MySQL values.
1925
1926 Convert Python values to MySQL values based on the Python
1927 type of each value to convert. The converted values are
1928 escaped and quoted.
1929
1930 Raises MySQLInterfaceError when a Python value can not
1931 be converted.
1932
1933 @param self MySQL instance
1934 @param args Python values to be converted
1935
1936 @return PyTuple which contains all converted values.
1937 @retval PyTuple OK
1938 @retval NULL Exception
1939 */
1940 PyObject*
MySQL_convert_to_mysql(MySQL * self,PyObject * args)1941 MySQL_convert_to_mysql(MySQL *self, PyObject *args)
1942 {
1943 PyObject *prepared;
1944 int i;
1945 Py_ssize_t size;
1946 char error[100];
1947
1948 size = PyTuple_Size(args);
1949 prepared = PyTuple_New(size);
1950
1951 for (i= 0; i < size; i++) {
1952 PyObject *value= PyTuple_GetItem(args, i);
1953 PyObject *new_value= NULL;
1954
1955 if (value == NULL)
1956 {
1957 goto error;
1958 }
1959
1960 // None is SQL's NULL
1961 if (value == Py_None)
1962 {
1963 PyTuple_SET_ITEM(prepared, i, PyBytes_FromString("NULL"));
1964 continue;
1965 }
1966
1967 if (PyLong_Check(value) || PyFloat_Check(value))
1968 {
1969 PyObject *str = PyObject_Str(value);
1970 PyTuple_SET_ITEM(prepared, i,
1971 PyBytes_FromString(
1972 (const char *)PyUnicode_1BYTE_DATA(
1973 str)));
1974 Py_DECREF(str);
1975 continue;
1976 }
1977
1978 // All values that need to be quoted
1979 if (PyUnicode_Check(value)
1980 || PyUnicode_Check(value)
1981 || PyBytes_Check(value))
1982 {
1983 new_value= MySQL_escape_string(self, value);
1984 }
1985 else if (PyDateTime_Check(value))
1986 {
1987 // datetime is handled first
1988 new_value= pytomy_datetime(value);
1989 } else if (PyDate_CheckExact(value))
1990 {
1991 new_value= pytomy_date(value);
1992 }
1993 else if (PyTime_Check(value))
1994 {
1995 new_value= pytomy_time(value);
1996 }
1997 else if (PyDelta_CheckExact(value))
1998 {
1999 new_value= pytomy_timedelta(value);
2000 }
2001 else if (strcmp((value)->ob_type->tp_name, "decimal.Decimal") == 0)
2002 {
2003 new_value= pytomy_decimal(value);
2004 }
2005 else if (self->converter_str_fallback == Py_True)
2006 {
2007 PyObject *str= PyObject_Str(value);
2008 new_value= PyBytes_FromString(
2009 (const char *)PyUnicode_1BYTE_DATA(str)
2010 );
2011 Py_DECREF(str);
2012 }
2013 else
2014 {
2015 PyOS_snprintf(error, 100,
2016 "Python type %s cannot be converted",
2017 (value)->ob_type->tp_name);
2018 PyErr_SetString(MySQLInterfaceError, (const char*)error);
2019 goto error;
2020 }
2021
2022 if (!new_value)
2023 {
2024 PyOS_snprintf(error, 100,
2025 "Failed converting Python '%s'",
2026 (value)->ob_type->tp_name);
2027 PyErr_SetString(MySQLInterfaceError, error);
2028 goto error;
2029 }
2030
2031 // Some conversions could return None instead of raising errors
2032 if (new_value == Py_None)
2033 {
2034 PyTuple_SET_ITEM(prepared, i, PyBytes_FromString("NULL"));
2035
2036 }
2037 else if (PyBytes_Check(new_value))
2038 {
2039 PyObject *quoted= PyBytes_FromFormat("'%s'", PyBytes_AsString(new_value));
2040 PyTuple_SET_ITEM(prepared, i, quoted);
2041 }
2042 else if (PyUnicode_Check(new_value))
2043 {
2044 PyObject *quoted= PyBytes_FromFormat("'%s'", PyUnicode_AS_DATA(new_value));
2045 PyTuple_SET_ITEM(prepared, i, quoted);
2046 }
2047 else
2048 {
2049 PyErr_SetString(PyExc_ValueError, (const char*)"Fail!");
2050 goto error;
2051 }
2052 Py_DECREF(new_value);
2053 }
2054 return prepared;
2055
2056 error:
2057 Py_XDECREF(prepared);
2058 return NULL;
2059 }
2060
2061 /**
2062 Execute an SQL query.
2063
2064 Execute an SQL query using the current connection. The
2065 arguments allowed are statement, buffered, raw and
2066 raw_as_string.
2067
2068 buffered and raw, if not provided, will have the default
2069 set from the MySQL instance. raw_as_string is a special
2070 argument for Python v2 and will return PyString instead
2071 of PyByteArray (compatible with Connector/Python v1.x).
2072
2073 Raises TypeError when one of the arguments has an invalid
2074 type.
2075 Raises MySQLInterfaceError for any MySQL error returned
2076 by the MySQL server.
2077
2078 @param self MySQL instance
2079 @param args Python values to be converted
2080
2081 @return PyTuple which contains all converted values.
2082 @retval PyBool_type OK
2083 */
2084 PyObject*
MySQL_query(MySQL * self,PyObject * args,PyObject * kwds)2085 MySQL_query(MySQL *self, PyObject *args, PyObject *kwds)
2086 {
2087 PyObject *buffered= NULL, *raw= NULL, *raw_as_string= NULL,
2088 *query_attrs= NULL, *retval= NULL;
2089 int res= 0, stmt_length;
2090 char *stmt= NULL;
2091 static char *kwlist[]=
2092 {
2093 "statement", "buffered", "raw",
2094 "raw_as_string", "query_attrs", NULL
2095 };
2096
2097 IS_CONNECTED(self);
2098 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s#|O!O!O!O!", kwlist,
2099 &stmt, &stmt_length,
2100 &PyBool_Type, &buffered,
2101 &PyBool_Type, &raw,
2102 &PyBool_Type, &raw_as_string,
2103 &PyList_Type, &query_attrs))
2104 {
2105 return NULL;
2106 }
2107
2108 MYSQL_BIND *mybinds = NULL;
2109 struct MySQL_binding *bindings = NULL;
2110 Py_ssize_t size = 0;
2111 if ((query_attrs != NULL) && PyList_Size(query_attrs)) {
2112 size = PyList_Size(query_attrs);
2113 mybinds = calloc(size, sizeof(MYSQL_BIND));
2114 bindings = calloc(size, sizeof(struct MySQL_binding));
2115 const char **names = calloc(size, sizeof(char *));
2116 PyObject *retval = NULL;
2117 int i = 0, res = 0;
2118
2119 for (i = 0; i < (int) size; i++) {
2120 struct MySQL_binding *pbind = &bindings[i];
2121 MYSQL_BIND *mbind = &mybinds[i];
2122 PyObject *attr_tuple = PyList_GetItem(query_attrs, i);
2123 PyObject *attr_name = PyTuple_GetItem(attr_tuple, 0);
2124 names[i] = PyUnicode_AsUTF8(attr_name);
2125 PyObject *value = PyTuple_GetItem(attr_tuple, 1);
2126
2127 if (value == NULL) {
2128 goto cleanup;
2129 }
2130
2131 /* None is SQL's NULL */
2132 if (value == Py_None) {
2133 mbind->buffer_type = MYSQL_TYPE_NULL;
2134 mbind->buffer = "NULL";
2135 mbind->is_null = (bool_ *) 1;
2136 continue;
2137 }
2138
2139 /* LONG */
2140 if (PyLong_Check(value)) {
2141 pbind->buffer.l = PyLong_AsLong(value);
2142 mbind->buffer = &pbind->buffer.l;
2143 #if LONG_MAX >= INT64_T_MAX
2144 mbind->buffer_type = MYSQL_TYPE_LONGLONG;
2145 #else
2146 mbind->buffer_type = MYSQL_TYPE_LONG;
2147 #endif
2148 mbind->is_null = (bool_ *) 0;
2149 mbind->length = sizeof(mbind->buffer_type);
2150 continue;
2151 }
2152
2153 /* FLOAT */
2154 if (PyFloat_Check(value)) {
2155 pbind->buffer.f = (float) PyFloat_AsDouble(value);
2156 mbind->buffer = &pbind->buffer.f;
2157 mbind->buffer_type = MYSQL_TYPE_FLOAT;
2158 mbind->is_null = (bool_ *) 0;
2159 mbind->length = 0;
2160 continue;
2161 }
2162
2163 /* STRING */
2164 if (PyUnicode_Check(value) || PyUnicode_Check(value) || PyBytes_Check(value)) {
2165 pbind->str_value = value;
2166 mbind->buffer_type = MYSQL_TYPE_STRING;
2167 }
2168 /* DATETIME */
2169 else if (PyDateTime_Check(value)) {
2170 MYSQL_TIME *datetime = &pbind->buffer.t;
2171 datetime->year = PyDateTime_GET_YEAR(value);
2172 datetime->month = PyDateTime_GET_MONTH(value);
2173 datetime->day = PyDateTime_GET_DAY(value);
2174 datetime->hour = PyDateTime_DATE_GET_HOUR(value);
2175 datetime->minute = PyDateTime_DATE_GET_MINUTE(value);
2176 datetime->second = PyDateTime_DATE_GET_SECOND(value);
2177 if (PyDateTime_DATE_GET_MICROSECOND(value)) {
2178 datetime->second_part = PyDateTime_DATE_GET_MICROSECOND(value);
2179 } else {
2180 datetime->second_part = 0;
2181 }
2182
2183 mbind->buffer_type = MYSQL_TYPE_DATETIME;
2184 mbind->buffer = datetime;
2185 mbind->is_null = (bool_ *) 0;
2186 continue;
2187 }
2188 /* DATE */
2189 else if (PyDate_CheckExact(value)) {
2190 MYSQL_TIME *date = &pbind->buffer.t;
2191 date->year = PyDateTime_GET_YEAR(value);
2192 date->month = PyDateTime_GET_MONTH(value);
2193 date->day = PyDateTime_GET_DAY(value);
2194
2195 mbind->buffer_type = MYSQL_TYPE_DATE;
2196 mbind->buffer = date;
2197 mbind->is_null = (bool_ *) 0;
2198 continue;
2199 }
2200 /* TIME */
2201 else if (PyTime_Check(value)) {
2202 MYSQL_TIME *time = &pbind->buffer.t;
2203 time->hour = PyDateTime_TIME_GET_HOUR(value);
2204 time->minute = PyDateTime_TIME_GET_MINUTE(value);
2205 time->second = PyDateTime_TIME_GET_SECOND(value);
2206 if (PyDateTime_TIME_GET_MICROSECOND(value)) {
2207 time->second_part = PyDateTime_TIME_GET_MICROSECOND(value);
2208 } else {
2209 time->second_part = 0;
2210 }
2211
2212 mbind->buffer_type = MYSQL_TYPE_TIME;
2213 mbind->buffer = time;
2214 mbind->is_null = (bool_ *) 0;
2215 mbind->length = 0;
2216 continue;
2217 }
2218 /* datetime.timedelta is TIME */
2219 else if (PyDelta_CheckExact(value)) {
2220 MYSQL_TIME *time = &pbind->buffer.t;
2221 time->hour = PyDateTime_TIME_GET_HOUR(value);
2222 time->minute = PyDateTime_TIME_GET_MINUTE(value);
2223 time->second = PyDateTime_TIME_GET_SECOND(value);
2224 if (PyDateTime_TIME_GET_MICROSECOND(value)) {
2225 time->second_part = PyDateTime_TIME_GET_MICROSECOND(value);
2226 } else {
2227 time->second_part = 0;
2228 }
2229
2230 mbind->buffer_type = MYSQL_TYPE_TIME;
2231 mbind->buffer = time;
2232 mbind->is_null = (bool_ *) 0;
2233 mbind->length = 0;
2234 continue;
2235 }
2236 /* DECIMAL */
2237 else if (strcmp((value)->ob_type->tp_name, "decimal.Decimal") == 0) {
2238 pbind->str_value = pytomy_decimal(value);
2239 mbind[i].buffer_type = MYSQL_TYPE_DECIMAL;
2240 } else {
2241 PyErr_Format(PyExc_ValueError,
2242 "Python type %s cannot be converted",
2243 (value)->ob_type->tp_name);
2244 goto cleanup;
2245 }
2246
2247 if (!pbind->str_value) {
2248 PyErr_Format(PyExc_ValueError,
2249 "Failed converting Python '%s'",
2250 (value)->ob_type->tp_name);
2251 goto cleanup;
2252 }
2253
2254 /* Some conversions could return None instead of raising errors */
2255 if (pbind->str_value == Py_None) {
2256 mbind->buffer = "NULL";
2257 mbind->buffer_type = MYSQL_TYPE_NULL;
2258 mbind->is_null = (bool_ *) 0;
2259 } else if (PyBytes_Check(pbind->str_value)) {
2260 mbind->buffer = PyBytes_AsString(pbind->str_value);
2261 mbind->buffer_length = (unsigned long) PyBytes_Size(pbind->str_value);
2262 mbind->length = &mbind->buffer_length;
2263 mbind->is_null = (bool_ *) 0;
2264 } else if (PyUnicode_Check(pbind->str_value)) {
2265 Py_ssize_t len;
2266 mbind->buffer = PyUnicode_AsUTF8AndSize(pbind->str_value, &len);
2267 mbind->buffer_length = (unsigned long) len;
2268 mbind->length = &mbind->buffer_length;
2269 mbind->is_null = (bool_ *) 0;
2270 } else {
2271 PyErr_SetString(PyExc_ValueError,
2272 "Failed to bind query attribute");
2273 goto cleanup;
2274 }
2275 }
2276 int status;
2277 /* bind attributes */
2278 status = mysql_bind_param(&self->session, (int) size, mybinds, names);
2279 if (status) {
2280 PyErr_SetString(PyExc_ValueError,
2281 "Failed to bind query attributes");
2282 goto cleanup;
2283 }
2284 }
2285
2286 Py_BEGIN_ALLOW_THREADS
2287 res= mysql_real_query(&self->session, stmt, stmt_length);
2288 Py_END_ALLOW_THREADS
2289
2290 if (res != 0)
2291 {
2292 raise_with_session(&self->session, NULL);
2293 return NULL;
2294 }
2295
2296 if ((&self->session)->field_count == 0)
2297 {
2298 MySQL_reset_result(self);
2299 self->have_result_set= Py_False;
2300 Py_RETURN_TRUE;
2301 }
2302
2303 if (raw_as_string)
2304 {
2305 self->raw_as_string= raw_as_string;
2306 }
2307
2308 if (buffered)
2309 {
2310 self->buffered= buffered;
2311 }
2312 else
2313 {
2314 self->buffered= self->buffered_at_connect;
2315 }
2316
2317 if (raw)
2318 {
2319 self->raw= raw;
2320 }
2321 else
2322 {
2323 self->raw= self->raw_at_connect;
2324 }
2325
2326 mysql_get_character_set_info(&self->session, &self->cs);
2327 retval = MySQL_handle_result(self);
2328
2329 cleanup:
2330 for (int i = 0; i < size; i++) {
2331 switch (mybinds[i].buffer_type) {
2332 case MYSQL_TYPE_DECIMAL:
2333 Py_XDECREF(bindings[i].str_value);
2334 break;
2335 default:
2336 break;
2337 }
2338 }
2339 if (bindings != NULL) free(bindings);
2340 if (mybinds != NULL) free(mybinds);
2341 return retval;
2342 }
2343
2344 /**
2345 Get the current thread or connection ID.
2346
2347 @param self MySQL instance
2348
2349 @return MySQL connection thread ID
2350 @retval PyInt Python v3
2351 @retval PyLong Python v2
2352 */
2353 PyObject*
MySQL_thread_id(MySQL * self)2354 MySQL_thread_id(MySQL *self)
2355 {
2356 unsigned long thread_id;
2357
2358 IS_CONNECTED(self);
2359
2360 Py_BEGIN_ALLOW_THREADS
2361 thread_id= (unsigned long)mysql_thread_id(&self->session);
2362 Py_END_ALLOW_THREADS
2363
2364 return PyLong_FromUnsignedLong(thread_id);
2365 }
2366
2367 /**
2368 Select and set the default (current) database.
2369
2370 Select and set the default or current database for the
2371 current connection.
2372
2373 Raises MySQLInterfaceError for any MySQL error returned
2374 by the MySQL server.
2375
2376 @param self MySQL instance
2377 @param value name of the database
2378
2379 @return MySQL character set name.
2380 @retval None OK
2381 @retval NULL Exception.
2382 */
2383 PyObject*
MySQL_select_db(MySQL * self,PyObject * value)2384 MySQL_select_db(MySQL *self, PyObject* value)
2385 {
2386 int res;
2387 PyObject *db;
2388 const char *charset;
2389
2390 charset= my2py_charset_name(&self->session);
2391 db= str_to_bytes(charset, value);
2392
2393 if (db)
2394 {
2395 Py_BEGIN_ALLOW_THREADS
2396 res= (int)mysql_select_db(&self->session, PyBytes_AsString(db));
2397 Py_END_ALLOW_THREADS
2398
2399 if (res != 0)
2400 {
2401 raise_with_session(&self->session, NULL);
2402 return NULL;
2403 }
2404 Py_RETURN_NONE;
2405 }
2406
2407 Py_XDECREF(db);
2408
2409 PyErr_SetString(PyExc_ValueError, "db must be a string");
2410 return NULL;
2411 }
2412
2413 /**
2414 Return the warning count of previous SQL statement.
2415
2416 @param self MySQL instance
2417
2418 @return MySQL connection thread ID
2419 @retval PyInt Python v3
2420 @retval PyLong Python v2
2421 */
2422 PyObject*
MySQL_warning_count(MySQL * self)2423 MySQL_warning_count(MySQL *self)
2424 {
2425 unsigned int count;
2426
2427 CHECK_SESSION(self);
2428
2429 Py_BEGIN_ALLOW_THREADS
2430 count= mysql_warning_count(&self->session);
2431 Py_END_ALLOW_THREADS
2432
2433 return PyLong_FromUnsignedLong(count);
2434 }
2435
2436 /**
2437 Return number of rows updated by the last statement.
2438
2439 Return the number of rows changed, inserted or deleted
2440 by the last UPDATE, INSERT or DELETE queries.
2441
2442 @param self MySQL instance
2443
2444 @return MySQL connection thread ID
2445 @retval PyInt Python v3
2446 @retval PyLong Python v2
2447 */
2448 PyObject*
MySQL_affected_rows(MySQL * self)2449 MySQL_affected_rows(MySQL *self)
2450 {
2451 unsigned PY_LONG_LONG affected= 0;
2452
2453 CHECK_SESSION(&self->session);
2454
2455 Py_BEGIN_ALLOW_THREADS
2456 affected= mysql_affected_rows(&self->session);
2457 Py_END_ALLOW_THREADS
2458
2459 if ((my_ulonglong) - 1 == affected)
2460 {
2461 affected= 0;
2462 }
2463
2464 return PyLong_FromUnsignedLongLong(affected);
2465 }
2466
2467 /**
2468 Return number of columns in last result.
2469
2470 @param self MySQL instance
2471
2472 @return MySQL connection thread ID
2473 @retval PyInt Python v3
2474 @retval PyLong Python v2
2475 */
2476 PyObject*
MySQL_field_count(MySQL * self)2477 MySQL_field_count(MySQL *self)
2478 {
2479 unsigned int count= 0;
2480
2481 CHECK_SESSION(&self->session);
2482
2483 Py_BEGIN_ALLOW_THREADS
2484 count= mysql_field_count(&self->session);
2485 Py_END_ALLOW_THREADS
2486
2487 return PyLong_FromUnsignedLong(count);
2488 }
2489
2490 /**
2491 Roll back the current transaction.
2492
2493 Raises MySQLInterfaceError on errors.
2494
2495 @param self MySQL instance
2496
2497 @return PyNone or NULL.
2498 @retval PyNone OK
2499 @retval NULL Exception.
2500 */
2501 PyObject*
MySQL_rollback(MySQL * self)2502 MySQL_rollback(MySQL *self)
2503 {
2504 int res= 0;
2505
2506 IS_CONNECTED(self);
2507
2508 Py_BEGIN_ALLOW_THREADS
2509 res= mysql_rollback(&self->session);
2510 Py_END_ALLOW_THREADS
2511
2512 if (res)
2513 {
2514 raise_with_session(&self->session, NULL);
2515 return NULL;
2516 }
2517
2518 Py_RETURN_NONE;
2519 }
2520
2521 /**
2522 Check whether any more results exists.
2523
2524 @param self MySQL instance
2525 @param args Python values to be converted
2526
2527 @return Boolean Object Py_True or Py_False
2528 @retval Py_True More results available
2529 @retval Py_False No more results available
2530 */
2531 PyObject*
MySQL_more_results(MySQL * self)2532 MySQL_more_results(MySQL *self)
2533 {
2534 int res= 0;
2535
2536 CHECK_SESSION(self);
2537
2538 Py_BEGIN_ALLOW_THREADS
2539 res= mysql_more_results(&self->session);
2540 Py_END_ALLOW_THREADS
2541
2542 if (res == 1)
2543 {
2544 Py_RETURN_TRUE;
2545 }
2546
2547 Py_RETURN_FALSE;
2548 }
2549
2550 /**
2551 Handle a result.
2552
2553 Handle a result after executing a statement. It will
2554 either store or use the result.
2555
2556 Raises MySQLInterfaceError for any MySQL error returned
2557 by the MySQL server.
2558
2559 @param self MySQL instance
2560
2561 @return Boolean Object Py_True or Py_False
2562 @retval Py_True OK
2563 @retval NULL Exception
2564 */
2565 PyObject*
MySQL_handle_result(MySQL * self)2566 MySQL_handle_result(MySQL *self)
2567 {
2568 Py_BEGIN_ALLOW_THREADS
2569 if (self->buffered == Py_True)
2570 {
2571 self->result= mysql_store_result(&self->session);
2572 }
2573 else
2574 {
2575 self->result= mysql_use_result(&self->session);
2576 }
2577 Py_END_ALLOW_THREADS
2578
2579 if (self->result == NULL && mysql_errno(&self->session))
2580 {
2581 // Must be an error
2582 raise_with_session(&self->session, NULL);
2583 return NULL;
2584 }
2585
2586 if (self->result && (&self->session)->field_count)
2587 {
2588 self->have_result_set= Py_True;
2589 }
2590 else
2591 {
2592 self->have_result_set= Py_False;
2593 }
2594
2595 Py_RETURN_TRUE;
2596 }
2597
2598 /**
2599 Initiates the next result with multiple-results.
2600
2601 Raises MySQLInterfaceError for any MySQL error returned
2602 by the MySQL server.
2603
2604 @param self MySQL instance
2605
2606 @return Boolean Object Py_True or Py_False
2607 @retval Py_True OK
2608 @retval NULL Exception
2609 */
2610 PyObject*
MySQL_next_result(MySQL * self)2611 MySQL_next_result(MySQL *self)
2612 {
2613 int have_more= 0;
2614
2615 if (mysql_more_results(&self->session) == 0)
2616 {
2617 // No more results
2618 Py_RETURN_FALSE;
2619 }
2620
2621 MySQL_free_result(self);
2622 // We had a result before, we check if we can get next one
2623 Py_BEGIN_ALLOW_THREADS
2624 have_more= mysql_next_result(&self->session);
2625 Py_END_ALLOW_THREADS
2626 if (have_more > 0)
2627 {
2628 raise_with_session(&self->session, NULL);
2629 return NULL;
2630 }
2631
2632 MySQL_free_result(self);
2633 return MySQL_handle_result(self);
2634 }
2635
2636 /**
2637 Fetch column information for active MySQL result.
2638
2639 Fetch column information for active MySQL result.
2640
2641 The returned PyObject is a PyList which consists of
2642 PyTuple objects.
2643
2644 Raises MySQLInterfaceError for any MySQL error returned
2645 by the MySQL server.
2646
2647 @param result a MySQL result
2648
2649 @return PyList of PyTuple objects
2650 @retval PyList OK
2651 @retval NULL Exception
2652 */
2653 PyObject*
MySQL_fetch_fields(MySQL * self)2654 MySQL_fetch_fields(MySQL *self)
2655 {
2656 unsigned int count;
2657
2658 CHECK_SESSION(self);
2659
2660 if (!self->result)
2661 {
2662 raise_with_string(PyUnicode_FromString("No result"), NULL);
2663 return NULL;
2664 }
2665
2666 if (self->fields)
2667 {
2668 Py_INCREF(self->fields);
2669 return self->fields;
2670 }
2671
2672 Py_BEGIN_ALLOW_THREADS
2673 count= mysql_num_fields(self->result);
2674 Py_END_ALLOW_THREADS
2675
2676 return fetch_fields(self->result, count, &self->cs, self->use_unicode);
2677 }
2678
2679 /**
2680 Fetch the next row from the active result.
2681
2682 Fetch the next row from the active result. The row is returned
2683 as a tuple which contains the values converted to Python types,
2684 unless raw was set.
2685
2686 The returned PyObject is a PyList which consists of
2687 PyTuple objects.
2688
2689 Raises MySQLInterfaceError for any MySQL error returned
2690 by the MySQL server.
2691
2692 @param result a MySQL result
2693
2694 @return PyTuple with row values.
2695 @retval PyTuple OK
2696 @retval PyNone No row available
2697 @retval NULL Exception
2698 */
2699 PyObject*
MySQL_fetch_row(MySQL * self)2700 MySQL_fetch_row(MySQL *self)
2701 {
2702 MYSQL *session;
2703 MYSQL_ROW row;
2704 PyObject *result_row;
2705 PyObject *field_info;
2706 PyObject *value;
2707 PyObject *mod_decimal, *decimal, *dec_args;
2708 unsigned long *field_lengths;
2709 unsigned int num_fields;
2710 unsigned int i;
2711 unsigned long field_charsetnr, field_type, field_flags;
2712 const char *charset= NULL;
2713
2714 CHECK_SESSION(self);
2715
2716 if (!self->result)
2717 {
2718 Py_RETURN_NONE;
2719 }
2720
2721 session= &self->session;
2722 charset= my2py_charset_name(session);
2723
2724 Py_BEGIN_ALLOW_THREADS
2725 row= mysql_fetch_row(self->result);
2726 Py_END_ALLOW_THREADS
2727
2728
2729 if (row == NULL)
2730 {
2731 if (mysql_errno(session))
2732 {
2733 raise_with_session(session, NULL);
2734 return NULL;
2735 }
2736 Py_RETURN_NONE;
2737 }
2738
2739 Py_BEGIN_ALLOW_THREADS
2740 num_fields= mysql_num_fields(self->result);
2741 field_lengths= mysql_fetch_lengths(self->result);
2742 Py_END_ALLOW_THREADS
2743 if (field_lengths == NULL)
2744 {
2745 Py_RETURN_NONE;
2746 }
2747
2748 if (self->fields == NULL) {
2749 self->fields= fetch_fields(self->result, num_fields, &self->cs,
2750 self->use_unicode);
2751 }
2752
2753 result_row = PyTuple_New(num_fields);
2754 for (i= 0; i < num_fields; i++) {
2755 if (row[i] == NULL)
2756 {
2757 Py_INCREF(Py_None);
2758 PyTuple_SET_ITEM(result_row, i, Py_None);
2759 continue;
2760 }
2761 // Raw result
2762 if (self->raw == Py_True)
2763 {
2764 if (self->raw_as_string && self->raw_as_string == Py_True)
2765 {
2766 PyTuple_SET_ITEM(result_row, i,
2767 PyUnicode_FromStringAndSize(row[i],
2768 field_lengths[i]));
2769 }
2770 else
2771 {
2772 PyTuple_SET_ITEM(result_row, i,
2773 PyByteArray_FromStringAndSize(row[i],
2774 field_lengths[i]));
2775 }
2776 continue;
2777 }
2778
2779 field_info= PyList_GetItem(self->fields, i);
2780 if (!field_info)
2781 {
2782 Py_XDECREF(result_row);
2783 Py_RETURN_NONE;
2784 }
2785
2786 field_charsetnr= PyLong_AsUnsignedLong(PyTuple_GetItem(field_info, 6));
2787 field_type= PyLong_AsUnsignedLong(PyTuple_GetItem(field_info, 8));
2788 field_flags= PyLong_AsUnsignedLong(PyTuple_GetItem(field_info, 9));
2789
2790 // Convert MySQL values to Python objects
2791 if (field_type == MYSQL_TYPE_TINY ||
2792 field_type == MYSQL_TYPE_SHORT ||
2793 field_type == MYSQL_TYPE_LONG ||
2794 field_type == MYSQL_TYPE_LONGLONG ||
2795 field_type == MYSQL_TYPE_INT24 ||
2796 field_type == MYSQL_TYPE_YEAR) {
2797 if (field_flags && ZEROFILL_FLAG) {
2798 PyTuple_SET_ITEM(result_row, i, PyLong_FromString(row[i], NULL, 10));
2799 }
2800 else
2801 {
2802 PyTuple_SET_ITEM(result_row, i, PyLong_FromString(row[i], NULL, 0));
2803 }
2804 }
2805 else if (field_type == MYSQL_TYPE_DATETIME ||
2806 field_type == MYSQL_TYPE_TIMESTAMP)
2807 {
2808 PyTuple_SET_ITEM(result_row, i,
2809 mytopy_datetime(row[i], field_lengths[i]));
2810 }
2811 else if (field_type == MYSQL_TYPE_DATE)
2812 {
2813 PyTuple_SET_ITEM(result_row, i, mytopy_date(row[i]));
2814 }
2815 else if (field_type == MYSQL_TYPE_TIME)
2816 {
2817 // The correct conversion is to a timedelta
2818 PyTuple_SET_ITEM(result_row, i, mytopy_time(row[i],
2819 field_lengths[i]));
2820 }
2821 else if (field_type == MYSQL_TYPE_VARCHAR ||
2822 field_type == MYSQL_TYPE_STRING ||
2823 field_type == MYSQL_TYPE_ENUM ||
2824 field_type == MYSQL_TYPE_VAR_STRING)
2825 {
2826 value= mytopy_string(row[i],
2827 field_type,
2828 field_charsetnr,
2829 field_lengths[i],
2830 charset,
2831 self->use_unicode);
2832 if (!value)
2833 {
2834 goto error;
2835 }
2836 else
2837 {
2838 if (field_flags & SET_FLAG)
2839 {
2840 if (!strlen(row[i]))
2841 {
2842 value= PySet_New(NULL);
2843 }
2844 else
2845 {
2846 value= PySet_New(PyUnicode_Split(
2847 value, PyUnicode_FromString(","), -1));
2848 }
2849 if (!value)
2850 {
2851 goto error;
2852 }
2853 }
2854 PyTuple_SET_ITEM(result_row, i, value);
2855 }
2856 }
2857 else if (field_type == MYSQL_TYPE_NEWDECIMAL ||
2858 field_type == MYSQL_TYPE_DECIMAL)
2859 {
2860 mod_decimal= PyImport_ImportModule("decimal");
2861 if (mod_decimal) {
2862 dec_args= PyTuple_New(1);
2863 PyTuple_SET_ITEM(dec_args, 0, PyUnicode_FromString(row[i]));
2864 decimal= PyObject_GetAttrString(mod_decimal, "Decimal");
2865 PyTuple_SET_ITEM(result_row, i,
2866 PyObject_Call(decimal, dec_args, NULL));
2867 Py_DECREF(dec_args);
2868 Py_DECREF(decimal);
2869 }
2870 Py_XDECREF(mod_decimal);
2871 }
2872 else if (field_type == MYSQL_TYPE_FLOAT ||
2873 field_type == MYSQL_TYPE_DOUBLE)
2874 {
2875 char *end;
2876 double val= PyOS_string_to_double(row[i], &end, NULL);
2877
2878 if (*end == '\0')
2879 {
2880 value= PyFloat_FromDouble(val);
2881 }
2882 else
2883 {
2884 value= Py_None;
2885 }
2886
2887 PyTuple_SET_ITEM(result_row, i, value);
2888 }
2889 else if (field_type == MYSQL_TYPE_BIT)
2890 {
2891 PyTuple_SET_ITEM(result_row, i,
2892 mytopy_bit(row[i], field_lengths[i]));
2893 }
2894 else if (field_type == MYSQL_TYPE_BLOB)
2895 {
2896 if ((field_flags & BLOB_FLAG) && (field_flags & BINARY_FLAG))
2897 {
2898 value= PyBytes_FromStringAndSize(row[i], field_lengths[i]);
2899 }
2900 else
2901 {
2902 value= mytopy_string(row[i],
2903 field_type,
2904 field_charsetnr,
2905 field_lengths[i],
2906 charset,
2907 self->use_unicode);
2908 }
2909 PyTuple_SET_ITEM(result_row, i, value);
2910 }
2911 else if (field_type == MYSQL_TYPE_GEOMETRY)
2912 {
2913 PyTuple_SET_ITEM(result_row, i,
2914 PyByteArray_FromStringAndSize(row[i],
2915 field_lengths[i]));
2916 }
2917 else
2918 {
2919 // Do our best to convert whatever we got from MySQL to a str/bytes
2920 value = mytopy_string(row[i],
2921 field_type,
2922 field_charsetnr,
2923 field_lengths[i],
2924 charset,
2925 self->use_unicode);
2926 PyTuple_SET_ITEM(result_row, i, value);
2927 }
2928 }
2929
2930 return result_row;
2931 error:
2932 Py_DECREF(result_row);
2933 return NULL;
2934 }
2935
2936 /**
2937 Get number of rows in active result.
2938
2939 @param self MySQL instance
2940
2941 Raises MySQLError when there is no result.
2942
2943 @return Number of rows as PyInt
2944 @retval PyInt Python v3
2945 @retval PyLong Python v2
2946 @retval NULL Exception
2947 */
2948 PyObject*
MySQL_num_rows(MySQL * self)2949 MySQL_num_rows(MySQL *self)
2950 {
2951 my_ulonglong count;
2952
2953 if (!self->result)
2954 {
2955 raise_with_string(PyUnicode_FromString(
2956 "Statement did not return result set"), NULL);
2957 return NULL;
2958 }
2959
2960 Py_BEGIN_ALLOW_THREADS
2961 count= mysql_num_rows(self->result);
2962 Py_END_ALLOW_THREADS
2963
2964 return PyLong_FromUnsignedLongLong(count);
2965 }
2966
2967 /**
2968 Get number of fields in active result.
2969
2970 @param self MySQL instance
2971
2972 @return Number of fields as PyInt
2973 @retval PyInt Python v3
2974 @retval PyLong Python v2
2975 @retval None No result available
2976 */
2977 PyObject*
MySQL_num_fields(MySQL * self)2978 MySQL_num_fields(MySQL *self)
2979 {
2980 unsigned int count;
2981
2982 if (!self->result)
2983 {
2984 Py_RETURN_NONE;
2985 }
2986
2987 Py_BEGIN_ALLOW_THREADS
2988 count= mysql_num_fields(self->result);
2989 Py_END_ALLOW_THREADS
2990
2991 return PyLong_FromUnsignedLong(count);
2992 }
2993
2994 /**
2995 Flush or reset tables and caches.
2996
2997 Flush or reset tables and caches. The only argument currently
2998 allowed is an integer.
2999
3000 Raises TypeError when first argument is not an integer.
3001
3002 @param self MySQL instance
3003 @param args positional arguments
3004
3005 @return Number of fields as PyInt
3006 @retval PyNone OK
3007 @retval NULL Exception
3008 */
3009 PyObject*
MySQL_refresh(MySQL * self,PyObject * args)3010 MySQL_refresh(MySQL *self, PyObject *args)
3011 {
3012 unsigned int options;
3013 int res;
3014
3015 IS_CONNECTED(self);
3016
3017 if (!PyArg_ParseTuple(args, "I", &options))
3018 {
3019 return NULL;
3020 }
3021
3022 Py_BEGIN_ALLOW_THREADS
3023 res= mysql_refresh(&self->session, options);
3024 Py_END_ALLOW_THREADS
3025
3026 if (res)
3027 {
3028 raise_with_session(&self->session, NULL);
3029 return NULL;
3030 }
3031
3032 Py_RETURN_NONE;
3033 }
3034
3035 /**
3036 Resets current connection.
3037
3038 Resets this connection to MySQL.
3039
3040 @param self MySQL instance
3041
3042 @return Boolean Object Py_True or Py_False
3043 @retval Py_True for success
3044 @retval Py_False if an error occurred
3045 */
3046 PyObject*
MySQL_reset_connection(MySQL * self)3047 MySQL_reset_connection(MySQL *self)
3048 {
3049 int res;
3050
3051 if (!self->connected)
3052 {
3053 Py_RETURN_FALSE;
3054 }
3055
3056 res= mysql_reset_connection(&self->session);
3057
3058 if (!res)
3059 {
3060 Py_RETURN_TRUE;
3061 }
3062
3063 Py_RETURN_FALSE;
3064 }
3065
3066 /**
3067 Shut down the MySQL server.
3068
3069 Shut down the MySQL server. The only argument currently allowed
3070 is an integer which describes the shutdown type.
3071
3072 Raises TypeError when first argument is not an integer.
3073 Raises MySQLErrorInterface when an error is retured by
3074 the MySQL server.
3075
3076 @param self MySQL instance
3077 @param args positional arguments
3078
3079 @return Number of fields as PyInt
3080 @retval PyNone OK
3081 @retval NULL Exception
3082 */
3083 PyObject*
MySQL_shutdown(MySQL * self,PyObject * args)3084 MySQL_shutdown(MySQL *self, PyObject *args)
3085 {
3086 unsigned int level= 0;
3087 int res;
3088
3089 CHECK_SESSION(self);
3090
3091 if (!PyArg_ParseTuple(args, "I", &level))
3092 {
3093 return NULL;
3094 }
3095
3096 Py_BEGIN_ALLOW_THREADS
3097 res= mysql_shutdown(&self->session, level);
3098 Py_END_ALLOW_THREADS
3099
3100 if (res)
3101 {
3102 raise_with_session(&self->session, NULL);
3103 return NULL;
3104 }
3105
3106 Py_RETURN_NONE;
3107 }
3108
3109 /**
3110 Get the server status as string.
3111
3112 Get the server status as string.
3113
3114 Raises MySQLErrorInterface when an error is retured by
3115 the MySQL server.
3116
3117 @param self MySQL instance
3118
3119 @return Number of fields as PyInt
3120 @retval PyBytes Python v3
3121 @retval PyByteArray Python v2
3122 @retval NULL Exception
3123 */
3124 PyObject*
MySQL_stat(MySQL * self)3125 MySQL_stat(MySQL *self)
3126 {
3127 const char *stat;
3128
3129 CHECK_SESSION(self);
3130
3131 Py_BEGIN_ALLOW_THREADS
3132 stat= mysql_stat(&self->session);
3133 Py_END_ALLOW_THREADS
3134
3135 if (!stat)
3136 {
3137 raise_with_session(&self->session, NULL);
3138 return NULL;
3139 }
3140
3141 return PyBytes_FromString(stat);
3142 }
3143
3144 /**
3145 Prepare a SQL statement.
3146
3147 Prepare a SQL statement using the current connection.
3148
3149 Raises MySQLInterfaceError for any MySQL error returned
3150 by the MySQL server.
3151
3152 @param self MySQLPrepStmt instance
3153 @param args SQL statement to be prepared
3154
3155 @return PyTuple which contains all converted values.
3156 @retval PyBool_type OK
3157 */
3158 PyObject*
MySQL_stmt_prepare(MySQL * self,PyObject * args)3159 MySQL_stmt_prepare(MySQL *self, PyObject *args)
3160 {
3161 MYSQL *mysql= NULL;
3162 MYSQL_STMT *mysql_stmt= NULL;
3163 MYSQL_RES *mysql_res= NULL;
3164 int res= 0;
3165 const char *stmt_char= NULL;
3166 unsigned long stmt_length= 0;
3167 unsigned long param_count= 0;
3168 PyObject *stmt;
3169 PyObject *prep_stmt;
3170
3171 IS_CONNECTED(self);
3172
3173 if (!PyArg_ParseTuple(args, "S", &stmt))
3174 {
3175 return NULL;
3176 }
3177 stmt_char= PyBytes_AsString(stmt);
3178 stmt_length= strlen(stmt_char);
3179 mysql= &self->session;
3180
3181 Py_BEGIN_ALLOW_THREADS
3182 mysql_stmt= mysql_stmt_init(mysql);
3183 Py_END_ALLOW_THREADS
3184
3185 if (!mysql_stmt)
3186 {
3187 goto error;
3188 }
3189
3190 Py_BEGIN_ALLOW_THREADS
3191 res= mysql_stmt_prepare(mysql_stmt, stmt_char, stmt_length);
3192 Py_END_ALLOW_THREADS
3193
3194 if (res)
3195 {
3196 goto error;
3197 }
3198
3199 /* Get the parameter count from the statement */
3200 Py_BEGIN_ALLOW_THREADS
3201 param_count= mysql_stmt_param_count(mysql_stmt);
3202 Py_END_ALLOW_THREADS
3203
3204 /* Create MySQLPrepStmt object */
3205 prep_stmt= PyObject_CallObject((PyObject *) &MySQLPrepStmtType, NULL);
3206 ((MySQLPrepStmt *) prep_stmt)->stmt= mysql_stmt;
3207 ((MySQLPrepStmt *) prep_stmt)->res= mysql_res;
3208 ((MySQLPrepStmt *) prep_stmt)->param_count= param_count;
3209 ((MySQLPrepStmt *) prep_stmt)->use_unicode= self->use_unicode;
3210 ((MySQLPrepStmt *) prep_stmt)->cs= self->cs;
3211 ((MySQLPrepStmt *) prep_stmt)->charset= my2py_charset_name(mysql);
3212
3213 Py_INCREF(prep_stmt);
3214
3215 return prep_stmt;
3216
3217 error:
3218 Py_BEGIN_ALLOW_THREADS
3219 mysql_stmt_close(mysql_stmt);
3220 Py_END_ALLOW_THREADS
3221 PyErr_SetString(MySQLInterfaceError, mysql_stmt_error(mysql_stmt));
3222 return NULL;
3223 }
3224
3225 /**
3226 MySQLPrepStmt instance creation function.
3227
3228 MySQLPrepStmt instance creation function. It allocates the new
3229 MySQLPrepStmt instance and sets default values private members.
3230
3231 @param type type of object being created
3232 @param args positional arguments
3233 @param kwargs keyword arguments
3234
3235 @return Instance of MySQPrepStmt
3236 @retval PyObject OK
3237 @retval NULL Exception
3238 */
3239 PyObject *
MySQLPrepStmt_new(PyTypeObject * type,PyObject * args,PyObject * kwds)3240 MySQLPrepStmt_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
3241 {
3242 MySQLPrepStmt *self= (MySQLPrepStmt *)type->tp_alloc(type, 0);
3243
3244 if (!self)
3245 {
3246 return NULL;
3247 }
3248 self->bind= NULL;
3249 self->res= NULL;
3250 self->stmt= NULL;
3251 self->charset= NULL;
3252 self->param_count= 0;
3253 self->column_count= 0;
3254 self->cols= NULL;
3255 self->fields= NULL;
3256 self->have_result_set= Py_False;
3257
3258 return (PyObject *)self;
3259 }
3260
3261 /**
3262 MySQLPrepStmt instance initialization function.
3263
3264 @param self MySQLPrepStmt instance
3265 @param args positional arguments
3266 @param kwargs keyword arguments
3267
3268 @return Instance of MySQLPrepStmt
3269 @retval PyObject OK
3270 @retval NULL Exception
3271 */
3272 int
MySQLPrepStmt_init(MySQLPrepStmt * self,PyObject * args,PyObject * kwds)3273 MySQLPrepStmt_init(MySQLPrepStmt *self, PyObject *args, PyObject *kwds)
3274 {
3275 return 0;
3276 }
3277
3278 /**
3279 MySQLPrepStmt instance destructor function.
3280
3281 MySQLPrepStmt instance destructor freeing result (if any) and
3282 closing the statement.
3283
3284 @param self MySQLPrepStmt instance
3285 */
3286 void
MySQLPrepStmt_dealloc(MySQLPrepStmt * self)3287 MySQLPrepStmt_dealloc(MySQLPrepStmt *self)
3288 {
3289 if (self)
3290 {
3291 MySQLPrepStmt_free_result(self);
3292 MySQLPrepStmt_close(self);
3293 Py_TYPE(self)->tp_free((PyObject*)self);
3294 }
3295 }
3296
3297 /**
3298 Executes a prepared statement.
3299
3300 Binds the values of the prepared statement executes it.
3301
3302 Raises MySQLInterfaceError for any MySQL error returned
3303 by the MySQL server.
3304
3305 @param self MySQLPrepStmt instance
3306 @param args Python values to bind
3307
3308 @return PyTuple which contains all converted values.
3309 @retval PyBool_type OK
3310 */
3311 PyObject*
MySQLPrepStmt_execute(MySQLPrepStmt * self,PyObject * args)3312 MySQLPrepStmt_execute(MySQLPrepStmt *self, PyObject *args)
3313 {
3314 Py_ssize_t size= PyTuple_Size(args);
3315 MYSQL_BIND *mbinds= calloc(size, sizeof(MYSQL_BIND));
3316 struct MySQL_binding *bindings= calloc(size, sizeof(struct MySQL_binding));
3317 PyObject *value;
3318 PyObject *retval= NULL;
3319 int i= 0, res= 0;
3320
3321 for (i= 0; i < size; i++)
3322 {
3323 struct MySQL_binding *pbind= &bindings[i];
3324 MYSQL_BIND *mbind= &mbinds[i];
3325 value= PyTuple_GetItem(args, i);
3326
3327 if (value == NULL)
3328 {
3329 goto cleanup;
3330 }
3331
3332 /* None is SQL's NULL */
3333 if (value == Py_None)
3334 {
3335 mbind->buffer_type= MYSQL_TYPE_NULL;
3336 mbind->buffer= "NULL";
3337 mbind->is_null= (bool_ *)1;
3338 continue;
3339 }
3340
3341 /* LONG */
3342 if (PyLong_Check(value))
3343 {
3344 pbind->buffer.l= PyLong_AsLong(value);
3345 mbind->buffer= &pbind->buffer.l;
3346 #if LONG_MAX >= INT64_T_MAX
3347 mbind->buffer_type= MYSQL_TYPE_LONGLONG;
3348 #else
3349 mbind->buffer_type= MYSQL_TYPE_LONG;
3350 #endif
3351 mbind->is_null= (bool_ *)0;
3352 mbind->length= 0;
3353 continue;
3354 }
3355
3356 /* FLOAT */
3357 if (PyFloat_Check(value))
3358 {
3359 pbind->buffer.f= (float)PyFloat_AsDouble(value);
3360 mbind->buffer= &pbind->buffer.f;
3361 mbind->buffer_type= MYSQL_TYPE_FLOAT;
3362 mbind->is_null= (bool_ *)0;
3363 mbind->length= 0;
3364 continue;
3365 }
3366
3367 /* STRING */
3368 if (PyUnicode_Check(value) || PyUnicode_Check(value) || PyBytes_Check(value))
3369 {
3370 pbind->str_value= value;
3371 mbind->buffer_type= MYSQL_TYPE_STRING;
3372 }
3373 /* DATETIME */
3374 else if (PyDateTime_Check(value))
3375 {
3376 MYSQL_TIME *datetime= &pbind->buffer.t;
3377 datetime->year= PyDateTime_GET_YEAR(value);
3378 datetime->month= PyDateTime_GET_MONTH(value);
3379 datetime->day= PyDateTime_GET_DAY(value);
3380 datetime->hour= PyDateTime_DATE_GET_HOUR(value);
3381 datetime->minute= PyDateTime_DATE_GET_MINUTE(value);
3382 datetime->second= PyDateTime_DATE_GET_SECOND(value);
3383 if (PyDateTime_DATE_GET_MICROSECOND(value))
3384 {
3385 datetime->second_part= PyDateTime_DATE_GET_MICROSECOND(value);
3386 }
3387 else
3388 {
3389 datetime->second_part= 0;
3390 }
3391
3392 mbind->buffer_type= MYSQL_TYPE_DATETIME;
3393 mbind->buffer= datetime;
3394 mbind->is_null= (bool_ *)0;
3395 continue;
3396 }
3397 /* DATE */
3398 else if (PyDate_CheckExact(value))
3399 {
3400 MYSQL_TIME *date= &pbind->buffer.t;
3401 date->year= PyDateTime_GET_YEAR(value);
3402 date->month= PyDateTime_GET_MONTH(value);
3403 date->day= PyDateTime_GET_DAY(value);
3404
3405 mbind->buffer_type= MYSQL_TYPE_DATE;
3406 mbind->buffer= date;
3407 mbind->is_null= (bool_ *)0;
3408 continue;
3409 }
3410 /* TIME */
3411 else if (PyTime_Check(value))
3412 {
3413 MYSQL_TIME *time= &pbind->buffer.t;
3414 time->hour= PyDateTime_TIME_GET_HOUR(value);
3415 time->minute= PyDateTime_TIME_GET_MINUTE(value);
3416 time->second= PyDateTime_TIME_GET_SECOND(value);
3417 if (PyDateTime_TIME_GET_MICROSECOND(value))
3418 {
3419 time->second_part= PyDateTime_TIME_GET_MICROSECOND(value);
3420 }
3421 else
3422 {
3423 time->second_part= 0;
3424 }
3425
3426 mbind->buffer_type= MYSQL_TYPE_TIME;
3427 mbind->buffer= time;
3428 mbind->is_null= (bool_ *)0;
3429 mbind->length= 0;
3430 continue;
3431 }
3432 /* datetime.timedelta is TIME */
3433 else if (PyDelta_CheckExact(value))
3434 {
3435 MYSQL_TIME *time= &pbind->buffer.t;
3436 time->hour= PyDateTime_TIME_GET_HOUR(value);
3437 time->minute= PyDateTime_TIME_GET_MINUTE(value);
3438 time->second= PyDateTime_TIME_GET_SECOND(value);
3439 if (PyDateTime_TIME_GET_MICROSECOND(value))
3440 {
3441 time->second_part= PyDateTime_TIME_GET_MICROSECOND(value);
3442 }
3443 else
3444 {
3445 time->second_part= 0;
3446 }
3447
3448 mbind->buffer_type= MYSQL_TYPE_TIME;
3449 mbind->buffer= time;
3450 mbind->is_null= (bool_ *)0;
3451 mbind->length= 0;
3452 continue;
3453 }
3454 /* DECIMAL */
3455 else if (strcmp((value)->ob_type->tp_name, "decimal.Decimal") == 0)
3456 {
3457 pbind->str_value= pytomy_decimal(value);
3458 mbind[i].buffer_type= MYSQL_TYPE_DECIMAL;
3459 }
3460 else if (self->converter_str_fallback == Py_True)
3461 {
3462 PyObject *str= PyObject_Str(value);
3463 pbind->str_value= PyBytes_FromString(
3464 (const char *)PyUnicode_1BYTE_DATA(str)
3465 );
3466 mbind->buffer_type= MYSQL_TYPE_STRING;
3467 Py_DECREF(str);
3468 }
3469 else
3470 {
3471 retval= PyErr_Format(MySQLInterfaceError,
3472 (const char*)"Python type %s cannot be converted",
3473 (value)->ob_type->tp_name);
3474 goto cleanup;
3475 }
3476
3477 if (!pbind->str_value)
3478 {
3479 retval= PyErr_Format(MySQLInterfaceError,
3480 (const char*)"Failed converting Python '%s'",
3481 (value)->ob_type->tp_name);
3482 goto cleanup;
3483 }
3484
3485 /* Some conversions could return None instead of raising errors */
3486 if (pbind->str_value == Py_None)
3487 {
3488 mbind->buffer= "NULL";
3489 mbind->buffer_type= MYSQL_TYPE_NULL;
3490 mbind->is_null= (bool_ *)0;
3491 }
3492 else if (PyBytes_Check(pbind->str_value))
3493 {
3494 mbind->buffer= PyBytes_AsString(pbind->str_value);
3495 mbind->buffer_length= (unsigned long)PyBytes_Size(pbind->str_value);
3496 mbind->length= &mbind->buffer_length;
3497 mbind->is_null= (bool_ *)0;
3498 }
3499 else if (PyUnicode_Check(pbind->str_value))
3500 {
3501 Py_ssize_t len;
3502 mbind->buffer= PyUnicode_AsUTF8AndSize(pbind->str_value, &len);
3503 mbind->buffer_length= (unsigned long)len;
3504 mbind->length= &mbind->buffer_length;
3505 mbind->is_null= (bool_ *)0;
3506 }
3507 else
3508 {
3509 PyErr_SetString(PyExc_ValueError,
3510 (const char*)"Failed to bind parameter");
3511 goto cleanup;
3512 }
3513 }
3514
3515 if (mysql_stmt_bind_param(self->stmt, mbinds))
3516 {
3517 retval= PyErr_Format(MySQLInterfaceError,
3518 (const char*)"Bind the parameters: %s",
3519 mysql_stmt_error(self->stmt));
3520 goto cleanup;
3521 }
3522
3523 Py_BEGIN_ALLOW_THREADS
3524 res= mysql_stmt_execute(self->stmt);
3525 Py_END_ALLOW_THREADS
3526
3527 if (res)
3528 {
3529 retval= PyErr_Format(MySQLInterfaceError,
3530 (const char*)"Error while executing statement: %s",
3531 mysql_stmt_error(self->stmt));
3532 goto cleanup;
3533 }
3534
3535 retval= MySQLPrepStmt_handle_result(self);
3536 goto cleanup;
3537
3538 cleanup:
3539 for (i= 0; i < size; i++)
3540 {
3541 switch (mbinds[i].buffer_type)
3542 {
3543 case MYSQL_TYPE_DECIMAL:
3544 Py_XDECREF(bindings[i].str_value);
3545 break;
3546 default:
3547 // Nothing to do
3548 break;
3549 }
3550 }
3551 free(bindings);
3552 free(mbinds);
3553 return retval;
3554 }
3555
3556 /**
3557 Handles a prepared statement result.
3558
3559 Handles a result after executing a prepared statement. It will
3560 either store or use the result.
3561
3562 Raises MySQLInterfaceError for any MySQL error returned
3563 by the MySQL server.
3564
3565 @param self MySQLPrepStmt instance
3566
3567 @return Boolean Object Py_True or Py_False
3568 @retval Py_True OK
3569 @retval NULL Exception
3570 */
3571 PyObject*
MySQLPrepStmt_handle_result(MySQLPrepStmt * self)3572 MySQLPrepStmt_handle_result(MySQLPrepStmt *self)
3573 {
3574 unsigned int i= 0;
3575
3576 Py_BEGIN_ALLOW_THREADS
3577 self->res = mysql_stmt_result_metadata(self->stmt);
3578 Py_END_ALLOW_THREADS
3579
3580 if (!self->res)
3581 {
3582 /* No result set */
3583 self->have_result_set= Py_False;
3584 Py_RETURN_TRUE;
3585 }
3586
3587 self->have_result_set= Py_True;
3588
3589 Py_BEGIN_ALLOW_THREADS
3590 self->column_count= mysql_num_fields(self->res);
3591 self->bind= calloc(self->column_count, sizeof(MYSQL_BIND));
3592 self->cols= calloc(self->column_count, sizeof(struct column_info));
3593
3594 for (i= 0; i < self->column_count; ++i)
3595 {
3596 MYSQL_FIELD *field= mysql_fetch_field(self->res);
3597 switch (field->type)
3598 {
3599 case MYSQL_TYPE_NULL:
3600 self->bind[i].buffer_type= MYSQL_TYPE_NULL;
3601 self->bind[i].buffer= NULL;
3602 self->bind[i].is_null= &self->cols[i].is_null;
3603 break;
3604 case MYSQL_TYPE_BIT:
3605 self->bind[i].buffer_type= MYSQL_TYPE_BIT;
3606 self->bind[i].buffer= NULL;
3607 self->bind[i].buffer_length= 0;
3608 break;
3609 case MYSQL_TYPE_TINY:
3610 case MYSQL_TYPE_SHORT:
3611 case MYSQL_TYPE_LONG:
3612 case MYSQL_TYPE_INT24:
3613 case MYSQL_TYPE_YEAR:
3614 #if LONG_MAX >= INT64_T_MAX
3615 case MYSQL_TYPE_LONGLONG:
3616 self->bind[i].buffer_type= MYSQL_TYPE_LONGLONG;
3617 #else
3618 self->bind[i].buffer_type= MYSQL_TYPE_LONG;
3619 #endif
3620 self->bind[i].buffer= &self->cols[i].small_buffer.l;
3621 self->bind[i].buffer_length= sizeof(long);
3622 break;
3623 case MYSQL_TYPE_FLOAT:
3624 self->bind[i].buffer_type= MYSQL_TYPE_FLOAT;
3625 self->bind[i].buffer= &self->cols[i].small_buffer.f;
3626 self->bind[i].buffer_length= sizeof(float);
3627 break;
3628 case MYSQL_TYPE_DOUBLE:
3629 self->bind[i].buffer_type= MYSQL_TYPE_DOUBLE;
3630 self->bind[i].buffer= &self->cols[i].small_buffer.d;
3631 self->bind[i].buffer_length= sizeof(double);
3632 break;
3633 default:
3634 self->bind[i].buffer_type= MYSQL_TYPE_STRING;
3635 self->bind[i].buffer= NULL;
3636 self->bind[i].buffer_length= 0;
3637 break;
3638 }
3639 self->bind[i].length= &self->cols[i].length;
3640 self->bind[i].is_null= &self->cols[i].is_null;
3641 self->bind[i].error= &self->cols[i].is_error;
3642 }
3643 Py_END_ALLOW_THREADS
3644
3645 if (mysql_stmt_bind_result(self->stmt, self->bind))
3646 {
3647 mysql_free_result(self->res);
3648 free(self->cols);
3649 free(self->bind);
3650 PyErr_SetString(MySQLInterfaceError, mysql_stmt_error(self->stmt));
3651 return NULL;
3652 }
3653
3654 mysql_field_seek(self->res, 0);
3655 self->fields= MySQLPrepStmt_fetch_fields(self);
3656
3657 Py_RETURN_TRUE;
3658 }
3659
3660 /**
3661 Fetch the next row from the active result.
3662
3663 Fetch the next row from the active result. The row is returned
3664 as a tuple which contains the values converted to Python types,
3665 unless raw was set.
3666
3667 The returned PyObject is a PyList which consists of
3668 PyTuple objects.
3669
3670 Raises MySQLInterfaceError for any MySQL error returned
3671 by the MySQL server.
3672
3673 @param self MySQLPrepStmt instance
3674
3675 @return PyTuple with row values.
3676 @retval PyTuple OK
3677 @retval PyNone No row available
3678 @retval NULL Exception
3679 */
3680 PyObject*
MySQLPrepStmt_fetch_row(MySQLPrepStmt * self)3681 MySQLPrepStmt_fetch_row(MySQLPrepStmt *self)
3682 {
3683 PyObject *obj;
3684 PyObject *row;
3685 PyObject *field_info;
3686 PyObject *mod_decimal, *decimal, *dec_args;
3687 unsigned long field_flags;
3688 unsigned int i= 0;
3689 int fetch= 0;
3690
3691 row= PyTuple_New(self->column_count);
3692
3693 mysql_field_seek(self->res, 0);
3694 for (i= 0; i < self->column_count; ++i) {
3695 MYSQL_FIELD *field= mysql_fetch_field(self->res);
3696 switch (field->type) {
3697 case MYSQL_TYPE_NULL:
3698 case MYSQL_TYPE_TINY:
3699 case MYSQL_TYPE_SHORT:
3700 case MYSQL_TYPE_INT24:
3701 case MYSQL_TYPE_LONG:
3702 case MYSQL_TYPE_LONGLONG:
3703 case MYSQL_TYPE_YEAR:
3704 case MYSQL_TYPE_FLOAT:
3705 case MYSQL_TYPE_DOUBLE:
3706 break;
3707 default:
3708 self->bind[i].buffer= NULL;
3709 self->bind[i].buffer_length= 0;
3710 self->cols[i].length= 0;
3711 }
3712 }
3713
3714 /* Fetch to get real size */
3715 Py_BEGIN_ALLOW_THREADS
3716 fetch= mysql_stmt_fetch(self->stmt);
3717 Py_END_ALLOW_THREADS
3718
3719 if (fetch == 1) {
3720 PyErr_Format(MySQLInterfaceError,
3721 (const char*)"Error while fetching: %s",
3722 mysql_stmt_error(self->stmt));
3723 goto cleanup;
3724 } else if (fetch == MYSQL_NO_DATA) {
3725 Py_XDECREF(row);
3726 Py_RETURN_NONE;
3727 }
3728
3729 mysql_field_seek(self->res, 0);
3730 for (i= 0; i < self->column_count; ++i) {
3731 MYSQL_FIELD *field;
3732 Py_BEGIN_ALLOW_THREADS
3733 field= mysql_fetch_field(self->res);
3734 Py_END_ALLOW_THREADS
3735
3736 if (self->cols[i].is_null)
3737 {
3738 Py_INCREF(Py_None);
3739 PyTuple_SET_ITEM(row, i, Py_None);
3740 continue;
3741 }
3742
3743 if (self->fields == NULL)
3744 {
3745 self->fields= MySQLPrepStmt_fetch_fields(self);
3746 }
3747
3748 field_info= PyList_GetItem(self->fields, i);
3749 if (!field_info)
3750 {
3751 PyErr_SetString(PyExc_ValueError,
3752 (const char*)"Error while fetching field information");
3753 goto cleanup;
3754 }
3755 field_flags= PyLong_AsUnsignedLong(PyTuple_GetItem(field_info, 9));
3756
3757 switch (field->type) {
3758 case MYSQL_TYPE_TINY:
3759 case MYSQL_TYPE_SHORT:
3760 case MYSQL_TYPE_INT24:
3761 case MYSQL_TYPE_LONG:
3762 #if LONG_MAX >= INT64_T_MAX
3763 case MYSQL_TYPE_LONGLONG:
3764 #endif
3765 case MYSQL_TYPE_YEAR:
3766 PyTuple_SET_ITEM(
3767 row, i, PyLong_FromLong(self->cols[i].small_buffer.l));
3768 break;
3769 case MYSQL_TYPE_FLOAT:
3770 PyTuple_SET_ITEM(
3771 row, i, PyFloat_FromDouble(self->cols[i].small_buffer.f));
3772 break;
3773 case MYSQL_TYPE_DOUBLE:
3774 PyTuple_SET_ITEM(
3775 row, i, PyFloat_FromDouble(self->cols[i].small_buffer.d));
3776 break;
3777 case MYSQL_TYPE_DATETIME:
3778 case MYSQL_TYPE_TIMESTAMP:
3779 case MYSQL_TYPE_DATE:
3780 case MYSQL_TYPE_TIME:
3781 case MYSQL_TYPE_DECIMAL:
3782 case MYSQL_TYPE_NEWDECIMAL:
3783 obj= PyBytes_FromStringAndSize(NULL, self->cols[i].length);
3784 self->bind[i].buffer= PyBytes_AsString(obj);
3785 self->bind[i].buffer_length= self->cols[i].length;
3786
3787 Py_BEGIN_ALLOW_THREADS
3788 mysql_stmt_fetch_column(self->stmt, &self->bind[i], i, 0);
3789 Py_END_ALLOW_THREADS
3790
3791 if (self->cols[i].is_error)
3792 {
3793 PyErr_SetString(MySQLInterfaceError,
3794 mysql_stmt_error(self->stmt));
3795 goto cleanup;
3796 }
3797
3798 if (field->type == MYSQL_TYPE_DATE)
3799 {
3800 PyTuple_SET_ITEM(row, i, mytopy_date(PyBytes_AsString(obj)));
3801 }
3802 else if (field->type == MYSQL_TYPE_TIME)
3803 {
3804 PyTuple_SET_ITEM(row, i,
3805 mytopy_time(PyBytes_AsString(obj), self->cols[i].length));
3806 }
3807 else if (field->type == MYSQL_TYPE_DATETIME ||
3808 field->type == MYSQL_TYPE_TIMESTAMP)
3809 {
3810 PyTuple_SET_ITEM( row, i,
3811 mytopy_datetime(PyBytes_AsString(obj), self->cols[i].length));
3812 }
3813 else /* MYSQL_TYPE_DECIMAL or MYSQL_TYPE_NEWDECIMAL */
3814 {
3815 mod_decimal= PyImport_ImportModule("decimal");
3816 if (mod_decimal)
3817 {
3818 dec_args= PyTuple_New(1);
3819 PyTuple_SET_ITEM(dec_args, 0,
3820 PyUnicode_FromString(PyBytes_AsString(obj)));
3821 decimal= PyObject_GetAttrString(mod_decimal, "Decimal");
3822 PyTuple_SET_ITEM(row, i,
3823 PyObject_Call(decimal, dec_args, NULL));
3824 Py_DECREF(dec_args);
3825 Py_DECREF(decimal);
3826 }
3827 Py_XDECREF(mod_decimal);
3828 }
3829 break;
3830 /* MYSQL_TYPE_CHAR, MYSQL_TYPE_VARCHAR, MYSQL_TYPE_STRING, */
3831 /* MYSQL_TYPE_VAR_STRING, MYSQL_TYPE_GEOMETRY, MYSQL_TYPE_BLOB */
3832 /* MYSQL_TYPE_ENUM, MYSQL_TYPE_SET or MYSQL_TYPE_BIT */
3833 default:
3834 if (field_flags & SET_FLAG) /* MYSQL_TYPE_SET */
3835 {
3836 char *rest= NULL;
3837 char *token;
3838 PyObject* set= PySet_New(NULL);
3839
3840 obj= PyBytes_FromStringAndSize(NULL, self->cols[i].length);
3841 self->bind[i].buffer= PyBytes_AsString(obj);
3842 self->bind[i].buffer_length= self->cols[i].length;
3843
3844 Py_BEGIN_ALLOW_THREADS
3845 mysql_stmt_fetch_column(self->stmt, &self->bind[i], i, 0);
3846 Py_END_ALLOW_THREADS
3847
3848 if (self->cols[i].is_error)
3849 {
3850 PyErr_SetString(MySQLInterfaceError,
3851 mysql_stmt_error(self->stmt));
3852 goto cleanup;
3853 }
3854
3855 for (token= strtok_r(PyBytes_AsString(obj), ",", &rest);
3856 token != NULL;
3857 token= strtok_r(NULL, ",", &rest))
3858 {
3859 PyObject *us= PyUnicode_FromString(token);
3860 PySet_Add(set, us);
3861 Py_DECREF(us);
3862 }
3863 PyTuple_SET_ITEM(row, i, set);
3864 Py_XDECREF(obj);
3865 }
3866 else if (field->type == MYSQL_TYPE_GEOMETRY)
3867 {
3868 obj= PyByteArray_FromStringAndSize(NULL, self->cols[i].length);
3869 self->bind[i].buffer= PyByteArray_AsString(obj);
3870 self->bind[i].buffer_length= self->cols[i].length;
3871
3872 Py_BEGIN_ALLOW_THREADS
3873 mysql_stmt_fetch_column(self->stmt, &self->bind[i], i, 0);
3874 Py_END_ALLOW_THREADS
3875
3876 if (self->cols[i].is_error)
3877 {
3878 PyErr_SetString(MySQLInterfaceError,
3879 mysql_stmt_error(self->stmt));
3880 goto cleanup;
3881 }
3882
3883 PyTuple_SET_ITEM(row, i, obj);
3884 }
3885 else
3886 {
3887 obj= PyBytes_FromStringAndSize(NULL, self->cols[i].length);
3888 self->bind[i].buffer= PyBytes_AsString(obj);
3889 self->bind[i].buffer_length= self->cols[i].length;
3890
3891 Py_BEGIN_ALLOW_THREADS
3892 mysql_stmt_fetch_column(self->stmt, &self->bind[i], i, 0);
3893 Py_END_ALLOW_THREADS
3894
3895 if (self->cols[i].is_error)
3896 {
3897 PyErr_SetString(MySQLInterfaceError,
3898 mysql_stmt_error(self->stmt));
3899 goto cleanup;
3900 }
3901
3902 if (field->type == MYSQL_TYPE_BIT)
3903 {
3904 PyTuple_SET_ITEM(row, i,
3905 mytopy_bit(PyBytes_AsString(obj), self->cols[i].length));
3906 }
3907 else if (field->charsetnr == 63) /* 'binary' charset */
3908 {
3909 PyTuple_SET_ITEM(row, i, PyByteArray_FromObject(obj));
3910 }
3911 else
3912 {
3913 PyTuple_SET_ITEM(row, i, PyUnicode_FromString(PyBytes_AsString(obj)));
3914 }
3915 Py_XDECREF(obj);
3916 }
3917 break;
3918 }
3919 }
3920
3921 return row;
3922
3923 cleanup:
3924 Py_XDECREF(row);
3925 return NULL;
3926 }
3927
3928 /**
3929 Fetch column information for active MySQL Statement result.
3930
3931 The returned PyObject is a PyList which consists of
3932 PyTuple objects.
3933
3934 Raises MySQLInterfaceError for any MySQL error returned
3935 by the MySQL server.
3936
3937 @param self MySQLPrepStmt instance
3938
3939 @return PyList of PyTuple objects
3940 @retval PyList OK
3941 @retval NULL Exception
3942 */
3943 PyObject*
MySQLPrepStmt_fetch_fields(MySQLPrepStmt * self)3944 MySQLPrepStmt_fetch_fields(MySQLPrepStmt *self)
3945 {
3946 unsigned int num_fields;
3947
3948 if (!self->res)
3949 {
3950 PyErr_SetString(MySQLInterfaceError, "No result");
3951 return NULL;
3952 }
3953
3954 if (self->fields)
3955 {
3956 Py_INCREF(self->fields);
3957 return self->fields;
3958 }
3959
3960 Py_BEGIN_ALLOW_THREADS
3961 num_fields= mysql_num_fields(self->res);
3962 Py_END_ALLOW_THREADS
3963
3964 return fetch_fields(self->res, num_fields, &self->cs, self->use_unicode);
3965 }
3966
3967 /**
3968 Resets the prepared statement.
3969
3970 Resets a prepared statement on client and server to state after prepare.
3971
3972 @param self MySQLPrepStmt instance
3973
3974 @return None
3975 @retval Py_None OK
3976 */
3977 PyObject*
MySQLPrepStmt_reset(MySQLPrepStmt * self)3978 MySQLPrepStmt_reset(MySQLPrepStmt *self)
3979 {
3980 int res= 0;
3981
3982 if (self->stmt)
3983 {
3984 Py_BEGIN_ALLOW_THREADS
3985 res= mysql_stmt_reset(self->stmt);
3986 Py_END_ALLOW_THREADS
3987 if (res)
3988 {
3989 PyErr_SetString(MySQLInterfaceError, mysql_stmt_error(self->stmt));
3990 return NULL;
3991 }
3992 }
3993
3994 Py_RETURN_NONE;
3995 }
3996
3997 /**
3998 Closes the prepared statement.
3999
4000 @param self MySQLPrepStmt instance
4001
4002 @return None
4003 @retval Py_None OK
4004 */
4005 PyObject*
MySQLPrepStmt_close(MySQLPrepStmt * self)4006 MySQLPrepStmt_close(MySQLPrepStmt *self)
4007 {
4008 int res= 0;
4009
4010 if (!self->stmt)
4011 {
4012 PyErr_SetString(MySQLInterfaceError, mysql_stmt_error(self->stmt));
4013 return NULL;
4014 }
4015
4016 MySQLPrepStmt_free_result(self);
4017
4018 Py_BEGIN_ALLOW_THREADS
4019 res= mysql_stmt_close(self->stmt);
4020 Py_END_ALLOW_THREADS
4021
4022 if (res)
4023 {
4024 PyErr_SetString(MySQLInterfaceError, mysql_stmt_error(self->stmt));
4025 return NULL;
4026 }
4027
4028 Py_RETURN_NONE;
4029 }
4030
4031 /**
4032 Free stored result.
4033
4034 Releases memory associated with the result set produced by execution
4035 of the prepared statement.
4036
4037 @param self MySQLPrepStmt instance
4038
4039 @return None
4040 @retval Py_None OK
4041 */
4042 PyObject *
MySQLPrepStmt_free_result(MySQLPrepStmt * self)4043 MySQLPrepStmt_free_result(MySQLPrepStmt *self)
4044 {
4045 if (self->res)
4046 {
4047 Py_BEGIN_ALLOW_THREADS
4048 mysql_stmt_free_result(self->stmt);
4049 Py_END_ALLOW_THREADS
4050 }
4051
4052 Py_XDECREF(self->fields);
4053 self->fields= NULL;
4054 self->res= NULL;
4055 self->have_result_set= Py_False;
4056
4057 Py_RETURN_NONE;
4058 }
4059