Create SQL function referring to a table or column that does not exist (yet)

故事扮演 提交于 2019-12-22 18:33:39

问题


I want to load some SQL functions in a empty database through psql:

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

I use --set ON_ERROR_STOP=1 because I want that psql fails if the script contains errors.

The content of functions.sql is:

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

My problem is, that psql checks if test_table exists when loading function and fails with this error:

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

But I don't want that psql check if the table exists because I will create this table later.

Following workarounds would work but I cannot use them:

  • Ignore errors. I want psql to exit with an error if the script contains i.e. sql syntax errors.
  • Use plpgsql functions instead of sql. Of course I could, but simple sql functions are often the best choice.
  • Create the table first. My real scenario is actually more complex than this example.

回答1:


The error message comes from Postgres, not from psql.

Workaround

If you cannot create the table first (for what ever reason), you could "fake it until you make it": Create a temporary table with matching structure. You only need column names and types to match. For your example function:

CREATE TEMP TABLE test_table (id int);

Then CREATE FUNCTION goes through. Dropping the table later is not prohibited. Postgres does not save dependencies for code in the function body. So you can drop the table once the function is created. If you call the function after dropping the temp table, you get an error.

After you create the actual table later, the function will work normally.

Disable parsing of SQL function at creation time?

To my knowledge, this is not possible. Maybe there is a compile-time option for Postgres to disable it. The manual advises to use PL/PgSQL for cases like yours:

Note: The entire body of a SQL function is parsed before any of it is executed. While a SQL function can contain commands that alter the system catalogs (e.g., CREATE TABLE), the effects of such commands will not be visible during parse analysis of later commands in the function. Thus, for example, CREATE TABLE foo (...); INSERT INTO foo VALUES(...); will not work as desired if packaged up into a single SQL function, since foo won't exist yet when the INSERT command is parsed. It's recommended to use PL/PgSQL instead of a SQL function in this type of situation.

Bold emphasis mine.




回答2:


You can set the configuration variable check_function_bodies to false before creating the functions.

For instance, this should let you create your test function even though test_table doesn't exist:

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;

Documentation: http://www.postgresql.org/docs/9.5/static/runtime-config-client.html#GUC-CHECK-FUNCTION-BODIES




回答3:


You could split up yur sql-file e.g. into DDL, DML and then executing the functions.

So something like

file1:

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

file2:

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

And then calling psql something like

psql -f file1 
psql -f file2
psql -f ....


来源:https://stackoverflow.com/questions/29162066/create-sql-function-referring-to-a-table-or-column-that-does-not-exist-yet

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!