diff options
author | mike bayer <mike_mp@zzzcomputing.com> | 2021-10-31 17:09:56 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-10-31 17:09:56 +0000 |
commit | a1adb21cbe83ecce467194ab0e3277db581dfa4d (patch) | |
tree | be97a906403ea59bf8767c7642684aa3aedbf731 /lib/sqlalchemy/dialects | |
parent | de61582933b800272cc818d36de9e0b7f86aa4fe (diff) | |
parent | ed78e679eafe787f4c152b78726bf1e1b91ab465 (diff) | |
download | sqlalchemy-a1adb21cbe83ecce467194ab0e3277db581dfa4d.tar.gz |
Merge "Remove deprecated dialects and drivers" into main
Diffstat (limited to 'lib/sqlalchemy/dialects')
-rw-r--r-- | lib/sqlalchemy/dialects/__init__.py | 24 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/firebird/__init__.py | 41 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/firebird/base.py | 989 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/firebird/fdb.py | 112 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/firebird/kinterbasdb.py | 202 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/mssql/__init__.py | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/mssql/mxodbc.py | 150 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/mysql/__init__.py | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/mysql/base.py | 7 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/mysql/oursql.py | 273 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/__init__.py | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/pygresql.py | 278 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/postgresql/pypostgresql.py | 126 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/sybase/__init__.py | 67 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/sybase/base.py | 1100 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/sybase/mxodbc.py | 34 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/sybase/pyodbc.py | 89 | ||||
-rw-r--r-- | lib/sqlalchemy/dialects/sybase/pysybase.py | 106 |
18 files changed, 5 insertions, 3597 deletions
diff --git a/lib/sqlalchemy/dialects/__init__.py b/lib/sqlalchemy/dialects/__init__.py index e06eb0995..af1045dd2 100644 --- a/lib/sqlalchemy/dialects/__init__.py +++ b/lib/sqlalchemy/dialects/__init__.py @@ -5,15 +5,7 @@ # This module is part of SQLAlchemy and is released under # the MIT License: https://www.opensource.org/licenses/mit-license.php -__all__ = ( - "firebird", - "mssql", - "mysql", - "oracle", - "postgresql", - "sqlite", - "sybase", -) +__all__ = ("mssql", "mysql", "oracle", "postgresql", "sqlite") from .. import util @@ -33,19 +25,7 @@ def _auto_fn(name): driver = "base" try: - if dialect == "firebird": - try: - module = __import__("sqlalchemy_firebird") - except ImportError: - module = __import__("sqlalchemy.dialects.firebird").dialects - module = getattr(module, dialect) - elif dialect == "sybase": - try: - module = __import__("sqlalchemy_sybase") - except ImportError: - module = __import__("sqlalchemy.dialects.sybase").dialects - module = getattr(module, dialect) - elif dialect == "mariadb": + if dialect == "mariadb": # it's "OK" for us to hardcode here since _auto_fn is already # hardcoded. if mysql / mariadb etc were third party dialects # they would just publish all the entrypoints, which would actually diff --git a/lib/sqlalchemy/dialects/firebird/__init__.py b/lib/sqlalchemy/dialects/firebird/__init__.py deleted file mode 100644 index d4a054c3b..000000000 --- a/lib/sqlalchemy/dialects/firebird/__init__.py +++ /dev/null @@ -1,41 +0,0 @@ -# firebird/__init__.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -from sqlalchemy.dialects.firebird.base import BIGINT -from sqlalchemy.dialects.firebird.base import BLOB -from sqlalchemy.dialects.firebird.base import CHAR -from sqlalchemy.dialects.firebird.base import DATE -from sqlalchemy.dialects.firebird.base import FLOAT -from sqlalchemy.dialects.firebird.base import NUMERIC -from sqlalchemy.dialects.firebird.base import SMALLINT -from sqlalchemy.dialects.firebird.base import TEXT -from sqlalchemy.dialects.firebird.base import TIME -from sqlalchemy.dialects.firebird.base import TIMESTAMP -from sqlalchemy.dialects.firebird.base import VARCHAR -from . import base # noqa -from . import fdb # noqa -from . import kinterbasdb # noqa - - -base.dialect = dialect = fdb.dialect - -__all__ = ( - "SMALLINT", - "BIGINT", - "FLOAT", - "FLOAT", - "DATE", - "TIME", - "TEXT", - "NUMERIC", - "FLOAT", - "TIMESTAMP", - "VARCHAR", - "CHAR", - "BLOB", - "dialect", -) diff --git a/lib/sqlalchemy/dialects/firebird/base.py b/lib/sqlalchemy/dialects/firebird/base.py deleted file mode 100644 index 91e2c04a7..000000000 --- a/lib/sqlalchemy/dialects/firebird/base.py +++ /dev/null @@ -1,989 +0,0 @@ -# firebird/base.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -r""" - -.. dialect:: firebird - :name: Firebird - -.. note:: - - The Firebird dialect within SQLAlchemy **is not currently supported**. - It is not tested within continuous integration and is likely to have - many issues and caveats not currently handled. Consider using the - `external dialect <https://github.com/pauldex/sqlalchemy-firebird>`_ - instead. - -.. deprecated:: 1.4 The internal Firebird dialect is deprecated and will be - removed in a future version. Use the external dialect. - -Firebird Dialects ------------------ - -Firebird offers two distinct dialects_ (not to be confused with a -SQLAlchemy ``Dialect``): - -dialect 1 - This is the old syntax and behaviour, inherited from Interbase pre-6.0. - -dialect 3 - This is the newer and supported syntax, introduced in Interbase 6.0. - -The SQLAlchemy Firebird dialect detects these versions and -adjusts its representation of SQL accordingly. However, -support for dialect 1 is not well tested and probably has -incompatibilities. - -Locking Behavior ----------------- - -Firebird locks tables aggressively. For this reason, a DROP TABLE may -hang until other transactions are released. SQLAlchemy does its best -to release transactions as quickly as possible. The most common cause -of hanging transactions is a non-fully consumed result set, i.e.:: - - result = engine.execute(text("select * from table")) - row = result.fetchone() - return - -Where above, the ``CursorResult`` has not been fully consumed. The -connection will be returned to the pool and the transactional state -rolled back once the Python garbage collector reclaims the objects -which hold onto the connection, which often occurs asynchronously. -The above use case can be alleviated by calling ``first()`` on the -``CursorResult`` which will fetch the first row and immediately close -all remaining cursor/connection resources. - -RETURNING support ------------------ - -Firebird 2.0 supports returning a result set from inserts, and 2.1 -extends that to deletes and updates. This is generically exposed by -the SQLAlchemy ``returning()`` method, such as:: - - # INSERT..RETURNING - result = table.insert().returning(table.c.col1, table.c.col2).\ - values(name='foo') - print(result.fetchall()) - - # UPDATE..RETURNING - raises = empl.update().returning(empl.c.id, empl.c.salary).\ - where(empl.c.sales>100).\ - values(dict(salary=empl.c.salary * 1.1)) - print(raises.fetchall()) - - -.. _dialects: https://mc-computing.com/Databases/Firebird/SQL_Dialect.html -""" - -import datetime - -from sqlalchemy import exc -from sqlalchemy import sql -from sqlalchemy import types as sqltypes -from sqlalchemy import util -from sqlalchemy.engine import default -from sqlalchemy.engine import reflection -from sqlalchemy.sql import compiler -from sqlalchemy.sql import expression -from sqlalchemy.types import BIGINT -from sqlalchemy.types import BLOB -from sqlalchemy.types import DATE -from sqlalchemy.types import FLOAT -from sqlalchemy.types import INTEGER -from sqlalchemy.types import Integer -from sqlalchemy.types import NUMERIC -from sqlalchemy.types import SMALLINT -from sqlalchemy.types import TEXT -from sqlalchemy.types import TIME -from sqlalchemy.types import TIMESTAMP - - -RESERVED_WORDS = set( - [ - "active", - "add", - "admin", - "after", - "all", - "alter", - "and", - "any", - "as", - "asc", - "ascending", - "at", - "auto", - "avg", - "before", - "begin", - "between", - "bigint", - "bit_length", - "blob", - "both", - "by", - "case", - "cast", - "char", - "character", - "character_length", - "char_length", - "check", - "close", - "collate", - "column", - "commit", - "committed", - "computed", - "conditional", - "connect", - "constraint", - "containing", - "count", - "create", - "cross", - "cstring", - "current", - "current_connection", - "current_date", - "current_role", - "current_time", - "current_timestamp", - "current_transaction", - "current_user", - "cursor", - "database", - "date", - "day", - "dec", - "decimal", - "declare", - "default", - "delete", - "desc", - "descending", - "disconnect", - "distinct", - "do", - "domain", - "double", - "drop", - "else", - "end", - "entry_point", - "escape", - "exception", - "execute", - "exists", - "exit", - "external", - "extract", - "fetch", - "file", - "filter", - "float", - "for", - "foreign", - "from", - "full", - "function", - "gdscode", - "generator", - "gen_id", - "global", - "grant", - "group", - "having", - "hour", - "if", - "in", - "inactive", - "index", - "inner", - "input_type", - "insensitive", - "insert", - "int", - "integer", - "into", - "is", - "isolation", - "join", - "key", - "leading", - "left", - "length", - "level", - "like", - "long", - "lower", - "manual", - "max", - "maximum_segment", - "merge", - "min", - "minute", - "module_name", - "month", - "names", - "national", - "natural", - "nchar", - "no", - "not", - "null", - "numeric", - "octet_length", - "of", - "on", - "only", - "open", - "option", - "or", - "order", - "outer", - "output_type", - "overflow", - "page", - "pages", - "page_size", - "parameter", - "password", - "plan", - "position", - "post_event", - "precision", - "primary", - "privileges", - "procedure", - "protected", - "rdb$db_key", - "read", - "real", - "record_version", - "recreate", - "recursive", - "references", - "release", - "reserv", - "reserving", - "retain", - "returning_values", - "returns", - "revoke", - "right", - "rollback", - "rows", - "row_count", - "savepoint", - "schema", - "second", - "segment", - "select", - "sensitive", - "set", - "shadow", - "shared", - "singular", - "size", - "smallint", - "snapshot", - "some", - "sort", - "sqlcode", - "stability", - "start", - "starting", - "starts", - "statistics", - "sub_type", - "sum", - "suspend", - "table", - "then", - "time", - "timestamp", - "to", - "trailing", - "transaction", - "trigger", - "trim", - "uncommitted", - "union", - "unique", - "update", - "upper", - "user", - "using", - "value", - "values", - "varchar", - "variable", - "varying", - "view", - "wait", - "when", - "where", - "while", - "with", - "work", - "write", - "year", - ] -) - - -class _StringType(sqltypes.String): - """Base for Firebird string types.""" - - def __init__(self, charset=None, **kw): - self.charset = charset - super(_StringType, self).__init__(**kw) - - -class VARCHAR(_StringType, sqltypes.VARCHAR): - """Firebird VARCHAR type""" - - __visit_name__ = "VARCHAR" - - def __init__(self, length=None, **kwargs): - super(VARCHAR, self).__init__(length=length, **kwargs) - - -class CHAR(_StringType, sqltypes.CHAR): - """Firebird CHAR type""" - - __visit_name__ = "CHAR" - - def __init__(self, length=None, **kwargs): - super(CHAR, self).__init__(length=length, **kwargs) - - -class _FBDateTime(sqltypes.DateTime): - def bind_processor(self, dialect): - def process(value): - if type(value) == datetime.date: - return datetime.datetime(value.year, value.month, value.day) - else: - return value - - return process - - -colspecs = {sqltypes.DateTime: _FBDateTime} - -ischema_names = { - "SHORT": SMALLINT, - "LONG": INTEGER, - "QUAD": FLOAT, - "FLOAT": FLOAT, - "DATE": DATE, - "TIME": TIME, - "TEXT": TEXT, - "INT64": BIGINT, - "DOUBLE": FLOAT, - "TIMESTAMP": TIMESTAMP, - "VARYING": VARCHAR, - "CSTRING": CHAR, - "BLOB": BLOB, -} - - -# TODO: date conversion types (should be implemented as _FBDateTime, -# _FBDate, etc. as bind/result functionality is required) - - -class FBTypeCompiler(compiler.GenericTypeCompiler): - def visit_boolean(self, type_, **kw): - return self.visit_SMALLINT(type_, **kw) - - def visit_datetime(self, type_, **kw): - return self.visit_TIMESTAMP(type_, **kw) - - def visit_TEXT(self, type_, **kw): - return "BLOB SUB_TYPE 1" - - def visit_BLOB(self, type_, **kw): - return "BLOB SUB_TYPE 0" - - def _extend_string(self, type_, basic): - charset = getattr(type_, "charset", None) - if charset is None: - return basic - else: - return "%s CHARACTER SET %s" % (basic, charset) - - def visit_CHAR(self, type_, **kw): - basic = super(FBTypeCompiler, self).visit_CHAR(type_, **kw) - return self._extend_string(type_, basic) - - def visit_VARCHAR(self, type_, **kw): - if not type_.length: - raise exc.CompileError( - "VARCHAR requires a length on dialect %s" % self.dialect.name - ) - basic = super(FBTypeCompiler, self).visit_VARCHAR(type_, **kw) - return self._extend_string(type_, basic) - - -class FBCompiler(sql.compiler.SQLCompiler): - """Firebird specific idiosyncrasies""" - - ansi_bind_rules = True - - # def visit_contains_op_binary(self, binary, operator, **kw): - # cant use CONTAINING b.c. it's case insensitive. - - # def visit_not_contains_op_binary(self, binary, operator, **kw): - # cant use NOT CONTAINING b.c. it's case insensitive. - - def visit_now_func(self, fn, **kw): - return "CURRENT_TIMESTAMP" - - def visit_startswith_op_binary(self, binary, operator, **kw): - return "%s STARTING WITH %s" % ( - binary.left._compiler_dispatch(self, **kw), - binary.right._compiler_dispatch(self, **kw), - ) - - def visit_not_startswith_op_binary(self, binary, operator, **kw): - return "%s NOT STARTING WITH %s" % ( - binary.left._compiler_dispatch(self, **kw), - binary.right._compiler_dispatch(self, **kw), - ) - - def visit_mod_binary(self, binary, operator, **kw): - return "mod(%s, %s)" % ( - self.process(binary.left, **kw), - self.process(binary.right, **kw), - ) - - def visit_alias(self, alias, asfrom=False, **kwargs): - if self.dialect._version_two: - return super(FBCompiler, self).visit_alias( - alias, asfrom=asfrom, **kwargs - ) - else: - # Override to not use the AS keyword which FB 1.5 does not like - if asfrom: - alias_name = ( - isinstance(alias.name, expression._truncated_label) - and self._truncated_identifier("alias", alias.name) - or alias.name - ) - - return ( - self.process(alias.element, asfrom=asfrom, **kwargs) - + " " - + self.preparer.format_alias(alias, alias_name) - ) - else: - return self.process(alias.element, **kwargs) - - def visit_substring_func(self, func, **kw): - s = self.process(func.clauses.clauses[0]) - start = self.process(func.clauses.clauses[1]) - if len(func.clauses.clauses) > 2: - length = self.process(func.clauses.clauses[2]) - return "SUBSTRING(%s FROM %s FOR %s)" % (s, start, length) - else: - return "SUBSTRING(%s FROM %s)" % (s, start) - - def visit_length_func(self, function, **kw): - if self.dialect._version_two: - return "char_length" + self.function_argspec(function) - else: - return "strlen" + self.function_argspec(function) - - visit_char_length_func = visit_length_func - - def function_argspec(self, func, **kw): - # TODO: this probably will need to be - # narrowed to a fixed list, some no-arg functions - # may require parens - see similar example in the oracle - # dialect - if func.clauses is not None and len(func.clauses): - return self.process(func.clause_expr, **kw) - else: - return "" - - def default_from(self): - return " FROM rdb$database" - - def visit_sequence(self, seq, **kw): - return "gen_id(%s, 1)" % self.preparer.format_sequence(seq) - - def get_select_precolumns(self, select, **kw): - """Called when building a ``SELECT`` statement, position is just - before column list Firebird puts the limit and offset right - after the ``SELECT``... - """ - - result = "" - if select._limit_clause is not None: - result += "FIRST %s " % self.process(select._limit_clause, **kw) - if select._offset_clause is not None: - result += "SKIP %s " % self.process(select._offset_clause, **kw) - result += super(FBCompiler, self).get_select_precolumns(select, **kw) - return result - - def limit_clause(self, select, **kw): - """Already taken care of in the `get_select_precolumns` method.""" - - return "" - - def returning_clause(self, stmt, returning_cols): - columns = [ - self._label_returning_column(stmt, c) - for c in expression._select_iterables(returning_cols) - ] - - return "RETURNING " + ", ".join(columns) - - -class FBDDLCompiler(sql.compiler.DDLCompiler): - """Firebird syntactic idiosyncrasies""" - - def visit_create_sequence(self, create): - """Generate a ``CREATE GENERATOR`` statement for the sequence.""" - - # no syntax for these - # https://www.firebirdsql.org/manual/generatorguide-sqlsyntax.html - if create.element.start is not None: - raise NotImplementedError( - "Firebird SEQUENCE doesn't support START WITH" - ) - if create.element.increment is not None: - raise NotImplementedError( - "Firebird SEQUENCE doesn't support INCREMENT BY" - ) - - if self.dialect._version_two: - return "CREATE SEQUENCE %s" % self.preparer.format_sequence( - create.element - ) - else: - return "CREATE GENERATOR %s" % self.preparer.format_sequence( - create.element - ) - - def visit_drop_sequence(self, drop): - """Generate a ``DROP GENERATOR`` statement for the sequence.""" - - if self.dialect._version_two: - return "DROP SEQUENCE %s" % self.preparer.format_sequence( - drop.element - ) - else: - return "DROP GENERATOR %s" % self.preparer.format_sequence( - drop.element - ) - - def visit_computed_column(self, generated): - if generated.persisted is not None: - raise exc.CompileError( - "Firebird computed columns do not support a persistence " - "method setting; set the 'persisted' flag to None for " - "Firebird support." - ) - return "GENERATED ALWAYS AS (%s)" % self.sql_compiler.process( - generated.sqltext, include_table=False, literal_binds=True - ) - - -class FBIdentifierPreparer(sql.compiler.IdentifierPreparer): - """Install Firebird specific reserved words.""" - - reserved_words = RESERVED_WORDS - illegal_initial_characters = compiler.ILLEGAL_INITIAL_CHARACTERS.union( - ["_"] - ) - - def __init__(self, dialect): - super(FBIdentifierPreparer, self).__init__(dialect, omit_schema=True) - - -class FBExecutionContext(default.DefaultExecutionContext): - def fire_sequence(self, seq, type_): - """Get the next value from the sequence using ``gen_id()``.""" - - return self._execute_scalar( - "SELECT gen_id(%s, 1) FROM rdb$database" - % self.identifier_preparer.format_sequence(seq), - type_, - ) - - -class FBDialect(default.DefaultDialect): - """Firebird dialect""" - - name = "firebird" - supports_statement_cache = True - - max_identifier_length = 31 - - supports_sequences = True - sequences_optional = False - supports_default_values = True - postfetch_lastrowid = False - - supports_native_boolean = False - - requires_name_normalize = True - supports_empty_insert = False - - statement_compiler = FBCompiler - ddl_compiler = FBDDLCompiler - preparer = FBIdentifierPreparer - type_compiler = FBTypeCompiler - execution_ctx_cls = FBExecutionContext - - colspecs = colspecs - ischema_names = ischema_names - - construct_arguments = [] - - # defaults to dialect ver. 3, - # will be autodetected off upon - # first connect - _version_two = True - - def __init__(self, *args, **kwargs): - util.warn_deprecated( - "The firebird dialect is deprecated and will be removed " - "in a future version. This dialect is superseded by the external " - "dialect https://github.com/pauldex/sqlalchemy-firebird.", - version="1.4", - ) - super(FBDialect, self).__init__(*args, **kwargs) - - def initialize(self, connection): - super(FBDialect, self).initialize(connection) - self._version_two = ( - "firebird" in self.server_version_info - and self.server_version_info >= (2,) - ) or ( - "interbase" in self.server_version_info - and self.server_version_info >= (6,) - ) - - if not self._version_two: - # TODO: whatever other pre < 2.0 stuff goes here - self.ischema_names = ischema_names.copy() - self.ischema_names["TIMESTAMP"] = sqltypes.DATE - self.colspecs = {sqltypes.DateTime: sqltypes.DATE} - - self.implicit_returning = self._version_two and self.__dict__.get( - "implicit_returning", True - ) - - def has_table(self, connection, table_name, schema=None): - """Return ``True`` if the given table exists, ignoring - the `schema`.""" - self._ensure_has_table_connection(connection) - - tblqry = """ - SELECT 1 AS has_table FROM rdb$database - WHERE EXISTS (SELECT rdb$relation_name - FROM rdb$relations - WHERE rdb$relation_name=?) - """ - c = connection.exec_driver_sql( - tblqry, [self.denormalize_name(table_name)] - ) - return c.first() is not None - - def has_sequence(self, connection, sequence_name, schema=None): - """Return ``True`` if the given sequence (generator) exists.""" - - genqry = """ - SELECT 1 AS has_sequence FROM rdb$database - WHERE EXISTS (SELECT rdb$generator_name - FROM rdb$generators - WHERE rdb$generator_name=?) - """ - c = connection.exec_driver_sql( - genqry, [self.denormalize_name(sequence_name)] - ) - return c.first() is not None - - @reflection.cache - def get_table_names(self, connection, schema=None, **kw): - # there are two queries commonly mentioned for this. - # this one, using view_blr, is at the Firebird FAQ among other places: - # https://www.firebirdfaq.org/faq174/ - s = """ - select rdb$relation_name - from rdb$relations - where rdb$view_blr is null - and (rdb$system_flag is null or rdb$system_flag = 0); - """ - - # the other query is this one. It's not clear if there's really - # any difference between these two. This link: - # https://www.alberton.info/firebird_sql_meta_info.html#.Ur3vXfZGni8 - # states them as interchangeable. Some discussion at [ticket:2898] - # SELECT DISTINCT rdb$relation_name - # FROM rdb$relation_fields - # WHERE rdb$system_flag=0 AND rdb$view_context IS NULL - - return [ - self.normalize_name(row[0]) - for row in connection.exec_driver_sql(s) - ] - - @reflection.cache - def get_view_names(self, connection, schema=None, **kw): - # see https://www.firebirdfaq.org/faq174/ - s = """ - select rdb$relation_name - from rdb$relations - where rdb$view_blr is not null - and (rdb$system_flag is null or rdb$system_flag = 0); - """ - return [ - self.normalize_name(row[0]) - for row in connection.exec_driver_sql(s) - ] - - @reflection.cache - def get_view_definition(self, connection, view_name, schema=None, **kw): - qry = """ - SELECT rdb$view_source AS view_source - FROM rdb$relations - WHERE rdb$relation_name=? - """ - rp = connection.exec_driver_sql( - qry, [self.denormalize_name(view_name)] - ) - row = rp.first() - if row: - return row["view_source"] - else: - return None - - @reflection.cache - def get_pk_constraint(self, connection, table_name, schema=None, **kw): - # Query to extract the PK/FK constrained fields of the given table - keyqry = """ - SELECT se.rdb$field_name AS fname - FROM rdb$relation_constraints rc - JOIN rdb$index_segments se ON rc.rdb$index_name=se.rdb$index_name - WHERE rc.rdb$constraint_type=? AND rc.rdb$relation_name=? - """ - tablename = self.denormalize_name(table_name) - # get primary key fields - c = connection.exec_driver_sql(keyqry, ["PRIMARY KEY", tablename]) - pkfields = [self.normalize_name(r["fname"]) for r in c.fetchall()] - return {"constrained_columns": pkfields, "name": None} - - @reflection.cache - def get_column_sequence( - self, connection, table_name, column_name, schema=None, **kw - ): - tablename = self.denormalize_name(table_name) - colname = self.denormalize_name(column_name) - # Heuristic-query to determine the generator associated to a PK field - genqry = """ - SELECT trigdep.rdb$depended_on_name AS fgenerator - FROM rdb$dependencies tabdep - JOIN rdb$dependencies trigdep - ON tabdep.rdb$dependent_name=trigdep.rdb$dependent_name - AND trigdep.rdb$depended_on_type=14 - AND trigdep.rdb$dependent_type=2 - JOIN rdb$triggers trig ON - trig.rdb$trigger_name=tabdep.rdb$dependent_name - WHERE tabdep.rdb$depended_on_name=? - AND tabdep.rdb$depended_on_type=0 - AND trig.rdb$trigger_type=1 - AND tabdep.rdb$field_name=? - AND (SELECT count(*) - FROM rdb$dependencies trigdep2 - WHERE trigdep2.rdb$dependent_name = trigdep.rdb$dependent_name) = 2 - """ - genr = connection.exec_driver_sql(genqry, [tablename, colname]).first() - if genr is not None: - return dict(name=self.normalize_name(genr["fgenerator"])) - - @reflection.cache - def get_columns(self, connection, table_name, schema=None, **kw): - # Query to extract the details of all the fields of the given table - tblqry = """ - SELECT r.rdb$field_name AS fname, - r.rdb$null_flag AS null_flag, - t.rdb$type_name AS ftype, - f.rdb$field_sub_type AS stype, - f.rdb$field_length/ - COALESCE(cs.rdb$bytes_per_character,1) AS flen, - f.rdb$field_precision AS fprec, - f.rdb$field_scale AS fscale, - COALESCE(r.rdb$default_source, - f.rdb$default_source) AS fdefault - FROM rdb$relation_fields r - JOIN rdb$fields f ON r.rdb$field_source=f.rdb$field_name - JOIN rdb$types t - ON t.rdb$type=f.rdb$field_type AND - t.rdb$field_name='RDB$FIELD_TYPE' - LEFT JOIN rdb$character_sets cs ON - f.rdb$character_set_id=cs.rdb$character_set_id - WHERE f.rdb$system_flag=0 AND r.rdb$relation_name=? - ORDER BY r.rdb$field_position - """ - # get the PK, used to determine the eventual associated sequence - pk_constraint = self.get_pk_constraint(connection, table_name) - pkey_cols = pk_constraint["constrained_columns"] - - tablename = self.denormalize_name(table_name) - # get all of the fields for this table - c = connection.exec_driver_sql(tblqry, [tablename]) - cols = [] - while True: - row = c.fetchone() - if row is None: - break - name = self.normalize_name(row["fname"]) - orig_colname = row["fname"] - - # get the data type - colspec = row["ftype"].rstrip() - coltype = self.ischema_names.get(colspec) - if coltype is None: - util.warn( - "Did not recognize type '%s' of column '%s'" - % (colspec, name) - ) - coltype = sqltypes.NULLTYPE - elif issubclass(coltype, Integer) and row["fprec"] != 0: - coltype = NUMERIC( - precision=row["fprec"], scale=row["fscale"] * -1 - ) - elif colspec in ("VARYING", "CSTRING"): - coltype = coltype(row["flen"]) - elif colspec == "TEXT": - coltype = TEXT(row["flen"]) - elif colspec == "BLOB": - if row["stype"] == 1: - coltype = TEXT() - else: - coltype = BLOB() - else: - coltype = coltype() - - # does it have a default value? - defvalue = None - if row["fdefault"] is not None: - # the value comes down as "DEFAULT 'value'": there may be - # more than one whitespace around the "DEFAULT" keyword - # and it may also be lower case - # (see also https://tracker.firebirdsql.org/browse/CORE-356) - defexpr = row["fdefault"].lstrip() - assert defexpr[:8].rstrip().upper() == "DEFAULT", ( - "Unrecognized default value: %s" % defexpr - ) - defvalue = defexpr[8:].strip() - if defvalue == "NULL": - # Redundant - defvalue = None - col_d = { - "name": name, - "type": coltype, - "nullable": not bool(row["null_flag"]), - "default": defvalue, - "autoincrement": "auto", - } - - if orig_colname.lower() == orig_colname: - col_d["quote"] = True - - # if the PK is a single field, try to see if its linked to - # a sequence thru a trigger - if len(pkey_cols) == 1 and name == pkey_cols[0]: - seq_d = self.get_column_sequence(connection, tablename, name) - if seq_d is not None: - col_d["sequence"] = seq_d - - cols.append(col_d) - return cols - - @reflection.cache - def get_foreign_keys(self, connection, table_name, schema=None, **kw): - # Query to extract the details of each UK/FK of the given table - fkqry = """ - SELECT rc.rdb$constraint_name AS cname, - cse.rdb$field_name AS fname, - ix2.rdb$relation_name AS targetrname, - se.rdb$field_name AS targetfname - FROM rdb$relation_constraints rc - JOIN rdb$indices ix1 ON ix1.rdb$index_name=rc.rdb$index_name - JOIN rdb$indices ix2 ON ix2.rdb$index_name=ix1.rdb$foreign_key - JOIN rdb$index_segments cse ON - cse.rdb$index_name=ix1.rdb$index_name - JOIN rdb$index_segments se - ON se.rdb$index_name=ix2.rdb$index_name - AND se.rdb$field_position=cse.rdb$field_position - WHERE rc.rdb$constraint_type=? AND rc.rdb$relation_name=? - ORDER BY se.rdb$index_name, se.rdb$field_position - """ - tablename = self.denormalize_name(table_name) - - c = connection.exec_driver_sql(fkqry, ["FOREIGN KEY", tablename]) - fks = util.defaultdict( - lambda: { - "name": None, - "constrained_columns": [], - "referred_schema": None, - "referred_table": None, - "referred_columns": [], - } - ) - - for row in c: - cname = self.normalize_name(row["cname"]) - fk = fks[cname] - if not fk["name"]: - fk["name"] = cname - fk["referred_table"] = self.normalize_name(row["targetrname"]) - fk["constrained_columns"].append(self.normalize_name(row["fname"])) - fk["referred_columns"].append( - self.normalize_name(row["targetfname"]) - ) - return list(fks.values()) - - @reflection.cache - def get_indexes(self, connection, table_name, schema=None, **kw): - qry = """ - SELECT ix.rdb$index_name AS index_name, - ix.rdb$unique_flag AS unique_flag, - ic.rdb$field_name AS field_name - FROM rdb$indices ix - JOIN rdb$index_segments ic - ON ix.rdb$index_name=ic.rdb$index_name - LEFT OUTER JOIN rdb$relation_constraints - ON rdb$relation_constraints.rdb$index_name = - ic.rdb$index_name - WHERE ix.rdb$relation_name=? AND ix.rdb$foreign_key IS NULL - AND rdb$relation_constraints.rdb$constraint_type IS NULL - ORDER BY index_name, ic.rdb$field_position - """ - c = connection.exec_driver_sql( - qry, [self.denormalize_name(table_name)] - ) - - indexes = util.defaultdict(dict) - for row in c: - indexrec = indexes[row["index_name"]] - if "name" not in indexrec: - indexrec["name"] = self.normalize_name(row["index_name"]) - indexrec["column_names"] = [] - indexrec["unique"] = bool(row["unique_flag"]) - - indexrec["column_names"].append( - self.normalize_name(row["field_name"]) - ) - - return list(indexes.values()) diff --git a/lib/sqlalchemy/dialects/firebird/fdb.py b/lib/sqlalchemy/dialects/firebird/fdb.py deleted file mode 100644 index 4687809df..000000000 --- a/lib/sqlalchemy/dialects/firebird/fdb.py +++ /dev/null @@ -1,112 +0,0 @@ -# firebird/fdb.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -""" -.. dialect:: firebird+fdb - :name: fdb - :dbapi: pyodbc - :connectstring: firebird+fdb://user:password@host:port/path/to/db[?key=value&key=value...] - :url: https://pypi.org/project/fdb/ - - fdb is a kinterbasdb compatible DBAPI for Firebird. - - .. versionchanged:: 0.9 - The fdb dialect is now the default dialect - under the ``firebird://`` URL space, as ``fdb`` is now the official - Python driver for Firebird. - -Arguments ----------- - -The ``fdb`` dialect is based on the -:mod:`sqlalchemy.dialects.firebird.kinterbasdb` dialect, however does not -accept every argument that Kinterbasdb does. - -* ``enable_rowcount`` - True by default, setting this to False disables - the usage of "cursor.rowcount" with the - Kinterbasdb dialect, which SQLAlchemy ordinarily calls upon automatically - after any UPDATE or DELETE statement. When disabled, SQLAlchemy's - CursorResult 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. Additionally, - cursor.rowcount may not return correct results with older versions - of Firebird, and setting this flag to False will also cause the - SQLAlchemy ORM to ignore its usage. The behavior can also be controlled on a - per-execution basis using the ``enable_rowcount`` option with - :meth:`_engine.Connection.execution_options`:: - - conn = engine.connect().execution_options(enable_rowcount=True) - r = conn.execute(stmt) - print(r.rowcount) - -* ``retaining`` - False by default. Setting this to True will pass the - ``retaining=True`` keyword argument to the ``.commit()`` and ``.rollback()`` - methods of the DBAPI connection, which can improve performance in some - situations, but apparently with significant caveats. - Please read the fdb and/or kinterbasdb DBAPI documentation in order to - understand the implications of this flag. - - .. versionchanged:: 0.9.0 - the ``retaining`` flag defaults to ``False``. - In 0.8 it defaulted to ``True``. - - .. seealso:: - - https://pythonhosted.org/fdb/usage-guide.html#retaining-transactions - - information on the "retaining" flag. - -""" # noqa - -from .kinterbasdb import FBDialect_kinterbasdb -from ... import util - - -class FBDialect_fdb(FBDialect_kinterbasdb): - supports_statement_cache = True - - def __init__(self, enable_rowcount=True, retaining=False, **kwargs): - super(FBDialect_fdb, self).__init__( - enable_rowcount=enable_rowcount, retaining=retaining, **kwargs - ) - - @classmethod - def dbapi(cls): - return __import__("fdb") - - def create_connect_args(self, url): - opts = url.translate_connect_args(username="user") - if opts.get("port"): - opts["host"] = "%s/%s" % (opts["host"], opts["port"]) - del opts["port"] - opts.update(url.query) - - util.coerce_kw_type(opts, "type_conv", int) - - return ([], opts) - - def _get_server_version_info(self, connection): - """Get the version of the Firebird server used by a connection. - - Returns a tuple of (`major`, `minor`, `build`), three integers - representing the version of the attached server. - """ - - # This is the simpler approach (the other uses the services api), - # that for backward compatibility reasons returns a string like - # LI-V6.3.3.12981 Firebird 2.0 - # where the first version is a fake one resembling the old - # Interbase signature. - - isc_info_firebird_version = 103 - fbconn = connection.connection - - version = fbconn.db_info(isc_info_firebird_version) - - return self._parse_version_info(version) - - -dialect = FBDialect_fdb diff --git a/lib/sqlalchemy/dialects/firebird/kinterbasdb.py b/lib/sqlalchemy/dialects/firebird/kinterbasdb.py deleted file mode 100644 index 102222de0..000000000 --- a/lib/sqlalchemy/dialects/firebird/kinterbasdb.py +++ /dev/null @@ -1,202 +0,0 @@ -# firebird/kinterbasdb.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -""" -.. dialect:: firebird+kinterbasdb - :name: kinterbasdb - :dbapi: kinterbasdb - :connectstring: firebird+kinterbasdb://user:password@host:port/path/to/db[?key=value&key=value...] - :url: https://firebirdsql.org/index.php?op=devel&sub=python - -Arguments ----------- - -The Kinterbasdb backend accepts the ``enable_rowcount`` and ``retaining`` -arguments accepted by the :mod:`sqlalchemy.dialects.firebird.fdb` dialect. -In addition, it also accepts the following: - -* ``type_conv`` - select the kind of mapping done on the types: by default - SQLAlchemy uses 200 with Unicode, datetime and decimal support. See - the linked documents below for further information. - -* ``concurrency_level`` - set the backend policy with regards to threading - issues: by default SQLAlchemy uses policy 1. See the linked documents - below for further information. - -.. seealso:: - - https://sourceforge.net/projects/kinterbasdb - - https://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_param_conv_dynamic_type_translation - - https://kinterbasdb.sourceforge.net/dist_docs/usage.html#special_issue_concurrency - -""" # noqa - -import decimal -from re import match - -from .base import FBDialect -from .base import FBExecutionContext -from ... import types as sqltypes -from ... import util - - -class _kinterbasdb_numeric(object): - def bind_processor(self, dialect): - def process(value): - if isinstance(value, decimal.Decimal): - return str(value) - else: - return value - - return process - - -class _FBNumeric_kinterbasdb(_kinterbasdb_numeric, sqltypes.Numeric): - pass - - -class _FBFloat_kinterbasdb(_kinterbasdb_numeric, sqltypes.Float): - pass - - -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_statement_cache = True - supports_sane_rowcount = False - supports_sane_multi_rowcount = False - execution_ctx_cls = FBExecutionContext_kinterbasdb - - supports_native_decimal = True - - colspecs = util.update_copy( - FBDialect.colspecs, - { - sqltypes.Numeric: _FBNumeric_kinterbasdb, - sqltypes.Float: _FBFloat_kinterbasdb, - }, - ) - - def __init__( - self, - type_conv=200, - concurrency_level=1, - enable_rowcount=True, - retaining=False, - **kwargs - ): - super(FBDialect_kinterbasdb, self).__init__(**kwargs) - self.enable_rowcount = enable_rowcount - self.type_conv = type_conv - self.concurrency_level = concurrency_level - self.retaining = retaining - if enable_rowcount: - self.supports_sane_rowcount = True - - @classmethod - def dbapi(cls): - return __import__("kinterbasdb") - - def do_execute(self, cursor, statement, parameters, context=None): - # kinterbase does not accept a None, but wants an empty list - # when there are no arguments. - cursor.execute(statement, parameters or []) - - def do_rollback(self, dbapi_connection): - dbapi_connection.rollback(self.retaining) - - def do_commit(self, dbapi_connection): - dbapi_connection.commit(self.retaining) - - def create_connect_args(self, url): - opts = url.translate_connect_args(username="user") - if opts.get("port"): - opts["host"] = "%s/%s" % (opts["host"], opts["port"]) - del opts["port"] - opts.update(url.query) - - util.coerce_kw_type(opts, "type_conv", int) - - type_conv = opts.pop("type_conv", self.type_conv) - concurrency_level = opts.pop( - "concurrency_level", self.concurrency_level - ) - - if self.dbapi is not None: - initialized = getattr(self.dbapi, "initialized", None) - if initialized is None: - # CVS rev 1.96 changed the name of the attribute: - # https://kinterbasdb.cvs.sourceforge.net/viewvc/kinterbasdb/ - # Kinterbasdb-3.0/__init__.py?r1=1.95&r2=1.96 - initialized = getattr(self.dbapi, "_initialized", False) - if not initialized: - self.dbapi.init( - type_conv=type_conv, concurrency_level=concurrency_level - ) - return ([], opts) - - def _get_server_version_info(self, connection): - """Get the version of the Firebird server used by a connection. - - Returns a tuple of (`major`, `minor`, `build`), three integers - representing the version of the attached server. - """ - - # This is the simpler approach (the other uses the services api), - # that for backward compatibility reasons returns a string like - # LI-V6.3.3.12981 Firebird 2.0 - # where the first version is a fake one resembling the old - # Interbase signature. - - fbconn = connection.connection - version = fbconn.server_version - - return self._parse_version_info(version) - - def _parse_version_info(self, version): - m = match( - r"\w+-V(\d+)\.(\d+)\.(\d+)\.(\d+)( \w+ (\d+)\.(\d+))?", version - ) - if not m: - raise AssertionError( - "Could not determine version from string '%s'" % version - ) - - if m.group(5) != None: - return tuple([int(x) for x in m.group(6, 7, 4)] + ["firebird"]) - else: - return tuple([int(x) for x in m.group(1, 2, 3)] + ["interbase"]) - - def is_disconnect(self, e, connection, cursor): - if isinstance( - e, (self.dbapi.OperationalError, self.dbapi.ProgrammingError) - ): - msg = str(e) - return ( - "Error writing data to the connection" in msg - or "Unable to complete network request to host" in msg - or "Invalid connection state" in msg - or "Invalid cursor state" in msg - or "connection shutdown" in msg - ) - else: - return False - - -dialect = FBDialect_kinterbasdb diff --git a/lib/sqlalchemy/dialects/mssql/__init__.py b/lib/sqlalchemy/dialects/mssql/__init__.py index 3aa1e344a..7d32b3d50 100644 --- a/lib/sqlalchemy/dialects/mssql/__init__.py +++ b/lib/sqlalchemy/dialects/mssql/__init__.py @@ -6,7 +6,6 @@ # the MIT License: https://www.opensource.org/licenses/mit-license.php from . import base # noqa -from . import mxodbc # noqa from . import pymssql # noqa from . import pyodbc # noqa from .base import BIGINT diff --git a/lib/sqlalchemy/dialects/mssql/mxodbc.py b/lib/sqlalchemy/dialects/mssql/mxodbc.py deleted file mode 100644 index 3f3fe4ed1..000000000 --- a/lib/sqlalchemy/dialects/mssql/mxodbc.py +++ /dev/null @@ -1,150 +0,0 @@ -# mssql/mxodbc.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -""" -.. dialect:: mssql+mxodbc - :name: mxODBC - :dbapi: mxodbc - :connectstring: mssql+mxodbc://<username>:<password>@<dsnname> - :url: https://www.egenix.com/ - -.. deprecated:: 1.4 The mxODBC DBAPI is deprecated and will be removed - in a future version. Please use one of the supported DBAPIs to - connect to mssql. - -Execution Modes ---------------- - -mxODBC features two styles of statement execution, using the -``cursor.execute()`` and ``cursor.executedirect()`` methods (the second being -an extension to the DBAPI specification). The former makes use of a particular -API call specific to the SQL Server Native Client ODBC driver known -SQLDescribeParam, while the latter does not. - -mxODBC apparently only makes repeated use of a single prepared statement -when SQLDescribeParam is used. The advantage to prepared statement reuse is -one of performance. The disadvantage is that SQLDescribeParam has a limited -set of scenarios in which bind parameters are understood, including that they -cannot be placed within the argument lists of function calls, anywhere outside -the FROM, or even within subqueries within the FROM clause - making the usage -of bind parameters within SELECT statements impossible for all but the most -simplistic statements. - -For this reason, the mxODBC dialect uses the "native" mode by default only for -INSERT, UPDATE, and DELETE statements, and uses the escaped string mode for -all other statements. - -This behavior can be controlled via -:meth:`~sqlalchemy.sql.expression.Executable.execution_options` using the -``native_odbc_execute`` flag with a value of ``True`` or ``False``, where a -value of ``True`` will unconditionally use native bind parameters and a value -of ``False`` will unconditionally use string-escaped parameters. - -""" - - -from .base import _MSDate -from .base import _MSDateTime -from .base import _MSTime -from .base import MSDialect -from .base import VARBINARY -from .pyodbc import _MSNumeric_pyodbc -from .pyodbc import MSExecutionContext_pyodbc -from ... import types as sqltypes -from ...connectors.mxodbc import MxODBCConnector - - -class _MSNumeric_mxodbc(_MSNumeric_pyodbc): - """Include pyodbc's numeric processor.""" - - -class _MSDate_mxodbc(_MSDate): - def bind_processor(self, dialect): - def process(value): - if value is not None: - return "%s-%s-%s" % (value.year, value.month, value.day) - else: - return None - - return process - - -class _MSTime_mxodbc(_MSTime): - def bind_processor(self, dialect): - def process(value): - if value is not None: - return "%s:%s:%s" % (value.hour, value.minute, value.second) - else: - return None - - return process - - -class _VARBINARY_mxodbc(VARBINARY): - - """ - mxODBC Support for VARBINARY column types. - - This handles the special case for null VARBINARY values, - which maps None values to the mx.ODBC.Manager.BinaryNull symbol. - """ - - def bind_processor(self, dialect): - if dialect.dbapi is None: - return None - - DBAPIBinary = dialect.dbapi.Binary - - def process(value): - if value is not None: - return DBAPIBinary(value) - else: - # should pull from mx.ODBC.Manager.BinaryNull - return dialect.dbapi.BinaryNull - - return process - - -class MSExecutionContext_mxodbc(MSExecutionContext_pyodbc): - """ - The pyodbc execution context is useful for enabling - SELECT SCOPE_IDENTITY in cases where OUTPUT clause - does not work (tables with insert triggers). - """ - - # todo - investigate whether the pyodbc execution context - # is really only being used in cases where OUTPUT - # won't work. - - -class MSDialect_mxodbc(MxODBCConnector, MSDialect): - - # this is only needed if "native ODBC" mode is used, - # which is now disabled by default. - # statement_compiler = MSSQLStrictCompiler - supports_statement_cache = True - - execution_ctx_cls = MSExecutionContext_mxodbc - - # flag used by _MSNumeric_mxodbc - _need_decimal_fix = True - - colspecs = { - sqltypes.Numeric: _MSNumeric_mxodbc, - sqltypes.DateTime: _MSDateTime, - sqltypes.Date: _MSDate_mxodbc, - sqltypes.Time: _MSTime_mxodbc, - VARBINARY: _VARBINARY_mxodbc, - sqltypes.LargeBinary: _VARBINARY_mxodbc, - } - - def __init__(self, description_encoding=None, **params): - super(MSDialect_mxodbc, self).__init__(**params) - self.description_encoding = description_encoding - - -dialect = MSDialect_mxodbc diff --git a/lib/sqlalchemy/dialects/mysql/__init__.py b/lib/sqlalchemy/dialects/mysql/__init__.py index c83fec0c3..9fe6f6d84 100644 --- a/lib/sqlalchemy/dialects/mysql/__init__.py +++ b/lib/sqlalchemy/dialects/mysql/__init__.py @@ -10,7 +10,6 @@ from . import cymysql # noqa from . import mariadbconnector # noqa from . import mysqlconnector # noqa from . import mysqldb # noqa -from . import oursql # noqa from . import pymysql # noqa from . import pyodbc # noqa from .base import BIGINT diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py index ad38fee97..4827df12f 100644 --- a/lib/sqlalchemy/dialects/mysql/base.py +++ b/lib/sqlalchemy/dialects/mysql/base.py @@ -1559,12 +1559,11 @@ class MySQLCompiler(compiler.SQLCompiler): # artificial limit if one wasn't provided # https://dev.mysql.com/doc/refman/5.0/en/select.html if limit_clause is None: + # TODO: remove ?? # hardwire the upper limit. Currently - # needed by OurSQL with Python 3 - # (https://bugs.launchpad.net/oursql/+bug/686232), - # but also is consistent with the usage of the upper + # needed consistent with the usage of the upper # bound as part of MySQL's "syntax" for OFFSET with - # no LIMIT + # no LIMIT. return " \n LIMIT %s, %s" % ( self.process(offset_clause, **kw), "18446744073709551615", diff --git a/lib/sqlalchemy/dialects/mysql/oursql.py b/lib/sqlalchemy/dialects/mysql/oursql.py deleted file mode 100644 index 6ec7ce9b9..000000000 --- a/lib/sqlalchemy/dialects/mysql/oursql.py +++ /dev/null @@ -1,273 +0,0 @@ -# mysql/oursql.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -""" - -.. dialect:: mysql+oursql - :name: OurSQL - :dbapi: oursql - :connectstring: mysql+oursql://<user>:<password>@<host>[:<port>]/<dbname> - :url: https://packages.python.org/oursql/ - -.. note:: - - The OurSQL MySQL dialect is legacy and is no longer supported upstream, - and is **not tested as part of SQLAlchemy's continuous integration**. - The recommended MySQL dialects are mysqlclient and PyMySQL. - -.. deprecated:: 1.4 The OurSQL DBAPI is deprecated and will be removed - in a future version. Please use one of the supported DBAPIs to - connect to mysql. - -Unicode -------- - -Please see :ref:`mysql_unicode` for current recommendations on unicode -handling. - - -""" - - -from .base import BIT -from .base import MySQLDialect -from .base import MySQLExecutionContext -from ... import types as sqltypes -from ... import util - - -class _oursqlBIT(BIT): - def result_processor(self, dialect, coltype): - """oursql already converts mysql bits, so.""" - - return None - - -class MySQLExecutionContext_oursql(MySQLExecutionContext): - @property - def plain_query(self): - return self.execution_options.get("_oursql_plain_query", False) - - -class MySQLDialect_oursql(MySQLDialect): - driver = "oursql" - supports_statement_cache = True - - if util.py2k: - supports_unicode_binds = True - supports_unicode_statements = True - - supports_native_decimal = True - - supports_sane_rowcount = True - supports_sane_multi_rowcount = True - execution_ctx_cls = MySQLExecutionContext_oursql - - colspecs = util.update_copy( - MySQLDialect.colspecs, {sqltypes.Time: sqltypes.Time, BIT: _oursqlBIT} - ) - - @classmethod - def dbapi(cls): - util.warn_deprecated( - "The OurSQL DBAPI is deprecated and will be removed " - "in a future version. Please use one of the supported DBAPIs to " - "connect to mysql.", - version="1.4", - ) - return __import__("oursql") - - def do_execute(self, cursor, statement, parameters, context=None): - """Provide an implementation of - *cursor.execute(statement, parameters)*.""" - - if context and context.plain_query: - cursor.execute(statement, plain_query=True) - else: - cursor.execute(statement, parameters) - - def do_begin(self, connection): - connection.cursor().execute("BEGIN", plain_query=True) - - def _xa_query(self, connection, query, xid): - if util.py2k: - arg = connection.connection._escape_string(xid) - else: - charset = self._connection_charset - arg = connection.connection._escape_string( - xid.encode(charset) - ).decode(charset) - arg = "'%s'" % arg - connection.execution_options(_oursql_plain_query=True).exec_driver_sql( - query % arg - ) - - # Because mysql is bad, these methods have to be - # reimplemented to use _PlainQuery. Basically, some queries - # refuse to return any data if they're run through - # the parameterized query API, or refuse to be parameterized - # in the first place. - def do_begin_twophase(self, connection, xid): - self._xa_query(connection, "XA BEGIN %s", xid) - - def do_prepare_twophase(self, connection, xid): - self._xa_query(connection, "XA END %s", xid) - self._xa_query(connection, "XA PREPARE %s", xid) - - def do_rollback_twophase( - self, connection, xid, is_prepared=True, recover=False - ): - if not is_prepared: - self._xa_query(connection, "XA END %s", xid) - self._xa_query(connection, "XA ROLLBACK %s", xid) - - def do_commit_twophase( - self, connection, xid, is_prepared=True, recover=False - ): - if not is_prepared: - self.do_prepare_twophase(connection, xid) - self._xa_query(connection, "XA COMMIT %s", xid) - - # Q: why didn't we need all these "plain_query" overrides earlier ? - # am i on a newer/older version of OurSQL ? - def has_table(self, connection, table_name, schema=None): - return MySQLDialect.has_table( - self, - connection.connect().execution_options(_oursql_plain_query=True), - table_name, - schema, - ) - - def get_table_options(self, connection, table_name, schema=None, **kw): - return MySQLDialect.get_table_options( - self, - connection.connect().execution_options(_oursql_plain_query=True), - table_name, - schema=schema, - **kw - ) - - def get_columns(self, connection, table_name, schema=None, **kw): - return MySQLDialect.get_columns( - self, - connection.connect().execution_options(_oursql_plain_query=True), - table_name, - schema=schema, - **kw - ) - - def get_view_names(self, connection, schema=None, **kw): - return MySQLDialect.get_view_names( - self, - connection.connect().execution_options(_oursql_plain_query=True), - schema=schema, - **kw - ) - - def get_table_names(self, connection, schema=None, **kw): - return MySQLDialect.get_table_names( - self, - connection.connect().execution_options(_oursql_plain_query=True), - schema, - ) - - def get_schema_names(self, connection, **kw): - return MySQLDialect.get_schema_names( - self, - connection.connect().execution_options(_oursql_plain_query=True), - **kw - ) - - def initialize(self, connection): - return MySQLDialect.initialize( - self, connection.execution_options(_oursql_plain_query=True) - ) - - def _show_create_table( - self, connection, table, charset=None, full_name=None - ): - return MySQLDialect._show_create_table( - self, - connection.connect(close_with_result=True).execution_options( - _oursql_plain_query=True - ), - table, - charset, - full_name, - ) - - def is_disconnect(self, e, connection, cursor): - if isinstance(e, self.dbapi.ProgrammingError): - return ( - e.errno is None - and "cursor" not in e.args[1] - and e.args[1].endswith("closed") - ) - else: - return e.errno in (2006, 2013, 2014, 2045, 2055) - - def create_connect_args(self, url): - opts = url.translate_connect_args( - database="db", username="user", password="passwd" - ) - opts.update(url.query) - - util.coerce_kw_type(opts, "port", int) - util.coerce_kw_type(opts, "compress", bool) - util.coerce_kw_type(opts, "autoping", bool) - util.coerce_kw_type(opts, "raise_on_warnings", bool) - - util.coerce_kw_type(opts, "default_charset", bool) - if opts.pop("default_charset", False): - opts["charset"] = None - else: - util.coerce_kw_type(opts, "charset", str) - opts["use_unicode"] = opts.get("use_unicode", True) - util.coerce_kw_type(opts, "use_unicode", bool) - - # FOUND_ROWS must be set in CLIENT_FLAGS to enable - # supports_sane_rowcount. - opts.setdefault("found_rows", True) - - ssl = {} - for key in [ - "ssl_ca", - "ssl_key", - "ssl_cert", - "ssl_capath", - "ssl_cipher", - ]: - if key in opts: - ssl[key[4:]] = opts[key] - util.coerce_kw_type(ssl, key[4:], str) - del opts[key] - if ssl: - opts["ssl"] = ssl - - return [[], opts] - - def _extract_error_code(self, exception): - return exception.errno - - def _detect_charset(self, connection): - """Sniff out the character set in use for connection results.""" - - return connection.connection.charset - - def _compat_fetchall(self, rp, charset=None): - """oursql isn't super-broken like MySQLdb, yaaay.""" - return rp.fetchall() - - def _compat_fetchone(self, rp, charset=None): - """oursql isn't super-broken like MySQLdb, yaaay.""" - return rp.fetchone() - - def _compat_first(self, rp, charset=None): - return rp.first() - - -dialect = MySQLDialect_oursql diff --git a/lib/sqlalchemy/dialects/postgresql/__init__.py b/lib/sqlalchemy/dialects/postgresql/__init__.py index 0de84e579..056de66be 100644 --- a/lib/sqlalchemy/dialects/postgresql/__init__.py +++ b/lib/sqlalchemy/dialects/postgresql/__init__.py @@ -8,8 +8,6 @@ from . import base from . import pg8000 # noqa from . import psycopg2 # noqa from . import psycopg2cffi # noqa -from . import pygresql # noqa -from . import pypostgresql # noqa from .array import All from .array import Any from .array import ARRAY diff --git a/lib/sqlalchemy/dialects/postgresql/pygresql.py b/lib/sqlalchemy/dialects/postgresql/pygresql.py deleted file mode 100644 index 42ef3c31e..000000000 --- a/lib/sqlalchemy/dialects/postgresql/pygresql.py +++ /dev/null @@ -1,278 +0,0 @@ -# postgresql/pygresql.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -""" -.. dialect:: postgresql+pygresql - :name: pygresql - :dbapi: pgdb - :connectstring: postgresql+pygresql://user:password@host:port/dbname[?key=value&key=value...] - :url: https://www.pygresql.org/ - -.. note:: - - The pygresql dialect is **not tested as part of SQLAlchemy's continuous - integration** and may have unresolved issues. The recommended PostgreSQL - dialect is psycopg2. - -.. deprecated:: 1.4 The pygresql DBAPI is deprecated and will be removed - in a future version. Please use one of the supported DBAPIs to - connect to PostgreSQL. - -""" # noqa - -import decimal -import re - -from .base import _DECIMAL_TYPES -from .base import _FLOAT_TYPES -from .base import _INT_TYPES -from .base import PGCompiler -from .base import PGDialect -from .base import PGIdentifierPreparer -from .base import UUID -from .hstore import HSTORE -from .json import JSON -from .json import JSONB -from ... import exc -from ... import processors -from ... import util -from ...sql.elements import Null -from ...types import JSON as Json -from ...types import Numeric - - -class _PGNumeric(Numeric): - def bind_processor(self, dialect): - return None - - def result_processor(self, dialect, coltype): - if not isinstance(coltype, int): - coltype = coltype.oid - if self.asdecimal: - if coltype in _FLOAT_TYPES: - return processors.to_decimal_processor_factory( - decimal.Decimal, self._effective_decimal_return_scale - ) - elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: - # PyGreSQL returns Decimal natively for 1700 (numeric) - return None - else: - raise exc.InvalidRequestError( - "Unknown PG numeric type: %d" % coltype - ) - else: - if coltype in _FLOAT_TYPES: - # PyGreSQL returns float natively for 701 (float8) - return None - elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES: - return processors.to_float - else: - raise exc.InvalidRequestError( - "Unknown PG numeric type: %d" % coltype - ) - - -class _PGHStore(HSTORE): - def bind_processor(self, dialect): - if not dialect.has_native_hstore: - return super(_PGHStore, self).bind_processor(dialect) - hstore = dialect.dbapi.Hstore - - def process(value): - if isinstance(value, dict): - return hstore(value) - return value - - return process - - def result_processor(self, dialect, coltype): - if not dialect.has_native_hstore: - return super(_PGHStore, self).result_processor(dialect, coltype) - - -class _PGJSON(JSON): - def bind_processor(self, dialect): - if not dialect.has_native_json: - return super(_PGJSON, self).bind_processor(dialect) - json = dialect.dbapi.Json - - def process(value): - if value is self.NULL: - value = None - elif isinstance(value, Null) or ( - value is None and self.none_as_null - ): - return None - if value is None or isinstance(value, (dict, list)): - return json(value) - return value - - return process - - def result_processor(self, dialect, coltype): - if not dialect.has_native_json: - return super(_PGJSON, self).result_processor(dialect, coltype) - - -class _PGJSONB(JSONB): - def bind_processor(self, dialect): - if not dialect.has_native_json: - return super(_PGJSONB, self).bind_processor(dialect) - json = dialect.dbapi.Json - - def process(value): - if value is self.NULL: - value = None - elif isinstance(value, Null) or ( - value is None and self.none_as_null - ): - return None - if value is None or isinstance(value, (dict, list)): - return json(value) - return value - - return process - - def result_processor(self, dialect, coltype): - if not dialect.has_native_json: - return super(_PGJSONB, self).result_processor(dialect, coltype) - - -class _PGUUID(UUID): - def bind_processor(self, dialect): - if not dialect.has_native_uuid: - return super(_PGUUID, self).bind_processor(dialect) - uuid = dialect.dbapi.Uuid - - def process(value): - if value is None: - return None - if isinstance(value, (str, bytes)): - if len(value) == 16: - return uuid(bytes=value) - return uuid(value) - if isinstance(value, int): - return uuid(int=value) - return value - - return process - - def result_processor(self, dialect, coltype): - if not dialect.has_native_uuid: - return super(_PGUUID, self).result_processor(dialect, coltype) - if not self.as_uuid: - - def process(value): - if value is not None: - return str(value) - - return process - - -class _PGCompiler(PGCompiler): - def visit_mod_binary(self, binary, operator, **kw): - return ( - self.process(binary.left, **kw) - + " %% " - + self.process(binary.right, **kw) - ) - - def post_process_text(self, text): - return text.replace("%", "%%") - - -class _PGIdentifierPreparer(PGIdentifierPreparer): - def _escape_identifier(self, value): - value = value.replace(self.escape_quote, self.escape_to_quote) - return value.replace("%", "%%") - - -class PGDialect_pygresql(PGDialect): - - driver = "pygresql" - supports_statement_cache = True - - statement_compiler = _PGCompiler - preparer = _PGIdentifierPreparer - - @classmethod - def dbapi(cls): - import pgdb - - util.warn_deprecated( - "The pygresql DBAPI is deprecated and will be removed " - "in a future version. Please use one of the supported DBAPIs to " - "connect to PostgreSQL.", - version="1.4", - ) - - return pgdb - - colspecs = util.update_copy( - PGDialect.colspecs, - { - Numeric: _PGNumeric, - HSTORE: _PGHStore, - Json: _PGJSON, - JSON: _PGJSON, - JSONB: _PGJSONB, - UUID: _PGUUID, - }, - ) - - def __init__(self, **kwargs): - super(PGDialect_pygresql, self).__init__(**kwargs) - try: - version = self.dbapi.version - m = re.match(r"(\d+)\.(\d+)", version) - version = (int(m.group(1)), int(m.group(2))) - except (AttributeError, ValueError, TypeError): - version = (0, 0) - self.dbapi_version = version - if version < (5, 0): - has_native_hstore = has_native_json = has_native_uuid = False - if version != (0, 0): - util.warn( - "PyGreSQL is only fully supported by SQLAlchemy" - " since version 5.0." - ) - else: - self.supports_unicode_statements = True - self.supports_unicode_binds = True - has_native_hstore = has_native_json = has_native_uuid = True - self.has_native_hstore = has_native_hstore - self.has_native_json = has_native_json - self.has_native_uuid = has_native_uuid - - def create_connect_args(self, url): - opts = url.translate_connect_args(username="user") - if "port" in opts: - opts["host"] = "%s:%s" % ( - opts.get("host", "").rsplit(":", 1)[0], - opts.pop("port"), - ) - opts.update(url.query) - return [], opts - - def is_disconnect(self, e, connection, cursor): - if isinstance(e, self.dbapi.Error): - if not connection: - return False - try: - connection = connection.connection - except AttributeError: - pass - else: - if not connection: - return False - try: - return connection.closed - except AttributeError: # PyGreSQL < 5.0 - return connection._cnx is None - return False - - -dialect = PGDialect_pygresql diff --git a/lib/sqlalchemy/dialects/postgresql/pypostgresql.py b/lib/sqlalchemy/dialects/postgresql/pypostgresql.py deleted file mode 100644 index 1d646df44..000000000 --- a/lib/sqlalchemy/dialects/postgresql/pypostgresql.py +++ /dev/null @@ -1,126 +0,0 @@ -# postgresql/pypostgresql.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -""" -.. dialect:: postgresql+pypostgresql - :name: py-postgresql - :dbapi: pypostgresql - :connectstring: postgresql+pypostgresql://user:password@host:port/dbname[?key=value&key=value...] - :url: https://python.projects.pgfoundry.org/ - -.. note:: - - The pypostgresql dialect is **not tested as part of SQLAlchemy's continuous - integration** and may have unresolved issues. The recommended PostgreSQL - driver is psycopg2. - -.. deprecated:: 1.4 The py-postgresql DBAPI is deprecated and will be removed - in a future version. This DBAPI is superseded by the external - version available at external-dialect_. Please use the external version or - one of the supported DBAPIs to connect to PostgreSQL. - -.. TODO update link -.. _external-dialect: https://github.com/PyGreSQL - -""" # noqa - -from .base import PGDialect -from .base import PGExecutionContext -from ... import processors -from ... import types as sqltypes -from ... import util - - -class PGNumeric(sqltypes.Numeric): - def bind_processor(self, dialect): - return processors.to_str - - def result_processor(self, dialect, coltype): - if self.asdecimal: - return None - else: - return processors.to_float - - -class PGExecutionContext_pypostgresql(PGExecutionContext): - pass - - -class PGDialect_pypostgresql(PGDialect): - driver = "pypostgresql" - - supports_statement_cache = True - supports_unicode_statements = True - supports_unicode_binds = True - description_encoding = None - default_paramstyle = "pyformat" - - # requires trunk version to support sane rowcounts - # TODO: use dbapi version information to set this flag appropriately - supports_sane_rowcount = True - supports_sane_multi_rowcount = False - - execution_ctx_cls = PGExecutionContext_pypostgresql - colspecs = util.update_copy( - PGDialect.colspecs, - { - sqltypes.Numeric: PGNumeric, - # prevents PGNumeric from being used - sqltypes.Float: sqltypes.Float, - }, - ) - - @classmethod - def dbapi(cls): - from postgresql.driver import dbapi20 - - # TODO update link - util.warn_deprecated( - "The py-postgresql DBAPI is deprecated and will be removed " - "in a future version. This DBAPI is superseded by the external" - "version available at https://github.com/PyGreSQL. Please " - "use one of the supported DBAPIs to connect to PostgreSQL.", - version="1.4", - ) - - return dbapi20 - - _DBAPI_ERROR_NAMES = [ - "Error", - "InterfaceError", - "DatabaseError", - "DataError", - "OperationalError", - "IntegrityError", - "InternalError", - "ProgrammingError", - "NotSupportedError", - ] - - @util.memoized_property - def dbapi_exception_translation_map(self): - if self.dbapi is None: - return {} - - return dict( - (getattr(self.dbapi, name).__name__, name) - for name in self._DBAPI_ERROR_NAMES - ) - - def create_connect_args(self, url): - opts = url.translate_connect_args(username="user") - if "port" in opts: - opts["port"] = int(opts["port"]) - else: - opts["port"] = 5432 - opts.update(url.query) - return ([], opts) - - def is_disconnect(self, e, connection, cursor): - return "connection is closed" in str(e) - - -dialect = PGDialect_pypostgresql diff --git a/lib/sqlalchemy/dialects/sybase/__init__.py b/lib/sqlalchemy/dialects/sybase/__init__.py deleted file mode 100644 index 87a90fb06..000000000 --- a/lib/sqlalchemy/dialects/sybase/__init__.py +++ /dev/null @@ -1,67 +0,0 @@ -# sybase/__init__.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -from . import base # noqa -from . import pyodbc # noqa -from . import pysybase # noqa -from .base import BIGINT -from .base import BINARY -from .base import BIT -from .base import CHAR -from .base import DATE -from .base import DATETIME -from .base import FLOAT -from .base import IMAGE -from .base import INT -from .base import INTEGER -from .base import MONEY -from .base import NCHAR -from .base import NUMERIC -from .base import NVARCHAR -from .base import SMALLINT -from .base import SMALLMONEY -from .base import TEXT -from .base import TIME -from .base import TINYINT -from .base import UNICHAR -from .base import UNITEXT -from .base import UNIVARCHAR -from .base import VARBINARY -from .base import VARCHAR - - -# default dialect -base.dialect = dialect = pyodbc.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 deleted file mode 100644 index 120093015..000000000 --- a/lib/sqlalchemy/dialects/sybase/base.py +++ /dev/null @@ -1,1100 +0,0 @@ -# sybase/base.py -# Copyright (C) 2010-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# get_select_precolumns(), limit_clause() implementation -# copyright (C) 2007 Fisch Asset Management -# AG https://www.fam.ch, with coding by Alexander Houben -# alexander.houben@thor-solutions.ch -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -""" - -.. dialect:: sybase - :name: Sybase - -.. note:: - - The Sybase dialect within SQLAlchemy **is not currently supported**. - It is not tested within continuous integration and is likely to have - many issues and caveats not currently handled. Consider using the - `external dialect <https://github.com/gordthompson/sqlalchemy-sybase>`_ - instead. - -.. deprecated:: 1.4 The internal Sybase dialect is deprecated and will be - removed in a future version. Use the external dialect. - -""" - -import re - -from sqlalchemy import exc -from sqlalchemy import schema as sa_schema -from sqlalchemy import types as sqltypes -from sqlalchemy import util -from sqlalchemy.engine import default -from sqlalchemy.engine import reflection -from sqlalchemy.sql import compiler -from sqlalchemy.sql import text -from sqlalchemy.types import BIGINT -from sqlalchemy.types import BINARY -from sqlalchemy.types import CHAR -from sqlalchemy.types import DATE -from sqlalchemy.types import DATETIME -from sqlalchemy.types import DECIMAL -from sqlalchemy.types import FLOAT -from sqlalchemy.types import INT # noqa -from sqlalchemy.types import INTEGER -from sqlalchemy.types import NCHAR -from sqlalchemy.types import NUMERIC -from sqlalchemy.types import NVARCHAR -from sqlalchemy.types import REAL -from sqlalchemy.types import SMALLINT -from sqlalchemy.types import TEXT -from sqlalchemy.types import TIME -from sqlalchemy.types import TIMESTAMP -from sqlalchemy.types import Unicode -from sqlalchemy.types import VARBINARY -from sqlalchemy.types import VARCHAR - - -RESERVED_WORDS = set( - [ - "add", - "all", - "alter", - "and", - "any", - "as", - "asc", - "backup", - "begin", - "between", - "bigint", - "binary", - "bit", - "bottom", - "break", - "by", - "call", - "capability", - "cascade", - "case", - "cast", - "char", - "char_convert", - "character", - "check", - "checkpoint", - "close", - "comment", - "commit", - "connect", - "constraint", - "contains", - "continue", - "convert", - "create", - "cross", - "cube", - "current", - "current_timestamp", - "current_user", - "cursor", - "date", - "dbspace", - "deallocate", - "dec", - "decimal", - "declare", - "default", - "delete", - "deleting", - "desc", - "distinct", - "do", - "double", - "drop", - "dynamic", - "else", - "elseif", - "encrypted", - "end", - "endif", - "escape", - "except", - "exception", - "exec", - "execute", - "existing", - "exists", - "externlogin", - "fetch", - "first", - "float", - "for", - "force", - "foreign", - "forward", - "from", - "full", - "goto", - "grant", - "group", - "having", - "holdlock", - "identified", - "if", - "in", - "index", - "index_lparen", - "inner", - "inout", - "insensitive", - "insert", - "inserting", - "install", - "instead", - "int", - "integer", - "integrated", - "intersect", - "into", - "iq", - "is", - "isolation", - "join", - "key", - "lateral", - "left", - "like", - "lock", - "login", - "long", - "match", - "membership", - "message", - "mode", - "modify", - "natural", - "new", - "no", - "noholdlock", - "not", - "notify", - "null", - "numeric", - "of", - "off", - "on", - "open", - "option", - "options", - "or", - "order", - "others", - "out", - "outer", - "over", - "passthrough", - "precision", - "prepare", - "primary", - "print", - "privileges", - "proc", - "procedure", - "publication", - "raiserror", - "readtext", - "real", - "reference", - "references", - "release", - "remote", - "remove", - "rename", - "reorganize", - "resource", - "restore", - "restrict", - "return", - "revoke", - "right", - "rollback", - "rollup", - "save", - "savepoint", - "scroll", - "select", - "sensitive", - "session", - "set", - "setuser", - "share", - "smallint", - "some", - "sqlcode", - "sqlstate", - "start", - "stop", - "subtrans", - "subtransaction", - "synchronize", - "syntax_error", - "table", - "temporary", - "then", - "time", - "timestamp", - "tinyint", - "to", - "top", - "tran", - "trigger", - "truncate", - "tsequal", - "unbounded", - "union", - "unique", - "unknown", - "unsigned", - "update", - "updating", - "user", - "using", - "validate", - "values", - "varbinary", - "varchar", - "variable", - "varying", - "view", - "wait", - "waitfor", - "when", - "where", - "while", - "window", - "with", - "with_cube", - "with_lparen", - "with_rollup", - "within", - "work", - "writetext", - ] -) - - -class _SybaseUnitypeMixin(object): - """these types appear to return a buffer object.""" - - def result_processor(self, dialect, coltype): - def process(value): - if value is not None: - return str(value) # decode("ucs-2") - else: - return None - - return process - - -class UNICHAR(_SybaseUnitypeMixin, sqltypes.Unicode): - __visit_name__ = "UNICHAR" - - -class UNIVARCHAR(_SybaseUnitypeMixin, sqltypes.Unicode): - __visit_name__ = "UNIVARCHAR" - - -class UNITEXT(_SybaseUnitypeMixin, sqltypes.UnicodeText): - __visit_name__ = "UNITEXT" - - -class TINYINT(sqltypes.Integer): - __visit_name__ = "TINYINT" - - -class BIT(sqltypes.TypeEngine): - __visit_name__ = "BIT" - - -class MONEY(sqltypes.TypeEngine): - __visit_name__ = "MONEY" - - -class SMALLMONEY(sqltypes.TypeEngine): - __visit_name__ = "SMALLMONEY" - - -class UNIQUEIDENTIFIER(sqltypes.TypeEngine): - __visit_name__ = "UNIQUEIDENTIFIER" - - -class IMAGE(sqltypes.LargeBinary): - __visit_name__ = "IMAGE" - - -class SybaseTypeCompiler(compiler.GenericTypeCompiler): - def visit_large_binary(self, type_, **kw): - return self.visit_IMAGE(type_) - - def visit_boolean(self, type_, **kw): - return self.visit_BIT(type_) - - def visit_unicode(self, type_, **kw): - return self.visit_NVARCHAR(type_) - - def visit_UNICHAR(self, type_, **kw): - return "UNICHAR(%d)" % type_.length - - def visit_UNIVARCHAR(self, type_, **kw): - return "UNIVARCHAR(%d)" % type_.length - - def visit_UNITEXT(self, type_, **kw): - return "UNITEXT" - - def visit_TINYINT(self, type_, **kw): - return "TINYINT" - - def visit_IMAGE(self, type_, **kw): - return "IMAGE" - - def visit_BIT(self, type_, **kw): - return "BIT" - - def visit_MONEY(self, type_, **kw): - return "MONEY" - - def visit_SMALLMONEY(self, type_, **kw): - return "SMALLMONEY" - - def visit_UNIQUEIDENTIFIER(self, type_, **kw): - return "UNIQUEIDENTIFIER" - - -ischema_names = { - "bigint": BIGINT, - "int": INTEGER, - "integer": INTEGER, - "smallint": SMALLINT, - "tinyint": TINYINT, - "unsigned bigint": BIGINT, # TODO: unsigned flags - "unsigned int": INTEGER, # TODO: unsigned flags - "unsigned smallint": SMALLINT, # TODO: unsigned flags - "numeric": NUMERIC, - "decimal": DECIMAL, - "dec": DECIMAL, - "float": FLOAT, - "double": NUMERIC, # TODO - "double precision": NUMERIC, # TODO - "real": REAL, - "smallmoney": SMALLMONEY, - "money": MONEY, - "smalldatetime": DATETIME, - "datetime": DATETIME, - "date": DATE, - "time": TIME, - "char": CHAR, - "character": CHAR, - "varchar": VARCHAR, - "character varying": VARCHAR, - "char varying": VARCHAR, - "unichar": UNICHAR, - "unicode character": UNIVARCHAR, - "nchar": NCHAR, - "national char": NCHAR, - "national character": NCHAR, - "nvarchar": NVARCHAR, - "nchar varying": NVARCHAR, - "national char varying": NVARCHAR, - "national character varying": NVARCHAR, - "text": TEXT, - "unitext": UNITEXT, - "binary": BINARY, - "varbinary": VARBINARY, - "image": IMAGE, - "bit": BIT, - # not in documentation for ASE 15.7 - "long varchar": TEXT, # TODO - "timestamp": TIMESTAMP, - "uniqueidentifier": UNIQUEIDENTIFIER, -} - - -class SybaseInspector(reflection.Inspector): - def __init__(self, conn): - reflection.Inspector.__init__(self, conn) - - def get_table_id(self, table_name, schema=None): - """Return the table id from `table_name` and `schema`.""" - - return self.dialect.get_table_id( - self.bind, table_name, schema, info_cache=self.info_cache - ) - - -class SybaseExecutionContext(default.DefaultExecutionContext): - _enable_identity_insert = False - - def set_ddl_autocommit(self, connection, value): - """Must be implemented by subclasses to accommodate DDL executions. - - "connection" is the raw unwrapped DBAPI connection. "value" - is True or False. when True, the connection should be configured - such that a DDL can take place subsequently. when False, - a DDL has taken place and the connection should be resumed - into non-autocommit mode. - - """ - raise NotImplementedError() - - 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) - ) - - 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." - ) - - self.root_connection.engine.logger.info( - "AUTOCOMMIT (Assuming no Sybase 'ddl in tran')" - ) - - self.set_ddl_autocommit( - self.root_connection.connection.connection, True - ) - - def post_exec(self): - if self.isddl: - self.set_ddl_autocommit(self.root_connection, False) - - 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): - ansi_bind_rules = True - - extract_map = util.update_copy( - compiler.SQLCompiler.extract_map, - {"doy": "dayofyear", "dow": "weekday", "milliseconds": "millisecond"}, - ) - - def get_from_hint_text(self, table, text): - return text - - def limit_clause(self, select, **kw): - text = "" - if select._limit_clause is not None: - text += " ROWS LIMIT " + self.process(select._limit_clause, **kw) - if select._offset_clause is not None: - if select._limit_clause is None: - text += " ROWS" - text += " OFFSET " + self.process(select._offset_clause, **kw) - return text - - def visit_extract(self, extract, **kw): - field = self.extract_map.get(extract.field, extract.field) - return 'DATEPART("%s", %s)' % (field, self.process(extract.expr, **kw)) - - def visit_now_func(self, fn, **kw): - return "GETDATE()" - - def for_update_clause(self, select): - # "FOR UPDATE" is only allowed on "DECLARE CURSOR" - # which SQLAlchemy doesn't use - return "" - - def order_by_clause(self, select, **kw): - kw["literal_binds"] = True - order_by = self.process(select._order_by_clause, **kw) - - # SybaseSQL only allows ORDER BY in subqueries if there is a LIMIT - if order_by and (not self.is_subquery() or select._limit): - return " ORDER BY " + order_by - else: - return "" - - def delete_table_clause(self, delete_stmt, from_table, extra_froms): - """If we have extra froms make sure we render any alias as hint.""" - ashint = False - if extra_froms: - ashint = True - return from_table._compiler_dispatch( - self, asfrom=True, iscrud=True, ashint=ashint - ) - - def delete_extra_from_clause( - self, delete_stmt, from_table, extra_froms, from_hints, **kw - ): - """Render the DELETE .. FROM clause specific to Sybase.""" - kw["asfrom"] = True - return "FROM " + ", ".join( - t._compiler_dispatch(self, fromhints=from_hints, **kw) - for t in [from_table] + extra_froms - ) - - -class SybaseDDLCompiler(compiler.DDLCompiler): - def get_column_specification(self, column, **kwargs): - colspec = ( - self.preparer.format_column(column) - + " " - + self.dialect.type_compiler.process( - column.type, type_expression=column - ) - ) - - if column.table is None: - raise exc.CompileError( - "The Sybase dialect requires Table-bound " - "columns in order to generate DDL" - ) - seq_col = column.table._autoincrement_column - - # 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: - default = self.get_column_default_string(column) - if default is not None: - colspec += " DEFAULT " + default - - if column.nullable is not None: - if not column.nullable or column.primary_key: - colspec += " NOT NULL" - else: - colspec += " NULL" - - return colspec - - def visit_drop_index(self, drop): - index = drop.element - return "\nDROP INDEX %s.%s" % ( - self.preparer.quote_identifier(index.table.name), - self._prepared_index_name(drop.element, include_schema=False), - ) - - -class SybaseIdentifierPreparer(compiler.IdentifierPreparer): - reserved_words = RESERVED_WORDS - - -class SybaseDialect(default.DefaultDialect): - name = "sybase" - supports_unicode_statements = False - supports_sane_rowcount = False - supports_sane_multi_rowcount = False - supports_statement_cache = True - - supports_native_boolean = False - supports_unicode_binds = False - postfetch_lastrowid = True - - colspecs = {} - ischema_names = ischema_names - - type_compiler = SybaseTypeCompiler - statement_compiler = SybaseSQLCompiler - ddl_compiler = SybaseDDLCompiler - preparer = SybaseIdentifierPreparer - inspector = SybaseInspector - - construct_arguments = [] - - def __init__(self, *args, **kwargs): - util.warn_deprecated( - "The Sybase dialect is deprecated and will be removed " - "in a future version. This dialect is superseded by the external " - "dialect https://github.com/gordthompson/sqlalchemy-sybase.", - version="1.4", - ) - super(SybaseDialect, self).__init__(*args, **kwargs) - - def _get_default_schema_name(self, connection): - return connection.scalar( - text("SELECT user_name() as user_name").columns(username=Unicode) - ) - - def initialize(self, connection): - super(SybaseDialect, self).initialize(connection) - if ( - self.server_version_info is not None - and self.server_version_info < (15,) - ): - self.max_identifier_length = 30 - else: - self.max_identifier_length = 255 - - def get_table_id(self, connection, table_name, schema=None, **kw): - """Fetch the id for schema.table_name. - - Several reflection methods require the table id. The idea for using - this method is that it can be fetched one time and cached for - subsequent calls. - - """ - - table_id = None - if schema is None: - schema = self.default_schema_name - - TABLEID_SQL = text( - """ - SELECT o.id AS id - FROM sysobjects o JOIN sysusers u ON o.uid=u.uid - WHERE u.name = :schema_name - AND o.name = :table_name - AND o.type in ('U', 'V') - """ - ) - - if util.py2k: - if isinstance(schema, unicode): # noqa - schema = schema.encode("ascii") - if isinstance(table_name, unicode): # noqa - table_name = table_name.encode("ascii") - result = connection.execute( - TABLEID_SQL, schema_name=schema, table_name=table_name - ) - table_id = result.scalar() - if table_id is None: - raise exc.NoSuchTableError(table_name) - return table_id - - @reflection.cache - def get_columns(self, connection, table_name, schema=None, **kw): - table_id = self.get_table_id( - connection, table_name, schema, info_cache=kw.get("info_cache") - ) - - COLUMN_SQL = text( - """ - SELECT col.name AS name, - t.name AS type, - (col.status & 8) AS nullable, - (col.status & 128) AS autoincrement, - com.text AS 'default', - col.prec AS precision, - col.scale AS scale, - col.length AS length - FROM systypes t, syscolumns col LEFT OUTER JOIN syscomments com ON - col.cdefault = com.id - WHERE col.usertype = t.usertype - AND col.id = :table_id - ORDER BY col.colid - """ - ) - - results = connection.execute(COLUMN_SQL, table_id=table_id) - - columns = [] - for ( - name, - type_, - nullable, - autoincrement, - default_, - precision, - scale, - length, - ) in results: - col_info = self._get_column_info( - name, - type_, - bool(nullable), - bool(autoincrement), - default_, - precision, - scale, - length, - ) - columns.append(col_info) - - return columns - - def _get_column_info( - self, - name, - type_, - nullable, - autoincrement, - default, - precision, - scale, - length, - ): - - coltype = self.ischema_names.get(type_, None) - - kwargs = {} - - if coltype in (NUMERIC, DECIMAL): - args = (precision, scale) - elif coltype == FLOAT: - args = (precision,) - elif coltype in (CHAR, VARCHAR, UNICHAR, UNIVARCHAR, NCHAR, NVARCHAR): - args = (length,) - else: - args = () - - if coltype: - coltype = coltype(*args, **kwargs) - # is this necessary - # if is_array: - # coltype = ARRAY(coltype) - else: - util.warn( - "Did not recognize type '%s' of column '%s'" % (type_, name) - ) - coltype = sqltypes.NULLTYPE - - if default: - default = default.replace("DEFAULT", "").strip() - default = re.sub("^'(.*)'$", lambda m: m.group(1), default) - else: - default = None - - column_info = dict( - name=name, - type=coltype, - nullable=nullable, - default=default, - autoincrement=autoincrement, - ) - return column_info - - @reflection.cache - def get_foreign_keys(self, connection, table_name, schema=None, **kw): - - table_id = self.get_table_id( - connection, table_name, schema, info_cache=kw.get("info_cache") - ) - - table_cache = {} - column_cache = {} - foreign_keys = [] - - table_cache[table_id] = {"name": table_name, "schema": schema} - - COLUMN_SQL = text( - """ - SELECT c.colid AS id, c.name AS name - FROM syscolumns c - WHERE c.id = :table_id - """ - ) - - results = connection.execute(COLUMN_SQL, table_id=table_id) - columns = {} - for col in results: - columns[col["id"]] = col["name"] - column_cache[table_id] = columns - - REFCONSTRAINT_SQL = text( - """ - SELECT o.name AS name, r.reftabid AS reftable_id, - r.keycnt AS 'count', - r.fokey1 AS fokey1, r.fokey2 AS fokey2, r.fokey3 AS fokey3, - r.fokey4 AS fokey4, r.fokey5 AS fokey5, r.fokey6 AS fokey6, - r.fokey7 AS fokey7, r.fokey1 AS fokey8, r.fokey9 AS fokey9, - r.fokey10 AS fokey10, r.fokey11 AS fokey11, r.fokey12 AS fokey12, - r.fokey13 AS fokey13, r.fokey14 AS fokey14, r.fokey15 AS fokey15, - r.fokey16 AS fokey16, - r.refkey1 AS refkey1, r.refkey2 AS refkey2, r.refkey3 AS refkey3, - r.refkey4 AS refkey4, r.refkey5 AS refkey5, r.refkey6 AS refkey6, - r.refkey7 AS refkey7, r.refkey1 AS refkey8, r.refkey9 AS refkey9, - r.refkey10 AS refkey10, r.refkey11 AS refkey11, - r.refkey12 AS refkey12, r.refkey13 AS refkey13, - r.refkey14 AS refkey14, r.refkey15 AS refkey15, - r.refkey16 AS refkey16 - FROM sysreferences r JOIN sysobjects o on r.tableid = o.id - WHERE r.tableid = :table_id - """ - ) - referential_constraints = connection.execute( - REFCONSTRAINT_SQL, table_id=table_id - ).fetchall() - - REFTABLE_SQL = text( - """ - SELECT o.name AS name, u.name AS 'schema' - FROM sysobjects o JOIN sysusers u ON o.uid = u.uid - WHERE o.id = :table_id - """ - ) - - for r in referential_constraints: - reftable_id = r["reftable_id"] - - if reftable_id not in table_cache: - c = connection.execute(REFTABLE_SQL, table_id=reftable_id) - reftable = c.fetchone() - c.close() - table_info = {"name": reftable["name"], "schema": None} - if ( - schema is not None - or reftable["schema"] != self.default_schema_name - ): - table_info["schema"] = reftable["schema"] - - table_cache[reftable_id] = table_info - results = connection.execute(COLUMN_SQL, table_id=reftable_id) - reftable_columns = {} - for col in results: - reftable_columns[col["id"]] = col["name"] - column_cache[reftable_id] = reftable_columns - - reftable = table_cache[reftable_id] - reftable_columns = column_cache[reftable_id] - - constrained_columns = [] - referred_columns = [] - for i in range(1, r["count"] + 1): - constrained_columns.append(columns[r["fokey%i" % i]]) - referred_columns.append(reftable_columns[r["refkey%i" % i]]) - - fk_info = { - "constrained_columns": constrained_columns, - "referred_schema": reftable["schema"], - "referred_table": reftable["name"], - "referred_columns": referred_columns, - "name": r["name"], - } - - foreign_keys.append(fk_info) - - return foreign_keys - - @reflection.cache - def get_indexes(self, connection, table_name, schema=None, **kw): - table_id = self.get_table_id( - connection, table_name, schema, info_cache=kw.get("info_cache") - ) - - INDEX_SQL = text( - """ - SELECT object_name(i.id) AS table_name, - i.keycnt AS 'count', - i.name AS name, - (i.status & 0x2) AS 'unique', - index_col(object_name(i.id), i.indid, 1) AS col_1, - index_col(object_name(i.id), i.indid, 2) AS col_2, - index_col(object_name(i.id), i.indid, 3) AS col_3, - index_col(object_name(i.id), i.indid, 4) AS col_4, - index_col(object_name(i.id), i.indid, 5) AS col_5, - index_col(object_name(i.id), i.indid, 6) AS col_6, - index_col(object_name(i.id), i.indid, 7) AS col_7, - index_col(object_name(i.id), i.indid, 8) AS col_8, - index_col(object_name(i.id), i.indid, 9) AS col_9, - index_col(object_name(i.id), i.indid, 10) AS col_10, - index_col(object_name(i.id), i.indid, 11) AS col_11, - index_col(object_name(i.id), i.indid, 12) AS col_12, - index_col(object_name(i.id), i.indid, 13) AS col_13, - index_col(object_name(i.id), i.indid, 14) AS col_14, - index_col(object_name(i.id), i.indid, 15) AS col_15, - index_col(object_name(i.id), i.indid, 16) AS col_16 - FROM sysindexes i, sysobjects o - WHERE o.id = i.id - AND o.id = :table_id - AND (i.status & 2048) = 0 - AND i.indid BETWEEN 1 AND 254 - """ - ) - - results = connection.execute(INDEX_SQL, table_id=table_id) - indexes = [] - for r in results: - column_names = [] - for i in range(1, r["count"]): - column_names.append(r["col_%i" % (i,)]) - index_info = { - "name": r["name"], - "unique": bool(r["unique"]), - "column_names": column_names, - } - indexes.append(index_info) - - return indexes - - @reflection.cache - def get_pk_constraint(self, connection, table_name, schema=None, **kw): - table_id = self.get_table_id( - connection, table_name, schema, info_cache=kw.get("info_cache") - ) - - PK_SQL = text( - """ - SELECT object_name(i.id) AS table_name, - i.keycnt AS 'count', - i.name AS name, - index_col(object_name(i.id), i.indid, 1) AS pk_1, - index_col(object_name(i.id), i.indid, 2) AS pk_2, - index_col(object_name(i.id), i.indid, 3) AS pk_3, - index_col(object_name(i.id), i.indid, 4) AS pk_4, - index_col(object_name(i.id), i.indid, 5) AS pk_5, - index_col(object_name(i.id), i.indid, 6) AS pk_6, - index_col(object_name(i.id), i.indid, 7) AS pk_7, - index_col(object_name(i.id), i.indid, 8) AS pk_8, - index_col(object_name(i.id), i.indid, 9) AS pk_9, - index_col(object_name(i.id), i.indid, 10) AS pk_10, - index_col(object_name(i.id), i.indid, 11) AS pk_11, - index_col(object_name(i.id), i.indid, 12) AS pk_12, - index_col(object_name(i.id), i.indid, 13) AS pk_13, - index_col(object_name(i.id), i.indid, 14) AS pk_14, - index_col(object_name(i.id), i.indid, 15) AS pk_15, - index_col(object_name(i.id), i.indid, 16) AS pk_16 - FROM sysindexes i, sysobjects o - WHERE o.id = i.id - AND o.id = :table_id - AND (i.status & 2048) = 2048 - AND i.indid BETWEEN 1 AND 254 - """ - ) - - results = connection.execute(PK_SQL, table_id=table_id) - pks = results.fetchone() - results.close() - - constrained_columns = [] - if pks: - for i in range(1, pks["count"] + 1): - constrained_columns.append(pks["pk_%i" % (i,)]) - return { - "constrained_columns": constrained_columns, - "name": pks["name"], - } - else: - return {"constrained_columns": [], "name": None} - - @reflection.cache - def get_schema_names(self, connection, **kw): - - SCHEMA_SQL = text("SELECT u.name AS name FROM sysusers u") - - schemas = connection.execute(SCHEMA_SQL) - - return [s["name"] for s in schemas] - - @reflection.cache - def get_table_names(self, connection, schema=None, **kw): - if schema is None: - schema = self.default_schema_name - - TABLE_SQL = text( - """ - SELECT o.name AS name - FROM sysobjects o JOIN sysusers u ON o.uid = u.uid - WHERE u.name = :schema_name - AND o.type = 'U' - """ - ) - - if util.py2k: - if isinstance(schema, unicode): # noqa - schema = schema.encode("ascii") - - tables = connection.execute(TABLE_SQL, schema_name=schema) - - return [t["name"] for t in tables] - - @reflection.cache - def get_view_definition(self, connection, view_name, schema=None, **kw): - if schema is None: - schema = self.default_schema_name - - VIEW_DEF_SQL = text( - """ - SELECT c.text - FROM syscomments c JOIN sysobjects o ON c.id = o.id - WHERE o.name = :view_name - AND o.type = 'V' - """ - ) - - if util.py2k: - if isinstance(view_name, unicode): # noqa - view_name = view_name.encode("ascii") - - view = connection.execute(VIEW_DEF_SQL, view_name=view_name) - - return view.scalar() - - @reflection.cache - def get_view_names(self, connection, schema=None, **kw): - if schema is None: - schema = self.default_schema_name - - VIEW_SQL = text( - """ - SELECT o.name AS name - FROM sysobjects o JOIN sysusers u ON o.uid = u.uid - WHERE u.name = :schema_name - AND o.type = 'V' - """ - ) - - if util.py2k: - if isinstance(schema, unicode): # noqa - schema = schema.encode("ascii") - views = connection.execute(VIEW_SQL, schema_name=schema) - - return [v["name"] for v in views] - - def has_table(self, connection, table_name, schema=None): - self._ensure_has_table_connection(connection) - - try: - self.get_table_id(connection, table_name, schema) - except exc.NoSuchTableError: - return False - else: - return True diff --git a/lib/sqlalchemy/dialects/sybase/mxodbc.py b/lib/sqlalchemy/dialects/sybase/mxodbc.py deleted file mode 100644 index 4e8c8aeab..000000000 --- a/lib/sqlalchemy/dialects/sybase/mxodbc.py +++ /dev/null @@ -1,34 +0,0 @@ -# sybase/mxodbc.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php -""" - -.. dialect:: sybase+mxodbc - :name: mxODBC - :dbapi: mxodbc - :connectstring: sybase+mxodbc://<username>:<password>@<dsnname> - :url: https://www.egenix.com/ - -.. note:: - - This dialect is a stub only and is likely non functional at this time. - -""" -from sqlalchemy.connectors.mxodbc import MxODBCConnector -from sqlalchemy.dialects.sybase.base import SybaseDialect -from sqlalchemy.dialects.sybase.base import SybaseExecutionContext - - -class SybaseExecutionContext_mxodbc(SybaseExecutionContext): - pass - - -class SybaseDialect_mxodbc(MxODBCConnector, SybaseDialect): - execution_ctx_cls = SybaseExecutionContext_mxodbc - supports_statement_cache = True - - -dialect = SybaseDialect_mxodbc diff --git a/lib/sqlalchemy/dialects/sybase/pyodbc.py b/lib/sqlalchemy/dialects/sybase/pyodbc.py deleted file mode 100644 index afc315f26..000000000 --- a/lib/sqlalchemy/dialects/sybase/pyodbc.py +++ /dev/null @@ -1,89 +0,0 @@ -# sybase/pyodbc.py -# Copyright (C) 2005-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -""" -.. dialect:: sybase+pyodbc - :name: PyODBC - :dbapi: pyodbc - :connectstring: sybase+pyodbc://<username>:<password>@<dsnname>[/<database>] - :url: https://pypi.org/project/pyodbc/ - -Unicode Support ---------------- - -The pyodbc driver currently supports usage of these Sybase types with -Unicode or multibyte strings:: - - CHAR - NCHAR - NVARCHAR - TEXT - VARCHAR - -Currently *not* supported are:: - - UNICHAR - UNITEXT - UNIVARCHAR - -""" # noqa - -import decimal - -from sqlalchemy import processors -from sqlalchemy import types as sqltypes -from sqlalchemy.connectors.pyodbc import PyODBCConnector -from sqlalchemy.dialects.sybase.base import SybaseDialect -from sqlalchemy.dialects.sybase.base import SybaseExecutionContext - - -class _SybNumeric_pyodbc(sqltypes.Numeric): - """Turns Decimals with adjusted() < -6 into floats. - - It's not yet known how to get decimals with many - significant digits or very large adjusted() into Sybase - via pyodbc. - - """ - - def bind_processor(self, dialect): - super_process = super(_SybNumeric_pyodbc, self).bind_processor(dialect) - - def process(value): - if self.asdecimal and isinstance(value, decimal.Decimal): - - if value.adjusted() < -6: - return processors.to_float(value) - - if super_process: - return super_process(value) - else: - return value - - return process - - -class SybaseExecutionContext_pyodbc(SybaseExecutionContext): - def set_ddl_autocommit(self, connection, value): - if value: - connection.autocommit = True - else: - connection.autocommit = False - - -class SybaseDialect_pyodbc(PyODBCConnector, SybaseDialect): - execution_ctx_cls = SybaseExecutionContext_pyodbc - supports_statement_cache = True - - colspecs = {sqltypes.Numeric: _SybNumeric_pyodbc} - - @classmethod - def dbapi(cls): - return PyODBCConnector.dbapi() - - -dialect = SybaseDialect_pyodbc diff --git a/lib/sqlalchemy/dialects/sybase/pysybase.py b/lib/sqlalchemy/dialects/sybase/pysybase.py deleted file mode 100644 index 0f408e801..000000000 --- a/lib/sqlalchemy/dialects/sybase/pysybase.py +++ /dev/null @@ -1,106 +0,0 @@ -# sybase/pysybase.py -# Copyright (C) 2010-2021 the SQLAlchemy authors and contributors -# <see AUTHORS file> -# -# This module is part of SQLAlchemy and is released under -# the MIT License: https://www.opensource.org/licenses/mit-license.php - -""" -.. dialect:: sybase+pysybase - :name: Python-Sybase - :dbapi: Sybase - :connectstring: sybase+pysybase://<username>:<password>@<dsn>/[database name] - :url: https://python-sybase.sourceforge.net/ - -Unicode Support ---------------- - -The python-sybase driver does not appear to support non-ASCII strings of any -kind at this time. - -""" # noqa - -from sqlalchemy import processors -from sqlalchemy import types as sqltypes -from sqlalchemy.dialects.sybase.base import SybaseDialect -from sqlalchemy.dialects.sybase.base import SybaseExecutionContext -from sqlalchemy.dialects.sybase.base import SybaseSQLCompiler - - -class _SybNumeric(sqltypes.Numeric): - def result_processor(self, dialect, type_): - if not self.asdecimal: - return processors.to_float - else: - return sqltypes.Numeric.result_processor(self, dialect, type_) - - -class SybaseExecutionContext_pysybase(SybaseExecutionContext): - def set_ddl_autocommit(self, dbapi_connection, value): - if value: - # call commit() on the Sybase connection directly, - # to avoid any side effects of calling a Connection - # transactional method inside of pre_exec() - dbapi_connection.commit() - - 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] - - -class SybaseSQLCompiler_pysybase(SybaseSQLCompiler): - def bindparam_string(self, name, **kw): - return "@" + name - - -class SybaseDialect_pysybase(SybaseDialect): - driver = "pysybase" - execution_ctx_cls = SybaseExecutionContext_pysybase - statement_compiler = SybaseSQLCompiler_pysybase - - supports_statement_cache = True - - colspecs = {sqltypes.Numeric: _SybNumeric, sqltypes.Float: sqltypes.Float} - - @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): - vers = connection.exec_driver_sql("select @@version_number").scalar() - # i.e. 15500, 15000, 12500 == (15, 5, 0, 0), (15, 0, 0, 0), - # (12, 5, 0, 0) - return (vers / 1000, vers % 1000 / 100, vers % 100 / 10, vers % 10) - - def is_disconnect(self, e, connection, cursor): - 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 |