]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add support for "real" sequences in mssql
authorGord Thompson <gord@gordthompson.com>
Sun, 19 Apr 2020 17:47:19 +0000 (11:47 -0600)
committerGord Thompson <gord@gordthompson.com>
Fri, 29 May 2020 14:10:38 +0000 (08:10 -0600)
Added support for "CREATE SEQUENCE" and full :class:`.Sequence` support for
Microsoft SQL Server.  This removes the deprecated feature of using
:class:`.Sequence` objects to manipulate IDENTITY characteristics which
should now be performed using ``mssql_identity_start`` and
``mssql_identity_increment`` as documented at :ref:`mssql_identity`. The
change includes a new parameter :paramref:`.Sequence.data_type` to
accommodate SQL Server's choice of datatype, which for that backend
includes INTEGER and BIGINT.   The default starting value for SQL Server's
version of :class:`.Sequence` has been set at 1; this default is now
emitted within the CREATE SEQUENCE DDL for all backends.

Fixes: #4235
Fixes: #4633
Change-Id: I6aa55c441e8146c2f002e2e201a7f645e667b916

25 files changed:
doc/build/changelog/migration_14.rst
doc/build/changelog/unreleased_14/4235.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/mssql/base.py
lib/sqlalchemy/dialects/mssql/information_schema.py
lib/sqlalchemy/dialects/postgresql/base.py
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/sql/compiler.py
lib/sqlalchemy/sql/schema.py
lib/sqlalchemy/testing/plugin/plugin_base.py
lib/sqlalchemy/testing/requirements.py
lib/sqlalchemy/testing/suite/test_insert.py
lib/sqlalchemy/testing/suite/test_sequence.py
test/dialect/mssql/test_compiler.py
test/dialect/mssql/test_query.py
test/dialect/mssql/test_sequence.py [new file with mode: 0644]
test/dialect/mssql/test_types.py
test/engine/test_execute.py
test/orm/test_session.py
test/requirements.py
test/sql/test_compiler.py
test/sql/test_defaults.py
test/sql/test_functions.py
test/sql/test_insert_exec.py
test/sql/test_returning.py
test/sql/test_sequences.py

index 9c7d5fa55db5faff77533e21f48383fa3cfd7189..6702f86f0cd7a8771eaed993dfc57e0363067d58 100644 (file)
@@ -1599,4 +1599,31 @@ was not created.
 
     :ref:`defaults_sequences`
 
-:ticket:`4976`
\ No newline at end of file
+:ticket:`4976`
+
+
+.. _change_4235:
+
+Added Sequence support distinct from IDENTITY to SQL Server
+-----------------------------------------------------------
+
+The :class:`.Sequence` construct is now fully functional with Microsoft
+SQL Server.  When applied to a :class:`.Column`, the DDL for the table will
+no longer include IDENTITY keywords and instead will rely upon "CREATE SEQUENCE"
+to ensure a sequence is present which will then be used for INSERT statements
+on the table.
+
+The :class:`.Sequence` prior to version 1.3 was used to control parameters for
+the IDENTITY column in SQL Server; this usage emitted deprecation warnings
+throughout 1.3 and is now removed in 1.4.  For control of paramters for an
+IDENTITY column, the ``mssql_identity_start`` and ``mssql_identity_increment``
+parameters should be used; see the MSSQL dialect documentation linked below.
+
+
+.. seealso::
+
+    :ref:`mssql_identity`
+
+:ticket:`4235`
+
+:ticket:`4633`
diff --git a/doc/build/changelog/unreleased_14/4235.rst b/doc/build/changelog/unreleased_14/4235.rst
new file mode 100644 (file)
index 0000000..acd1ff1
--- /dev/null
@@ -0,0 +1,18 @@
+.. change::
+    :tags: sql, mssql
+    :tickets: 4235, 4633
+
+    Added support for "CREATE SEQUENCE" and full :class:`.Sequence` support for
+    Microsoft SQL Server.  This removes the deprecated feature of using
+    :class:`.Sequence` objects to manipulate IDENTITY characteristics which
+    should now be performed using ``mssql_identity_start`` and
+    ``mssql_identity_increment`` as documented at :ref:`mssql_identity`. The
+    change includes a new parameter :paramref:`.Sequence.data_type` to
+    accommodate SQL Server's choice of datatype, which for that backend
+    includes INTEGER, BIGINT, and DECIMAL(n, 0).   The default starting value
+    for SQL Server's version of :class:`.Sequence` has been set at 1; this
+    default is now emitted within the CREATE SEQUENCE DDL for all backends.
+
+    .. seealso::
+
+        :ref:`change_4235`
\ No newline at end of file
index 5e07045978435e612d1e866b8a5ffb068d323c62..9a38a788182433e6ae6dd080964e52fa778a2459 100644 (file)
@@ -22,7 +22,8 @@ behavior for an integer primary key column, described at
 the first
 integer primary key column in a :class:`_schema.Table`
 will be considered to be the
-identity column and will generate DDL as such::
+identity column - unless it is associated with a :class:`.Sequence` - and will
+generate DDL as such::
 
     from sqlalchemy import Table, MetaData, Column, Integer
 
@@ -76,6 +77,10 @@ is set to ``False`` on any integer primary key column::
    the ``mssql_identity_start`` and ``mssql_identity_increment`` parameters
    documented at :ref:`mssql_identity`.
 
+.. versionchanged::  1.4   Removed the ability to use a :class:`.Sequence`
+   object to modify IDENTITY characteristics. :class:`.Sequence` objects
+   now only manipulate true T-SQL SEQUENCE types.
+
 .. note::
 
     There can only be one IDENTITY column on the table.  When using
@@ -216,6 +221,17 @@ how SQLAlchemy handles this:
 This
 is an auxiliary use case suitable for testing and bulk insert scenarios.
 
+SEQUENCE support
+----------------
+
+The :class:`.Sequence` object now creates "real" sequences, i.e.,
+``CREATE SEQUENCE``. To provide compatibility with other dialects,
+:class:`.Sequence` defaults to a data type of Integer and a start value of 1,
+even though the T-SQL defaults are BIGINT and -9223372036854775808,
+respectively.
+
+.. versionadded:: 1.4.0
+
 MAX on VARCHAR / NVARCHAR
 -------------------------
 
@@ -701,6 +717,7 @@ import re
 from . import information_schema as ischema
 from ... import exc
 from ... import schema as sa_schema
+from ... import Sequence
 from ... import sql
 from ... import types as sqltypes
 from ... import util
@@ -713,6 +730,7 @@ from ...sql import expression
 from ...sql import func
 from ...sql import quoted_name
 from ...sql import util as sql_util
+from ...sql.type_api import to_instance
 from ...types import BIGINT
 from ...types import BINARY
 from ...types import CHAR
@@ -1465,18 +1483,20 @@ class MSExecutionContext(default.DefaultExecutionContext):
 
         if self.isinsert:
             tbl = self.compiled.statement.table
-            seq_column = tbl._autoincrement_column
-            insert_has_sequence = seq_column is not None
+            id_column = tbl._autoincrement_column
+            insert_has_identity = (id_column is not None) and (
+                not isinstance(id_column.default, Sequence)
+            )
 
