Initial Fork from https://bitbucket.org/raphaelmutschler/calibreserver/
This commit is contained in:
@@ -0,0 +1,438 @@
|
||||
"""Global database feature support policy.
|
||||
|
||||
Provides decorators to mark tests requiring specific feature support from the
|
||||
target database.
|
||||
|
||||
External dialect test suites should subclass SuiteRequirements
|
||||
to provide specific inclusion/exlusions.
|
||||
|
||||
"""
|
||||
|
||||
from . import exclusions, config
|
||||
|
||||
|
||||
class Requirements(object):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
|
||||
@property
|
||||
def db(self):
|
||||
return config.db
|
||||
|
||||
class SuiteRequirements(Requirements):
|
||||
|
||||
@property
|
||||
def create_table(self):
|
||||
"""target platform can emit basic CreateTable DDL."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def drop_table(self):
|
||||
"""target platform can emit basic DropTable DDL."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def foreign_keys(self):
|
||||
"""Target database must support foreign keys."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def on_update_cascade(self):
|
||||
""""target database must support ON UPDATE..CASCADE behavior in
|
||||
foreign keys."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def deferrable_fks(self):
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def on_update_or_deferrable_fks(self):
|
||||
# TODO: exclusions should be composable,
|
||||
# somehow only_if([x, y]) isn't working here, negation/conjunctions
|
||||
# getting confused.
|
||||
return exclusions.only_if(
|
||||
lambda: self.on_update_cascade.enabled or self.deferrable_fks.enabled
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def self_referential_foreign_keys(self):
|
||||
"""Target database must support self-referential foreign keys."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def foreign_key_ddl(self):
|
||||
"""Target database must support the DDL phrases for FOREIGN KEY."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def named_constraints(self):
|
||||
"""target database must support names for constraints."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def subqueries(self):
|
||||
"""Target database must support subqueries."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def offset(self):
|
||||
"""target database can render OFFSET, or an equivalent, in a SELECT."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def boolean_col_expressions(self):
|
||||
"""Target database must support boolean expressions as columns"""
|
||||
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def nullsordering(self):
|
||||
"""Target backends that support nulls ordering."""
|
||||
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def standalone_binds(self):
|
||||
"""target database/driver supports bound parameters as column expressions
|
||||
without being in the context of a typed column.
|
||||
|
||||
"""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def intersect(self):
|
||||
"""Target database must support INTERSECT or equivalent."""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def except_(self):
|
||||
"""Target database must support EXCEPT or equivalent (i.e. MINUS)."""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def window_functions(self):
|
||||
"""Target database must support window functions."""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def autoincrement_insert(self):
|
||||
"""target platform generates new surrogate integer primary key values
|
||||
when insert() is executed, excluding the pk column."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def empty_inserts(self):
|
||||
"""target platform supports INSERT with no values, i.e.
|
||||
INSERT DEFAULT VALUES or equivalent."""
|
||||
|
||||
return exclusions.only_if(
|
||||
lambda: self.config.db.dialect.supports_empty_insert or \
|
||||
self.config.db.dialect.supports_default_values,
|
||||
"empty inserts not supported"
|
||||
)
|
||||
|
||||
@property
|
||||
def insert_from_select(self):
|
||||
"""target platform supports INSERT from a SELECT."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def returning(self):
|
||||
"""target platform supports RETURNING."""
|
||||
|
||||
return exclusions.only_if(
|
||||
lambda: self.config.db.dialect.implicit_returning,
|
||||
"'returning' not supported by database"
|
||||
)
|
||||
|
||||
@property
|
||||
def denormalized_names(self):
|
||||
"""Target database must have 'denormalized', i.e.
|
||||
UPPERCASE as case insensitive names."""
|
||||
|
||||
return exclusions.skip_if(
|
||||
lambda: not self.db.dialect.requires_name_normalize,
|
||||
"Backend does not require denormalized names."
|
||||
)
|
||||
|
||||
@property
|
||||
def multivalues_inserts(self):
|
||||
"""target database must support multiple VALUES clauses in an
|
||||
INSERT statement."""
|
||||
|
||||
return exclusions.skip_if(
|
||||
lambda: not self.db.dialect.supports_multivalues_insert,
|
||||
"Backend does not support multirow inserts."
|
||||
)
|
||||
|
||||
|
||||
@property
|
||||
def implements_get_lastrowid(self):
|
||||
""""target dialect implements the executioncontext.get_lastrowid()
|
||||
method without reliance on RETURNING.
|
||||
|
||||
"""
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def emulated_lastrowid(self):
|
||||
""""target dialect retrieves cursor.lastrowid, or fetches
|
||||
from a database-side function after an insert() construct executes,
|
||||
within the get_lastrowid() method.
|
||||
|
||||
Only dialects that "pre-execute", or need RETURNING to get last
|
||||
inserted id, would return closed/fail/skip for this.
|
||||
|
||||
"""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def dbapi_lastrowid(self):
|
||||
""""target platform includes a 'lastrowid' accessor on the DBAPI
|
||||
cursor object.
|
||||
|
||||
"""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def views(self):
|
||||
"""Target database must support VIEWs."""
|
||||
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def schemas(self):
|
||||
"""Target database must support external schemas, and have one
|
||||
named 'test_schema'."""
|
||||
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def sequences(self):
|
||||
"""Target database must support SEQUENCEs."""
|
||||
|
||||
return exclusions.only_if([
|
||||
lambda: self.config.db.dialect.supports_sequences
|
||||
], "no sequence support")
|
||||
|
||||
@property
|
||||
def sequences_optional(self):
|
||||
"""Target database supports sequences, but also optionally
|
||||
as a means of generating new PK values."""
|
||||
|
||||
return exclusions.only_if([
|
||||
lambda: self.config.db.dialect.supports_sequences and \
|
||||
self.config.db.dialect.sequences_optional
|
||||
], "no sequence support, or sequences not optional")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def reflects_pk_names(self):
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def table_reflection(self):
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def view_reflection(self):
|
||||
return self.views
|
||||
|
||||
@property
|
||||
def schema_reflection(self):
|
||||
return self.schemas
|
||||
|
||||
@property
|
||||
def primary_key_constraint_reflection(self):
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def foreign_key_constraint_reflection(self):
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def index_reflection(self):
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def unbounded_varchar(self):
|
||||
"""Target database must support VARCHAR with no length"""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def unicode_data(self):
|
||||
"""Target database/dialect must support Python unicode objects with
|
||||
non-ASCII characters represented, delivered as bound parameters
|
||||
as well as in result rows.
|
||||
|
||||
"""
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def unicode_ddl(self):
|
||||
"""Target driver must support some degree of non-ascii symbol names."""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def datetime(self):
|
||||
"""target dialect supports representation of Python
|
||||
datetime.datetime() objects."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def datetime_microseconds(self):
|
||||
"""target dialect supports representation of Python
|
||||
datetime.datetime() with microsecond objects."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def datetime_historic(self):
|
||||
"""target dialect supports representation of Python
|
||||
datetime.datetime() objects with historic (pre 1970) values."""
|
||||
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def date(self):
|
||||
"""target dialect supports representation of Python
|
||||
datetime.date() objects."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def date_historic(self):
|
||||
"""target dialect supports representation of Python
|
||||
datetime.datetime() objects with historic (pre 1970) values."""
|
||||
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def time(self):
|
||||
"""target dialect supports representation of Python
|
||||
datetime.time() objects."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def time_microseconds(self):
|
||||
"""target dialect supports representation of Python
|
||||
datetime.time() with microsecond objects."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def precision_numerics_general(self):
|
||||
"""target backend has general support for moderately high-precision
|
||||
numerics."""
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def precision_numerics_enotation_small(self):
|
||||
"""target backend supports Decimal() objects using E notation
|
||||
to represent very small values."""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def precision_numerics_enotation_large(self):
|
||||
"""target backend supports Decimal() objects using E notation
|
||||
to represent very large values."""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def precision_numerics_many_significant_digits(self):
|
||||
"""target backend supports values with many digits on both sides,
|
||||
such as 319438950232418390.273596, 87673.594069654243
|
||||
|
||||
"""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def precision_numerics_retains_significant_digits(self):
|
||||
"""A precision numeric type will return empty significant digits,
|
||||
i.e. a value such as 10.000 will come back in Decimal form with
|
||||
the .000 maintained."""
|
||||
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def text_type(self):
|
||||
"""Target database must support an unbounded Text() "
|
||||
"type such as TEXT or CLOB"""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def empty_strings_varchar(self):
|
||||
"""target database can persist/return an empty string with a
|
||||
varchar.
|
||||
|
||||
"""
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def empty_strings_text(self):
|
||||
"""target database can persist/return an empty string with an
|
||||
unbounded text."""
|
||||
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def update_from(self):
|
||||
"""Target must support UPDATE..FROM syntax"""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def update_where_target_in_subquery(self):
|
||||
"""Target must support UPDATE where the same table is present in a
|
||||
subquery in the WHERE clause.
|
||||
|
||||
This is an ANSI-standard syntax that apparently MySQL can't handle,
|
||||
such as:
|
||||
|
||||
UPDATE documents SET flag=1 WHERE documents.title IN
|
||||
(SELECT max(documents.title) AS title
|
||||
FROM documents GROUP BY documents.user_id
|
||||
)
|
||||
"""
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def mod_operator_as_percent_sign(self):
|
||||
"""target database must use a plain percent '%' as the 'modulus'
|
||||
operator."""
|
||||
return exclusions.closed()
|
||||
|
||||
@property
|
||||
def unicode_connections(self):
|
||||
"""Target driver must support non-ASCII characters being passed at all."""
|
||||
return exclusions.open()
|
||||
|
||||
@property
|
||||
def skip_mysql_on_windows(self):
|
||||
"""Catchall for a large variety of MySQL on Windows failures"""
|
||||
return exclusions.open()
|
||||
|
||||
def _has_mysql_on_windows(self):
|
||||
return False
|
||||
|
||||
def _has_mysql_fully_case_sensitive(self):
|
||||
return False
|
||||
Reference in New Issue
Block a user