]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- create_all(), drop_all(), create(), drop() all raise
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 22 Jun 2008 16:53:07 +0000 (16:53 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 22 Jun 2008 16:53:07 +0000 (16:53 +0000)
an error if the table name or schema name contains
more characters than that dialect's configured
character limit.  Some DB's can handle too-long
table names during usage, and SQLA can handle this
as well. But various reflection/
checkfirst-during-create scenarios fail since we are
looking for the name within the DB's catalog tables.
[ticket:571]

CHANGES
lib/sqlalchemy/engine/default.py
lib/sqlalchemy/exceptions.py
lib/sqlalchemy/sql/compiler.py
test/sql/labels.py

diff --git a/CHANGES b/CHANGES
index e6572d368e147b1d227aaf0b2bfe40cc9870aa4d..37741670c875d8415739c90870a5a92e7ac62f73 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -26,11 +26,22 @@ CHANGES
       a transaction is in progress [ticket:976].  This
       flag is always True with a "transactional" 
       (in 0.5 a non-"autocommit") Session.
-    
+
+- schema
+    - create_all(), drop_all(), create(), drop() all raise
+      an error if the table name or schema name contains
+      more characters than that dialect's configured
+      character limit.  Some DB's can handle too-long
+      table names during usage, and SQLA can handle this
+      as well. But various reflection/
+      checkfirst-during-create scenarios fail since we are
+      looking for the name within the DB's catalog tables.
+      [ticket:571]
+
 - postgres
     - Repaired server_side_cursors to properly detect 
       text() clauses.
-      
+
 - mysql
     - Added 'CALL' to the list of SQL keywords which return
       result rows.
index 3c1721f9d9e39ea396127e7e63a6c2ddaa6f2c1e..f4c8d42711e284d4c10851e1ce2d4ac634497891 100644 (file)
@@ -16,7 +16,7 @@ as the base class for their own corresponding classes.
 import re, random
 from sqlalchemy.engine import base
 from sqlalchemy.sql import compiler, expression
-
+from sqlalchemy import exceptions
 
 AUTOCOMMIT_REGEXP = re.compile(r'\s*(?:UPDATE|INSERT|CREATE|DELETE|DROP|ALTER)',
                                re.I | re.UNICODE)
@@ -81,7 +81,10 @@ class DefaultDialect(base.Dialect):
             typeobj = typeobj()
         return typeobj
 
-
+    def validate_identifier(self, ident):
+        if len(ident) > self.max_identifier_length:
+            raise exceptions.IdentifierError("Identifier '%s' exceeds maximum length of %d characters" % (ident, self.max_identifier_length))
+        
     def oid_column_name(self, column):
         return None
 
index 43623df93fa0ce1efb4c239c19c059ef8c3c9802..8b74bb193ee0ef8fa1131d948921e691a41b2696 100644 (file)
@@ -34,7 +34,9 @@ class ConcurrentModificationError(SQLAlchemyError):
 class CircularDependencyError(SQLAlchemyError):
     """Raised by topological sorts when a circular dependency is detected"""
 
-
+class IdentifierError(SQLAlchemyError):
+    """Raised when a schema name is beyond the max character limit"""
+    
 class FlushError(SQLAlchemyError):
     """Raised when an invalid condition is detected upon a ``flush()``."""
 
index 1fe9ef0622b2c40e5fe2d9441dfc8b504b5264d1..439710cee545f246ec0926bebb6a80f86300fef0 100644 (file)
@@ -752,8 +752,14 @@ class SchemaGenerator(DDLBase):
     def get_column_specification(self, column, first_pk=False):
         raise NotImplementedError()
 
+    def _can_create(self, table):
+        self.dialect.validate_identifier(table.name)
+        if table.schema:
+            self.dialect.validate_identifier(table.schema)
+        return not self.checkfirst or not self.dialect.has_table(self.connection, table.name, schema=table.schema)
+
     def visit_metadata(self, metadata):
-        collection = [t for t in metadata.table_iterator(reverse=False, tables=self.tables) if (not self.checkfirst or not self.dialect.has_table(self.connection, t.name, schema=t.schema))]
+        collection = [t for t in metadata.table_iterator(reverse=False, tables=self.tables) if self._can_create(t)]
         for table in collection:
             self.traverse_single(table)
         if self.dialect.supports_alter:
@@ -909,13 +915,19 @@ class SchemaDropper(DDLBase):
         self.dialect = dialect
 
     def visit_metadata(self, metadata):
-        collection = [t for t in metadata.table_iterator(reverse=True, tables=self.tables) if (not self.checkfirst or  self.dialect.has_table(self.connection, t.name, schema=t.schema))]
+        collection = [t for t in metadata.table_iterator(reverse=True, tables=self.tables) if self._can_drop(t)]
         if self.dialect.supports_alter:
             for alterable in self.find_alterables(collection):
                 self.drop_foreignkey(alterable)
         for table in collection:
             self.traverse_single(table)
 
+    def _can_drop(self, table):
+        self.dialect.validate_identifier(table.name)
+        if table.schema:
+            self.dialect.validate_identifier(table.schema)
+        return not self.checkfirst or self.dialect.has_table(self.connection, table.name, schema=table.schema)
+
     def visit_index(self, index):
         self.append("\nDROP INDEX " + self.preparer.format_index(index))
         self.execute()
index cbcd4636eb1d06071b3b0960edbdbd19f97247d3..ff130846bc4ecfa4219ff4d9f8db9630a11de45c 100644 (file)
@@ -1,5 +1,6 @@
 import testenv; testenv.configure_for_tests()
 from sqlalchemy import *
+from sqlalchemy import exceptions
 from testlib import *
 from sqlalchemy.engine import default
 
@@ -38,6 +39,14 @@ class LongLabelsTest(TestBase, AssertsCompiledSQL):
         metadata.drop_all()
         testing.db.dialect.max_identifier_length = maxlen
 
+    def test_too_long_name_disallowed(self):
+        m = MetaData(testing.db)
+        t1 = Table("this_name_is_too_long_for_what_were_doing_in_this_test", m, Column('foo', Integer))
+        self.assertRaises(exceptions.IdentifierError, m.create_all)
+        self.assertRaises(exceptions.IdentifierError, m.drop_all)
+        self.assertRaises(exceptions.IdentifierError, t1.create)
+        self.assertRaises(exceptions.IdentifierError, t1.drop)
+        
     def test_result(self):
         table1.insert().execute(**{"this_is_the_primarykey_column":1, "this_is_the_data_column":"data1"})
         table1.insert().execute(**{"this_is_the_primarykey_column":2, "this_is_the_data_column":"data2"})