SQL Инъекционно-безопасный вызов полиморфной функции

Несколько раз я нашел рефакторинг кода веб-приложения и в конечном итоге хочу сделать что-то вроде этого (Groovy в этом случае, но может быть что угодно):

Map getData(String relationName, Integer rowId) {
    def sql = Sql.newInstance([...])
    def result = sql.firstRow('SELECT getRelationRow(?,?)', relationName, rowId)
    sql.close()
    return new HashMap(result)
}

где хранимая процедура getRelationRow(relname text, rowid integer) выполняет динамический sql для извлечения строки указанного rowid в запрошенном отношении. Лучшим примером, который я видел для такой функции, является эта полиморфная функция с использованием типа anyelement и называется

SELECT * FROM data_of(NULL::pcdmet, 17);

Однако для вызова этого в приведенном выше коде потребуется

def result = sql.firstRow("SELECT * FROM data_of(NULL::${relationName},?)",  rowId)

то есть потребовалось, чтобы имя отношения вставлялось в запрос, что угрожает SQL Injection. Итак, есть ли возможность сохранить полиморфную доброту хранимой процедуры, но разрешить ее вызывать с именами общих имен?

Ответ 1

Я не думаю, что это можно сделать так. Я предполагаю, что Groovy использует подготовленные инструкции здесь, что требует, чтобы типы ввода и возврата были известны во время подготовки, тогда как моя функция выводит тип возвращаемого значения из типа полиморфного ввода.

Я уверен, вам нужна конкатенация строк. Но не волнуйтесь, есть функции типа pg_escape() для дезинфекции имен таблиц и невозможности SQLi. Не знаю Groovy, но это тоже должно быть.

Или это?

В зависимости от функции data_of(..) в конце этого связанного ответа:

С PREPARE Я могу сделать эту работу, явно объявив тип возвращаемого значения:

PREPARE fooplan ("relationName") AS  -- table name works as row type
SELECT * FROM data_of($1, $2);

Тогда я могу передать NULL, который передается в "relationName" из подготовленного контекста:

EXECUTE fooplan(NULL, 1);

Таким образом, это может работать в конце концов, если ваш интерфейс поддерживает это. Но вы все равно должны объединить имя таблицы в качестве типа возвращаемых данных (и, следовательно, защититься от SQLi). Поймать 22, я думаю.