From 54fa46c4f908640b0f8657b4d1786830e36eedf1 Mon Sep 17 00:00:00 2001 From: Ethan Dalool Date: Sat, 13 Aug 2022 07:23:53 -0700 Subject: [PATCH] Use named column inserts, but remove require_all parameter. I am yet again bumping into the limits of extract_table_column_map as it does not use a real parser and does not recognize generated columns, which causes issues when I use that column list to enforce an insert. It probably would be better to move even farther away from extracting columns and just asking the database instead. Anyway we get slightly improved ergonomics of insert_filler at the cost of not being able to enforce all columns within this function. --- voussoirkit/sqlhelpers.py | 51 +++++++++++++++------------------------ 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/voussoirkit/sqlhelpers.py b/voussoirkit/sqlhelpers.py index 0b933a1..f73f4da 100644 --- a/voussoirkit/sqlhelpers.py +++ b/voussoirkit/sqlhelpers.py @@ -57,41 +57,30 @@ def delete_filler(pairs): qmarks = f'WHERE {qmarks}' return (qmarks, bindings) -def insert_filler(column_names, values, require_all=True): +def insert_filler(pairs): ''' Manually aligning the bindings for INSERT statements is annoying. - Given the table's column names and a dictionary of {column: value}, - return the question marks and the list of bindings in the right order. + Given a dictionary of {column: value}, return the question marks and the + list of bindings in the right order. - require_all: - If `values` does not contain one of the column names, should we raise - an exception? - Otherwise, that column will simply receive None. - - >>> column_names=['id', 'name', 'score'], - >>> values={'score': 20, 'id': '1111', 'name': 'James'} - >>> insert_filler(column_names, scores) - ('?, ?, ?', ['1111', 'James', 20]) + >>> insert_filler({'score': 20, 'id': '1111', 'name': 'James'}) + ('(id, name, score) VALUES (?, ?, ?)', ['1111', 'James', 20]) In context: - (qmarks, bindings) = insert_filler(COLUMN_NAMES, data) - query = f'INSERT INTO table VALUES({qmarks})' + (qmarks, bindings) = insert_filler(pairs) + query = f'INSERT INTO table {qmarks}' cur.execute(query, bindings) ''' - values = values.copy() - missings = [] - for column in column_names: - if column in values: - continue - if require_all: - missings.append(column) - else: - values[column] = None - if missings: - raise ValueError(f'Missing columns {missings}.') - qmarks = '?' * len(column_names) + column_names = [] + bindings = [] + for (key, value) in pairs.items(): + column_names.append(key) + bindings.append(value) + + column_names = ', '.join(column_names) + qmarks = '?' * len(pairs) qmarks = ', '.join(qmarks) - bindings = [values[column] for column in column_names] + qmarks = f'({column_names}) VALUES ({qmarks})' return (qmarks, bindings) def update_filler(pairs, where_key): @@ -232,10 +221,10 @@ def _extract_table_name(create_table_statement): def _extract_columns_from_table(create_table_statement): # CREATE TABLE table_name(column_name TYPE MODIFIERS, ...) constraints = {'constraint', 'foreign', 'check', 'primary', 'unique'} - column_names = create_table_statement.split('(')[1].rsplit(')', 1)[0] - column_names = column_names.split(',') - column_names = [x.strip() for x in column_names] - column_names = [x.split(' ')[0] for x in column_names] + column_statements = create_table_statement.split('(')[1].rsplit(')', 1)[0] + column_statements = column_statements.split(',') + column_statements = [x.strip() for x in column_statements] + column_names = [x.split(' ')[0] for x in column_statements] column_names = [c for c in column_names if c.lower() not in constraints] return column_names