On 05/18/2017 06:56 PM, jan.karstens via sqlalchemy wrote:
Upgrading a specific dialect (EXASOL) to 1.1.9 made me stumble across this test (part of test_reflection.py):

https://github.com/zzzeek/sqlalchemy/blob/rel_1_1_9/lib/sqlalchemy/testing/suite/test_reflection.py#L737

def test_get_table_names(self):
        tablenames = [
        t for t in inspect(testing.db).get_table_names()
        if t.lower() in ("t1", "t2")]
        
        eq_(tablenames[0].upper(), tablenames[0].lower())
        eq_(tablenames[1].upper(), tablenames[1].lower())


The test case fails and this does not look too unexpected to me. The tables T1 and T2 are added to the tablenames array, and u"t1".upper() is always different from u"t1".lower().

Am I missing the purpose of the test case or is this always bound to fail?


we have two provided dialects, Oracle and Firebird, for which the backend databases behave like this:

1. create a table called "table1":

   CREATE TABLE tablw1 (some_col integer)

2. ask the database what the name of "table1" is:

SQL> SELECT table_name FROM <system catalog stuff, returns the name of "table1"
   TABLE_NAME
   ----------

   TABLE1


Now what just happened. We created the table with the name "table1" and then it insisted we just named it "TABLE1". What's going on here? Basically, we didn't quote the name "table1" in our CREATE TABLE statement, which means in virtually all databases that the name is case insensitive. It means this table will respond to:

SELECT * FROM TABLE1
SELECT * FROM table1
SELECT * FROM TaBLe1

etc.


In SQLAlchemy, we first wrote things with SQLite, Mysql, Postgresql in mind. In these databases, if you don't quote the name, the system catalogs give you back the name *in lowercase*. Plus, lowercase names are much more of a Python thing.

What all this means is that *SQLAlchemy considers an all lower case table name to be **case insensitive**, and any other combination casings to be **case sensitive**. This could really use a whole chapter in the docs, but for now you can see it at:

http://docs.sqlalchemy.org/en/latest/core/metadata.html?highlight=table%20name#sqlalchemy.schema.Table.params.name

"Names which contain no upper case characters will be treated as case insensitive names, and will not be quoted unless they are a reserved word or contain special characters. A name with any number of upper case characters is considered to be case sensitive, and will be sent as quoted."


So now we have this round trip case that everyone expects to work:

t = Table("table1", metadata, autoload_with=engine)

t.create(some_other_engine)

t2 = Table("table1", some_other_metadata, autoload_with=some_other_engine)

That is, we can go back and forth, using "table1", and we get that same name back. If we got back "TABLE1", then it would create the table as follows:

CREATE TABLE "TABLE1" (some_col integer)

and now we have a **case sensitive** name. It means we can only SELECT from it like this:

SELECT * FROM "TABLE1"

These SQL statements OTOH will fail:

SELECT * FROM table1
SELECT * FROM TaBlE1
SELECT * FROM "table1"

this statement *might* fail depending on backend:

SELECT * FROM TABLE1



So that's a lot of detail. Let's talk about EXASOL, which I have noted, you have called EXASOL, and not "Exasol", which while this is a database I know nothing about, leads to the impression that EXASOL LIKES THINGS TO BE CAPITALIZED, basically the way ORACLE and FIREBIRD SEEM TO THINK IS GREAT.

If that's the case, then you need to take the steps that the Oracle / Firebird DBs take which is to "normalize" / "denormalize" the names that you get back from the server. To do that, start with the top-level dialect flag:

class EXASOLDialect(default.DefaultDialect):
    # ...

    requires_name_normalize = True

then you're going to want to denormalize names every time you pass them to a system catalog query, and normalize names every time you receive rows back from the system catalogs - look inside of lib/sqlalchemy/oracle/base.py for examples of this, such as:

    @reflection.cache
    def get_table_names(self, connection, schema=None, **kw):
        schema = self.denormalize_name(schema or self.default_schema_name)

        if schema is None:
            schema = self.default_schema_name

        sql_str = "SELECT table_name FROM all_tables WHERE "
        sql_str += (
            "OWNER = :owner "
            "AND IOT_NAME IS NULL "
            "AND DURATION IS NULL")

        cursor = connection.execute(sql.text(sql_str), owner=schema)
        return [self.normalize_name(row[0]) for row in cursor]

where you can see to get the table names, we denormalize the incoming schema name, so that if it is "myschema", we call it "MYSCHEMA", and if it is '"myschema"', we call it '"myschema"', so that it matches either case insensitively or sensitively per user request, and then when we get names back, we convert a name like "TABLE1" to "table1", that is, if we see an UPPERCASE name, we assume that is a case insensitive name and we convert to lowercase.

In general, when you see a test that is nonsensical and is failing, just run it against the default dialect which is SQLite, and watch what it does, and use pdb.set_trace() to get in there and poke around at the state of things. That will give you direct insight into what's going on within a particular test.









--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description.
---
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.

--
SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 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