mirror of https://github.com/python/cpython
bpo-44688: Remove ASCII limitation from `sqlite3` collation names (GH-27395)
This commit is contained in:
parent
8d0647485d
commit
5269c09145
|
@ -402,6 +402,10 @@ Connection Objects
|
||||||
|
|
||||||
con.create_collation("reverse", None)
|
con.create_collation("reverse", None)
|
||||||
|
|
||||||
|
.. versionchanged:: 3.11
|
||||||
|
The collation name can contain any Unicode character. Earlier, only
|
||||||
|
ASCII characters were allowed.
|
||||||
|
|
||||||
|
|
||||||
.. method:: interrupt()
|
.. method:: interrupt()
|
||||||
|
|
||||||
|
|
|
@ -213,6 +213,11 @@ sqlite3
|
||||||
:meth:`~sqlite3.Connection.set_authorizer`.
|
:meth:`~sqlite3.Connection.set_authorizer`.
|
||||||
(Contributed by Erlend E. Aasland in :issue:`44491`.)
|
(Contributed by Erlend E. Aasland in :issue:`44491`.)
|
||||||
|
|
||||||
|
* Collation name :meth:`~sqlite3.Connection.create_collation` can now
|
||||||
|
contain any Unicode character. Collation names with invalid characters
|
||||||
|
now raise :exc:`UnicodeEncodeError` instead of :exc:`sqlite3.ProgrammingError`.
|
||||||
|
(Contributed by Erlend E. Aasland in :issue:`44688`.)
|
||||||
|
|
||||||
|
|
||||||
Removed
|
Removed
|
||||||
=======
|
=======
|
||||||
|
|
|
@ -40,8 +40,7 @@ class CollationTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_create_collation_not_ascii(self):
|
def test_create_collation_not_ascii(self):
|
||||||
con = sqlite.connect(":memory:")
|
con = sqlite.connect(":memory:")
|
||||||
with self.assertRaises(sqlite.ProgrammingError):
|
con.create_collation("collä", lambda x, y: (x > y) - (x < y))
|
||||||
con.create_collation("collä", lambda x, y: (x > y) - (x < y))
|
|
||||||
|
|
||||||
def test_create_collation_bad_upper(self):
|
def test_create_collation_bad_upper(self):
|
||||||
class BadUpperStr(str):
|
class BadUpperStr(str):
|
||||||
|
|
|
@ -278,7 +278,7 @@ class RegressionTests(unittest.TestCase):
|
||||||
def test_collation(self):
|
def test_collation(self):
|
||||||
def collation_cb(a, b):
|
def collation_cb(a, b):
|
||||||
return 1
|
return 1
|
||||||
self.assertRaises(sqlite.ProgrammingError, self.con.create_collation,
|
self.assertRaises(UnicodeEncodeError, self.con.create_collation,
|
||||||
# Lone surrogate cannot be encoded to the default encoding (utf8)
|
# Lone surrogate cannot be encoded to the default encoding (utf8)
|
||||||
"\uDC80", collation_cb)
|
"\uDC80", collation_cb)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
:meth:`sqlite3.Connection.create_collation` now accepts non-ASCII collation
|
||||||
|
names. Patch by Erlend E. Aasland.
|
|
@ -722,13 +722,14 @@ PyDoc_STRVAR(pysqlite_connection_create_collation__doc__,
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
|
pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
|
||||||
PyObject *name, PyObject *callable);
|
const char *name,
|
||||||
|
PyObject *callable);
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
pysqlite_connection_create_collation(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs)
|
pysqlite_connection_create_collation(pysqlite_Connection *self, PyObject *const *args, Py_ssize_t nargs)
|
||||||
{
|
{
|
||||||
PyObject *return_value = NULL;
|
PyObject *return_value = NULL;
|
||||||
PyObject *name;
|
const char *name;
|
||||||
PyObject *callable;
|
PyObject *callable;
|
||||||
|
|
||||||
if (!_PyArg_CheckPositional("create_collation", nargs, 2, 2)) {
|
if (!_PyArg_CheckPositional("create_collation", nargs, 2, 2)) {
|
||||||
|
@ -738,10 +739,15 @@ pysqlite_connection_create_collation(pysqlite_Connection *self, PyObject *const
|
||||||
_PyArg_BadArgument("create_collation", "argument 1", "str", args[0]);
|
_PyArg_BadArgument("create_collation", "argument 1", "str", args[0]);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (PyUnicode_READY(args[0]) == -1) {
|
Py_ssize_t name_length;
|
||||||
|
name = PyUnicode_AsUTF8AndSize(args[0], &name_length);
|
||||||
|
if (name == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (strlen(name) != (size_t)name_length) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "embedded null character");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
name = args[0];
|
|
||||||
callable = args[1];
|
callable = args[1];
|
||||||
return_value = pysqlite_connection_create_collation_impl(self, name, callable);
|
return_value = pysqlite_connection_create_collation_impl(self, name, callable);
|
||||||
|
|
||||||
|
@ -811,4 +817,4 @@ exit:
|
||||||
#ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
#ifndef PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
||||||
#define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
#define PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF
|
||||||
#endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */
|
#endif /* !defined(PYSQLITE_CONNECTION_LOAD_EXTENSION_METHODDEF) */
|
||||||
/*[clinic end generated code: output=30f11f2d8f09bdf0 input=a9049054013a1b77]*/
|
/*[clinic end generated code: output=a7a899c4e41381ac input=a9049054013a1b77]*/
|
||||||
|
|
|
@ -1720,7 +1720,7 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
_sqlite3.Connection.create_collation as pysqlite_connection_create_collation
|
_sqlite3.Connection.create_collation as pysqlite_connection_create_collation
|
||||||
|
|
||||||
name: unicode
|
name: str
|
||||||
callback as callable: object
|
callback as callable: object
|
||||||
/
|
/
|
||||||
|
|
||||||
|
@ -1729,61 +1729,26 @@ Creates a collation function. Non-standard.
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
|
pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
|
||||||
PyObject *name, PyObject *callable)
|
const char *name,
|
||||||
/*[clinic end generated code: output=0f63b8995565ae22 input=5c3898813a776cf2]*/
|
PyObject *callable)
|
||||||
|
/*[clinic end generated code: output=a4ceaff957fdef9a input=301647aab0f2fb1d]*/
|
||||||
{
|
{
|
||||||
PyObject* uppercase_name = 0;
|
|
||||||
Py_ssize_t i, len;
|
|
||||||
_Py_IDENTIFIER(upper);
|
|
||||||
const char *uppercase_name_str;
|
|
||||||
int rc;
|
|
||||||
unsigned int kind;
|
|
||||||
const void *data;
|
|
||||||
|
|
||||||
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
|
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
|
||||||
goto finally;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uppercase_name = _PyObject_CallMethodIdOneArg((PyObject *)&PyUnicode_Type,
|
int rc;
|
||||||
&PyId_upper, name);
|
|
||||||
if (!uppercase_name) {
|
|
||||||
goto finally;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PyUnicode_READY(uppercase_name))
|
|
||||||
goto finally;
|
|
||||||
len = PyUnicode_GET_LENGTH(uppercase_name);
|
|
||||||
kind = PyUnicode_KIND(uppercase_name);
|
|
||||||
data = PyUnicode_DATA(uppercase_name);
|
|
||||||
for (i=0; i<len; i++) {
|
|
||||||
Py_UCS4 ch = PyUnicode_READ(kind, data, i);
|
|
||||||
if ((ch >= '0' && ch <= '9')
|
|
||||||
|| (ch >= 'A' && ch <= 'Z')
|
|
||||||
|| (ch == '_'))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
PyErr_SetString(self->ProgrammingError,
|
|
||||||
"invalid character in collation name");
|
|
||||||
goto finally;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uppercase_name_str = PyUnicode_AsUTF8(uppercase_name);
|
|
||||||
if (!uppercase_name_str)
|
|
||||||
goto finally;
|
|
||||||
|
|
||||||
int flags = SQLITE_UTF8;
|
int flags = SQLITE_UTF8;
|
||||||
if (callable == Py_None) {
|
if (callable == Py_None) {
|
||||||
rc = sqlite3_create_collation_v2(self->db, uppercase_name_str, flags,
|
rc = sqlite3_create_collation_v2(self->db, name, flags,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!PyCallable_Check(callable)) {
|
if (!PyCallable_Check(callable)) {
|
||||||
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
|
PyErr_SetString(PyExc_TypeError, "parameter must be callable");
|
||||||
goto finally;
|
return NULL;
|
||||||
}
|
}
|
||||||
rc = sqlite3_create_collation_v2(self->db, uppercase_name_str, flags,
|
rc = sqlite3_create_collation_v2(self->db, name, flags,
|
||||||
Py_NewRef(callable),
|
Py_NewRef(callable),
|
||||||
&pysqlite_collation_callback,
|
&pysqlite_collation_callback,
|
||||||
&_destructor);
|
&_destructor);
|
||||||
|
@ -1798,16 +1763,10 @@ pysqlite_connection_create_collation_impl(pysqlite_Connection *self,
|
||||||
Py_DECREF(callable);
|
Py_DECREF(callable);
|
||||||
}
|
}
|
||||||
_pysqlite_seterror(self->db);
|
_pysqlite_seterror(self->db);
|
||||||
goto finally;
|
|
||||||
}
|
|
||||||
|
|
||||||
finally:
|
|
||||||
Py_XDECREF(uppercase_name);
|
|
||||||
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return Py_NewRef(Py_None);
|
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
|
|
Loading…
Reference in New Issue