summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/changelog_09.rst22
-rw-r--r--doc/build/changelog/migration_09.rst28
-rw-r--r--doc/build/core/metadata.rst5
-rw-r--r--doc/build/core/reflection.rst4
-rw-r--r--doc/build/core/sqlelement.rst2
-rw-r--r--lib/sqlalchemy/dialects/mssql/base.py4
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py7
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py3
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py8
-rw-r--r--lib/sqlalchemy/engine/base.py11
-rw-r--r--lib/sqlalchemy/engine/default.py2
-rw-r--r--lib/sqlalchemy/engine/reflection.py49
-rw-r--r--lib/sqlalchemy/sql/base.py1
-rw-r--r--lib/sqlalchemy/sql/compiler.py99
-rw-r--r--lib/sqlalchemy/sql/elements.py116
-rw-r--r--lib/sqlalchemy/sql/schema.py127
-rw-r--r--lib/sqlalchemy/sql/selectable.py1
-rw-r--r--lib/sqlalchemy/sql/sqltypes.py15
-rw-r--r--lib/sqlalchemy/sql/util.py1
-rw-r--r--test/ext/declarative/test_basic.py6
-rw-r--r--test/profiles.txt122
-rw-r--r--test/sql/test_metadata.py32
-rw-r--r--test/sql/test_quote.py149
23 files changed, 622 insertions, 192 deletions
diff --git a/doc/build/changelog/changelog_09.rst b/doc/build/changelog/changelog_09.rst
index be7a7fc7c..b0492e897 100644
--- a/doc/build/changelog/changelog_09.rst
+++ b/doc/build/changelog/changelog_09.rst
@@ -7,6 +7,28 @@
:version: 0.9.0
.. change::
+ :tags: bug, sql
+ :tickets: 2812
+
+ A rework to the way that "quoted" identifiers are handled, in that
+ instead of relying upon various ``quote=True`` flags being passed around,
+ these flags are converted into rich string objects with quoting information
+ included at the point at which they are passed to common schema constructs
+ like :class:`.Table`, :class:`.Column`, etc. This solves the issue
+ of various methods that don't correctly honor the "quote" flag such
+ as :meth:`.Engine.has_table` and related methods. The :class:`.quoted_name`
+ object is a string subclass that can also be used explicitly if needed;
+ the object will hold onto the quoting preferences passed and will
+ also bypass the "name normalization" performed by dialects that
+ standardize on uppercase symbols, such as Oracle, Firebird and DB2.
+ The upshot is that the "uppercase" backends can now work with force-quoted
+ names, such as lowercase-quoted names and new reserved words.
+
+ .. seealso::
+
+ :ref:`change_2812`
+
+ .. change::
:tags: feature, orm
:tickets: 2793
diff --git a/doc/build/changelog/migration_09.rst b/doc/build/changelog/migration_09.rst
index b8328ce7b..82314cce4 100644
--- a/doc/build/changelog/migration_09.rst
+++ b/doc/build/changelog/migration_09.rst
@@ -292,6 +292,34 @@ against ``b_value`` directly.
:ticket:`2751`
+.. _change_2812:
+
+Schema identifiers now carry along their own quoting information
+---------------------------------------------------------------------
+
+This change simplifies the Core's usage of so-called "quote" flags, such
+as the ``quote`` flag passed to :class:`.Table` and :class:`.Column`. The flag
+is now internalized within the string name itself, which is now represented
+as an instance of :class:`.quoted_name`, a string subclass. The
+:class:`.IdentifierPreparer` now relies solely on the quoting preferences
+reported by the :class:`.quoted_name` object rather than checking for any
+explicit ``quote`` flags in most cases. The issue resolved here includes
+that various case-sensitive methods such as :meth:`.Engine.has_table` as well
+as similar methods within dialects now function with explicitly quoted names,
+without the need to complicate or introduce backwards-incompatible changes
+to those APIs (many of which are 3rd party) with the details of quoting flags -
+in particular, a wider range of identifiers now function correctly with the
+so-called "uppercase" backends like Oracle, Firebird, and DB2 (backends that
+store and report upon table and column names using all uppercase for case
+insensitive names).
+
+The :class:`.quoted_name` object is used internally as needed; however if
+other keywords require fixed quoting preferences, the class is available
+publically.
+
+:ticket:`2812`
+
+
New Features
============
diff --git a/doc/build/core/metadata.rst b/doc/build/core/metadata.rst
index e4b50f63a..d6fc8c6af 100644
--- a/doc/build/core/metadata.rst
+++ b/doc/build/core/metadata.rst
@@ -1,12 +1,15 @@
.. _metadata_toplevel:
+
.. _metadata_describing_toplevel:
+
.. _metadata_describing:
-.. module:: sqlalchemy.schema
==================================
Describing Databases with MetaData
==================================
+.. module:: sqlalchemy.schema
+
This section discusses the fundamental :class:`.Table`, :class:`.Column`
and :class:`.MetaData` objects.
diff --git a/doc/build/core/reflection.rst b/doc/build/core/reflection.rst
index 17ff0b99e..2e9a2de64 100644
--- a/doc/build/core/reflection.rst
+++ b/doc/build/core/reflection.rst
@@ -112,6 +112,8 @@ object's dictionary of tables::
for table in reversed(meta.sorted_tables):
someengine.execute(table.delete())
+.. _metadata_reflection_inspector:
+
Fine Grained Reflection with Inspector
--------------------------------------
@@ -128,5 +130,5 @@ database is also available. This is known as the "Inspector"::
.. autoclass:: sqlalchemy.engine.reflection.Inspector
:members:
:undoc-members:
-
+
diff --git a/doc/build/core/sqlelement.rst b/doc/build/core/sqlelement.rst
index 953f48c98..0676f18d0 100644
--- a/doc/build/core/sqlelement.rst
+++ b/doc/build/core/sqlelement.rst
@@ -105,6 +105,8 @@ used to construct any kind of typed SQL expression.
:members:
:special-members:
+.. autoclass:: sqlalchemy.sql.elements.quoted_name
+
.. autoclass:: UnaryExpression
:members:
diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py
index 7621f4aab..b1b168035 100644
--- a/lib/sqlalchemy/dialects/mssql/base.py
+++ b/lib/sqlalchemy/dialects/mssql/base.py
@@ -1012,7 +1012,7 @@ class MSDDLCompiler(compiler.DDLCompiler):
for col in index.kwargs["mssql_include"]]
text += " INCLUDE (%s)" \
- % ', '.join([preparer.quote(c.name, c.quote)
+ % ', '.join([preparer.quote(c.name)
for c in inclusions])
return text
@@ -1035,7 +1035,7 @@ class MSIdentifierPreparer(compiler.IdentifierPreparer):
def _escape_identifier(self, value):
return value
- def quote_schema(self, schema, force=True):
+ def quote_schema(self, schema, force=None):
"""Prepare a quoted table and schema name."""
result = '.'.join([self.quote(x, force) for x in schema.split('.')])
return result
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py
index b36b70774..fd6a47a10 100644
--- a/lib/sqlalchemy/dialects/mysql/base.py
+++ b/lib/sqlalchemy/dialects/mysql/base.py
@@ -1451,7 +1451,7 @@ class MySQLDDLCompiler(compiler.DDLCompiler):
constraint_string += ", \n\t"
constraint_string += "KEY %s (%s)" % (
self.preparer.quote(
- "idx_autoinc_%s" % auto_inc_column.name, None
+ "idx_autoinc_%s" % auto_inc_column.name
),
self.preparer.format_column(auto_inc_column)
)
@@ -1557,7 +1557,7 @@ class MySQLDDLCompiler(compiler.DDLCompiler):
if 'mysql_using' in index.kwargs:
using = index.kwargs['mysql_using']
- text += " USING %s" % (preparer.quote(using, index.quote))
+ text += " USING %s" % (preparer.quote(using))
return text
@@ -1566,8 +1566,7 @@ class MySQLDDLCompiler(compiler.DDLCompiler):
visit_primary_key_constraint(constraint)
if "mysql_using" in constraint.kwargs:
using = constraint.kwargs['mysql_using']
- text += " USING %s" % (
- self.preparer.quote(using, constraint.quote))
+ text += " USING %s" % (self.preparer.quote(using))
return text
def visit_drop_index(self, drop):
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index e013799db..b82d3016f 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -362,7 +362,8 @@ class _OracleRowid(oracle.ROWID):
class OracleCompiler_cx_oracle(OracleCompiler):
- def bindparam_string(self, name, quote=None, **kw):
+ def bindparam_string(self, name, **kw):
+ quote = getattr(name, 'quote', None)
if quote is True or quote is not False and \
self.preparer._bindparam_requires_quotes(name):
quoted_name = '"%s"' % name
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index 6ccf7190e..8938b3193 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -1094,7 +1094,7 @@ class PGDDLCompiler(compiler.DDLCompiler):
if 'postgresql_using' in index.kwargs:
using = index.kwargs['postgresql_using']
- text += "USING %s " % preparer.quote(using, index.quote)
+ text += "USING %s " % preparer.quote(using)
ops = index.kwargs.get('postgresql_ops', {})
text += "(%s)" \
@@ -1128,7 +1128,7 @@ class PGDDLCompiler(compiler.DDLCompiler):
elements = []
for c in constraint.columns:
op = constraint.operators[c.name]
- elements.append(self.preparer.quote(c.name, c.quote)+' WITH '+op)
+ elements.append(self.preparer.quote(c.name) + ' WITH '+op)
text += "EXCLUDE USING %s (%s)" % (constraint.using, ', '.join(elements))
if constraint.where is not None:
sqltext = sql_util.expression_as_ddl(constraint.where)
@@ -1250,9 +1250,9 @@ class PGIdentifierPreparer(compiler.IdentifierPreparer):
if not type_.name:
raise exc.CompileError("Postgresql ENUM type requires a name.")
- name = self.quote(type_.name, type_.quote)
+ name = self.quote(type_.name)
if not self.omit_schema and use_schema and type_.schema is not None:
- name = self.quote_schema(type_.schema, type_.quote) + "." + name
+ name = self.quote_schema(type_.schema) + "." + name
return name
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index 735113a26..9a10e829e 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -1672,6 +1672,17 @@ class Engine(Connectable, log.Identified):
return self.dialect.get_table_names(conn, schema)
def has_table(self, table_name, schema=None):
+ """Return True if the given backend has a table of the given name.
+
+ .. seealso::
+
+ :ref:`metadata_reflection_inspector` - detailed schema inspection using
+ the :class:`.Inspector` interface.
+
+ :class:`.quoted_name` - used to pass quoting information along
+ with a schema identifier.
+
+ """
return self.run_callable(self.dialect.has_table, table_name, schema)
def raw_connection(self):
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 90c7f5993..609375c39 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -27,6 +27,7 @@ AUTOCOMMIT_REGEXP = re.compile(
re.I | re.UNICODE)
+
class DefaultDialect(interfaces.Dialect):
"""Default implementation of Dialect"""
@@ -160,6 +161,7 @@ class DefaultDialect(interfaces.Dialect):
self._encoder = codecs.getencoder(self.encoding)
self._decoder = processors.to_unicode_processor_factory(self.encoding)
+
@util.memoized_property
def _type_memos(self):
return weakref.WeakKeyDictionary()
diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py
index 29ede9579..340af1abb 100644
--- a/lib/sqlalchemy/engine/reflection.py
+++ b/lib/sqlalchemy/engine/reflection.py
@@ -169,7 +169,7 @@ class Inspector(object):
database's default schema is
used, else the named schema is searched. If the database does not
support named schemas, behavior is undefined if ``schema`` is not
- passed as ``None``.
+ passed as ``None``. For special quoting, use :class:`.quoted_name`.
:param order_by: Optional, may be the string "foreign_key" to sort
the result on foreign key dependencies.
@@ -206,6 +206,13 @@ class Inspector(object):
This currently includes some options that apply to MySQL tables.
+ :param table_name: string name of the table. For special quoting,
+ use :class:`.quoted_name`.
+
+ :param schema: string schema name; if omitted, uses the default schema
+ of the database connection. For special quoting,
+ use :class:`.quoted_name`.
+
"""
if hasattr(self.dialect, 'get_table_options'):
return self.dialect.get_table_options(
@@ -217,6 +224,8 @@ class Inspector(object):
"""Return all view names in `schema`.
:param schema: Optional, retrieve names from a non-default schema.
+ For special quoting, use :class:`.quoted_name`.
+
"""
return self.dialect.get_view_names(self.bind, schema,
@@ -226,6 +235,8 @@ class Inspector(object):
"""Return definition for `view_name`.
:param schema: Optional, retrieve names from a non-default schema.
+ For special quoting, use :class:`.quoted_name`.
+
"""
return self.dialect.get_view_definition(
@@ -251,6 +262,14 @@ class Inspector(object):
attrs
dict containing optional column attributes
+
+ :param table_name: string name of the table. For special quoting,
+ use :class:`.quoted_name`.
+
+ :param schema: string schema name; if omitted, uses the default schema
+ of the database connection. For special quoting,
+ use :class:`.quoted_name`.
+
"""
col_defs = self.dialect.get_columns(self.bind, table_name, schema,
@@ -288,6 +307,13 @@ class Inspector(object):
name
optional name of the primary key constraint.
+ :param table_name: string name of the table. For special quoting,
+ use :class:`.quoted_name`.
+
+ :param schema: string schema name; if omitted, uses the default schema
+ of the database connection. For special quoting,
+ use :class:`.quoted_name`.
+
"""
return self.dialect.get_pk_constraint(self.bind, table_name, schema,
info_cache=self.info_cache,
@@ -315,6 +341,13 @@ class Inspector(object):
name
optional name of the foreign key constraint.
+ :param table_name: string name of the table. For special quoting,
+ use :class:`.quoted_name`.
+
+ :param schema: string schema name; if omitted, uses the default schema
+ of the database connection. For special quoting,
+ use :class:`.quoted_name`.
+
"""
return self.dialect.get_foreign_keys(self.bind, table_name, schema,
@@ -336,6 +369,13 @@ class Inspector(object):
unique
boolean
+ :param table_name: string name of the table. For special quoting,
+ use :class:`.quoted_name`.
+
+ :param schema: string schema name; if omitted, uses the default schema
+ of the database connection. For special quoting,
+ use :class:`.quoted_name`.
+
"""
return self.dialect.get_indexes(self.bind, table_name,
@@ -354,6 +394,13 @@ class Inspector(object):
column_names
list of column names in order
+ :param table_name: string name of the table. For special quoting,
+ use :class:`.quoted_name`.
+
+ :param schema: string schema name; if omitted, uses the default schema
+ of the database connection. For special quoting,
+ use :class:`.quoted_name`.
+
.. versionadded:: 0.9.0
"""
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
index 2a3176d04..85c6e730e 100644
--- a/lib/sqlalchemy/sql/base.py
+++ b/lib/sqlalchemy/sql/base.py
@@ -30,6 +30,7 @@ class Immutable(object):
return self
+
def _from_objects(*elements):
return itertools.chain(*[element._from_objects for element in elements])
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
index 5d05cbc29..8e3cf1729 100644
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -24,7 +24,7 @@ To generate user-defined SQL strings, see
import re
from . import schema, sqltypes, operators, functions, \
- util as sql_util, visitors, elements, selectable
+ util as sql_util, visitors, elements, selectable, base
from .. import util, exc
import decimal
import itertools
@@ -280,10 +280,6 @@ class _CompileLabel(visitors.Visitable):
def type(self):
return self.element.type
- @property
- def quote(self):
- return self.element.quote
-
class SQLCompiler(Compiled):
"""Default implementation of Compiled.
@@ -548,16 +544,14 @@ class SQLCompiler(Compiled):
if is_literal:
name = self.escape_literal_column(name)
else:
- name = self.preparer.quote(name, column.quote)
+ name = self.preparer.quote(name)
table = column.table
if table is None or not include_table or not table.named_with_column:
return name
else:
if table.schema:
- schema_prefix = self.preparer.quote_schema(
- table.schema,
- table.quote_schema) + '.'
+ schema_prefix = self.preparer.quote_schema(table.schema) + '.'
else:
schema_prefix = ''
tablename = table.name
@@ -565,7 +559,7 @@ class SQLCompiler(Compiled):
tablename = self._truncated_identifier("alias", tablename)
return schema_prefix + \
- self.preparer.quote(tablename, table.quote) + \
+ self.preparer.quote(tablename) + \
"." + name
def escape_literal_column(self, text):
@@ -953,7 +947,7 @@ class SQLCompiler(Compiled):
self.binds[bindparam.key] = self.binds[name] = bindparam
- return self.bindparam_string(name, quote=bindparam.quote, **kwargs)
+ return self.bindparam_string(name, **kwargs)
def render_literal_bindparam(self, bindparam, **kw):
value = bindparam.value
@@ -1023,8 +1017,7 @@ class SQLCompiler(Compiled):
self.anon_map[derived] = anonymous_counter + 1
return derived + "_" + str(anonymous_counter)
- def bindparam_string(self, name, quote=None,
- positional_names=None, **kw):
+ def bindparam_string(self, name, positional_names=None, **kw):
if self.positional:
if positional_names is not None:
positional_names.append(name)
@@ -1574,12 +1567,10 @@ class SQLCompiler(Compiled):
fromhints=None, **kwargs):
if asfrom or ashint:
if getattr(table, "schema", None):
- ret = self.preparer.quote_schema(table.schema,
- table.quote_schema) + \
- "." + self.preparer.quote(table.name,
- table.quote)
+ ret = self.preparer.quote_schema(table.schema) + \
+ "." + self.preparer.quote(table.name)
else:
- ret = self.preparer.quote(table.name, table.quote)
+ ret = self.preparer.quote(table.name)
if fromhints and table in fromhints:
ret = self.format_from_hint_text(ret, table,
fromhints[table], iscrud)
@@ -1796,8 +1787,7 @@ class SQLCompiler(Compiled):
if name is None:
name = col.key
bindparam = elements.BindParameter(name, value,
- type_=col.type, required=required,
- quote=col.quote)
+ type_=col.type, required=required)
bindparam._is_crud = True
return bindparam._compiler_dispatch(self)
@@ -2193,11 +2183,11 @@ class DDLCompiler(Compiled):
return self.sql_compiler.post_process_text(ddl.statement % context)
def visit_create_schema(self, create):
- schema = self.preparer.format_schema(create.element, create.quote)
+ schema = self.preparer.format_schema(create.element)
return "CREATE SCHEMA " + schema
def visit_drop_schema(self, drop):
- schema = self.preparer.format_schema(drop.element, drop.quote)
+ schema = self.preparer.format_schema(drop.element)
text = "DROP SCHEMA " + schema
if drop.cascade:
text += " CASCADE"
@@ -2325,8 +2315,7 @@ class DDLCompiler(Compiled):
def _prepared_index_name(self, index, include_schema=False):
if include_schema and index.table is not None and index.table.schema:
schema = index.table.schema
- schema_name = self.preparer.quote_schema(schema,
- index.table.quote_schema)
+ schema_name = self.preparer.quote_schema(schema)
else:
schema_name = None
@@ -2340,9 +2329,7 @@ class DDLCompiler(Compiled):
else:
self.dialect.validate_identifier(ident)
- index_name = self.preparer.quote(
- ident,
- index.quote)
+ index_name = self.preparer.quote(ident)
if schema_name:
index_name = schema_name + "." + index_name
@@ -2424,7 +2411,7 @@ class DDLCompiler(Compiled):
text += "CONSTRAINT %s " % \
self.preparer.format_constraint(constraint)
text += "PRIMARY KEY "
- text += "(%s)" % ', '.join(self.preparer.quote(c.name, c.quote)
+ text += "(%s)" % ', '.join(self.preparer.quote(c.name)
for c in constraint)
text += self.define_constraint_deferrability(constraint)
return text
@@ -2437,11 +2424,11 @@ class DDLCompiler(Compiled):
preparer.format_constraint(constraint)
remote_table = list(constraint._elements.values())[0].column.table
text += "FOREIGN KEY(%s) REFERENCES %s (%s)" % (
- ', '.join(preparer.quote(f.parent.name, f.parent.quote)
+ ', '.join(preparer.quote(f.parent.name)
for f in constraint._elements.values()),
self.define_constraint_remote_table(
constraint, remote_table, preparer),
- ', '.join(preparer.quote(f.column.name, f.column.quote)
+ ', '.join(preparer.quote(f.column.name)
for f in constraint._elements.values())
)
text += self.define_constraint_match(constraint)
@@ -2460,7 +2447,7 @@ class DDLCompiler(Compiled):
text += "CONSTRAINT %s " % \
self.preparer.format_constraint(constraint)
text += "UNIQUE (%s)" % (
- ', '.join(self.preparer.quote(c.name, c.quote)
+ ', '.join(self.preparer.quote(c.name)
for c in constraint))
text += self.define_constraint_deferrability(constraint)
return text
@@ -2714,15 +2701,25 @@ class IdentifierPreparer(object):
or not self.legal_characters.match(util.text_type(value))
or (lc_value != value))
- def quote_schema(self, schema, force):
- """Quote a schema.
+ def quote_schema(self, schema, force=None):
+ """Conditionally quote a schema.
+
+ Subclasses can override this to provide database-dependent
+ quoting behavior for schema names.
+
+ the 'force' flag should be considered deprecated.
- Subclasses should override this to provide database-dependent
- quoting behavior.
"""
return self.quote(schema, force)
- def quote(self, ident, force):
+ def quote(self, ident, force=None):
+ """Conditionally quote an identifier.
+
+ the 'force' flag should be considered deprecated.
+ """
+
+ force = getattr(ident, "quote", None)
+
if force is None:
if ident in self._strings:
return self._strings[ident]
@@ -2738,38 +2735,35 @@ class IdentifierPreparer(object):
return ident
def format_sequence(self, sequence, use_schema=True):
- name = self.quote(sequence.name, sequence.quote)
- if not self.omit_schema and use_schema and \
- sequence.schema is not None:
- name = self.quote_schema(sequence.schema, sequence.quote) + \
- "." + name
+ name = self.quote(sequence.name)
+ if not self.omit_schema and use_schema and sequence.schema is not None:
+ name = self.quote_schema(sequence.schema) + "." + name
return name
def format_label(self, label, name=None):
- return self.quote(name or label.name, label.quote)
+ return self.quote(name or label.name)
def format_alias(self, alias, name=None):
- return self.quote(name or alias.name, alias.quote)
+ return self.quote(name or alias.name)
def format_savepoint(self, savepoint, name=None):
- return self.quote(name or savepoint.ident, savepoint.quote)
+ return self.quote(name or savepoint.ident)
def format_constraint(self, constraint):
- return self.quote(constraint.name, constraint.quote)
+ return self.quote(constraint.name)
def format_table(self, table, use_schema=True, name=None):
"""Prepare a quoted table and schema name."""
if name is None:
name = table.name
- result = self.quote(name, table.quote)
+ result = self.quote(name)
if not self.omit_schema and use_schema \
and getattr(table, "schema", None):
- result = self.quote_schema(table.schema, table.quote_schema) + \
- "." + result
+ result = self.quote_schema(table.schema) + "." + result
return result
- def format_schema(self, name, quote):
+ def format_schema(self, name, quote=None):
"""Prepare a quoted schema name."""
return self.quote(name, quote)
@@ -2784,10 +2778,9 @@ class IdentifierPreparer(object):
if use_table:
return self.format_table(
column.table, use_schema=False,
- name=table_name) + "." + \
- self.quote(name, column.quote)
+ name=table_name) + "." + self.quote(name)
else:
- return self.quote(name, column.quote)
+ return self.quote(name)
else:
# literal textual elements get stuck into ColumnClause a lot,
# which shouldn't get quoted
@@ -2807,7 +2800,7 @@ class IdentifierPreparer(object):
if not self.omit_schema and use_schema and \
getattr(table, 'schema', None):
- return (self.quote_schema(table.schema, table.quote_schema),
+ return (self.quote_schema(table.schema),
self.format_table(table, use_schema=False))
else:
return (self.format_table(table, use_schema=False), )
diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py
index 17fb40628..99dd193f3 100644
--- a/lib/sqlalchemy/sql/elements.py
+++ b/lib/sqlalchemy/sql/elements.py
@@ -530,7 +530,6 @@ class ColumnElement(ClauseElement, operators.ColumnOperators):
__visit_name__ = 'column'
primary_key = False
foreign_keys = []
- quote = None
_label = None
_key_label = None
_alt_names = ()
@@ -693,7 +692,6 @@ class BindParameter(ColumnElement):
"""
__visit_name__ = 'bindparam'
- quote = None
_is_crud = False
@@ -778,6 +776,8 @@ class BindParameter(ColumnElement):
if value is NO_ARG:
value = None
+ if quote is not None:
+ key = quoted_name(key, quote)
if unique:
self.key = _anonymous_label('%%(%d %s)s' % (id(self), key
@@ -800,7 +800,6 @@ class BindParameter(ColumnElement):
self.callable = callable_
self.isoutparam = isoutparam
self.required = required
- self.quote = quote
if type_ is None:
if _compared_to_type is not None:
self.type = \
@@ -1838,7 +1837,6 @@ class Label(ColumnElement):
self.key = self._label = self._key_label = self.name
self._element = element
self._type = type_
- self.quote = element.quote
self._proxies = [element]
@util.memoized_property
@@ -2027,6 +2025,12 @@ class ColumnClause(Immutable, ColumnElement):
else:
label = t.name + "_" + name
+ # propagate name quoting rules for labels.
+ if getattr(name, "quote", None) is not None:
+ label = quoted_name(label, name.quote)
+ elif getattr(t.name, "quote", None) is not None:
+ label = quoted_name(label, t.name.quote)
+
# ensure the label name doesn't conflict with that
# of an existing column
if label in t.c:
@@ -2078,7 +2082,6 @@ class _IdentifiedClause(Executable, ClauseElement):
__visit_name__ = 'identified'
_execution_options = \
Executable._execution_options.union({'autocommit': False})
- quote = None
def __init__(self, ident):
self.ident = ident
@@ -2096,10 +2099,92 @@ class ReleaseSavepointClause(_IdentifiedClause):
__visit_name__ = 'release_savepoint'
-class _truncated_label(util.text_type):
+class quoted_name(util.text_type):
+ """Represent a SQL identifier combined with quoting preferences.
+
+ :class:`.quoted_name` is a Python unicode/str subclass which
+ represents a particular identifier name along with a
+ ``quote`` flag. This ``quote`` flag, when set to
+ ``True`` or ``False``, overrides automatic quoting behavior
+ for this identifier in order to either unconditionally quote
+ or to not quote the name. If left at its default of ``None``,
+ quoting behavior is applied to the identifier on a per-backend basis
+ based on an examination of the token itself.
+
+ A :class:`.quoted_name` object with ``quote=True`` is also
+ prevented from being modified in the case of a so-called
+ "name normalize" option. Certain database backends, such as
+ Oracle, Firebird, and DB2 "normalize" case-insensitive names
+ as uppercase. The SQLAlchemy dialects for these backends
+ convert from SQLAlchemy's lower-case-means-insensitive convention
+ to the upper-case-means-insensitive conventions of those backends.
+ The ``quote=True`` flag here will prevent this conversion from occurring
+ to support an identifier that's quoted as all lower case against
+ such a backend.
+
+ The :class:`.quoted_name` object is normally created automatically
+ when specifying the name for key schema constructs such as :class:`.Table`,
+ :class:`.Column`, and others. The class can also be passed explicitly
+ as the name to any function that receives a name which can be quoted.
+ Such as to use the :meth:`.Engine.has_table` method with an unconditionally
+ quoted name::
+
+ from sqlaclchemy import create_engine
+ from sqlalchemy.sql.elements import quoted_name
+
+ engine = create_engine("oracle+cx_oracle://some_dsn")
+ engine.has_table(quoted_name("some_table", True))
+
+ The above logic will run the "has table" logic against the Oracle backend,
+ passing the name exactly as ``"some_table"`` without converting to
+ upper case.
+
+ .. versionadded:: 0.9.0
+
+ """
+
+ def __new__(cls, value, quote):
+ if value is None:
+ return None
+ elif isinstance(value, cls) and (
+ quote is None or value.quote == quote
+ ):
+ return value
+ self = super(quoted_name, cls).__new__(cls, value)
+ self.quote = quote
+ return self
+
+ def __reduce__(self):
+ return quoted_name, (util.text_type(self), self.quote)
+
+ @util.memoized_instancemethod
+ def lower(self):
+ if self.quote:
+ return self
+ else:
+ return util.text_type(self).lower()
+
+ @util.memoized_instancemethod
+ def upper(self):
+ if self.quote:
+ return self
+ else:
+ return util.text_type(self).upper()
+
+ def __repr__(self):
+ return "'%s'" % self
+
+class _truncated_label(quoted_name):
"""A unicode subclass used to identify symbolic "
"names that may require truncation."""
+ def __new__(cls, value, quote=None):
+ quote = getattr(value, "quote", quote)
+ return super(_truncated_label, cls).__new__(cls, value, quote)
+
+ def __reduce__(self):
+ return self.__class__, (util.text_type(self), self.quote)
+
def apply_map(self, map_):
return self
@@ -2116,16 +2201,25 @@ class _anonymous_label(_truncated_label):
def __add__(self, other):
return _anonymous_label(
- util.text_type(self) +
- util.text_type(other))
+ quoted_name(
+ util.text_type.__add__(self, util.text_type(other)),
+ self.quote)
+ )
def __radd__(self, other):
return _anonymous_label(
- util.text_type(other) +
- util.text_type(self))
+ quoted_name(
+ util.text_type.__add__(util.text_type(other), self),
+ self.quote)
+ )
def apply_map(self, map_):
- return self % map_
+ if self.quote is not None:
+ # preserve quoting only if necessary
+ return quoted_name(self % map_, self.quote)
+ else:
+ # else skip the constructor call
+ return self % map_
def _as_truncated(value):
diff --git a/lib/sqlalchemy/sql/schema.py b/lib/sqlalchemy/sql/schema.py
index ca83236c7..b190c3874 100644
--- a/lib/sqlalchemy/sql/schema.py
+++ b/lib/sqlalchemy/sql/schema.py
@@ -37,7 +37,7 @@ from . import type_api
from .base import _bind_or_error, ColumnCollection
from .elements import ClauseElement, ColumnClause, _truncated_label, \
_as_truncated, TextClause, _literal_as_text,\
- ColumnElement, _find_columns
+ ColumnElement, _find_columns, quoted_name
from .selectable import TableClause
import collections
import sqlalchemy
@@ -67,7 +67,6 @@ class SchemaItem(SchemaEventTarget, visitors.Visitable):
"""Base class for items that define a database schema."""
__visit_name__ = 'schema_item'
- quote = None
def _init_items(self, *args):
"""Initialize the list of child items for this SchemaItem."""
@@ -83,6 +82,17 @@ class SchemaItem(SchemaEventTarget, visitors.Visitable):
def __repr__(self):
return util.generic_repr(self)
+ @property
+ @util.deprecated('0.9', 'Use ``<obj>.name.quote``')
+ def quote(self):
+ """Return the value of the ``quote`` flag passed
+ to this schema object, for those schema items which
+ have a ``name`` field.
+
+ """
+
+ return self.name.quote
+
@util.memoized_property
def info(self):
"""Info dictionary associated with the object, allowing user-defined
@@ -114,25 +124,29 @@ class Table(SchemaItem, TableClause):
a second time will return the *same* :class:`.Table` object - in this way
the :class:`.Table` constructor acts as a registry function.
- See also:
+ .. seealso::
- :ref:`metadata_describing` - Introduction to database metadata
+ :ref:`metadata_describing` - Introduction to database metadata
Constructor arguments are as follows:
:param name: The name of this table as represented in the database.
- This property, along with the *schema*, indicates the *singleton
- identity* of this table in relation to its parent :class:`.MetaData`.
+ The table name, along with the value of the ``schema`` parameter,
+ forms a key which uniquely identifies this :class:`.Table` within
+ the owning :class:`.MetaData` collection.
Additional calls to :class:`.Table` with the same name, metadata,
and schema name will return the same :class:`.Table` object.
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. Names with any number of upper
- case characters will be quoted and sent exactly. Note that this
- behavior applies even for databases which standardize upper
- case names as case insensitive such as Oracle.
+ 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.
+
+ To enable unconditional quoting for the table name, specify the flag
+ ``quote=True`` to the constructor, or use the :class:`.quoted_name`
+ construct to specify the name.
:param metadata: a :class:`.MetaData` object which will contain this
table. The metadata is used as a point of association of this table
@@ -263,9 +277,17 @@ class Table(SchemaItem, TableClause):
:param quote_schema: same as 'quote' but applies to the schema identifier.
- :param schema: The *schema name* for this table, which is required if
+ :param schema: The schema name for this table, which is required if
the table resides in a schema other than the default selected schema
- for the engine's database connection. Defaults to ``None``.
+ for the engine's database connection. Defaults to ``None``.
+
+ The quoting rules for the schema name are the same as those for the
+ ``name`` parameter, in that quoting is applied for reserved words or
+ case-sensitive names; to enable unconditional quoting for the
+ schema name, specify the flag
+ ``quote_schema=True`` to the constructor, or use the :class:`.quoted_name`
+ construct to specify the name.
+
:param useexisting: Deprecated. Use extend_existing.
@@ -329,6 +351,15 @@ class Table(SchemaItem, TableClause):
#metadata._remove_table(name, schema)
raise
+
+ @property
+ @util.deprecated('0.9', 'Use ``table.schema.quote``')
+ def quote_schema(self):
+ """Return the value of the ``quote_schema`` flag passed
+ to this :class:`.Table`."""
+
+ return self.schema.quote
+
def __init__(self, *args, **kw):
"""Constructor for :class:`~.schema.Table`.
@@ -341,15 +372,15 @@ class Table(SchemaItem, TableClause):
# calling the superclass constructor.
def _init(self, name, metadata, *args, **kwargs):
- super(Table, self).__init__(name)
+ super(Table, self).__init__(quoted_name(name, kwargs.pop('quote', None)))
self.metadata = metadata
+
self.schema = kwargs.pop('schema', None)
if self.schema is None:
self.schema = metadata.schema
- self.quote_schema = kwargs.pop(
- 'quote_schema', metadata.quote_schema)
else:
- self.quote_schema = kwargs.pop('quote_schema', None)
+ quote_schema = kwargs.pop('quote_schema', None)
+ self.schema = quoted_name(self.schema, quote_schema)
self.indexes = set()
self.constraints = set()
@@ -370,7 +401,7 @@ class Table(SchemaItem, TableClause):
include_columns = kwargs.pop('include_columns', None)
self.implicit_returning = kwargs.pop('implicit_returning', True)
- self.quote = kwargs.pop('quote', None)
+
if 'info' in kwargs:
self.info = kwargs.pop('info')
if 'listeners' in kwargs:
@@ -444,7 +475,8 @@ class Table(SchemaItem, TableClause):
for key in ('quote', 'quote_schema'):
if key in kwargs:
- setattr(self, key, kwargs.pop(key))
+ raise exc.ArgumentError(
+ "Can't redefine 'quote' or 'quote_schema' arguments")
if 'info' in kwargs:
self.info = kwargs.pop('info')
@@ -597,7 +629,9 @@ class Table(SchemaItem, TableClause):
:class:`.Table`, using the given :class:`.Connectable`
for connectivity.
- See also :meth:`.MetaData.create_all`.
+ .. seealso::
+
+ :meth:`.MetaData.create_all`.
"""
@@ -612,7 +646,9 @@ class Table(SchemaItem, TableClause):
:class:`.Table`, using the given :class:`.Connectable`
for connectivity.
- See also :meth:`.MetaData.drop_all`.
+ .. seealso::
+
+ :meth:`.MetaData.drop_all`.
"""
if bind is None:
@@ -925,6 +961,12 @@ class Column(SchemaItem, ColumnClause):
"May not pass type_ positionally and as a keyword.")
type_ = args.pop(0)
+ if name is not None:
+ name = quoted_name(name, kwargs.pop('quote', None))
+ elif "quote" in kwargs:
+ raise exc.ArgumentError("Explicit 'name' is required when "
+ "sending 'quote' argument")
+
super(Column, self).__init__(name, type_)
self.key = kwargs.pop('key', name)
self.primary_key = kwargs.pop('primary_key', False)
@@ -935,7 +977,6 @@ class Column(SchemaItem, ColumnClause):
self.index = kwargs.pop('index', None)
self.unique = kwargs.pop('unique', None)
self.system = kwargs.pop('system', False)
- self.quote = kwargs.pop('quote', None)
self.doc = kwargs.pop('doc', None)
self.onupdate = kwargs.pop('onupdate', None)
self.autoincrement = kwargs.pop('autoincrement', True)
@@ -988,6 +1029,10 @@ class Column(SchemaItem, ColumnClause):
raise exc.ArgumentError(
"Unknown arguments passed to Column: " + repr(list(kwargs)))
+# @property
+# def quote(self):
+# return getattr(self.name, "quote", None)
+
def __str__(self):
if self.name is None:
return "(no name)"
@@ -1123,7 +1168,7 @@ class Column(SchemaItem, ColumnClause):
nullable=self.nullable,
unique=self.unique,
system=self.system,
- quote=self.quote,
+ #quote=self.quote,
index=self.index,
autoincrement=self.autoincrement,
default=self.default,
@@ -1161,7 +1206,6 @@ class Column(SchemaItem, ColumnClause):
key=key if key else name if name else self.key,
primary_key=self.primary_key,
nullable=self.nullable,
- quote=self.quote,
_proxies=[self], *fk)
except TypeError:
util.raise_from_cause(
@@ -1791,7 +1835,11 @@ class Sequence(DefaultGenerator):
be emitted as well. For platforms that don't support sequences,
the :class:`.Sequence` construct is ignored.
- See also: :class:`.CreateSequence` :class:`.DropSequence`
+ .. seealso::
+
+ :class:`.CreateSequence`
+
+ :class:`.DropSequence`
"""
@@ -1828,6 +1876,8 @@ class Sequence(DefaultGenerator):
forces quoting of the schema name on or off. When left at its
default of ``None``, normal quoting rules based on casing and reserved
words take place.
+ :param quote_schema: set the quoting preferences for the ``schema``
+ name.
:param metadata: optional :class:`.MetaData` object which will be
associated with this :class:`.Sequence`. A :class:`.Sequence`
that is associated with a :class:`.MetaData` gains access to the
@@ -1855,17 +1905,14 @@ class Sequence(DefaultGenerator):
"""
super(Sequence, self).__init__(for_update=for_update)
- self.name = name
+ self.name = quoted_name(name, quote)
self.start = start
self.increment = increment
self.optional = optional
- self.quote = quote
if metadata is not None and schema is None and metadata.schema:
self.schema = schema = metadata.schema
- self.quote_schema = metadata.quote_schema
else:
- self.schema = schema
- self.quote_schema = quote_schema
+ self.schema = quoted_name(schema, quote_schema)
self.metadata = metadata
self._key = _get_table_key(name, schema)
if metadata:
@@ -2556,7 +2603,7 @@ class Index(ColumnCollectionMixin, SchemaItem):
# objects are present
ColumnCollectionMixin.__init__(self, *columns)
- self.name = name
+ self.name = quoted_name(name, kw.pop("quote", None))
self.unique = kw.pop('unique', False)
self.kwargs = kw
@@ -2598,7 +2645,9 @@ class Index(ColumnCollectionMixin, SchemaItem):
:class:`.Index`, using the given :class:`.Connectable`
for connectivity.
- See also :meth:`.MetaData.create_all`.
+ .. seealso::
+
+ :meth:`.MetaData.create_all`.
"""
if bind is None:
@@ -2611,7 +2660,9 @@ class Index(ColumnCollectionMixin, SchemaItem):
:class:`.Index`, using the given :class:`.Connectable`
for connectivity.
- See also :meth:`.MetaData.drop_all`.
+ .. seealso::
+
+ :meth:`.MetaData.drop_all`.
"""
if bind is None:
@@ -2653,12 +2704,9 @@ class MetaData(SchemaItem):
MetaData is a thread-safe object after tables have been explicitly defined
or loaded via reflection.
- See also:
-
- :ref:`metadata_describing` - Introduction to database metadata
+ .. seealso::
- .. index::
- single: thread safety; MetaData
+ :ref:`metadata_describing` - Introduction to database metadata
"""
@@ -2695,8 +2743,7 @@ class MetaData(SchemaItem):
"""
self.tables = util.immutabledict()
- self.schema = schema
- self.quote_schema = quote_schema
+ self.schema = quoted_name(schema, quote_schema)
self._schemas = set()
self._sequences = {}
self._fk_memos = collections.defaultdict(list)
@@ -2742,7 +2789,6 @@ class MetaData(SchemaItem):
def __getstate__(self):
return {'tables': self.tables,
'schema': self.schema,
- 'quote_schema': self.quote_schema,
'schemas': self._schemas,
'sequences': self._sequences,
'fk_memos': self._fk_memos}
@@ -2750,7 +2796,6 @@ class MetaData(SchemaItem):
def __setstate__(self, state):
self.tables = state['tables']
self.schema = state['schema']
- self.quote_schema = state['quote_schema']
self._bind = None
self._sequences = state['sequences']
self._schemas = state['schemas']
diff --git a/lib/sqlalchemy/sql/selectable.py b/lib/sqlalchemy/sql/selectable.py
index c32de77ea..e06262c6d 100644
--- a/lib/sqlalchemy/sql/selectable.py
+++ b/lib/sqlalchemy/sql/selectable.py
@@ -136,7 +136,6 @@ class FromClause(Selectable):
__visit_name__ = 'fromclause'
named_with_column = False
_hide_froms = []
- quote = None
schema = None
_memoized_property = util.group_expirable_memoized_property(["_columns"])
diff --git a/lib/sqlalchemy/sql/sqltypes.py b/lib/sqlalchemy/sql/sqltypes.py
index 38e0d1bd3..db0ad248c 100644
--- a/lib/sqlalchemy/sql/sqltypes.py
+++ b/lib/sqlalchemy/sql/sqltypes.py
@@ -12,6 +12,7 @@ import datetime as dt
import codecs
from .type_api import TypeEngine, TypeDecorator, to_instance
+from .elements import quoted_name
from .default_comparator import _DefaultColumnComparator
from .. import exc, util, processors
from .base import _bind_or_error, SchemaEventTarget
@@ -840,8 +841,11 @@ class SchemaType(SchemaEventTarget):
"""
def __init__(self, **kw):
- self.name = kw.pop('name', None)
- self.quote = kw.pop('quote', None)
+ name = kw.pop('name', None)
+ if name is not None:
+ self.name = quoted_name(name, kw.pop('quote', None))
+ else:
+ self.name = None
self.schema = kw.pop('schema', None)
self.metadata = kw.pop('metadata', None)
self.inherit_schema = kw.pop('inherit_schema', False)
@@ -896,7 +900,6 @@ class SchemaType(SchemaEventTarget):
schema = kw.pop('schema', self.schema)
metadata = kw.pop('metadata', self.metadata)
return impltype(name=self.name,
- quote=self.quote,
schema=schema,
metadata=metadata,
inherit_schema=self.inherit_schema,
@@ -1008,10 +1011,7 @@ class Enum(String, SchemaType):
owning :class:`.Table`. If this behavior is desired,
set the ``inherit_schema`` flag to ``True``.
- :param quote: Force quoting to be on or off on the type's name. If
- left as the default of `None`, the usual schema-level "case
- sensitive"/"reserved name" rules are used to determine if this
- type's name should be quoted.
+ :param quote: Set explicit quoting preferences for the type's name.
:param inherit_schema: When ``True``, the "schema" from the owning
:class:`.Table` will be copied to the "schema" attribute of this
@@ -1071,7 +1071,6 @@ class Enum(String, SchemaType):
metadata = kw.pop('metadata', self.metadata)
if issubclass(impltype, Enum):
return impltype(name=self.name,
- quote=self.quote,
schema=schema,
metadata=metadata,
convert_unicode=self.convert_unicode,
diff --git a/lib/sqlalchemy/sql/util.py b/lib/sqlalchemy/sql/util.py
index b927f1b3c..2a5c2e277 100644
--- a/lib/sqlalchemy/sql/util.py
+++ b/lib/sqlalchemy/sql/util.py
@@ -269,7 +269,6 @@ def expression_as_ddl(clause):
elif isinstance(element, ColumnClause) and \
element.table is not None:
col = ColumnClause(element.name)
- col.quote = element.quote
return col
else:
return None
diff --git a/test/ext/declarative/test_basic.py b/test/ext/declarative/test_basic.py
index 2de0032dd..8917d7772 100644
--- a/test/ext/declarative/test_basic.py
+++ b/test/ext/declarative/test_basic.py
@@ -1276,8 +1276,10 @@ class DeclarativeTest(DeclarativeTestBase):
# case
sa.orm.configure_mappers()
- eq_(str(list(Address.user_id.property.columns[0].foreign_keys)[0]),
- "ForeignKey('users.id')")
+ eq_(
+ list(Address.user_id.property.columns[0].foreign_keys)[0].column,
+ User.__table__.c.id
+ )
Base.metadata.create_all()
u1 = User(name='u1', addresses=[Address(email='one'),
Address(email='two')])
diff --git a/test/profiles.txt b/test/profiles.txt
index 4a392d208..b3c83391b 100644
--- a/test/profiles.txt
+++ b/test/profiles.txt
@@ -16,6 +16,7 @@
test.aaa_profiling.test_compiler.CompileTest.test_insert 2.6_sqlite_pysqlite_nocextensions 72
test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_cextensions 72
test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_mysqldb_nocextensions 72
+test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_mysql_oursql_nocextensions 72
test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_oracle_cx_oracle_nocextensions 72
test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_cextensions 72
test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_postgresql_psycopg2_nocextensions 72
@@ -23,7 +24,10 @@ test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_cex
test.aaa_profiling.test_compiler.CompileTest.test_insert 2.7_sqlite_pysqlite_nocextensions 72
test.aaa_profiling.test_compiler.CompileTest.test_insert 3.2_postgresql_psycopg2_nocextensions 74
test.aaa_profiling.test_compiler.CompileTest.test_insert 3.2_sqlite_pysqlite_nocextensions 74
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_mysql_oursql_cextensions 77
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_mysql_oursql_nocextensions 77
test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_oracle_cx_oracle_nocextensions 76
+test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_postgresql_psycopg2_cextensions 77
test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_postgresql_psycopg2_nocextensions 74
test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_sqlite_pysqlite_cextensions 76
test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_sqlite_pysqlite_nocextensions 74
@@ -33,68 +37,67 @@ test.aaa_profiling.test_compiler.CompileTest.test_insert 3.3_sqlite_pysqlite_noc
test.aaa_profiling.test_compiler.CompileTest.test_select 2.6_sqlite_pysqlite_nocextensions 141
test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_cextensions 141
test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_mysqldb_nocextensions 141
-test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_nocextensions 141
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_mysql_oursql_nocextensions 148
+test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_oracle_cx_oracle_nocextensions 148
test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_cextensions 141
test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_postgresql_psycopg2_nocextensions 141
test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_cextensions 141
test.aaa_profiling.test_compiler.CompileTest.test_select 2.7_sqlite_pysqlite_nocextensions 141
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.2_postgresql_psycopg2_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.2_sqlite_pysqlite_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_oracle_cx_oracle_nocextensions 153
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_postgresql_psycopg2_nocextensions 151
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_sqlite_pysqlite_cextensions 157
-test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_sqlite_pysqlite_nocextensions 151
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_mysql_oursql_cextensions 163
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_mysql_oursql_nocextensions 163
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_postgresql_psycopg2_cextensions 163
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_postgresql_psycopg2_nocextensions 163
+test.aaa_profiling.test_compiler.CompileTest.test_select 3.3_sqlite_pysqlite_cextensions 163
# TEST: test.aaa_profiling.test_compiler.CompileTest.test_select_labels
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.6_sqlite_pysqlite_nocextensions 175
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_cextensions 175
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_mysqldb_nocextensions 175
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_mysql_oursql_nocextensions 181
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_oracle_cx_oracle_nocextensions 175
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_cextensions 175
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_postgresql_psycopg2_nocextensions 175
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_cextensions 175
test.aaa_profiling.test_compiler.CompileTest.test_select_labels 2.7_sqlite_pysqlite_nocextensions 175
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.2_postgresql_psycopg2_nocextensions 185
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.2_sqlite_pysqlite_nocextensions 185
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_oracle_cx_oracle_nocextensions 187
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_postgresql_psycopg2_nocextensions 185
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_sqlite_pysqlite_cextensions 191
-test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_sqlite_pysqlite_nocextensions 185
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_mysql_oursql_cextensions 196
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_mysql_oursql_nocextensions 196
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_postgresql_psycopg2_cextensions 196
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_postgresql_psycopg2_nocextensions 196
+test.aaa_profiling.test_compiler.CompileTest.test_select_labels 3.3_sqlite_pysqlite_cextensions 196
# TEST: test.aaa_profiling.test_compiler.CompileTest.test_update
test.aaa_profiling.test_compiler.CompileTest.test_update 2.6_sqlite_pysqlite_nocextensions 75
test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_cextensions 75
test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_mysqldb_nocextensions 75
+test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_mysql_oursql_nocextensions 77
test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_oracle_cx_oracle_nocextensions 75
test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_cextensions 75
test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_postgresql_psycopg2_nocextensions 75
test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_cextensions 75
test.aaa_profiling.test_compiler.CompileTest.test_update 2.7_sqlite_pysqlite_nocextensions 75
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.2_postgresql_psycopg2_nocextensions 75
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.2_sqlite_pysqlite_nocextensions 75
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_oracle_cx_oracle_nocextensions 77
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_postgresql_psycopg2_nocextensions 75
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_sqlite_pysqlite_cextensions 77
-test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_sqlite_pysqlite_nocextensions 75
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_mysql_oursql_cextensions 80
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_mysql_oursql_nocextensions 80
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_postgresql_psycopg2_cextensions 80
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_postgresql_psycopg2_nocextensions 80
+test.aaa_profiling.test_compiler.CompileTest.test_update 3.3_sqlite_pysqlite_cextensions 80
# TEST: test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.6_sqlite_pysqlite_nocextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_cextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_nocextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_nocextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_cextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_nocextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_cextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_nocextensions 137
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.2_postgresql_psycopg2_nocextensions 136
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.2_sqlite_pysqlite_nocextensions 136
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_oracle_cx_oracle_nocextensions 138
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_postgresql_psycopg2_nocextensions 136
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_sqlite_pysqlite_cextensions 143
-test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_sqlite_pysqlite_nocextensions 136
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_cextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_mysqldb_nocextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_mysql_oursql_nocextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_oracle_cx_oracle_nocextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_cextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_postgresql_psycopg2_nocextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_cextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 2.7_sqlite_pysqlite_nocextensions 149
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_mysql_oursql_cextensions 151
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_mysql_oursql_nocextensions 151
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_postgresql_psycopg2_cextensions 151
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_postgresql_psycopg2_nocextensions 151
+test.aaa_profiling.test_compiler.CompileTest.test_update_whereclause 3.3_sqlite_pysqlite_cextensions 151
# TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline
@@ -104,6 +107,8 @@ test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_postgresql_psycop
test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_postgresql_psycopg2_nocextensions 51049
test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_cextensions 30008
test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 2.7_sqlite_pysqlite_nocextensions 39025
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_postgresql_psycopg2_cextensions 32141
+test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_postgresql_psycopg2_nocextensions 41144
test.aaa_profiling.test_orm.DeferOptionsTest.test_baseline 3.3_sqlite_pysqlite_cextensions 31190
# TEST: test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols
@@ -114,6 +119,8 @@ test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_postgresql
test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_postgresql_psycopg2_nocextensions 32835
test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_cextensions 29812
test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 2.7_sqlite_pysqlite_nocextensions 32817
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.3_postgresql_psycopg2_cextensions 31858
+test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.3_postgresql_psycopg2_nocextensions 34861
test.aaa_profiling.test_orm.DeferOptionsTest.test_defer_many_cols 3.3_sqlite_pysqlite_cextensions 30960
# TEST: test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity
@@ -129,6 +136,7 @@ test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.2_postgresql_psycopg2_nocextensions 18987
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.2_sqlite_pysqlite_nocextensions 18987
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.3_oracle_cx_oracle_nocextensions 18987
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.3_postgresql_psycopg2_cextensions 18987
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.3_postgresql_psycopg2_nocextensions 18987
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.3_sqlite_pysqlite_cextensions 18987
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_identity 3.3_sqlite_pysqlite_nocextensions 18987
@@ -146,6 +154,7 @@ test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.2_postgresql_psycopg2_nocextensions 121790
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.2_sqlite_pysqlite_nocextensions 121822
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.3_oracle_cx_oracle_nocextensions 130792
+test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.3_postgresql_psycopg2_cextensions 126077
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.3_postgresql_psycopg2_nocextensions 121822
test.aaa_profiling.test_orm.LoadManyToOneFromIdentityTest.test_many_to_one_load_no_identity 3.3_sqlite_pysqlite_cextensions 164074
@@ -161,6 +170,7 @@ test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.
test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 2.7_sqlite_pysqlite_nocextensions 21790
test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.2_postgresql_psycopg2_nocextensions 20424
test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.3_oracle_cx_oracle_nocextensions 21244
+test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.3_postgresql_psycopg2_cextensions 20268
test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.3_postgresql_psycopg2_nocextensions 20344
test.aaa_profiling.test_orm.MergeBackrefsTest.test_merge_pending_with_all_pks 3.3_sqlite_pysqlite_cextensions 23404
@@ -176,6 +186,7 @@ test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_cexten
test.aaa_profiling.test_orm.MergeTest.test_merge_load 2.7_sqlite_pysqlite_nocextensions 1521
test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.2_postgresql_psycopg2_nocextensions 1332
test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.3_oracle_cx_oracle_nocextensions 1366
+test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.3_postgresql_psycopg2_cextensions 1358
test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.3_postgresql_psycopg2_nocextensions 1357
test.aaa_profiling.test_orm.MergeTest.test_merge_load 3.3_sqlite_pysqlite_cextensions 1598
@@ -192,6 +203,7 @@ test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 2.7_sqlite_pysqlite_noc
test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.2_postgresql_psycopg2_nocextensions 127,19
test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.2_sqlite_pysqlite_nocextensions 127,19
test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_oracle_cx_oracle_nocextensions 134,19
+test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_postgresql_psycopg2_cextensions 132,20
test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_postgresql_psycopg2_nocextensions 127,19
test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_sqlite_pysqlite_cextensions 134,19
test.aaa_profiling.test_orm.MergeTest.test_merge_no_load 3.3_sqlite_pysqlite_nocextensions 127,19
@@ -209,6 +221,7 @@ test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 2.7_sqlite_pysqlit
test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.2_postgresql_psycopg2_nocextensions 75
test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.2_sqlite_pysqlite_nocextensions 75
test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.3_oracle_cx_oracle_nocextensions 74
+test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.3_postgresql_psycopg2_cextensions 74
test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.3_postgresql_psycopg2_nocextensions 74
test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.3_sqlite_pysqlite_cextensions 74
test.aaa_profiling.test_pool.QueuePoolTest.test_first_connect 3.3_sqlite_pysqlite_nocextensions 74
@@ -226,6 +239,7 @@ test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 2.7_sqlite_pysqli
test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.2_postgresql_psycopg2_nocextensions 23
test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.2_sqlite_pysqlite_nocextensions 23
test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.3_oracle_cx_oracle_nocextensions 22
+test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.3_postgresql_psycopg2_cextensions 23
test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.3_postgresql_psycopg2_nocextensions 22
test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.3_sqlite_pysqlite_cextensions 23
test.aaa_profiling.test_pool.QueuePoolTest.test_second_connect 3.3_sqlite_pysqlite_nocextensions 22
@@ -243,6 +257,7 @@ test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 2.7_sq
test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.2_postgresql_psycopg2_nocextensions 8
test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.2_sqlite_pysqlite_nocextensions 8
test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.3_oracle_cx_oracle_nocextensions 8
+test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.3_postgresql_psycopg2_cextensions 8
test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.3_postgresql_psycopg2_nocextensions 8
test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.3_sqlite_pysqlite_cextensions 8
test.aaa_profiling.test_pool.QueuePoolTest.test_second_samethread_connect 3.3_sqlite_pysqlite_nocextensions 8
@@ -260,6 +275,7 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.2_postgresql_psycopg2_nocextensions 41
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.2_sqlite_pysqlite_nocextensions 41
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.3_oracle_cx_oracle_nocextensions 41
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.3_postgresql_psycopg2_cextensions 41
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.3_postgresql_psycopg2_nocextensions 41
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.3_sqlite_pysqlite_cextensions 41
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_connection_execute 3.3_sqlite_pysqlite_nocextensions 41
@@ -277,6 +293,7 @@ test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 2.7_
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.2_postgresql_psycopg2_nocextensions 71
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.2_sqlite_pysqlite_nocextensions 71
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.3_oracle_cx_oracle_nocextensions 71
+test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.3_postgresql_psycopg2_cextensions 71
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.3_postgresql_psycopg2_nocextensions 71
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.3_sqlite_pysqlite_cextensions 71
test.aaa_profiling.test_resultset.ExecutionTest.test_minimal_engine_execute 3.3_sqlite_pysqlite_nocextensions 71
@@ -294,6 +311,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 2.7
test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.2_postgresql_psycopg2_nocextensions 15
test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.2_sqlite_pysqlite_nocextensions 15
test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.3_oracle_cx_oracle_nocextensions 15
+test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.3_postgresql_psycopg2_cextensions 15
test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.3_postgresql_psycopg2_nocextensions 15
test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.3_sqlite_pysqlite_cextensions 15
test.aaa_profiling.test_resultset.ResultSetTest.test_contains_doesnt_compile 3.3_sqlite_pysqlite_nocextensions 15
@@ -311,6 +329,7 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_string 2.7_sqlite_pysqlite_
test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.2_postgresql_psycopg2_nocextensions 14459
test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.2_sqlite_pysqlite_nocextensions 14430
test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.3_oracle_cx_oracle_nocextensions 14548
+test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.3_postgresql_psycopg2_cextensions 497
test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.3_postgresql_psycopg2_nocextensions 14457
test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.3_sqlite_pysqlite_cextensions 453
test.aaa_profiling.test_resultset.ResultSetTest.test_string 3.3_sqlite_pysqlite_nocextensions 14430
@@ -328,41 +347,46 @@ test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 2.7_sqlite_pysqlite
test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.2_postgresql_psycopg2_nocextensions 14459
test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.2_sqlite_pysqlite_nocextensions 14430
test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.3_oracle_cx_oracle_nocextensions 14548
+test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.3_postgresql_psycopg2_cextensions 497
test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.3_postgresql_psycopg2_nocextensions 14457
test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.3_sqlite_pysqlite_cextensions 453
test.aaa_profiling.test_resultset.ResultSetTest.test_unicode 3.3_sqlite_pysqlite_nocextensions 14430
# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 5340
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 5175
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 3.2_postgresql_psycopg2_nocextensions 4828
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 3.3_postgresql_psycopg2_nocextensions 4792
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 3.3_postgresql_psycopg2_cextensions 5157
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_1a_populate 3.3_postgresql_psycopg2_nocextensions 5179
# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 256
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 256
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 3.3_postgresql_psycopg2_nocextensions 251
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 3.3_postgresql_psycopg2_cextensions 259
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_2_insert 3.3_postgresql_psycopg2_nocextensions 259
# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 3425
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 3625
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 3749
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 3.2_postgresql_psycopg2_nocextensions 3401
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 3.3_postgresql_psycopg2_nocextensions 3385
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 3.3_postgresql_psycopg2_cextensions 3569
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_3_properties 3.3_postgresql_psycopg2_nocextensions 3665
# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 11045
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 11688
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 12747
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 3.2_postgresql_psycopg2_nocextensions 11849
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 3.3_postgresql_psycopg2_nocextensions 11803
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 3.3_postgresql_psycopg2_cextensions 11548
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_4_expressions 3.3_postgresql_psycopg2_nocextensions 12720
# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1050
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1167
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 3.2_postgresql_psycopg2_nocextensions 1114
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 3.3_postgresql_psycopg2_cextensions 1044
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 3.3_postgresql_psycopg2_nocextensions 1106
# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing
@@ -370,20 +394,23 @@ test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_5_aggregates 3.3_postgr
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 1811
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 1858
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 3.2_postgresql_psycopg2_nocextensions 1731
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 3.3_postgresql_psycopg2_nocextensions 1721
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 3.3_postgresql_psycopg2_cextensions 1846
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_6_editing 3.3_postgresql_psycopg2_nocextensions 1853
# TEST: test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_cextensions 2300
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 2.7_postgresql_psycopg2_nocextensions 2559
test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 3.2_postgresql_psycopg2_nocextensions 2483
-test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 3.3_postgresql_psycopg2_nocextensions 2473
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 3.3_postgresql_psycopg2_cextensions 2460
+test.aaa_profiling.test_zoomark.ZooMarkTest.test_profile_7_multiview 3.3_postgresql_psycopg2_nocextensions 2652
# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_cextensions 6157
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 2.7_postgresql_psycopg2_nocextensions 6276
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 3.2_postgresql_psycopg2_nocextensions 6252
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 3.3_postgresql_psycopg2_cextensions 6286
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 3.3_postgresql_psycopg2_nocextensions 6251
# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert
@@ -391,20 +418,23 @@ test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_1a_populate 3.3_pos
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_cextensions 391
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 2.7_postgresql_psycopg2_nocextensions 398
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 3.2_postgresql_psycopg2_nocextensions 395
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 3.3_postgresql_psycopg2_cextensions 391
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_2_insert 3.3_postgresql_psycopg2_nocextensions 394
# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 6422
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_cextensions 6765
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 2.7_postgresql_psycopg2_nocextensions 6654
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 3.2_postgresql_psycopg2_nocextensions 6560
-test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 3.3_postgresql_psycopg2_nocextensions 6560
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 3.3_postgresql_psycopg2_cextensions 6895
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_3_properties 3.3_postgresql_psycopg2_nocextensions 6999
# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_cextensions 19145
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 2.7_postgresql_psycopg2_nocextensions 20576
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 3.2_postgresql_psycopg2_nocextensions 20279
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 3.3_postgresql_psycopg2_cextensions 20117
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 3.3_postgresql_psycopg2_nocextensions 20279
# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates
@@ -412,6 +442,7 @@ test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_4_expressions 3.3_p
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_cextensions 1063
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 2.7_postgresql_psycopg2_nocextensions 1171
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 3.2_postgresql_psycopg2_nocextensions 1120
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 3.3_postgresql_psycopg2_cextensions 1059
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 3.3_postgresql_psycopg2_nocextensions 1113
# TEST: test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing
@@ -419,4 +450,5 @@ test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_5_aggregates 3.3_po
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_cextensions 2686
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 2.7_postgresql_psycopg2_nocextensions 2749
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 3.2_postgresql_psycopg2_nocextensions 2749
+test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 3.3_postgresql_psycopg2_cextensions 2796
test.aaa_profiling.test_zoomark_orm.ZooMarkTest.test_profile_6_editing 3.3_postgresql_psycopg2_nocextensions 2749
diff --git a/test/sql/test_metadata.py b/test/sql/test_metadata.py
index 851e9b920..00426b227 100644
--- a/test/sql/test_metadata.py
+++ b/test/sql/test_metadata.py
@@ -581,11 +581,13 @@ class MetaDataTest(fixtures.TestBase, ComparesTables):
kw['quote_schema'] = quote_schema
t = Table(name, metadata, **kw)
eq_(t.schema, exp_schema, "test %d, table schema" % i)
- eq_(t.quote_schema, exp_quote_schema,
+ eq_(t.schema.quote if t.schema is not None else None,
+ exp_quote_schema,
"test %d, table quote_schema" % i)
seq = Sequence(name, metadata=metadata, **kw)
eq_(seq.schema, exp_schema, "test %d, seq schema" % i)
- eq_(seq.quote_schema, exp_quote_schema,
+ eq_(seq.schema.quote if seq.schema is not None else None,
+ exp_quote_schema,
"test %d, seq quote_schema" % i)
def test_manual_dependencies(self):
@@ -1039,7 +1041,7 @@ class UseExistingTest(fixtures.TablesTest):
meta2 = self._useexisting_fixture()
users = Table('users', meta2, quote=True, autoload=True,
keep_existing=True)
- assert not users.quote
+ assert not users.name.quote
def test_keep_existing_add_column(self):
meta2 = self._useexisting_fixture()
@@ -1060,7 +1062,7 @@ class UseExistingTest(fixtures.TablesTest):
users = Table('users', meta2, quote=True,
autoload=True,
keep_existing=True)
- assert users.quote
+ assert users.name.quote
def test_keep_existing_add_column_no_orig(self):
meta2 = self._notexisting_fixture()
@@ -1080,7 +1082,7 @@ class UseExistingTest(fixtures.TablesTest):
meta2 = self._useexisting_fixture()
users = Table('users', meta2, quote=True,
keep_existing=True)
- assert not users.quote
+ assert not users.name.quote
def test_keep_existing_add_column_no_reflection(self):
meta2 = self._useexisting_fixture()
@@ -1097,9 +1099,12 @@ class UseExistingTest(fixtures.TablesTest):
def test_extend_existing_quote(self):
meta2 = self._useexisting_fixture()
- users = Table('users', meta2, quote=True, autoload=True,
- extend_existing=True)
- assert users.quote
+ assert_raises_message(
+ tsa.exc.ArgumentError,
+ "Can't redefine 'quote' or 'quote_schema' arguments",
+ Table, 'users', meta2, quote=True, autoload=True,
+ extend_existing=True
+ )
def test_extend_existing_add_column(self):
meta2 = self._useexisting_fixture()
@@ -1120,7 +1125,7 @@ class UseExistingTest(fixtures.TablesTest):
users = Table('users', meta2, quote=True,
autoload=True,
extend_existing=True)
- assert users.quote
+ assert users.name.quote
def test_extend_existing_add_column_no_orig(self):
meta2 = self._notexisting_fixture()
@@ -1138,9 +1143,12 @@ class UseExistingTest(fixtures.TablesTest):
def test_extend_existing_quote_no_reflection(self):
meta2 = self._useexisting_fixture()
- users = Table('users', meta2, quote=True,
- extend_existing=True)
- assert users.quote
+ assert_raises_message(
+ tsa.exc.ArgumentError,
+ "Can't redefine 'quote' or 'quote_schema' arguments",
+ Table, 'users', meta2, quote=True,
+ extend_existing=True
+ )
def test_extend_existing_add_column_no_reflection(self):
meta2 = self._useexisting_fixture()
diff --git a/test/sql/test_quote.py b/test/sql/test_quote.py
index c92f1ac80..db1e0b8a5 100644
--- a/test/sql/test_quote.py
+++ b/test/sql/test_quote.py
@@ -1,9 +1,10 @@
from sqlalchemy import *
from sqlalchemy import sql, schema
from sqlalchemy.sql import compiler
-from sqlalchemy.testing import fixtures, AssertsCompiledSQL
+from sqlalchemy.testing import fixtures, AssertsCompiledSQL, eq_
from sqlalchemy import testing
-
+from sqlalchemy.sql.elements import quoted_name, _truncated_label, _anonymous_label
+from sqlalchemy.testing.util import picklers
class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
__dialect__ = 'default'
@@ -61,6 +62,49 @@ class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
assert 'MixedCase' in t2.c
+ @testing.provide_metadata
+ def test_has_table_case_sensitive(self):
+ preparer = testing.db.dialect.identifier_preparer
+ if testing.db.dialect.requires_name_normalize:
+ testing.db.execute("CREATE TABLE TAB1 (id INTEGER)")
+ else:
+ testing.db.execute("CREATE TABLE tab1 (id INTEGER)")
+ testing.db.execute('CREATE TABLE %s (id INTEGER)' %
+ preparer.quote_identifier("tab2"))
+ testing.db.execute('CREATE TABLE %s (id INTEGER)' %
+ preparer.quote_identifier("TAB3"))
+ testing.db.execute('CREATE TABLE %s (id INTEGER)' %
+ preparer.quote_identifier("TAB4"))
+
+ t1 = Table('tab1', self.metadata,
+ Column('id', Integer, primary_key=True),
+ )
+ t2 = Table('tab2', self.metadata,
+ Column('id', Integer, primary_key=True),
+ quote=True
+ )
+ t3 = Table('TAB3', self.metadata,
+ Column('id', Integer, primary_key=True),
+ )
+ t4 = Table('TAB4', self.metadata,
+ Column('id', Integer, primary_key=True),
+ quote=True)
+
+ insp = inspect(testing.db)
+ assert testing.db.has_table(t1.name)
+ eq_([c['name'] for c in insp.get_columns(t1.name)], ['id'])
+
+ assert testing.db.has_table(t2.name)
+ eq_([c['name'] for c in insp.get_columns(t2.name)], ['id'])
+
+ assert testing.db.has_table(t3.name)
+ eq_([c['name'] for c in insp.get_columns(t3.name)], ['id'])
+
+ assert testing.db.has_table(t4.name)
+ eq_([c['name'] for c in insp.get_columns(t4.name)], ['id'])
+
+
+
def test_basic(self):
table1.insert().execute(
{'lowercase': 1, 'UPPERCASE': 2, 'MixedCase': 3, 'a123': 4},
@@ -299,7 +343,7 @@ class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
'FROM create.foreign'
)
- def test_subquery(self):
+ def test_subquery_one(self):
# Lower case names, should not quote
metadata = MetaData()
t1 = Table('t1', metadata,
@@ -318,6 +362,7 @@ class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
'WHERE anon.col1 = :col1_1'
)
+ def test_subquery_two(self):
# Lower case names, quotes on, should quote
metadata = MetaData()
t1 = Table('t1', metadata,
@@ -336,6 +381,7 @@ class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
'WHERE anon."col1" = :col1_1'
)
+ def test_subquery_three(self):
# Not lower case names, should quote
metadata = MetaData()
t1 = Table('T1', metadata,
@@ -355,6 +401,8 @@ class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
'"Anon"."Col1" = :Col1_1'
)
+ def test_subquery_four(self):
+
# Not lower case names, quotes off, should not quote
metadata = MetaData()
t1 = Table('T1', metadata,
@@ -513,7 +561,7 @@ class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
') AS "Alias1"'
)
- def test_apply_labels(self):
+ def test_apply_labels_should_quote(self):
# Not lower case names, should quote
metadata = MetaData()
t1 = Table('T1', metadata,
@@ -527,6 +575,7 @@ class QuoteTest(fixtures.TestBase, AssertsCompiledSQL):
'"Foo"."T1"'
)
+ def test_apply_labels_shouldnt_quote(self):
# Not lower case names, quotes off
metadata = MetaData()
t1 = Table('T1', metadata,
@@ -619,3 +668,95 @@ class PreparerTest(fixtures.TestBase):
a_eq(unformat('`foo`.bar'), ['foo', 'bar'])
a_eq(unformat('`foo`.`b``a``r`.`baz`'), ['foo', 'b`a`r', 'baz'])
+class QuotedIdentTest(fixtures.TestBase):
+ def test_concat_quotetrue(self):
+ q1 = quoted_name("x", True)
+ self._assert_not_quoted("y" + q1)
+
+ def test_concat_quotefalse(self):
+ q1 = quoted_name("x", False)
+ self._assert_not_quoted("y" + q1)
+
+ def test_concat_quotenone(self):
+ q1 = quoted_name("x", None)
+ self._assert_not_quoted("y" + q1)
+
+ def test_rconcat_quotetrue(self):
+ q1 = quoted_name("x", True)
+ self._assert_not_quoted("y" + q1)
+
+ def test_rconcat_quotefalse(self):
+ q1 = quoted_name("x", False)
+ self._assert_not_quoted("y" + q1)
+
+ def test_rconcat_quotenone(self):
+ q1 = quoted_name("x", None)
+ self._assert_not_quoted("y" + q1)
+
+ def test_concat_anon(self):
+ q1 = _anonymous_label(quoted_name("x", True))
+ assert isinstance(q1, _anonymous_label)
+ value = q1 + "y"
+ assert isinstance(value, _anonymous_label)
+ self._assert_quoted(value, True)
+
+ def test_rconcat_anon(self):
+ q1 = _anonymous_label(quoted_name("x", True))
+ assert isinstance(q1, _anonymous_label)
+ value = "y" + q1
+ assert isinstance(value, _anonymous_label)
+ self._assert_quoted(value, True)
+
+ def test_coerce_quoted_switch(self):
+ q1 = quoted_name("x", False)
+ q2 = quoted_name(q1, True)
+ eq_(q2.quote, True)
+
+ def test_coerce_quoted_none(self):
+ q1 = quoted_name("x", False)
+ q2 = quoted_name(q1, None)
+ eq_(q2.quote, False)
+
+ def test_coerce_quoted_retain(self):
+ q1 = quoted_name("x", False)
+ q2 = quoted_name(q1, False)
+ eq_(q2.quote, False)
+
+ def test_coerce_none(self):
+ q1 = quoted_name(None, False)
+ eq_(q1, None)
+
+ def test_apply_map_quoted(self):
+ q1 = _anonymous_label(quoted_name("x%s", True))
+ q2 = q1.apply_map(('bar'))
+ eq_(q2, "xbar")
+ eq_(q2.quote, True)
+
+ def test_apply_map_plain(self):
+ q1 = _anonymous_label(quoted_name("x%s", None))
+ q2 = q1.apply_map(('bar'))
+ eq_(q2, "xbar")
+ self._assert_not_quoted(q2)
+
+ def test_pickle_quote(self):
+ q1 = quoted_name("x", True)
+ for loads, dumps in picklers():
+ q2 = loads(dumps(q1))
+ eq_(str(q1), str(q2))
+ eq_(q1.quote, q2.quote)
+
+ def test_pickle_anon_label(self):
+ q1 = _anonymous_label(quoted_name("x", True))
+ for loads, dumps in picklers():
+ q2 = loads(dumps(q1))
+ assert isinstance(q2, _anonymous_label)
+ eq_(str(q1), str(q2))
+ eq_(q1.quote, q2.quote)
+
+ def _assert_quoted(self, value, quote):
+ assert isinstance(value, quoted_name)
+ eq_(value.quote, quote)
+
+ def _assert_not_quoted(self, value):
+ assert not isinstance(value, quoted_name)
+