[Python-checkins] gh-91922: Fix sqlite connection on nonstardard locales and paths (GH-92926)

serhiy-storchaka webhook-mailer at python.org
Fri May 20 04:53:18 EDT 2022


https://github.com/python/cpython/commit/d8537580921b2e02f477ff1a8dedcf82c24ef0c2
commit: d8537580921b2e02f477ff1a8dedcf82c24ef0c2
branch: main
author: Serhiy Storchaka <storchaka at gmail.com>
committer: serhiy-storchaka <storchaka at gmail.com>
date: 2022-05-20T11:53:05+03:00
summary:

gh-91922: Fix sqlite connection on nonstardard locales and paths (GH-92926)

files:
A Misc/NEWS.d/next/Library/2022-05-18-17-18-41.gh-issue-91922.DwWIsJ.rst
M Lib/test/test_sqlite3/test_dbapi.py
M Modules/_sqlite/clinic/connection.c.h
M Modules/_sqlite/connection.c

diff --git a/Lib/test/test_sqlite3/test_dbapi.py b/Lib/test/test_sqlite3/test_dbapi.py
index 8a218973794db..40f91a193b2cd 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 0000000000000..30f7bba115606
--- /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 1e27c5e0afb12..dd86fb5b64f3f 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 @@ pysqlite_connection_init(PyObject *self, PyObject *args, PyObject *kwargs)
     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 @@ getlimit(pysqlite_Connection *self, PyObject *arg)
 #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 333847f0fafb9..7f7de8e709228 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;



More information about the Python-checkins mailing list