]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- Column() supports a keyword argument "sqlite_autoincrement", which
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 18 Dec 2009 21:08:35 +0000 (21:08 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 18 Dec 2009 21:08:35 +0000 (21:08 +0000)
applies the SQLite keyword "AUTOINCREMENT" to columns within DDL -
will prevent generation of a separate PRIMARY KEY constraint.
[ticket:1016]
- added docs
- fixed underlines in mysql.rst

CHANGES
doc/build/reference/dialects/mysql.rst
lib/sqlalchemy/dialects/sqlite/base.py
lib/sqlalchemy/schema.py
lib/sqlalchemy/sql/compiler.py
test/dialect/test_sqlite.py

diff --git a/CHANGES b/CHANGES
index fcd7a1163e41bc096da7cb30a6378fe9701fa10b..775d4c0140ba26ac162564d56b1afec0145c6658 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -661,20 +661,25 @@ CHANGES
       the MSSQL dialect documentation for more information.
 
 - sqlite
-    - DATE, TIME and DATETIME types can now take optional storage_format and
-      regexp argument. storage_format can be used to store those types using
-      a custom string format. regexp allows to use a custom regular expression
-      to match string values from the database.
+    - DATE, TIME and DATETIME types can now take optional storage_format
+      and regexp argument. storage_format can be used to store those types
+      using a custom string format. regexp allows to use a custom regular
+      expression to match string values from the database.
     - Time and DateTime types now use by a default a stricter regular
-      expression to match strings from the database. Use the regexp argument
-      if you are using data stored in a legacy format.
+      expression to match strings from the database. Use the regexp
+      argument if you are using data stored in a legacy format.
     - __legacy_microseconds__ on SQLite Time and DateTime types is not
-      supported anymore. You should use the storage_format argument instead.
+      supported anymore. You should use the storage_format argument
+      instead.
     - Date, Time and DateTime types are now stricter in what they accept as
-      bind parameters: Date type only accepts date objects (and datetime ones,
-      because they inherit from date), Time only accepts time objects, and
-      DateTime only accepts date and datetime objects.
-
+      bind parameters: Date type only accepts date objects (and datetime
+      ones, because they inherit from date), Time only accepts time
+      objects, and DateTime only accepts date and datetime objects.
+    - Column() supports a keyword argument "sqlite_autoincrement", which
+      applies the SQLite keyword "AUTOINCREMENT" to columns within DDL - 
+      will prevent generation of a separate PRIMARY KEY constraint.
+      [ticket:1016]
+      
 - new dialects
     - postgresql+pg8000
     - postgresql+pypostgresql (partial)
index c310f6c52419f0c51a6a2756259ae9db1d661827..c45da7423467cbd7ee2c510b3eb0c9445b0a8bd1 100644 (file)
@@ -139,7 +139,7 @@ MySQL Column Types
    :show-inheritance:
 
 MySQL-Python Notes
---------------
+--------------------
 
 .. automodule:: sqlalchemy.dialects.mysql.mysqldb
 
@@ -149,7 +149,7 @@ OurSQL Notes
 .. automodule:: sqlalchemy.dialects.mysql.oursql
 
 MyConnPY Notes
---------------
+----------------
 
 .. automodule:: sqlalchemy.dialects.mysql.myconnpy
 
index 235a17a66fd2bbc5c03f78b6b4e5f98d93799e4e..27fc9b462759b52adf021050460784669839b27c 100644 (file)
@@ -20,7 +20,25 @@ These types represent dates and times as ISO formatted strings, which also nicel
 support ordering.   There's no reliance on typical "libc" internals for these functions
 so historical dates are fully supported.
 
+Auto Incrementing Beahvior
+--------------------------
 
+Background on SQLite's autoincrement is at: http://sqlite.org/autoinc.html
+
+Two things to note:
+
+* The AUTOINCREMENT keyword is **not** required for SQLite tables to
+  generate primary key values automatically. AUTOINCREMENT only means that
+  the algorithm used to generate ROWID values should be slightly different.
+* SQLite does **not** generate primary key (i.e. ROWID) values, even for
+  one column, if the table has a composite (i.e. multi-column) primary key.
+  This is regardless of the AUTOINCREMENT keyword being present or not.
+
+To specifically render the AUTOINCREMENT keyword on a SQLAlchemy column
+when rendering DDL, add the flag ``sqlite_autoincrement=True``::
+
+    Column('id', Integer, primary_key=True, sqlite_autoincrement=True)
+    
 """
 
 import datetime, re, time
@@ -238,8 +256,32 @@ class SQLiteDDLCompiler(compiler.DDLCompiler):
 
         if not column.nullable:
             colspec += " NOT NULL"
+
+        if column.primary_key and \
+             column.table.kwargs.get('sqlite_autoincrement', False) and \
+             len(column.table.primary_key.columns) == 1 and \
+             isinstance(column.type, sqltypes.Integer) and \
+             not column.foreign_keys:
+             colspec += " PRIMARY KEY AUTOINCREMENT"
+            
         return colspec
 
+    def visit_primary_key_constraint(self, constraint):
+        # for columns with sqlite_autoincrement=True,
+        # the PRIMARY KEY constraint can only be inline
+        # with the column itself.
+        if len(constraint.columns) == 1:
+            c = list(constraint)[0]
+            if c.primary_key and \
+                c.table.kwargs.get('sqlite_autoincrement', False) and \
+                isinstance(c.type, sqltypes.Integer) and \
+                not c.foreign_keys:
+                return ''
+        return super(SQLiteDDLCompiler, self).\
+                    visit_primary_key_constraint(constraint)
+
     def visit_create_index(self, create):
         index = create.element
         preparer = self.preparer
index d3a15dc8b3197907144bfe2af46d61f092a9cee2..455582c878ea02daf181f97a1fde3ce6c0afe623 100644 (file)
@@ -509,7 +509,8 @@ class Column(SchemaItem, expression.ColumnClause):
             SERIAL on Postgresql, and IDENTITY on MS-SQL.  It does 
             *not* issue AUTOINCREMENT for SQLite since this is a
             special SQLite flag that is not required for autoincrementing
-            behavior.
+            behavior.  See the SQLite dialect documentation for
+            information on SQLite's AUTOINCREMENT.
             
           * The column will be considered to be available as 
             cursor.lastrowid or equivalent, for those dialects which
index a41a149d1dcc7c6cd027d57f33349e6c7713141f..f589e4e4e47028c3cc14d08d8f8e883a101752ef 100644 (file)
@@ -982,7 +982,9 @@ class DDLCompiler(engine.Compiled):
         # On some DB order is significant: visit PK first, then the
         # other constraints (engine.ReflectionTest.testbasic failed on FB2)
         if table.primary_key:
-            text += ", \n\t" + self.process(table.primary_key)
+            pk = self.process(table.primary_key)
+            if pk:
+                text += ", \n\t" + pk
         
         const = ", \n\t".join(p for p in 
                         (self.process(constraint) for constraint in table.constraints 
index 7fced60d94aa0f3d9e617a4cda187b7d464f3fdf..2aca6b7761ffcacfd2db2b3f9533fcabeb71b879 100644 (file)
@@ -3,7 +3,7 @@
 from sqlalchemy.test.testing import eq_, assert_raises, assert_raises_message
 import datetime
 from sqlalchemy import *
-from sqlalchemy import exc, sql
+from sqlalchemy import exc, sql, schema
 from sqlalchemy.dialects.sqlite import base as sqlite, pysqlite as pysqlite_dialect
 from sqlalchemy.test import *
 
@@ -528,4 +528,26 @@ class MatchTest(TestBase, AssertsCompiledSQL):
                                            ).order_by(matchtable.c.id).execute().fetchall()
         eq_([1, 3], [r.id for r in results])
 
+class TestAutoIncrement(TestBase, AssertsCompiledSQL):
+
+    def test_sqlite_autoincrement(self):
+        table = Table('autoinctable', MetaData(),
+                      Column('id', Integer, primary_key=True),
+                      Column('x', Integer, default=None),
+                      sqlite_autoincrement=True)
+        self.assert_compile(
+            schema.CreateTable(table),
+            "CREATE TABLE autoinctable (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, x INTEGER)",
+            dialect=sqlite.dialect()
+        )
+
+    def test_sqlite_no_autoincrement(self):
+        table = Table('noautoinctable', MetaData(),
+                      Column('id', Integer, primary_key=True),
+                      Column('x', Integer, default=None))
+        self.assert_compile(
+            schema.CreateTable(table),
+            "CREATE TABLE noautoinctable (id INTEGER NOT NULL, x INTEGER, PRIMARY KEY (id))",
+            dialect=sqlite.dialect()
+        )