Skip to content

Commit 3e6019c

Browse files
gh-92206: Improve scoping of sqlite3 bind param functions (#92250)
1 parent 804f252 commit 3e6019c

File tree

3 files changed

+250
-257
lines changed

3 files changed

+250
-257
lines changed

Modules/_sqlite/cursor.c

Lines changed: 249 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,18 @@
2222
*/
2323

2424
#include "cursor.h"
25+
#include "microprotocols.h"
2526
#include "module.h"
2627
#include "util.h"
2728

29+
typedef enum {
30+
TYPE_LONG,
31+
TYPE_FLOAT,
32+
TYPE_UNICODE,
33+
TYPE_BUFFER,
34+
TYPE_UNKNOWN
35+
} parameter_type;
36+
2837
#define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self)))
2938
#include "clinic/cursor.c.h"
3039
#undef clinic_state
@@ -506,6 +515,245 @@ stmt_step(sqlite3_stmt *statement)
506515
return rc;
507516
}
508517

518+
static int
519+
bind_param(pysqlite_Statement *self, int pos, PyObject *parameter)
520+
{
521+
int rc = SQLITE_OK;
522+
const char *string;
523+
Py_ssize_t buflen;
524+
parameter_type paramtype;
525+
526+
if (parameter == Py_None) {
527+
rc = sqlite3_bind_null(self->st, pos);
528+
goto final;
529+
}
530+
531+
if (PyLong_CheckExact(parameter)) {
532+
paramtype = TYPE_LONG;
533+
} else if (PyFloat_CheckExact(parameter)) {
534+
paramtype = TYPE_FLOAT;
535+
} else if (PyUnicode_CheckExact(parameter)) {
536+
paramtype = TYPE_UNICODE;
537+
} else if (PyLong_Check(parameter)) {
538+
paramtype = TYPE_LONG;
539+
} else if (PyFloat_Check(parameter)) {
540+
paramtype = TYPE_FLOAT;
541+
} else if (PyUnicode_Check(parameter)) {
542+
paramtype = TYPE_UNICODE;
543+
} else if (PyObject_CheckBuffer(parameter)) {
544+
paramtype = TYPE_BUFFER;
545+
} else {
546+
paramtype = TYPE_UNKNOWN;
547+
}
548+
549+
switch (paramtype) {
550+
case TYPE_LONG: {
551+
sqlite_int64 value = _pysqlite_long_as_int64(parameter);
552+
if (value == -1 && PyErr_Occurred())
553+
rc = -1;
554+
else
555+
rc = sqlite3_bind_int64(self->st, pos, value);
556+
break;
557+
}
558+
case TYPE_FLOAT: {
559+
double value = PyFloat_AsDouble(parameter);
560+
if (value == -1 && PyErr_Occurred()) {
561+
rc = -1;
562+
}
563+
else {
564+
rc = sqlite3_bind_double(self->st, pos, value);
565+
}
566+
break;
567+
}
568+
case TYPE_UNICODE:
569+
string = PyUnicode_AsUTF8AndSize(parameter, &buflen);
570+
if (string == NULL)
571+
return -1;
572+
if (buflen > INT_MAX) {
573+
PyErr_SetString(PyExc_OverflowError,
574+
"string longer than INT_MAX bytes");
575+
return -1;
576+
}
577+
rc = sqlite3_bind_text(self->st, pos, string, (int)buflen, SQLITE_TRANSIENT);
578+
break;
579+
case TYPE_BUFFER: {
580+
Py_buffer view;
581+
if (PyObject_GetBuffer(parameter, &view, PyBUF_SIMPLE) != 0) {
582+
return -1;
583+
}
584+
if (view.len > INT_MAX) {
585+
PyErr_SetString(PyExc_OverflowError,
586+
"BLOB longer than INT_MAX bytes");
587+
PyBuffer_Release(&view);
588+
return -1;
589+
}
590+
rc = sqlite3_bind_blob(self->st, pos, view.buf, (int)view.len, SQLITE_TRANSIENT);
591+
PyBuffer_Release(&view);
592+
break;
593+
}
594+
case TYPE_UNKNOWN:
595+
rc = -1;
596+
}
597+
598+
final:
599+
return rc;
600+
}
601+
602+
/* returns 0 if the object is one of Python's internal ones that don't need to be adapted */
603+
static inline int
604+
need_adapt(pysqlite_state *state, PyObject *obj)
605+
{
606+
if (state->BaseTypeAdapted) {
607+
return 1;
608+
}
609+
610+
if (PyLong_CheckExact(obj) || PyFloat_CheckExact(obj)
611+
|| PyUnicode_CheckExact(obj) || PyByteArray_CheckExact(obj)) {
612+
return 0;
613+
} else {
614+
return 1;
615+
}
616+
}
617+
618+
static void
619+
bind_parameters(pysqlite_state *state, pysqlite_Statement *self,
620+
PyObject *parameters)
621+
{
622+
PyObject* current_param;
623+
PyObject* adapted;
624+
const char* binding_name;
625+
int i;
626+
int rc;
627+
int num_params_needed;
628+
Py_ssize_t num_params;
629+
630+
Py_BEGIN_ALLOW_THREADS
631+
num_params_needed = sqlite3_bind_parameter_count(self->st);
632+
Py_END_ALLOW_THREADS
633+
634+
if (PyTuple_CheckExact(parameters) || PyList_CheckExact(parameters) || (!PyDict_Check(parameters) && PySequence_Check(parameters))) {
635+
/* parameters passed as sequence */
636+
if (PyTuple_CheckExact(parameters)) {
637+
num_params = PyTuple_GET_SIZE(parameters);
638+
} else if (PyList_CheckExact(parameters)) {
639+
num_params = PyList_GET_SIZE(parameters);
640+
} else {
641+
num_params = PySequence_Size(parameters);
642+
if (num_params == -1) {
643+
return;
644+
}
645+
}
646+
if (num_params != num_params_needed) {
647+
PyErr_Format(state->ProgrammingError,
648+
"Incorrect number of bindings supplied. The current "
649+
"statement uses %d, and there are %zd supplied.",
650+
num_params_needed, num_params);
651+
return;
652+
}
653+
for (i = 0; i < num_params; i++) {
654+
if (PyTuple_CheckExact(parameters)) {
655+
PyObject *item = PyTuple_GET_ITEM(parameters, i);
656+
current_param = Py_NewRef(item);
657+
} else if (PyList_CheckExact(parameters)) {
658+
PyObject *item = PyList_GetItem(parameters, i);
659+
current_param = Py_XNewRef(item);
660+
} else {
661+
current_param = PySequence_GetItem(parameters, i);
662+
}
663+
if (!current_param) {
664+
return;
665+
}
666+
667+
if (!need_adapt(state, current_param)) {
668+
adapted = current_param;
669+
} else {
670+
PyObject *protocol = (PyObject *)state->PrepareProtocolType;
671+
adapted = pysqlite_microprotocols_adapt(state, current_param,
672+
protocol,
673+
current_param);
674+
Py_DECREF(current_param);
675+
if (!adapted) {
676+
return;
677+
}
678+
}
679+
680+
rc = bind_param(self, i + 1, adapted);
681+
Py_DECREF(adapted);
682+
683+
if (rc != SQLITE_OK) {
684+
if (!PyErr_Occurred()) {
685+
PyErr_Format(state->InterfaceError,
686+
"Error binding parameter %d - "
687+
"probably unsupported type.", i);
688+
}
689+
return;
690+
}
691+
}
692+
} else if (PyDict_Check(parameters)) {
693+
/* parameters passed as dictionary */
694+
for (i = 1; i <= num_params_needed; i++) {
695+
PyObject *binding_name_obj;
696+
Py_BEGIN_ALLOW_THREADS
697+
binding_name = sqlite3_bind_parameter_name(self->st, i);
698+
Py_END_ALLOW_THREADS
699+
if (!binding_name) {
700+
PyErr_Format(state->ProgrammingError,
701+
"Binding %d has no name, but you supplied a "
702+
"dictionary (which has only names).", i);
703+
return;
704+
}
705+
706+
binding_name++; /* skip first char (the colon) */
707+
binding_name_obj = PyUnicode_FromString(binding_name);
708+
if (!binding_name_obj) {
709+
return;
710+
}
711+
if (PyDict_CheckExact(parameters)) {
712+
PyObject *item = PyDict_GetItemWithError(parameters, binding_name_obj);
713+
current_param = Py_XNewRef(item);
714+
} else {
715+
current_param = PyObject_GetItem(parameters, binding_name_obj);
716+
}
717+
Py_DECREF(binding_name_obj);
718+
if (!current_param) {
719+
if (!PyErr_Occurred() || PyErr_ExceptionMatches(PyExc_LookupError)) {
720+
PyErr_Format(state->ProgrammingError,
721+
"You did not supply a value for binding "
722+
"parameter :%s.", binding_name);
723+
}
724+
return;
725+
}
726+
727+
if (!need_adapt(state, current_param)) {
728+
adapted = current_param;
729+
} else {
730+
PyObject *protocol = (PyObject *)state->PrepareProtocolType;
731+
adapted = pysqlite_microprotocols_adapt(state, current_param,
732+
protocol,
733+
current_param);
734+
Py_DECREF(current_param);
735+
if (!adapted) {
736+
return;
737+
}
738+
}
739+
740+
rc = bind_param(self, i, adapted);
741+
Py_DECREF(adapted);
742+
743+
if (rc != SQLITE_OK) {
744+
if (!PyErr_Occurred()) {
745+
PyErr_Format(state->InterfaceError,
746+
"Error binding parameter :%s - "
747+
"probably unsupported type.", binding_name);
748+
}
749+
return;
750+
}
751+
}
752+
} else {
753+
PyErr_SetString(PyExc_ValueError, "parameters are of unsupported type");
754+
}
755+
}
756+
509757
PyObject *
510758
_pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation, PyObject* second_argument)
511759
{
@@ -617,7 +865,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* operation
617865

618866
pysqlite_statement_mark_dirty(self->statement);
619867

620-
pysqlite_statement_bind_parameters(state, self->statement, parameters);
868+
bind_parameters(state, self->statement, parameters);
621869
if (PyErr_Occurred()) {
622870
goto error;
623871
}

0 commit comments

Comments
 (0)