Update SQLAlchemy
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# postgresql/__init__.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -14,7 +14,7 @@ from sqlalchemy.dialects.postgresql.base import \
|
||||
DATE, BYTEA, BOOLEAN, INTERVAL, ARRAY, ENUM, dialect
|
||||
|
||||
__all__ = (
|
||||
'INTEGER', 'BIGINT', 'SMALLINT', 'VARCHAR', 'CHAR', 'TEXT', 'NUMERIC', 'FLOAT', 'REAL', 'INET',
|
||||
'INTEGER', 'BIGINT', 'SMALLINT', 'VARCHAR', 'CHAR', 'TEXT', 'NUMERIC', 'FLOAT', 'REAL', 'INET',
|
||||
'CIDR', 'UUID', 'BIT', 'MACADDR', 'DOUBLE_PRECISION', 'TIMESTAMP', 'TIME',
|
||||
'DATE', 'BYTEA', 'BOOLEAN', 'INTERVAL', 'ARRAY', 'ENUM', 'dialect'
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# postgresql/base.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -21,7 +21,7 @@ default corresponding to the column.
|
||||
To specify a specific named sequence to be used for primary key generation,
|
||||
use the :func:`~sqlalchemy.schema.Sequence` construct::
|
||||
|
||||
Table('sometable', metadata,
|
||||
Table('sometable', metadata,
|
||||
Column('id', Integer, Sequence('some_id_seq'), primary_key=True)
|
||||
)
|
||||
|
||||
@@ -51,7 +51,7 @@ parameter are ``READ COMMITTED``, ``READ UNCOMMITTED``, ``REPEATABLE READ``,
|
||||
and ``SERIALIZABLE``::
|
||||
|
||||
engine = create_engine(
|
||||
"postgresql+pg8000://scott:tiger@localhost/test",
|
||||
"postgresql+pg8000://scott:tiger@localhost/test",
|
||||
isolation_level="READ UNCOMMITTED"
|
||||
)
|
||||
|
||||
@@ -75,18 +75,19 @@ the current ``search_path``, the "schema" attribute of the resulting
|
||||
remote table matches that of the referencing table, and the "schema" argument
|
||||
was explicitly stated on the referencing table.
|
||||
|
||||
The best practice here is to not use the ``schema`` argument
|
||||
The best practice here is to not use the ``schema`` argument
|
||||
on :class:`.Table` for any schemas that are present in ``search_path``.
|
||||
``search_path`` defaults to "public", but care should be taken
|
||||
to inspect the actual value using::
|
||||
|
||||
SHOW search_path;
|
||||
|
||||
Prior to version 0.7.3, cross-schema foreign keys when the schemas
|
||||
were also in the ``search_path`` could make an incorrect assumption
|
||||
if the schemas were explicitly stated on each :class:`.Table`.
|
||||
.. versionchanged:: 0.7.3
|
||||
Prior to this version, cross-schema foreign keys when the schemas
|
||||
were also in the ``search_path`` could make an incorrect assumption
|
||||
if the schemas were explicitly stated on each :class:`.Table`.
|
||||
|
||||
Background on PG's ``search_path`` is at:
|
||||
Background on PG's ``search_path`` is at:
|
||||
http://www.postgresql.org/docs/9.0/static/ddl-schemas.html#DDL-SCHEMAS-PATH
|
||||
|
||||
INSERT/UPDATE...RETURNING
|
||||
@@ -125,7 +126,7 @@ to the PostgreSQL dialect.
|
||||
Partial Indexes
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Partial indexes add criterion to the index definition so that the index is
|
||||
Partial indexes add criterion to the index definition so that the index is
|
||||
applied to a subset of rows. These can be specified on :class:`.Index`
|
||||
using the ``postgresql_where`` keyword argument::
|
||||
|
||||
@@ -137,13 +138,16 @@ Operator Classes
|
||||
PostgreSQL allows the specification of an *operator class* for each column of
|
||||
an index (see http://www.postgresql.org/docs/8.3/interactive/indexes-opclass.html).
|
||||
The :class:`.Index` construct allows these to be specified via the ``postgresql_ops``
|
||||
keyword argument (new as of SQLAlchemy 0.7.2)::
|
||||
keyword argument::
|
||||
|
||||
Index('my_index', my_table.c.id, my_table.c.data,
|
||||
Index('my_index', my_table.c.id, my_table.c.data,
|
||||
postgresql_ops={
|
||||
'data': 'text_pattern_ops',
|
||||
'data': 'text_pattern_ops',
|
||||
'id': 'int4_ops'
|
||||
})
|
||||
})
|
||||
|
||||
.. versionadded:: 0.7.2
|
||||
``postgresql_ops`` keyword argument to :class:`.Index` construct.
|
||||
|
||||
Note that the keys in the ``postgresql_ops`` dictionary are the "key" name of
|
||||
the :class:`.Column`, i.e. the name used to access it from the ``.c`` collection
|
||||
@@ -345,24 +349,27 @@ class ARRAY(sqltypes.MutableType, sqltypes.Concatenable, sqltypes.TypeEngine):
|
||||
the fly
|
||||
|
||||
:param mutable=False: Specify whether lists passed to this
|
||||
class should be considered mutable - this enables
|
||||
"mutable types" mode in the ORM. Be sure to read the
|
||||
notes for :class:`.MutableType` regarding ORM
|
||||
performance implications (default changed from ``True`` in
|
||||
0.7.0).
|
||||
class should be considered mutable - this enables
|
||||
"mutable types" mode in the ORM. Be sure to read the
|
||||
notes for :class:`.MutableType` regarding ORM
|
||||
performance implications.
|
||||
|
||||
.. note::
|
||||
|
||||
This functionality is now superseded by the
|
||||
``sqlalchemy.ext.mutable`` extension described in
|
||||
:ref:`mutable_toplevel`.
|
||||
.. versionchanged:: 0.7.0
|
||||
Default changed from ``True``\ .
|
||||
|
||||
.. versionchanged:: 0.7
|
||||
This functionality is now superseded by the
|
||||
``sqlalchemy.ext.mutable`` extension described in
|
||||
:ref:`mutable_toplevel`.
|
||||
|
||||
:param as_tuple=False: Specify whether return results
|
||||
should be converted to tuples from lists. DBAPIs such
|
||||
as psycopg2 return lists by default. When tuples are
|
||||
returned, the results are hashable. This flag can only
|
||||
be set to ``True`` when ``mutable`` is set to
|
||||
``False``. (new in 0.6.5)
|
||||
``False``.
|
||||
|
||||
.. versionadded:: 0.6.5
|
||||
|
||||
"""
|
||||
if isinstance(item_type, ARRAY):
|
||||
@@ -444,37 +451,37 @@ PGArray = ARRAY
|
||||
|
||||
class ENUM(sqltypes.Enum):
|
||||
"""Postgresql ENUM type.
|
||||
|
||||
|
||||
This is a subclass of :class:`.types.Enum` which includes
|
||||
support for PG's ``CREATE TYPE``.
|
||||
|
||||
:class:`~.postgresql.ENUM` is used automatically when
|
||||
|
||||
:class:`~.postgresql.ENUM` is used automatically when
|
||||
using the :class:`.types.Enum` type on PG assuming
|
||||
the ``native_enum`` is left as ``True``. However, the
|
||||
the ``native_enum`` is left as ``True``. However, the
|
||||
:class:`~.postgresql.ENUM` class can also be instantiated
|
||||
directly in order to access some additional Postgresql-specific
|
||||
options, namely finer control over whether or not
|
||||
options, namely finer control over whether or not
|
||||
``CREATE TYPE`` should be emitted.
|
||||
|
||||
Note that both :class:`.types.Enum` as well as
|
||||
|
||||
Note that both :class:`.types.Enum` as well as
|
||||
:class:`~.postgresql.ENUM` feature create/drop
|
||||
methods; the base :class:`.types.Enum` type ultimately
|
||||
delegates to the :meth:`~.postgresql.ENUM.create` and
|
||||
:meth:`~.postgresql.ENUM.drop` methods present here.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *enums, **kw):
|
||||
"""Construct an :class:`~.postgresql.ENUM`.
|
||||
|
||||
|
||||
Arguments are the same as that of
|
||||
:class:`.types.Enum`, but also including
|
||||
the following parameters.
|
||||
|
||||
:param create_type: Defaults to True.
|
||||
Indicates that ``CREATE TYPE`` should be
|
||||
emitted, after optionally checking for the
|
||||
presence of the type, when the parent
|
||||
|
||||
:param create_type: Defaults to True.
|
||||
Indicates that ``CREATE TYPE`` should be
|
||||
emitted, after optionally checking for the
|
||||
presence of the type, when the parent
|
||||
table is being created; and additionally
|
||||
that ``DROP TYPE`` is called when the table
|
||||
is dropped. When ``False``, no check
|
||||
@@ -485,31 +492,32 @@ class ENUM(sqltypes.Enum):
|
||||
are called directly.
|
||||
Setting to ``False`` is helpful
|
||||
when invoking a creation scheme to a SQL file
|
||||
without access to the actual database -
|
||||
without access to the actual database -
|
||||
the :meth:`~.postgresql.ENUM.create` and
|
||||
:meth:`~.postgresql.ENUM.drop` methods can
|
||||
be used to emit SQL to a target bind.
|
||||
(new in 0.7.4)
|
||||
|
||||
|
||||
.. versionadded:: 0.7.4
|
||||
|
||||
"""
|
||||
self.create_type = kw.pop("create_type", True)
|
||||
super(ENUM, self).__init__(*enums, **kw)
|
||||
|
||||
def create(self, bind=None, checkfirst=True):
|
||||
"""Emit ``CREATE TYPE`` for this
|
||||
"""Emit ``CREATE TYPE`` for this
|
||||
:class:`~.postgresql.ENUM`.
|
||||
|
||||
|
||||
If the underlying dialect does not support
|
||||
Postgresql CREATE TYPE, no action is taken.
|
||||
|
||||
|
||||
:param bind: a connectable :class:`.Engine`,
|
||||
:class:`.Connection`, or similar object to emit
|
||||
SQL.
|
||||
:param checkfirst: if ``True``, a query against
|
||||
:param checkfirst: if ``True``, a query against
|
||||
the PG catalog will be first performed to see
|
||||
if the type does not exist already before
|
||||
creating.
|
||||
|
||||
|
||||
"""
|
||||
if not bind.dialect.supports_native_enum:
|
||||
return
|
||||
@@ -519,19 +527,19 @@ class ENUM(sqltypes.Enum):
|
||||
bind.execute(CreateEnumType(self))
|
||||
|
||||
def drop(self, bind=None, checkfirst=True):
|
||||
"""Emit ``DROP TYPE`` for this
|
||||
"""Emit ``DROP TYPE`` for this
|
||||
:class:`~.postgresql.ENUM`.
|
||||
|
||||
|
||||
If the underlying dialect does not support
|
||||
Postgresql DROP TYPE, no action is taken.
|
||||
|
||||
|
||||
:param bind: a connectable :class:`.Engine`,
|
||||
:class:`.Connection`, or similar object to emit
|
||||
SQL.
|
||||
:param checkfirst: if ``True``, a query against
|
||||
:param checkfirst: if ``True``, a query against
|
||||
the PG catalog will be first performed to see
|
||||
if the type actually exists before dropping.
|
||||
|
||||
|
||||
"""
|
||||
if not bind.dialect.supports_native_enum:
|
||||
return
|
||||
@@ -543,7 +551,7 @@ class ENUM(sqltypes.Enum):
|
||||
def _check_for_name_in_memos(self, checkfirst, kw):
|
||||
"""Look in the 'ddl runner' for 'memos', then
|
||||
note our name in that collection.
|
||||
|
||||
|
||||
This to ensure a particular named enum is operated
|
||||
upon only once within any kind of create/drop
|
||||
sequence without relying upon "checkfirst".
|
||||
@@ -620,14 +628,14 @@ class PGCompiler(compiler.SQLCompiler):
|
||||
|
||||
def visit_match_op(self, binary, **kw):
|
||||
return "%s @@ to_tsquery(%s)" % (
|
||||
self.process(binary.left),
|
||||
self.process(binary.left),
|
||||
self.process(binary.right))
|
||||
|
||||
def visit_ilike_op(self, binary, **kw):
|
||||
escape = binary.modifiers.get("escape", None)
|
||||
return '%s ILIKE %s' % \
|
||||
(self.process(binary.left), self.process(binary.right)) \
|
||||
+ (escape and
|
||||
+ (escape and
|
||||
(' ESCAPE ' + self.render_literal_value(escape, None))
|
||||
or '')
|
||||
|
||||
@@ -635,7 +643,7 @@ class PGCompiler(compiler.SQLCompiler):
|
||||
escape = binary.modifiers.get("escape", None)
|
||||
return '%s NOT ILIKE %s' % \
|
||||
(self.process(binary.left), self.process(binary.right)) \
|
||||
+ (escape and
|
||||
+ (escape and
|
||||
(' ESCAPE ' + self.render_literal_value(escape, None))
|
||||
or '')
|
||||
|
||||
@@ -675,6 +683,10 @@ class PGCompiler(compiler.SQLCompiler):
|
||||
def for_update_clause(self, select):
|
||||
if select.for_update == 'nowait':
|
||||
return " FOR UPDATE NOWAIT"
|
||||
elif select.for_update == 'read':
|
||||
return " FOR SHARE"
|
||||
elif select.for_update == 'read_nowait':
|
||||
return " FOR SHARE NOWAIT"
|
||||
else:
|
||||
return super(PGCompiler, self).for_update_clause(select)
|
||||
|
||||
@@ -682,9 +694,9 @@ class PGCompiler(compiler.SQLCompiler):
|
||||
|
||||
columns = [
|
||||
self.process(
|
||||
self.label_select_column(None, c, asfrom=False),
|
||||
within_columns_clause=True,
|
||||
result_map=self.result_map)
|
||||
self.label_select_column(None, c, asfrom=False),
|
||||
within_columns_clause=True,
|
||||
result_map=self.result_map)
|
||||
for c in expression._select_iterables(returning_cols)
|
||||
]
|
||||
|
||||
@@ -698,8 +710,8 @@ class PGCompiler(compiler.SQLCompiler):
|
||||
affinity = None
|
||||
|
||||
casts = {
|
||||
sqltypes.Date:'date',
|
||||
sqltypes.DateTime:'timestamp',
|
||||
sqltypes.Date:'date',
|
||||
sqltypes.DateTime:'timestamp',
|
||||
sqltypes.Interval:'interval', sqltypes.Time:'time'
|
||||
}
|
||||
cast = casts.get(affinity, None)
|
||||
@@ -718,7 +730,7 @@ class PGDDLCompiler(compiler.DDLCompiler):
|
||||
column is column.table._autoincrement_column and \
|
||||
not isinstance(impl_type, sqltypes.SmallInteger) and \
|
||||
(
|
||||
column.default is None or
|
||||
column.default is None or
|
||||
(
|
||||
isinstance(column.default, schema.Sequence) and
|
||||
column.default.optional
|
||||
@@ -773,7 +785,7 @@ class PGDDLCompiler(compiler.DDLCompiler):
|
||||
text += "(%s)" \
|
||||
% (
|
||||
', '.join([
|
||||
preparer.format_column(c) +
|
||||
preparer.format_column(c) +
|
||||
(c.key in ops and (' ' + ops[c.key]) or '')
|
||||
for c in index.columns])
|
||||
)
|
||||
@@ -831,14 +843,14 @@ class PGTypeCompiler(compiler.GenericTypeCompiler):
|
||||
|
||||
def visit_TIMESTAMP(self, type_):
|
||||
return "TIMESTAMP%s %s" % (
|
||||
getattr(type_, 'precision', None) and "(%d)" %
|
||||
getattr(type_, 'precision', None) and "(%d)" %
|
||||
type_.precision or "",
|
||||
(type_.timezone and "WITH" or "WITHOUT") + " TIME ZONE"
|
||||
)
|
||||
|
||||
def visit_TIME(self, type_):
|
||||
return "TIME%s %s" % (
|
||||
getattr(type_, 'precision', None) and "(%d)" %
|
||||
getattr(type_, 'precision', None) and "(%d)" %
|
||||
type_.precision or "",
|
||||
(type_.timezone and "WITH" or "WITHOUT") + " TIME ZONE"
|
||||
)
|
||||
@@ -920,21 +932,21 @@ class PGExecutionContext(default.DefaultExecutionContext):
|
||||
return self._execute_scalar("select %s" %
|
||||
column.server_default.arg, column.type)
|
||||
|
||||
elif (column.default is None or
|
||||
elif (column.default is None or
|
||||
(column.default.is_sequence and
|
||||
column.default.optional)):
|
||||
|
||||
# execute the sequence associated with a SERIAL primary
|
||||
# execute the sequence associated with a SERIAL primary
|
||||
# key column. for non-primary-key SERIAL, the ID just
|
||||
# generates server side.
|
||||
|
||||
try:
|
||||
seq_name = column._postgresql_seq_name
|
||||
except AttributeError:
|
||||
tab = column.table.name
|
||||
col = column.name
|
||||
tab = tab[0:29 + max(0, (29 - len(col)))]
|
||||
col = col[0:29 + max(0, (29 - len(tab)))]
|
||||
tab = column.table.name
|
||||
col = column.name
|
||||
tab = tab[0:29 + max(0, (29 - len(col)))]
|
||||
col = col[0:29 + max(0, (29 - len(tab)))]
|
||||
column._postgresql_seq_name = seq_name = "%s_%s_seq" % (tab, col)
|
||||
|
||||
sch = column.table.schema
|
||||
@@ -1004,7 +1016,7 @@ class PGDialect(default.DefaultDialect):
|
||||
else:
|
||||
return None
|
||||
|
||||
_isolation_lookup = set(['SERIALIZABLE',
|
||||
_isolation_lookup = set(['SERIALIZABLE',
|
||||
'READ UNCOMMITTED', 'READ COMMITTED', 'REPEATABLE READ'])
|
||||
|
||||
def set_isolation_level(self, connection, level):
|
||||
@@ -1012,9 +1024,9 @@ class PGDialect(default.DefaultDialect):
|
||||
if level not in self._isolation_lookup:
|
||||
raise exc.ArgumentError(
|
||||
"Invalid value '%s' for isolation_level. "
|
||||
"Valid isolation levels for %s are %s" %
|
||||
"Valid isolation levels for %s are %s" %
|
||||
(level, self.name, ", ".join(self._isolation_lookup))
|
||||
)
|
||||
)
|
||||
cursor = connection.cursor()
|
||||
cursor.execute(
|
||||
"SET SESSION CHARACTERISTICS AS TRANSACTION "
|
||||
@@ -1035,13 +1047,13 @@ class PGDialect(default.DefaultDialect):
|
||||
def do_prepare_twophase(self, connection, xid):
|
||||
connection.execute("PREPARE TRANSACTION '%s'" % xid)
|
||||
|
||||
def do_rollback_twophase(self, connection, xid,
|
||||
def do_rollback_twophase(self, connection, xid,
|
||||
is_prepared=True, recover=False):
|
||||
if is_prepared:
|
||||
if recover:
|
||||
#FIXME: ugly hack to get out of transaction
|
||||
# context when commiting recoverable transactions
|
||||
# Must find out a way how to make the dbapi not
|
||||
#FIXME: ugly hack to get out of transaction
|
||||
# context when committing recoverable transactions
|
||||
# Must find out a way how to make the dbapi not
|
||||
# open a transaction.
|
||||
connection.execute("ROLLBACK")
|
||||
connection.execute("ROLLBACK PREPARED '%s'" % xid)
|
||||
@@ -1050,7 +1062,7 @@ class PGDialect(default.DefaultDialect):
|
||||
else:
|
||||
self.do_rollback(connection.connection)
|
||||
|
||||
def do_commit_twophase(self, connection, xid,
|
||||
def do_commit_twophase(self, connection, xid,
|
||||
is_prepared=True, recover=False):
|
||||
if is_prepared:
|
||||
if recover:
|
||||
@@ -1102,10 +1114,10 @@ class PGDialect(default.DefaultDialect):
|
||||
"n.oid=c.relnamespace where n.nspname=:schema and "
|
||||
"relname=:name",
|
||||
bindparams=[
|
||||
sql.bindparam('name',
|
||||
sql.bindparam('name',
|
||||
unicode(table_name), type_=sqltypes.Unicode),
|
||||
sql.bindparam('schema',
|
||||
unicode(schema), type_=sqltypes.Unicode)]
|
||||
sql.bindparam('schema',
|
||||
unicode(schema), type_=sqltypes.Unicode)]
|
||||
)
|
||||
)
|
||||
return bool(cursor.first())
|
||||
@@ -1121,7 +1133,7 @@ class PGDialect(default.DefaultDialect):
|
||||
bindparams=[
|
||||
sql.bindparam('name', unicode(sequence_name),
|
||||
type_=sqltypes.Unicode)
|
||||
]
|
||||
]
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -1133,7 +1145,7 @@ class PGDialect(default.DefaultDialect):
|
||||
bindparams=[
|
||||
sql.bindparam('name', unicode(sequence_name),
|
||||
type_=sqltypes.Unicode),
|
||||
sql.bindparam('schema',
|
||||
sql.bindparam('schema',
|
||||
unicode(schema), type_=sqltypes.Unicode)
|
||||
]
|
||||
)
|
||||
@@ -1261,13 +1273,13 @@ class PGDialect(default.DefaultDialect):
|
||||
SELECT relname
|
||||
FROM pg_class c
|
||||
WHERE relkind = 'v'
|
||||
AND '%(schema)s' = (select nspname from pg_namespace n
|
||||
AND '%(schema)s' = (select nspname from pg_namespace n
|
||||
where n.oid = c.relnamespace)
|
||||
""" % dict(schema=current_schema)
|
||||
# Py3K
|
||||
#view_names = [row[0] for row in connection.execute(s)]
|
||||
# Py2K
|
||||
view_names = [row[0].decode(self.encoding)
|
||||
view_names = [row[0].decode(self.encoding)
|
||||
for row in connection.execute(s)]
|
||||
# end Py2K
|
||||
return view_names
|
||||
@@ -1301,10 +1313,10 @@ class PGDialect(default.DefaultDialect):
|
||||
SQL_COLS = """
|
||||
SELECT a.attname,
|
||||
pg_catalog.format_type(a.atttypid, a.atttypmod),
|
||||
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid)
|
||||
for 128)
|
||||
(SELECT substring(pg_catalog.pg_get_expr(d.adbin, d.adrelid)
|
||||
for 128)
|
||||
FROM pg_catalog.pg_attrdef d
|
||||
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum
|
||||
WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum
|
||||
AND a.atthasdef)
|
||||
AS DEFAULT,
|
||||
a.attnotnull, a.attnum, a.attrelid as table_oid
|
||||
@@ -1313,8 +1325,8 @@ class PGDialect(default.DefaultDialect):
|
||||
AND a.attnum > 0 AND NOT a.attisdropped
|
||||
ORDER BY a.attnum
|
||||
"""
|
||||
s = sql.text(SQL_COLS,
|
||||
bindparams=[sql.bindparam('table_oid', type_=sqltypes.Integer)],
|
||||
s = sql.text(SQL_COLS,
|
||||
bindparams=[sql.bindparam('table_oid', type_=sqltypes.Integer)],
|
||||
typemap={'attname':sqltypes.Unicode, 'default':sqltypes.Unicode}
|
||||
)
|
||||
c = connection.execute(s, table_oid=table_oid)
|
||||
@@ -1325,7 +1337,7 @@ class PGDialect(default.DefaultDialect):
|
||||
# format columns
|
||||
columns = []
|
||||
for name, format_type, default, notnull, attnum, table_oid in rows:
|
||||
## strip (5) from character varying(5), timestamp(5)
|
||||
## strip (5) from character varying(5), timestamp(5)
|
||||
# with time zone, etc
|
||||
attype = re.sub(r'\([\d,]+\)', '', format_type)
|
||||
|
||||
@@ -1350,13 +1362,13 @@ class PGDialect(default.DefaultDialect):
|
||||
args = (53, )
|
||||
elif attype == 'integer':
|
||||
args = ()
|
||||
elif attype in ('timestamp with time zone',
|
||||
elif attype in ('timestamp with time zone',
|
||||
'time with time zone'):
|
||||
kwargs['timezone'] = True
|
||||
if charlen:
|
||||
kwargs['precision'] = int(charlen)
|
||||
args = ()
|
||||
elif attype in ('timestamp without time zone',
|
||||
elif attype in ('timestamp without time zone',
|
||||
'time without time zone', 'time'):
|
||||
kwargs['timezone'] = False
|
||||
if charlen:
|
||||
@@ -1397,7 +1409,7 @@ class PGDialect(default.DefaultDialect):
|
||||
# A table can't override whether the domain is nullable.
|
||||
nullable = domain['nullable']
|
||||
if domain['default'] and not default:
|
||||
# It can, however, override the default
|
||||
# It can, however, override the default
|
||||
# value, but can't set it to null.
|
||||
default = domain['default']
|
||||
continue
|
||||
@@ -1423,7 +1435,7 @@ class PGDialect(default.DefaultDialect):
|
||||
sch = schema
|
||||
if '.' not in match.group(2) and sch is not None:
|
||||
# unconditionally quote the schema name. this could
|
||||
# later be enhanced to obey quoting rules /
|
||||
# later be enhanced to obey quoting rules /
|
||||
# "quote schema"
|
||||
default = match.group(1) + \
|
||||
('"%s"' % sch) + '.' + \
|
||||
@@ -1439,27 +1451,40 @@ class PGDialect(default.DefaultDialect):
|
||||
table_oid = self.get_table_oid(connection, table_name, schema,
|
||||
info_cache=kw.get('info_cache'))
|
||||
|
||||
PK_SQL = """
|
||||
SELECT a.attname
|
||||
FROM
|
||||
if self.server_version_info < (8, 4):
|
||||
# unnest() and generate_subscripts() both introduced in
|
||||
# version 8.4
|
||||
PK_SQL = """
|
||||
SELECT a.attname
|
||||
FROM
|
||||
pg_class t
|
||||
join pg_index ix on t.oid = ix.indrelid
|
||||
join pg_attribute a
|
||||
join pg_attribute a
|
||||
on t.oid=a.attrelid and a.attnum=ANY(ix.indkey)
|
||||
WHERE
|
||||
t.oid = :table_oid and
|
||||
ix.indisprimary = 't'
|
||||
ORDER BY
|
||||
a.attnum
|
||||
"""
|
||||
t = sql.text(PK_SQL, typemap={'attname':sqltypes.Unicode})
|
||||
WHERE
|
||||
t.oid = :table_oid and ix.indisprimary = 't'
|
||||
ORDER BY a.attnum
|
||||
"""
|
||||
else:
|
||||
PK_SQL = """
|
||||
SELECT a.attname
|
||||
FROM pg_attribute a JOIN (
|
||||
SELECT unnest(ix.indkey) attnum,
|
||||
generate_subscripts(ix.indkey, 1) ord
|
||||
FROM pg_index ix
|
||||
WHERE ix.indrelid = :table_oid AND ix.indisprimary
|
||||
) k ON a.attnum=k.attnum
|
||||
WHERE a.attrelid = :table_oid
|
||||
ORDER BY k.ord
|
||||
"""
|
||||
t = sql.text(PK_SQL, typemap={'attname': sqltypes.Unicode})
|
||||
c = connection.execute(t, table_oid=table_oid)
|
||||
primary_keys = [r[0] for r in c.fetchall()]
|
||||
return primary_keys
|
||||
|
||||
@reflection.cache
|
||||
def get_pk_constraint(self, connection, table_name, schema=None, **kw):
|
||||
cols = self.get_primary_keys(connection, table_name,
|
||||
cols = self.get_primary_keys(connection, table_name,
|
||||
schema=schema, **kw)
|
||||
|
||||
table_oid = self.get_table_oid(connection, table_name, schema,
|
||||
@@ -1486,14 +1511,14 @@ class PGDialect(default.DefaultDialect):
|
||||
info_cache=kw.get('info_cache'))
|
||||
|
||||
FK_SQL = """
|
||||
SELECT r.conname,
|
||||
SELECT r.conname,
|
||||
pg_catalog.pg_get_constraintdef(r.oid, true) as condef,
|
||||
n.nspname as conschema
|
||||
FROM pg_catalog.pg_constraint r,
|
||||
pg_namespace n,
|
||||
pg_class c
|
||||
|
||||
WHERE r.conrelid = :table AND
|
||||
WHERE r.conrelid = :table AND
|
||||
r.contype = 'f' AND
|
||||
c.oid = confrelid AND
|
||||
n.oid = c.relnamespace
|
||||
@@ -1510,7 +1535,7 @@ class PGDialect(default.DefaultDialect):
|
||||
'(?:(.*?)\.)?(.*?)\((.*?)\)', condef).groups()
|
||||
constrained_columns, referred_schema, \
|
||||
referred_table, referred_columns = m
|
||||
constrained_columns = [preparer._unquote_identifier(x)
|
||||
constrained_columns = [preparer._unquote_identifier(x)
|
||||
for x in re.split(r'\s*,\s*', constrained_columns)]
|
||||
|
||||
if referred_schema:
|
||||
@@ -1525,7 +1550,7 @@ class PGDialect(default.DefaultDialect):
|
||||
# and an explicit schema was given for the referencing table.
|
||||
referred_schema = schema
|
||||
referred_table = preparer._unquote_identifier(referred_table)
|
||||
referred_columns = [preparer._unquote_identifier(x)
|
||||
referred_columns = [preparer._unquote_identifier(x)
|
||||
for x in re.split(r'\s*,\s', referred_columns)]
|
||||
fkey_d = {
|
||||
'name' : conname,
|
||||
@@ -1548,11 +1573,11 @@ class PGDialect(default.DefaultDialect):
|
||||
ix.indisunique, ix.indexprs, ix.indpred,
|
||||
a.attname
|
||||
FROM
|
||||
pg_class t
|
||||
pg_class t
|
||||
join pg_index ix on t.oid = ix.indrelid
|
||||
join pg_class i on i.oid=ix.indexrelid
|
||||
left outer join
|
||||
pg_attribute a
|
||||
left outer join
|
||||
pg_attribute a
|
||||
on t.oid=a.attrelid and a.attnum=ANY(ix.indkey)
|
||||
WHERE
|
||||
t.relkind = 'r'
|
||||
@@ -1604,13 +1629,12 @@ class PGDialect(default.DefaultDialect):
|
||||
SQL_ENUMS = """
|
||||
SELECT t.typname as "name",
|
||||
-- no enum defaults in 8.4 at least
|
||||
-- t.typdefault as "default",
|
||||
-- t.typdefault as "default",
|
||||
pg_catalog.pg_type_is_visible(t.oid) as "visible",
|
||||
n.nspname as "schema",
|
||||
e.enumlabel as "label"
|
||||
FROM pg_catalog.pg_type t
|
||||
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
||||
LEFT JOIN pg_catalog.pg_constraint r ON t.oid = r.contypid
|
||||
LEFT JOIN pg_catalog.pg_enum e ON t.oid = e.enumtypid
|
||||
WHERE t.typtype = 'e'
|
||||
ORDER BY "name", e.oid -- e.oid gives us label order
|
||||
@@ -1625,8 +1649,8 @@ class PGDialect(default.DefaultDialect):
|
||||
for enum in c.fetchall():
|
||||
if enum['visible']:
|
||||
# 'visible' just means whether or not the enum is in a
|
||||
# schema that's on the search path -- or not overriden by
|
||||
# a schema with higher presedence. If it's not visible,
|
||||
# schema that's on the search path -- or not overridden by
|
||||
# a schema with higher precedence. If it's not visible,
|
||||
# it will be prefixed with the schema-name when it's used.
|
||||
name = enum['name']
|
||||
else:
|
||||
@@ -1652,7 +1676,6 @@ class PGDialect(default.DefaultDialect):
|
||||
n.nspname as "schema"
|
||||
FROM pg_catalog.pg_type t
|
||||
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
||||
LEFT JOIN pg_catalog.pg_constraint r ON t.oid = r.contypid
|
||||
WHERE t.typtype = 'd'
|
||||
"""
|
||||
|
||||
@@ -1665,16 +1688,16 @@ class PGDialect(default.DefaultDialect):
|
||||
attype = re.search('([^\(]+)', domain['attype']).group(1)
|
||||
if domain['visible']:
|
||||
# 'visible' just means whether or not the domain is in a
|
||||
# schema that's on the search path -- or not overriden by
|
||||
# a schema with higher presedence. If it's not visible,
|
||||
# schema that's on the search path -- or not overridden by
|
||||
# a schema with higher precedence. If it's not visible,
|
||||
# it will be prefixed with the schema-name when it's used.
|
||||
name = domain['name']
|
||||
else:
|
||||
name = "%s.%s" % (domain['schema'], domain['name'])
|
||||
|
||||
domains[name] = {
|
||||
'attype':attype,
|
||||
'nullable': domain['nullable'],
|
||||
'attype':attype,
|
||||
'nullable': domain['nullable'],
|
||||
'default': domain['default']
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# postgresql/pg8000.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# postgresql/psycopg2.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -10,7 +10,7 @@ Driver
|
||||
------
|
||||
|
||||
The psycopg2 driver is available at http://pypi.python.org/pypi/psycopg2/ .
|
||||
The dialect has several behaviors which are specifically tailored towards compatibility
|
||||
The dialect has several behaviors which are specifically tailored towards compatibility
|
||||
with this module.
|
||||
|
||||
Note that psycopg1 is **not** supported.
|
||||
@@ -48,7 +48,7 @@ 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
|
||||
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::
|
||||
|
||||
@@ -61,11 +61,11 @@ See also:
|
||||
Per-Statement/Connection Execution Options
|
||||
-------------------------------------------
|
||||
|
||||
The following DBAPI-specific options are respected when used with
|
||||
The following DBAPI-specific options are respected when used with
|
||||
:meth:`.Connection.execution_options`, :meth:`.Executable.execution_options`,
|
||||
:meth:`.Query.execution_options`, in addition to those not specific to DBAPIs:
|
||||
|
||||
* isolation_level - Set the transaction isolation level for the lifespan of a
|
||||
* isolation_level - Set the transaction isolation level for the lifespan of a
|
||||
:class:`.Connection` (can only be set on a connection, not a statement or query).
|
||||
This includes the options ``SERIALIZABLE``, ``READ COMMITTED``,
|
||||
``READ UNCOMMITTED`` and ``REPEATABLE READ``.
|
||||
@@ -79,8 +79,8 @@ By default, the psycopg2 driver uses the ``psycopg2.extensions.UNICODE``
|
||||
extension, such that the DBAPI receives and returns all strings as Python
|
||||
Unicode objects directly - SQLAlchemy passes these values through without
|
||||
change. Psycopg2 here will encode/decode string values based on the
|
||||
current "client encoding" setting; by default this is the value in
|
||||
the ``postgresql.conf`` file, which often defaults to ``SQL_ASCII``.
|
||||
current "client encoding" setting; by default this is the value in
|
||||
the ``postgresql.conf`` file, which often defaults to ``SQL_ASCII``.
|
||||
Typically, this can be changed to ``utf-8``, as a more useful default::
|
||||
|
||||
#client_encoding = sql_ascii # actually, defaults to database
|
||||
@@ -90,26 +90,27 @@ Typically, this can be changed to ``utf-8``, as a more useful default::
|
||||
A second way to affect the client encoding is to set it within Psycopg2
|
||||
locally. SQLAlchemy will call psycopg2's ``set_client_encoding()``
|
||||
method (see: http://initd.org/psycopg/docs/connection.html#connection.set_client_encoding)
|
||||
on all new connections based on the value passed to
|
||||
on all new connections based on the value passed to
|
||||
:func:`.create_engine` using the ``client_encoding`` parameter::
|
||||
|
||||
engine = create_engine("postgresql://user:pass@host/dbname", client_encoding='utf8')
|
||||
|
||||
This overrides the encoding specified in the Postgresql client configuration.
|
||||
The psycopg2-specific ``client_encoding`` parameter to :func:`.create_engine` is new as of
|
||||
SQLAlchemy 0.7.3.
|
||||
|
||||
.. versionadded:: 0.7.3
|
||||
The psycopg2-specific ``client_encoding`` parameter to :func:`.create_engine`.
|
||||
|
||||
SQLAlchemy can also be instructed to skip the usage of the psycopg2
|
||||
``UNICODE`` extension and to instead utilize it's own unicode encode/decode
|
||||
services, which are normally reserved only for those DBAPIs that don't
|
||||
fully support unicode directly. Passing ``use_native_unicode=False``
|
||||
services, which are normally reserved only for those DBAPIs that don't
|
||||
fully support unicode directly. Passing ``use_native_unicode=False``
|
||||
to :func:`.create_engine` will disable usage of ``psycopg2.extensions.UNICODE``.
|
||||
SQLAlchemy will instead encode data itself into Python bytestrings on the way
|
||||
SQLAlchemy will instead encode data itself into Python bytestrings on the way
|
||||
in and coerce from bytes on the way back,
|
||||
using the value of the :func:`.create_engine` ``encoding`` parameter, which
|
||||
using the value of the :func:`.create_engine` ``encoding`` parameter, which
|
||||
defaults to ``utf-8``.
|
||||
SQLAlchemy's own unicode encode/decode functionality is steadily becoming
|
||||
obsolete as more DBAPIs support unicode fully along with the approach of
|
||||
obsolete as more DBAPIs support unicode fully along with the approach of
|
||||
Python 3; in modern usage psycopg2 should be relied upon to handle unicode.
|
||||
|
||||
Transactions
|
||||
@@ -131,7 +132,7 @@ at the API level what level should be used.
|
||||
NOTICE logging
|
||||
---------------
|
||||
|
||||
The psycopg2 dialect will log Postgresql NOTICE messages via the
|
||||
The psycopg2 dialect will log Postgresql NOTICE messages via the
|
||||
``sqlalchemy.dialects.postgresql`` logger::
|
||||
|
||||
import logging
|
||||
@@ -219,8 +220,8 @@ class PGExecutionContext_psycopg2(PGExecutionContext):
|
||||
(self.compiled and isinstance(self.compiled.statement, expression.Selectable) \
|
||||
or \
|
||||
(
|
||||
(not self.compiled or
|
||||
isinstance(self.compiled.statement, expression._TextClause))
|
||||
(not self.compiled or
|
||||
isinstance(self.compiled.statement, expression._TextClause))
|
||||
and self.statement and SERVER_SIDE_CURSOR_RE.match(self.statement))
|
||||
)
|
||||
)
|
||||
@@ -248,7 +249,7 @@ class PGExecutionContext_psycopg2(PGExecutionContext):
|
||||
|
||||
def _log_notices(self, cursor):
|
||||
for notice in cursor.connection.notices:
|
||||
# NOTICE messages have a
|
||||
# NOTICE messages have a
|
||||
# newline character at the end
|
||||
logger.info(notice.rstrip())
|
||||
|
||||
@@ -290,7 +291,7 @@ class PGDialect_psycopg2(PGDialect):
|
||||
}
|
||||
)
|
||||
|
||||
def __init__(self, server_side_cursors=False, use_native_unicode=True,
|
||||
def __init__(self, server_side_cursors=False, use_native_unicode=True,
|
||||
client_encoding=None, **kwargs):
|
||||
PGDialect.__init__(self, **kwargs)
|
||||
self.server_side_cursors = server_side_cursors
|
||||
@@ -298,12 +299,12 @@ class PGDialect_psycopg2(PGDialect):
|
||||
self.supports_unicode_binds = use_native_unicode
|
||||
self.client_encoding = client_encoding
|
||||
if self.dbapi and hasattr(self.dbapi, '__version__'):
|
||||
m = re.match(r'(\d+)\.(\d+)(?:\.(\d+))?',
|
||||
m = re.match(r'(\d+)\.(\d+)(?:\.(\d+))?',
|
||||
self.dbapi.__version__)
|
||||
if m:
|
||||
self.psycopg2_version = tuple(
|
||||
int(x)
|
||||
for x in m.group(1, 2, 3)
|
||||
int(x)
|
||||
for x in m.group(1, 2, 3)
|
||||
if x is not None)
|
||||
|
||||
@classmethod
|
||||
@@ -315,8 +316,8 @@ class PGDialect_psycopg2(PGDialect):
|
||||
def _isolation_lookup(self):
|
||||
extensions = __import__('psycopg2.extensions').extensions
|
||||
return {
|
||||
'READ COMMITTED':extensions.ISOLATION_LEVEL_READ_COMMITTED,
|
||||
'READ UNCOMMITTED':extensions.ISOLATION_LEVEL_READ_UNCOMMITTED,
|
||||
'READ COMMITTED':extensions.ISOLATION_LEVEL_READ_COMMITTED,
|
||||
'READ UNCOMMITTED':extensions.ISOLATION_LEVEL_READ_UNCOMMITTED,
|
||||
'REPEATABLE READ':extensions.ISOLATION_LEVEL_REPEATABLE_READ,
|
||||
'SERIALIZABLE':extensions.ISOLATION_LEVEL_SERIALIZABLE
|
||||
}
|
||||
@@ -327,9 +328,9 @@ class PGDialect_psycopg2(PGDialect):
|
||||
except KeyError:
|
||||
raise exc.ArgumentError(
|
||||
"Invalid value '%s' for isolation_level. "
|
||||
"Valid isolation levels for %s are %s" %
|
||||
"Valid isolation levels for %s are %s" %
|
||||
(level, self.name, ", ".join(self._isolation_lookup))
|
||||
)
|
||||
)
|
||||
|
||||
connection.set_isolation_level(level)
|
||||
|
||||
@@ -369,9 +370,10 @@ class PGDialect_psycopg2(PGDialect):
|
||||
def is_disconnect(self, e, connection, cursor):
|
||||
if isinstance(e, self.dbapi.OperationalError):
|
||||
# these error messages from libpq: interfaces/libpq/fe-misc.c.
|
||||
# TODO: these are sent through gettext in libpq and we can't
|
||||
# check within other locales - consider using connection.closed
|
||||
return 'closed the connection' in str(e) or \
|
||||
# TODO: these are sent through gettext in libpq and we can't
|
||||
# check within other locales - consider using connection.closed
|
||||
return 'terminating connection' in str(e) or \
|
||||
'closed the connection' in str(e) or \
|
||||
'connection not open' in str(e) or \
|
||||
'could not receive data from server' in str(e)
|
||||
elif isinstance(e, self.dbapi.InterfaceError):
|
||||
@@ -379,7 +381,7 @@ class PGDialect_psycopg2(PGDialect):
|
||||
return 'connection already closed' in str(e) or \
|
||||
'cursor already closed' in str(e)
|
||||
elif isinstance(e, self.dbapi.ProgrammingError):
|
||||
# not sure where this path is originally from, it may
|
||||
# not sure where this path is originally from, it may
|
||||
# be obsolete. It really says "losed", not "closed".
|
||||
return "losed the connection unexpectedly" in str(e)
|
||||
else:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# postgresql/pypostgresql.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
@@ -40,7 +40,7 @@ class PGDialect_pypostgresql(PGDialect):
|
||||
default_paramstyle = 'pyformat'
|
||||
|
||||
# requires trunk version to support sane rowcounts
|
||||
# TODO: use dbapi version information to set this flag appropariately
|
||||
# TODO: use dbapi version information to set this flag appropriately
|
||||
supports_sane_rowcount = True
|
||||
supports_sane_multi_rowcount = False
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# postgresql/zxjdbc.py
|
||||
# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
# the MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
Reference in New Issue
Block a user