-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Michael Bayer ha scritto:
> On Feb 28, 2010, at 8:12 PM, Manlio Perillo wrote:
> 
> Michael Bayer ha scritto:
>>>> [...]
>>>> * I have noted that some database objects not directly attached to a
>>>> metadata, like a Sequence, can leave the database in an inconsistent
>>>> state if the associated test suite fails.
>>>>
>>>> I have the same problem with the Schema object.
>>>> If, for some reason, in the test suite there is an error before the
>>>> schema is dropped, the successive run of the test suite will fail,
>>>> since the test will try to create an already existing schema.
>>>>
>>>> Is there a good solution?
>>>>
>>>>> the tests support a --dropfirst option that will remove any errant Table 
>>>>> objects.  I haven't observed errant sequences to be an issue except 
>>>>> perhaps on Oracle, but this system could be enhanced to drop all the 
>>>>> sequences too.
>>>>> for testing a CREATE SCHEMA construct I would just check the SQL string 
>>>>> and not actually execute anything.   unit tests should not create any 
>>>>> schemas.    see the existing schema-listing tests that just use the three 
>>>>> which are part of the documented testing environment.
> I can't, since I have also to check the `has_schema` method in the Dialect.
> 
>> call has_schema() on "test_schema", which will exist in a full test 
>> environment.  then call it on something like "sa_fake_schema_123" to test 
>> the "not found" condition.
> 

Ok, done.

I'm not sure, however, if `test_schema` method test should go in
`test/engine/test_reflection.py`, since it is not directly related to
reflection.

With the old patch
(http://www.sqlalchemy.org/trac/attachment/ticket/1679/schema.patch)
the code was in the HasSchemaTest class, so it was ok (the same is done
for HasSequenceTest).


P.S.: I have removed the supports_schema flag.


Thanks  Manlio
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkuLuxsACgkQscQJ24LbaURcuQCfRwMZWyW+zPEacm9g/BVURSqq
R50AnRY7AJx/OjQhhz/xUFZaEIKLgNe2
=/PF3
-----END PGP SIGNATURE-----
-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalch...@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

diff --git a/lib/sqlalchemy/__init__.py b/lib/sqlalchemy/__init__.py
--- a/lib/sqlalchemy/__init__.py
+++ b/lib/sqlalchemy/__init__.py
@@ -104,6 +104,7 @@
     PrimaryKeyConstraint,
     Sequence,
     Table,
+    Schema,
     ThreadLocalMetaData,
     UniqueConstraint,
     )
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -650,6 +650,19 @@
     def _get_default_schema_name(self, connection):
         return connection.scalar("select current_schema()")
 
+    def has_schema(self, connection, schema):
+        cursor = connection.execute(
+            sql.text(
+                "select nspname from pg_namespace where lower(nspname)=:schema",
+                bindparams=[
+                    sql.bindparam(
+                        'schema', unicode(schema.lower()),
+                        type_=sqltypes.Unicode)]
+            )
+        )
+
+        return bool(cursor.first())
+
     def has_table(self, connection, table_name, schema=None):
         # seems like case gets folded in pg_class...
         if schema is None:
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -80,6 +80,45 @@
     else:
         return schema + "." + name
 
+class Schema(SchemaItem):
+    """Represent a schema in a database.
+    
+    e.g.::
+    
+        myschema = Schema("myschema")
+
+    Constructor arguments are as follows:
+    
+    :param name: The name of this schema as represented in the database. 
+
+        Names which contain no upper case characters
+        will be treated as case insensitive names, and will not be quoted
+        unless they are a reserved word.  Names with any number of upper
+        case characters will be quoted and sent exactly.  Note that this
+        behavior applies even for databases which standardize upper 
+        case names as case insensitive such as Oracle.
+
+    :param info: A dictionary which defaults to ``{}``.  A space to store application 
+        specific data. This must be a dictionary.
+
+    :param quote: Force quoting of this schema's name on or off, corresponding
+        to ``True`` or ``False``.  When left at its default of ``None``,
+        the schema identifier will be quoted according to whether the name is
+        case sensitive (identifiers with at least one upper case character are 
+        treated as case sensitive), or if it's a reserved word.  This flag 
+        is only needed to force quoting of a reserved word which is not known
+        by the SQLAlchemy dialect.
+
+    """
+    
+    __visit_name__ = 'schema'
+
+    def __init__(self, name, **kwargs):
+        self.name = name
+        self.quote = kwargs.pop('quote', None)
+        self.info = kwargs.pop('info', {})
+
+
 class Table(SchemaItem, expression.TableClause):
     """Represent a table in a database.
     
@@ -2309,6 +2348,20 @@
         """
         return False
 