-            if insert_has_sequence:
+            if insert_has_identity:
                 compile_state = self.compiled.compile_state
                 self._enable_identity_insert = (
-                    seq_column.key in self.compiled_parameters[0]
+                    id_column.key in self.compiled_parameters[0]
                 ) or (
                     compile_state._dict_parameters
                     and (
-                        seq_column.key in compile_state._dict_parameters
-                        or seq_column in compile_state._dict_parameters
+                        id_column.key in compile_state._dict_parameters
+                        or id_column in compile_state._dict_parameters
                     )
                 )
 
@@ -1485,7 +1505,7 @@ class MSExecutionContext(default.DefaultExecutionContext):
 
             self._select_lastrowid = (
                 not self.compiled.inline
-                and insert_has_sequence
+                and insert_has_identity
                 and not self.compiled.returning
                 and not self._enable_identity_insert
                 and not self.executemany
@@ -1570,6 +1590,23 @@ class MSExecutionContext(default.DefaultExecutionContext):
             except Exception:
                 pass
 
+    def get_result_cursor_strategy(self, result):
+        if self._result_strategy:
+            return self._result_strategy
+        else:
+            return super(MSExecutionContext, self).get_result_cursor_strategy(
+                result
+            )
+
+    def fire_sequence(self, seq, type_):
+        return self._execute_scalar(
+            (
+                "SELECT NEXT VALUE FOR %s"
+                % self.dialect.identifier_preparer.format_sequence(seq)
+            ),
+            type_,
+        )
+
 
 class MSSQLCompiler(compiler.SQLCompiler):
     returning_precedes_values = True
@@ -1972,6 +2009,9 @@ class MSSQLCompiler(compiler.SQLCompiler):
             self.process(binary.right),
         )
 
+    def visit_sequence(self, seq, **kw):
+        return "NEXT VALUE FOR %s" % self.preparer.format_sequence(seq)
+
 
 class MSSQLStrictCompiler(MSSQLCompiler):
 
@@ -2050,41 +2090,16 @@ class MSDDLCompiler(compiler.DDLCompiler):
                 "in order to generate DDL"
             )
 
-        # install an IDENTITY Sequence if we either a sequence or an implicit
-        # IDENTITY column
-        if isinstance(column.default, sa_schema.Sequence):
-
-            if (
-                column.default.start is not None
-                or column.default.increment is not None
-                or column is not column.table._autoincrement_column
-            ):
-                util.warn_deprecated(
-                    "Use of Sequence with SQL Server in order to affect the "
-                    "parameters of the IDENTITY value is deprecated, as "
-                    "Sequence "
-                    "will correspond to an actual SQL Server "
-                    "CREATE SEQUENCE in "
-                    "a future release.  Please use the mssql_identity_start "
-                    "and mssql_identity_increment parameters.",
-                    version="1.3",
-                )
-            if column.default.start == 0:
-                start = 0
-            else:
-                start = column.default.start or 1
-
-            colspec += " IDENTITY(%s,%s)" % (
-                start,
-                column.default.increment or 1,
-            )
-        elif (
+        if (
             column is column.table._autoincrement_column
             or column.autoincrement is True
         ):
-            start = column.dialect_options["mssql"]["identity_start"]
-            increment = column.dialect_options["mssql"]["identity_increment"]
-            colspec += " IDENTITY(%s,%s)" % (start, increment)
+            if not isinstance(column.default, Sequence):
+                start = column.dialect_options["mssql"]["identity_start"]
+                increment = column.dialect_options["mssql"][
+                    "identity_increment"
+                ]
+                colspec += " IDENTITY(%s,%s)" % (start, increment)
         else:
             default = self.get_column_default_string(column)
             if default is not None:
@@ -2203,6 +2218,18 @@ class MSDDLCompiler(compiler.DDLCompiler):
             text += " PERSISTED"
         return text
 
+    def visit_create_sequence(self, create, **kw):
+
+        if create.element.data_type is not None:
+            data_type = create.element.data_type
+        else:
+            data_type = to_instance(self.dialect.sequence_default_column_type)
+
+        prefix = " AS %s" % self.type_compiler.process(data_type)
+        return super(MSDDLCompiler, self).visit_create_sequence(
+            create, prefix=prefix, **kw
+        )
+
 
 class MSIdentifierPreparer(compiler.IdentifierPreparer):
     reserved_words = RESERVED_WORDS
@@ -2363,6 +2390,12 @@ class MSDialect(default.DefaultDialect):
 
     ischema_names = ischema_names
 
+    supports_sequences = True
+    # T-SQL's actual default is BIGINT
+    sequence_default_column_type = INTEGER
+    # T-SQL's actual default is -9223372036854775808
+    default_sequence_base = 1
+
     supports_native_boolean = False
     non_native_boolean_check_constraint = False
     supports_unicode_binds = True
@@ -2565,6 +2598,21 @@ class MSDialect(default.DefaultDialect):
 
         return c.first() is not None
 
+    @_db_plus_owner
+    def has_sequence(self, connection, sequencename, dbname, owner, schema):
+        sequences = ischema.sequences
+
+        s = sql.select([sequences.c.sequence_name]).where(
+            sequences.c.sequence_name == sequencename
+        )
+
+        if owner:
+            s = s.where(sequences.c.sequence_schema == owner)
+
+        c = connection.execute(s)
+
+        return c.first() is not None
+
     @reflection.cache
     def get_schema_names(self, connection, **kw):
         s = sql.select(
index e9ab6f4f3bc2aafbfb935b43a4140bd05686b410..6cdde83865dc7e1285a93bbd3546ee3384d11f3e 100644 (file)
@@ -173,3 +173,12 @@ computed_columns = Table(
     Column("definition", CoerceUnicode),
     schema="sys",
 )
+
+sequences = Table(
+    "SEQUENCES",
+    ischema,
+    Column("SEQUENCE_CATALOG", CoerceUnicode, key="sequence_catalog"),
+    Column("SEQUENCE_SCHEMA", CoerceUnicode, key="sequence_schema"),
+    Column("SEQUENCE_NAME", CoerceUnicode, key="sequence_name"),
+    schema="INFORMATION_SCHEMA",
+)
index a85a36bb7188b82c225ea303bfd6d1a4c6350d36..441e77a378eb0dcf61c456c4703a67c45d3e573d 100644 (file)
@@ -2446,6 +2446,7 @@ class PGDialect(default.DefaultDialect):
 
     supports_sequences = True
     sequences_optional = True
+    sequence_default_column_type = INTEGER
     preexecute_autoincrement_sequences = True
     postfetch_lastrowid = False
 
index d0f5cfe96ea111bed061cc7e56c6a6750a3316b8..c682a8ee11b745093eea568d52f87e44678e8c2c 100644 (file)
@@ -23,6 +23,7 @@ from . import cursor as _cursor
 from . import interfaces
 from .. import event
 from .. import exc
+from .. import Integer
 from .. import pool
 from .. import processors
 from .. import types as sqltypes
@@ -58,6 +59,8 @@ class DefaultDialect(interfaces.Dialect):
     # not cx_oracle.
     execute_sequence_format = tuple
 
+    sequence_default_column_type = Integer
+
     supports_views = True
     supports_sequences = False
     sequences_optional = False
index fc66ca5177f5ecd7ab5c43e09ea99d4fb32f9e06..4bd19e04b53579fdb907fda137d56273dc89f226 100644 (file)
@@ -3743,14 +3743,17 @@ class DDLCompiler(Compiled):
             drop.element, use_table=True
         )
 
-    def visit_create_sequence(self, create, **kw):
+    def visit_create_sequence(self, create, prefix=None, **kw):
         text = "CREATE SEQUENCE %s" % self.preparer.format_sequence(
             create.element
         )
+        if prefix:
+            text += prefix
         if create.element.increment is not None:
             text += " INCREMENT BY %d" % create.element.increment
-        if create.element.start is not None:
-            text += " START WITH %d" % create.element.start
+        if create.element.start is None:
+            create.element.start = self.dialect.default_sequence_base
+        text += " START WITH %d" % create.element.start
         if create.element.minvalue is not None:
             text += " MINVALUE %d" % create.element.minvalue
         if create.element.maxvalue is not None:
index 263f579def725191bfc4e806dca1a56dce0cef42..ee411174cd9b6a0471821281cdfd900f5297951a 100644 (file)
@@ -49,6 +49,7 @@ from .elements import ColumnElement
 from .elements import quoted_name
 from .elements import TextClause
 from .selectable import TableClause
+from .type_api import to_instance
 from .visitors import InternalTraversal
 from .. import event
 from .. import exc
@@ -2312,6 +2313,7 @@ class IdentityOptions(object):
         cycle=None,
         cache=None,
         order=None,
+        data_type=None,
     ):
         """Construct a :class:`.IdentityOptions` object.
 
@@ -2330,7 +2332,8 @@ class IdentityOptions(object):
          sequence which are calculated in advance.
         :param order: optional boolean value; if true, renders the
          ORDER keyword.
-         name.
+        :param data_type: The type to be returned by the sequence.
+
         """
         self.start = start
         self.increment = increment
