mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-06-05 07:17:06 -04:00
- The functionality of result.rowcount is now disabled
by default, and can be re-enabled using the 'enable_rowcount' flag with create_engine(), as well as the 'enable_rowcount' execution context flag on a per-execute basis. This because cursor.rowcount requires cursor access (can't be evaluated lazily since the result auto-closes) and also incurs an expensive round-trip.
This commit is contained in:
@@ -134,7 +134,16 @@ CHANGES
|
||||
- STRING/FIXED_CHAR now convert to unicode natively.
|
||||
SQLAlchemy's String types then don't need to
|
||||
apply any kind of conversions.
|
||||
|
||||
|
||||
- firebird
|
||||
- The functionality of result.rowcount is now disabled
|
||||
by default, and can be re-enabled using the 'enable_rowcount'
|
||||
flag with create_engine(), as well as the 'enable_rowcount'
|
||||
execution context flag on a per-execute basis. This because
|
||||
cursor.rowcount requires cursor access (can't be evaluated
|
||||
lazily since the result auto-closes) and also incurs an
|
||||
expensive round-trip.
|
||||
|
||||
- examples
|
||||
- Updated attribute_shard.py example to use a more robust
|
||||
method of searching a Query for binary expressions which
|
||||
|
||||
@@ -13,21 +13,34 @@ The connection URL is of the form
|
||||
|
||||
Kinterbasedb backend specific keyword arguments are:
|
||||
|
||||
type_conv
|
||||
select the kind of mapping done on the types: by default SQLAlchemy
|
||||
* type_conv - select the kind of mapping done on the types: by default SQLAlchemy
|
||||
uses 200 with Unicode, datetime and decimal support (see details__).
|
||||
|
||||
concurrency_level
|
||||
set the backend policy with regards to threading issues: by default
|
||||
* concurrency_level - set the backend policy with regards to threading issues: by default
|
||||
SQLAlchemy uses policy 1 (see details__).
|
||||
|
||||
* enable_rowcount - False by default, this enables the usage of "cursor.rowcount" with the
|
||||
Kinterbasdb dialect. When disabled, SQLAlchemy's ResultProxy will
|
||||
return -1 for result.rowcount. The rationale here is that Kinterbasdb
|
||||
requires a second round trip to the database when .rowcount is called -
|
||||
since SQLA's resultproxy automatically closes the cursor after a
|
||||
non-result-returning statement, rowcount must be called, if at all,
|
||||
before the result object is returned. The behavior can also be
|
||||
controlled on a per-execution basis using the `enable_rowcount`
|
||||
option with :meth:`execution_options()`::
|
||||
|
||||
conn = engine.connect().execution_options(enable_rowcount=True)
|
||||
r = conn.execute(stmt)
|
||||
print r.rowcount
|
||||
|
||||
__ http://sourceforge.net/projects/kinterbasdb
|
||||
__ http://firebirdsql.org/index.php?op=devel&sub=python
|
||||
__ http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_param_conv_dynamic_type_translation
|
||||
__ http://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurrency
|
||||
"""
|
||||
|
||||
from sqlalchemy.dialects.firebird.base import FBDialect, FBCompiler
|
||||
from sqlalchemy.dialects.firebird.base import FBDialect, \
|
||||
FBCompiler, FBExecutionContext
|
||||
from sqlalchemy import util, types as sqltypes
|
||||
|
||||
class _FBNumeric_kinterbasdb(sqltypes.Numeric):
|
||||
@@ -38,11 +51,21 @@ class _FBNumeric_kinterbasdb(sqltypes.Numeric):
|
||||
else:
|
||||
return value
|
||||
return process
|
||||
|
||||
|
||||
class FBExecutionContext_kinterbasdb(FBExecutionContext):
|
||||
@property
|
||||
def rowcount(self):
|
||||
if self.execution_options.get('enable_rowcount',
|
||||
self.dialect.enable_rowcount):
|
||||
return self.cursor.rowcount
|
||||
else:
|
||||
return -1
|
||||
|
||||
class FBDialect_kinterbasdb(FBDialect):
|
||||
driver = 'kinterbasdb'
|
||||
supports_sane_rowcount = False
|
||||
supports_sane_multi_rowcount = False
|
||||
execution_ctx_cls = FBExecutionContext_kinterbasdb
|
||||
|
||||
supports_native_decimal = True
|
||||
|
||||
@@ -54,12 +77,14 @@ class FBDialect_kinterbasdb(FBDialect):
|
||||
|
||||
)
|
||||
|
||||
def __init__(self, type_conv=200, concurrency_level=1, **kwargs):
|
||||
def __init__(self, type_conv=200, concurrency_level=1, enable_rowcount=False, **kwargs):
|
||||
super(FBDialect_kinterbasdb, self).__init__(**kwargs)
|
||||
|
||||
self.enable_rowcount = enable_rowcount
|
||||
self.type_conv = type_conv
|
||||
self.concurrency_level = concurrency_level
|
||||
|
||||
if enable_rowcount:
|
||||
self.supports_sane_rowcount = True
|
||||
|
||||
@classmethod
|
||||
def dbapi(cls):
|
||||
k = __import__('kinterbasdb')
|
||||
|
||||
@@ -265,6 +265,7 @@ class CompileTest(TestBase, AssertsCompiledSQL):
|
||||
class MiscTest(TestBase):
|
||||
__only_on__ = 'firebird'
|
||||
|
||||
@testing.provide_metadata
|
||||
def test_strlen(self):
|
||||
# On FB the length() function is implemented by an external
|
||||
# UDF, strlen(). Various SA tests fail because they pass a
|
||||
@@ -272,23 +273,49 @@ class MiscTest(TestBase):
|
||||
# the maximum string length the UDF was declared to accept).
|
||||
# This test checks that at least it works ok in other cases.
|
||||
|
||||
meta = MetaData(testing.db)
|
||||
t = Table('t1', meta,
|
||||
t = Table('t1', metadata,
|
||||
Column('id', Integer, Sequence('t1idseq'), primary_key=True),
|
||||
Column('name', String(10))
|
||||
)
|
||||
meta.create_all()
|
||||
try:
|
||||
t.insert(values=dict(name='dante')).execute()
|
||||
t.insert(values=dict(name='alighieri')).execute()
|
||||
select([func.count(t.c.id)],func.length(t.c.name)==5).execute().first()[0] == 1
|
||||
finally:
|
||||
meta.drop_all()
|
||||
metadata.create_all()
|
||||
t.insert(values=dict(name='dante')).execute()
|
||||
t.insert(values=dict(name='alighieri')).execute()
|
||||
select([func.count(t.c.id)],func.length(t.c.name)==5).execute().first()[0] == 1
|
||||
|
||||
def test_server_version_info(self):
|
||||
version = testing.db.dialect.server_version_info
|
||||
assert len(version) == 3, "Got strange version info: %s" % repr(version)
|
||||
|
||||
@testing.provide_metadata
|
||||
def test_rowcount_flag(self):
|
||||
engine = engines.testing_engine(options={'enable_rowcount':True})
|
||||
assert engine.dialect.supports_sane_rowcount
|
||||
metadata.bind = engine
|
||||
t = Table('t1', metadata,
|
||||
Column('data', String(10))
|
||||
)
|
||||
metadata.create_all()
|
||||
r = t.insert().execute({'data':'d1'}, {'data':'d2'}, {'data': 'd3'})
|
||||
r = t.update().where(t.c.data=='d2').values(data='d3').execute()
|
||||
eq_(r.rowcount, 1)
|
||||
r = t.delete().where(t.c.data == 'd3').execute()
|
||||
eq_(r.rowcount, 2)
|
||||
|
||||
r = t.delete().execution_options(enable_rowcount=False).execute()
|
||||
eq_(r.rowcount, -1)
|
||||
|
||||
|
||||
engine = engines.testing_engine(options={'enable_rowcount':False})
|
||||
assert not engine.dialect.supports_sane_rowcount
|
||||
metadata.bind = engine
|
||||
r = t.insert().execute({'data':'d1'}, {'data':'d2'}, {'data':'d3'})
|
||||
r = t.update().where(t.c.data=='d2').values(data='d3').execute()
|
||||
eq_(r.rowcount, -1)
|
||||
r = t.delete().where(t.c.data == 'd3').execute()
|
||||
eq_(r.rowcount, -1)
|
||||
r = t.delete().execution_options(enable_rowcount=True).execute()
|
||||
eq_(r.rowcount, 1)
|
||||
|
||||
def test_percents_in_text(self):
|
||||
for expr, result in (
|
||||
(text("select '%' from rdb$database"), '%'),
|
||||
|
||||
Reference in New Issue
Block a user