SQL0526N when referencing a DGTT (Declared global temporary table) in a FUNCTION - db2

I would like to return content from a pre-created DGTT as a result-set for a table function.
But when I reference it, I get SQL SQL0526N / SQLSTATE 42995 error.
I can't see why. Documentation says only 3 reasons for 42995 sqlstate error:
Restrictions on the use of declared temporary tables: Declared temporary tables cannot:
- Be specified in an ALTER, COMMENT, GRANT, LOCK, RENAME or REVOKE statement (SQLSTATE 42995).
- Be referenced in an AUDIT, CREATE ALIAS, or CREATE VIEW statement (SQLSTATE 42995).
- Be specified in referential constraints (SQLSTATE 42995).
But none of them are being used.. The function is pretty simple:
BEGIN ATOMIC
CALL DBAREP.SP_ROW_CHECKSUM(sSchema, sTable, sColumnList, sWhere, iRows) ;
RETURN
(SELECT * FROM SESSION.DBAREP_ROW_CHECKSUM ) ;
END#
What rule am I violating this time?

It's really not explicitly stated in docs, but Declared GTTs can't be used in inlined compound statements (with BEGIN ATOMIC ... END).
You may use Created GTT in an inlined compound statement instead.

Related

Can I create and access a table in the same SQL function?

I am trying to create a Postgres SQL-function which runs some routine for my database.
The SQL-function calls a plpgsql-function which creates several temporary tables, but doesn't return anything (RETURNS void).
One of the tables created by the plpgsql-function is supposed to be used in my sql-function.
CREATE OR REPLACE FUNCTION public.my_sql_function()
RETURNS text AS
$BODY$
select public.my_plpsql_function(); -- this returns void, but has created a temp table "tmp_tbl"
DROP TABLE IF EXISTS mytable CASCADE;
CREATE TABLE mytable (
skov_id int8 PRIMARY KEY,
skov_stor int4,
skov_areal_ha numeric,
virkningfra timestamp(0) without time zone,
plannoejagtighed float8,
vertikalnoejagtighed float8,
geom geometry(MultiPolygon,25832),
orig_geom geometry(Polygon, 25832)
);
INSERT INTO mytable
select * from tmp_tbl ....
$BODY$ LANGUAGE sql;
When I try to run the lines, I get the following error:
ERROR: relation "tmp_tbl" does not exist
pgAdmin underlines the line select * from tmp_tbl ... as the part with an error.
So the SQL-function doesn't notice that the plpsql-function has created a temporary table.
Is there a workaround?
Creating and accessing a table in the same SQL function is generally impossible. Makes no difference whether you create the table in the SQL function directly or in a nested function call. All objects must be visible to begin with.
There is a big, fat note at the top of the chapter Query Language (SQL) Functions in the manual pointing that out:
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.
Related:
Why can PL/pgSQL functions have side effect, while SQL functions can't?
Difference between language sql and language plpgsql in PostgreSQL functions
I think so it is not possible - and minimally it should not by possible in future versions. SQL functions are similar to views, and then references to database object should be valid in function's creating time.
There is not any workaround - if you need temp table, use PLpgSQL, or try to write your code without temp table (it can be much better).

How can I define a plpgsql function that accepts a parameter of a type that is not schema qualified and is not yet created

My database is structured with a schema per application user. In each schema there is an identical table named "entries" with the exact same DDL. I also have a common schema that hosts some functions that operate on those "entries" tables, and during execution the search path defines which schema's entries table the function operates on. I had a problem defining those functions before i create any user schema because they reference the non yet created tables in the user schemata. That problem was solved using set check_function_bodies = off; But now I have a similar problem with a function that references those tables as an inout parameter. I cannot create that function because a parameter that it accepts is of type "entries" and I get an error of "type entries does not exist". set check_function_bodies = off does not solve this problem. Is there a way to solve this problem?
The function signature is
create or replace function common.reconcile_user_entry(inout e entries) returns void
I really dont want to define this function in each user schema as it will always be the same, and i dont want to have to recompile it in every schema when I need to make a change.
You could use inheritance for that.
Define a table common.entries just like all other entries tables and change the other entries tables to inherit from it:
ALTER TABLE schema1.entries INHERIT common.entries;
Then define the function to have common.entries as parameter or return type, and you can use it with any of the inherited tables.
I think you could use table inheritance.
You would have to create a "parent" table in common schema for every table you have in users schema. Then inherit users tables from them. Use "parent" table names when defining function parameters. See example:
CREATE TABLE parent(id integer);
CREATE TABLE child() INHERITS (parent);
INSERT INTO child(id) VALUES (1);
CREATE FUNCTION get_id(IN prm_row parent) RETURNS integer
LANGUAGE SQL
AS $$
SELECT prm_row.id;
$$;
SELECT get_id((SELECT (child) FROM child WHERE id = 1));
Note, that parent table will containt the records from all the child tables, so you should use permission to prevent users accessing the parent tables.
Instead of going with inheritance I ended up solving it by circumventing the problem like this:
Instead of defining the function signature as
create or replace function common.reconcile_user_entry(inout e entries) returns void
I defined it as
create or replace function common.reconcile_user_entry(inout r record) returns void
and in the function code I declare a variable e entries; and then assign the record into the variable:
declare
e entries;
begin
select into e r.*
...
Regarding the inheritance solution, it works, but I found the above solution much simpler.
I know there must be caveats and hidden problems with my solution, if someone can point, please do.

