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