bpo-39652: Truncate the column name after '[' only if PARSE_COLNAMES is set. (GH-18942)
This commit is contained in:
parent
684d2b9a07
commit
b146568dfc
|
@ -165,9 +165,10 @@ Module functions and constants
|
||||||
that 'mytype' is the type of the column. It will try to find an entry of
|
that 'mytype' is the type of the column. It will try to find an entry of
|
||||||
'mytype' in the converters dictionary and then use the converter function found
|
'mytype' in the converters dictionary and then use the converter function found
|
||||||
there to return the value. The column name found in :attr:`Cursor.description`
|
there to return the value. The column name found in :attr:`Cursor.description`
|
||||||
is only the first word of the column name, i. e. if you use something like
|
does not include the type, i. e. if you use something like
|
||||||
``'as "x [datetime]"'`` in your SQL, then we will parse out everything until the
|
``'as "Expiration date [datetime]"'`` in your SQL, then we will parse out
|
||||||
first blank for the column name: the column name would simply be "x".
|
everything until the first ``'['`` for the column name and strip
|
||||||
|
the preceeding space: the column name would simply be "Expiration date".
|
||||||
|
|
||||||
|
|
||||||
.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])
|
.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements, uri])
|
||||||
|
|
|
@ -68,7 +68,7 @@ class RegressionTests(unittest.TestCase):
|
||||||
def CheckColumnNameWithSpaces(self):
|
def CheckColumnNameWithSpaces(self):
|
||||||
cur = self.con.cursor()
|
cur = self.con.cursor()
|
||||||
cur.execute('select 1 as "foo bar [datetime]"')
|
cur.execute('select 1 as "foo bar [datetime]"')
|
||||||
self.assertEqual(cur.description[0][0], "foo bar")
|
self.assertEqual(cur.description[0][0], "foo bar [datetime]")
|
||||||
|
|
||||||
cur.execute('select 1 as "foo baz"')
|
cur.execute('select 1 as "foo baz"')
|
||||||
self.assertEqual(cur.description[0][0], "foo baz")
|
self.assertEqual(cur.description[0][0], "foo baz")
|
||||||
|
|
|
@ -275,13 +275,13 @@ class ColNamesTests(unittest.TestCase):
|
||||||
|
|
||||||
def CheckColName(self):
|
def CheckColName(self):
|
||||||
self.cur.execute("insert into test(x) values (?)", ("xxx",))
|
self.cur.execute("insert into test(x) values (?)", ("xxx",))
|
||||||
self.cur.execute('select x as "x [bar]" from test')
|
self.cur.execute('select x as "x y [bar]" from test')
|
||||||
val = self.cur.fetchone()[0]
|
val = self.cur.fetchone()[0]
|
||||||
self.assertEqual(val, "<xxx>")
|
self.assertEqual(val, "<xxx>")
|
||||||
|
|
||||||
# Check if the stripping of colnames works. Everything after the first
|
# Check if the stripping of colnames works. Everything after the first
|
||||||
# whitespace should be stripped.
|
# '[' (and the preceeding space) should be stripped.
|
||||||
self.assertEqual(self.cur.description[0][0], "x")
|
self.assertEqual(self.cur.description[0][0], "x y")
|
||||||
|
|
||||||
def CheckCaseInConverterName(self):
|
def CheckCaseInConverterName(self):
|
||||||
self.cur.execute("select 'other' as \"x [b1b1]\"")
|
self.cur.execute("select 'other' as \"x [b1b1]\"")
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
The column name found in ``sqlite3.Cursor.description`` is now truncated on
|
||||||
|
the first '[' only if the PARSE_COLNAMES option is set.
|
|
@ -193,22 +193,30 @@ pysqlite_build_row_cast_map(pysqlite_Cursor* self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
_pysqlite_build_column_name(const char* colname)
|
_pysqlite_build_column_name(pysqlite_Cursor *self, const char *colname)
|
||||||
{
|
{
|
||||||
const char* pos;
|
const char* pos;
|
||||||
|
Py_ssize_t len;
|
||||||
|
|
||||||
if (!colname) {
|
if (!colname) {
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pos = colname;; pos++) {
|
if (self->connection->detect_types & PARSE_COLNAMES) {
|
||||||
if (*pos == 0 || *pos == '[') {
|
for (pos = colname; *pos; pos++) {
|
||||||
if ((*pos == '[') && (pos > colname) && (*(pos-1) == ' ')) {
|
if (*pos == '[') {
|
||||||
pos--;
|
if ((pos != colname) && (*(pos-1) == ' ')) {
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return PyUnicode_FromStringAndSize(colname, pos - colname);
|
|
||||||
}
|
}
|
||||||
|
len = pos - colname;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
len = strlen(colname);
|
||||||
|
}
|
||||||
|
return PyUnicode_FromStringAndSize(colname, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -370,6 +378,7 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
|
||||||
PyObject* result;
|
PyObject* result;
|
||||||
int numcols;
|
int numcols;
|
||||||
PyObject* descriptor;
|
PyObject* descriptor;
|
||||||
|
PyObject* column_name;
|
||||||
PyObject* second_argument = NULL;
|
PyObject* second_argument = NULL;
|
||||||
sqlite_int64 lastrowid;
|
sqlite_int64 lastrowid;
|
||||||
|
|
||||||
|
@ -536,7 +545,13 @@ _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject* args)
|
||||||
if (!descriptor) {
|
if (!descriptor) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
PyTuple_SetItem(descriptor, 0, _pysqlite_build_column_name(sqlite3_column_name(self->statement->st, i)));
|
column_name = _pysqlite_build_column_name(self,
|
||||||
|
sqlite3_column_name(self->statement->st, i));
|
||||||
|
if (!column_name) {
|
||||||
|
Py_DECREF(descriptor);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
PyTuple_SetItem(descriptor, 0, column_name);
|
||||||
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None);
|
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 1, Py_None);
|
||||||
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None);
|
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 2, Py_None);
|
||||||
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);
|
Py_INCREF(Py_None); PyTuple_SetItem(descriptor, 3, Py_None);
|
||||||
|
|
Loading…
Reference in New Issue