On 05/16/2016 04:34 PM, Михаил Доронин wrote:
I've opened issue here
<https://bitbucket.org/zzzeek/sqlalchemy/issues/3714/sqlalchemy-core-returns-string-instead-of>,
please read it, it's very brief.

Now Mike Bayer says that it's not a sqlalchemy bug, and says that I can
use cast to hint to mysql to return datetime.date, but this won't work
with sqlite which we're trying to use for tests. So it's doesn't solve
my problem as I really want to use sqlite for tests.

So I got a few questions

Why ``` literal(d, Date)``` isn't working with mysql?

the DBAPI turns it into a string, and on the MySQL side it is as though you said:

"SELECT '2016-01-01'"

nothing about a date there. A string is sent back, the driver sees string, you get a string.



I've explicitly
stated that this column is of type Date.

your test case did not include any Column objects so there's nothing on the database side that tells MySQL this is a date. If you are dealing with regular tables and columns and selecting from those, rather from free-standing literals which is always an awkward use case due to the lack of typing information, both backends would work equally well.


But mysql still returns
strings. What is the use of this type parameter in ```literal``` function?

that tells SQLAlchemy what datatype to treat this as on the python side before passing it in. In the case of MySQL Date, this means, "do nothing, the driver handles it".


Could someone point me to a sqlite bug report which says that cast isn't
working correctly with casting string to date? Maybe I can fix this...

There's no bug that I know of, only that if you go into the sqlite console, CAST acts in a totally useless, non-SQL way:

sqlite> select CAST ('2016-06-05' AS DATE);
2016

There's probably some esoteric reason in SQLite's extremely weird typing system that causes this. The reason you get a date back when you don't use cast is because for SQLite, SQLAlchemy's Date type actually does a string->date conversion. SQLite doesn't have a native "date" type which is probably why the cast acts weird.


Are there any other workarounds that are actually portable? I mean
besides changing my own functions logic to deal with corner cases like
if result is string, than convert it to date etc.

your test was extremely specific, and is attempting to do a round trip of a date literal.

To make that exact thing work you need to build a @compiles recipe using a form such as that at http://docs.sqlalchemy.org/en/rel_1_0/core/compiler.html#utc-timestamp-function

from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.elements import Cast


class MakeADate(Cast):
    def __init__(self, elem):
        super(MakeADate, self).__init__(elem, Date)


@compiles(MakeADate)
def _default_date(elem, compiler, **kw):
    return compiler.visit_cast(elem, **kw)


@compiles(MakeADate, "sqlite")
def _sqlite_date(elem, compiler, **kw):
    return compiler.process(elem.clause, **kw)



full example:


from sqlalchemy import (
    create_engine,
    Column,
    String,
    Integer,
    Date
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.sql import select, cast
from sqlalchemy.sql.expression import union, literal, alias
from itertools import chain
from datetime import date

Base = declarative_base()

e1 = create_engine("mysql://scott:tiger@localhost/test", echo=True)

e2 = create_engine("sqlite://", echo='debug')


from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.elements import Cast


class MakeADate(Cast):
    def __init__(self, elem):
        super(MakeADate, self).__init__(elem, Date)


@compiles(MakeADate)
def _default_date(elem, compiler, **kw):
    return compiler.visit_cast(elem, **kw)


@compiles(MakeADate, "sqlite")
def _sqlite_date(elem, compiler, **kw):
    return compiler.process(elem.clause, **kw)


dates = (
    date(2016, 1, 1),
    date(2016, 1, 2),
)


for engine in e1, e2:
    session = Session(engine)

    selects = tuple(select([MakeADate(d)]) for d in dates)

    data = alias(union(*selects, use_labels=True), 'dates')
    stmt = select((data,))
    result = session.execute(stmt).fetchall()
    assert tuple(chain.from_iterable(result)) == dates






--
You received this message because you are subscribed to the Google
Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to sqlalchemy+unsubscr...@googlegroups.com
<mailto:sqlalchemy+unsubscr...@googlegroups.com>.
To post to this group, send email to sqlalchemy@googlegroups.com
<mailto:sqlalchemy@googlegroups.com>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to