summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/schema.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2006-08-31 18:58:22 +0000
committerMike Bayer <mike_mp@zzzcomputing.com>2006-08-31 18:58:22 +0000
commit0c7250a4740b26875d94fc9a1cb714fc970ea700 (patch)
tree79f3661459b4aab78681ae8c42f51944858ef0e9 /lib/sqlalchemy/schema.py
parent2e077dc627bfbc9f81cdb363a1ea1acf32f4cfe9 (diff)
downloadsqlalchemy-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.py94
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.