Skip to content

Commit 0920b61

Browse files
author
Erlend Egeberg Aasland
authored
bpo-45512: Use Argument Clinic to set sqlite3 isolation level (GH-29593)
1 parent df3e53d commit 0920b61

File tree

2 files changed

+67
-72
lines changed

2 files changed

+67
-72
lines changed

Modules/_sqlite/clinic/connection.c.h

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,22 +63,7 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs)
6363
}
6464
}
6565
if (fastargs[3]) {
66-
if (fastargs[3] == Py_None) {
67-
isolation_level = NULL;
68-
}
69-
else if (PyUnicode_Check(fastargs[3])) {
70-
Py_ssize_t isolation_level_length;
71-
isolation_level = PyUnicode_AsUTF8AndSize(fastargs[3], &isolation_level_length);
72-
if (isolation_level == NULL) {
73-
goto exit;
74-
}
75-
if (strlen(isolation_level) != (size_t)isolation_level_length) {
76-
PyErr_SetString(PyExc_ValueError, "embedded null character");
77-
goto exit;
78-
}
79-
}
80-
else {
81-
_PyArg_BadArgument("Connection", "argument 'isolation_level'", "str or None", fastargs[3]);
66+
if (!isolation_level_converter(fastargs[3], &isolation_level)) {
8267
goto exit;
8368
}
8469
if (!--noptargs) {
@@ -851,4 +836,4 @@ getlimit(pysqlite_Connection *self, PyObject *arg)
851836
#ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
852837
#define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
853838
#endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */
854-
/*[clinic end generated code: output=6f267f20e77f92d0 input=a9049054013a1b77]*/
839+
/*[clinic end generated code: output=c2faf6563397091b input=a9049054013a1b77]*/

Modules/_sqlite/connection.c

Lines changed: 65 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,60 @@
3333
#define HAVE_TRACE_V2
3434
#endif
3535

36+
static const char *
37+
get_isolation_level(const char *level)
38+
{
39+
assert(level != NULL);
40+
static const char *const allowed_levels[] = {
41+
"",
42+
"DEFERRED",
43+
"IMMEDIATE",
44+
"EXCLUSIVE",
45+
NULL
46+
};
47+
for (int i = 0; allowed_levels[i] != NULL; i++) {
48+
const char *candidate = allowed_levels[i];
49+
if (sqlite3_stricmp(level, candidate) == 0) {
50+
return candidate;
51+
}
52+
}
53+
PyErr_SetString(PyExc_ValueError,
54+
"isolation_level string must be '', 'DEFERRED', "
55+
"'IMMEDIATE', or 'EXCLUSIVE'");
56+
return NULL;
57+
}
58+
59+
static int
60+
isolation_level_converter(PyObject *str_or_none, const char **result)
61+
{
62+
if (Py_IsNone(str_or_none)) {
63+
*result = NULL;
64+
}
65+
else if (PyUnicode_Check(str_or_none)) {
66+
Py_ssize_t sz;
67+
const char *str = PyUnicode_AsUTF8AndSize(str_or_none, &sz);
68+
if (str == NULL) {
69+
return 0;
70+
}
71+
if (strlen(str) != (size_t)sz) {
72+
PyErr_SetString(PyExc_ValueError, "embedded null character");
73+
return 0;
74+
}
75+
76+
const char *level = get_isolation_level(str);
77+
if (level == NULL) {
78+
return 0;
79+
}
80+
*result = level;
81+
}
82+
else {
83+
PyErr_SetString(PyExc_TypeError,
84+
"isolation_level must be str or None");
85+
return 0;
86+
}
87+
return 1;
88+
}
89+
3690
static int
3791
clinic_fsconverter(PyObject *pathlike, const char **result)
3892
{
@@ -100,29 +154,6 @@ new_statement_cache(pysqlite_Connection *self, pysqlite_state *state,
100154
return res;
101155
}
102156

103-
static const char *
104-
get_isolation_level(const char *level)
105-
{
106-
assert(level != NULL);
107-
static const char *const allowed_levels[] = {
108-
"",
109-
"DEFERRED",
110-
"IMMEDIATE",
111-
"EXCLUSIVE",
112-
NULL
113-
};
114-
for (int i = 0; allowed_levels[i] != NULL; i++) {
115-
const char *candidate = allowed_levels[i];
116-
if (sqlite3_stricmp(level, candidate) == 0) {
117-
return candidate;
118-
}
119-
}
120-
PyErr_SetString(PyExc_ValueError,
121-
"isolation_level string must be '', 'DEFERRED', "
122-
"'IMMEDIATE', or 'EXCLUSIVE'");
123-
return NULL;
124-
}
125-
126157
/*[python input]
127158
class FSConverter_converter(CConverter):
128159
type = "const char *"
@@ -131,16 +162,21 @@ class FSConverter_converter(CConverter):
131162
self.c_default = "NULL"
132163
def cleanup(self):
133164
return f"PyMem_Free((void *){self.name});\n"
165+
166+
class IsolationLevel_converter(CConverter):
167+
type = "const char *"
168+
converter = "isolation_level_converter"
169+
134170
[python start generated code]*/
135-
/*[python end generated code: output=da39a3ee5e6b4b0d input=7b3be538bc4058c0]*/
171+
/*[python end generated code: output=da39a3ee5e6b4b0d input=be142323885672ab]*/
136172

137173
/*[clinic input]
138174
_sqlite3.Connection.__init__ as pysqlite_connection_init
139175
140176
database: FSConverter
141177
timeout: double = 5.0
142178
detect_types: int = 0
143-
isolation_level: str(accept={str, NoneType}) = ""
179+
isolation_level: IsolationLevel = ""
144180
check_same_thread: bool(accept={int}) = True
145181
factory: object(c_default='(PyObject*)clinic_state()->ConnectionType') = ConnectionType
146182
cached_statements as cache_size: int = 128
@@ -153,7 +189,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
153189
int detect_types, const char *isolation_level,
154190
int check_same_thread, PyObject *factory,
155191
int cache_size, int uri)
156-
/*[clinic end generated code: output=7d640ae1d83abfd4 input=35e316f66d9f70fd]*/
192+
/*[clinic end generated code: output=7d640ae1d83abfd4 input=342173993434ba1e]*/
157193
{
158194
if (PySys_Audit("sqlite3.connect", "s", database) < 0) {
159195
return -1;
@@ -189,15 +225,6 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
189225
return -1;
190226
}
191227

192-
// Convert isolation level to begin statement.
193-
const char *level = NULL;
194-
if (isolation_level != NULL) {
195-
level = get_isolation_level(isolation_level);
196-
if (level == NULL) {
197-
return -1;
198-
}
199-
}
200-
201228
// Create LRU statement cache; returns a new reference.
202229
PyObject *statement_cache = new_statement_cache(self, state, cache_size);
203230
if (statement_cache == NULL) {
@@ -215,7 +242,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self,
215242
self->db = db;
216243
self->state = state;
217244
self->detect_types = detect_types;
218-
self->isolation_level = level;
245+
self->isolation_level = isolation_level;
219246
self->check_same_thread = check_same_thread;
220247
self->thread_ident = PyThread_get_thread_ident();
221248
self->statement_cache = statement_cache;
@@ -1375,26 +1402,9 @@ pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* iso
13751402
return -1;
13761403
}
13771404
Py_DECREF(res);
1405+
return 0;
13781406
}
1379-
else if (PyUnicode_Check(isolation_level)) {
1380-
Py_ssize_t len;
1381-
const char *cstr_level = PyUnicode_AsUTF8AndSize(isolation_level, &len);
1382-
if (cstr_level == NULL) {
1383-
return -1;
1384-
}
1385-
if (strlen(cstr_level) != (size_t)len) {
1386-
PyErr_SetString(PyExc_ValueError, "embedded null character");
1387-
return -1;
1388-
}
1389-
const char *level = get_isolation_level(cstr_level);
1390-
if (level == NULL) {
1391-
return -1;
1392-
}
1393-
self->isolation_level = level;
1394-
}
1395-
else {
1396-
PyErr_SetString(PyExc_TypeError,
1397-
"isolation_level must be str or None");
1407+
if (!isolation_level_converter(isolation_level, &self->isolation_level)) {
13981408
return -1;
13991409
}
14001410
return 0;

0 commit comments

Comments
 (0)