diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2005-10-22 22:57:32 +0000 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2005-10-22 22:57:32 +0000 |
commit | f313c15e5f61c78bc1ed0cc8deb47e0e652848c6 (patch) | |
tree | 729a155b1618dc6678f6c1130325296a8cd8a06f /lib/sqlalchemy | |
parent | fe12e56166ba6da0466fb36c2bf499005f2746d7 (diff) | |
download | sqlalchemy-f313c15e5f61c78bc1ed0cc8deb47e0e652848c6.tar.gz |
oids rows insert sort orders galore
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r-- | lib/sqlalchemy/databases/postgres.py | 23 | ||||
-rw-r--r-- | lib/sqlalchemy/engine.py | 4 | ||||
-rw-r--r-- | lib/sqlalchemy/mapper.py | 36 | ||||
-rw-r--r-- | lib/sqlalchemy/objectstore.py | 22 | ||||
-rw-r--r-- | lib/sqlalchemy/schema.py | 1 | ||||
-rw-r--r-- | lib/sqlalchemy/sql.py | 11 | ||||
-rw-r--r-- | lib/sqlalchemy/util.py | 8 |
7 files changed, 73 insertions, 32 deletions
diff --git a/lib/sqlalchemy/databases/postgres.py b/lib/sqlalchemy/databases/postgres.py index cfaf63b57..74c2b5366 100644 --- a/lib/sqlalchemy/databases/postgres.py +++ b/lib/sqlalchemy/databases/postgres.py @@ -92,8 +92,13 @@ class PGSQLEngine(ansisql.ANSISQLEngine): raise "not implemented" def last_inserted_ids(self): - return self.context.last_inserted_ids - + table = self.context.last_inserted_table + if self.context.lastrowid is not None and table is not None and len(table.primary_keys): + row = sql.select(table.primary_keys, table.rowid_column == self.context.lastrowid).execute().fetchone() + return [v for v in row] + else: + return None + def pre_exec(self, connection, cursor, statement, parameters, echo = None, compiled = None, **kwargs): if True: return # if a sequence was explicitly defined we do it here @@ -123,18 +128,10 @@ class PGSQLEngine(ansisql.ANSISQLEngine): def post_exec(self, connection, cursor, statement, parameters, echo = None, compiled = None, **kwargs): if compiled is None: return if getattr(compiled, "isinsert", False): - # psycopg wants to return internal rowids, which I guess is what DBAPI2 really - # specifies. - # well then post exec to get the row. I guess this could be genericised to - # be for all inserts somehow if the "rowid" col could be gotten off a table. table = compiled.statement.table - if len(table.primary_keys): - # TODO: cache this statement against the table to avoid multiple re-compiles - # TODO: instead of "oid" have the Table object have a "rowid_col" property - # that gives this col generically - row = sql.select(table.primary_keys, sql.ColumnClause("oid",table) == bindparam('oid', cursor.lastrowid) ).execute().fetchone() - self.context.last_inserted_ids = [v for v in row] - + self.context.last_inserted_table = table + self.context.lastrowid = cursor.lastrowid + def dbapi(self): return self.module diff --git a/lib/sqlalchemy/engine.py b/lib/sqlalchemy/engine.py index 8be019ea5..4e01d1684 100644 --- a/lib/sqlalchemy/engine.py +++ b/lib/sqlalchemy/engine.py @@ -78,6 +78,10 @@ class SQLEngine(schema.SchemaEngine): def compiler(self, statement, bindparams): raise NotImplementedError() + def rowid_column_name(self): + """returns the ROWID column name for this engine.""" + return "oid" + def create(self, table, **params): table.accept_visitor(self.schemagenerator(self.proxy(), **params)) diff --git a/lib/sqlalchemy/mapper.py b/lib/sqlalchemy/mapper.py index 42e0945bb..5e39e10f6 100644 --- a/lib/sqlalchemy/mapper.py +++ b/lib/sqlalchemy/mapper.py @@ -90,8 +90,16 @@ def mapper(class_, table = None, engine = None, autoload = False, *args, **param return _mappers[hashkey] def clear_mappers(): + """removes all mappers that have been created thus far. when new mappers are + created, they will be assigned to their classes as their primary mapper.""" _mappers.clear() - + +def clear_mapper(m): + """removes the given mapper from the storage of mappers. when a new mapper is + created for the previous mapper's class, it will be used as that classes' + new primary mapper.""" + del _mappers[m.hash_key] + def eagerload(name): """returns a MapperOption that will convert the property of the given name into an eager load. Used with mapper.options()""" @@ -261,16 +269,20 @@ class Mapper(object): return self.hashkey def _init_class(self): + """sets up our classes' overridden __init__ method, this mappers hash key as its '_mapper' property, + and our columns as its 'c' property. if the class already had a mapper, the old __init__ method + is kept the same.""" + if not hasattr(self.class_, '_mapper'): + oldinit = self.class_.__init__ + def init(self, *args, **kwargs): + nohist = kwargs.pop('_mapper_nohistory', False) + if oldinit is not None: + oldinit(self, *args, **kwargs) + if not nohist: + objectstore.uow().register_new(self) + self.class_.__init__ = init self.class_._mapper = self.hashkey self.class_.c = self.c - oldinit = self.class_.__init__ - def init(self, *args, **kwargs): - nohist = kwargs.pop('_mapper_nohistory', False) - if oldinit is not None: - oldinit(self, *args, **kwargs) - if not nohist: - objectstore.uow().register_new(self) - self.class_.__init__ = init def set_property(self, key, prop): self.props[key] = prop @@ -938,7 +950,11 @@ class LazyLoader(PropertyLoader): params = {} for key in self.lazybinds.keys(): params[key] = row[key] - result = self.mapper.select(self.lazywhere, **params) + if self.secondary is not None: + order_by = [self.secondary.rowid_column] + else: + order_by = [self.target.rowid_column] + result = self.mapper.select(self.lazywhere, order_by=order_by,**params) if self.uselist: return result else: diff --git a/lib/sqlalchemy/objectstore.py b/lib/sqlalchemy/objectstore.py index 7cc9a69fb..3e216a768 100644 --- a/lib/sqlalchemy/objectstore.py +++ b/lib/sqlalchemy/objectstore.py @@ -54,20 +54,42 @@ def get_row_key(row, class_, table, primary_keys): """ return (class_, table, tuple([row[column.label] for column in primary_keys])) +def begin(): + """begins a new UnitOfWork transaction. the next commit will affect only + objects that are created, modified, or deleted following the begin statement.""" + uow().begin() + def commit(*obj): + """commits the current UnitOfWork transaction. if a transaction was begun + via begin(), commits only those objects that were created, modified, or deleted + since that begin statement. otherwise commits all objects that have been + changed.""" uow().commit(*obj) def clear(): + """removes all current UnitOfWorks and IdentityMaps for this thread and + establishes a new one. It is probably a good idea to discard all + current mapped object instances, as they are no longer in the Identity Map.""" uow.set(UnitOfWork()) def delete(*obj): + """registers the given objects as to be deleted upon the next commit""" uw = uow() for o in obj: uw.register_deleted(o) def has_key(key): + """returns True if the current thread-local IdentityMap contains the given instance key""" return uow().identity_map.has_key(key) +def has_instance(instance): + """returns True if the current thread-local IdentityMap contains the given instance""" + return uow().identity_map.has_key(instance_key(instance)) + +def instance_key(instance): + """returns the IdentityMap key for the given instance""" + return object_mapper(instance).instance_key(instance) + class UOWListElement(attributes.ListElement): def __init__(self, obj, key, data=None, deleteremoved=False): attributes.ListElement.__init__(self, obj, key, data=data) diff --git a/lib/sqlalchemy/schema.py b/lib/sqlalchemy/schema.py index 9fad004b1..47db789f4 100644 --- a/lib/sqlalchemy/schema.py +++ b/lib/sqlalchemy/schema.py @@ -130,7 +130,6 @@ class Column(SchemaItem): original = property(lambda s: s._orig or s) engine = property(lambda s: s.table.engine) - def _set_parent(self, table): table.columns[self.key] = self diff --git a/lib/sqlalchemy/sql.py b/lib/sqlalchemy/sql.py index 3ab1e5ec2..b2272541d 100644 --- a/lib/sqlalchemy/sql.py +++ b/lib/sqlalchemy/sql.py @@ -401,7 +401,6 @@ class CompoundClause(ClauseElement): """represents a list of clauses joined by an operator""" def __init__(self, operator, *clauses): self.operator = operator - self.fromobj = [] self.clauses = [] self.parens = False for c in clauses: @@ -418,7 +417,6 @@ class CompoundClause(ClauseElement): elif isinstance(clause, CompoundClause): clause.parens = True self.clauses.append(clause) - self.fromobj += clause._get_from_objects() def accept_visitor(self, visitor): for c in self.clauses: @@ -426,7 +424,10 @@ class CompoundClause(ClauseElement): visitor.visit_compound(self) def _get_from_objects(self): - return self.fromobj + f = [] + for c in self.clauses: + f += c._get_from_objects() + return f def hash_key(self): return string.join([c.hash_key() for c in self.clauses], self.operator) @@ -621,9 +622,13 @@ class TableImpl(Selectable): def __init__(self, table): self.table = table self.id = self.table.name + self.rowid_column = schema.Column(self.table.engine.rowid_column_name(), types.Integer) + self.rowid_column._set_parent(table) + del self.table.c[self.rowid_column.key] def get_from_text(self): return self.table.name + def group_parenthesized(self): return False diff --git a/lib/sqlalchemy/util.py b/lib/sqlalchemy/util.py index 2498316c1..1425d231b 100644 --- a/lib/sqlalchemy/util.py +++ b/lib/sqlalchemy/util.py @@ -24,19 +24,17 @@ class OrderedProperties(object): """ def __init__(self): self.__dict__['_list'] = [] - def keys(self): return self._list - def __iter__(self): return iter([self[x] for x in self._list]) - def __setitem__(self, key, object): setattr(self, key, object) - def __getitem__(self, key): return getattr(self, key) - + def __delitem__(self, key): + delattr(self, key) + del self._list[self._list.index(key)] def __setattr__(self, key, object): if not hasattr(self, key): self._list.append(key) |