Я пытаюсь выполнить SQL-запрос из расширения C, созданного для PostgreSQL, используя интерфейс программирования сервера (SPI).Запрос должен создать новую схему с довольно большим количеством таблиц.(По сути, это должно настроить рабочее пространство для работы пользователей.) Но поскольку пользователь должен иметь возможность создавать несколько рабочих областей, я не знаю имя схемы при написании сценария.Поэтому мне нужен способ предоставить это во время выполнения.Но я не могу заставить его работать.
Я пытаюсь сделать это, используя SPI_execute_with_args
, поскольку документация гласит следующее:
SPI_execute_with_args
выполняет команду, которая может включать ссылки на внешние параметры.Текст команды ссылается на параметр как $n
, и вызов определяет типы данных и значения для каждого такого символа.read_only
и count
имеют ту же интерпретацию, что и в SPI_execute
.
Основное преимущество этой процедуры по сравнению с SPI_execute
состоит в том, что значения данных могут быть вставлены в команду без утомительного цитирования / экранирования,и, таким образом, с гораздо меньшим риском атак с использованием SQL-инъекций.
Сценарий SQL выглядит следующим образом (Если я заменим $1
на реальное имя схемы вручную и запустим его как обычный сценарий,все работает как надо):
CREATE SCHEMA $1;
ALTER SCHEMA $1 OWNER TO some_user;
CREATE FUNCTION $1.foo() ...
CREATE TABLE $1.bar ...
...
Но теперь я хочу запустить его из кода C, и поскольку в документации отсутствуют какие-либо рабочие примеры по SPI, мне пришлось поискать в Google что-нибудь, что могло бы помочьменя дальше.И я нашел этот пример на SO, где функция выглядит следующим образом:
...
Datum
foo(PG_FUNCTION_ARGS)
{
int ret;
Datum args[1];
Oid argtypes[1] = { INT4OID };
Datum result;
bool isnull;
SPI_connect();
args[0] = PG_GETARG_INT32(0);
/* ensure expected result type by casting */
ret = SPI_execute_with_args("SELECT ($1 + 10)::int",
1, argtypes, args, NULL,
true, 1);
Assert(SPI_processed == 1);
result = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
Assert(!isnull);
SPI_finish();
PG_RETURN_DATUM(result);
}
...
Это работает как следует (заменяет $1
на число, введенное какпараметр).
Но как только я начинаю изменять его для работы с собственным запросом, все ломается.Я даже не могу заставить его работать только с первой строкой запроса.
Для записи я также попытался просто выполнить простой запрос SELECT '$1'
и заменить его различными переменными.Но ничего, кроме примера, не работает.Либо сервер аварийно завершает работу, возвращает неверный синтаксис на $1
или просто возвращает $1
в качестве ответа.
Если я прав, кажется, что имеет значение где и что вы хотите заменить $1
на .И этот SPI не просто выполняет «поиск и замену» на $1
?
. Я пробовал несколько разных OID: s при тестировании различных типов переменных, таких как: ANYOID
, CSTRINGOID
,CHAROID
, REGNAMESPACEOID
, TEXTOID
и т. Д. И я попытался отправить переменную в виде массива чисто символов и в качестве указателя на текстовый блок, выделенный с помощью SPI_palloc()
или palloc()
.Но безуспешно ...
Пример кода, который я собрал из примеров и документации, которые я нашел:
PG_FUNCTION_INFO_V1(foobar);
Datum foobar(PG_FUNCTION_ARGS)
{
Datum arguments[1];
Oid argument_types[1] = { ANYOID };
Datum result;
bool isnull;
arguments[0] = "some_text";
SPI_connect();
SPI_execute_with_args("SELECT '$1'", 1, argument_types, arguments, NULL, false, 0);
result = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
SPI_finish();
PG_RETURN_DATUM(result);
}
При запуске этого кода я получаю следующий результат:
SELECT foobar();
foobar
--------
$1
(1 row)
Я не уверен, что это лучший способ сделать это, но даже если это не так, было бы неплохо узнать больше о том, как работает эта функция SPIкак мне понадобится в дальнейшем.
У кого-нибудь есть какие-нибудь рабочие примеры для этого или что-то, что подтолкнуло бы меня в правильном направлении?