diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2022-05-20 08:53:05 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-05-20 08:53:05 (GMT) |
commit | d8537580921b2e02f477ff1a8dedcf82c24ef0c2 (patch) | |
tree | 6f4d48893407cdbcf9d99fa6083c470995a6334a | |
parent | 0e12781ad9dec6e46ccb85969c0eb7be1ecad81d (diff) | |
download | cpython-d8537580921b2e02f477ff1a8dedcf82c24ef0c2.zip cpython-d8537580921b2e02f477ff1a8dedcf82c24ef0c2.tar.gz cpython-d8537580921b2e02f477ff1a8dedcf82c24ef0c2.tar.bz2 |
gh-91922: Fix sqlite connection on nonstardard locales and paths (GH-92926)
-rw-r--r-- | Lib/test/test_sqlite3/test_dbapi.py | 28 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-05-18-17-18-41.gh-issue-91922.DwWIsJ.rst | 3 | ||||
-rw-r--r-- | Modules/_sqlite/clinic/connection.c.h | 17 | ||||
-rw-r--r-- | Modules/_sqlite/connection.c | 56 |
4 files changed, 40 insertions, 64 deletions
diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py index 8a21897..40f91a1 100644 --- a/Lib/test/test_sqlite3/test_dbapi.py +++ b/Lib/test/test_sqlite3/test_dbapi.py @@ -21,21 +21,19 @@ # 3. This notice may not be removed or altered from any source distribution. import contextlib +import os import sqlite3 as sqlite import subprocess import sys import threading import unittest +import urllib.parse -from test.support import ( - SHORT_TIMEOUT, - bigmemtest, - check_disallow_instantiation, - threading_helper, -) +from test.support import SHORT_TIMEOUT, bigmemtest, check_disallow_instantiation +from test.support import threading_helper from _testcapi import INT_MAX, ULLONG_MAX from os import SEEK_SET, SEEK_CUR, SEEK_END -from test.support.os_helper import TESTFN, unlink, temp_dir +from test.support.os_helper import TESTFN, TESTFN_UNDECODABLE, unlink, temp_dir, FakePath # Helper for tests using TESTFN @@ -654,11 +652,19 @@ class OpenTests(unittest.TestCase): def test_open_with_path_like_object(self): """ Checks that we can successfully connect to a database using an object that is PathLike, i.e. has __fspath__(). """ - class Path: - def __fspath__(self): - return TESTFN - path = Path() + path = FakePath(TESTFN) with managed_connect(path) as cx: + self.assertTrue(os.path.exists(path)) + cx.execute(self._sql) + + @unittest.skipIf(sys.platform == "win32", "skipped on Windows") + @unittest.skipIf(sys.platform == "darwin", "skipped on macOS") + @unittest.skipUnless(TESTFN_UNDECODABLE, "only works if there are undecodable paths") + def test_open_with_undecodable_path(self): + self.addCleanup(unlink, TESTFN_UNDECODABLE) + path = TESTFN_UNDECODABLE + with managed_connect(path) as cx: + self.assertTrue(os.path.exists(path)) cx.execute(self._sql) def test_open_uri(self): diff --git a/Misc/NEWS.d/next/Library/2022-05-18-17-18-41.gh-issue-91922.DwWIsJ.rst b/Misc/NEWS.d/next/Library/2022-05-18-17-18-41.gh-issue-91922.DwWIsJ.rst new file mode 100644 index 0000000..30f7bba --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-05-18-17-18-41.gh-issue-91922.DwWIsJ.rst @@ -0,0 +1,3 @@ +Fix function :func:`sqlite.connect` and the :class:`sqlite.Connection` +constructor on non-UTF-8 locales. Also, they now support bytes paths +non-decodable with the current FS encoding. diff --git a/Modules/_sqlite/clinic/connection.c.h b/Modules/_sqlite/clinic/connection.c.h index 1e27c5e..dd86fb5 100644 --- a/Modules/_sqlite/clinic/connection.c.h +++ b/Modules/_sqlite/clinic/connection.c.h @@ -3,9 +3,9 @@ preserve [clinic start generated code]*/ static int -pysqlite_connection_init_impl(pysqlite_Connection *self, - const char *database, double timeout, - int detect_types, const char *isolation_level, +pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database, + double timeout, int detect_types, + const char *isolation_level, int check_same_thread, PyObject *factory, int cache_size, int uri); @@ -19,7 +19,7 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs) PyObject * const *fastargs; Py_ssize_t nargs = PyTuple_GET_SIZE(args); Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; - const char *database = NULL; + PyObject *database; double timeout = 5.0; int detect_types = 0; const char *isolation_level = ""; @@ -32,9 +32,7 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs) if (!fastargs) { goto exit; } - if (!clinic_fsconverter(fastargs[0], &database)) { - goto exit; - } + database = fastargs[0]; if (!noptargs) { goto skip_optional_pos; } @@ -102,9 +100,6 @@ skip_optional_pos: return_value = pysqlite_connection_init_impl((pysqlite_Connection *)self, database, timeout, detect_types, isolation_level, check_same_thread, factory, cache_size, uri); exit: - /* Cleanup for database */ - PyMem_Free((void *)database); - return return_value; } @@ -1236,4 +1231,4 @@ exit: #ifndef DESERIALIZE_METHODDEF #define DESERIALIZE_METHODDEF #endif /* !defined(DESERIALIZE_METHODDEF) */ -/*[clinic end generated code: output=d21767843c480a10 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fb8908674e9f25ff input=a9049054013a1b77]*/ diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c index 333847f..7f7de8e 100644 --- a/Modules/_sqlite/connection.c +++ b/Modules/_sqlite/connection.c @@ -92,32 +92,6 @@ isolation_level_converter(PyObject *str_or_none, const char **result) return 1; } -static int -clinic_fsconverter(PyObject *pathlike, const char **result) -{ - PyObject *bytes = NULL; - Py_ssize_t len; - char *str; - - if (!PyUnicode_FSConverter(pathlike, &bytes)) { - goto error; - } - if (PyBytes_AsStringAndSize(bytes, &str, &len) < 0) { - goto error; - } - if ((*result = (const char *)PyMem_Malloc(len+1)) == NULL) { - goto error; - } - - memcpy((void *)(*result), str, len+1); - Py_DECREF(bytes); - return 1; - -error: - Py_XDECREF(bytes); - return 0; -} - #define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self))) #include "clinic/connection.c.h" #undef clinic_state @@ -159,25 +133,17 @@ new_statement_cache(pysqlite_Connection *self, pysqlite_state *state, } /*[python input] -class FSConverter_converter(CConverter): - type = "const char *" - converter = "clinic_fsconverter" - def converter_init(self): - self.c_default = "NULL" - def cleanup(self): - return f"PyMem_Free((void *){self.name});\n" - class IsolationLevel_converter(CConverter): type = "const char *" converter = "isolation_level_converter" [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=be142323885672ab]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=cbcfe85b253061c2]*/ /*[clinic input] _sqlite3.Connection.__init__ as pysqlite_connection_init - database: FSConverter + database: object timeout: double = 5.0 detect_types: int = 0 isolation_level: IsolationLevel = "" @@ -188,14 +154,19 @@ _sqlite3.Connection.__init__ as pysqlite_connection_init [clinic start generated code]*/ static int -pysqlite_connection_init_impl(pysqlite_Connection *self, - const char *database, double timeout, - int detect_types, const char *isolation_level, +pysqlite_connection_init_impl(pysqlite_Connection *self, PyObject *database, + double timeout, int detect_types, + const char *isolation_level, int check_same_thread, PyObject *factory, int cache_size, int uri) -/*[clinic end generated code: output=7d640ae1d83abfd4 input=342173993434ba1e]*/ +/*[clinic end generated code: output=839eb2fee4293bda input=b8ce63dc6f70a383]*/ { - if (PySys_Audit("sqlite3.connect", "s", database) < 0) { + if (PySys_Audit("sqlite3.connect", "O", database) < 0) { + return -1; + } + + PyObject *bytes; + if (!PyUnicode_FSConverter(database, &bytes)) { return -1; } @@ -210,7 +181,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, sqlite3 *db; int rc; Py_BEGIN_ALLOW_THREADS - rc = sqlite3_open_v2(database, &db, + rc = sqlite3_open_v2(PyBytes_AS_STRING(bytes), &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | (uri ? SQLITE_OPEN_URI : 0), NULL); if (rc == SQLITE_OK) { @@ -218,6 +189,7 @@ pysqlite_connection_init_impl(pysqlite_Connection *self, } Py_END_ALLOW_THREADS + Py_DECREF(bytes); if (db == NULL && rc == SQLITE_NOMEM) { PyErr_NoMemory(); return -1; |