summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorBrad Allen <bradallen137@gmail.com>2010-03-15 11:42:35 -0600
committerBrad Allen <bradallen137@gmail.com>2010-03-15 11:42:35 -0600
commit00ac90accb8d29cd665a9c14089c51a32afb3bd8 (patch)
tree0c3ae3d78d0f2b929e49e071ef8ce426e20eaf23 /lib
parent96209a12486df72ab0b2285441de8505489e26b3 (diff)
parent5dcc32fd5a81c41b50bc35573d190a60a344c3c6 (diff)
downloadsqlalchemy-00ac90accb8d29cd665a9c14089c51a32afb3bd8.tar.gz
merged mainline default branch
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/connectors/mxodbc.py7
-rw-r--r--lib/sqlalchemy/dialects/firebird/kinterbasdb.py6
-rw-r--r--lib/sqlalchemy/dialects/informix/informixdb.py8
-rw-r--r--lib/sqlalchemy/dialects/maxdb/sapdb.py4
-rw-r--r--lib/sqlalchemy/dialects/mssql/zxjdbc.py10
-rw-r--r--lib/sqlalchemy/dialects/mysql/mysqlconnector.py16
-rw-r--r--lib/sqlalchemy/dialects/mysql/mysqldb.py16
-rw-r--r--lib/sqlalchemy/dialects/mysql/oursql.py8
-rw-r--r--lib/sqlalchemy/dialects/mysql/pyodbc.py10
-rw-r--r--lib/sqlalchemy/dialects/mysql/zxjdbc.py8
-rw-r--r--lib/sqlalchemy/dialects/oracle/__init__.py6
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py39
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py160
-rw-r--r--lib/sqlalchemy/dialects/oracle/zxjdbc.py26
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py26
-rw-r--r--lib/sqlalchemy/dialects/postgresql/pg8000.py16
-rw-r--r--lib/sqlalchemy/dialects/postgresql/psycopg2.py36
-rw-r--r--lib/sqlalchemy/dialects/postgresql/pypostgresql.py8
-rw-r--r--lib/sqlalchemy/dialects/postgresql/zxjdbc.py4
-rw-r--r--lib/sqlalchemy/dialects/sqlite/base.py28
-rw-r--r--lib/sqlalchemy/dialects/sqlite/pysqlite.py4
-rw-r--r--lib/sqlalchemy/dialects/sybase/__init__.py20
-rw-r--r--lib/sqlalchemy/dialects/sybase/base.py389
-rw-r--r--lib/sqlalchemy/dialects/sybase/mxodbc.py11
-rw-r--r--lib/sqlalchemy/dialects/sybase/pyodbc.py12
-rw-r--r--lib/sqlalchemy/dialects/sybase/pysybase.py81
-rw-r--r--lib/sqlalchemy/dialects/sybase/schema.py51
-rw-r--r--lib/sqlalchemy/engine/__init__.py12
-rw-r--r--lib/sqlalchemy/engine/base.py39
-rw-r--r--lib/sqlalchemy/engine/default.py56
-rw-r--r--lib/sqlalchemy/engine/strategies.py15
-rw-r--r--lib/sqlalchemy/log.py26
-rw-r--r--lib/sqlalchemy/orm/query.py30
-rw-r--r--lib/sqlalchemy/orm/strategies.py2
-rw-r--r--lib/sqlalchemy/pool.py85
-rw-r--r--lib/sqlalchemy/test/config.py1
-rw-r--r--lib/sqlalchemy/test/engines.py23
37 files changed, 748 insertions, 551 deletions
diff --git a/lib/sqlalchemy/connectors/mxodbc.py b/lib/sqlalchemy/connectors/mxodbc.py
index de9638151..2374b4c95 100644
--- a/lib/sqlalchemy/connectors/mxodbc.py
+++ b/lib/sqlalchemy/connectors/mxodbc.py
@@ -29,16 +29,15 @@ class MxODBCConnector(Connector):
raise ImportError, "Unrecognized platform for mxODBC import"
return module
- def visit_pool(self, pool):
- def connect(conn, rec):
+ def on_connect(self):
+ def connect(conn):
conn.stringformat = self.dbapi.MIXED_STRINGFORMAT
conn.datetimeformat = self.dbapi.PYDATETIME_DATETIMEFORMAT
conn.errorhandler = error_handler
# Alternatives to experiment with:
#conn.bindmethod = self.dbapi.BIND_USING_PYTHONTYPE
#conn.bindmethod = self.dbapi.BIND_USING_SQLTYPE
-
- pool.add_listener({'connect':connect})
+ return connect
def create_connect_args(self, url):
""" Return a tuple of *args,**kwargs for creating a connection.
diff --git a/lib/sqlalchemy/dialects/firebird/kinterbasdb.py b/lib/sqlalchemy/dialects/firebird/kinterbasdb.py
index ca2c561da..66d001e0c 100644
--- a/lib/sqlalchemy/dialects/firebird/kinterbasdb.py
+++ b/lib/sqlalchemy/dialects/firebird/kinterbasdb.py
@@ -30,13 +30,13 @@ __ http://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurr
from sqlalchemy.dialects.firebird.base import FBDialect, FBCompiler
-class Firebird_kinterbasdb(FBDialect):
+class FBDialect_kinterbasdb(FBDialect):
driver = 'kinterbasdb'
supports_sane_rowcount = False
supports_sane_multi_rowcount = False
def __init__(self, type_conv=200, concurrency_level=1, **kwargs):
- super(Firebird_kinterbasdb, self).__init__(**kwargs)
+ super(FBDialect_kinterbasdb, self).__init__(**kwargs)
self.type_conv = type_conv
self.concurrency_level = concurrency_level
@@ -98,4 +98,4 @@ class Firebird_kinterbasdb(FBDialect):
else:
return False
-dialect = Firebird_kinterbasdb
+dialect = FBDialect_kinterbasdb
diff --git a/lib/sqlalchemy/dialects/informix/informixdb.py b/lib/sqlalchemy/dialects/informix/informixdb.py
index 722a0f0f4..a1305c4db 100644
--- a/lib/sqlalchemy/dialects/informix/informixdb.py
+++ b/lib/sqlalchemy/dialects/informix/informixdb.py
@@ -1,16 +1,16 @@
from sqlalchemy.dialects.informix.base import InformixDialect
from sqlalchemy.engine import default
-class InfoExecutionContext(default.DefaultExecutionContext):
+class InformixExecutionContext_informixdb(default.DefaultExecutionContext):
def post_exec(self):
if self.isinsert:
self._lastrowid = [self.cursor.sqlerrd[1]]
-class Informix_informixdb(InformixDialect):
+class InformixDialect_informixdb(InformixDialect):
driver = 'informixdb'
default_paramstyle = 'qmark'
- execution_context_cls = InfoExecutionContext
+ execution_context_cls = InformixExecutionContext_informixdb
@classmethod
def dbapi(cls):
@@ -43,4 +43,4 @@ class Informix_informixdb(InformixDialect):
return False
-dialect = Informix_informixdb
+dialect = InformixDialect_informixdb
diff --git a/lib/sqlalchemy/dialects/maxdb/sapdb.py b/lib/sqlalchemy/dialects/maxdb/sapdb.py
index 10e61228e..f36323996 100644
--- a/lib/sqlalchemy/dialects/maxdb/sapdb.py
+++ b/lib/sqlalchemy/dialects/maxdb/sapdb.py
@@ -1,6 +1,6 @@
from sqlalchemy.dialects.maxdb.base import MaxDBDialect
-class MaxDB_sapdb(MaxDBDialect):
+class MaxDBDialect_sapdb(MaxDBDialect):
driver = 'sapdb'
@classmethod
@@ -14,4 +14,4 @@ class MaxDB_sapdb(MaxDBDialect):
return [], opts
-dialect = MaxDB_sapdb \ No newline at end of file
+dialect = MaxDBDialect_sapdb \ No newline at end of file
diff --git a/lib/sqlalchemy/dialects/mssql/zxjdbc.py b/lib/sqlalchemy/dialects/mssql/zxjdbc.py
index 28b9547d8..b11eb173d 100644
--- a/lib/sqlalchemy/dialects/mssql/zxjdbc.py
+++ b/lib/sqlalchemy/dialects/mssql/zxjdbc.py
@@ -22,12 +22,12 @@ from sqlalchemy.connectors.zxJDBC import ZxJDBCConnector
from sqlalchemy.dialects.mssql.base import MSDialect, MSExecutionContext
from sqlalchemy.engine import base
-class MS_zxjdbcExecutionContext(MSExecutionContext):
+class MSExecutionContext_zxjdbc(MSExecutionContext):
_embedded_scope_identity = False
def pre_exec(self):
- super(MS_zxjdbcExecutionContext, self).pre_exec()
+ super(MSExecutionContext_zxjdbc, self).pre_exec()
# scope_identity after the fact returns null in jTDS so we must
# embed it
if self._select_lastrowid and self.dialect.use_scope_identity:
@@ -52,13 +52,13 @@ class MS_zxjdbcExecutionContext(MSExecutionContext):
self.cursor.execute("SET IDENTITY_INSERT %s OFF" % table)
-class MS_zxjdbc(ZxJDBCConnector, MSDialect):
+class MSDialect_zxjdbc(ZxJDBCConnector, MSDialect):
jdbc_db_name = 'jtds:sqlserver'
jdbc_driver_name = 'net.sourceforge.jtds.jdbc.Driver'
- execution_ctx_cls = MS_zxjdbcExecutionContext
+ execution_ctx_cls = MSExecutionContext_zxjdbc
def _get_server_version_info(self, connection):
return tuple(int(x) for x in connection.connection.dbversion.split('.'))
-dialect = MS_zxjdbc
+dialect = MSDialect_zxjdbc
diff --git a/lib/sqlalchemy/dialects/mysql/mysqlconnector.py b/lib/sqlalchemy/dialects/mysql/mysqlconnector.py
index 6b3888cb0..33c18f6d6 100644
--- a/lib/sqlalchemy/dialects/mysql/mysqlconnector.py
+++ b/lib/sqlalchemy/dialects/mysql/mysqlconnector.py
@@ -15,13 +15,13 @@ from sqlalchemy.sql import operators as sql_operators
from sqlalchemy import exc, log, schema, sql, types as sqltypes, util
from sqlalchemy import processors
-class MySQL_mysqlconnectorExecutionContext(MySQLExecutionContext):
+class MySQLExecutionContext_mysqlconnector(MySQLExecutionContext):
def get_lastrowid(self):
return self.cursor.lastrowid
-class MySQL_mysqlconnectorCompiler(MySQLCompiler):
+class MySQLCompiler_mysqlconnector(MySQLCompiler):
def visit_mod(self, binary, **kw):
return self.process(binary.left) + " %% " + self.process(binary.right)
@@ -37,7 +37,7 @@ class _DecimalType(_NumericType):
class _myconnpyNumeric(_DecimalType, NUMERIC):
pass
-class MySQL_mysqlconnectorIdentifierPreparer(MySQLIdentifierPreparer):
+class MySQLIdentifierPreparer_mysqlconnector(MySQLIdentifierPreparer):
def _escape_identifier(self, value):
value = value.replace(self.escape_quote, self.escape_to_quote)
@@ -49,7 +49,7 @@ class _myconnpyBIT(BIT):
return None
-class MySQL_mysqlconnector(MySQLDialect):
+class MySQLDialect_mysqlconnector(MySQLDialect):
driver = 'mysqlconnector'
supports_unicode_statements = True
supports_unicode_binds = True
@@ -57,10 +57,10 @@ class MySQL_mysqlconnector(MySQLDialect):
supports_sane_multi_rowcount = True
default_paramstyle = 'format'
- execution_ctx_cls = MySQL_mysqlconnectorExecutionContext
- statement_compiler = MySQL_mysqlconnectorCompiler
+ execution_ctx_cls = MySQLExecutionContext_mysqlconnector
+ statement_compiler = MySQLCompiler_mysqlconnector
- preparer = MySQL_mysqlconnectorIdentifierPreparer
+ preparer = MySQLIdentifierPreparer_mysqlconnector
colspecs = util.update_copy(
MySQLDialect.colspecs,
@@ -128,4 +128,4 @@ class MySQL_mysqlconnector(MySQLDialect):
def _compat_fetchone(self, rp, charset=None):
return rp.fetchone()
-dialect = MySQL_mysqlconnector
+dialect = MySQLDialect_mysqlconnector
diff --git a/lib/sqlalchemy/dialects/mysql/mysqldb.py b/lib/sqlalchemy/dialects/mysql/mysqldb.py
index 8cfd5930f..038e58a4c 100644
--- a/lib/sqlalchemy/dialects/mysql/mysqldb.py
+++ b/lib/sqlalchemy/dialects/mysql/mysqldb.py
@@ -30,7 +30,7 @@ from sqlalchemy.sql import operators as sql_operators
from sqlalchemy import exc, log, schema, sql, types as sqltypes, util
from sqlalchemy import processors
-class MySQL_mysqldbExecutionContext(MySQLExecutionContext):
+class MySQLExecutionContext_mysqldb(MySQLExecutionContext):
@property
def rowcount(self):
@@ -40,7 +40,7 @@ class MySQL_mysqldbExecutionContext(MySQLExecutionContext):
return self.cursor.rowcount
-class MySQL_mysqldbCompiler(MySQLCompiler):
+class MySQLCompiler_mysqldb(MySQLCompiler):
def visit_mod(self, binary, **kw):
return self.process(binary.left) + " %% " + self.process(binary.right)
@@ -62,22 +62,22 @@ class _MySQLdbNumeric(_DecimalType, NUMERIC):
class _MySQLdbDecimal(_DecimalType, DECIMAL):
pass
-class MySQL_mysqldbIdentifierPreparer(MySQLIdentifierPreparer):
+class MySQLIdentifierPreparer_mysqldb(MySQLIdentifierPreparer):
def _escape_identifier(self, value):
value = value.replace(self.escape_quote, self.escape_to_quote)
return value.replace("%", "%%")
-class MySQL_mysqldb(MySQLDialect):
+class MySQLDialect_mysqldb(MySQLDialect):
driver = 'mysqldb'
supports_unicode_statements = False
supports_sane_rowcount = True
supports_sane_multi_rowcount = True
default_paramstyle = 'format'
- execution_ctx_cls = MySQL_mysqldbExecutionContext
- statement_compiler = MySQL_mysqldbCompiler
- preparer = MySQL_mysqldbIdentifierPreparer
+ execution_ctx_cls = MySQLExecutionContext_mysqldb
+ statement_compiler = MySQLCompiler_mysqldb
+ preparer = MySQLIdentifierPreparer_mysqldb
colspecs = util.update_copy(
MySQLDialect.colspecs,
@@ -190,4 +190,4 @@ class MySQL_mysqldb(MySQLDialect):
return 'latin1'
-dialect = MySQL_mysqldb
+dialect = MySQLDialect_mysqldb
diff --git a/lib/sqlalchemy/dialects/mysql/oursql.py b/lib/sqlalchemy/dialects/mysql/oursql.py
index 83db1bc72..6c8bbcac4 100644
--- a/lib/sqlalchemy/dialects/mysql/oursql.py
+++ b/lib/sqlalchemy/dialects/mysql/oursql.py
@@ -46,13 +46,13 @@ class _oursqlBIT(BIT):
return None
-class MySQL_oursqlExecutionContext(MySQLExecutionContext):
+class MySQLExecutionContext_oursql(MySQLExecutionContext):
@property
def plain_query(self):
return self.execution_options.get('_oursql_plain_query', False)
-class MySQL_oursql(MySQLDialect):
+class MySQLDialect_oursql(MySQLDialect):
driver = 'oursql'
# Py3K
# description_encoding = None
@@ -65,7 +65,7 @@ class MySQL_oursql(MySQLDialect):
supports_sane_rowcount = True
supports_sane_multi_rowcount = True
- execution_ctx_cls = MySQL_oursqlExecutionContext
+ execution_ctx_cls = MySQLExecutionContext_oursql
colspecs = util.update_copy(
MySQLDialect.colspecs,
@@ -249,4 +249,4 @@ class MySQL_oursql(MySQLDialect):
return rp.first()
-dialect = MySQL_oursql
+dialect = MySQLDialect_oursql
diff --git a/lib/sqlalchemy/dialects/mysql/pyodbc.py b/lib/sqlalchemy/dialects/mysql/pyodbc.py
index 7cb2f3642..5add45b21 100644
--- a/lib/sqlalchemy/dialects/mysql/pyodbc.py
+++ b/lib/sqlalchemy/dialects/mysql/pyodbc.py
@@ -8,7 +8,7 @@ from sqlalchemy.engine import base as engine_base
from sqlalchemy import util
import re
-class MySQL_pyodbcExecutionContext(MySQLExecutionContext):
+class MySQLExecutionContext_pyodbc(MySQLExecutionContext):
def get_lastrowid(self):
cursor = self.create_cursor()
@@ -17,16 +17,16 @@ class MySQL_pyodbcExecutionContext(MySQLExecutionContext):
cursor.close()
return lastrowid
-class MySQL_pyodbc(PyODBCConnector, MySQLDialect):
+class MySQLDialect_pyodbc(PyODBCConnector, MySQLDialect):
supports_unicode_statements = False
- execution_ctx_cls = MySQL_pyodbcExecutionContext
+ execution_ctx_cls = MySQLExecutionContext_pyodbc
pyodbc_driver_name = "MySQL"
def __init__(self, **kw):
# deal with http://code.google.com/p/pyodbc/issues/detail?id=25
kw.setdefault('convert_unicode', True)
- super(MySQL_pyodbc, self).__init__(**kw)
+ super(MySQLDialect_pyodbc, self).__init__(**kw)
def _detect_charset(self, connection):
"""Sniff out the character set in use for connection results."""
@@ -54,4 +54,4 @@ class MySQL_pyodbc(PyODBCConnector, MySQLDialect):
else:
return None
-dialect = MySQL_pyodbc
+dialect = MySQLDialect_pyodbc
diff --git a/lib/sqlalchemy/dialects/mysql/zxjdbc.py b/lib/sqlalchemy/dialects/mysql/zxjdbc.py
index dcb46789a..f4cf0013c 100644
--- a/lib/sqlalchemy/dialects/mysql/zxjdbc.py
+++ b/lib/sqlalchemy/dialects/mysql/zxjdbc.py
@@ -38,7 +38,7 @@ class _ZxJDBCBit(BIT):
return process
-class MySQL_zxjdbcExecutionContext(MySQLExecutionContext):
+class MySQLExecutionContext_zxjdbc(MySQLExecutionContext):
def get_lastrowid(self):
cursor = self.create_cursor()
cursor.execute("SELECT LAST_INSERT_ID()")
@@ -47,11 +47,11 @@ class MySQL_zxjdbcExecutionContext(MySQLExecutionContext):
return lastrowid
-class MySQL_zxjdbc(ZxJDBCConnector, MySQLDialect):
+class MySQLDialect_zxjdbc(ZxJDBCConnector, MySQLDialect):
jdbc_db_name = 'mysql'
jdbc_driver_name = 'com.mysql.jdbc.Driver'
- execution_ctx_cls = MySQL_zxjdbcExecutionContext
+ execution_ctx_cls = MySQLExecutionContext_zxjdbc
colspecs = util.update_copy(
MySQLDialect.colspecs,
@@ -101,4 +101,4 @@ class MySQL_zxjdbc(ZxJDBCConnector, MySQLDialect):
version.append(n)
return tuple(version)
-dialect = MySQL_zxjdbc
+dialect = MySQLDialect_zxjdbc
diff --git a/lib/sqlalchemy/dialects/oracle/__init__.py b/lib/sqlalchemy/dialects/oracle/__init__.py
index eb47e80cb..78d3c8fab 100644
--- a/lib/sqlalchemy/dialects/oracle/__init__.py
+++ b/lib/sqlalchemy/dialects/oracle/__init__.py
@@ -5,11 +5,13 @@ base.dialect = cx_oracle.dialect
from sqlalchemy.dialects.oracle.base import \
VARCHAR, NVARCHAR, CHAR, DATE, DATETIME, NUMBER,\
BLOB, BFILE, CLOB, NCLOB, TIMESTAMP, RAW,\
- FLOAT, DOUBLE_PRECISION, LONG, dialect, INTERVAL
+ FLOAT, DOUBLE_PRECISION, LONG, dialect, INTERVAL,\
+ VARCHAR2, NVARCHAR2
__all__ = (
'VARCHAR', 'NVARCHAR', 'CHAR', 'DATE', 'DATETIME', 'NUMBER',
'BLOB', 'BFILE', 'CLOB', 'NCLOB', 'TIMESTAMP', 'RAW',
-'FLOAT', 'DOUBLE_PRECISION', 'LONG', 'dialect', 'INTERVAL'
+'FLOAT', 'DOUBLE_PRECISION', 'LONG', 'dialect', 'INTERVAL',
+'VARCHAR2', 'NVARCHAR2'
)
diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py
index 3107c8b6c..eccf11380 100644
--- a/lib/sqlalchemy/dialects/oracle/base.py
+++ b/lib/sqlalchemy/dialects/oracle/base.py
@@ -120,7 +120,15 @@ from sqlalchemy import types as sqltypes
from sqlalchemy.types import VARCHAR, NVARCHAR, CHAR, DATE, DATETIME, \
BLOB, CLOB, TIMESTAMP, FLOAT
-RESERVED_WORDS = set('''SHARE RAW DROP BETWEEN FROM DESC OPTION PRIOR LONG THEN DEFAULT ALTER IS INTO MINUS INTEGER NUMBER GRANT IDENTIFIED ALL TO ORDER ON FLOAT DATE HAVING CLUSTER NOWAIT RESOURCE ANY TABLE INDEX FOR UPDATE WHERE CHECK SMALLINT WITH DELETE BY ASC REVOKE LIKE SIZE RENAME NOCOMPRESS NULL GROUP VALUES 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'''.split())
+RESERVED_WORDS = set('SHARE RAW DROP BETWEEN FROM DESC OPTION PRIOR LONG THEN '
+ 'DEFAULT ALTER IS INTO MINUS INTEGER NUMBER GRANT IDENTIFIED '
+ 'ALL TO ORDER ON FLOAT DATE HAVING CLUSTER NOWAIT RESOURCE ANY '
+ 'TABLE INDEX FOR UPDATE WHERE CHECK SMALLINT WITH DELETE BY ASC '
+ 'REVOKE LIKE SIZE RENAME NOCOMPRESS NULL GROUP VALUES 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'.split())
class RAW(sqltypes.LargeBinary):
pass
@@ -590,22 +598,31 @@ class OracleDialect(default.DefaultDialect):
def normalize_name(self, name):
if name is None:
return None
- elif (name.upper() == name and
- not self.identifier_preparer._requires_quotes(name.lower().decode(self.encoding))):
- return name.lower().decode(self.encoding)
+ # Py2K
+ if isinstance(name, str):
+ name = name.decode(self.encoding)
+ # end Py2K
+ if name.upper() == name and \
+ not self.identifier_preparer._requires_quotes(name.lower()):
+ return name.lower()
else:
- return name.decode(self.encoding)
+ return name
def denormalize_name(self, name):
if name is None:
return None
elif name.lower() == name and not self.identifier_preparer._requires_quotes(name.lower()):
- return name.upper().encode(self.encoding)
+ name = name.upper()
+ # Py2K
+ if not self.supports_unicode_binds:
+ name = name.encode(self.encoding)
else:
- return name.encode(self.encoding)
+ name = unicode(name)
+ # end Py2K
+ return name
def _get_default_schema_name(self, connection):
- return self.normalize_name(connection.execute('SELECT USER FROM DUAL').scalar())
+ return self.normalize_name(connection.execute(u'SELECT USER FROM DUAL').scalar())
def table_names(self, connection, schema):
# note that table_names() isnt loading DBLINKed or synonym'ed tables
@@ -664,7 +681,11 @@ class OracleDialect(default.DefaultDialect):
resolve_synonyms=False, dblink='', **kw):
if resolve_synonyms:
- actual_name, owner, dblink, synonym = self._resolve_synonym(connection, desired_owner=self.denormalize_name(schema), desired_synonym=self.denormalize_name(table_name))
+ actual_name, owner, dblink, synonym = self._resolve_synonym(
+ connection,
+ desired_owner=self.denormalize_name(schema),
+ desired_synonym=self.denormalize_name(table_name)
+ )
else:
actual_name, owner, dblink, synonym = None, None, None, None
if not actual_name:
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index f4c2e295f..eee2bb1ba 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -100,28 +100,35 @@ class _LOBMixin(object):
# return the cx_oracle.LOB directly.
return None
- super_process = super(_LOBMixin, self).result_processor(dialect, coltype)
- if super_process:
- def process(value):
- if value is not None:
- return super_process(value.read())
- else:
- return super_process(value)
- else:
- def process(value):
- if value is not None:
- return value.read()
- else:
- return value
+ def process(value):
+ if value is not None:
+ return value.read()
+ else:
+ return value
return process
class _NativeUnicodeMixin(object):
+ # Py2K
+ def bind_processor(self, dialect):
+ if dialect._cx_oracle_with_unicode:
+ def process(value):
+ if value is None:
+ return value
+ else:
+ return unicode(value)
+ return process
+ else:
+ return super(_NativeUnicodeMixin, self).bind_processor(dialect)
+ # end Py2K
+
def result_processor(self, dialect, coltype):
# if we know cx_Oracle will return unicode,
# don't process results
- if self.convert_unicode != 'force' and \
+ if dialect._cx_oracle_with_unicode:
+ return None
+ elif self.convert_unicode != 'force' and \
dialect._cx_oracle_native_nvarchar and \
- coltype == dialect.dbapi.UNICODE:
+ coltype in dialect._cx_oracle_unicode_types:
return None
else:
return super(_NativeUnicodeMixin, self).result_processor(dialect, coltype)
@@ -132,7 +139,7 @@ class _OracleChar(_NativeUnicodeMixin, sqltypes.CHAR):
class _OracleNVarChar(_NativeUnicodeMixin, sqltypes.NVARCHAR):
def get_dbapi_type(self, dbapi):
- return dbapi.UNICODE
+ return getattr(dbapi, 'UNICODE', dbapi.STRING)
class _OracleText(_LOBMixin, sqltypes.Text):
def get_dbapi_type(self, dbapi):
@@ -141,27 +148,23 @@ class _OracleText(_LOBMixin, sqltypes.Text):
class _OracleString(_NativeUnicodeMixin, sqltypes.String):
pass
-class _OracleUnicodeText(_NativeUnicodeMixin, sqltypes.UnicodeText):
+class _OracleUnicodeText(_LOBMixin, _NativeUnicodeMixin, sqltypes.UnicodeText):
def get_dbapi_type(self, dbapi):
return dbapi.NCLOB
def result_processor(self, dialect, coltype):
- if not dialect.auto_convert_lobs:
- # return the cx_oracle.LOB directly.
+ lob_processor = _LOBMixin.result_processor(self, dialect, coltype)
+ if lob_processor is None:
return None
- if dialect._cx_oracle_native_nvarchar:
+ string_processor = _NativeUnicodeMixin.result_processor(self, dialect, coltype)
+
+ if string_processor is None:
+ return lob_processor
+ else:
def process(value):
- if value is not None:
- return value.read()
- else:
- return value
+ return string_processor(lob_processor(value))
return process
- else:
- # TODO: this is wrong - we are getting a LOB here
- # no matter what version of oracle, so process()
- # is still needed
- return super(_OracleUnicodeText, self).result_processor(dialect, coltype)
class _OracleInteger(sqltypes.Integer):
def result_processor(self, dialect, coltype):
@@ -204,7 +207,7 @@ colspecs = {
sqltypes.NVARCHAR : _OracleNVarChar,
}
-class Oracle_cx_oracleCompiler(OracleCompiler):
+class OracleCompiler_cx_oracle(OracleCompiler):
def bindparam_string(self, name):
if self.preparer._bindparam_requires_quotes(name):
quoted_name = '"%s"' % name
@@ -213,7 +216,9 @@ class Oracle_cx_oracleCompiler(OracleCompiler):
else:
return OracleCompiler.bindparam_string(self, name)
-class Oracle_cx_oracleExecutionContext(OracleExecutionContext):
+
+class OracleExecutionContext_cx_oracle(OracleExecutionContext):
+
def pre_exec(self):
quoted_bind_names = getattr(self.compiled, '_quoted_bind_names', {})
if quoted_bind_names:
@@ -227,9 +232,8 @@ class Oracle_cx_oracleExecutionContext(OracleExecutionContext):
# on String, including that outparams/RETURNING
# breaks for varchars
self.set_input_sizes(quoted_bind_names,
- exclude_types=[
- self.dialect.dbapi.STRING,
- self.dialect.dbapi.UNICODE])
+ exclude_types=self.dialect._cx_oracle_string_types
+ )
if len(self.compiled_parameters) == 1:
for key in self.compiled.binds:
@@ -266,7 +270,7 @@ class Oracle_cx_oracleExecutionContext(OracleExecutionContext):
if self.cursor.description is not None:
for column in self.cursor.description:
type_code = column[1]
- if type_code in self.dialect.ORACLE_BINARY_TYPES:
+ if type_code in self.dialect._cx_oracle_binary_types:
result = base.BufferedColumnResultProxy(self)
if result is None:
@@ -297,6 +301,25 @@ class Oracle_cx_oracleExecutionContext(OracleExecutionContext):
return result
+class OracleExecutionContext_cx_oracle_with_unicode(OracleExecutionContext_cx_oracle):
+ """Support WITH_UNICODE in Python 2.xx.
+
+ WITH_UNICODE allows cx_Oracle's Python 3 unicode handling behavior under Python 2.x.
+ This mode in some cases disallows and in other cases silently
+ passes corrupted data when non-Python-unicode strings (a.k.a. plain old Python strings)
+ are passed as arguments to connect(), the statement sent to execute(), or any of the bind
+ parameter keys or values sent to execute(). This optional context
+ therefore ensures that all statements are passed as Python unicode objects.
+
+ """
+ def __init__(self, *arg, **kw):
+ OracleExecutionContext_cx_oracle.__init__(self, *arg, **kw)
+ self.statement = unicode(self.statement)
+
+ def _execute_scalar(self, stmt):
+ return super(OracleExecutionContext_cx_oracle_with_unicode, self).\
+ _execute_scalar(unicode(stmt))
+
class ReturningResultProxy(base.FullyBufferedResultProxy):
"""Result proxy which stuffs the _returning clause + outparams into the fetch."""
@@ -316,11 +339,12 @@ class ReturningResultProxy(base.FullyBufferedResultProxy):
return ret
def _buffer_rows(self):
- return [tuple(self._returning_params["ret_%d" % i] for i, c in enumerate(self._returning_params))]
+ return [tuple(self._returning_params["ret_%d" % i]
+ for i, c in enumerate(self._returning_params))]
-class Oracle_cx_oracle(OracleDialect):
- execution_ctx_cls = Oracle_cx_oracleExecutionContext
- statement_compiler = Oracle_cx_oracleCompiler
+class OracleDialect_cx_oracle(OracleDialect):
+ execution_ctx_cls = OracleExecutionContext_cx_oracle
+ statement_compiler = OracleCompiler_cx_oracle
driver = "cx_oracle"
colspecs = colspecs
@@ -340,17 +364,50 @@ class Oracle_cx_oracle(OracleDialect):
self.auto_setinputsizes = auto_setinputsizes
self.auto_convert_lobs = auto_convert_lobs
- def vers(num):
- return tuple([int(x) for x in num.split('.')])
-
if hasattr(self.dbapi, 'version'):
- cx_oracle_ver = vers(self.dbapi.version)
+ cx_oracle_ver = tuple([int(x) for x in self.dbapi.version.split('.')])
self.supports_unicode_binds = cx_oracle_ver >= (5, 0)
self._cx_oracle_native_nvarchar = cx_oracle_ver >= (5, 0)
+ else:
+ cx_oracle_ver = None
- if self.dbapi is None or not self.auto_convert_lobs or not 'CLOB' in self.dbapi.__dict__:
+ def types(*names):
+ return set([getattr(self.dbapi, name, None) for name in names]).difference([None])
+
+ self._cx_oracle_string_types = types("STRING", "UNICODE", "NCLOB", "CLOB")
+ self._cx_oracle_unicode_types = types("UNICODE", "NCLOB")
+ self._cx_oracle_binary_types = types("BFILE", "CLOB", "NCLOB", "BLOB")
+
+ if cx_oracle_ver is None:
+ # this occurs in tests with mock DBAPIs
+ self._cx_oracle_string_types = set()
+ self._cx_oracle_with_unicode = False
+ elif cx_oracle_ver >= (5,) and not hasattr(self.dbapi, 'UNICODE'):
+ # cx_Oracle WITH_UNICODE mode. *only* python
+ # unicode objects accepted for anything
+ self.supports_unicode_statements = True
+ self.supports_unicode_binds = True
+ self._cx_oracle_with_unicode = True
+ # Py2K
+ # There's really no reason to run with WITH_UNICODE under Python 2.x.
+ # Give the user a hint.
+ util.warn("cx_Oracle is compiled under Python 2.xx using the "
+ "WITH_UNICODE flag. Consider recompiling cx_Oracle without "
+ "this flag, which is in no way necessary for full support of Unicode. "
+ "Otherwise, all string-holding bind parameters must "
+ "be explicitly typed using SQLAlchemy's String type or one of its subtypes,"
+ "or otherwise be passed as Python unicode. Plain Python strings "
+ "passed as bind parameters will be silently corrupted by cx_Oracle."
+ )
+ self.execution_ctx_cls = OracleExecutionContext_cx_oracle_with_unicode
+ # end Py2K
+ else:
+ self._cx_oracle_with_unicode = False
+
+ if cx_oracle_ver is None or \
+ not self.auto_convert_lobs or \
+ not hasattr(self.dbapi, 'CLOB'):
self.dbapi_type_map = {}
- self.ORACLE_BINARY_TYPES = []
else:
# only use this for LOB objects. using it for strings, dates
# etc. leads to a little too much magic, reflection doesn't know if it should
@@ -361,7 +418,6 @@ class Oracle_cx_oracle(OracleDialect):
self.dbapi.BLOB: oracle.BLOB(),
self.dbapi.BINARY: oracle.RAW(),
}
- self.ORACLE_BINARY_TYPES = [getattr(self.dbapi, k) for k in ["BFILE", "CLOB", "NCLOB", "BLOB"] if hasattr(self.dbapi, k)]
@classmethod
def dbapi(cls):
@@ -395,6 +451,14 @@ class Oracle_cx_oracle(OracleDialect):
threaded=self.threaded,
twophase=self.allow_twophase,
)
+
+ # Py2K
+ if self._cx_oracle_with_unicode:
+ for k, v in opts.items():
+ if isinstance(v, str):
+ opts[k] = unicode(v)
+ # end Py2K
+
if 'mode' in url.query:
opts['mode'] = url.query['mode']
if isinstance(opts['mode'], basestring):
@@ -405,8 +469,6 @@ class Oracle_cx_oracle(OracleDialect):
opts['mode'] = self.dbapi.SYSOPER
else:
util.coerce_kw_type(opts, 'mode', int)
- # Can't set 'handle' or 'pool' via URL query args, use connect_args
-
return ([], opts)
def _get_server_version_info(self, connection):
@@ -442,4 +504,4 @@ class Oracle_cx_oracle(OracleDialect):
def do_recover_twophase(self, connection):
pass
-dialect = Oracle_cx_oracle
+dialect = OracleDialect_cx_oracle
diff --git a/lib/sqlalchemy/dialects/oracle/zxjdbc.py b/lib/sqlalchemy/dialects/oracle/zxjdbc.py
index fba16288a..d742654a0 100644
--- a/lib/sqlalchemy/dialects/oracle/zxjdbc.py
+++ b/lib/sqlalchemy/dialects/oracle/zxjdbc.py
@@ -50,20 +50,20 @@ class _ZxJDBCNumeric(sqltypes.Numeric):
return process
-class Oracle_zxjdbcCompiler(OracleCompiler):
+class OracleCompiler_zxjdbc(OracleCompiler):
def returning_clause(self, stmt, returning_cols):
- columnlist = list(expression._select_iterables(returning_cols))
+ self.returning_cols = list(expression._select_iterables(returning_cols))
# within_columns_clause=False so that labels (foo AS bar) don't render
columns = [self.process(c, within_columns_clause=False, result_map=self.result_map)
- for c in columnlist]
+ for c in self.returning_cols]
if not hasattr(self, 'returning_parameters'):
self.returning_parameters = []
binds = []
- for i, col in enumerate(columnlist):
+ for i, col in enumerate(self.returning_cols):
dbtype = col.type.dialect_impl(self.dialect).get_dbapi_type(self.dialect.dbapi)
self.returning_parameters.append((i + 1, dbtype))
@@ -74,7 +74,7 @@ class Oracle_zxjdbcCompiler(OracleCompiler):
return 'RETURNING ' + ', '.join(columns) + " INTO " + ", ".join(binds)
-class Oracle_zxjdbcExecutionContext(OracleExecutionContext):
+class OracleExecutionContext_zxjdbc(OracleExecutionContext):
def pre_exec(self):
if hasattr(self.compiled, 'returning_parameters'):
@@ -123,10 +123,8 @@ class ReturningResultProxy(base.FullyBufferedResultProxy):
super(ReturningResultProxy, self).__init__(context)
def _cursor_description(self):
- returning = self.context.compiled.returning
-
ret = []
- for c in returning:
+ for c in self.context.compiled.returning_cols:
if hasattr(c, 'name'):
ret.append((c.name, c.type))
else:
@@ -163,12 +161,12 @@ class ReturningParam(object):
self.type)
-class Oracle_zxjdbc(ZxJDBCConnector, OracleDialect):
+class OracleDialect_zxjdbc(ZxJDBCConnector, OracleDialect):
jdbc_db_name = 'oracle'
jdbc_driver_name = 'oracle.jdbc.OracleDriver'
- statement_compiler = Oracle_zxjdbcCompiler
- execution_ctx_cls = Oracle_zxjdbcExecutionContext
+ statement_compiler = OracleCompiler_zxjdbc
+ execution_ctx_cls = OracleExecutionContext_zxjdbc
colspecs = util.update_copy(
OracleDialect.colspecs,
@@ -179,7 +177,7 @@ class Oracle_zxjdbc(ZxJDBCConnector, OracleDialect):
)
def __init__(self, *args, **kwargs):
- super(Oracle_zxjdbc, self).__init__(*args, **kwargs)
+ super(OracleDialect_zxjdbc, self).__init__(*args, **kwargs)
global SQLException, zxJDBC
from java.sql import SQLException
from com.ziclix.python.sql import zxJDBC
@@ -198,7 +196,7 @@ class Oracle_zxjdbc(ZxJDBCConnector, OracleDialect):
self.DataHandler = OracleReturningDataHandler
def initialize(self, connection):
- super(Oracle_zxjdbc, self).initialize(connection)
+ super(OracleDialect_zxjdbc, self).initialize(connection)
self.implicit_returning = connection.connection.driverversion >= '10.2'
def _create_jdbc_url(self, url):
@@ -208,4 +206,4 @@ class Oracle_zxjdbc(ZxJDBCConnector, OracleDialect):
version = re.search(r'Release ([\d\.]+)', connection.connection.dbversion).group(1)
return tuple(int(x) for x in version.split('.'))
-dialect = Oracle_zxjdbc
+dialect = OracleDialect_zxjdbc
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index 7d4cbbbd8..cbd92ccfe 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -600,21 +600,19 @@ class PGDialect(default.DefaultDialect):
if not self.supports_native_enum:
self.colspecs = self.colspecs.copy()
del self.colspecs[ENUM]
-
- def visit_pool(self, pool):
- if self.isolation_level is not None:
- class SetIsolationLevel(object):
- def __init__(self, isolation_level):
- self.isolation_level = isolation_level
-
- def connect(self, conn, rec):
- cursor = conn.cursor()
- cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL %s"
- % self.isolation_level)
- cursor.execute("COMMIT")
- cursor.close()
- pool.add_listener(SetIsolationLevel(self.isolation_level))
+ def on_connect(self):
+ if self.isolation_level is not None:
+ def connect(conn):
+ cursor = conn.cursor()
+ cursor.execute("SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL %s"
+ % self.isolation_level)
+ cursor.execute("COMMIT")
+ cursor.close()
+ return connect
+ else:
+ return None
+
def do_begin_twophase(self, connection, xid):
self.do_begin(connection.connection)
diff --git a/lib/sqlalchemy/dialects/postgresql/pg8000.py b/lib/sqlalchemy/dialects/postgresql/pg8000.py
index 079b05530..9824ab104 100644
--- a/lib/sqlalchemy/dialects/postgresql/pg8000.py
+++ b/lib/sqlalchemy/dialects/postgresql/pg8000.py
@@ -50,11 +50,11 @@ class _PGNumeric(sqltypes.Numeric):
else:
raise exc.InvalidRequestError("Unknown PG numeric type: %d" % coltype)
-class PostgreSQL_pg8000ExecutionContext(PGExecutionContext):
+class PGExecutionContext_pg8000(PGExecutionContext):
pass
-class PostgreSQL_pg8000Compiler(PGCompiler):
+class PGCompiler_pg8000(PGCompiler):
def visit_mod(self, binary, **kw):
return self.process(binary.left) + " %% " + self.process(binary.right)
@@ -65,13 +65,13 @@ class PostgreSQL_pg8000Compiler(PGCompiler):
return text.replace('%', '%%')
-class PostgreSQL_pg8000IdentifierPreparer(PGIdentifierPreparer):
+class PGIdentifierPreparer_pg8000(PGIdentifierPreparer):
def _escape_identifier(self, value):
value = value.replace(self.escape_quote, self.escape_to_quote)
return value.replace('%', '%%')
-class PostgreSQL_pg8000(PGDialect):
+class PGDialect_pg8000(PGDialect):
driver = 'pg8000'
supports_unicode_statements = True
@@ -80,9 +80,9 @@ class PostgreSQL_pg8000(PGDialect):
default_paramstyle = 'format'
supports_sane_multi_rowcount = False
- execution_ctx_cls = PostgreSQL_pg8000ExecutionContext
- statement_compiler = PostgreSQL_pg8000Compiler
- preparer = PostgreSQL_pg8000IdentifierPreparer
+ execution_ctx_cls = PGExecutionContext_pg8000
+ statement_compiler = PGCompiler_pg8000
+ preparer = PGIdentifierPreparer_pg8000
colspecs = util.update_copy(
PGDialect.colspecs,
@@ -105,4 +105,4 @@ class PostgreSQL_pg8000(PGDialect):
def is_disconnect(self, e):
return "connection is closed" in str(e)
-dialect = PostgreSQL_pg8000
+dialect = PGDialect_pg8000
diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
index 712124288..c239a3ee0 100644
--- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py
+++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py
@@ -102,7 +102,7 @@ SERVER_SIDE_CURSOR_RE = re.compile(
r'\s*SELECT',
re.I | re.UNICODE)
-class PostgreSQL_psycopg2ExecutionContext(PGExecutionContext):
+class PGExecutionContext_psycopg2(PGExecutionContext):
def create_cursor(self):
# TODO: coverage for server side cursors + select.for_update()
@@ -136,7 +136,7 @@ class PostgreSQL_psycopg2ExecutionContext(PGExecutionContext):
return base.ResultProxy(self)
-class PostgreSQL_psycopg2Compiler(PGCompiler):
+class PGCompiler_psycopg2(PGCompiler):
def visit_mod(self, binary, **kw):
return self.process(binary.left) + " %% " + self.process(binary.right)
@@ -144,19 +144,19 @@ class PostgreSQL_psycopg2Compiler(PGCompiler):
return text.replace('%', '%%')
-class PostgreSQL_psycopg2IdentifierPreparer(PGIdentifierPreparer):
+class PGIdentifierPreparer_psycopg2(PGIdentifierPreparer):
def _escape_identifier(self, value):
value = value.replace(self.escape_quote, self.escape_to_quote)
return value.replace('%', '%%')
-class PostgreSQL_psycopg2(PGDialect):
+class PGDialect_psycopg2(PGDialect):
driver = 'psycopg2'
supports_unicode_statements = False
default_paramstyle = 'pyformat'
supports_sane_multi_rowcount = False
- execution_ctx_cls = PostgreSQL_psycopg2ExecutionContext
- statement_compiler = PostgreSQL_psycopg2Compiler
- preparer = PostgreSQL_psycopg2IdentifierPreparer
+ execution_ctx_cls = PGExecutionContext_psycopg2
+ statement_compiler = PGCompiler_psycopg2
+ preparer = PGIdentifierPreparer_psycopg2
colspecs = util.update_copy(
PGDialect.colspecs,
@@ -179,20 +179,18 @@ class PostgreSQL_psycopg2(PGDialect):
psycopg = __import__('psycopg2')
return psycopg
- _unwrap_connection = None
-
- def visit_pool(self, pool):
+ def on_connect(self):
+ base_on_connect = super(PGDialect_psycopg2, self).on_connect()
if self.dbapi and self.use_native_unicode:
extensions = __import__('psycopg2.extensions').extensions
- def connect(conn, rec):
- if self._unwrap_connection:
- conn = self._unwrap_connection(conn)
- if conn is None:
- return
+ def connect(conn):
extensions.register_type(extensions.UNICODE, conn)
- pool.add_listener({'first_connect': connect, 'connect':connect})
- super(PostgreSQL_psycopg2, self).visit_pool(pool)
-
+ if base_on_connect:
+ base_on_connect(conn)
+ return connect
+ else:
+ return base_on_connect
+
def create_connect_args(self, url):
opts = url.translate_connect_args(username='user')
if 'port' in opts:
@@ -211,5 +209,5 @@ class PostgreSQL_psycopg2(PGDialect):
else:
return False
-dialect = PostgreSQL_psycopg2
+dialect = PGDialect_psycopg2
diff --git a/lib/sqlalchemy/dialects/postgresql/pypostgresql.py b/lib/sqlalchemy/dialects/postgresql/pypostgresql.py
index 87582a6cd..2e7ea201c 100644
--- a/lib/sqlalchemy/dialects/postgresql/pypostgresql.py
+++ b/lib/sqlalchemy/dialects/postgresql/pypostgresql.py
@@ -24,10 +24,10 @@ class PGNumeric(sqltypes.Numeric):
else:
return processors.to_float
-class PostgreSQL_pypostgresqlExecutionContext(PGExecutionContext):
+class PGExecutionContext_pypostgresql(PGExecutionContext):
pass
-class PostgreSQL_pypostgresql(PGDialect):
+class PGDialect_pypostgresql(PGDialect):
driver = 'pypostgresql'
supports_unicode_statements = True
@@ -40,7 +40,7 @@ class PostgreSQL_pypostgresql(PGDialect):
supports_sane_rowcount = True
supports_sane_multi_rowcount = False
- execution_ctx_cls = PostgreSQL_pypostgresqlExecutionContext
+ execution_ctx_cls = PGExecutionContext_pypostgresql
colspecs = util.update_copy(
PGDialect.colspecs,
{
@@ -66,4 +66,4 @@ class PostgreSQL_pypostgresql(PGDialect):
def is_disconnect(self, e):
return "connection is closed" in str(e)
-dialect = PostgreSQL_pypostgresql
+dialect = PGDialect_pypostgresql
diff --git a/lib/sqlalchemy/dialects/postgresql/zxjdbc.py b/lib/sqlalchemy/dialects/postgresql/zxjdbc.py
index 02cce4504..a886901c8 100644
--- a/lib/sqlalchemy/dialects/postgresql/zxjdbc.py
+++ b/lib/sqlalchemy/dialects/postgresql/zxjdbc.py
@@ -9,11 +9,11 @@ The official Postgresql JDBC driver is at http://jdbc.postgresql.org/.
from sqlalchemy.connectors.zxJDBC import ZxJDBCConnector
from sqlalchemy.dialects.postgresql.base import PGDialect
-class PostgreSQL_zxjdbc(ZxJDBCConnector, PGDialect):
+class PGDialect_zxjdbc(ZxJDBCConnector, PGDialect):
jdbc_db_name = 'postgresql'
jdbc_driver_name = 'org.postgresql.Driver'
def _get_server_version_info(self, connection):
return tuple(int(x) for x in connection.connection.dbversion.split('.'))
-dialect = PostgreSQL_zxjdbc
+dialect = PGDialect_zxjdbc
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py
index 5bcf90151..dfc09f025 100644
--- a/lib/sqlalchemy/dialects/sqlite/base.py
+++ b/lib/sqlalchemy/dialects/sqlite/base.py
@@ -360,21 +360,21 @@ class SQLiteDialect(default.DefaultDialect):
# hypothetical driver ?)
self.native_datetime = native_datetime
- def visit_pool(self, pool):
+ def on_connect(self):
if self.isolation_level is not None:
- class SetIsolationLevel(object):
- def __init__(self, isolation_level):
- if isolation_level == 'READ UNCOMMITTED':
- self.isolation_level = 1
- else:
- self.isolation_level = 0
-
- def connect(self, conn, rec):
- cursor = conn.cursor()
- cursor.execute("PRAGMA read_uncommitted = %d" % self.isolation_level)
- cursor.close()
- pool.add_listener(SetIsolationLevel(self.isolation_level))
-
+ if self.isolation_level == 'READ UNCOMMITTED':
+ isolation_level = 1
+ else:
+ isolation_level = 0
+
+ def connect(conn):
+ cursor = conn.cursor()
+ cursor.execute("PRAGMA read_uncommitted = %d" % isolation_level)
+ cursor.close()
+ return connect
+ else:
+ return None
+
def table_names(self, connection, schema):
if schema is not None:
qschema = self.identifier_preparer.quote_identifier(schema)
diff --git a/lib/sqlalchemy/dialects/sqlite/pysqlite.py b/lib/sqlalchemy/dialects/sqlite/pysqlite.py
index 3a9cdcda3..b48abbb7d 100644
--- a/lib/sqlalchemy/dialects/sqlite/pysqlite.py
+++ b/lib/sqlalchemy/dialects/sqlite/pysqlite.py
@@ -168,7 +168,7 @@ class _SQLite_pysqliteDate(DATE):
else:
return DATE.result_processor(self, dialect, coltype)
-class SQLite_pysqlite(SQLiteDialect):
+class SQLiteDialect_pysqlite(SQLiteDialect):
default_paramstyle = 'qmark'
poolclass = pool.SingletonThreadPool
@@ -238,4 +238,4 @@ class SQLite_pysqlite(SQLiteDialect):
def is_disconnect(self, e):
return isinstance(e, self.dbapi.ProgrammingError) and "Cannot operate on a closed database." in str(e)
-dialect = SQLite_pysqlite
+dialect = SQLiteDialect_pysqlite
diff --git a/lib/sqlalchemy/dialects/sybase/__init__.py b/lib/sqlalchemy/dialects/sybase/__init__.py
index f8baf339e..573aedde3 100644
--- a/lib/sqlalchemy/dialects/sybase/__init__.py
+++ b/lib/sqlalchemy/dialects/sybase/__init__.py
@@ -1,4 +1,20 @@
-from sqlalchemy.dialects.sybase import base, pyodbc
+from sqlalchemy.dialects.sybase import base, pysybase
+
+
+from base import CHAR, VARCHAR, TIME, NCHAR, NVARCHAR,\
+ TEXT,DATE,DATETIME, FLOAT, NUMERIC,\
+ BIGINT,INT, INTEGER, SMALLINT, BINARY,\
+ VARBINARY,UNITEXT,UNICHAR,UNIVARCHAR,\
+ IMAGE,BIT,MONEY,SMALLMONEY,TINYINT
# default dialect
-base.dialect = pyodbc.dialect \ No newline at end of file
+base.dialect = pysybase.dialect
+
+__all__ = (
+ 'CHAR', 'VARCHAR', 'TIME', 'NCHAR', 'NVARCHAR',
+ 'TEXT','DATE','DATETIME', 'FLOAT', 'NUMERIC',
+ 'BIGINT','INT', 'INTEGER', 'SMALLINT', 'BINARY',
+ 'VARBINARY','UNITEXT','UNICHAR','UNIVARCHAR',
+ 'IMAGE','BIT','MONEY','SMALLMONEY','TINYINT',
+ 'dialect'
+)
diff --git a/lib/sqlalchemy/dialects/sybase/base.py b/lib/sqlalchemy/dialects/sybase/base.py
index 886a773d8..b3ac45558 100644
--- a/lib/sqlalchemy/dialects/sybase/base.py
+++ b/lib/sqlalchemy/dialects/sybase/base.py
@@ -5,39 +5,25 @@
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
-"""Support for the Sybase iAnywhere database.
+"""Support for Sybase Adaptive Server Enterprise (ASE).
-This is not (yet) a full backend for Sybase ASE.
+Note that this dialect is no longer specific to Sybase iAnywhere.
+ASE is the primary support platform.
-This dialect is *not* ported to SQLAlchemy 0.6.
-
-This dialect is *not* tested on SQLAlchemy 0.6.
-
-
-Known issues / TODO:
-
- * Uses the mx.ODBC driver from egenix (version 2.1.0)
- * The current version of sqlalchemy.databases.sybase only supports
- mx.ODBC.Windows (other platforms such as mx.ODBC.unixODBC still need
- some development)
- * Support for pyodbc has been built in but is not yet complete (needs
- further development)
- * Results of running tests/alltests.py:
- Ran 934 tests in 287.032s
- FAILED (failures=3, errors=1)
- * Tested on 'Adaptive Server Anywhere 9' (version 9.0.1.1751)
"""
-import datetime, operator
-
-from sqlalchemy import util, sql, schema, exc
-from sqlalchemy.sql import compiler, expression
-from sqlalchemy.engine import default, base
+import operator
+from sqlalchemy.sql import compiler, expression, text, bindparam
+from sqlalchemy.engine import default, base, reflection
from sqlalchemy import types as sqltypes
from sqlalchemy.sql import operators as sql_operators
-from sqlalchemy import MetaData, Table, Column
-from sqlalchemy import String, Integer, SMALLINT, CHAR, ForeignKey
-from sqlalchemy.dialects.sybase.schema import *
+from sqlalchemy import schema as sa_schema
+from sqlalchemy import util, sql, exc
+
+from sqlalchemy.types import CHAR, VARCHAR, TIME, NCHAR, NVARCHAR,\
+ TEXT,DATE,DATETIME, FLOAT, NUMERIC,\
+ BIGINT,INT, INTEGER, SMALLINT, BINARY,\
+ VARBINARY, DECIMAL, TIMESTAMP, Unicode
RESERVED_WORDS = set([
"add", "all", "alter", "and",
@@ -99,23 +85,33 @@ RESERVED_WORDS = set([
])
-class SybaseImage(sqltypes.LargeBinary):
- __visit_name__ = 'IMAGE'
+class UNICHAR(sqltypes.Unicode):
+ __visit_name__ = 'UNICHAR'
+
+class UNIVARCHAR(sqltypes.Unicode):
+ __visit_name__ = 'UNIVARCHAR'
+
+class UNITEXT(sqltypes.UnicodeText):
+ __visit_name__ = 'UNITEXT'
+
+class TINYINT(sqltypes.Integer):
+ __visit_name__ = 'TINYINT'
-class SybaseBit(sqltypes.TypeEngine):
+class BIT(sqltypes.TypeEngine):
__visit_name__ = 'BIT'
-class SybaseMoney(sqltypes.TypeEngine):
+class MONEY(sqltypes.TypeEngine):
__visit_name__ = "MONEY"
-class SybaseSmallMoney(SybaseMoney):
+class SMALLMONEY(sqltypes.TypeEngine):
__visit_name__ = "SMALLMONEY"
-class SybaseUniqueIdentifier(sqltypes.TypeEngine):
+class UNIQUEIDENTIFIER(sqltypes.TypeEngine):
__visit_name__ = "UNIQUEIDENTIFIER"
-
-class SybaseBoolean(sqltypes.Boolean):
- pass
+
+class IMAGE(sqltypes.LargeBinary):
+ __visit_name__ = 'IMAGE'
+
class SybaseTypeCompiler(compiler.GenericTypeCompiler):
def visit_large_binary(self, type_):
@@ -123,6 +119,15 @@ class SybaseTypeCompiler(compiler.GenericTypeCompiler):
def visit_boolean(self, type_):
return self.visit_BIT(type_)
+
+ def visit_UNICHAR(self, type_):
+ return "UNICHAR(%d)" % type_.length
+
+ def visit_UNITEXT(self, type_):
+ return "UNITEXT"
+
+ def visit_TINYINT(self, type_):
+ return "TINYINT"
def visit_IMAGE(self, type_):
return "IMAGE"
@@ -140,57 +145,68 @@ class SybaseTypeCompiler(compiler.GenericTypeCompiler):
return "UNIQUEIDENTIFIER"
colspecs = {
- sqltypes.LargeBinary : SybaseImage,
- sqltypes.Boolean : SybaseBoolean,
}
ischema_names = {
- 'integer' : sqltypes.INTEGER,
- 'unsigned int' : sqltypes.Integer,
- 'unsigned smallint' : sqltypes.SmallInteger,
- 'unsigned bigint' : sqltypes.BigInteger,
- 'bigint': sqltypes.BIGINT,
- 'smallint' : sqltypes.SMALLINT,
- 'tinyint' : sqltypes.SmallInteger,
- 'varchar' : sqltypes.VARCHAR,
- 'long varchar' : sqltypes.Text,
- 'char' : sqltypes.CHAR,
- 'decimal' : sqltypes.DECIMAL,
- 'numeric' : sqltypes.NUMERIC,
- 'float' : sqltypes.FLOAT,
- 'double' : sqltypes.Numeric,
- 'binary' : sqltypes.LargeBinary,
- 'long binary' : sqltypes.LargeBinary,
- 'varbinary' : sqltypes.LargeBinary,
- 'bit': SybaseBit,
- 'image' : SybaseImage,
- 'timestamp': sqltypes.TIMESTAMP,
- 'money': SybaseMoney,
- 'smallmoney': SybaseSmallMoney,
- 'uniqueidentifier': SybaseUniqueIdentifier,
+ 'integer' : INTEGER,
+ 'unsigned int' : INTEGER, # TODO: unsigned flags
+ 'unsigned smallint' : SMALLINT, # TODO: unsigned flags
+ 'unsigned bigint' : BIGINT, # TODO: unsigned flags
+ 'bigint': BIGINT,
+ 'smallint' : SMALLINT,
+ 'tinyint' : TINYINT,
+ 'varchar' : VARCHAR,
+ 'long varchar' : TEXT, # TODO
+ 'char' : CHAR,
+ 'decimal' : DECIMAL,
+ 'numeric' : NUMERIC,
+ 'float' : FLOAT,
+ 'double' : NUMERIC, # TODO
+ 'binary' : BINARY,
+ 'varbinary' : VARBINARY,
+ 'bit': BIT,
+ 'image' : IMAGE,
+ 'timestamp': TIMESTAMP,
+ 'money': MONEY,
+ 'smallmoney': MONEY,
+ 'uniqueidentifier': UNIQUEIDENTIFIER,
}
class SybaseExecutionContext(default.DefaultExecutionContext):
+ _enable_identity_insert = False
+
+ def pre_exec(self):
+ if self.isinsert:
+ tbl = self.compiled.statement.table
+ seq_column = tbl._autoincrement_column
+ insert_has_sequence = seq_column is not None
+
+ if insert_has_sequence:
+ self._enable_identity_insert = seq_column.key in self.compiled_parameters[0]
+ else:
+ self._enable_identity_insert = False
+
+ if self._enable_identity_insert:
+ self.cursor.execute("SET IDENTITY_INSERT %s ON" %
+ self.dialect.identifier_preparer.format_table(tbl))
def post_exec(self):
- if self.compiled.isinsert:
- table = self.compiled.statement.table
- # get the inserted values of the primary key
-
- # get any sequence IDs first (using @@identity)
- self.cursor.execute("SELECT @@identity AS lastrowid")
- row = self.cursor.fetchone()
- lastrowid = int(row[0])
- if lastrowid > 0:
- # an IDENTITY was inserted, fetch it
- # FIXME: always insert in front ? This only works if the IDENTITY is the first column, no ?!
- if not hasattr(self, '_last_inserted_ids') or self._last_inserted_ids is None:
- self._last_inserted_ids = [lastrowid]
- else:
- self._last_inserted_ids = [lastrowid] + self._last_inserted_ids[1:]
-
+
+ if self._enable_identity_insert:
+ self.cursor.execute(
+ "SET IDENTITY_INSERT %s OFF" %
+ self.dialect.identifier_preparer.
+ format_table(self.compiled.statement.table)
+ )
+
+ def get_lastrowid(self):
+ cursor = self.create_cursor()
+ cursor.execute("SELECT @@identity AS lastrowid")
+ lastrowid = cursor.fetchone()[0]
+ cursor.close()
+ return lastrowid
class SybaseSQLCompiler(compiler.SQLCompiler):
@@ -204,12 +220,6 @@ class SybaseSQLCompiler(compiler.SQLCompiler):
def visit_mod(self, binary, **kw):
return "MOD(%s, %s)" % (self.process(binary.left), self.process(binary.right))
- def bindparam_string(self, name):
- res = super(SybaseSQLCompiler, self).bindparam_string(name)
- if name.lower().startswith('literal'):
- res = 'STRING(%s)' % res
- return res
-
def get_select_precolumns(self, select):
s = select._distinct and "DISTINCT " or ""
if select._limit:
@@ -230,32 +240,22 @@ class SybaseSQLCompiler(compiler.SQLCompiler):
# Limit in sybase is after the select keyword
return ""
- def visit_binary(self, binary):
+ def dont_visit_binary(self, binary):
"""Move bind parameters to the right-hand side of an operator, where possible."""
if isinstance(binary.left, expression._BindParamClause) and binary.operator == operator.eq:
return self.process(expression._BinaryExpression(binary.right, binary.left, binary.operator))
else:
return super(SybaseSQLCompiler, self).visit_binary(binary)
- def label_select_column(self, select, column, asfrom):
+ def dont_label_select_column(self, select, column, asfrom):
if isinstance(column, expression.Function):
return column.label(None)
else:
return super(SybaseSQLCompiler, self).label_select_column(select, column, asfrom)
- function_rewrites = {'current_date': 'getdate',
- }
- def visit_function(self, func):
- func.name = self.function_rewrites.get(func.name, func.name)
- res = super(SybaseSQLCompiler, self).visit_function(func)
- if func.name.lower() == 'getdate':
- # apply CAST operator
- # FIXME: what about _pyodbc ?
- cast = expression._Cast(func, SybaseDate_mxodbc)
- # infinite recursion
- # res = self.visit_cast(cast)
- res = "CAST(%s AS %s)" % (res, self.process(cast.typeclause))
- return res
+# def visit_getdate_func(self, fn, **kw):
+ # TODO: need to cast? something ?
+# pass
def visit_extract(self, extract):
field = self.extract_map.get(extract.field, extract.field)
@@ -277,27 +277,38 @@ class SybaseSQLCompiler(compiler.SQLCompiler):
class SybaseDDLCompiler(compiler.DDLCompiler):
def get_column_specification(self, column, **kwargs):
+ colspec = self.preparer.format_column(column) + " " + \
+ self.dialect.type_compiler.process(column.type)
- colspec = self.preparer.format_column(column)
+ if column.table is None:
+ raise exc.InvalidRequestError("The Sybase dialect requires Table-bound "\
+ "columns in order to generate DDL")
+ seq_col = column.table._autoincrement_column
- if (not getattr(column.table, 'has_sequence', False)) and column.primary_key and \
- column.autoincrement and isinstance(column.type, sqltypes.Integer):
- if column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional):
- column.sequence = schema.Sequence(column.name + '_seq')
+
- if hasattr(column, 'sequence'):
- column.table.has_sequence = column
- #colspec += " numeric(30,0) IDENTITY"
- colspec += " Integer IDENTITY"
+ # install a IDENTITY Sequence if we have an implicit IDENTITY column
+ if seq_col is column:
+ sequence = isinstance(column.default, sa_schema.Sequence) and column.default
+ if sequence:
+ start, increment = sequence.start or 1, sequence.increment or 1
+ else:
+ start, increment = 1, 1
+ if (start, increment) == (1, 1):
+ colspec += " IDENTITY"
+ else:
+ # TODO: need correct syntax for this
+ colspec += " IDENTITY(%s,%s)" % (start, increment)
else:
- colspec += " " + self.dialect.type_compiler.process(column.type)
-
- if not column.nullable:
- colspec += " NOT NULL"
+ if column.nullable is not None:
+ if not column.nullable or column.primary_key:
+ colspec += " NOT NULL"
+ else:
+ colspec += " NULL"
- default = self.get_column_default_string(column)
- if default is not None:
- colspec += " DEFAULT " + default
+ default = self.get_column_default_string(column)
+ if default is not None:
+ colspec += " DEFAULT " + default
return colspec
@@ -316,6 +327,11 @@ class SybaseDialect(default.DefaultDialect):
supports_unicode_statements = False
supports_sane_rowcount = False
supports_sane_multi_rowcount = False
+
+ supports_native_boolean = False
+ supports_unicode_binds = False
+ postfetch_lastrowid = True
+
colspecs = colspecs
ischema_names = ischema_names
@@ -324,120 +340,47 @@ class SybaseDialect(default.DefaultDialect):
ddl_compiler = SybaseDDLCompiler
preparer = SybaseIdentifierPreparer
- ported_sqla_06 = False
-
- schema_name = "dba"
-
- def __init__(self, **params):
- super(SybaseDialect, self).__init__(**params)
- self.text_as_varchar = False
-
- def last_inserted_ids(self):
- return self.context.last_inserted_ids
-
def _get_default_schema_name(self, connection):
- # TODO
- return self.schema_name
+ return connection.scalar(
+ text("SELECT user_name() as user_name", typemap={'user_name':Unicode})
+ )
+
+ @reflection.cache
+ def get_table_names(self, connection, schema=None, **kw):
+ if schema is None:
+ schema = self.default_schema_name
+ return self.table_names(connection, schema)
def table_names(self, connection, schema):
- """Ignore the schema and the charset for now."""
- s = sql.select([tables.c.table_name],
- sql.not_(tables.c.table_name.like("SYS%")) and
- tables.c.creator >= 100
- )
- rp = connection.execute(s)
- return [row[0] for row in rp.fetchall()]
+
+ result = connection.execute(
+ text("select sysobjects.name from sysobjects, sysusers "
+ "where sysobjects.uid=sysusers.uid and "
+ "sysusers.name=:schemaname and "
+ "sysobjects.type='U'",
+ bindparams=[
+ bindparam('schemaname', schema)
+ ])
+ )
+ return [r[0] for r in result]
def has_table(self, connection, tablename, schema=None):
- # FIXME: ignore schemas for sybase
- s = sql.select([tables.c.table_name], tables.c.table_name == tablename)
- return connection.execute(s).first() is not None
+ if schema is None:
+ schema = self.default_schema_name
+
+ result = connection.execute(
+ text("select sysobjects.name from sysobjects, sysusers "
+ "where sysobjects.uid=sysusers.uid and "
+ "sysobjects.name=:tablename and "
+ "sysusers.name=:schemaname and "
+ "sysobjects.type='U'",
+ bindparams=[
+ bindparam('tablename', tablename),
+ bindparam('schemaname', schema)
+ ])
+ )
+ return result.scalar() is not None
def reflecttable(self, connection, table, include_columns):
- # Get base columns
- if table.schema is not None:
- current_schema = table.schema
- else:
- current_schema = self.default_schema_name
-
- s = sql.select([columns, domains], tables.c.table_name==table.name, from_obj=[columns.join(tables).join(domains)], order_by=[columns.c.column_id])
-
- c = connection.execute(s)
- found_table = False
- # makes sure we append the columns in the correct order
- while True:
- row = c.fetchone()
- if row is None:
- break
- found_table = True
- (name, type, nullable, charlen, numericprec, numericscale, default, primary_key, max_identity, table_id, column_id) = (
- row[columns.c.column_name],
- row[domains.c.domain_name],
- row[columns.c.nulls] == 'Y',
- row[columns.c.width],
- row[domains.c.precision],
- row[columns.c.scale],
- row[columns.c.default],
- row[columns.c.pkey] == 'Y',
- row[columns.c.max_identity],
- row[tables.c.table_id],
- row[columns.c.column_id],
- )
- if include_columns and name not in include_columns:
- continue
-
- # FIXME: else problems with SybaseBinary(size)
- if numericscale == 0:
- numericscale = None
-
- args = []
- for a in (charlen, numericprec, numericscale):
- if a is not None:
- args.append(a)
- coltype = self.ischema_names.get(type, None)
- if coltype == SybaseString and charlen == -1:
- coltype = SybaseText()
- else:
- if coltype is None:
- util.warn("Did not recognize type '%s' of column '%s'" %
- (type, name))
- coltype = sqltypes.NULLTYPE
- coltype = coltype(*args)
- colargs = []
- if default is not None:
- colargs.append(schema.DefaultClause(sql.text(default)))
-
- # any sequences ?
- col = schema.Column(name, coltype, nullable=nullable, primary_key=primary_key, *colargs)
- if int(max_identity) > 0:
- col.sequence = schema.Sequence(name + '_identity')
- col.sequence.start = int(max_identity)
- col.sequence.increment = 1
-
- # append the column
- table.append_column(col)
-
- # any foreign key constraint for this table ?
- # note: no multi-column foreign keys are considered
- s = "select st1.table_name, sc1.column_name, st2.table_name, sc2.column_name from systable as st1 join sysfkcol on st1.table_id=sysfkcol.foreign_table_id join sysforeignkey join systable as st2 on sysforeignkey.primary_table_id = st2.table_id join syscolumn as sc1 on sysfkcol.foreign_column_id=sc1.column_id and sc1.table_id=st1.table_id join syscolumn as sc2 on sysfkcol.primary_column_id=sc2.column_id and sc2.table_id=st2.table_id where st1.table_name='%(table_name)s';" % { 'table_name' : table.name }
- c = connection.execute(s)
- foreignKeys = {}
- while True:
- row = c.fetchone()
- if row is None:
- break
- (foreign_table, foreign_column, primary_table, primary_column) = (
- row[0], row[1], row[2], row[3],
- )
- if not primary_table in foreignKeys.keys():
- foreignKeys[primary_table] = [['%s' % (foreign_column)], ['%s.%s'%(primary_table, primary_column)]]
- else:
- foreignKeys[primary_table][0].append('%s'%(foreign_column))
- foreignKeys[primary_table][1].append('%s.%s'%(primary_table, primary_column))
- for primary_table in foreignKeys.iterkeys():
- #table.append_constraint(schema.ForeignKeyConstraint(['%s.%s'%(foreign_table, foreign_column)], ['%s.%s'%(primary_table,primary_column)]))
- table.append_constraint(schema.ForeignKeyConstraint(foreignKeys[primary_table][0], foreignKeys[primary_table][1], link_to_name=True))
-
- if not found_table:
- raise exc.NoSuchTableError(table.name)
+ raise NotImplementedError()
diff --git a/lib/sqlalchemy/dialects/sybase/mxodbc.py b/lib/sqlalchemy/dialects/sybase/mxodbc.py
index 86a23d5bc..14817998f 100644
--- a/lib/sqlalchemy/dialects/sybase/mxodbc.py
+++ b/lib/sqlalchemy/dialects/sybase/mxodbc.py
@@ -1,10 +1,17 @@
+"""
+Support for Sybase via mxodbc.
+
+This dialect is a stub only and is likely non functional at this time.
+
+
+"""
from sqlalchemy.dialects.sybase.base import SybaseDialect, SybaseExecutionContext
from sqlalchemy.connectors.mxodbc import MxODBCConnector
class SybaseExecutionContext_mxodbc(SybaseExecutionContext):
pass
-class Sybase_mxodbc(MxODBCConnector, SybaseDialect):
+class SybaseDialect_mxodbc(MxODBCConnector, SybaseDialect):
execution_ctx_cls = SybaseExecutionContext_mxodbc
-dialect = Sybase_mxodbc \ No newline at end of file
+dialect = SybaseDialect_mxodbc
diff --git a/lib/sqlalchemy/dialects/sybase/pyodbc.py b/lib/sqlalchemy/dialects/sybase/pyodbc.py
index 61c6f3292..642ae3219 100644
--- a/lib/sqlalchemy/dialects/sybase/pyodbc.py
+++ b/lib/sqlalchemy/dialects/sybase/pyodbc.py
@@ -1,3 +1,11 @@
+"""
+Support for Sybase via pyodbc.
+
+This dialect is a stub only and is likely non functional at this time.
+
+
+"""
+
from sqlalchemy.dialects.sybase.base import SybaseDialect, SybaseExecutionContext
from sqlalchemy.connectors.pyodbc import PyODBCConnector
@@ -5,7 +13,7 @@ class SybaseExecutionContext_pyodbc(SybaseExecutionContext):
pass
-class Sybase_pyodbc(PyODBCConnector, SybaseDialect):
+class SybaseDialect_pyodbc(PyODBCConnector, SybaseDialect):
execution_ctx_cls = SybaseExecutionContext_pyodbc
-dialect = Sybase_pyodbc \ No newline at end of file
+dialect = SybaseDialect_pyodbc
diff --git a/lib/sqlalchemy/dialects/sybase/pysybase.py b/lib/sqlalchemy/dialects/sybase/pysybase.py
new file mode 100644
index 000000000..195407384
--- /dev/null
+++ b/lib/sqlalchemy/dialects/sybase/pysybase.py
@@ -0,0 +1,81 @@
+# pysybase.py
+# Copyright (C) 2010 Michael Bayer mike_mp@zzzcomputing.com
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+"""
+Support for Sybase via the python-sybase driver.
+
+http://python-sybase.sourceforge.net/
+
+Connect strings are of the form::
+
+ sybase+pysybase://<username>:<password>@<dsn>/[database name]
+
+"""
+
+from sqlalchemy.dialects.sybase.base import SybaseDialect, \
+ SybaseExecutionContext, SybaseSQLCompiler
+
+
+class SybaseExecutionContext_pysybase(SybaseExecutionContext):
+ def pre_exec(self):
+ SybaseExecutionContext.pre_exec(self)
+
+ for param in self.parameters:
+ for key in list(param):
+ param["@" + key] = param[key]
+ del param[key]
+
+ if self.isddl:
+ # TODO: to enhance this, we can detect "ddl in tran" on the
+ # database settings. this error message should be improved to
+ # include a note about that.
+ if not self.should_autocommit:
+ raise exc.InvalidRequestError("The Sybase dialect only supports "
+ "DDL in 'autocommit' mode at this time.")
+ # call commit() on the Sybase connection directly,
+ # to avoid any side effects of calling a Connection
+ # transactional method inside of pre_exec()
+ self.root_connection.engine.logger.info("COMMIT (Assuming no Sybase 'ddl in tran')")
+ self.root_connection.connection.commit()
+
+class SybaseSQLCompiler_pysybase(SybaseSQLCompiler):
+ def bindparam_string(self, name):
+ return "@" + name
+
+class SybaseDialect_pysybase(SybaseDialect):
+ driver = 'pysybase'
+ execution_ctx_cls = SybaseExecutionContext_pysybase
+ statement_compiler = SybaseSQLCompiler_pysybase
+
+ @classmethod
+ def dbapi(cls):
+ import Sybase
+ return Sybase
+
+ def create_connect_args(self, url):
+ opts = url.translate_connect_args(username='user', password='passwd')
+
+ return ([opts.pop('host')], opts)
+
+ def do_executemany(self, cursor, statement, parameters, context=None):
+ # calling python-sybase executemany yields:
+ # TypeError: string too long for buffer
+ for param in parameters:
+ cursor.execute(statement, param)
+
+ def _get_server_version_info(self, connection):
+ return connection.scalar("select @@version_number")
+
+ def is_disconnect(self, e):
+ if isinstance(e, (self.dbapi.OperationalError, self.dbapi.ProgrammingError)):
+ msg = str(e)
+ return ('Unable to complete network request to host' in msg or
+ 'Invalid connection state' in msg or
+ 'Invalid cursor state' in msg)
+ else:
+ return False
+
+dialect = SybaseDialect_pysybase
diff --git a/lib/sqlalchemy/dialects/sybase/schema.py b/lib/sqlalchemy/dialects/sybase/schema.py
deleted file mode 100644
index 15ac6b27b..000000000
--- a/lib/sqlalchemy/dialects/sybase/schema.py
+++ /dev/null
@@ -1,51 +0,0 @@
-from sqlalchemy import *
-
-ischema = MetaData()
-
-tables = Table("SYSTABLE", ischema,
- Column("table_id", Integer, primary_key=True),
- Column("file_id", SMALLINT),
- Column("table_name", CHAR(128)),
- Column("table_type", CHAR(10)),
- Column("creator", Integer),
- #schema="information_schema"
- )
-
-domains = Table("SYSDOMAIN", ischema,
- Column("domain_id", Integer, primary_key=True),
- Column("domain_name", CHAR(128)),
- Column("type_id", SMALLINT),
- Column("precision", SMALLINT, quote=True),
- #schema="information_schema"
- )
-
-columns = Table("SYSCOLUMN", ischema,
- Column("column_id", Integer, primary_key=True),
- Column("table_id", Integer, ForeignKey(tables.c.table_id)),
- Column("pkey", CHAR(1)),
- Column("column_name", CHAR(128)),
- Column("nulls", CHAR(1)),
- Column("width", SMALLINT),
- Column("domain_id", SMALLINT, ForeignKey(domains.c.domain_id)),
- # FIXME: should be mx.BIGINT
- Column("max_identity", Integer),
- # FIXME: should be mx.ODBC.Windows.LONGVARCHAR
- Column("default", String),
- Column("scale", Integer),
- #schema="information_schema"
- )
-
-foreignkeys = Table("SYSFOREIGNKEY", ischema,
- Column("foreign_table_id", Integer, ForeignKey(tables.c.table_id), primary_key=True),
- Column("foreign_key_id", SMALLINT, primary_key=True),
- Column("primary_table_id", Integer, ForeignKey(tables.c.table_id)),
- #schema="information_schema"
- )
-fkcols = Table("SYSFKCOL", ischema,
- Column("foreign_table_id", Integer, ForeignKey(columns.c.table_id), primary_key=True),
- Column("foreign_key_id", SMALLINT, ForeignKey(foreignkeys.c.foreign_key_id), primary_key=True),
- Column("foreign_column_id", Integer, ForeignKey(columns.c.column_id), primary_key=True),
- Column("primary_column_id", Integer),
- #schema="information_schema"
- )
-
diff --git a/lib/sqlalchemy/engine/__init__.py b/lib/sqlalchemy/engine/__init__.py
index 8911485cb..9a53545df 100644
--- a/lib/sqlalchemy/engine/__init__.py
+++ b/lib/sqlalchemy/engine/__init__.py
@@ -118,7 +118,7 @@ def create_engine(*args, **kwargs):
Pool. Specific dialects also accept keyword arguments that
are unique to that dialect. Here, we describe the parameters
that are common to most ``create_engine()`` usage.
-
+
:param assert_unicode: Deprecated. A warning is raised in all cases when a non-Unicode
object is passed when SQLAlchemy would coerce into an encoding
(note: but **not** when the DBAPI handles unicode objects natively).
@@ -144,6 +144,11 @@ def create_engine(*args, **kwargs):
connections. Usage of this function causes connection
parameters specified in the URL argument to be bypassed.
+ :param logging_name: String identifier which will be used within
+ the "name" field of logging records generated within the
+ "sqlalchemy.engine" logger. Defaults to a hexstring of the
+ object's id.
+
:param echo=False: if True, the Engine will log all statements
as well as a repr() of their parameter lists to the engines
logger, which defaults to sys.stdout. The ``echo`` attribute of
@@ -153,6 +158,11 @@ def create_engine(*args, **kwargs):
controls a Python logger; see :ref:`dbengine_logging` for
information on how to configure logging directly.
+ :param pool_logging_name: String identifier which will be used within
+ the "name" field of logging records generated within the
+ "sqlalchemy.pool" logger. Defaults to a hexstring of the object's
+ id.
+
:param echo_pool=False: if True, the connection pool will log
all checkouts/checkins to the logging stream, which defaults to
sys.stdout. This flag ultimately controls a Python logger; see
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index ea6282954..095f7a960 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -169,6 +169,7 @@ class Dialect(object):
Given a :class:`~sqlalchemy.engine.url.URL` object, returns a tuple
consisting of a `*args`/`**kwargs` suitable to send directly
to the dbapi's connect function.
+
"""
raise NotImplementedError()
@@ -183,6 +184,7 @@ class Dialect(object):
The returned result is cached *per dialect class* so can
contain no dialect-instance state.
+
"""
raise NotImplementedError()
@@ -192,6 +194,13 @@ class Dialect(object):
Allows dialects to configure options based on server version info or
other properties.
+
+ The connection passed here is a SQLAlchemy Connection object,
+ with full capabilities.
+
+ The initalize() method of the base dialect should be called via
+ super().
+
"""
pass
@@ -204,6 +213,12 @@ class Dialect(object):
properties from the database. If include_columns (a list or
set) is specified, limit the autoload to the given column
names.
+
+ The default implementation uses the
+ :class:`~sqlalchemy.engine.reflection.Inspector` interface to
+ provide the output, building upon the granular table/column/
+ constraint etc. methods of :class:`Dialect`.
+
"""
raise NotImplementedError()
@@ -458,8 +473,22 @@ class Dialect(object):
raise NotImplementedError()
- def visit_pool(self, pool):
- """Executed after a pool is created."""
+ def on_connect(self):
+ """return a callable which sets up a newly created DBAPI connection.
+
+ The callable accepts a single argument "conn" which is the
+ DBAPI connection itself. It has no return value.
+
+ This is used to set dialect-wide per-connection options such as isolation
+ modes, unicode modes, etc.
+
+ If a callable is returned, it will be assembled into a pool listener
+ that receives the direct DBAPI connection, with all wrappers removed.
+
+ If None is returned, no listener will be generated.
+
+ """
+ return None
class ExecutionContext(object):
@@ -1387,17 +1416,19 @@ class TwoPhaseTransaction(Transaction):
self.connection._commit_twophase_impl(self.xid, self._is_prepared)
-class Engine(Connectable):
+class Engine(Connectable, log.Identified):
"""
Connects a :class:`~sqlalchemy.pool.Pool` and :class:`~sqlalchemy.engine.base.Dialect`
together to provide a source of database connectivity and behavior.
"""
- def __init__(self, pool, dialect, url, echo=None, proxy=None):
+ def __init__(self, pool, dialect, url, logging_name=None, echo=None, proxy=None):
self.pool = pool
self.url = url
self.dialect = dialect
+ if logging_name:
+ self.logging_name = logging_name
self.echo = echo
self.engine = self
self.logger = log.instance_logger(self, echoflag=echo)
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 077627949..ce24a9ae4 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -135,28 +135,54 @@ class DefaultDialect(base.Dialect):
self.default_schema_name = None
self.returns_unicode_strings = self._check_unicode_returns(connection)
-
+
+ self.do_rollback(connection.connection)
+
+ def on_connect(self):
+ """return a callable which sets up a newly created DBAPI connection.
+
+ This is used to set dialect-wide per-connection options such as isolation
+ modes, unicode modes, etc.
+
+ If a callable is returned, it will be assembled into a pool listener
+ that receives the direct DBAPI connection, with all wrappers removed.
+
+ If None is returned, no listener will be generated.
+
+ """
+ return None
+
def _check_unicode_returns(self, connection):
- cursor = connection.connection.cursor()
+ # Py2K
+ if self.supports_unicode_statements:
+ cast_to = unicode
+ else:
+ cast_to = str
+ # end Py2K
+ # Py3K
+ #cast_to = str
def check_unicode(type_):
- cursor.execute(
- str(
- expression.select(
- [expression.cast(
- expression.literal_column("'test unicode returns'"), type_)
- ]).compile(dialect=self)
+ cursor = connection.connection.cursor()
+ try:
+ cursor.execute(
+ cast_to(
+ expression.select(
+ [expression.cast(
+ expression.literal_column("'test unicode returns'"), type_)
+ ]).compile(dialect=self)
+ )
)
- )
-
- row = cursor.fetchone()
- return isinstance(row[0], unicode)
-
+ row = cursor.fetchone()
+
+ return isinstance(row[0], unicode)
+ finally:
+ cursor.close()
+
# detect plain VARCHAR
unicode_for_varchar = check_unicode(sqltypes.VARCHAR(60))
# detect if there's an NVARCHAR type with different behavior available
unicode_for_unicode = check_unicode(sqltypes.Unicode(60))
- cursor.close()
if unicode_for_unicode and not unicode_for_varchar:
return "conditional"
@@ -247,6 +273,7 @@ class DefaultExecutionContext(base.ExecutionContext):
isinsert = False
isupdate = False
isdelete = False
+ isddl = False
executemany = False
result_map = None
compiled = None
@@ -266,6 +293,7 @@ class DefaultExecutionContext(base.ExecutionContext):
if compiled_ddl is not None:
self.compiled = compiled = compiled_ddl
+ self.isddl = True
if compiled.statement._execution_options:
self.execution_options = compiled.statement._execution_options
diff --git a/lib/sqlalchemy/engine/strategies.py b/lib/sqlalchemy/engine/strategies.py
index 7a8856ba8..7fc39b91a 100644
--- a/lib/sqlalchemy/engine/strategies.py
+++ b/lib/sqlalchemy/engine/strategies.py
@@ -90,7 +90,8 @@ class DefaultEngineStrategy(EngineStrategy):
# consume pool arguments from kwargs, translating a few of
# the arguments
- translate = {'echo': 'echo_pool',
+ translate = {'logging_name': 'pool_logging_name',
+ 'echo': 'echo_pool',
'timeout': 'pool_timeout',
'recycle': 'pool_recycle',
'use_threadlocal':'pool_threadlocal'}
@@ -129,8 +130,16 @@ class DefaultEngineStrategy(EngineStrategy):
engine = engineclass(pool, dialect, u, **engine_args)
if _initialize:
- dialect.visit_pool(pool)
-
+ do_on_connect = dialect.on_connect()
+ if do_on_connect:
+ def on_connect(conn, rec):
+ conn = getattr(conn, '_sqla_unwrap', conn)
+ if conn is None:
+ return
+ do_on_connect(conn)
+
+ pool.add_listener({'first_connect': on_connect, 'connect':on_connect})
+
def first_connect(conn, rec):
c = base.Connection(engine, connection=conn)
dialect.initialize(c)
diff --git a/lib/sqlalchemy/log.py b/lib/sqlalchemy/log.py
index 3f861d60a..49c779fed 100644
--- a/lib/sqlalchemy/log.py
+++ b/lib/sqlalchemy/log.py
@@ -28,7 +28,7 @@ is equivalent to::
import logging
import sys
-
+from sqlalchemy import util
rootlogger = logging.getLogger('sqlalchemy')
if rootlogger.level == logging.NOTSET:
@@ -58,22 +58,28 @@ def class_logger(cls, enable=False):
cls.logger = logger
_logged_classes.add(cls)
+
+class Identified(object):
+ @util.memoized_property
+ def logging_name(self):
+ # limit the number of loggers by chopping off the hex(id).
+ # some novice users unfortunately create an unlimited number
+ # of Engines in their applications which would otherwise
+ # cause the app to run out of memory.
+ return "0x...%s" % hex(id(self))[-4:]
+
+
def instance_logger(instance, echoflag=None):
- """create a logger for an instance.
+ """create a logger for an instance that implements :class:`Identified`.
Warning: this is an expensive call which also results in a permanent
increase in memory overhead for each call. Use only for
low-volume, long-time-spanning objects.
"""
-
- # limit the number of loggers by chopping off the hex(id).
- # many novice users unfortunately create an unlimited number
- # of Engines in their applications which would otherwise
- # cause the app to run out of memory.
- name = "%s.%s.0x...%s" % (instance.__class__.__module__,
- instance.__class__.__name__,
- hex(id(instance))[-4:])
+
+ name = "%s.%s.%s" % (instance.__class__.__module__,
+ instance.__class__.__name__, instance.logging_name)
if echoflag is not None:
l = logging.getLogger(name)
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py
index 1b5bcb7fe..fde93ff1b 100644
--- a/lib/sqlalchemy/orm/query.py
+++ b/lib/sqlalchemy/orm/query.py
@@ -1537,17 +1537,23 @@ class Query(object):
only_load_props=None, passive=None):
lockmode = lockmode or self._lockmode
+ mapper = self._mapper_zero()
if not self._populate_existing and \
not refresh_state and \
- not self._mapper_zero().always_refresh and \
+ not mapper.always_refresh and \
lockmode is None:
instance = self.session.identity_map.get(key)
if instance:
+ # item present in identity map with a different class
+ if not issubclass(instance.__class__, mapper.class_):
+ return None
+
state = attributes.instance_state(instance)
+
+ # expired - ensure it still exists
if state.expired:
if passive is attributes.PASSIVE_NO_FETCH:
return attributes.PASSIVE_NO_RESULT
-
try:
state()
except orm_exc.ObjectDeletedError:
@@ -1570,8 +1576,6 @@ class Query(object):
q = self._clone()
if ident is not None:
- mapper = q._mapper_zero()
- params = {}
(_get_clause, _get_params) = mapper._get_clause
# None present in ident - turn those comparisons
@@ -1587,14 +1591,16 @@ class Query(object):
_get_clause = q._adapt_clause(_get_clause, True, False)
q._criterion = _get_clause
- for i, primary_key in enumerate(mapper.primary_key):
- try:
- params[_get_params[primary_key].key] = ident[i]
- except IndexError:
- raise sa_exc.InvalidRequestError(
- "Could not find enough values to formulate primary "
- "key for query.get(); primary key columns are %s" %
- ','.join("'%s'" % c for c in mapper.primary_key))
+ params = dict([
+ (_get_params[primary_key].key, id_val)
+ for id_val, primary_key in zip(ident, mapper.primary_key)
+ ])
+
+ if len(params) != len(mapper.primary_key):
+ raise sa_exc.InvalidRequestError(
+ "Incorrect number of values in identifier to formulate primary "
+ "key for query.get(); primary key columns are %s" %
+ ','.join("'%s'" % c for c in mapper.primary_key))
q._params = params
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index eea7a8b12..bd96dd5b7 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -377,7 +377,7 @@ class LazyLoader(AbstractRelationLoader):
callable_=self._class_level_loader,
uselist = self.parent_property.uselist,
typecallable = self.parent_property.collection_class,
- active_history = self.parent_property.direction is not interfaces.MANYTOONE,
+ active_history = not self.use_get,
)
def lazy_clause(self, state, reverse_direction=False, alias_secondary=False, adapt_source=None):
diff --git a/lib/sqlalchemy/pool.py b/lib/sqlalchemy/pool.py
index 6dbadcb3b..3be63ced3 100644
--- a/lib/sqlalchemy/pool.py
+++ b/lib/sqlalchemy/pool.py
@@ -25,7 +25,8 @@ from sqlalchemy.util import threading, pickle, as_interface, memoized_property
proxies = {}
def manage(module, **params):
- """Return a proxy for a DB-API module that automatically pools connections.
+ """Return a proxy for a DB-API module that automatically
+ pools connections.
Given a DB-API 2.0 module and pool management parameters, returns
a proxy for the module that will automatically pool connections,
@@ -55,11 +56,14 @@ def clear_managers():
manager.close()
proxies.clear()
-class Pool(object):
+class Pool(log.Identified):
"""Abstract base class for connection pools."""
- def __init__(self, creator, recycle=-1, echo=None, use_threadlocal=False,
- reset_on_return=True, listeners=None):
+ def __init__(self,
+ creator, recycle=-1, echo=None,
+ use_threadlocal=False,
+ logging_name=None,
+ reset_on_return=True, listeners=None):
"""
Construct a Pool.
@@ -72,6 +76,11 @@ class Pool(object):
timeout is surpassed the connection will be closed and
replaced with a newly opened connection. Defaults to -1.
+ :param logging_name: String identifier which will be used within
+ the "name" field of logging records generated within the
+ "sqlalchemy.pool" logger. Defaults to a hexstring of the object's
+ id.
+
:param echo: If True, connections being pulled and retrieved
from the pool will be logged to the standard output, as well
as pool sizing information. Echoing can also be achieved by
@@ -99,6 +108,8 @@ class Pool(object):
pool.
"""
+ if logging_name:
+ self.logging_name = logging_name
self.logger = log.instance_logger(self, echoflag=echo)
self._threadconns = threading.local()
self._creator = creator
@@ -232,7 +243,8 @@ class _ConnectionRecord(object):
if self.__pool._on_connect:
for l in self.__pool._on_connect:
l.connect(self.connection, self)
- elif (self.__pool._recycle > -1 and time.time() - self.starttime > self.__pool._recycle):
+ elif self.__pool._recycle > -1 and \
+ time.time() - self.starttime > self.__pool._recycle:
self.__pool.logger.info("Connection %r exceeded timeout; recycling",
self.connection)
self.__close()
@@ -247,11 +259,11 @@ class _ConnectionRecord(object):
try:
self.__pool.logger.debug("Closing connection %r", self.connection)
self.connection.close()
+ except (SystemExit, KeyboardInterrupt):
+ raise
except Exception, e:
self.__pool.logger.debug("Connection %r threw an error on close: %s",
self.connection, e)
- if isinstance(e, (SystemExit, KeyboardInterrupt)):
- raise
def __connect(self):
try:
@@ -282,6 +294,7 @@ def _finalize_fairy(connection, connection_record, pool, ref=None):
connection_record.invalidate(e=e)
if isinstance(e, (SystemExit, KeyboardInterrupt)):
raise
+
if connection_record is not None:
connection_record.fairy = None
pool.logger.debug("Connection %r being returned to pool", connection)
@@ -295,7 +308,8 @@ _refs = set()
class _ConnectionFairy(object):
"""Proxies a DB-API connection and provides return-on-dereference support."""
- __slots__ = '_pool', '__counter', 'connection', '_connection_record', '__weakref__', '_detached_info'
+ __slots__ = '_pool', '__counter', 'connection', \
+ '_connection_record', '__weakref__', '_detached_info'
def __init__(self, pool):
self._pool = pool
@@ -471,9 +485,9 @@ class SingletonThreadPool(Pool):
"""
- def __init__(self, creator, pool_size=5, **params):
- params['use_threadlocal'] = True
- Pool.__init__(self, creator, **params)
+ def __init__(self, creator, pool_size=5, **kw):
+ kw['use_threadlocal'] = True
+ Pool.__init__(self, creator, **kw)
self._conn = threading.local()
self._all_conns = set()
self.size = pool_size
@@ -536,7 +550,7 @@ class QueuePool(Pool):
"""A Pool that imposes a limit on the number of open connections."""
def __init__(self, creator, pool_size=5, max_overflow=10, timeout=30,
- **params):
+ **kw):
"""
Construct a QueuePool.
@@ -598,7 +612,7 @@ class QueuePool(Pool):
pool.
"""
- Pool.__init__(self, creator, **params)
+ Pool.__init__(self, creator, **kw)
self._pool = sqla_queue.Queue(pool_size)
self._overflow = 0 - pool_size
self._max_overflow = max_overflow
@@ -634,7 +648,10 @@ class QueuePool(Pool):
if not wait:
return self.do_get()
else:
- raise exc.TimeoutError("QueuePool limit of size %d overflow %d reached, connection timed out, timeout %d" % (self.size(), self.overflow(), self._timeout))
+ raise exc.TimeoutError(
+ "QueuePool limit of size %d overflow %d reached, "
+ "connection timed out, timeout %d" %
+ (self.size(), self.overflow(), self._timeout))
if self._overflow_lock is not None:
self._overflow_lock.acquire()
@@ -664,8 +681,12 @@ class QueuePool(Pool):
self.logger.info("Pool disposed. %s", self.status())
def status(self):
- tup = (self.size(), self.checkedin(), self.overflow(), self.checkedout())
- return "Pool size: %d Connections in pool: %d Current Overflow: %d Current Checked out connections: %d" % tup
+ return "Pool size: %d Connections in pool: %d "\
+ "Current Overflow: %d Current Checked out "\
+ "connections: %d" % (self.size(),
+ self.checkedin(),
+ self.overflow(),
+ self.checkedout())
def size(self):
return self._pool.maxsize
@@ -787,7 +808,6 @@ class StaticPool(Pool):
def do_get(self):
return self.connection
-
class AssertionPool(Pool):
"""A Pool that allows at most one checked out connection at any given time.
@@ -797,8 +817,6 @@ class AssertionPool(Pool):
"""
- ## TODO: modify this to handle an arbitrary connection count.
-
def __init__(self, *args, **kw):
self._conn = None
self._checked_out = False
@@ -823,7 +841,8 @@ class AssertionPool(Pool):
def recreate(self):
self.logger.info("Pool recreating")
- return AssertionPool(self._creator, echo=self.echo, listeners=self.listeners)
+ return AssertionPool(self._creator, echo=self.echo,
+ listeners=self.listeners)
def do_get(self):
if self._checked_out:
@@ -843,7 +862,7 @@ class _DBProxy(object):
to the underlying DB-API module.
"""
- def __init__(self, module, poolclass=QueuePool, **params):
+ def __init__(self, module, poolclass=QueuePool, **kw):
"""Initializes a new proxy.
module
@@ -853,10 +872,11 @@ class _DBProxy(object):
a Pool class, defaulting to QueuePool
Other parameters are sent to the Pool object's constructor.
+
"""
self.module = module
- self.params = params
+ self.kw = kw
self.poolclass = poolclass
self.pools = {}
self._create_pool_mutex = threading.Lock()
@@ -871,15 +891,15 @@ class _DBProxy(object):
def __getattr__(self, key):
return getattr(self.module, key)
- def get_pool(self, *args, **params):
- key = self._serialize(*args, **params)
+ def get_pool(self, *args, **kw):
+ key = self._serialize(*args, **kw)
try:
return self.pools[key]
except KeyError:
self._create_pool_mutex.acquire()
try:
if key not in self.pools:
- pool = self.poolclass(lambda: self.module.connect(*args, **params), **self.params)
+ pool = self.poolclass(lambda: self.module.connect(*args, **kw), **self.kw)
self.pools[key] = pool
return pool
else:
@@ -887,7 +907,7 @@ class _DBProxy(object):
finally:
self._create_pool_mutex.release()
- def connect(self, *args, **params):
+ def connect(self, *args, **kw):
"""Activate a connection to the database.
Connect to the database using this DBProxy's module and the given
@@ -898,18 +918,19 @@ class _DBProxy(object):
If the pool has no available connections and allows new connections
to be created, a new database connection will be made.
+
"""
- return self.get_pool(*args, **params).connect()
+ return self.get_pool(*args, **kw).connect()
- def dispose(self, *args, **params):
- """Dispose the connection pool referenced by the given connect arguments."""
+ def dispose(self, *args, **kw):
+ """Dispose the pool referenced by the given connect arguments."""
- key = self._serialize(*args, **params)
+ key = self._serialize(*args, **kw)
try:
del self.pools[key]
except KeyError:
pass
- def _serialize(self, *args, **params):
- return pickle.dumps([args, params])
+ def _serialize(self, *args, **kw):
+ return pickle.dumps([args, kw])
diff --git a/lib/sqlalchemy/test/config.py b/lib/sqlalchemy/test/config.py
index eec962d80..efbe00fef 100644
--- a/lib/sqlalchemy/test/config.py
+++ b/lib/sqlalchemy/test/config.py
@@ -1,5 +1,6 @@
import optparse, os, sys, re, ConfigParser, time, warnings
+
# 2to3
import StringIO
diff --git a/lib/sqlalchemy/test/engines.py b/lib/sqlalchemy/test/engines.py
index 2f3d11bda..0cfd58d20 100644
--- a/lib/sqlalchemy/test/engines.py
+++ b/lib/sqlalchemy/test/engines.py
@@ -80,10 +80,12 @@ def close_open_connections(fn):
testing_reaper.close_all()
return function_named(decorated, fn.__name__)
-def all_dialects():
+def all_dialects(exclude=None):
import sqlalchemy.databases as d
for name in d.__all__:
# TEMPORARY
+ if exclude and name in exclude:
+ continue
mod = getattr(d, name, None)
if not mod:
mod = getattr(__import__('sqlalchemy.databases.%s' % name).databases, name)
@@ -240,7 +242,11 @@ class ReplayableSession(object):
else:
buffer.append(result)
return result
-
+
+ @property
+ def _sqla_unwrap(self):
+ return self._subject
+
def __getattribute__(self, key):
try:
return object.__getattribute__(self, key)
@@ -273,7 +279,11 @@ class ReplayableSession(object):
return self
else:
return result
-
+
+ @property
+ def _sqla_unwrap(self):
+ return None
+
def __getattribute__(self, key):
try:
return object.__getattribute__(self, key)
@@ -288,10 +298,3 @@ class ReplayableSession(object):
else:
return result
-def unwrap_connection(conn):
- if conn.__class__.__name__ == 'Recorder':
- return conn._subject
- elif conn.__class__.__name__ == 'Player':
- return None
- else:
- return conn