diff options
Diffstat (limited to 'lib/sqlalchemy/schema.py')
-rw-r--r-- | lib/sqlalchemy/schema.py | 144 |
1 files changed, 77 insertions, 67 deletions
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 9a4bf4109..1f0b52ace 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -27,7 +27,7 @@ are part of the SQL expression language, they are usable as components in SQL expressions. """ import re, inspect -from sqlalchemy import types, exceptions, util, databases +from sqlalchemy import types, exc, util, databases from sqlalchemy.sql import expression, visitors URL = None @@ -42,10 +42,11 @@ class SchemaItem(object): """Base class for items that define a database schema.""" __metaclass__ = expression._FigureVisitName - + quote = None + def _init_items(self, *args): """Initialize the list of child items for this SchemaItem.""" - + for item in args: if item is not None: item._set_parent(self) @@ -95,7 +96,7 @@ class _TableSingleton(expression._FigureVisitName): try: table = metadata.tables[key] if not useexisting and table._cant_override(*args, **kwargs): - raise exceptions.InvalidRequestError( + raise exc.InvalidRequestError( "Table '%s' is already defined for this MetaData instance. " "Specify 'useexisting=True' to redefine options and " "columns on an existing Table object." % key) @@ -104,7 +105,7 @@ class _TableSingleton(expression._FigureVisitName): return table except KeyError: if mustexist: - raise exceptions.InvalidRequestError( + raise exc.InvalidRequestError( "Table '%s' not defined" % (key)) try: return type.__call__(self, name, metadata, *args, **kwargs) @@ -182,17 +183,19 @@ class Table(SchemaItem, expression.TableClause): Deprecated; this is an oracle-only argument - "schema" should be used in its place. - quote - When True, indicates that the Table identifier must be quoted. - This flag does *not* disable quoting; for case-insensitive names, - use an all lower case identifier. + quote + Force quoting of the identifier on or off, based on `True` or + `False`. Defaults to `None`. This flag is rarely needed, + as quoting is normally applied + automatically for known reserved words, as well as for + "case sensitive" identifiers. An identifier is "case sensitive" + if it contains non-lowercase letters, otherwise it's + considered to be "case insensitive". quote_schema - When True, indicates that the schema identifier must be quoted. - This flag does *not* disable quoting; for case-insensitive names, - use an all lower case identifier. + same as 'quote' but applies to the schema identifier. + """ - super(Table, self).__init__(name) self.metadata = metadata self.schema = kwargs.pop('schema', kwargs.pop('owner', None)) @@ -214,7 +217,7 @@ class Table(SchemaItem, expression.TableClause): self._set_parent(metadata) - self.__extra_kwargs(**kwargs) + self.__extra_kwargs(**kwargs) # load column definitions from the database if 'autoload' is defined # we do it after the table is in the singleton dictionary to support @@ -234,7 +237,7 @@ class Table(SchemaItem, expression.TableClause): autoload_with = kwargs.pop('autoload_with', None) schema = kwargs.pop('schema', None) if schema and schema != self.schema: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Can't change schema of existing table from '%s' to '%s'", (self.schema, schema)) @@ -258,8 +261,8 @@ class Table(SchemaItem, expression.TableClause): ['autoload', 'autoload_with', 'schema', 'owner'])) def __extra_kwargs(self, **kwargs): - self.quote = kwargs.pop('quote', False) - self.quote_schema = kwargs.pop('quote_schema', False) + self.quote = kwargs.pop('quote', None) + self.quote_schema = kwargs.pop('quote_schema', None) if kwargs.get('info'): self._info = kwargs.pop('info') @@ -488,9 +491,13 @@ class Column(SchemaItem, expression._ColumnClause): or subtype of Integer. quote - When True, indicates that the Column identifier must be quoted. - This flag does *not* disable quoting; for case-insensitive names, - use an all lower case identifier. + Force quoting of the identifier on or off, based on `True` or + `False`. Defaults to `None`. This flag is rarely needed, + as quoting is normally applied + automatically for known reserved words, as well as for + "case sensitive" identifiers. An identifier is "case sensitive" + if it contains non-lowercase letters, otherwise it's + considered to be "case insensitive". """ name = kwargs.pop('name', None) @@ -499,7 +506,7 @@ class Column(SchemaItem, expression._ColumnClause): args = list(args) if isinstance(args[0], basestring): if name is not None: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "May not pass name positionally and as a keyword.") name = args.pop(0) if args: @@ -507,7 +514,7 @@ class Column(SchemaItem, expression._ColumnClause): (isinstance(args[0], type) and issubclass(args[0], types.AbstractType))): if type_ is not None: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "May not pass type_ positionally and as a keyword.") type_ = args.pop(0) @@ -520,15 +527,17 @@ class Column(SchemaItem, expression._ColumnClause): self.default = kwargs.pop('default', None) self.index = kwargs.pop('index', None) self.unique = kwargs.pop('unique', None) - self.quote = kwargs.pop('quote', False) + self.quote = kwargs.pop('quote', None) self.onupdate = kwargs.pop('onupdate', None) self.autoincrement = kwargs.pop('autoincrement', True) self.constraints = util.Set() self.foreign_keys = util.OrderedSet() + util.set_creation_order(self) + if kwargs.get('info'): self._info = kwargs.pop('info') if kwargs: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Unknown arguments passed to Column: " + repr(kwargs.keys())) def __str__(self): @@ -545,7 +554,7 @@ class Column(SchemaItem, expression._ColumnClause): bind = property(bind) def references(self, column): - """Return True if this references the given column via a foreign key.""" + """Return True if this Column references the given column via foreign key.""" for fk in self.foreign_keys: if fk.references(column.table): return True @@ -576,14 +585,14 @@ class Column(SchemaItem, expression._ColumnClause): def _set_parent(self, table): if self.name is None: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Column must be constructed with a name or assign .name " "before adding to a Table.") if self.key is None: self.key = self.name self.metadata = table.metadata if getattr(self, 'table', None) is not None: - raise exceptions.ArgumentError("this Column already has a table!") + raise exc.ArgumentError("this Column already has a table!") if not self._is_oid: self._pre_existing_column = table._columns.get(self.key) @@ -594,7 +603,7 @@ class Column(SchemaItem, expression._ColumnClause): if self.primary_key: table.primary_key.replace(self) elif self.key in table.primary_key: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Trying to redefine primary-key column '%s' as a " "non-primary-key column on table '%s'" % ( self.key, table.fullname)) @@ -604,14 +613,14 @@ class Column(SchemaItem, expression._ColumnClause): if self.index: if isinstance(self.index, basestring): - raise exceptions.ArgumentError( + raise exc.ArgumentError( "The 'index' keyword argument on Column is boolean only. " "To create indexes with a specific name, create an " "explicit Index object external to the Table.") Index('ix_%s' % self._label, self, unique=self.unique) elif self.unique: if isinstance(self.unique, basestring): - raise exceptions.ArgumentError( + raise exc.ArgumentError( "The 'unique' keyword argument on Column is boolean only. " "To create unique constraints or indexes with a specific " "name, append an explicit UniqueConstraint to the Table's " @@ -631,17 +640,17 @@ class Column(SchemaItem, expression._ColumnClause): """Create a copy of this ``Column``, unitialized. This is used in ``Table.tometadata``. - """ + """ return Column(self.name, self.type, self.default, key = self.key, primary_key = self.primary_key, nullable = self.nullable, _is_oid = self._is_oid, quote=self.quote, index=self.index, autoincrement=self.autoincrement, *[c.copy() for c in self.constraints]) - - def _make_proxy(self, selectable, name = None): + + def _make_proxy(self, selectable, name=None): """Create a *proxy* for this column. This is a copy of this ``Column`` referenced by a different parent (such as an alias or select statement). - """ + """ fk = [ForeignKey(f._colspec) for f in self.foreign_keys] c = Column(name or self.name, self.type, self.default, key = name or self.key, primary_key = self.primary_key, nullable = self.nullable, _is_oid = self._is_oid, quote=self.quote, *fk) c.table = selectable @@ -654,7 +663,6 @@ class Column(SchemaItem, expression._ColumnClause): [c._init_items(f) for f in fk] return c - def get_children(self, schema_visitor=False, **kwargs): if schema_visitor: return [x for x in (self.default, self.onupdate) if x is not None] + \ @@ -670,8 +678,8 @@ class ForeignKey(SchemaItem): For a composite (multiple column) FOREIGN KEY, use a ForeignKeyConstraint within the Table definition. - """ + """ def __init__(self, column, constraint=None, use_alter=False, name=None, onupdate=None, ondelete=None, deferrable=None, initially=None): """Construct a column-level FOREIGN KEY. @@ -742,14 +750,15 @@ class ForeignKey(SchemaItem): def references(self, table): """Return True if the given table is referenced by this ForeignKey.""" - return table.corresponding_column(self.column) is not None def get_referent(self, table): """Return the column in the given table referenced by this ForeignKey. Returns None if this ``ForeignKey`` does not reference the given table. + """ + return table.corresponding_column(self.column) def column(self): @@ -766,22 +775,22 @@ class ForeignKey(SchemaItem): parenttable = c.table break else: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Parent column '%s' does not descend from a " "table-attached Column" % str(self.parent)) m = re.match(r"^(.+?)(?:\.(.+?))?(?:\.(.+?))?$", self._colspec, re.UNICODE) if m is None: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Invalid foreign key column specification: %s" % self._colspec) if m.group(3) is None: (tname, colname) = m.group(1, 2) schema = None else: - (schema,tname,colname) = m.group(1,2,3) + (schema, tname, colname) = m.group(1, 2, 3) if _get_table_key(tname, schema) not in parenttable.metadata: - raise exceptions.NoReferencedTableError( + raise exc.NoReferencedTableError( "Could not find table '%s' with which to generate a " "foreign key" % tname) table = Table(tname, parenttable.metadata, @@ -797,13 +806,13 @@ class ForeignKey(SchemaItem): else: self._column = table.c[colname] except KeyError, e: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Could not create ForeignKey '%s' on table '%s': " "table '%s' has no column named '%s'" % ( self._colspec, parenttable.name, table.name, str(e))) - - elif isinstance(self._colspec, expression.Operators): - self._column = self._colspec.clause_element() + + elif hasattr(self._colspec, '__clause_element__'): + self._column = self._colspec.__clause_element__() else: self._column = self._colspec @@ -906,12 +915,11 @@ class ColumnDefault(DefaultGenerator): defaulted = argspec[3] is not None and len(argspec[3]) or 0 if positionals - defaulted > 1: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "ColumnDefault Python function takes zero or one " "positional arguments") return fn - def _visit_name(self): if self.for_update: return "column_onupdate" @@ -926,12 +934,12 @@ class Sequence(DefaultGenerator): """Represents a named database sequence.""" def __init__(self, name, start=None, increment=None, schema=None, - optional=False, quote=False, **kwargs): + optional=False, quote=None, **kwargs): super(Sequence, self).__init__(**kwargs) self.name = name self.start = start self.increment = increment - self.optional=optional + self.optional = optional self.quote = quote self.schema = schema self.kwargs = kwargs @@ -960,7 +968,6 @@ class Sequence(DefaultGenerator): bind = _bind_or_error(self) bind.drop(self, checkfirst=checkfirst) - class Constraint(SchemaItem): """A table-level SQL constraint, such as a KEY. @@ -989,8 +996,11 @@ class Constraint(SchemaItem): self.initially = initially def __contains__(self, x): - return self.columns.contains_column(x) - + return x in self.columns + + def contains_column(self, col): + return self.columns.contains_column(col) + def keys(self): return self.columns.keys() @@ -1105,7 +1115,7 @@ class ForeignKeyConstraint(Constraint): self.onupdate = onupdate self.ondelete = ondelete if self.name is None and use_alter: - raise exceptions.ArgumentError("Alterable ForeignKey/ForeignKeyConstraint requires a name") + raise exc.ArgumentError("Alterable ForeignKey/ForeignKeyConstraint requires a name") self.use_alter = use_alter def _set_parent(self, table): @@ -1113,7 +1123,7 @@ class ForeignKeyConstraint(Constraint): if self not in table.constraints: table.constraints.add(self) for (c, r) in zip(self.__colnames, self.__refcolnames): - self.append_element(c,r) + self.append_element(c, r) def append_element(self, col, refcol): fk = ForeignKey(refcol, constraint=self, name=self.name, onupdate=self.onupdate, ondelete=self.ondelete, use_alter=self.use_alter) @@ -1159,7 +1169,7 @@ class PrimaryKeyConstraint(Constraint): deferrable=kwargs.pop('deferrable', None), initially=kwargs.pop('initially', None)) if kwargs: - raise exceptions.ArgumentError( + raise exc.ArgumentError( 'Unknown PrimaryKeyConstraint argument(s): %s' % ', '.join([repr(x) for x in kwargs.keys()])) @@ -1174,14 +1184,14 @@ class PrimaryKeyConstraint(Constraint): def add(self, col): self.columns.add(col) - col.primary_key=True + col.primary_key = True append_column = add def replace(self, col): self.columns.replace(col) def remove(self, col): - col.primary_key=False + col.primary_key = False del self.columns[col.key] def copy(self): @@ -1222,7 +1232,7 @@ class UniqueConstraint(Constraint): deferrable=kwargs.pop('deferrable', None), initially=kwargs.pop('initially', None)) if kwargs: - raise exceptions.ArgumentError( + raise exc.ArgumentError( 'Unknown UniqueConstraint argument(s): %s' % ', '.join([repr(x) for x in kwargs.keys()])) @@ -1295,11 +1305,11 @@ class Index(SchemaItem): self._set_parent(column.table) elif column.table != self.table: # all columns muse be from same table - raise exceptions.ArgumentError( + raise exc.ArgumentError( "All index columns must be from same table. " "%s is from %s not %s" % (column, column.table, self.table)) elif column.name in [ c.name for c in self.columns ]: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "A column may not appear twice in the " "same index (%s already has column %s)" % (self.name, column)) self.columns.append(column) @@ -1370,7 +1380,7 @@ class MetaData(SchemaItem): self.ddl_listeners = util.defaultdict(list) if reflect: if not bind: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "A bind must be supplied in conjunction with reflect=True") self.reflect() @@ -1508,7 +1518,7 @@ class MetaData(SchemaItem): missing = [name for name in only if name not in available] if missing: s = schema and (" schema '%s'" % schema) or '' - raise exceptions.InvalidRequestError( + raise exc.InvalidRequestError( 'Could not reflect: requested table(s) not available ' 'in %s%s: (%s)' % (bind.engine.url, s, ', '.join(missing))) load = [name for name in only if name not in current] @@ -1777,12 +1787,12 @@ class DDL(object): """ if not isinstance(statement, basestring): - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Expected a string or unicode SQL statement, got '%r'" % statement) if (on is not None and (not isinstance(on, basestring) and not callable(on))): - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Expected the name of a database dialect or a callable for " "'on' criteria, got type '%s'." % type(on).__name__) @@ -1858,10 +1868,10 @@ class DDL(object): """ if not hasattr(schema_item, 'ddl_listeners'): - raise exceptions.ArgumentError( + raise exc.ArgumentError( "%s does not support DDL events" % type(schema_item).__name__) if event not in schema_item.ddl_events: - raise exceptions.ArgumentError( + raise exc.ArgumentError( "Unknown event, expected one of (%s), got '%r'" % (', '.join(schema_item.ddl_events), event)) schema_item.ddl_listeners[event].append(self) @@ -1955,5 +1965,5 @@ def _bind_or_error(schemaitem): 'Execution can not proceed without a database to execute ' 'against. Either execute with an explicit connection or ' 'assign %s to enable implicit execution.') % (item, bindable) - raise exceptions.UnboundExecutionError(msg) + raise exc.UnboundExecutionError(msg) return bind |