@@ -2341,6 +2344,10 @@ class IdentityOptions(object):
         self.cycle = cycle
         self.cache = cache
         self.order = order
+        if data_type is not None:
+            self.data_type = to_instance(data_type)
+        else:
+            self.data_type = None
 
 
 class Sequence(IdentityOptions, roles.StatementRole, DefaultGenerator):
@@ -2393,6 +2400,7 @@ class Sequence(IdentityOptions, roles.StatementRole, DefaultGenerator):
         schema=None,
         cache=None,
         order=None,
+        data_type=None,
         optional=False,
         quote=None,
         metadata=None,
@@ -2402,6 +2410,7 @@ class Sequence(IdentityOptions, roles.StatementRole, DefaultGenerator):
         """Construct a :class:`.Sequence` object.
 
         :param name: the name of the sequence.
+
         :param start: the starting index of the sequence.  This value is
          used when the CREATE SEQUENCE command is emitted to the database
          as the value of the "START WITH" clause.   If ``None``, the
@@ -2478,6 +2487,12 @@ class Sequence(IdentityOptions, roles.StatementRole, DefaultGenerator):
 
          .. versionadded:: 1.1.12
 
+        :param data_type: The type to be returned by the sequence, for
+         dialects that allow us to choose between INTEGER, BIGINT, etc.
+         (e.g., mssql).
+
+         .. versionadded:: 1.4.0
+
         :param optional: boolean value, when ``True``, indicates that this
          :class:`.Sequence` object only needs to be explicitly generated
          on backends that don't provide another way to generate primary
@@ -2542,6 +2557,7 @@ class Sequence(IdentityOptions, roles.StatementRole, DefaultGenerator):
             cycle=cycle,
             cache=cache,
             order=order,
+            data_type=data_type,
         )
         self.name = quoted_name(name, quote)
         self.optional = optional
index bf168efce89bd9830371611543f4739595cee792..b31a4ff3e378fdb4bbfc49758b08c33de4645ea2 100644 (file)
@@ -477,6 +477,8 @@ def _prep_testing_database(options, file_config):
                         )
                     )
 
+            # TODO: need to do a get_sequences and drop them also after tables
+
 
 @post
 def _reverse_topological(options, file_config):
index 31011a9708c5d785532bd82f61f9043d8860065f..2d51e7c9b3555a5c9836fe4c9c3de0a17717cb57 100644 (file)
@@ -19,6 +19,7 @@ import platform
 import sys
 
 from . import exclusions
+from . import fails_on_everything_except
 from .. import util
 
 
@@ -1189,3 +1190,13 @@ class SuiteRequirements(Requirements):
             lambda config: not config.db.dialect.supports_is_distinct_from,
             "driver doesn't support an IS DISTINCT FROM construct",
         )
+
+    @property
+    def emulated_lastrowid_even_with_sequences(self):
+        """"target dialect retrieves cursor.lastrowid or an equivalent
+        after an insert() construct executes, even if the table has a
+        Sequence on it..
+        """
+        return fails_on_everything_except(
+            "mysql", "sqlite+pysqlite", "sqlite+pysqlcipher", "sybase",
+        )
index 92e38ab20ebf0be5ddf4132590c2f6fa73f7c61e..65741941fd9e5646396ced747d733bad8ae54750 100644 (file)
@@ -41,7 +41,9 @@ class LastrowidTest(fixtures.TablesTest):
 
     def _assert_round_trip(self, table, conn):
         row = conn.execute(table.select()).first()
-        eq_(row, (conn.engine.dialect.default_sequence_base, "some data"))
+        eq_(
+            row, (conn.dialect.default_sequence_base, "some data",),
+        )
 
     def test_autoincrement_on_insert(self, connection):
 
@@ -282,7 +284,9 @@ class ReturningTest(fixtures.TablesTest):
 
     def _assert_round_trip(self, table, conn):
         row = conn.execute(table.select()).first()
-        eq_(row, (conn.engine.dialect.default_sequence_base, "some data"))
+        eq_(
+            row, (conn.dialect.default_sequence_base, "some data",),
+        )
 
     @classmethod
     def define_tables(cls, metadata):
index db5582c21307f4ae8f1c1eeaf12b3d13b002a8e2..dda447c0d0be2313742edf89dff44341f1f04449 100644 (file)
@@ -23,7 +23,7 @@ class SequenceTest(fixtures.TablesTest):
         Table(
             "seq_pk",
             metadata,
-            Column("id", Integer, Sequence("tab_id_seq"), primary_key=True),
+            Column("id", Integer, Sequence("tab_id_seq"), primary_key=True,),
             Column("data", String(50)),
         )
 
@@ -33,7 +33,7 @@ class SequenceTest(fixtures.TablesTest):
             Column(
                 "id",
                 Integer,
-                Sequence("tab_id_seq", optional=True),
+                Sequence("tab_id_seq", data_type=Integer, optional=True),
                 primary_key=True,
             ),
             Column("data", String(50)),
@@ -45,11 +45,11 @@ class SequenceTest(fixtures.TablesTest):
 
     def test_insert_lastrowid(self, connection):
         r = connection.execute(self.tables.seq_pk.insert(), data="some data")
-        eq_(r.inserted_primary_key, [1])
+        eq_(r.inserted_primary_key, [testing.db.dialect.default_sequence_base])
 
     def test_nextval_direct(self, connection):
         r = connection.execute(self.tables.seq_pk.c.id.default)
-        eq_(r, 1)
+        eq_(r, testing.db.dialect.default_sequence_base)
 
     @requirements.sequences_optional
     def test_optional_seq(self, connection):
@@ -60,7 +60,7 @@ class SequenceTest(fixtures.TablesTest):
 
     def _assert_round_trip(self, table, conn):
         row = conn.execute(table.select()).first()
-        eq_(row, (1, "some data"))
+        eq_(row, (testing.db.dialect.default_sequence_base, "some data"))
 
 
 class SequenceCompilerTest(testing.AssertsCompiledSQL, fixtures.TestBase):
index b7a06c8e3eb4666452c7626c923f7a62da941343..99308132b702e19fc29e8da03998b8d08bc6df8c 100644 (file)
@@ -13,7 +13,6 @@ from sqlalchemy import MetaData
 from sqlalchemy import PrimaryKeyConstraint
 from sqlalchemy import schema
 from sqlalchemy import select
-from sqlalchemy import Sequence
 from sqlalchemy import sql
 from sqlalchemy import String
 from sqlalchemy import Table
@@ -1157,52 +1156,6 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
             "PRIMARY KEY (id))",
         )
 
-    def test_sequence_start_0(self):
-        metadata = MetaData()
-        tbl = Table(
-            "test",
-            metadata,
-            Column("id", Integer, Sequence("", 0), primary_key=True),
-        )
-        with testing.expect_deprecated(
-            "Use of Sequence with SQL Server in order to affect "
-        ):
-            self.assert_compile(
-                schema.CreateTable(tbl),
-                "CREATE TABLE test (id INTEGER NOT NULL IDENTITY(0,1), "
-                "PRIMARY KEY (id))",
-            )
-
-    def test_sequence_non_primary_key(self):
-        metadata = MetaData()
-        tbl = Table(
-            "test",
-            metadata,
-            Column("id", Integer, Sequence("", start=5), primary_key=False),
-        )
-        with testing.expect_deprecated(
-            "Use of Sequence with SQL Server in order to affect "
-        ):
-            self.assert_compile(
-                schema.CreateTable(tbl),
-                "CREATE TABLE test (id INTEGER NOT NULL IDENTITY(5,1))",
-            )
-
-    def test_sequence_ignore_nullability(self):
-        metadata = MetaData()
-        tbl = Table(
-            "test",
-            metadata,
-            Column("id", Integer, Sequence("", start=5), nullable=True),
-        )
-        with testing.expect_deprecated(
-            "Use of Sequence with SQL Server in order to affect "
-        ):
-            self.assert_compile(
-                schema.CreateTable(tbl),
-                "CREATE TABLE test (id INTEGER NOT NULL IDENTITY(5,1))",
-            )
-
     def test_table_pkc_clustering(self):
         metadata = MetaData()
         tbl = Table(
index 2ad6c161d82444ee5427053f8cdde61bffcc34cb..77ecef3a3b0d0a8d4dca4c8834904faea357e7bc 100644 (file)
@@ -245,7 +245,7 @@ class QueryUnicodeTest(fixtures.TestBase):
     @testing.requires.mssql_freetds
     @testing.requires.python2
     @testing.provide_metadata
-    def test_convert_unicode(self):
+    def test_convert_unicode(self, connection):
         meta = self.metadata
         t1 = Table(
             "unitest_table",
@@ -253,26 +253,27 @@ class QueryUnicodeTest(fixtures.TestBase):
             Column("id", Integer, primary_key=True),
             Column("descr", mssql.MSText()),
         )
-        meta.create_all()
-        with testing.db.connect() as con:
-            con.execute(
-                ue(
-                    "insert into unitest_table values ('abc \xc3\xa9 def')"
-                ).encode("UTF-8")
+        meta.create_all(connection)
+        connection.execute(
+            ue("insert into unitest_table values ('abc \xc3\xa9 def')").encode(
+                "UTF-8"
             )
-            r = con.execute(t1.select()).first()
-            assert isinstance(r[1], util.text_type), (
-                "%s is %s instead of unicode, working on %s"
-                % (r[1], type(r[1]), meta.bind)
-            )
-            eq_(r[1], util.ue("abc \xc3\xa9 def"))
+        )
+        r = connection.execute(t1.select()).first()
+        assert isinstance(r[1], util.text_type), (
+            "%s is %s instead of unicode, working on %s"
+            % (r[1], type(r[1]), meta.bind)
+        )
+        eq_(r[1], util.ue("abc \xc3\xa9 def"))
 
 
 class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
     __only_on__ = "mssql"
     __backend__ = True
 
-    def test_fetchid_trigger(self):
+    @testing.provide_metadata
+    def test_fetchid_trigger(self, connection):
+        # TODO: investigate test hang on mssql when connection fixture is used
         """
         Verify identity return value on inserting to a trigger table.
 
