Создать функцию SQL, ссылающуюся на несуществующую (пока) таблицу или столбец.

Я хочу загрузить некоторые функции SQL в пустую базу данных через psql:

psql -d my_database -f fuctions.sql --set ON_ERROR_STOP=1 

Я использую --set ON_ERROR_STOP=1, потому что хочу, чтобы psql не работал, если скрипт содержит ошибки.

Содержание functions.sql:

CREATE or REPLACE FUNCTION test_function() RETURNS INT AS $$
  SELECT id from test_table;
$$ LANGUAGE sql;

Моя проблема в том, что psql проверяет, существует ли test_table при загрузке функции, и терпит неудачу с этой ошибкой:

ERROR:  relation "test_table" does not exist LINE 2: SELECT id from test_table;

Но я не хочу, чтобы psql проверял, существует ли таблица, потому что я создам эту таблицу позже.

Следующие обходные пути будут работать, но я не могу их использовать:

  • Игнорировать ошибки. Я хочу, чтобы psql завершал работу с ошибкой, если скрипт содержит, например, синтаксические ошибки sql.
  • Используйте функции plpgsql вместо sql. Конечно, я мог бы, но простые функции SQL часто являются лучшим выбором.
  • Сначала создайте таблицу. Мой реальный сценарий на самом деле сложнее, чем этот пример.

person Tom-db    schedule 20.03.2015    source источник
comment
Знаете ли вы имена затронутых таблиц (и соответствующих функций) заранее ( := еще до их существования)?   -  person joop    schedule 14.04.2015
comment
Вы можете использовать таблицу в качестве каталога приложений, где зарегистрированы таблицы (и функции). С помощью триггеров в этой таблице вы можете автоматизировать генерацию фактических функций (заменив функции-заглушки) и, возможно, даже таблиц.   -  person joop    schedule 16.04.2015


Ответы (3)


Сообщение об ошибке исходит от Postgres, а не от psql.

Обходной путь

Если вы не можете сначала создать таблицу (по какой-либо причине), вы можете «подделывать ее, пока не сделаете»: создайте временную таблицу с соответствующей структурой. Вам нужны только имена столбцов и типы, чтобы они совпадали. Для вашего примера функции:

CREATE TEMP TABLE test_table (id int);

Затем проходит CREATE FUNCTION. Опускать стол позже не запрещается. Postgres не сохраняет зависимости для кода в теле функции. Таким образом, вы можете удалить таблицу после создания функции. Если вы вызовете функцию после удаления временной таблицы, вы получите сообщение об ошибке.

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

Отключить синтаксический анализ функции SQL во время создания?

Насколько мне известно, это невозможно. Возможно, для Postgres есть параметр времени компиляции, чтобы отключить его. В руководстве рекомендуется использовать PL/PgSQL для таких случаев, как ваш:

Примечание. Все тело SQL-функции анализируется до того, как она будет выполнена. Хотя функция SQL может содержать команды, которые изменяют системные каталоги (например, CREATE TABLE), эффекты таких команд не будут видны во время синтаксического анализа более поздних команд в функции. Таким образом, например, CREATE TABLE foo (...); INSERT INTO foo VALUES(...); не будет работать должным образом, если его упаковать в одну функцию SQL, поскольку foo еще не будет существовать, когда будет разобрана команда INSERT. В подобных ситуациях рекомендуется использовать PL/PgSQL вместо функции SQL.

Жирный акцент мой.

person Erwin Brandstetter    schedule 20.03.2015
comment
На самом деле у меня большое количество скриптов и функций, и я не хочу проверять схему базы данных в каждой функции. Я надеялся, что в psql (или внутри функции sql?) есть простая опция, которая сообщает postgres «не проверять схему!» - person Tom-db; 20.03.2015
comment
@TommasoDiBucchianico: я не знаю ни одной возможности подавить синтаксический анализ во время создания функции. Я добавил цитату и ссылку на руководство. - person Erwin Brandstetter; 20.03.2015
comment
Спасибо, Эрвин, я буду использовать pl/pgsql вместо процедур sql. - person Tom-db; 16.04.2015

Вы можете установить для переменной конфигурации check_function_bodies значение false перед созданием функций.

Например, это должно позволить вам создать тестовую функцию, даже если test_table не существует:

BEGIN;
SET LOCAL check_function_bodies TO FALSE;
CREATE or REPLACE FUNCTION test_function() RETURNS INT AS $$
  SELECT id from test_table;
$$ LANGUAGE sql;
COMMIT;

Документация: http://www.postgresql.org/docs/9.5/static/runtime-config-client.html#GUC-CHECK-FUNCTION-BODIES

person Martin Bouladour    schedule 02.05.2016
comment
Можно ли принудительно выполнить повторную проверку после того, как все функции/таблицы будут на месте? похоже на компиляцию Oracle? - person Pyrocks; 29.01.2019

Вы можете разделить свой sql-файл, например. в DDL, DML, а затем выполнение функций.

Так что-то вроде

файл1:

CREATE TABLE foo (
   id int primary key,
   data int);

файл2:

CREATE or REPLACE FUNCTION test_function() RETURNS INT AS $$
  SELECT id from test_table;
$$ LANGUAGE sql;
....

А затем вызов psql что-то вроде

psql -f file1 
psql -f file2
psql -f ....
person frlan    schedule 20.03.2015
comment
Спасибо, но я не могу сначала создать таблицу. Мой реальный сценарий на самом деле более сложен, чем этот пример. - person Tom-db; 20.03.2015