def _collect_post_update_commands(base_mapper, uowtransaction, table, states_to_update, post_update_cols): """Identify sets of values to use in UPDATE statements for a list of states within a post_update operation. """ update = [] for state, state_dict, mapper, connection in states_to_update: if table not in mapper._pks_by_table: continue pks = mapper._pks_by_table[table] params = {} hasdata = False for col in mapper._cols_by_table[table]: if col in pks: params[col._label] = \ mapper._get_state_attr_by_column( state, state_dict, col) elif col in post_update_cols: prop = mapper._columntoproperty[col] history = attributes.get_state_history( state, prop.key, attributes.PASSIVE_NO_INITIALIZE) if history.added: value = history.added[0] params[col.key] = value hasdata = True if hasdata: update.append((state, state_dict, params, mapper, connection)) return update
def _collect_post_update_commands(base_mapper, uowtransaction, table, states_to_update, post_update_cols): """Identify sets of values to use in UPDATE statements for a list of states within a post_update operation. """ update = [] for state, state_dict, mapper, connection in states_to_update: if table not in mapper._pks_by_table: continue pks = mapper._pks_by_table[table] params = {} hasdata = False for col in mapper._cols_by_table[table]: if col in pks: params[col._label] = mapper._get_state_attr_by_column(state, state_dict, col) elif col in post_update_cols: prop = mapper._columntoproperty[col] history = attributes.get_state_history(state, prop.key, attributes.PASSIVE_NO_INITIALIZE) if history.added: value = history.added[0] params[col.key] = value hasdata = True if hasdata: update.append((state, state_dict, params, mapper, connection)) return update
def get_attribute_history(self, state, key, passive=True): hashkey = ("history", state, key) # cache the objects, not the states; the strong reference here # prevents newly loaded objects from being dereferenced during the # flush process if hashkey in self.attributes: (history, cached_passive) = self.attributes[hashkey] # if the cached lookup was "passive" and now we want non-passive, do a non-passive # lookup and re-cache if cached_passive and not passive: history = attributes.get_state_history(state, key, passive=False) self.attributes[hashkey] = (history, passive) else: history = attributes.get_state_history(state, key, passive=passive) self.attributes[hashkey] = (history, passive) if not history or not state.get_impl(key).uses_objects: return history else: return history.as_state()
def test_history(self): for base in (object, MyBaseClass, MyClass): class Foo(base): pass class Bar(base): pass attributes.register_class(Foo) attributes.register_class(Bar) attributes.register_attribute(Foo, "name", uselist=False, useobject=False) attributes.register_attribute(Foo, "bars", uselist=True, trackparent=True, useobject=True) attributes.register_attribute(Bar, "name", uselist=False, useobject=False) f1 = Foo() f1.name = 'f1' eq_(attributes.get_state_history(attributes.instance_state(f1), 'name'), (['f1'], (), ())) b1 = Bar() b1.name = 'b1' f1.bars.append(b1) eq_(attributes.get_state_history(attributes.instance_state(f1), 'bars'), ([b1], [], [])) attributes.instance_state(f1).commit_all(attributes.instance_dict(f1)) attributes.instance_state(b1).commit_all(attributes.instance_dict(b1)) eq_(attributes.get_state_history(attributes.instance_state(f1), 'name'), ((), ['f1'], ())) eq_(attributes.get_state_history(attributes.instance_state(f1), 'bars'), ((), [b1], ())) f1.name = 'f1mod' b2 = Bar() b2.name = 'b2' f1.bars.append(b2) eq_(attributes.get_state_history(attributes.instance_state(f1), 'name'), (['f1mod'], (), ['f1'])) eq_(attributes.get_state_history(attributes.instance_state(f1), 'bars'), ([b2], [b1], [])) f1.bars.remove(b1) eq_(attributes.get_state_history(attributes.instance_state(f1), 'bars'), ([b2], [], [b1]))
def test_history(self): for base in (object, MyBaseClass, MyClass): class Foo(base): pass class Bar(base): pass register_class(Foo) register_class(Bar) attributes.register_attribute(Foo, "name", uselist=False, useobject=False) attributes.register_attribute(Foo, "bars", uselist=True, trackparent=True, useobject=True) attributes.register_attribute(Bar, "name", uselist=False, useobject=False) f1 = Foo() f1.name = "f1" eq_( attributes.get_state_history(attributes.instance_state(f1), "name"), (["f1"], (), ()), ) b1 = Bar() b1.name = "b1" f1.bars.append(b1) eq_( attributes.get_state_history(attributes.instance_state(f1), "bars"), ([b1], [], []), ) attributes.instance_state(f1)._commit_all( attributes.instance_dict(f1)) attributes.instance_state(b1)._commit_all( attributes.instance_dict(b1)) eq_( attributes.get_state_history(attributes.instance_state(f1), "name"), ((), ["f1"], ()), ) eq_( attributes.get_state_history(attributes.instance_state(f1), "bars"), ((), [b1], ()), ) f1.name = "f1mod" b2 = Bar() b2.name = "b2" f1.bars.append(b2) eq_( attributes.get_state_history(attributes.instance_state(f1), "name"), (["f1mod"], (), ["f1"]), ) eq_( attributes.get_state_history(attributes.instance_state(f1), "bars"), ([b2], [b1], []), ) f1.bars.remove(b1) eq_( attributes.get_state_history(attributes.instance_state(f1), "bars"), ([b2], [], [b1]), )
def _collect_update_commands(base_mapper, uowtransaction, table, states_to_update): """Identify sets of values to use in UPDATE statements for a list of states. This function works intricately with the history system to determine exactly what values should be updated as well as how the row should be matched within an UPDATE statement. Includes some tricky scenarios where the primary key of an object might have been changed. """ update = [] for state, state_dict, mapper, connection, has_identity, instance_key, row_switch in states_to_update: if table not in mapper._pks_by_table: continue pks = mapper._pks_by_table[table] params = {} value_params = {} hasdata = hasnull = False for col in mapper._cols_by_table[table]: if col is mapper.version_id_col: params[col._label] = mapper._get_committed_state_attr_by_column( row_switch or state, row_switch and row_switch.dict or state_dict, col ) prop = mapper._columntoproperty[col] history = attributes.get_state_history(state, prop.key, attributes.PASSIVE_NO_INITIALIZE) if history.added: params[col.key] = history.added[0] hasdata = True else: params[col.key] = mapper.version_id_generator(params[col._label]) # HACK: check for history, in case the # history is only # in a different table than the one # where the version_id_col is. for prop in mapper._columntoproperty.values(): history = attributes.get_state_history(state, prop.key, attributes.PASSIVE_NO_INITIALIZE) if history.added: hasdata = True else: prop = mapper._columntoproperty[col] history = attributes.get_state_history(state, prop.key, attributes.PASSIVE_NO_INITIALIZE) if history.added: if isinstance(history.added[0], sql.ClauseElement): value_params[col] = history.added[0] else: value = history.added[0] params[col.key] = value if col in pks: if history.deleted and not row_switch: # if passive_updates and sync detected # this was a pk->pk sync, use the new # value to locate the row, since the # DB would already have set this if ("pk_cascaded", state, col) in uowtransaction.attributes: value = history.added[0] params[col._label] = value else: # use the old value to # locate the row value = history.deleted[0] params[col._label] = value hasdata = True else: # row switch logic can reach us here # remove the pk from the update params # so the update doesn't # attempt to include the pk in the # update statement del params[col.key] value = history.added[0] params[col._label] = value if value is None: hasnull = True else: hasdata = True elif col in pks: value = state.manager[prop.key].impl.get(state, state_dict) if value is None: hasnull = True params[col._label] = value if hasdata: if hasnull: raise sa_exc.FlushError("Can't update table " "using NULL for primary " "key value") update.append((state, state_dict, params, mapper, connection, value_params)) return update
def _collect_update_commands(base_mapper, uowtransaction, table, states_to_update): """Identify sets of values to use in UPDATE statements for a list of states. This function works intricately with the history system to determine exactly what values should be updated as well as how the row should be matched within an UPDATE statement. Includes some tricky scenarios where the primary key of an object might have been changed. """ update = [] for state, state_dict, mapper, connection, has_identity, \ instance_key, row_switch in states_to_update: if table not in mapper._pks_by_table: continue pks = mapper._pks_by_table[table] params = {} value_params = {} hasdata = hasnull = False for col in mapper._cols_by_table[table]: if col is mapper.version_id_col: params[col._label] = \ mapper._get_committed_state_attr_by_column( row_switch or state, row_switch and row_switch.dict or state_dict, col) prop = mapper._columntoproperty[col] history = attributes.get_state_history( state, prop.key, attributes.PASSIVE_NO_INITIALIZE ) if history.added: params[col.key] = history.added[0] hasdata = True else: params[col.key] = mapper.version_id_generator( params[col._label]) # HACK: check for history, in case the # history is only # in a different table than the one # where the version_id_col is. for prop in mapper._columntoproperty.itervalues(): history = attributes.get_state_history( state, prop.key, attributes.PASSIVE_NO_INITIALIZE) if history.added: hasdata = True else: prop = mapper._columntoproperty[col] history = attributes.get_state_history( state, prop.key, attributes.PASSIVE_NO_INITIALIZE) if history.added: if isinstance(history.added[0], sql.ClauseElement): value_params[col] = history.added[0] else: value = history.added[0] params[col.key] = value if col in pks: if history.deleted and \ not row_switch: # if passive_updates and sync detected # this was a pk->pk sync, use the new # value to locate the row, since the # DB would already have set this if ("pk_cascaded", state, col) in \ uowtransaction.attributes: value = history.added[0] params[col._label] = value else: # use the old value to # locate the row value = history.deleted[0] params[col._label] = value hasdata = True else: # row switch logic can reach us here # remove the pk from the update params # so the update doesn't # attempt to include the pk in the # update statement del params[col.key] value = history.added[0] params[col._label] = value if value is None: hasnull = True else: hasdata = True elif col in pks: value = state.manager[prop.key].impl.get( state, state_dict) if value is None: hasnull = True params[col._label] = value if hasdata: if hasnull: raise sa_exc.FlushError( "Can't update table " "using NULL for primary " "key value") update.append((state, state_dict, params, mapper, connection, value_params)) return update
def test_history(self): for base in (object, MyBaseClass, MyClass): class Foo(base): pass class Bar(base): pass register_class(Foo) register_class(Bar) attributes.register_attribute( Foo, "name", uselist=False, useobject=False ) attributes.register_attribute( Foo, "bars", uselist=True, trackparent=True, useobject=True ) attributes.register_attribute( Bar, "name", uselist=False, useobject=False ) f1 = Foo() f1.name = "f1" eq_( attributes.get_state_history( attributes.instance_state(f1), "name" ), (["f1"], (), ()), ) b1 = Bar() b1.name = "b1" f1.bars.append(b1) eq_( attributes.get_state_history( attributes.instance_state(f1), "bars" ), ([b1], [], []), ) attributes.instance_state(f1)._commit_all( attributes.instance_dict(f1) ) attributes.instance_state(b1)._commit_all( attributes.instance_dict(b1) ) eq_( attributes.get_state_history( attributes.instance_state(f1), "name" ), ((), ["f1"], ()), ) eq_( attributes.get_state_history( attributes.instance_state(f1), "bars" ), ((), [b1], ()), ) f1.name = "f1mod" b2 = Bar() b2.name = "b2" f1.bars.append(b2) eq_( attributes.get_state_history( attributes.instance_state(f1), "name" ), (["f1mod"], (), ["f1"]), ) eq_( attributes.get_state_history( attributes.instance_state(f1), "bars" ), ([b2], [b1], []), ) f1.bars.remove(b1) eq_( attributes.get_state_history( attributes.instance_state(f1), "bars" ), ([b2], [], [b1]), )