On Nov 24, 2014, at 2:19 PM, Sébastien de Menten <sdem...@gmail.com> wrote:Hello,When SA create a DATE column for sqlite it outputs as DDL a DATE type which is interpreted as INTEGER by sqlite.By default, this is not an issue as sqlite serialises a date as a string through the storage_format string format and the latter is equal to "%(year)04d-%(month)02d-%(day)02d ".Hence, the date 23/11/2014 gets formatted as 2014-11-23 and pushes to sqlite which stores it as a TEXT (even if the column is INTEGER as there is dynamic types in sqlite).However, if we change the storage_format to "%(year)04d%(month)02d%(day)02d", the date gets rendered to 20141123 which is pushed to sqlite and can be interpreted as an INTEGER (which is the default type of the column).However, once SA reads back the date, it cannot parse it anymore (if we use a regexp=r"(\d{4})(\d{2})(\d{2})") as it says that 20141123 is not a string (indeed sqlite handled the value as an INTEGER).A solution is to replace in the "CREATE TABLE" generation for the sqlite dialect, the DATE type by a TEXT type (and idem for DATETIME and TIME).
A hacky solution is to change the coltype in sqlite DDL Compiler (as below) ... but there should be a cleaner way to do it (but I am not familiar at all with Compilers and Dialects)class SQLiteDDLCompiler(compiler.DDLCompiler):def get_column_specification(self, column, **kwargs):if isinstance(column.type, (DATE, TIME, DATETIME)):coltype="TEXT"else:coltype = self.dialect.type_compiler.process(column.type)
SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
On Nov 24, 2014, at 6:01 PM, Mark Grandi <markg...@gmail.com> wrote:I was discussing this on IRC, and it seems to be that per the sqlite3 documentation, a DATETIME can be stored in one of 3 'types':SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
So I'm not too familiar on the sqlite internals, but it seems to be setting the actual type affinity when you try and insert data into the row/column, so if you store it as a ISO string, it sets it to TEXT, but if you are storing it like '20141008", then it thinks its an integer so therefore stores it as INTEGER.
I don't see a DATE_CHAR type listing on that url that Michael posted in the sqlite documentation,
but I honestly think the code should already work.
Represent a Python datetime object in SQLite using a string.
The default string storage format is:
"%(year)04d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(second)02d.%(microsecond)06d"
class MyEpochType(types.TypeDecorator): impl = types.Integer epoch = datetime.date(1970, 1, 1) def process_bind_param(self, value, dialect): return (value - self.epoch).days def process_result_value(self, value, dialect): return self.epoch + timedelta(days=value)
Since sqlite3 can store datetimes in 3 different formats, SQLAlchemy should be prepared to receive all 3 formats.
On Nov 24, 2014, at 6:01 PM, Mark Grandi <markg...@gmail.com> wrote:I was discussing this on IRC, and it seems to be that per the sqlite3 documentation, a DATETIME can be stored in one of 3 'types':SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:
TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.
So I'm not too familiar on the sqlite internals, but it seems to be setting the actual type affinity when you try and insert data into the row/column, so if you store it as a ISO string, it sets it to TEXT, but if you are storing it like '20141008", then it thinks its an integer so therefore stores it as INTEGER.
I don't see a DATE_CHAR type listing on that url that Michael posted in the sqlite documentation,SQLite doesn’t have “types” in the usual sense, it has what’s known as a “type affinity”. This affinity is determined based on either the name of the type, or of the data that’s placed within it. Per https://www.sqlite.org/datatype3.html, read section 2.1, “Determination of Type Affinity”. At the column level, this would be rule #2: "If the declared type of the column **contains any of the strings** "CHAR", "CLOB", or "TEXT" then that column has TEXT affinity. Notice that the type VARCHAR contains the string "CHAR" and is thus assigned TEXT affinity."but I honestly think the code should already work.Disagree because SQLAlchemy’s date types for SQLite are documented as implementing exactly one of the above schemes:The implementation classes are DATETIME, DATE and TIME. These types represent dates and times as **ISO formatted strings**, which also nicely support ordering.The types themselves also make this clear:Represent a Python datetime object in SQLite using a string.
The default string storage format is:
"%(year)04d-%(month)02d-%(day)02d %(hour)02d:%(min)02d:%(second)02d.%(microsecond)06d"The documentation is very clear that these types store strings only with limited ability to modify the format. There should not be an expectation that these types will attempt to figure out what type of conversion to use based on the kind of data actually observed; this kind of thing is guessing, and would be error prone and non-performant.
On Nov 24, 2014, at 11:44 PM, Sébastien de Menten <sdem...@gmail.com> wrote:If I am using a storage_format that does not render "ISO formatted strings" (for instance "20141125"), should I expect more issues than simply the one of this post (not being able to parse back) ?
I am thinking about date comparison or other similar things. From reading the doc "The storage format can be customized to some degree using the storage_format and regexpparameters, such as:", it was not clear to me that it had to stay an ISO formatted string (as recognised by sqlite in paragraph "Time Strings" of its doc).
If so, then it is indeed not a good idea to reuse the sqlite.DATETIME(storage_format="...", regexp="...") for my needs. I'll implement a TypeDecorator with, if dialect=sqlite:- type = sqlite.TEXT- process_bind_param that reuses sqlite.DATETIME(storage_format="...", regexp="...).bind_processor(engine)(value)- process_result_value that reuses sqlite.DATETIME(storage_format="...", regexp="...).result_processor(engine, sqlite.TEXT)(value)to avoid reimplementing the logic of parsing/writing of dates already implemented in the sqlite SA types.
On Nov 24, 2014, at 11:46 PM, Sébastien de Menten <sdem...@gmail.com> wrote:
On Monday, November 24, 2014 8:52:14 PM UTC+1, Michael Bayer wrote:On Nov 24, 2014, at 2:19 PM, Sébastien de Menten <sdem...@gmail.com> wrote:A hacky solution is to change the coltype in sqlite DDL Compiler (as below) ... but there should be a cleaner way to do it (but I am not familiar at all with Compilers and Dialects)class SQLiteDDLCompiler(compiler.DDLCompiler):def get_column_specification(self, column, **kwargs):if isinstance(column.type, (DATE, TIME, DATETIME)):coltype="TEXT"else:coltype = self.dialect.type_compiler.process(column.type)you can work around this right now with @compiles: http://docs.sqlalchemy.org/en/rel_0_9/core/compiler.html#changing-compilation-of-typesI have tried the following but without success (as it does not change, in the CREATE TABLE, the DATETIME to TEXT). Isn't it the way to use it ?
On Nov 24, 2014, at 11:44 PM, Sébastien de Menten <sdem...@gmail.com> wrote:If I am using a storage_format that does not render "ISO formatted strings" (for instance "20141125"), should I expect more issues than simply the one of this post (not being able to parse back) ?the fact that SQLite is interpreting as an integer when there’s no alpha chars is a new issue. Any format can be used, including straight numeric plus a single letter even, if the format is completely numeric then apparently the DDL type name has to be changed. this is something that should be at least documented or warned about at some point, at the moment I’m just trying to give you workarounds since there’s no immediate change planned.