Learn more  » Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

aroundthecode / SQLAlchemy   python

Repository URL to install this package:

Version: 1.2.10 

/ engine / default.py

# engine/default.py
# Copyright (C) 2005-2018 the SQLAlchemy authors and contributors
# <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

"""Default implementations of per-dialect sqlalchemy.engine classes.

These are semi-private implementation classes which are only of importance
to database dialect authors; dialects will usually use the classes here
as the base class for their own corresponding classes.

"""

import re
import random
from . import reflection, interfaces, result
from ..sql import compiler, expression, schema
from .. import types as sqltypes
from .. import exc, util, pool, processors
import codecs
import weakref
from .. import event

AUTOCOMMIT_REGEXP = re.compile(
    r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER)',
    re.I | re.UNICODE)

# When we're handed literal SQL, ensure it's a SELECT query
SERVER_SIDE_CURSOR_RE = re.compile(
    r'\s*SELECT',
    re.I | re.UNICODE)


class DefaultDialect(interfaces.Dialect):
    """Default implementation of Dialect"""

    statement_compiler = compiler.SQLCompiler
    ddl_compiler = compiler.DDLCompiler
    type_compiler = compiler.GenericTypeCompiler
    preparer = compiler.IdentifierPreparer
    supports_alter = True
    supports_comments = False
    inline_comments = False

    # the first value we'd get for an autoincrement
    # column.
    default_sequence_base = 1

    # most DBAPIs happy with this for execute().
    # not cx_oracle.
    execute_sequence_format = tuple

    supports_views = True
    supports_sequences = False
    sequences_optional = False
    preexecute_autoincrement_sequences = False
    postfetch_lastrowid = True
    implicit_returning = False

    supports_right_nested_joins = True
    cte_follows_insert = False

    supports_native_enum = False
    supports_native_boolean = False
    non_native_boolean_check_constraint = True

    supports_simple_order_by_label = True

    engine_config_types = util.immutabledict([
        ('convert_unicode', util.bool_or_str('force')),
        ('pool_timeout', util.asint),
        ('echo', util.bool_or_str('debug')),
        ('echo_pool', util.bool_or_str('debug')),
        ('pool_recycle', util.asint),
        ('pool_size', util.asint),
        ('max_overflow', util.asint),
        ('pool_threadlocal', util.asbool),
    ])

    # if the NUMERIC type
    # returns decimal.Decimal.
    # *not* the FLOAT type however.
    supports_native_decimal = False

    if util.py3k:
        supports_unicode_statements = True
        supports_unicode_binds = True
        returns_unicode_strings = True
        description_encoding = None
    else:
        supports_unicode_statements = False
        supports_unicode_binds = False
        returns_unicode_strings = False
        description_encoding = 'use_encoding'

    name = 'default'

    # length at which to truncate
    # any identifier.
    max_identifier_length = 9999

    # length at which to truncate
    # the name of an index.
    # Usually None to indicate
    # 'use max_identifier_length'.
    # thanks to MySQL, sigh
    max_index_name_length = None

    supports_sane_rowcount = True
    supports_sane_multi_rowcount = True
    colspecs = {}
    default_paramstyle = 'named'
    supports_default_values = False
    supports_empty_insert = True
    supports_multivalues_insert = False

    supports_server_side_cursors = False

    server_version_info = None

    construct_arguments = None
    """Optional set of argument specifiers for various SQLAlchemy
    constructs, typically schema items.

    To implement, establish as a series of tuples, as in::

        construct_arguments = [
            (schema.Index, {
                "using": False,
                "where": None,
                "ops": None
            })
        ]

    If the above construct is established on the PostgreSQL dialect,
    the :class:`.Index` construct will now accept the keyword arguments
    ``postgresql_using``, ``postgresql_where``, nad ``postgresql_ops``.
    Any other argument specified to the constructor of :class:`.Index`
    which is prefixed with ``postgresql_`` will raise :class:`.ArgumentError`.

    A dialect which does not include a ``construct_arguments`` member will
    not participate in the argument validation system.  For such a dialect,
    any argument name is accepted by all participating constructs, within
    the namespace of arguments prefixed with that dialect name.  The rationale
    here is so that third-party dialects that haven't yet implemented this
    feature continue to function in the old way.

    .. versionadded:: 0.9.2

    .. seealso::

        :class:`.DialectKWArgs` - implementing base class which consumes
        :attr:`.DefaultDialect.construct_arguments`


    """

    # indicates symbol names are
    # UPPERCASEd if they are case insensitive
    # within the database.
    # if this is True, the methods normalize_name()
    # and denormalize_name() must be provided.
    requires_name_normalize = False

    reflection_options = ()

    dbapi_exception_translation_map = util.immutabledict()
    """mapping used in the extremely unusual case that a DBAPI's
    published exceptions don't actually have the __name__ that they
    are linked towards.

    .. versionadded:: 1.0.5

    """

    def __init__(self, convert_unicode=False,
                 encoding='utf-8', paramstyle=None, dbapi=None,
                 implicit_returning=None,
                 supports_right_nested_joins=None,
                 case_sensitive=True,
                 supports_native_boolean=None,
                 empty_in_strategy='static',
                 label_length=None, **kwargs):

        if not getattr(self, 'ported_sqla_06', True):
            util.warn(
                "The %s dialect is not yet ported to the 0.6 format" %
                self.name)

        self.convert_unicode = convert_unicode
        self.encoding = encoding
        self.positional = False
        self._ischema = None
        self.dbapi = dbapi
        if paramstyle is not None:
            self.paramstyle = paramstyle
        elif self.dbapi is not None:
            self.paramstyle = self.dbapi.paramstyle
        else:
            self.paramstyle = self.default_paramstyle
        if implicit_returning is not None:
            self.implicit_returning = implicit_returning
        self.positional = self.paramstyle in ('qmark', 'format', 'numeric')
        self.identifier_preparer = self.preparer(self)
        self.type_compiler = self.type_compiler(self)
        if supports_right_nested_joins is not None:
            self.supports_right_nested_joins = supports_right_nested_joins
        if supports_native_boolean is not None:
            self.supports_native_boolean = supports_native_boolean
        self.case_sensitive = case_sensitive

        self.empty_in_strategy = empty_in_strategy
        if empty_in_strategy == 'static':
            self._use_static_in = True
        elif empty_in_strategy in ('dynamic', 'dynamic_warn'):
            self._use_static_in = False
            self._warn_on_empty_in = empty_in_strategy == 'dynamic_warn'
        else:
            raise exc.ArgumentError(
                "empty_in_strategy may be 'static', "
                "'dynamic', or 'dynamic_warn'")

        if label_length and label_length > self.max_identifier_length:
            raise exc.ArgumentError(
                "Label length of %d is greater than this dialect's"
                " maximum identifier length of %d" %
                (label_length, self.max_identifier_length))
        self.label_length = label_length

        if self.description_encoding == 'use_encoding':
            self._description_decoder = \
                processors.to_unicode_processor_factory(
                    encoding
                )
        elif self.description_encoding is not None:
            self._description_decoder = \
                processors.to_unicode_processor_factory(
                    self.description_encoding
                )
        self._encoder = codecs.getencoder(self.encoding)
        self._decoder = processors.to_unicode_processor_factory(self.encoding)

    @util.memoized_property
    def _type_memos(self):
        return weakref.WeakKeyDictionary()

    @property
    def dialect_description(self):
        return self.name + "+" + self.driver

    @property
    def supports_sane_rowcount_returning(self):
        return self.supports_sane_rowcount

    @classmethod
    def get_pool_class(cls, url):
        return getattr(cls, 'poolclass', pool.QueuePool)

    def initialize(self, connection):
        try:
            self.server_version_info = \
                self._get_server_version_info(connection)
        except NotImplementedError:
            self.server_version_info = None
        try:
            self.default_schema_name = \
                self._get_default_schema_name(connection)
        except NotImplementedError:
            self.default_schema_name = None

        try:
            self.default_isolation_level = \
                self.get_isolation_level(connection.connection)
        except NotImplementedError:
            self.default_isolation_level = None

        self.returns_unicode_strings = self._check_unicode_returns(connection)

        if self.description_encoding is not None and \
                self._check_unicode_description(connection):
            self._description_decoder = self.description_encoding = None

        self.do_rollback(connection.connection)

    def on_connect(self):
        """return a callable which sets up a newly created DBAPI connection.

        This is used to set dialect-wide per-connection options such as
        isolation modes, unicode modes, etc.

        If a callable is returned, it will be assembled into a pool listener
        that receives the direct DBAPI connection, with all wrappers removed.

        If None is returned, no listener will be generated.

        """
        return None

    def _check_unicode_returns(self, connection, additional_tests=None):
        if util.py2k and not self.supports_unicode_statements:
            cast_to = util.binary_type
        else:
            cast_to = util.text_type

        if self.positional:
            parameters = self.execute_sequence_format()
        else:
            parameters = {}

        def check_unicode(test):
            statement = cast_to(
                expression.select([test]).compile(dialect=self))
            try:
                cursor = connection.connection.cursor()
                connection._cursor_execute(cursor, statement, parameters)
                row = cursor.fetchone()
                cursor.close()
            except exc.DBAPIError as de:
                # note that _cursor_execute() will have closed the cursor
                # if an exception is thrown.
                util.warn("Exception attempting to "
                          "detect unicode returns: %r" % de)
                return False
            else:
                return isinstance(row[0], util.text_type)

        tests = [
            # detect plain VARCHAR
            expression.cast(
                expression.literal_column("'test plain returns'"),
                sqltypes.VARCHAR(60)
            ),
            # detect if there's an NVARCHAR type with different behavior
            # available
            expression.cast(
                expression.literal_column("'test unicode returns'"),
                sqltypes.Unicode(60)
            ),
        ]

        if additional_tests:
            tests += additional_tests
Loading ...