Cannot drop priorly modified new table in execute block

I'm not well acquainted with FB database and its subtleties.
On script executing, the problem occurres:
EXECUTE ibeblock
AS
BEGIN
-- 1. Create temporary table
execute statement 'recreate GLOBAL TEMPORARY table TMPTBL (ID bigint) /*on commit delete rows*/;';
commit;
-- 2. dummy fill of temporary table
insert into tmptbl (ID)
values (0xFE);
commit; -- not necessary
-- 3. perform some actions...
-- 4. Delete temporary table
execute statement 'drop table TMPTBL;';
commit; -- FAILURE!
END
The idea of script is primitive: 1) create temporary table; 2) fill it with records; 3) perform actions on other DB objects using populated records; 4) drop temp table.
For simulation, step-3 is useless (skipped). Step-4 leads to an error on commit: "This operation is not defined for system tables. unsuccessful metadata update. object TABLE "TMPTBL" is in use.".
Neither triggers nor constraints are applied for the table. Obviously, there should be nothing locking temp table.
Help, please, with resolution. Hopefully I missed something.
P.S.: FB 2.5, IBExpert 2017.12.13.1 used as DB managing tool
There are a number of problems with your code:
A global temporary table is intended as a permanent object, it is just the content that is temporary (either for transaction or connection duration). So normally you would create a global temporary table once, and not drop it, but instead reuse its definition.
Although you technically can execute DDL using execute statement, you are not supposed to, and it is not guaranteed to work. Your code is specifically an example of one of the things that will not work.
The problem here, is that you are trying to drop the table in the same transaction that used it (though to be honest, I'm surprised the insert even worked, because normally you can't insert into a table that was created in the same transaction).
The insert you executed on TMPTBL will mark the table in use, and given the transaction isn't committed yet, you can't drop the table: it is in use.
You shouldn't call commit in PSQL code (to be honest, I thought this wasn't even possible).
In short, you need to rethink how you use global temporary tables: define it once, and do not use execute statement to create it, but create it separately.
If you do want to create and drop it and not retain the definition of the global temporary table, then create it before the execute block, commit, then the execute block (with only the inserts and the 'perform some actions'), commit, and then drop it (and commit).
Alternatively, you might get away with executing the create using execute statement ... with autonomous transaction, the inserts and the 'perform some actions' in another execute statement ... with autonomous transaction, and finally the drop in yet another execute statement ... with autonomous transaction. However that makes your code very brittle, and this is not a recommend approach.
I have been forced again by devops guys to find robust solution to provide DB structure upgrades. Requirements: safely combine DDL and DML statements; ability to create temporary tables (for heavy selections); leave no garbage. Of course, upgrade is handled within single connection.
Referencing to the clues given by Mark a deeper insight and lots of experiments were made.
Here is template filescript that really worked out (isql native utility used):
SET TERM #;
-- 1. Create temporary table
EXECUTE BLOCK
AS
BEGIN
execute statement 'recreate GLOBAL TEMPORARY table TMPTBL (ID bigint) /*on commit preserve rows*/;';
END#
commit#
-- Data manipulations
EXECUTE BLOCK
AS
declare xid bigint;
BEGIN
-- 2. dummy fill of temporary table
begin
insert into TMPTBL (ID) values (0xFE);
end
-- 3. perform some actions...
for
select tt.ID
from TMPTBL tt
into :xid
do
begin
-- use :xid var
end
END#
commit#
-- 4. Delete temporary table
EXECUTE BLOCK
AS
BEGIN
execute statement 'drop table TMPTBL;';
END#
commit#
SET TERM ;#
Might be usefull for someone.
Damn, Firebird do drives crazy!

Create non conflicting temporary tables in a Pl/pgSQL function

