def drop_keys(session, key_table, keys):
<EOL>if not keys:<EOL><INDENT>return<EOL><DEDENT>for key_batch in _batch_postgres_query(<EOL>key_table, [{"<STR_LIT:name>": k[<NUM_LIT:0>], "<STR_LIT>": k[<NUM_LIT:1>]} for k in keys.items()]<EOL>):<EOL><INDENT>all_rows = (<EOL>session.query(key_table)<EOL>.filter(key_table.name.in_([key["<STR_LIT:name>"] for key in key_batch]))<EOL>.all()<EOL>)<EOL>to_delete = set()<EOL>to_update = []<EOL>candidate_classes = key_batch[<NUM_LIT:0>]["<STR_LIT>"]<EOL>for row in all_rows:<EOL><INDENT>row.candidate_classes = list(<EOL>set(row.candidate_classes) - set(candidate_classes)<EOL>)<EOL>if len(row.candidate_classes) == <NUM_LIT:0>:<EOL><INDENT>to_delete.add(row.name)<EOL><DEDENT>else:<EOL><INDENT>to_update.append(<EOL>{"<STR_LIT:name>": row.name, "<STR_LIT>": row.candidate_classes}<EOL>)<EOL><DEDENT><DEDENT>if to_delete:<EOL><INDENT>query = session.query(key_table).filter(key_table.name.in_(to_delete))<EOL>query.delete(synchronize_session="<STR_LIT>")<EOL><DEDENT>if to_update:<EOL><INDENT>stmt = insert(key_table.__table__)<EOL>stmt = stmt.on_conflict_do_update(<EOL>constraint=key_table.__table__.primary_key,<EOL>set_={<EOL>"<STR_LIT:name>": stmt.excluded.get("<STR_LIT:name>"),<EOL>"<STR_LIT>": stmt.excluded.get("<STR_LIT>"),<EOL>},<EOL>)<EOL>session.execute(stmt, to_update)<EOL>session.commit()<EOL><DEDENT><DEDENT>
Bulk drop annotation keys to the specified table.

    Rather than directly dropping the keys, this removes the candidate_classes
    specified for the given keys only. If all candidate_classes are removed for
    a key, the key is dropped.

    :param key_table: The sqlalchemy class to insert into.
    :param keys: A map of {name: [candidate_classes]}.