Kaydet (Commit) e11c9b3d authored tarafından Gerhard Häring's avatar Gerhard Häring

Implemented feature request 2157: Converter names are cut off at '('

characters. This avoids the common case of something like 'NUMBER(10)' not
being parsed as 'NUMBER', like expected. Also corrected the docs about
converter names being case-sensitive. They aren't any longer.
üst 5a366c3b
...@@ -114,10 +114,11 @@ Module functions and constants ...@@ -114,10 +114,11 @@ Module functions and constants
:func:`connect` function. :func:`connect` function.
Setting it makes the :mod:`sqlite3` module parse the declared type for each Setting it makes the :mod:`sqlite3` module parse the declared type for each
column it returns. It will parse out the first word of the declared type, i. e. column it returns. It will parse out the first word of the declared type,
for "integer primary key", it will parse out "integer". Then for that column, it i. e. for "integer primary key", it will parse out "integer", or for
will look into the converters dictionary and use the converter function "number(10)" it will parse out "number". Then for that column, it will look
registered for that type there. Converter names are case-sensitive! into the converters dictionary and use the converter function registered for
that type there.
.. data:: PARSE_COLNAMES .. data:: PARSE_COLNAMES
...@@ -666,10 +667,6 @@ and constructs a :class:`Point` object from it. ...@@ -666,10 +667,6 @@ and constructs a :class:`Point` object from it.
Converter functions **always** get called with a string, no matter under which Converter functions **always** get called with a string, no matter under which
data type you sent the value to SQLite. data type you sent the value to SQLite.
.. note::
Converter names are looked up in a case-sensitive manner.
:: ::
def convert_point(s): def convert_point(s):
......
...@@ -98,7 +98,7 @@ class DeclTypesTests(unittest.TestCase): ...@@ -98,7 +98,7 @@ class DeclTypesTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES) self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_DECLTYPES)
self.cur = self.con.cursor() self.cur = self.con.cursor()
self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob)") self.cur.execute("create table test(i int, s str, f float, b bool, u unicode, foo foo, bin blob, n1 number, n2 number(5))")
# override float, make them always return the same number # override float, make them always return the same number
sqlite.converters["FLOAT"] = lambda x: 47.2 sqlite.converters["FLOAT"] = lambda x: 47.2
...@@ -107,11 +107,13 @@ class DeclTypesTests(unittest.TestCase): ...@@ -107,11 +107,13 @@ class DeclTypesTests(unittest.TestCase):
sqlite.converters["BOOL"] = lambda x: bool(int(x)) sqlite.converters["BOOL"] = lambda x: bool(int(x))
sqlite.converters["FOO"] = DeclTypesTests.Foo sqlite.converters["FOO"] = DeclTypesTests.Foo
sqlite.converters["WRONG"] = lambda x: "WRONG" sqlite.converters["WRONG"] = lambda x: "WRONG"
sqlite.converters["NUMBER"] = float
def tearDown(self): def tearDown(self):
del sqlite.converters["FLOAT"] del sqlite.converters["FLOAT"]
del sqlite.converters["BOOL"] del sqlite.converters["BOOL"]
del sqlite.converters["FOO"] del sqlite.converters["FOO"]
del sqlite.converters["NUMBER"]
self.cur.close() self.cur.close()
self.con.close() self.con.close()
...@@ -203,6 +205,19 @@ class DeclTypesTests(unittest.TestCase): ...@@ -203,6 +205,19 @@ class DeclTypesTests(unittest.TestCase):
row = self.cur.fetchone() row = self.cur.fetchone()
self.failUnlessEqual(row[0], val) self.failUnlessEqual(row[0], val)
def CheckNumber1(self):
self.cur.execute("insert into test(n1) values (5)")
value = self.cur.execute("select n1 from test").fetchone()[0]
# if the converter is not used, it's an int instead of a float
self.failUnlessEqual(type(value), float)
def CheckNumber2(self):
"""Checks wether converter names are cut off at '(' characters"""
self.cur.execute("insert into test(n2) values (5)")
value = self.cur.execute("select n2 from test").fetchone()[0]
# if the converter is not used, it's an int instead of a float
self.failUnlessEqual(type(value), float)
class ColNamesTests(unittest.TestCase): class ColNamesTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES) self.con = sqlite.connect(":memory:", detect_types=sqlite.PARSE_COLNAMES)
......
...@@ -202,7 +202,11 @@ int pysqlite_build_row_cast_map(pysqlite_Cursor* self) ...@@ -202,7 +202,11 @@ int pysqlite_build_row_cast_map(pysqlite_Cursor* self)
decltype = sqlite3_column_decltype(self->statement->st, i); decltype = sqlite3_column_decltype(self->statement->st, i);
if (decltype) { if (decltype) {
for (pos = decltype;;pos++) { for (pos = decltype;;pos++) {
if (*pos == ' ' || *pos == 0) { /* Converter names are split at '(' and blanks.
* This allows 'INTEGER NOT NULL' to be treated as 'INTEGER' and
* 'NUMBER(10)' to be treated as 'NUMBER', for example.
* In other words, it will work as people expect it to work.*/
if (*pos == ' ' || *pos == '(' || *pos == 0) {
py_decltype = PyString_FromStringAndSize(decltype, pos - decltype); py_decltype = PyString_FromStringAndSize(decltype, pos - decltype);
if (!py_decltype) { if (!py_decltype) {
return -1; return -1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment