def _collect_delete_commands(base_mapper, uowtransaction, table, states_to_delete): """Identify values to use in DELETE statements for a list of states to be deleted.""" delete = util.defaultdict(list) for state, state_dict, mapper, has_identity, connection \ in states_to_delete: if not has_identity or table not in mapper._pks_by_table: continue params = {} delete[connection].append(params) for col in mapper._pks_by_table[table]: params[col.key] = \ value = \ mapper._get_state_attr_by_column( state, state_dict, col) if value is None: raise sa_exc.FlushError( "Can't delete from table " "using NULL for primary " "key value") if mapper.version_id_col is not None and \ table.c.contains_column(mapper.version_id_col): params[mapper.version_id_col.key] = \ mapper._get_committed_state_attr_by_column( state, state_dict, mapper.version_id_col) return delete
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