diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-08-31 18:58:22 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-08-31 18:58:22 +0000 |
commit | 0c7250a4740b26875d94fc9a1cb714fc970ea700 (patch) | |
tree | 79f3661459b4aab78681ae8c42f51944858ef0e9 /lib/sqlalchemy/schema.py | |
parent | 2e077dc627bfbc9f81cdb363a1ea1acf32f4cfe9 (diff) | |
download | sqlalchemy-0c7250a4740b26875d94fc9a1cb714fc970ea700.tar.gz |
- added case_sensitive argument to MetaData, Table, Column, determines
itself automatically based on if a parent schemaitem has a non-None
setting for the flag, or if not, then whether the identifier name is all lower
case or not. when set to True, quoting is applied to identifiers with mixed or
uppercase identifiers. quoting is also applied automatically in all cases to
identifiers that are known to be reserved words or contain other non-standard
characters. various database dialects can override all of this behavior, but
currently they are all using the default behavior. tested with postgres, mysql,
sqlite. needs more testing with firebird, oracle, ms-sql. part of the ongoing
work with [ticket:155]
Diffstat (limited to 'lib/sqlalchemy/schema.py')
-rw-r--r-- | lib/sqlalchemy/schema.py | 94 |
1 files changed, 67 insertions, 27 deletions
diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index f4e89ef24..6fc10c6cb 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -29,6 +29,8 @@ class SchemaItem(object): for item in args: if item is not None: item._set_parent(self) + def _get_parent(self): + raise NotImplementedError() def _set_parent(self, parent): """associate with this SchemaItem's parent object.""" raise NotImplementedError() @@ -39,6 +41,29 @@ class SchemaItem(object): return None def _get_engine(self): return self._derived_metadata().engine + + def _set_casing_strategy(self, name, kwargs, keyname='case_sensitive'): + setattr(self, '_SchemaItem__%s_setting' % keyname, kwargs.pop(keyname, None)) + def _determine_case_sensitive(self, name, keyname='case_sensitive'): + local = getattr(self, '_SchemaItem__%s_setting' % keyname, None) + if local is not None: + return local + parent = self + while parent is not None: + parent = parent._get_parent() + if parent is not None: + parentval = getattr(parent, '_SchemaItem__case_sensitive_setting', None) + if parentval is not None: + return parentval + return name is not None and name.lower() != name + def _get_case_sensitive(self): + try: + return self.__case_sensitive + except AttributeError: + self.__case_sensitive = self._determine_case_sensitive(self.name) + return self.__case_sensitive + case_sensitive = property(_get_case_sensitive) + engine = property(lambda s:s._get_engine()) metadata = property(lambda s:s._derived_metadata()) @@ -153,10 +178,10 @@ class Table(SchemaItem, sql.TableClause): quote_schema=False : indicates that the Namespace identifier must be properly escaped and quoted before being sent to the database. This flag overrides all other quoting behavior. - natural_case=True : indicates that the identifier should be interpreted by the database in the natural case for identifiers. + case_sensitive=True : indicates that the identifier should be interpreted by the database in the natural case for identifiers. Mixed case is not sufficient to cause this identifier to be quoted; it must contain an illegal character. - natural_case_schema=True : indicates that the identifier should be interpreted by the database in the natural case for identifiers. + case_sensitive_schema=True : indicates that the identifier should be interpreted by the database in the natural case for identifiers. Mixed case is not sufficient to cause this identifier to be quoted; it must contain an illegal character. """ super(Table, self).__init__(name) @@ -165,21 +190,26 @@ class Table(SchemaItem, sql.TableClause): self.indexes = util.OrderedProperties() self.constraints = [] self.primary_key = PrimaryKeyConstraint() - + self.quote = kwargs.get('quote', False) + self.quote_schema = kwargs.get('quote_schema', False) if self.schema is not None: self.fullname = "%s.%s" % (self.schema, self.name) else: self.fullname = self.name self.owner = kwargs.pop('owner', None) - self.quote = kwargs.pop('quote', False) - self.quote_schema = kwargs.pop('quote_schema', False) - default_natural_case = metadata.natural_case - if default_natural_case is None: - default_natural_case = True - self.natural_case = kwargs.pop('natural_case', default_natural_case) - self.natural_case_schema = kwargs.pop('natural_case_schema', default_natural_case) + + self._set_casing_strategy(name, kwargs) + self._set_casing_strategy(self.schema or '', kwargs, keyname='case_sensitive_schema') self.kwargs = kwargs + def _get_case_sensitive_schema(self): + try: + return getattr(self, '_SchemaItem__case_sensitive_schema') + except AttributeError: + setattr(self, '_SchemaItem__case_sensitive_schema', self._determine_case_sensitive(self.schema or '', keyname='case_sensitive_schema')) + return getattr(self, '_SchemaItem__case_sensitive_schema') + case_sensitive_schema = property(_get_case_sensitive_schema) + def _set_primary_key(self, pk): if getattr(self, '_primary_key', None) in self.constraints: self.constraints.remove(self._primary_key) @@ -220,7 +250,9 @@ class Table(SchemaItem, sql.TableClause): def append_index(self, index): self.indexes[index.name] = index - + + def _get_parent(self): + return self._metadata def _set_parent(self, metadata): metadata.tables[_get_table_key(self.name, self.schema)] = self self._metadata = metadata @@ -348,7 +380,7 @@ class Column(SchemaItem, sql.ColumnClause): quote=False : indicates that the Column identifier must be properly escaped and quoted before being sent to the database. - natural_case=True : indicates that the identifier should be interpreted by the database in the natural case for identifiers. + case_sensitive=True : indicates that the identifier should be interpreted by the database in the natural case for identifiers. Mixed case is not sufficient to cause this identifier to be quoted; it must contain an illegal character. """ name = str(name) # in case of incoming unicode @@ -362,7 +394,7 @@ class Column(SchemaItem, sql.ColumnClause): self.index = kwargs.pop('index', None) self.unique = kwargs.pop('unique', None) self.quote = kwargs.pop('quote', False) - self.natural_case = kwargs.pop('natural_case', True) + self._set_casing_strategy(name, kwargs) self.onupdate = kwargs.pop('onupdate', None) if self.index is not None and self.unique is not None: raise exceptions.ArgumentError("Column may not define both index and unique") @@ -405,13 +437,13 @@ class Column(SchemaItem, sql.ColumnClause): self.primary_key = True self.nullable = False self.table.primary_key.append(self) - + + def _get_parent(self): + return self.table def _set_parent(self, table): if getattr(self, 'table', None) is not None: raise exceptions.ArgumentError("this Column already has a table!") table.append_column(self) - if self.table.metadata.natural_case is not None: - self.natural_case = self.table.metadata.natural_case if self.index or self.unique: table.append_index_column(self, index=self.index, unique=self.unique) @@ -427,7 +459,7 @@ class Column(SchemaItem, sql.ColumnClause): def copy(self): """creates a copy of this Column, unitialized""" - return Column(self.name, self.type, self.default, key = self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden) + return Column(self.name, self.type, self.default, key = self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden, case_sensitive=self.case_sensitive, quote=self.quote) def _make_proxy(self, selectable, name = None): """creates a copy of this Column, initialized the way this Column is""" @@ -435,7 +467,7 @@ class Column(SchemaItem, sql.ColumnClause): fk = None else: fk = self.foreign_key.copy() - c = Column(name or self.name, self.type, fk, self.default, key = name or self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden) + c = Column(name or self.name, self.type, fk, self.default, key = name or self.key, primary_key = self.primary_key, nullable = self.nullable, hidden = self.hidden, case_sensitive=self.case_sensitive, quote=self.quote) c.table = selectable c.orig_set = self.orig_set c._parent = self @@ -539,7 +571,9 @@ class ForeignKey(SchemaItem): def accept_schema_visitor(self, visitor): """calls the visit_foreign_key method on the given visitor.""" visitor.visit_foreign_key(self) - + + def _get_parent(self): + return self.parent def _set_parent(self, column): self.parent = column @@ -565,6 +599,8 @@ class DefaultGenerator(SchemaItem): return self.column.table.metadata except AttributeError: return self._metadata + def _get_parent(self): + return self.column def _set_parent(self, column): self.column = column self._metadata = self.column.table.metadata @@ -604,14 +640,14 @@ class ColumnDefault(DefaultGenerator): class Sequence(DefaultGenerator): """represents a sequence, which applies to Oracle and Postgres databases.""" - def __init__(self, name, start = None, increment = None, optional=False, quote=False, natural_case=True, **kwargs): + def __init__(self, name, start = None, increment = None, optional=False, quote=False, **kwargs): super(Sequence, self).__init__(**kwargs) self.name = name self.start = start self.increment = increment self.optional=optional - self.natural_case = natural_case self.quote = quote + self._set_casing_strategy(name, kwargs) def __repr__(self): return "Sequence(%s)" % string.join( [repr(self.name)] + @@ -620,8 +656,6 @@ class Sequence(DefaultGenerator): def _set_parent(self, column): super(Sequence, self)._set_parent(column) column.sequence = self - if column.metadata.natural_case is not None: - self.natural_case = column.metadata.natural_case def create(self): self.engine.create(self) return self @@ -652,6 +686,8 @@ class Constraint(SchemaItem): self.columns[index] = item def copy(self): raise NotImplementedError() + def _get_parent(self): + return self.table class ForeignKeyConstraint(Constraint): """table-level foreign key constraint, represents a colleciton of ForeignKey objects.""" @@ -684,6 +720,7 @@ class PrimaryKeyConstraint(Constraint): super(PrimaryKeyConstraint, self).__init__(name=kwargs.pop('name', None)) self.__colnames = list(columns) def _set_parent(self, table): + self.table = table table.primary_key = self for c in self.__colnames: self.append(table.c[c]) @@ -700,6 +737,7 @@ class UniqueConstraint(Constraint): super(Constraint, self).__init__(name) self.__colnames = list(columns) def _set_parent(self, table): + self.table = table table.constraints.append(self) for c in self.__colnames: self.append(table.c[c]) @@ -734,7 +772,8 @@ class Index(SchemaItem): def _init_items(self, *args): for column in args: self.append_column(column) - + def _get_parent(self): + return self.table def append_column(self, column): # make sure all columns are from the same table # and no column is repeated @@ -776,18 +815,19 @@ class Index(SchemaItem): class MetaData(SchemaItem): """represents a collection of Tables and their associated schema constructs.""" - def __init__(self, name=None, natural_case=None, **kwargs): + def __init__(self, name=None, **kwargs): # a dictionary that stores Table objects keyed off their name (and possibly schema name) self.tables = {} self.name = name - self.natural_case = natural_case + self._set_casing_strategy(name, kwargs) def is_bound(self): return False def clear(self): self.tables.clear() def table_iterator(self, reverse=True): return self._sort_tables(self.tables.values(), reverse=reverse) - + def _get_parent(self): + return None def create_all(self, connectable=None, tables=None, engine=None): """create all tables stored in this metadata. |