+class CreateSchema(_CreateDropBase):
+    """Represent a CREATE SCHEMA statement."""
+    
+    __visit_name__ = "create_schema"
+    
+class DropSchema(_CreateDropBase):
+    """Represent a DROP SCHEMA statement."""
+
+    __visit_name__ = "drop_schema"
+
+    def __init__(self, element, cascade=False, **kw):
+        self.cascade = cascade
+        super(DropSchema, self).__init__(element, **kw)
+
 class CreateTable(_CreateDropBase):
     """Represent a CREATE TABLE statement."""
     
diff --git a/lib/sqlalchemy/sql/compiler.py b/lib/sqlalchemy/sql/compiler.py
--- a/lib/sqlalchemy/sql/compiler.py
+++ b/lib/sqlalchemy/sql/compiler.py
@@ -680,6 +680,9 @@
             text += " OFFSET " + str(select._offset)
         return text
 
+    def visit_schema(self, schema):
+        return None
+
     def visit_table(self, table, asfrom=False, **kwargs):
         if asfrom:
             if getattr(table, "schema", None):
@@ -978,6 +981,15 @@
         
         return ddl.statement % context
 
+    def visit_create_schema(self, create):
+        return "CREATE SCHEMA " + self.preparer.format_schema(create.element)
+        
+    def visit_drop_schema(self, drop):
+        text = "DROP SCHEMA " + self.preparer.format_schema(drop.element)
+        if drop.cascade:
+            text += " CASCADE"
+        return text
+
     def visit_create_table(self, create):
         table = create.element
         preparer = self.dialect.identifier_preparer
@@ -1421,6 +1433,13 @@
     def format_constraint(self, constraint):
         return self.quote(constraint.name, constraint.quote)
     
+    def format_schema(self, schema, name=None):
+        """Prepare a quoted schema name."""
+
+        if name is None:
+            name = schema.name
+        return self.quote(name, schema.quote)
+
     def format_table(self, table, use_schema=True, name=None):
         """Prepare a quoted table and schema name."""
 
diff --git a/test/engine/test_reflection.py b/test/engine/test_reflection.py
--- a/test/engine/test_reflection.py
+++ b/test/engine/test_reflection.py
@@ -833,6 +833,16 @@
 
 class SchemaTest(TestBase):
 
+    def test_schema(self):
+        s = schema.Schema('sa_schema')
+        t1 = str(schema.CreateSchema(s).compile(bind=testing.db))
+        t2 = str(schema.DropSchema(s).compile(bind=testing.db))
+        t3 = str(schema.DropSchema(s, cascade=True).compile(bind=testing.db))
+
+        assert t1 == "CREATE SCHEMA sa_schema"
+        assert t2 == "DROP SCHEMA sa_schema"
+        assert t3 == "DROP SCHEMA sa_schema CASCADE"
+
     def test_iteration(self):
         metadata = MetaData()
         table1 = Table('table1', metadata,
@@ -926,7 +936,15 @@
         testing.db.execute(schema.DropSequence(s2))
         eq_(testing.db.dialect.has_sequence(testing.db, 'user_id_seq', schema=test_schema), False)
         eq_(testing.db.dialect.has_sequence(testing.db, 'user_id_seq'), False)
-        
+
+
+class HasSchemaTest(TestBase):
+
+    @testing.requires.schemas
+    def test_has_schema(self):
+        eq_(testing.db.dialect.has_schema(testing.db, 'test_schema'), True)
+        eq_(testing.db.dialect.has_sequence(testing.db, 'sa_fake_schema_123'), False)
+
 # Tests related to engine.reflection
 
 

Reply via email to