I want to create a TEMPORARY TABLE in a Pl/pgSQL function because I want to index it before doing some process. The fact that any concurrent call to the function will try to reuse the same table seems to be a problem.
e.g. A first call to the function creates and uses a temporary table named "test" with data depending on the function parameters. A second concurrent call tries also to create and use the temporary table with the same name but with different data...
The doc says
"Temporary tables are automatically dropped at the end of a session,
or optionally at the end of the current transaction"
I guess the problem would not exist if temporary tables created with the "ON COMMIT DROP" option would only be visible to the current transaction. Is this the case?
If not, how to automatically create independent tables from two different function calls?
I could probably try to create a temporary name and check if a table with this name already exists but that seems like a lot of management to me...
Temporary tables of distinct sessions cannot conflict because each session has a dedicated temporary schema, only visible to the current session.
In current Postgres only one transaction runs inside the same session at a time. So only two successive calls in the same session can see the same temporary objects. ON COMMIT DROP, like you found, limits the lifespan of temp tables to the current transaction, avoiding conflicts with other transactions.
If you (can) have temp tables that don't die with the transaction (like if you want to keep using some of those tables after the end of the current transaction), then an alternative approach would be to truncate instead of create if the temp table already exists - which is a bit cheaper, too.
Wrapped into a function:
CREATE OR REPLACE FUNCTION f_create_or_trunc_temp_table(_tbl text, OUT _result "char") AS
$func$
BEGIN
SELECT INTO _result relkind
FROM pg_catalog.pg_class
WHERE relnamespace = pg_my_temp_schema() -- only temp objects!
AND relname = _tbl;
IF NOT FOUND THEN -- not found
EXECUTE format('CREATE TEMP TABLE %I(id int)', _tbl);
ELSIF _result = 'r' THEN -- table exists
EXECUTE format('TRUNCATE TABLE %I', _tbl); -- assuming identical table definition
ELSE -- other temp object occupies name
RAISE EXCEPTION 'Other temp object of type >>%<< occupies name >>%<<', _result, _tbl;
-- or do nothing, return more info or raise a warning / notice instead of an exception
END IF;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT f_create_or_trunc_temp_table('my_tbl');
This assumes identical table definition if the table exists. You might do more and also return more informative messages, etc. This is just the basic concept.
Related:
How can I determine if a table exists in the current search_path with PLPGSQL?
How to check if a table exists in a given schema
Temporary tables are visible only in the current session. Concurrent processes do not see each other's temporary tables even when they share the same names. Per the documentation:
PostgreSQL requires each session to issue its own CREATE TEMPORARY TABLE command for each temporary table to be used. This allows different sessions to use the same temporary table name for different purposes (...)

Is it possible to create a char column which is always lowercase?

I want to create a table like:
create table project_types (
id char(20) not null unique default 'xxx'
};
To use it from other tables as:
create table other_table (
...
fk_ptype char(20),
fk_ptype_on_other_table" foreign key (fk_ptype) references project_type(id)
);
The catch is I'd want all values inserted into project_types to become automatically lowercase: I don't want to be making the conversion on each possible query, I want a table that no matter what I throw at it, it returns back lowercase tokens.
I'm thinking about making a trigger on insert an on update, but I'm wondering if there's a better way to impose such a restriction. Also, this solution means I'll have to make the conversion on deletions.
For the ones that may suggest that I do this with enums: the types are dynamic, so I prefer this approach.
UPDATE 2017.04.17: The idea of this question is not to put controls/transformations everywhere in the stack: if database can handle whatever you throw at it, then you don't have to 1. check/transform in the front-end, 2. check/transform in the back-end code, and finally 3. check/transform in the database. You just avoid doing 1 and 2 because you know database will handle whatever you throw at it and that you'll have correct data when you select from it.
I'm tempted to choose #herbert-pimentel answer, but it seems the same approach cannot bo used for delete (I tried setting and on-delete trigger using the same function but it didn't work).
how about a trigger with before insert or update to ensure/transform your data lower case;
CREATE OR REPLACE FUNCTION public.fun_trg_lowercase()
RETURNS trigger AS
$BODY$
begin
NEW.my_char_field = lowercase(NEW.my_char_field);
RETURN NEW;
end;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE TRIGGER biu_lowercase_field
BEFORE INSERT OR UPDATE
ON mytable
FOR EACH ROW
EXECUTE PROCEDURE fun_trg_lowercase();
Check constraint:
create table project_types (
id char(20) not null unique default 'xxx'
check (id = lower(id))
);
You can use a special type of data for this purpose, called CITEXT (=case insensitive text). It is an additionally supplied module standard within PostgreSQL.
Citing the PostgreSQL documentation on CITEXT:
F.8.1. Rationale
The standard approach to doing case-insensitive matches in PostgreSQL has been to use the lower function when comparing values, for example
SELECT * FROM tab WHERE lower(col) = LOWER(?);
This works reasonably well, but has a number of drawbacks:
It makes your SQL statements verbose, and you always have to remember to use lower on both the column and the query value.
It won't use an index, unless you create a functional index using lower.
If you declare a column as UNIQUE or PRIMARY KEY, the implicitly generated index is case-sensitive. So it's useless for case-insensitive searches, and it won't enforce uniqueness case-insensitively.
The citext data type allows you to eliminate calls to lower in SQL queries, and allows a primary key to be case-insensitive. citext is locale-aware, just like text, which means that the matching of upper case and lower case characters is dependent on the rules of the database's LC_CTYPE setting. Again, this behavior is identical to the use of lower in queries. But because it's done transparently by the data type, you don't have to remember to do anything special in your queries.
So, in your specific case, you just would need to do:
One time:
CREATE EXTENSION citext ;
CREATE TABLE project_types
(
id citext PRIMARY KEY default 'xxx'
);
CREATE TABLE other_table
(
/* ... */
fk_ptype citext,
fk_ptype_on_other_table foreign key (fk_ptype) references project_type(id)
);
... and then, do nothing to your queries. Don't have any extra constraints and don't have any (apparently feared) trigger.