@@ -299,12 +300,12 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
         with the init parameter 'implicit_returning = False'.
         """
 
-        # todo: this same test needs to be tried in a multithreaded context
+        # TODO: this same test needs to be tried in a multithreaded context
         #      with multiple threads inserting to the same table.
-        # todo: check whether this error also occurs with clients other
+        # TODO: check whether this error also occurs with clients other
         #      than the SQL Server Native Client. Maybe an assert_raises
         #      test should be written.
-        meta = MetaData(testing.db)
+        meta = self.metadata
         t1 = Table(
             "t1",
             meta,
@@ -323,24 +324,31 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
             Column("id", Integer, mssql_identity_start=200, primary_key=True),
             Column("descr", String(200)),
         )
-        meta.create_all()
-        con = testing.db.connect()
-        con.exec_driver_sql(
-            """create trigger paj on t1 for insert as
-            insert into t2 (descr) select descr from inserted"""
+
+        event.listen(
+            meta,
+            "after_create",
+            DDL(
+                "create trigger paj on t1 for insert as "
+                "insert into t2 (descr) select descr from inserted"
+            ),
         )
 
-        try:
-            tr = con.begin()
-            r = con.execute(t2.insert(), descr="hello")
-            self.assert_(r.inserted_primary_key == [200])
-            r = con.execute(t1.insert(), descr="hello")
-            self.assert_(r.inserted_primary_key == [100])
+        # this DROP is not actually needed since SQL Server transactional
+        # DDL is reverting it with the connection fixture.  however,
+        # since we can use "if exists" it's safe to have this here in
+        # case things change.
+        event.listen(
+            meta, "before_drop", DDL("""drop trigger if exists paj""")
+        )
 
-        finally:
-            tr.commit()
-            con.exec_driver_sql("""drop trigger paj""")
-            meta.drop_all()
+        # seems to work with all linux drivers + backend.  not sure
+        # if windows drivers / servers have different behavior here.
+        meta.create_all(connection)
+        r = connection.execute(t2.insert(), descr="hello")
+        self.assert_(r.inserted_primary_key == [200])
+        r = connection.execute(t1.insert(), descr="hello")
+        self.assert_(r.inserted_primary_key == [100])
 
     @testing.provide_metadata
     def _test_disable_scope_identity(self):
@@ -430,8 +438,8 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
             options=dict(legacy_schema_aliasing=False)
         )
         meta.bind = eng
-        con = eng.connect()
-        con.exec_driver_sql("create schema paj")
+        conn = eng.connect()
+        conn.exec_driver_sql("create schema paj")
 
         @event.listens_for(meta, "after_drop")
         def cleanup(target, connection, **kw):
@@ -440,17 +448,17 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
         tbl = Table(
             "test", meta, Column("id", Integer, primary_key=True), schema="paj"
         )
-        tbl.create()
-        tbl.insert().execute({"id": 1})
-        eq_(tbl.select().scalar(), 1)
+        tbl.create(conn)
+        conn.execute(tbl.insert(), {"id": 1})
+        eq_(conn.scalar(tbl.select()), 1)
 
     @testing.provide_metadata
     def test_insertid_schema_legacy(self):
         meta = self.metadata
         eng = engines.testing_engine(options=dict(legacy_schema_aliasing=True))
         meta.bind = eng
-        con = eng.connect()
-        con.exec_driver_sql("create schema paj")
+        conn = eng.connect()
+        conn.exec_driver_sql("create schema paj")
 
         @event.listens_for(meta, "after_drop")
         def cleanup(target, connection, **kw):
@@ -464,7 +472,7 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
         eq_(tbl.select().scalar(), 1)
 
     @testing.provide_metadata
-    def test_returning_no_autoinc(self):
+    def test_returning_no_autoinc(self, connection):
         meta = self.metadata
         table = Table(
             "t1",
@@ -472,12 +480,11 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
             Column("id", Integer, primary_key=True),
             Column("data", String(50)),
         )
-        table.create()
-        result = (
+        table.create(connection)
+        result = connection.execute(
             table.insert()
             .values(id=1, data=func.lower("SomeString"))
             .returning(table.c.id, table.c.data)
-            .execute()
         )
         eq_(result.fetchall(), [(1, "somestring")])
 
@@ -488,8 +495,8 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
             options=dict(legacy_schema_aliasing=False)
         )
         meta.bind = eng
-        con = eng.connect()
-        con.exec_driver_sql("create schema paj")
+        conn = eng.connect()
+        conn.exec_driver_sql("create schema paj")
 
         @event.listens_for(meta, "after_drop")
         def cleanup(target, connection, **kw):
@@ -498,19 +505,19 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
         tbl = Table(
             "test", meta, Column("id", Integer, primary_key=True), schema="paj"
         )
-        tbl.create()
-        tbl.insert().execute({"id": 1})
-        eq_(tbl.select().scalar(), 1)
-        tbl.delete(tbl.c.id == 1).execute()
-        eq_(tbl.select().scalar(), None)
+        tbl.create(conn)
+        conn.execute(tbl.insert(), {"id": 1})
+        eq_(conn.scalar(tbl.select()), 1)
+        conn.execute(tbl.delete(tbl.c.id == 1))
+        eq_(conn.scalar(tbl.select()), None)
 
     @testing.provide_metadata
     def test_delete_schema_legacy(self):
         meta = self.metadata
         eng = engines.testing_engine(options=dict(legacy_schema_aliasing=True))
         meta.bind = eng
-        con = eng.connect()
-        con.exec_driver_sql("create schema paj")
+        conn = eng.connect()
+        conn.exec_driver_sql("create schema paj")
 
         @event.listens_for(meta, "after_drop")
         def cleanup(target, connection, **kw):
@@ -519,20 +526,20 @@ class QueryTest(testing.AssertsExecutionResults, fixtures.TestBase):
         tbl = Table(
             "test", meta, Column("id", Integer, primary_key=True), schema="paj"
         )
-        tbl.create()
-        tbl.insert().execute({"id": 1})
-        eq_(tbl.select().scalar(), 1)
-        tbl.delete(tbl.c.id == 1).execute()
-        eq_(tbl.select().scalar(), None)
+        tbl.create(conn)
+        conn.execute(tbl.insert(), {"id": 1})
+        eq_(conn.scalar(tbl.select()), 1)
+        conn.execute(tbl.delete(tbl.c.id == 1))
+        eq_(conn.scalar(tbl.select()), None)
 
     @testing.provide_metadata
-    def test_insertid_reserved(self):
+    def test_insertid_reserved(self, connection):
         meta = self.metadata
         table = Table("select", meta, Column("col", Integer, primary_key=True))
-        table.create()
+        table.create(connection)
 
-        table.insert().execute(col=7)
-        eq_(table.select().scalar(), 7)
+        connection.execute(table.insert(), {"col": 7})
+        eq_(connection.scalar(table.select()), 7)
 
 
 class Foo(object):
diff --git a/test/dialect/mssql/test_sequence.py b/test/dialect/mssql/test_sequence.py
new file mode 100644 (file)
index 0000000..61f315f
--- /dev/null
@@ -0,0 +1,70 @@
+from decimal import Decimal
+
+from sqlalchemy import BIGINT
+from sqlalchemy import Column
+from sqlalchemy import DECIMAL
+from sqlalchemy import Integer
+from sqlalchemy import Sequence
+from sqlalchemy import String
+from sqlalchemy import Table
+from sqlalchemy.future import select
+from sqlalchemy.testing import eq_
+from sqlalchemy.testing import fixtures
+
+
+class SequenceTest(fixtures.TablesTest):
+    __only_on__ = "mssql"
+    __backend__ = True
+
+    @classmethod
+    def define_tables(cls, metadata):
+        Table(
+            "int_seq_t",
+            metadata,
+            Column("id", Integer, default=Sequence("int_seq")),
+            Column("txt", String(50)),
+        )
+
+        Table(
+            "bigint_seq_t",
+            metadata,
+            Column(
+                "id",
+                BIGINT,
+                default=Sequence(
+                    "bigint_seq", data_type=BIGINT, start=3000000000
+                ),
+            ),
+            Column("txt", String(50)),
+        )
+
+        Table(
+            "decimal_seq_t",
+            metadata,
+            Column(
+                "id",
+                DECIMAL(10, 0),
+                default=Sequence(
+                    "decimal_seq", data_type=DECIMAL(10, 0), start=3000000000,
+                ),
+            ),
+            Column("txt", String(50)),
+        )
+
+    def test_int_seq(self, connection):
+        t = self.tables.int_seq_t
+        connection.execute(t.insert({"txt": "int_seq test"}))
+        result = connection.scalar(select(t.c.id))
+        eq_(result, 1)
+
+    def test_bigint_seq(self, connection):
+        t = self.tables.bigint_seq_t
+        connection.execute(t.insert({"txt": "bigint_seq test"}))
+        result = connection.scalar(select(t.c.id))
+        eq_(result, 3000000000)
+
+    def test_decimal_seq(self, connection):
+        t = self.tables.decimal_seq_t
+        connection.execute(t.insert({"txt": "decimal_seq test"}))
+        result = connection.scalar(select(t.c.id))
+        eq_(result, Decimal("3000000000"))
index f0ddc456457d0e8d3705276fe19254b4de02101c..37b83e911dd7f0ae3eb0ecf5fbb011e43054253d 100644 (file)
@@ -561,9 +561,10 @@ class TypeRoundTripTest(
             metadata,
             Column(
                 "id",
-                Integer,
+                testing.db.dialect.sequence_default_column_type,
                 Sequence("numeric_id_seq", optional=True),
                 primary_key=True,
+                autoincrement=False,
             ),
             Column(
                 "numericcol", Numeric(precision=38, scale=20, asdecimal=True)
@@ -651,7 +652,7 @@ class TypeRoundTripTest(
             metadata,
             Column(
                 "id",
-                Integer,
+                testing.db.dialect.sequence_default_column_type,
                 Sequence("numeric_id_seq", optional=True),
                 primary_key=True,
             ),
index cc4bb14175fc5a02a009a5452fbe1daec323dbd7..41f94aa980fce9f73cfc919ce85403b18495242e 100644 (file)
@@ -1001,7 +1001,7 @@ class MockStrategyTest(fixtures.TestBase):
             "testtable",
             metadata,
             Column(
-                "pk", Integer, Sequence("testtable_pk_seq"), primary_key=True
+                "pk", Integer, Sequence("testtable_pk_seq"), primary_key=True,
             ),
         )
 
@@ -1862,7 +1862,12 @@ class EngineEventsTest(fixtures.TestBase):
         t = Table(
             "t",
             self.metadata,
-            Column("x", Integer, Sequence("t_id_seq"), primary_key=True),
+            Column(
+                "x",
+                testing.db.dialect.sequence_default_column_type,
+                Sequence("t_id_seq"),
+                primary_key=True,
+            ),
             implicit_returning=False,
         )
         self.metadata.create_all(engine)
index fa4ffb0e6cacef80812600970b2b4aa787583c78..635f5ee1af264becfb27e2172f1b59f43232a9bd 100644 (file)
@@ -51,7 +51,7 @@ class ExecutionTest(_fixtures.FixtureTest):
         seq.create(testing.db)
         try:
             sess = create_session(bind=testing.db)
-            eq_(sess.execute(seq), 1)
+            eq_(sess.execute(seq), testing.db.dialect.default_sequence_base)
         finally:
             seq.drop(testing.db)
 
index 969b4ea83cf39845d212484e5b4bc6238fbc91e7..ec597109b00c2fd00b1db5a9456a93220509edcd 100644 (file)
@@ -1630,3 +1630,18 @@ class DefaultRequirements(SuiteRequirements):
     @property
     def supports_for_update_of(self):
         return only_if(lambda config: config.db.dialect.supports_for_update_of)
+
+    @property
+    def sequences_in_other_clauses(self):
+        """sequences allowed in WHERE, GROUP BY, HAVING, etc."""
+        return skip_if(["mssql", "oracle"])
+
+    @property
+    def supports_lastrowid_for_expressions(self):
+        """sequences allowed in WHERE, GROUP BY, HAVING, etc."""
+        return skip_if("mssql")
+
+    @property
+    def supports_sequence_for_autoincrement_column(self):
+        """for mssql, autoincrement means IDENTITY, not sequence"""
+        return skip_if("mssql")
index 20f31ba1e2c7cec5a32cd45e3929be87b030680f..df52a62c06d9cd34fec05c26beb5dbf58ecaa195 100644 (file)
@@ -4180,19 +4180,19 @@ class DDLTest(fixtures.TestBase, AssertsCompiledSQL):
 
         self.assert_compile(
             schema.CreateSequence(s1),
-            "CREATE SEQUENCE [SCHEMA__none].s1",
+            "CREATE SEQUENCE [SCHEMA__none].s1 START WITH 1",
             schema_translate_map=schema_translate_map,
         )
 
         self.assert_compile(
             schema.CreateSequence(s2),
-            "CREATE SEQUENCE [SCHEMA_foo].s2",
+            "CREATE SEQUENCE [SCHEMA_foo].s2 START WITH 1",
             schema_translate_map=schema_translate_map,
         )
 
         self.assert_compile(
             schema.CreateSequence(s3),
-            "CREATE SEQUENCE [SCHEMA_bar].s3",
+            "CREATE SEQUENCE [SCHEMA_bar].s3 START WITH 1",
             schema_translate_map=schema_translate_map,
         )
 
index 7352810ae7681a0740ee6e957a5017997e1ae43f..fa6c4d9a1d557446bd81515ba80d193507ffecbb 100644 (file)
@@ -998,7 +998,7 @@ class PKIncrementTest(fixtures.TablesTest):
             metadata,
             Column(
                 "id",
-                Integer,
+                testing.db.dialect.sequence_default_column_type,
                 Sequence("ai_id_seq", optional=True),
                 primary_key=True,
             ),
@@ -1036,11 +1036,24 @@ class PKIncrementTest(fixtures.TablesTest):
         self.assert_(last not in ids)
         ids.add(last)
 
-        eq_(ids, set([1, 2, 3, 4]))
+        eq_(
+            ids,
+            set(
+                range(
+                    testing.db.dialect.default_sequence_base,
+                    testing.db.dialect.default_sequence_base + 4,
+                )
+            ),
+        )
 
         eq_(
             list(bind.execute(aitable.select().order_by(aitable.c.id))),
-            [(1, 1, None), (2, None, "row 2"), (3, 3, "row 3"), (4, 4, None)],
+            [
+                (testing.db.dialect.default_sequence_base, 1, None),
+                (testing.db.dialect.default_sequence_base + 1, None, "row 2"),
+                (testing.db.dialect.default_sequence_base + 2, 3, "row 3"),
+                (testing.db.dialect.default_sequence_base + 3, 4, None),
+            ],
         )
 
     def test_autoincrement_autocommit(self):
@@ -1164,6 +1177,7 @@ class AutoIncrementTest(fixtures.TestBase):
         )
         return dataset_no_autoinc
 
+    @testing.skip_if(testing.requires.sequences)
     def test_col_w_optional_sequence_non_autoinc_no_firing(
         self, dataset_no_autoinc, connection
     ):
@@ -1215,7 +1229,7 @@ class SpecialTypePKTest(fixtures.TestBase):
     @classmethod
     def setup_class(cls):
         class MyInteger(TypeDecorator):
-            impl = Integer
+            impl = testing.db.dialect.sequence_default_column_type
 
             def process_bind_param(self, value, dialect):
                 if value is None:
@@ -1248,6 +1262,12 @@ class SpecialTypePKTest(fixtures.TestBase):
             t.create(conn)
             r = conn.execute(t.insert().values(data=5))
 
+            expected_result = "INT_" + str(
+                testing.db.dialect.default_sequence_base
+                if (arg and isinstance(arg[0], Sequence))
+                else 1
+            )
+
             # we don't pre-fetch 'server_default'.
             if "server_default" in kw and (
                 not testing.db.dialect.implicit_returning
@@ -1255,9 +1275,13 @@ class SpecialTypePKTest(fixtures.TestBase):
             ):
                 eq_(r.inserted_primary_key, [None])
             else:
-                eq_(r.inserted_primary_key, ["INT_1"])
+                eq_(
+                    r.inserted_primary_key, [expected_result],
+                )
 
-            eq_(conn.execute(t.select()).first(), ("INT_1", 5))
+            eq_(
+                conn.execute(t.select()).first(), (expected_result, 5),
+            )
 
     def test_plain(self):
         # among other things, tests that autoincrement
index 317c4677a80d96c1d8c99bcaea2105503518e409..4b35553919fe3f570f8334aad20e5c10ee6f585b 100644 (file)
@@ -991,7 +991,7 @@ class ExecuteTest(fixtures.TestBase):
             meta,
             Column(
                 "id",
-                Integer,
+                testing.db.dialect.sequence_default_column_type,
                 Sequence("t1idseq", optional=True),
                 primary_key=True,
             ),
@@ -1002,7 +1002,7 @@ class ExecuteTest(fixtures.TestBase):
             meta,
             Column(
                 "id",
-                Integer,
+                testing.db.dialect.sequence_default_column_type,
                 Sequence("t2idseq", optional=True),
                 primary_key=True,
             ),
index 9f9525f3cf5aa535aed83b79c6fc69a82a410441..a081766bc5e827913859ba0e4f67af26521ab8f9 100644 (file)
@@ -350,7 +350,12 @@ class TableInsertTest(fixtures.TablesTest):
         Table(
             "foo",
             metadata,
-            Column("id", Integer, Sequence("t_id_seq"), primary_key=True),
+            Column(
+                "id",
+                testing.db.dialect.sequence_default_column_type,
+                Sequence("t_id_seq"),
+                primary_key=True,
+            ),
             Column("data", String(50)),
             Column("x", Integer),
         )
@@ -396,7 +401,7 @@ class TableInsertTest(fixtures.TablesTest):
             t.insert().values(
                 id=func.next_value(Sequence("t_id_seq")), data="data", x=5
             ),
-            (1, "data", 5),
+            (testing.db.dialect.default_sequence_base, "data", 5),
         )
 
     def test_uppercase(self):
@@ -431,8 +436,8 @@ class TableInsertTest(fixtures.TablesTest):
         t = self.tables.foo
         self._test(
             t.insert().values(data="data", x=5),
-            (1, "data", 5),
-            inserted_primary_key=[1],
+            (testing.db.dialect.default_sequence_base, "data", 5),
+            inserted_primary_key=[testing.db.dialect.default_sequence_base],
         )
 
     def test_uppercase_direct_params(self):
@@ -452,9 +457,6 @@ class TableInsertTest(fixtures.TablesTest):
             returning=(1, 5),
         )
 
-    @testing.fails_on(
-        "mssql", "lowercase table doesn't support identity insert disable"
-    )
     def test_direct_params(self):
         t = self._fixture()
         self._test(
@@ -463,27 +465,26 @@ class TableInsertTest(fixtures.TablesTest):
             inserted_primary_key=[],
         )
 
-    @testing.fails_on(
-        "mssql", "lowercase table doesn't support identity insert disable"
-    )
     @testing.requires.returning
     def test_direct_params_returning(self):
         t = self._fixture()
         self._test(
             t.insert().values(id=1, data="data", x=5).returning(t.c.id, t.c.x),
-            (1, "data", 5),
-            returning=(1, 5),
+            (testing.db.dialect.default_sequence_base, "data", 5),
+            returning=(testing.db.dialect.default_sequence_base, 5),
         )
 
+    @testing.requires.emulated_lastrowid_even_with_sequences
     @testing.requires.emulated_lastrowid
     def test_implicit_pk(self):
         t = self._fixture()
         self._test(
             t.insert().values(data="data", x=5),
-            (1, "data", 5),
+            (testing.db.dialect.default_sequence_base, "data", 5),
             inserted_primary_key=[],
         )
 
+    @testing.requires.emulated_lastrowid_even_with_sequences
     @testing.requires.emulated_lastrowid
     def test_implicit_pk_multi_rows(self):
         t = self._fixture()
@@ -497,11 +498,12 @@ class TableInsertTest(fixtures.TablesTest):
             [(1, "d1", 5), (2, "d2", 6), (3, "d3", 7)],
         )
 
+    @testing.requires.emulated_lastrowid_even_with_sequences
     @testing.requires.emulated_lastrowid
     def test_implicit_pk_inline(self):
         t = self._fixture()
         self._test(
             t.insert().inline().values(data="data", x=5),
-            (1, "data", 5),
+            (testing.db.dialect.default_sequence_base, "data", 5),
             inserted_primary_key=[],
         )
index 5f655db6dd69f05241cdee623467272546b05e77..f856c15a4c978e9163e0b64239e76d30a8f37560 100644 (file)
@@ -234,7 +234,7 @@ class SequenceReturningTest(fixtures.TestBase):
         table = Table(
             "tables",
             meta,
-            Column("id", Integer, seq, primary_key=True),
+            Column("id", Integer, seq, primary_key=True,),
             Column("data", String(50)),
         )
         with testing.db.connect() as conn:
@@ -248,8 +248,11 @@ class SequenceReturningTest(fixtures.TestBase):
         r = connection.execute(
             table.insert().values(data="hi").returning(table.c.id)
         )
-        assert r.first() == (1,)
-        assert connection.execute(seq) == 2
+        eq_(r.first(), tuple([testing.db.dialect.default_sequence_base]))
+        eq_(
+            connection.execute(seq),
+            testing.db.dialect.default_sequence_base + 1,
+        )
 
 
 class KeyReturningTest(fixtures.TestBase, AssertsExecutionResults):
index 1d78c0904d4fa573b38381703fe93c04ec1f41cd..126f852badfa52d575833bd3e6417f1aff186399 100644 (file)
@@ -26,7 +26,8 @@ class SequenceDDLTest(fixtures.TestBase, testing.AssertsCompiledSQL):
 
     def test_create_drop_ddl(self):
         self.assert_compile(
-            CreateSequence(Sequence("foo_seq")), "CREATE SEQUENCE foo_seq"
+            CreateSequence(Sequence("foo_seq")),
+            "CREATE SEQUENCE foo_seq START WITH 1",
         )
 
         self.assert_compile(
@@ -36,7 +37,7 @@ class SequenceDDLTest(fixtures.TestBase, testing.AssertsCompiledSQL):
 
         self.assert_compile(
             CreateSequence(Sequence("foo_seq", increment=2)),
-            "CREATE SEQUENCE foo_seq INCREMENT BY 2",
+            "CREATE SEQUENCE foo_seq INCREMENT BY 2 START WITH 1",
         )
 
         self.assert_compile(
@@ -81,12 +82,12 @@ class SequenceDDLTest(fixtures.TestBase, testing.AssertsCompiledSQL):
 
         self.assert_compile(
             CreateSequence(Sequence("foo_seq", cache=1000, order=True)),
-            "CREATE SEQUENCE foo_seq CACHE 1000 ORDER",
+            "CREATE SEQUENCE foo_seq START WITH 1 CACHE 1000 ORDER",
         )
 
         self.assert_compile(
             CreateSequence(Sequence("foo_seq", order=True)),
-            "CREATE SEQUENCE foo_seq ORDER",
+            "CREATE SEQUENCE foo_seq START WITH 1 ORDER",
         )
 
         self.assert_compile(
@@ -111,7 +112,7 @@ class LegacySequenceExecTest(fixtures.TestBase):
         """asserts return of next_value is an int"""
 
         assert isinstance(ret, util.int_types)
-        assert ret > 0
+        assert ret >= testing.db.dialect.default_sequence_base
 
     def test_implicit_connectionless(self):
         s = Sequence("my_sequence", metadata=MetaData(testing.db))
@@ -169,7 +170,7 @@ class SequenceExecTest(fixtures.TestBase):
         """asserts return of next_value is an int"""
 
         assert isinstance(ret, util.int_types)
-        assert ret > 0
+        assert ret >= testing.db.dialect.default_sequence_base
 
     def test_execute(self, connection):
         s = Sequence("my_sequence")
@@ -202,7 +203,7 @@ class SequenceExecTest(fixtures.TestBase):
         s = Sequence("my_sequence")
         self._assert_seq_result(connection.scalar(select([s.next_value()])))
 
-    @testing.fails_on("oracle", "ORA-02287: sequence number not allowed here")
+    @testing.requires.sequences_in_other_clauses
     @testing.provide_metadata
     def test_func_embedded_whereclause(self, connection):
         """test can use next_value() in whereclause"""
@@ -224,43 +225,44 @@ class SequenceExecTest(fixtures.TestBase):
         """test can use next_value() in values() of _ValuesBase"""
 
         metadata = self.metadata
-        t1 = Table("t", metadata, Column("x", Integer))
+        t1 = Table("t", metadata, Column("x", Integer),)
         t1.create(testing.db)
         s = Sequence("my_sequence")
         connection.execute(t1.insert().values(x=s.next_value()))
         self._assert_seq_result(connection.scalar(t1.select()))
 
-    @testing.requires.supports_lastrowid
+    @testing.requires.no_lastrowid_support
     @testing.provide_metadata
-    def test_inserted_pk_no_returning_w_lastrowid(self):
-        """test inserted_primary_key contains the pk when
-        pk_col=next_value(), lastrowid is supported."""
+    def test_inserted_pk_no_returning_no_lastrowid(self):
+        """test inserted_primary_key contains [None] when
+        pk_col=next_value(), implicit returning is not used."""
 
         metadata = self.metadata
         t1 = Table("t", metadata, Column("x", Integer, primary_key=True))
         t1.create(testing.db)
+
         e = engines.testing_engine(options={"implicit_returning": False})
         s = Sequence("my_sequence")
-
         with e.connect() as conn:
             r = conn.execute(t1.insert().values(x=s.next_value()))
-            self._assert_seq_result(r.inserted_primary_key[0])
+            eq_(r.inserted_primary_key, [None])
 
-    @testing.requires.no_lastrowid_support
+    @testing.requires.supports_lastrowid
+    @testing.requires.supports_lastrowid_for_expressions
     @testing.provide_metadata
-    def test_inserted_pk_no_returning_no_lastrowid(self):
-        """test inserted_primary_key contains [None] when
-        pk_col=next_value(), implicit returning is not used."""
+    def test_inserted_pk_no_returning_w_lastrowid(self):
+        """test inserted_primary_key contains the pk when
+        pk_col=next_value(), lastrowid is supported."""
 
         metadata = self.metadata
-        t1 = Table("t", metadata, Column("x", Integer, primary_key=True))
+        t1 = Table("t", metadata, Column("x", Integer, primary_key=True,),)
         t1.create(testing.db)
-
         e = engines.testing_engine(options={"implicit_returning": False})
         s = Sequence("my_sequence")
+
         with e.connect() as conn:
             r = conn.execute(t1.insert().values(x=s.next_value()))
-            eq_(r.inserted_primary_key, [None])
+            self._assert_seq_result(r.inserted_primary_key[0])
 
     @testing.requires.returning
     @testing.provide_metadata
@@ -270,7 +272,7 @@ class SequenceExecTest(fixtures.TestBase):
 
         metadata = self.metadata
         s = Sequence("my_sequence")
-        t1 = Table("t", metadata, Column("x", Integer, primary_key=True))
+        t1 = Table("t", metadata, Column("x", Integer, primary_key=True,),)
         t1.create(testing.db)
 
         e = engines.testing_engine(options={"implicit_returning": True})
@@ -298,7 +300,7 @@ class SequenceTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         try:
             with testing.db.connect() as conn:
                 values = [conn.execute(seq) for i in range(3)]
-                start = seq.start or 1
+                start = seq.start or testing.db.dialect.default_sequence_base
                 inc = seq.increment or 1
                 eq_(values, list(range(start, start + inc * 3, inc)))
 
@@ -380,6 +382,7 @@ class SequenceTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         assert not self._has_sequence(connection, "s2")
 
     @testing.requires.returning
+    @testing.requires.supports_sequence_for_autoincrement_column
     @testing.provide_metadata
     def test_freestanding_sequence_via_autoinc(self, connection):
         t = Table(
@@ -416,7 +419,11 @@ class TableBoundSequenceTest(fixtures.TablesTest):
             "cartitems",
             metadata,
             Column(
-                "cart_id", Integer, Sequence("cart_id_seq"), primary_key=True
+                "cart_id",
+                Integer,
+                Sequence("cart_id_seq"),
+                primary_key=True,
+                autoincrement=False,
             ),
             Column("description", String(40)),
             Column("createdate", sa.DateTime()),
@@ -426,7 +433,7 @@ class TableBoundSequenceTest(fixtures.TablesTest):
         Table(
             "Manager",
             metadata,
-            Column("obj_id", Integer, Sequence("obj_id_seq")),
+            Column("obj_id", Integer, Sequence("obj_id_seq"),),
             Column("name", String(128)),
             Column(
                 "id",
@@ -443,7 +450,8 @@ class TableBoundSequenceTest(fixtures.TablesTest):
         connection.execute(cartitems.insert(), dict(description="there"))
         r = connection.execute(cartitems.insert(), dict(description="lala"))
 
-        eq_(r.inserted_primary_key[0], 3)
+        expected = 2 + testing.db.dialect.default_sequence_base
+        eq_(r.inserted_primary_key[0], expected)
 
         eq_(
             connection.scalar(
@@ -451,7 +459,7 @@ class TableBoundSequenceTest(fixtures.TablesTest):
                     cartitems.c.description == "lala"
                 ),
             ),
-            3,
+            expected,
         )
 
     def test_seq_nonpk(self):
@@ -471,15 +479,17 @@ class TableBoundSequenceTest(fixtures.TablesTest):
             conn.execute(
                 sometable.insert(), [{"name": "name3"}, {"name": "name4"}]
             )
+
+            dsb = testing.db.dialect.default_sequence_base
             eq_(
                 list(
                     conn.execute(sometable.select().order_by(sometable.c.id))
                 ),
                 [
-                    (1, "somename", 1),
-                    (2, "someother", 2),
-                    (3, "name3", 3),
-                    (4, "name4", 4),
+                    (dsb, "somename", dsb,),
+                    (dsb + 1, "someother", dsb + 1,),
+                    (dsb + 2, "name3", dsb + 2,),
+                    (dsb + 3, "name4", dsb + 3,),
                 ],
             )