summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/engine/ddl.py102
-rw-r--r--lib/sqlalchemy/schema.py103
2 files changed, 169 insertions, 36 deletions
diff --git a/lib/sqlalchemy/engine/ddl.py b/lib/sqlalchemy/engine/ddl.py
index 6b5684f64..dca183f9a 100644
--- a/lib/sqlalchemy/engine/ddl.py
+++ b/lib/sqlalchemy/engine/ddl.py
@@ -22,33 +22,58 @@ class SchemaGenerator(DDLBase):
self.preparer = dialect.identifier_preparer
self.dialect = dialect
- def _can_create(self, table):
+ def _can_create_table(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)
+ return not self.checkfirst or \
+ not self.dialect.has_table(self.connection,
+ table.name, schema=table.schema)
+
+ def _can_create_sequence(self, sequence):
+ return self.dialect.supports_sequences and \
+ (
+ (not self.dialect.sequences_optional or
+ not sequence.optional) and
+ (
+ not self.checkfirst or
+ not self.dialect.has_sequence(
+ self.connection,
+ sequence.name,
+ schema=sequence.schema)
+ )
+ )
def visit_metadata(self, metadata):
if self.tables:
tables = self.tables
else:
tables = metadata.tables.values()
- collection = [t for t in sql_util.sort_tables(tables) if self._can_create(t)]
+ collection = [t for t in sql_util.sort_tables(tables)
+ if self._can_create_table(t)]
+ seq_coll = [s for s in metadata._sequences
+ if s.column is None and self._can_create_sequence(s)]
metadata.dispatch.before_create(metadata, self.connection,
- tables=collection)
+ tables=collection,
+ checkfirst=self.checkfirst)
+
+ for seq in seq_coll:
+ self.traverse_single(seq, create_ok=True)
for table in collection:
self.traverse_single(table, create_ok=True)
metadata.dispatch.after_create(metadata, self.connection,
- tables=collection)
+ tables=collection,
+ checkfirst=self.checkfirst)
def visit_table(self, table, create_ok=False):
- if not create_ok and not self._can_create(table):
+ if not create_ok and not self._can_create_table(table):
return
- table.dispatch.before_create(table, self.connection)
+ table.dispatch.before_create(table, self.connection,
+ checkfirst=self.checkfirst)
for column in table.columns:
if column.default is not None:
@@ -60,15 +85,13 @@ class SchemaGenerator(DDLBase):
for index in table.indexes:
self.traverse_single(index)
- table.dispatch.after_create(table, self.connection)
+ table.dispatch.after_create(table, self.connection,
+ checkfirst=self.checkfirst)
- def visit_sequence(self, sequence):
- if self.dialect.supports_sequences:
- if ((not self.dialect.sequences_optional or
- not sequence.optional) and
- (not self.checkfirst or
- not self.dialect.has_sequence(self.connection, sequence.name, schema=sequence.schema))):
- self.connection.execute(schema.CreateSequence(sequence))
+ def visit_sequence(self, sequence, create_ok=False):
+ if not create_ok and not self._can_create_sequence(sequence):
+ return
+ self.connection.execute(schema.CreateSequence(sequence))
def visit_index(self, index):
self.connection.execute(schema.CreateIndex(index))
@@ -87,31 +110,52 @@ class SchemaDropper(DDLBase):
tables = self.tables
else:
tables = metadata.tables.values()
- collection = [t for t in reversed(sql_util.sort_tables(tables)) if self._can_drop(t)]
+ collection = [t for t in reversed(sql_util.sort_tables(tables))
+ if self._can_drop_table(t)]
+ seq_coll = [s for s in metadata._sequences
+ if s.column is None and self._can_drop_sequence(s)]
metadata.dispatch.before_drop(metadata, self.connection,
- tables=collection)
+ tables=collection,
+ checkfirst=self.checkfirst)
for table in collection:
self.traverse_single(table, drop_ok=True)
+ for seq in seq_coll:
+ self.traverse_single(seq, drop_ok=True)
+
metadata.dispatch.after_drop(metadata, self.connection,
- tables=collection)
+ tables=collection,
+ checkfirst=self.checkfirst)
- def _can_drop(self, table):
+ def _can_drop_table(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)
+ return not self.checkfirst or self.dialect.has_table(self.connection,
+ table.name, schema=table.schema)
+
+ def _can_drop_sequence(self, sequence):
+ return self.dialect.supports_sequences and \
+ ((not self.dialect.sequences_optional or
+ not sequence.optional) and
+ (not self.checkfirst or
+ self.dialect.has_sequence(
+ self.connection,
+ sequence.name,
+ schema=sequence.schema))
+ )
def visit_index(self, index):
self.connection.execute(schema.DropIndex(index))
def visit_table(self, table, drop_ok=False):
- if not drop_ok and not self._can_drop(table):
+ if not drop_ok and not self._can_drop_table(table):
return
- table.dispatch.before_drop(table, self.connection)
+ table.dispatch.before_drop(table, self.connection,
+ checkfirst=self.checkfirst)
for column in table.columns:
if column.default is not None:
@@ -119,12 +163,10 @@ class SchemaDropper(DDLBase):
self.connection.execute(schema.DropTable(table))
- table.dispatch.after_drop(table, self.connection)
+ table.dispatch.after_drop(table, self.connection,
+ checkfirst=self.checkfirst)
- def visit_sequence(self, sequence):
- if self.dialect.supports_sequences:
- if ((not self.dialect.sequences_optional or
- not sequence.optional) and
- (not self.checkfirst or
- self.dialect.has_sequence(self.connection, sequence.name, schema=sequence.schema))):
- self.connection.execute(schema.DropSequence(sequence))
+ def visit_sequence(self, sequence, drop_ok=False):
+ if not drop_ok and not self._can_drop_sequence(sequence):
+ return
+ self.connection.execute(schema.DropSequence(sequence))
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py
index 3a4bd90ce..40d7de945 100644
--- a/lib/sqlalchemy/schema.py
+++ b/lib/sqlalchemy/schema.py
@@ -1219,6 +1219,7 @@ class DefaultGenerator(SchemaItem):
is_sequence = False
is_server_default = False
+ column = None
def __init__(self, for_update=False):
self.for_update = for_update
@@ -1336,14 +1337,84 @@ class ColumnDefault(DefaultGenerator):
return "ColumnDefault(%r)" % self.arg
class Sequence(DefaultGenerator):
- """Represents a named database sequence."""
+ """Represents a named database sequence.
+
+ The :class:`.Sequence` object represents the name and configurational
+ parameters of a database sequence. It also represents
+ a construct that can be "executed" by a SQLAlchemy :class:`.Engine`
+ or :class:`.Connection`, rendering the appropriate "next value" function
+ for the target database and returning a result.
+
+ The :class:`.Sequence` is typically associated with a primary key column::
+
+ some_table = Table('some_table', metadata,
+ Column('id', Integer, Sequence('some_table_seq'), primary_key=True)
+ )
+
+ When CREATE TABLE is emitted for the above :class:`.Table`, if the
+ target platform supports sequences, a CREATE SEQUENCE statement will
+ be emitted as well. For platforms that don't support sequences,
+ the :class:`.Sequence` construct is ignored.
+
+ See also: :class:`.CreateSequence` :class:`.DropSequence`
+
+ """
__visit_name__ = 'sequence'
is_sequence = True
def __init__(self, name, start=None, increment=None, schema=None,
- optional=False, quote=None, metadata=None, for_update=False):
+ optional=False, quote=None, metadata=None,
+ for_update=False):
+ """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
+ clause is omitted, which on most platforms indicates a starting
+ value of 1.
+ :param increment: the increment value of the sequence. This
+ value is used when the CREATE SEQUENCE command is emitted to
+ the database as the value of the "INCREMENT BY" clause. If ``None``,
+ the clause is omitted, which on most platforms indicates an
+ increment of 1.
+ :param schema: Optional schema name for the sequence, if located
+ in a schema other than the default.
+ :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
+ key identifiers. Currently, it essentially means, "don't create
+ this sequence on the Postgresql backend, where the SERIAL keyword
+ creates a sequence for us automatically".
+ :param quote: boolean value, when ``True`` or ``False``, explicitly
+ forces quoting of the schema name on or off. When left at its
+ default of ``None``, normal quoting rules based on casing and reserved
+ words take place.
+ :param metadata: optional :class:`.MetaData` object which will be
+ associated with this :class:`.Sequence`. A :class:`.Sequence`
+ that is associated with a :class:`.MetaData` gains access to the
+ ``bind`` of that :class:`.MetaData`, meaning the :meth:`.Sequence.create`
+ and :meth:`.Sequence.drop` methods will make usage of that engine
+ automatically. Additionally, the appropriate CREATE SEQUENCE/
+ DROP SEQUENCE DDL commands will be emitted corresponding to this
+ :class:`.Sequence` when :meth:`.MetaData.create_all` and
+ :meth:`.MetaData.drop_all` are invoked (new in 0.7).
+
+ Note that when a :class:`.Sequence` is applied to a :class:`.Column`,
+ the :class:`.Sequence` is automatically associated with the
+ :class:`.MetaData` object of that column's parent :class:`.Table`,
+ when that association is made. The :class:`.Sequence` will then
+ be subject to automatic CREATE SEQUENCE/DROP SEQUENCE corresponding
+ to when the :class:`.Table` object itself is created or dropped,
+ rather than that of the :class:`.MetaData` object overall.
+ :param for_update: Indicates this :class:`.Sequence`, when associated
+ with a :class:`.Column`, should be invoked for UPDATE statements
+ on that column's table, rather than for INSERT statements, when
+ no value is otherwise present for that column in the statement.
+
+ """
super(Sequence, self).__init__(for_update=for_update)
self.name = name
self.start = start
@@ -1352,6 +1423,8 @@ class Sequence(DefaultGenerator):
self.quote = quote
self.schema = schema
self.metadata = metadata
+ if metadata:
+ self._set_metadata(metadata)
@util.memoized_property
def is_callable(self):
@@ -1372,7 +1445,11 @@ class Sequence(DefaultGenerator):
column._on_table_attach(self._set_table)
def _set_table(self, column, table):
- self.metadata = table.metadata
+ self._set_metadata(table.metadata)
+
+ def _set_metadata(self, metadata):
+ self.metadata = metadata
+ self.metadata._sequences.add(self)
@property
def bind(self):
@@ -1939,6 +2016,7 @@ class MetaData(SchemaItem):
"""
self.tables = util.immutabledict()
self._schemas = set()
+ self._sequences = set()
self.bind = bind
self.metadata = self
if reflect:
@@ -2335,7 +2413,7 @@ class DDLElement(expression.Executable, expression.ClauseElement):
self.target = target
@expression._generative
- def execute_if(self, dialect=None, callable_=None):
+ def execute_if(self, dialect=None, callable_=None, state=None):
"""Return a callable that will execute this
DDLElement conditionally.
@@ -2375,10 +2453,22 @@ class DDLElement(expression.Executable, expression.ClauseElement):
Optional keyword argument - a list of Table objects which are to
be created/ dropped within a MetaData.create_all() or drop_all()
method call.
-
+
+ :state:
+ Optional keyword argument - will be the ``state`` argument
+ passed to this function.
+
+ :checkfirst:
+ Keyword argument, will be True if the 'checkfirst' flag was
+ set during the call to ``create()``, ``create_all()``,
+ ``drop()``, ``drop_all()``.
+
If the callable returns a true value, the DDL statement will be
executed.
+ :param state: any value which will be passed to the callable_
+ as the ``state`` keyword argument.
+
See also:
:class:`.DDLEvents`
@@ -2388,6 +2478,7 @@ class DDLElement(expression.Executable, expression.ClauseElement):
"""
self.dialect = dialect
self.callable_ = callable_
+ self.state = state
def _should_execute(self, target, bind, **kw):
if self.on is not None and \
@@ -2401,7 +2492,7 @@ class DDLElement(expression.Executable, expression.ClauseElement):
if bind.engine.name not in self.dialect:
return False
if self.callable_ is not None and \
- not self.callable_(self, target, bind, **kw):
+ not self.callable_(self, target, bind, state=self.state, **kw):
return False
return True