260 lines
8.7 KiB
Python
260 lines
8.7 KiB
Python
|
"""Global database feature support policy.
|
||
|
|
||
|
Provides decorators to mark tests requiring specific feature support from the
|
||
|
target database.
|
||
|
|
||
|
"""
|
||
|
|
||
|
from testing import \
|
||
|
_block_unconditionally as no_support, \
|
||
|
_chain_decorators_on, \
|
||
|
exclude, \
|
||
|
emits_warning_on,\
|
||
|
skip_if,\
|
||
|
fails_on
|
||
|
|
||
|
import testing
|
||
|
import sys
|
||
|
|
||
|
def deferrable_constraints(fn):
|
||
|
"""Target database must support derferable constraints."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('firebird', 'not supported by database'),
|
||
|
no_support('mysql', 'not supported by database'),
|
||
|
no_support('mssql', 'not supported by database'),
|
||
|
)
|
||
|
|
||
|
def foreign_keys(fn):
|
||
|
"""Target database must support foreign keys."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('sqlite', 'not supported by database'),
|
||
|
)
|
||
|
|
||
|
|
||
|
def unbounded_varchar(fn):
|
||
|
"""Target database must support VARCHAR with no length"""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('firebird', 'not supported by database'),
|
||
|
no_support('oracle', 'not supported by database'),
|
||
|
no_support('mysql', 'not supported by database'),
|
||
|
)
|
||
|
|
||
|
def boolean_col_expressions(fn):
|
||
|
"""Target database must support boolean expressions as columns"""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('firebird', 'not supported by database'),
|
||
|
no_support('oracle', 'not supported by database'),
|
||
|
no_support('mssql', 'not supported by database'),
|
||
|
no_support('sybase', 'not supported by database'),
|
||
|
no_support('maxdb', 'FIXME: verify not supported by database'),
|
||
|
)
|
||
|
|
||
|
def identity(fn):
|
||
|
"""Target database must support GENERATED AS IDENTITY or a facsimile.
|
||
|
|
||
|
Includes GENERATED AS IDENTITY, AUTOINCREMENT, AUTO_INCREMENT, or other
|
||
|
column DDL feature that fills in a DB-generated identifier at INSERT-time
|
||
|
without requiring pre-execution of a SEQUENCE or other artifact.
|
||
|
|
||
|
"""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('firebird', 'not supported by database'),
|
||
|
no_support('oracle', 'not supported by database'),
|
||
|
no_support('postgresql', 'not supported by database'),
|
||
|
no_support('sybase', 'not supported by database'),
|
||
|
)
|
||
|
|
||
|
def independent_cursors(fn):
|
||
|
"""Target must support simultaneous, independent database cursors on a single connection."""
|
||
|
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('mssql+pyodbc', 'no driver support'),
|
||
|
no_support('mssql+mxodbc', 'no driver support'),
|
||
|
)
|
||
|
|
||
|
def independent_connections(fn):
|
||
|
"""Target must support simultaneous, independent database connections."""
|
||
|
|
||
|
# This is also true of some configurations of UnixODBC and probably win32
|
||
|
# ODBC as well.
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('sqlite', 'no driver support'),
|
||
|
exclude('mssql', '<', (9, 0, 0),
|
||
|
'SQL Server 2005+ is required for independent connections'),
|
||
|
)
|
||
|
|
||
|
def row_triggers(fn):
|
||
|
"""Target must support standard statement-running EACH ROW triggers."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
# no access to same table
|
||
|
no_support('mysql', 'requires SUPER priv'),
|
||
|
exclude('mysql', '<', (5, 0, 10), 'not supported by database'),
|
||
|
|
||
|
# huh? TODO: implement triggers for PG tests, remove this
|
||
|
no_support('postgresql', 'PG triggers need to be implemented for tests'),
|
||
|
)
|
||
|
|
||
|
def correlated_outer_joins(fn):
|
||
|
"""Target must support an outer join to a subquery which correlates to the parent."""
|
||
|
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('oracle', 'Raises "ORA-01799: a column may not be outer-joined to a subquery"')
|
||
|
)
|
||
|
|
||
|
def savepoints(fn):
|
||
|
"""Target database must support savepoints."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
emits_warning_on('mssql', 'Savepoint support in mssql is experimental and may lead to data loss.'),
|
||
|
no_support('access', 'not supported by database'),
|
||
|
no_support('sqlite', 'not supported by database'),
|
||
|
no_support('sybase', 'FIXME: guessing, needs confirmation'),
|
||
|
exclude('mysql', '<', (5, 0, 3), 'not supported by database'),
|
||
|
)
|
||
|
|
||
|
def denormalized_names(fn):
|
||
|
"""Target database must have 'denormalized', i.e. UPPERCASE as case insensitive names."""
|
||
|
|
||
|
return skip_if(
|
||
|
lambda: not testing.db.dialect.requires_name_normalize,
|
||
|
"Backend does not require denomralized names."
|
||
|
)(fn)
|
||
|
|
||
|
def schemas(fn):
|
||
|
"""Target database must support external schemas, and have one named 'test_schema'."""
|
||
|
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('sqlite', 'no schema support'),
|
||
|
no_support('firebird', 'no schema support')
|
||
|
)
|
||
|
|
||
|
def sequences(fn):
|
||
|
"""Target database must support SEQUENCEs."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('access', 'no SEQUENCE support'),
|
||
|
no_support('mssql', 'no SEQUENCE support'),
|
||
|
no_support('mysql', 'no SEQUENCE support'),
|
||
|
no_support('sqlite', 'no SEQUENCE support'),
|
||
|
no_support('sybase', 'no SEQUENCE support'),
|
||
|
)
|
||
|
|
||
|
def subqueries(fn):
|
||
|
"""Target database must support subqueries."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
exclude('mysql', '<', (4, 1, 1), 'no subquery support'),
|
||
|
)
|
||
|
|
||
|
def intersect(fn):
|
||
|
"""Target database must support INTERSECT or equivlaent."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
fails_on('firebird', 'no support for INTERSECT'),
|
||
|
fails_on('mysql', 'no support for INTERSECT'),
|
||
|
fails_on('sybase', 'no support for INTERSECT'),
|
||
|
)
|
||
|
|
||
|
def except_(fn):
|
||
|
"""Target database must support EXCEPT or equivlaent (i.e. MINUS)."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
fails_on('firebird', 'no support for EXCEPT'),
|
||
|
fails_on('mysql', 'no support for EXCEPT'),
|
||
|
fails_on('sybase', 'no support for EXCEPT'),
|
||
|
)
|
||
|
|
||
|
def offset(fn):
|
||
|
"""Target database must support some method of adding OFFSET or equivalent to a result set."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
fails_on('sybase', 'no support for OFFSET or equivalent'),
|
||
|
)
|
||
|
|
||
|
def returning(fn):
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('access', 'not supported by database'),
|
||
|
no_support('sqlite', 'not supported by database'),
|
||
|
no_support('mysql', 'not supported by database'),
|
||
|
no_support('maxdb', 'not supported by database'),
|
||
|
no_support('sybase', 'not supported by database'),
|
||
|
no_support('informix', 'not supported by database'),
|
||
|
)
|
||
|
|
||
|
def two_phase_transactions(fn):
|
||
|
"""Target database must support two-phase transactions."""
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('access', 'not supported by database'),
|
||
|
no_support('firebird', 'no SA implementation'),
|
||
|
no_support('maxdb', 'not supported by database'),
|
||
|
no_support('mssql', 'FIXME: guessing, needs confirmation'),
|
||
|
no_support('oracle', 'no SA implementation'),
|
||
|
no_support('sqlite', 'not supported by database'),
|
||
|
no_support('sybase', 'FIXME: guessing, needs confirmation'),
|
||
|
no_support('postgresql+zxjdbc', 'FIXME: JDBC driver confuses the transaction state, may '
|
||
|
'need separate XA implementation'),
|
||
|
exclude('mysql', '<', (5, 0, 3), 'not supported by database'),
|
||
|
)
|
||
|
|
||
|
def unicode_connections(fn):
|
||
|
"""Target driver must support some encoding of Unicode across the wire."""
|
||
|
# TODO: expand to exclude MySQLdb versions w/ broken unicode
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
exclude('mysql', '<', (4, 1, 1), 'no unicode connection support'),
|
||
|
)
|
||
|
|
||
|
def unicode_ddl(fn):
|
||
|
"""Target driver must support some encoding of Unicode across the wire."""
|
||
|
# TODO: expand to exclude MySQLdb versions w/ broken unicode
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
no_support('maxdb', 'database support flakey'),
|
||
|
no_support('oracle', 'FIXME: no support in database?'),
|
||
|
no_support('sybase', 'FIXME: guessing, needs confirmation'),
|
||
|
no_support('mssql+pymssql', 'no FreeTDS support'),
|
||
|
exclude('mysql', '<', (4, 1, 1), 'no unicode connection support'),
|
||
|
)
|
||
|
|
||
|
def sane_rowcount(fn):
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
skip_if(lambda: not testing.db.dialect.supports_sane_rowcount)
|
||
|
)
|
||
|
|
||
|
def python2(fn):
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
skip_if(
|
||
|
lambda: sys.version_info >= (3,),
|
||
|
"Python version 2.xx is required."
|
||
|
)
|
||
|
)
|
||
|
|
||
|
def _has_sqlite():
|
||
|
from sqlalchemy import create_engine
|
||
|
try:
|
||
|
e = create_engine('sqlite://')
|
||
|
return True
|
||
|
except ImportError:
|
||
|
return False
|
||
|
|
||
|
def sqlite(fn):
|
||
|
return _chain_decorators_on(
|
||
|
fn,
|
||
|
skip_if(lambda: not _has_sqlite())
|
||
|
)
|
||
|
|