Update sqlalchemy
This commit is contained in:
@@ -215,7 +215,7 @@ class FBCompiler(sql.compiler.SQLCompiler):
|
||||
# Override to not use the AS keyword which FB 1.5 does not like
|
||||
if asfrom:
|
||||
alias_name = isinstance(alias.name,
|
||||
expression._generated_label) and \
|
||||
expression._truncated_label) and \
|
||||
self._truncated_identifier("alias",
|
||||
alias.name) or alias.name
|
||||
|
||||
|
||||
@@ -791,6 +791,9 @@ class MSSQLCompiler(compiler.SQLCompiler):
|
||||
def get_from_hint_text(self, table, text):
|
||||
return text
|
||||
|
||||
def get_crud_hint_text(self, table, text):
|
||||
return text
|
||||
|
||||
def limit_clause(self, select):
|
||||
# Limit in mssql is after the select keyword
|
||||
return ""
|
||||
@@ -949,6 +952,13 @@ class MSSQLCompiler(compiler.SQLCompiler):
|
||||
]
|
||||
return 'OUTPUT ' + ', '.join(columns)
|
||||
|
||||
def get_cte_preamble(self, recursive):
|
||||
# SQL Server finds it too inconvenient to accept
|
||||
# an entirely optional, SQL standard specified,
|
||||
# "RECURSIVE" word with their "WITH",
|
||||
# so here we go
|
||||
return "WITH"
|
||||
|
||||
def label_select_column(self, select, column, asfrom):
|
||||
if isinstance(column, expression.Function):
|
||||
return column.label(None)
|
||||
|
||||
@@ -84,6 +84,23 @@ all lower case both within SQLAlchemy as well as on the MySQL
|
||||
database itself, especially if database reflection features are
|
||||
to be used.
|
||||
|
||||
Transaction Isolation Level
|
||||
---------------------------
|
||||
|
||||
:func:`.create_engine` accepts an ``isolation_level``
|
||||
parameter which results in the command ``SET SESSION
|
||||
TRANSACTION ISOLATION LEVEL <level>`` being invoked for
|
||||
every new connection. Valid values for this parameter are
|
||||
``READ COMMITTED``, ``READ UNCOMMITTED``,
|
||||
``REPEATABLE READ``, and ``SERIALIZABLE``::
|
||||
|
||||
engine = create_engine(
|
||||
"mysql://scott:tiger@localhost/test",
|
||||
isolation_level="READ UNCOMMITTED"
|
||||
)
|
||||
|
||||
(new in 0.7.6)
|
||||
|
||||
Keys
|
||||
----
|
||||
|
||||
@@ -221,8 +238,29 @@ simply passed through to the underlying CREATE INDEX command, so it *must* be
|
||||
an integer. MySQL only allows a length for an index if it is for a CHAR,
|
||||
VARCHAR, TEXT, BINARY, VARBINARY and BLOB.
|
||||
|
||||
Index Types
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Some MySQL storage engines permit you to specify an index type when creating
|
||||
an index or primary key constraint. SQLAlchemy provides this feature via the
|
||||
``mysql_using`` parameter on :class:`.Index`::
|
||||
|
||||
Index('my_index', my_table.c.data, mysql_using='hash')
|
||||
|
||||
As well as the ``mysql_using`` parameter on :class:`.PrimaryKeyConstraint`::
|
||||
|
||||
PrimaryKeyConstraint("data", mysql_using='hash')
|
||||
|
||||
The value passed to the keyword argument will be simply passed through to the
|
||||
underlying CREATE INDEX or PRIMARY KEY clause, so it *must* be a valid index
|
||||
type for your MySQL storage engine.
|
||||
|
||||
More information can be found at:
|
||||
|
||||
http://dev.mysql.com/doc/refman/5.0/en/create-index.html
|
||||
|
||||
http://dev.mysql.com/doc/refman/5.0/en/create-table.html
|
||||
|
||||
"""
|
||||
|
||||
import datetime, inspect, re, sys
|
||||
@@ -1331,7 +1369,8 @@ class MySQLCompiler(compiler.SQLCompiler):
|
||||
return ', '.join(t._compiler_dispatch(self, asfrom=True, **kw)
|
||||
for t in [from_table] + list(extra_froms))
|
||||
|
||||
def update_from_clause(self, update_stmt, from_table, extra_froms, **kw):
|
||||
def update_from_clause(self, update_stmt, from_table,
|
||||
extra_froms, from_hints, **kw):
|
||||
return None
|
||||
|
||||
|
||||
@@ -1421,35 +1460,50 @@ class MySQLDDLCompiler(compiler.DDLCompiler):
|
||||
table_opts.append(joiner.join((opt, arg)))
|
||||
return ' '.join(table_opts)
|
||||
|
||||
|
||||
def visit_create_index(self, create):
|
||||
index = create.element
|
||||
preparer = self.preparer
|
||||
table = preparer.format_table(index.table)
|
||||
columns = [preparer.quote(c.name, c.quote) for c in index.columns]
|
||||
name = preparer.quote(
|
||||
self._index_identifier(index.name),
|
||||
index.quote)
|
||||
|
||||
text = "CREATE "
|
||||
if index.unique:
|
||||
text += "UNIQUE "
|
||||
text += "INDEX %s ON %s " \
|
||||
% (preparer.quote(self._index_identifier(index.name),
|
||||
index.quote),preparer.format_table(index.table))
|
||||
text += "INDEX %s ON %s " % (name, table)
|
||||
|
||||
columns = ', '.join(columns)
|
||||
if 'mysql_length' in index.kwargs:
|
||||
length = index.kwargs['mysql_length']
|
||||
text += "(%s(%d))" % (columns, length)
|
||||
else:
|
||||
length = None
|
||||
if length is not None:
|
||||
text+= "(%s(%d))" \
|
||||
% (', '.join(preparer.quote(c.name, c.quote)
|
||||
for c in index.columns), length)
|
||||
else:
|
||||
text+= "(%s)" \
|
||||
% (', '.join(preparer.quote(c.name, c.quote)
|
||||
for c in index.columns))
|
||||
text += "(%s)" % (columns)
|
||||
|
||||
if 'mysql_using' in index.kwargs:
|
||||
using = index.kwargs['mysql_using']
|
||||
text += " USING %s" % (preparer.quote(using, index.quote))
|
||||
|
||||
return text
|
||||
|
||||
def visit_primary_key_constraint(self, constraint):
|
||||
text = super(MySQLDDLCompiler, self).\
|
||||
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))
|
||||
return text
|
||||
|
||||
def visit_drop_index(self, drop):
|
||||
index = drop.element
|
||||
|
||||
return "\nDROP INDEX %s ON %s" % \
|
||||
(self.preparer.quote(self._index_identifier(index.name), index.quote),
|
||||
(self.preparer.quote(
|
||||
self._index_identifier(index.name), index.quote
|
||||
),
|
||||
self.preparer.format_table(index.table))
|
||||
|
||||
def visit_drop_constraint(self, drop):
|
||||
@@ -1768,8 +1822,40 @@ class MySQLDialect(default.DefaultDialect):
|
||||
_backslash_escapes = True
|
||||
_server_ansiquotes = False
|
||||
|
||||
def __init__(self, use_ansiquotes=None, **kwargs):
|
||||
def __init__(self, use_ansiquotes=None, isolation_level=None, **kwargs):
|
||||
default.DefaultDialect.__init__(self, **kwargs)
|
||||
self.isolation_level = isolation_level
|
||||
|
||||
def on_connect(self):
|
||||
if self.isolation_level is not None:
|
||||
def connect(conn):
|
||||
self.set_isolation_level(conn, self.isolation_level)
|
||||
return connect
|
||||
else:
|
||||
return None
|
||||
|
||||
_isolation_lookup = set(['SERIALIZABLE',
|
||||
'READ UNCOMMITTED', 'READ COMMITTED', 'REPEATABLE READ'])
|
||||
|
||||
def set_isolation_level(self, connection, level):
|
||||
level = level.replace('_', ' ')
|
||||
if level not in self._isolation_lookup:
|
||||
raise exc.ArgumentError(
|
||||
"Invalid value '%s' for isolation_level. "
|
||||
"Valid isolation levels for %s are %s" %
|
||||
(level, self.name, ", ".join(self._isolation_lookup))
|
||||
)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("SET SESSION TRANSACTION ISOLATION LEVEL %s" % level)
|
||||
cursor.execute("COMMIT")
|
||||
cursor.close()
|
||||
|
||||
def get_isolation_level(self, connection):
|
||||
cursor = connection.cursor()
|
||||
cursor.execute('SELECT @@tx_isolation')
|
||||
val = cursor.fetchone()[0]
|
||||
cursor.close()
|
||||
return val.upper().replace("-", " ")
|
||||
|
||||
def do_commit(self, connection):
|
||||
"""Execute a COMMIT."""
|
||||
|
||||
@@ -158,7 +158,7 @@ RESERVED_WORDS = \
|
||||
'AS IN VIEW EXCLUSIVE COMPRESS SYNONYM SELECT INSERT EXISTS '\
|
||||
'NOT TRIGGER ELSE CREATE INTERSECT PCTFREE DISTINCT USER '\
|
||||
'CONNECT SET MODE OF UNIQUE VARCHAR2 VARCHAR LOCK OR CHAR '\
|
||||
'DECIMAL UNION PUBLIC AND START UID COMMENT CURRENT'.split())
|
||||
'DECIMAL UNION PUBLIC AND START UID COMMENT CURRENT LEVEL'.split())
|
||||
|
||||
NO_ARG_FNS = set('UID CURRENT_DATE SYSDATE USER '
|
||||
'CURRENT_TIME CURRENT_TIMESTAMP'.split())
|
||||
@@ -309,6 +309,9 @@ class OracleTypeCompiler(compiler.GenericTypeCompiler):
|
||||
"",
|
||||
)
|
||||
|
||||
def visit_LONG(self, type_):
|
||||
return "LONG"
|
||||
|
||||
def visit_TIMESTAMP(self, type_):
|
||||
if type_.timezone:
|
||||
return "TIMESTAMP WITH TIME ZONE"
|
||||
@@ -481,7 +484,7 @@ class OracleCompiler(compiler.SQLCompiler):
|
||||
"""Oracle doesn't like ``FROM table AS alias``. Is the AS standard SQL??"""
|
||||
|
||||
if asfrom or ashint:
|
||||
alias_name = isinstance(alias.name, expression._generated_label) and \
|
||||
alias_name = isinstance(alias.name, expression._truncated_label) and \
|
||||
self._truncated_identifier("alias", alias.name) or alias.name
|
||||
|
||||
if ashint:
|
||||
|
||||
@@ -77,7 +77,7 @@ with this feature but it should be regarded as experimental.
|
||||
Precision Numerics
|
||||
------------------
|
||||
|
||||
The SQLAlchemy dialect goes thorugh a lot of steps to ensure
|
||||
The SQLAlchemy dialect goes through a lot of steps to ensure
|
||||
that decimal numbers are sent and received with full accuracy.
|
||||
An "outputtypehandler" callable is associated with each
|
||||
cx_oracle connection object which detects numeric types and
|
||||
@@ -89,6 +89,21 @@ this behavior, and will coerce the ``Decimal`` to ``float`` if
|
||||
the ``asdecimal`` flag is ``False`` (default on :class:`.Float`,
|
||||
optional on :class:`.Numeric`).
|
||||
|
||||
Because the handler coerces to ``Decimal`` in all cases first,
|
||||
the feature can detract significantly from performance.
|
||||
If precision numerics aren't required, the decimal handling
|
||||
can be disabled by passing the flag ``coerce_to_decimal=False``
|
||||
to :func:`.create_engine`::
|
||||
|
||||
engine = create_engine("oracle+cx_oracle://dsn",
|
||||
coerce_to_decimal=False)
|
||||
|
||||
The ``coerce_to_decimal`` flag is new in 0.7.6.
|
||||
|
||||
Another alternative to performance is to use the
|
||||
`cdecimal <http://pypi.python.org/pypi/cdecimal/>`_ library;
|
||||
see :class:`.Numeric` for additional notes.
|
||||
|
||||
The handler attempts to use the "precision" and "scale"
|
||||
attributes of the result set column to best determine if
|
||||
subsequent incoming values should be received as ``Decimal`` as
|
||||
@@ -468,6 +483,7 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
auto_convert_lobs=True,
|
||||
threaded=True,
|
||||
allow_twophase=True,
|
||||
coerce_to_decimal=True,
|
||||
arraysize=50, **kwargs):
|
||||
OracleDialect.__init__(self, **kwargs)
|
||||
self.threaded = threaded
|
||||
@@ -491,7 +507,12 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
self._cx_oracle_unicode_types = types("UNICODE", "NCLOB")
|
||||
self._cx_oracle_binary_types = types("BFILE", "CLOB", "NCLOB", "BLOB")
|
||||
self.supports_unicode_binds = self.cx_oracle_ver >= (5, 0)
|
||||
self.supports_native_decimal = self.cx_oracle_ver >= (5, 0)
|
||||
|
||||
self.supports_native_decimal = (
|
||||
self.cx_oracle_ver >= (5, 0) and
|
||||
coerce_to_decimal
|
||||
)
|
||||
|
||||
self._cx_oracle_native_nvarchar = self.cx_oracle_ver >= (5, 0)
|
||||
|
||||
if self.cx_oracle_ver is None:
|
||||
@@ -603,7 +624,9 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
size, precision, scale):
|
||||
# convert all NUMBER with precision + positive scale to Decimal
|
||||
# this almost allows "native decimal" mode.
|
||||
if defaultType == cx_Oracle.NUMBER and precision and scale > 0:
|
||||
if self.supports_native_decimal and \
|
||||
defaultType == cx_Oracle.NUMBER and \
|
||||
precision and scale > 0:
|
||||
return cursor.var(
|
||||
cx_Oracle.STRING,
|
||||
255,
|
||||
@@ -614,7 +637,8 @@ class OracleDialect_cx_oracle(OracleDialect):
|
||||
# make a decision based on each value received - the type
|
||||
# may change from row to row (!). This kills
|
||||
# off "native decimal" mode, handlers still needed.
|
||||
elif defaultType == cx_Oracle.NUMBER \
|
||||
elif self.supports_native_decimal and \
|
||||
defaultType == cx_Oracle.NUMBER \
|
||||
and not precision and scale <= 0:
|
||||
return cursor.var(
|
||||
cx_Oracle.STRING,
|
||||
|
||||
@@ -47,9 +47,18 @@ Transaction Isolation Level
|
||||
:func:`.create_engine` accepts an ``isolation_level`` parameter which results
|
||||
in the command ``SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL
|
||||
<level>`` being invoked for every new connection. Valid values for this
|
||||
parameter are ``READ_COMMITTED``, ``READ_UNCOMMITTED``, ``REPEATABLE_READ``,
|
||||
and ``SERIALIZABLE``. Note that the psycopg2 dialect does *not* use this
|
||||
technique and uses psycopg2-specific APIs (see that dialect for details).
|
||||
parameter are ``READ COMMITTED``, ``READ UNCOMMITTED``, ``REPEATABLE READ``,
|
||||
and ``SERIALIZABLE``::
|
||||
|
||||
engine = create_engine(
|
||||
"postgresql+pg8000://scott:tiger@localhost/test",
|
||||
isolation_level="READ UNCOMMITTED"
|
||||
)
|
||||
|
||||
When using the psycopg2 dialect, a psycopg2-specific method of setting
|
||||
transaction isolation level is used, but the API of ``isolation_level``
|
||||
remains the same - see :ref:`psycopg2_isolation`.
|
||||
|
||||
|
||||
Remote / Cross-Schema Table Introspection
|
||||
-----------------------------------------
|
||||
|
||||
@@ -38,6 +38,26 @@ psycopg2-specific keyword arguments which are accepted by
|
||||
* *use_native_unicode* - Enable the usage of Psycopg2 "native unicode" mode
|
||||
per connection. True by default.
|
||||
|
||||
Unix Domain Connections
|
||||
------------------------
|
||||
|
||||
psycopg2 supports connecting via Unix domain connections. When the ``host``
|
||||
portion of the URL is omitted, SQLAlchemy passes ``None`` to psycopg2,
|
||||
which specifies Unix-domain communication rather than TCP/IP communication::
|
||||
|
||||
create_engine("postgresql+psycopg2://user:password@/dbname")
|
||||
|
||||
By default, the socket file used is to connect to a Unix-domain socket
|
||||
in ``/tmp``, or whatever socket directory was specified when PostgreSQL
|
||||
was built. This value can be overridden by passing a pathname to psycopg2,
|
||||
using ``host`` as an additional keyword argument::
|
||||
|
||||
create_engine("postgresql+psycopg2://user:password@/dbname?host=/var/lib/postgresql")
|
||||
|
||||
See also:
|
||||
|
||||
`PQconnectdbParams <http://www.postgresql.org/docs/9.1/static/libpq-connect.html#LIBPQ-PQCONNECTDBPARAMS>`_
|
||||
|
||||
Per-Statement/Connection Execution Options
|
||||
-------------------------------------------
|
||||
|
||||
@@ -97,6 +117,8 @@ Transactions
|
||||
|
||||
The psycopg2 dialect fully supports SAVEPOINT and two-phase commit operations.
|
||||
|
||||
.. _psycopg2_isolation:
|
||||
|
||||
Transaction Isolation Level
|
||||
---------------------------
|
||||
|
||||
|
||||
@@ -441,20 +441,6 @@ class SQLiteIdentifierPreparer(compiler.IdentifierPreparer):
|
||||
result = self.quote_schema(index.table.schema, index.table.quote_schema) + "." + result
|
||||
return result
|
||||
|
||||
class SQLiteExecutionContext(default.DefaultExecutionContext):
|
||||
def get_result_proxy(self):
|
||||
rp = base.ResultProxy(self)
|
||||
if rp._metadata:
|
||||
# adjust for dotted column names. SQLite
|
||||
# in the case of UNION may store col names as
|
||||
# "tablename.colname"
|
||||
# in cursor.description
|
||||
for colname in rp._metadata.keys:
|
||||
if "." in colname:
|
||||
trunc_col = colname.split(".")[1]
|
||||
rp._metadata._set_keymap_synonym(trunc_col, colname)
|
||||
return rp
|
||||
|
||||
class SQLiteDialect(default.DefaultDialect):
|
||||
name = 'sqlite'
|
||||
supports_alter = False
|
||||
@@ -472,7 +458,6 @@ class SQLiteDialect(default.DefaultDialect):
|
||||
ischema_names = ischema_names
|
||||
colspecs = colspecs
|
||||
isolation_level = None
|
||||
execution_ctx_cls = SQLiteExecutionContext
|
||||
|
||||
supports_cast = True
|
||||
supports_default_values = True
|
||||
@@ -540,6 +525,16 @@ class SQLiteDialect(default.DefaultDialect):
|
||||
else:
|
||||
return None
|
||||
|
||||
def _translate_colname(self, colname):
|
||||
# adjust for dotted column names. SQLite
|
||||
# in the case of UNION may store col names as
|
||||
# "tablename.colname"
|
||||
# in cursor.description
|
||||
if "." in colname:
|
||||
return colname.split(".")[1], colname
|
||||
else:
|
||||
return colname, None
|
||||
|
||||
@reflection.cache
|
||||
def get_table_names(self, connection, schema=None, **kw):
|
||||
if schema is not None:
|
||||
|
||||
Reference in New Issue
Block a user