From 819260603cb51a849e35565d1b2e2c35de890e5d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 14 Jun 2020 21:09:30 +0200 Subject: [PATCH 01/13] Convert _sqlite3.connect() and _sqlite3.Connection.__init__() to Argument Clinic --- Modules/_sqlite/clinic/connection.c.h | 103 ++++++++++++++++++++++- Modules/_sqlite/clinic/module.c.h | 114 +++++++++++++++++++++++++- Modules/_sqlite/connection.c | 47 +++++------ Modules/_sqlite/module.c | 102 ++++++++++++++--------- 4 files changed, 298 insertions(+), 68 deletions(-) diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index f231ecc2ae78be..b45922016f97a1 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -2,6 +2,107 @@ preserve [clinic start generated code]*/ +static int +pysqlite_connection_init_impl(pysqlite_Connection *self, + PyObject *database_obj, double timeout, + int detect_types, PyObject *isolation_level, + int check_same_thread, PyObject *factory, + int cached_statements, int uri); + +static int +pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + static const char * const _keywords[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", "uri", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "Connection", 0}; + PyObject *argsbuf[8]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + PyObject *database_obj; + double timeout = 5.0; + int detect_types = 0; + PyObject *isolation_level = NULL; + int check_same_thread = 1; + PyObject *factory = (PyObject*)pysqlite_ConnectionType; + int cached_statements = 100; + int uri = 0; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 8, 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!PyUnicode_FSConverter(fastargs[0], &database_obj)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (fastargs[1]) { + if (PyFloat_CheckExact(fastargs[1])) { + timeout = PyFloat_AS_DOUBLE(fastargs[1]); + } + else + { + timeout = PyFloat_AsDouble(fastargs[1]); + if (timeout == -1.0 && PyErr_Occurred()) { + goto exit; + } + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[2]) { + detect_types = _PyLong_AsInt(fastargs[2]); + if (detect_types == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[3]) { + isolation_level = fastargs[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[4]) { + check_same_thread = _PyLong_AsInt(fastargs[4]); + if (check_same_thread == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[5]) { + factory = fastargs[5]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (fastargs[6]) { + cached_statements = _PyLong_AsInt(fastargs[6]); + if (cached_statements == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + uri = PyObject_IsTrue(fastargs[7]); + if (uri < 0) { + goto exit; + } +skip_optional_pos: + return_value = pysqlite_connection_init_impl((pysqlite_Connection *)self, database_obj, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri); + +exit: + return return_value; +} + PyDoc_STRVAR(pysqlite_connection_cursor__doc__, "cursor($self, /, factory=)\n" "--\n" @@ -710,4 +811,4 @@ pysqlite_connection_exit(pysqlite_Connection *self, PyObject *const *args, Py_ss #ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */ -/*[clinic end generated code: output=c1bf09db3bcd0105 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=8332a2d5e3236bb3 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/module.c.h b/Modules/_sqlite/clinic/module.c.h index fb1e1187b209f8..02cc40b0c4af6c 100644 --- a/Modules/_sqlite/clinic/module.c.h +++ b/Modules/_sqlite/clinic/module.c.h @@ -2,6 +2,118 @@ preserve [clinic start generated code]*/ +PyDoc_STRVAR(pysqlite_connect__doc__, +"connect($module, /, database, timeout=5.0, detect_types=0,\n" +" isolation_level=, check_same_thread=True,\n" +" factory=ConnectionType, cached_statements=100, uri=False)\n" +"--\n" +"\n" +"Opens a connection to the SQLite database file database.\n" +"\n" +"You can use \":memory:\" to open a database connection to a database that resides\n" +"in RAM instead of on disk."); + +#define PYSQLITE_CONNECT_METHODDEF \ + {"connect", (PyCFunction)(void(*)(void))pysqlite_connect, METH_FASTCALL|METH_KEYWORDS, pysqlite_connect__doc__}, + +static PyObject * +pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, + int detect_types, PyObject *isolation_level, + int check_same_thread, PyObject *factory, + int cached_statements, int uri); + +static PyObject * +pysqlite_connect(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"database", "timeout", "detect_types", "isolation_level", "check_same_thread", "factory", "cached_statements", "uri", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "connect", 0}; + PyObject *argsbuf[8]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + PyObject *database; + double timeout = 5.0; + int detect_types = 0; + PyObject *isolation_level = NULL; + int check_same_thread = 1; + PyObject *factory = (PyObject*)pysqlite_ConnectionType; + int cached_statements = 100; + int uri = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 8, 0, argsbuf); + if (!args) { + goto exit; + } + if (!PyUnicode_FSConverter(args[0], &database)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[1]) { + if (PyFloat_CheckExact(args[1])) { + timeout = PyFloat_AS_DOUBLE(args[1]); + } + else + { + timeout = PyFloat_AsDouble(args[1]); + if (timeout == -1.0 && PyErr_Occurred()) { + goto exit; + } + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[2]) { + detect_types = _PyLong_AsInt(args[2]); + if (detect_types == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[3]) { + isolation_level = args[3]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[4]) { + check_same_thread = _PyLong_AsInt(args[4]); + if (check_same_thread == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[5]) { + factory = args[5]; + if (!--noptargs) { + goto skip_optional_pos; + } + } + if (args[6]) { + cached_statements = _PyLong_AsInt(args[6]); + if (cached_statements == -1 && PyErr_Occurred()) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + uri = PyObject_IsTrue(args[7]); + if (uri < 0) { + goto exit; + } +skip_optional_pos: + return_value = pysqlite_connect_impl(module, database, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri); + +exit: + return return_value; +} + PyDoc_STRVAR(pysqlite_complete_statement__doc__, "complete_statement($module, /, statement)\n" "--\n" @@ -219,4 +331,4 @@ pysqlite_adapt(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=d87990f941c209fa input=a9049054013a1b77]*/ +/*[clinic end generated code: output=c0ef08ee13444be8 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 28932726b74257..22913ea76efcef 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -57,40 +57,35 @@ static const char * const begin_statements[] = { static int pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored)); static void _pysqlite_drop_unused_cursor_references(pysqlite_Connection* self); + +/*[clinic input] +_sqlite3.Connection.__init__ as pysqlite_connection_init + + database as database_obj: object(converter='PyUnicode_FSConverter') + timeout: double = 5.0 + detect_types: int = 0 + isolation_level: object = NULL + check_same_thread: bool(accept={int}) = True + factory: object(c_default='(PyObject*)pysqlite_ConnectionType') = ConnectionType + cached_statements: int = 100 + uri: bool = False +[clinic start generated code]*/ + static int -pysqlite_connection_init(pysqlite_Connection *self, PyObject *args, - PyObject *kwargs) +pysqlite_connection_init_impl(pysqlite_Connection *self, + PyObject *database_obj, double timeout, + int detect_types, PyObject *isolation_level, + int check_same_thread, PyObject *factory, + int cached_statements, int uri) +/*[clinic end generated code: output=dc19df1c0e2b7b77 input=933dc59875f687ee]*/ { - static char *kwlist[] = { - "database", "timeout", "detect_types", "isolation_level", - "check_same_thread", "factory", "cached_statements", "uri", - NULL - }; - - const char* database; - PyObject* database_obj; - int detect_types = 0; - PyObject* isolation_level = NULL; - PyObject* factory = NULL; - int check_same_thread = 1; - int cached_statements = 100; - int uri = 0; - double timeout = 5.0; int rc; - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&|diOiOip", kwlist, - PyUnicode_FSConverter, &database_obj, &timeout, &detect_types, - &isolation_level, &check_same_thread, - &factory, &cached_statements, &uri)) - { - return -1; - } - if (PySys_Audit("sqlite3.connect", "O", database_obj) < 0) { return -1; } - database = PyBytes_AsString(database_obj); + const char *database = PyBytes_AsString(database_obj); self->initialized = 1; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index a5e5525481f756..bccac242c9cd5a 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -56,50 +56,73 @@ PyObject* _pysqlite_converters = NULL; int _pysqlite_enable_callback_tracebacks = 0; int pysqlite_BaseTypeAdapted = 0; -static PyObject* module_connect(PyObject* self, PyObject* args, PyObject* - kwargs) -{ - /* Python seems to have no way of extracting a single keyword-arg at - * C-level, so this code is redundant with the one in connection_init in - * connection.c and must always be copied from there ... */ +/* Python seems to have no way of extracting a single keyword-arg at + * C-level, so this code is redundant with the one in connection_init in + * connection.c and must always be copied from there ... */ +/*[clinic input] +_sqlite3.connect as pysqlite_connect - static char *kwlist[] = { - "database", "timeout", "detect_types", "isolation_level", - "check_same_thread", "factory", "cached_statements", "uri", - NULL - }; - PyObject* database; - int detect_types = 0; - PyObject* isolation_level; - PyObject* factory = NULL; - int check_same_thread = 1; - int cached_statements; - int uri = 0; - double timeout = 5.0; - - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|diOiOip", kwlist, - &database, &timeout, &detect_types, - &isolation_level, &check_same_thread, - &factory, &cached_statements, &uri)) - { - return NULL; - } + database: object(converter='PyUnicode_FSConverter') + timeout: double = 5.0 + detect_types: int = 0 + isolation_level: object = NULL + check_same_thread: bool(accept={int}) = True + factory: object(c_default='(PyObject*)pysqlite_ConnectionType') = ConnectionType + cached_statements: int = 100 + uri: bool = False + +Opens a connection to the SQLite database file database. - if (factory == NULL) { - factory = (PyObject*)pysqlite_ConnectionType; +You can use ":memory:" to open a database connection to a database that resides +in RAM instead of on disk. +[clinic start generated code]*/ + +static PyObject * +pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, + int detect_types, PyObject *isolation_level, + int check_same_thread, PyObject *factory, + int cached_statements, int uri) +/*[clinic end generated code: output=450ac9078b4868bb input=938c5058ce37130a]*/ +{ + int decref_isolation_level = 0; + + if (isolation_level == NULL) { + isolation_level = PyUnicode_FromString(""); + if (isolation_level == NULL) { + return NULL; + } + decref_isolation_level = 1; } - return PyObject_Call(factory, args, kwargs); + PyObject *obj_timeout = PyFloat_FromDouble(timeout); + PyObject *obj_detect_types = PyLong_FromLong(detect_types); + PyObject *obj_check_same_thread = PyLong_FromLong(check_same_thread); + PyObject *obj_cached_statements = PyLong_FromLong(cached_statements); + PyObject *obj_uri = PyBool_FromLong(uri); + + PyObject *result = PyObject_CallFunctionObjArgs(factory, + database, + obj_timeout, + obj_detect_types, + isolation_level, + obj_check_same_thread, + factory, + obj_cached_statements, + obj_uri, + NULL); + if (decref_isolation_level) { + Py_DECREF(isolation_level); + } + Py_XDECREF(database); + Py_XDECREF(obj_timeout); + Py_XDECREF(obj_detect_types); + Py_XDECREF(obj_check_same_thread); + Py_XDECREF(obj_cached_statements); + Py_XDECREF(obj_uri); + + return result; } -PyDoc_STRVAR(module_connect_doc, -"connect(database[, timeout, detect_types, isolation_level,\n\ - check_same_thread, factory, cached_statements, uri])\n\ -\n\ -Opens a connection to the SQLite database file *database*. You can use\n\ -\":memory:\" to open a database connection to a database that resides in\n\ -RAM instead of on disk."); - /*[clinic input] _sqlite3.complete_statement as pysqlite_complete_statement @@ -262,10 +285,9 @@ static int converters_init(PyObject* module) } static PyMethodDef module_methods[] = { - {"connect", (PyCFunction)(void(*)(void))module_connect, - METH_VARARGS | METH_KEYWORDS, module_connect_doc}, PYSQLITE_ADAPT_METHODDEF PYSQLITE_COMPLETE_STATEMENT_METHODDEF + PYSQLITE_CONNECT_METHODDEF PYSQLITE_ENABLE_CALLBACK_TRACE_METHODDEF PYSQLITE_ENABLE_SHARED_CACHE_METHODDEF PYSQLITE_REGISTER_ADAPTER_METHODDEF From b72a29cea7c641510bcbfce7811ce61a5923d3dd Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 2 Feb 2021 21:38:46 +0100 Subject: [PATCH 02/13] Add NEWS --- .../NEWS.d/next/Library/2020-10-01-21-46-34.bpo-40956._tvsZ7.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2020-10-01-21-46-34.bpo-40956._tvsZ7.rst diff --git a/Misc/NEWS.d/next/Library/2020-10-01-21-46-34.bpo-40956._tvsZ7.rst b/Misc/NEWS.d/next/Library/2020-10-01-21-46-34.bpo-40956._tvsZ7.rst new file mode 100644 index 00000000000000..adec299d2316a8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-10-01-21-46-34.bpo-40956._tvsZ7.rst @@ -0,0 +1 @@ +Use Argument Clinic in :mod:`sqlite3`. Patches by Erlend E. Aasland. From d361aebe0313adfab572375eb07b7d70c82b475f Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 8 Mar 2021 13:13:29 +0100 Subject: [PATCH 03/13] Add test for 'check_same_thread' keyword --- Lib/sqlite3/test/dbapi.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 39c9bf5b61143d..518f8d4f7f91ec 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -680,6 +680,21 @@ def run(cur, errors): if len(errors) > 0: self.fail("\n".join(errors)) + def test_dont_check_same_thread(self): + def run(con, err): + try: + cur = con.execute("select 1") + except sqlite.Error: + err.append("multi-threading not allowed") + + con = sqlite.connect(":memory:", check_same_thread=False) + err = [] + t = threading.Thread(target=run, kwargs={"con": con, "err": err}) + t.start() + t.join() + self.assertEqual(len(err), 0, "\n".join(err)) + + class ConstructorTests(unittest.TestCase): def test_date(self): d = sqlite.Date(2004, 10, 28) From a8613583120b7771d6ca60f98cf9541477c308bf Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Mon, 8 Mar 2021 13:21:26 +0100 Subject: [PATCH 04/13] Add test for 'database' keyword --- Lib/sqlite3/test/dbapi.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 518f8d4f7f91ec..0bee2e27103ddf 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -191,6 +191,10 @@ def test_open_uri(self): with self.assertRaises(sqlite.OperationalError): cx.execute('insert into test(id) values(1)') + def test_database_keyword(self): + with sqlite.connect(database=":memory:") as cx: + self.assertEqual(type(cx), sqlite.Connection) + class CursorTests(unittest.TestCase): def setUp(self): From 07164cb3485294ba830f779b427164517235c72b Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 5 Jun 2021 22:27:05 +0200 Subject: [PATCH 05/13] Harden --- Modules/_sqlite/module.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 5517b995d2c9de..38635e9f356490 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -91,6 +91,7 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, int cached_statements, int uri) /*[clinic end generated code: output=450ac9078b4868bb input=938c5058ce37130a]*/ { + PyObject *result = NULL; int decref_isolation_level = 0; if (isolation_level == NULL) { @@ -106,17 +107,20 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, PyObject *obj_check_same_thread = PyLong_FromLong(check_same_thread); PyObject *obj_cached_statements = PyLong_FromLong(cached_statements); PyObject *obj_uri = PyBool_FromLong(uri); + if (obj_timeout == NULL || obj_detect_types == NULL || + obj_check_same_thread == NULL || obj_cached_statements == NULL || + obj_uri == NULL) + { + goto error; + } + + result = PyObject_CallFunctionObjArgs(factory, database, obj_timeout, + obj_detect_types, isolation_level, + obj_check_same_thread, factory, + obj_cached_statements, obj_uri, + NULL); - PyObject *result = PyObject_CallFunctionObjArgs(factory, - database, - obj_timeout, - obj_detect_types, - isolation_level, - obj_check_same_thread, - factory, - obj_cached_statements, - obj_uri, - NULL); +error: if (decref_isolation_level) { Py_DECREF(isolation_level); } @@ -126,7 +130,6 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, Py_XDECREF(obj_check_same_thread); Py_XDECREF(obj_cached_statements); Py_XDECREF(obj_uri); - return result; } From a1e88c912eaa9f2b1a719171fe5b2760f4a80250 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 5 Jun 2021 22:31:04 +0200 Subject: [PATCH 06/13] Simplify ref handling --- Modules/_sqlite/module.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 38635e9f356490..807bec68b3e700 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -92,14 +92,15 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, /*[clinic end generated code: output=450ac9078b4868bb input=938c5058ce37130a]*/ { PyObject *result = NULL; - int decref_isolation_level = 0; if (isolation_level == NULL) { isolation_level = PyUnicode_FromString(""); if (isolation_level == NULL) { return NULL; } - decref_isolation_level = 1; + } + else { + Py_INCREF(isolation_level); } PyObject *obj_timeout = PyFloat_FromDouble(timeout); @@ -121,9 +122,7 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, NULL); error: - if (decref_isolation_level) { - Py_DECREF(isolation_level); - } + Py_DECREF(isolation_level); Py_XDECREF(database); Py_XDECREF(obj_timeout); Py_XDECREF(obj_detect_types); From 4977d783eea356e3d5c991d00feaa51e8603c474 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 5 Jun 2021 22:57:55 +0200 Subject: [PATCH 07/13] Use PyObject_CallFunction iso. PyObject_CallFunctionObjArgs in sqlite3.connect --- Modules/_sqlite/module.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 807bec68b3e700..20138cab953b60 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -91,8 +91,6 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, int cached_statements, int uri) /*[clinic end generated code: output=450ac9078b4868bb input=938c5058ce37130a]*/ { - PyObject *result = NULL; - if (isolation_level == NULL) { isolation_level = PyUnicode_FromString(""); if (isolation_level == NULL) { @@ -102,34 +100,13 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, else { Py_INCREF(isolation_level); } - - PyObject *obj_timeout = PyFloat_FromDouble(timeout); - PyObject *obj_detect_types = PyLong_FromLong(detect_types); - PyObject *obj_check_same_thread = PyLong_FromLong(check_same_thread); - PyObject *obj_cached_statements = PyLong_FromLong(cached_statements); - PyObject *obj_uri = PyBool_FromLong(uri); - if (obj_timeout == NULL || obj_detect_types == NULL || - obj_check_same_thread == NULL || obj_cached_statements == NULL || - obj_uri == NULL) - { - goto error; - } - - result = PyObject_CallFunctionObjArgs(factory, database, obj_timeout, - obj_detect_types, isolation_level, - obj_check_same_thread, factory, - obj_cached_statements, obj_uri, - NULL); - -error: + PyObject *res = PyObject_CallFunction(factory, "OdiOiOii", database, + timeout, detect_types, + isolation_level, check_same_thread, + factory, cached_statements, uri); + Py_DECREF(database); Py_DECREF(isolation_level); - Py_XDECREF(database); - Py_XDECREF(obj_timeout); - Py_XDECREF(obj_detect_types); - Py_XDECREF(obj_check_same_thread); - Py_XDECREF(obj_cached_statements); - Py_XDECREF(obj_uri); - return result; + return res; } /*[clinic input] From 1b2720f2ee450ec42f2d2dd4c2ad4ab0a214fb49 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 5 Jun 2021 23:09:10 +0200 Subject: [PATCH 08/13] Adjust comment --- Modules/_sqlite/module.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 20138cab953b60..3dc49559363ae5 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -63,9 +63,7 @@ pysqlite_get_state(PyObject *Py_UNUSED(module)) return &pysqlite_global_state; } -/* Python seems to have no way of extracting a single keyword-arg at - * C-level, so this code is redundant with the one in connection_init in - * connection.c and must always be copied from there ... */ +// NOTE: This must equal sqlite3.Connection.__init__ argument spec! /*[clinic input] _sqlite3.connect as pysqlite_connect From 39ed36be2c6b5109987190be9b3ab2d78b4937ab Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 6 Jun 2021 22:37:07 +0200 Subject: [PATCH 09/13] Remove unused var from test_dont_check_same_thread --- Lib/sqlite3/test/dbapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index a6462e957bdcf1..32a7f7ce20115d 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -731,7 +731,7 @@ def run(cur, errors): def test_dont_check_same_thread(self): def run(con, err): try: - cur = con.execute("select 1") + con.execute("select 1") except sqlite.Error: err.append("multi-threading not allowed") From 754fdc6fc4971df09e7f36aaa33f06fee04d089d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 13 Jun 2021 23:26:36 +0200 Subject: [PATCH 10/13] Resolve conflict: cached_statements now defaults to 128, not 100 --- Modules/_sqlite/connection.c | 2 +- Modules/_sqlite/module.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index bb76cc2683e25d..309c14c208204c 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -85,7 +85,7 @@ _sqlite3.Connection.__init__ as pysqlite_connection_init isolation_level: object = NULL check_same_thread: bool(accept={int}) = True factory: object(c_default='(PyObject*)pysqlite_ConnectionType') = ConnectionType - cached_statements: int = 100 + cached_statements: int = 128 uri: bool = False [clinic start generated code]*/ diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 3dc49559363ae5..76975e7049eb44 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -73,7 +73,7 @@ _sqlite3.connect as pysqlite_connect isolation_level: object = NULL check_same_thread: bool(accept={int}) = True factory: object(c_default='(PyObject*)pysqlite_ConnectionType') = ConnectionType - cached_statements: int = 100 + cached_statements: int = 128 uri: bool = False Opens a connection to the SQLite database file database. From e59af3bb2d6cb60f61ab60ab4846f2ff35ca2ded Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 13 Jun 2021 23:32:29 +0200 Subject: [PATCH 11/13] Clinic --- Modules/_sqlite/clinic/connection.c.h | 4 ++-- Modules/_sqlite/clinic/module.c.h | 6 +++--- Modules/_sqlite/connection.c | 2 +- Modules/_sqlite/module.c | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index b45922016f97a1..25647966480b6b 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -25,7 +25,7 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs) PyObject *isolation_level = NULL; int check_same_thread = 1; PyObject *factory = (PyObject*)pysqlite_ConnectionType; - int cached_statements = 100; + int cached_statements = 128; int uri = 0; fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 8, 0, argsbuf); @@ -811,4 +811,4 @@ pysqlite_connection_exit(pysqlite_Connection *self, PyObject *const *args, Py_ss #ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF #endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */ -/*[clinic end generated code: output=8332a2d5e3236bb3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=eb6b4eda33d396f1 input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/clinic/module.c.h b/Modules/_sqlite/clinic/module.c.h index 02cc40b0c4af6c..987686942e828a 100644 --- a/Modules/_sqlite/clinic/module.c.h +++ b/Modules/_sqlite/clinic/module.c.h @@ -5,7 +5,7 @@ preserve PyDoc_STRVAR(pysqlite_connect__doc__, "connect($module, /, database, timeout=5.0, detect_types=0,\n" " isolation_level=, check_same_thread=True,\n" -" factory=ConnectionType, cached_statements=100, uri=False)\n" +" factory=ConnectionType, cached_statements=128, uri=False)\n" "--\n" "\n" "Opens a connection to the SQLite database file database.\n" @@ -36,7 +36,7 @@ pysqlite_connect(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyOb PyObject *isolation_level = NULL; int check_same_thread = 1; PyObject *factory = (PyObject*)pysqlite_ConnectionType; - int cached_statements = 100; + int cached_statements = 128; int uri = 0; args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 8, 0, argsbuf); @@ -331,4 +331,4 @@ pysqlite_adapt(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=c0ef08ee13444be8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=9f2160e2d092db1e input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 309c14c208204c..9be770c866bc85 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -95,7 +95,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, int detect_types, PyObject *isolation_level, int check_same_thread, PyObject *factory, int cached_statements, int uri) -/*[clinic end generated code: output=dc19df1c0e2b7b77 input=933dc59875f687ee]*/ +/*[clinic end generated code: output=dc19df1c0e2b7b77 input=3706141ad7f68f73]*/ { int rc; diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index 76975e7049eb44..2fe3a14c1ad1bd 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -87,7 +87,7 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, int detect_types, PyObject *isolation_level, int check_same_thread, PyObject *factory, int cached_statements, int uri) -/*[clinic end generated code: output=450ac9078b4868bb input=938c5058ce37130a]*/ +/*[clinic end generated code: output=450ac9078b4868bb input=9d37b2ca6a07ed70]*/ { if (isolation_level == NULL) { isolation_level = PyUnicode_FromString(""); From af0fc7ef7af4e12f9660e6201af1e8d584fd2629 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 16 Jun 2021 00:26:05 +0200 Subject: [PATCH 12/13] Address review: wrap test_dont_check_same_thread with threading_helper.reap_threads --- Lib/sqlite3/test/dbapi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/sqlite3/test/dbapi.py b/Lib/sqlite3/test/dbapi.py index 32a7f7ce20115d..2924231245460c 100644 --- a/Lib/sqlite3/test/dbapi.py +++ b/Lib/sqlite3/test/dbapi.py @@ -27,6 +27,7 @@ import unittest from test.support.os_helper import TESTFN, unlink +from test.support import threading_helper # Helper for tests using TESTFN @@ -728,6 +729,7 @@ def run(cur, errors): if len(errors) > 0: self.fail("\n".join(errors)) + @threading_helper.reap_threads def test_dont_check_same_thread(self): def run(con, err): try: From 98f1a9d748e71c8e777dc51230d7f2866706a2c6 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Thu, 17 Jun 2021 23:23:00 +0200 Subject: [PATCH 13/13] Explain Py_DECREF of converted arguments --- Modules/_sqlite/connection.c | 2 +- Modules/_sqlite/module.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 6a7b81afa78892..827a60d83875ac 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -128,7 +128,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, (uri ? SQLITE_OPEN_URI : 0), NULL); Py_END_ALLOW_THREADS - Py_DECREF(database_obj); + Py_DECREF(database_obj); // needed bco. the AC FSConverter if (rc != SQLITE_OK) { _pysqlite_seterror(self->db); diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c index cf546259e8af0e..6adadf69216396 100644 --- a/Modules/_sqlite/module.c +++ b/Modules/_sqlite/module.c @@ -99,7 +99,7 @@ pysqlite_connect_impl(PyObject *module, PyObject *database, double timeout, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri); - Py_DECREF(database); + Py_DECREF(database); // needed bco. the AC FSConverter Py_DECREF(isolation_level); return res; }