From: Mike Bayer Date: Sat, 23 Sep 2006 17:21:56 +0000 (+0000) Subject: - added autoincrement=True to Column; will disable schema generation X-Git-Tag: rel_0_3_0~138 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=372a8d76a61649e7952ec9a465c731aa00352363;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - added autoincrement=True to Column; will disable schema generation of SERIAL/AUTO_INCREMENT/identity seq for postgres/mysql/mssql if explicitly set to False. #303 --- diff --git a/CHANGES b/CHANGES index 10ef7968b1..e077877c36 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,9 @@ preventing conflicts with lazy loader operation, fixes - added "batch=True" flag to mapper; if False, save_obj will fully save one object at a time including calls to before_XXXX and after_XXXX +- added autoincrement=True to Column; will disable schema generation +of SERIAL/AUTO_INCREMENT/identity seq for postgres/mysql/mssql if +explicitly set to False 0.2.8 - cleanup on connection methods + documentation. custom DBAPI diff --git a/lib/sqlalchemy/databases/mssql.py b/lib/sqlalchemy/databases/mssql.py index 1a30b9fe5b..6c4fa816c8 100644 --- a/lib/sqlalchemy/databases/mssql.py +++ b/lib/sqlalchemy/databases/mssql.py @@ -520,7 +520,7 @@ class MSSQLSchemaGenerator(ansisql.ANSISchemaGenerator): colspec = self.preparer.format_column(column) + " " + column.type.engine_impl(self.engine).get_col_spec() # install a IDENTITY Sequence if we have an implicit IDENTITY column - if column.primary_key and isinstance(column.type, sqltypes.Integer): + if column.primary_key and column.autoincrement and isinstance(column.type, sqltypes.Integer): if column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional): column.sequence = schema.Sequence(column.name + '_seq') diff --git a/lib/sqlalchemy/databases/mysql.py b/lib/sqlalchemy/databases/mysql.py index 74821707f4..f833489d22 100644 --- a/lib/sqlalchemy/databases/mysql.py +++ b/lib/sqlalchemy/databases/mysql.py @@ -439,7 +439,7 @@ class MySQLSchemaGenerator(ansisql.ANSISchemaGenerator): if not column.nullable: colspec += " NOT NULL" if column.primary_key: - if not column.foreign_key and first_pk and isinstance(column.type, sqltypes.Integer): + if not column.foreign_key and first_pk and column.autoincrement and isinstance(column.type, sqltypes.Integer): colspec += " AUTO_INCREMENT" return colspec diff --git a/lib/sqlalchemy/databases/postgres.py b/lib/sqlalchemy/databases/postgres.py index 7bf7869766..6fe51ad9af 100644 --- a/lib/sqlalchemy/databases/postgres.py +++ b/lib/sqlalchemy/databases/postgres.py @@ -490,7 +490,7 @@ class PGSchemaGenerator(ansisql.ANSISchemaGenerator): def get_column_specification(self, column, **kwargs): colspec = self.preparer.format_column(column) - if column.primary_key and not column.foreign_key and isinstance(column.type, sqltypes.Integer) and not isinstance(column.type, sqltypes.SmallInteger) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): + if column.primary_key and not column.foreign_key and column.autoincrement and isinstance(column.type, sqltypes.Integer) and not isinstance(column.type, sqltypes.SmallInteger) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): colspec += " SERIAL" else: colspec += " " + column.type.engine_impl(self.engine).get_col_spec() @@ -520,7 +520,7 @@ class PGDefaultRunner(ansisql.ANSIDefaultRunner): if isinstance(column.default, schema.PassiveDefault): c = self.proxy("select %s" % column.default.arg) return c.fetchone()[0] - elif isinstance(column.type, sqltypes.Integer) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): + elif (isinstance(column.type, sqltypes.Integer) and column.autoincrement) and (column.default is None or (isinstance(column.default, schema.Sequence) and column.default.optional)): sch = column.table.schema # TODO: this has to build into the Sequence object so we can get the quoting # logic from it diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 3cbb7f9e46..3ee03f1eed 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -367,14 +367,15 @@ class Column(SchemaItem, sql.ColumnClause): Generated SQL, however, will still reference the column by its actual name. primary_key=False : True if this column is a primary key column. Multiple columns can have this flag - set to specify composite primary keys. + set to specify composite primary keys. As an alternative, the primary key of a Table can be specified + via an explicit PrimaryKeyConstraint instance appended to the Table's list of objects. nullable=True : True if this column should allow nulls. Defaults to True unless this column is a primary key column. default=None : a scalar, python callable, or ClauseElement representing the "default value" for this column, which will be invoked upon insert if this column is not present in the insert list or is given a value - of None. + of None. The default expression will be converted into a ColumnDefault object upon initialization. hidden=False : indicates this column should not be listed in the table's list of columns. Used for the "oid" column, which generally @@ -392,8 +393,17 @@ class Column(SchemaItem, sql.ColumnClause): specify the same index name will all be included in the index, in the order of their creation. + autoincrement=True : Indicates that integer-based primary key columns should have autoincrementing behavior, + if supported by the underlying database. This will affect CREATE TABLE statements such that they will + use the databases "auto-incrementing" keyword (such as SERIAL for postgres, AUTO_INCREMENT for mysql) and will + also affect the behavior of some dialects during INSERT statement execution such that they will assume primary + key values are created in this manner. If a Column has an explicit ColumnDefault object (such as via the + "default" keyword, or a Sequence or PassiveDefault), then the value of autoincrement is ignored and is assumed + to be False. autoincrement value is only significant for a column with a type or subtype of Integer. + quote=False : indicates that the Column identifier must be properly escaped and quoted before being sent - to the database. + to the database. This flag should normally not be required as dialects can auto-detect conditions where quoting + is required. case_sensitive=True : indicates that the identifier should be interpreted by the database in the natural case for identifiers. Mixed case is not sufficient to cause this identifier to be quoted; it must contain an illegal character. @@ -411,6 +421,7 @@ class Column(SchemaItem, sql.ColumnClause): self.quote = kwargs.pop('quote', False) self._set_casing_strategy(name, kwargs) self.onupdate = kwargs.pop('onupdate', None) + self.autoincrement = kwargs.pop('autoincrement', True) self.__originating_column = self if self.index is not None and self.unique is not None: raise exceptions.ArgumentError("Column may not define both index and unique") diff --git a/test/sql/defaults.py b/test/sql/defaults.py index 57b4388a19..57de15c914 100644 --- a/test/sql/defaults.py +++ b/test/sql/defaults.py @@ -120,12 +120,44 @@ class DefaultTest(PersistTest): l = t.select(t.c.col1==pk).execute() l = l.fetchone() self.assert_(l['col3'] == 55) - -class SequenceTest(PersistTest): +class AutoIncrementTest(PersistTest): + @testbase.supported('postgres', 'mysql') + def testnonautoincrement(self): + meta = BoundMetaData(testbase.db) + nonai_table = Table("aitest", meta, + Column('id', Integer, autoincrement=False, primary_key=True), + Column('data', String(20))) + nonai_table.create() + try: + try: + # postgres will fail on first row, mysql fails on second row + nonai_table.insert().execute(data='row 1') + nonai_table.insert().execute(data='row 2') + assert False + except exceptions.SQLError, e: + print "Got exception", str(e) + assert True + + nonai_table.insert().execute(id=1, data='row 1') + finally: + nonai_table.drop() + + def testwithautoincrement(self): + meta = BoundMetaData(testbase.db) + table = Table("aitest", meta, + Column('id', Integer, primary_key=True), + Column('data', String(20))) + table.create() + try: + table.insert().execute(data='row 1') + table.insert().execute(data='row 2') + finally: + table.drop() + +class SequenceTest(PersistTest): + @testbase.supported('postgres', 'oracle') def setUpAll(self): - if testbase.db.engine.name != 'postgres' and testbase.db.engine.name != 'oracle': - return global cartitems cartitems = Table("cartitems", db, Column("cart_id", Integer, Sequence('cart_id_seq'), primary_key=True), @@ -159,9 +191,8 @@ class SequenceTest(PersistTest): x = cartitems.c.cart_id.sequence.execute() self.assert_(1 <= x <= 4) + @testbase.supported('postgres', 'oracle') def tearDownAll(self): - if testbase.db.engine.name != 'postgres' and testbase.db.engine.name != 'oracle': - return cartitems.drop() if __name__ == "__main__":