I am using PostgreSQL version 10 on macOS 10.12.6 and would like to use a custom plpgsql function in a query which shall be accessible to HugSQL. The following ansatz works correctly:
-- :name do-something! :! :1
CREATE OR REPLACE FUNCTION helper()
... (function body of helper)
LANGUAGE plpgsql;
INSERT INTO SomeTable (someColumn) VALUES (helper());
This works since HugSQL allows me to write multi-line SQL statements and I can include the function definition of helper().
However, I wonder whether it's actually efficient to do so since now I am redefining the function every time the query do-something! is run. I have tried to put the function definition at the top of the input file, but it only resulted in a compiler exception.
Question: What is the best way to this?
I found a solution. Here it is for the convenience of other users of HugSQL.
The function can be defined in the migrations file that sets up the tables (I use migratus for this purpose). The scope of the function definitions is identical to the scope of the tables, so if the migration reads
CREATE OR REPLACE FUNCTION helper()
... (function body of helper)
LANGUAGE plpqsql;
CREATE TABLE IF NOT EXISTS SomeTable
(...row definitions);
then the function helper() can be used in the query posted above without having to (re-)define it prior to usage:
-- :name do-something! :! :1
INSERT INTO SomeTable (someColumn) VALUES (helper());
Related
I'd like to create an IMMUTABLE wrapper function as discussed by https://stackoverflow.com/a/11007216/14731 but it's not clear how to proceed. The above Stackoverflow answer provides the following example:
For example, given:
CREATE OR REPLACE FUNCTION public.immutable_unaccent(regdictionary, text)
RETURNS text LANGUAGE c IMMUTABLE PARALLEL SAFE STRICT AS
'$libdir/unaccent', 'unaccent_dict';
CREATE OR REPLACE FUNCTION public.f_unaccent(text)
RETURNS text LANGUAGE sql IMMUTABLE PARALLEL SAFE STRICT AS
$func$
SELECT public.immutable_unaccent(regdictionary 'public.unaccent', $1)
$func$;
I scanned all the libraries in lib/ and as far as I can tell none of them export functions related to record_out(). Any ideas?
The record_out() function is an internal built-in function. You can get its definition like this:
select pg_get_functiondef('record_out'::regproc);
pg_get_functiondef
----------------------------------------------------------
CREATE OR REPLACE FUNCTION pg_catalog.record_out(record)+
RETURNS cstring +
LANGUAGE internal +
STABLE PARALLEL SAFE STRICT +
AS $function$record_out$function$ +
I don't know for what purpose you want the wrapping function. It only remains to warn that such a change may bring unexpected results.
I was running into the issue defined in the following article, and the first answer solved my main concern of naming a parameter the same name as a table column. My new concern is that my function/procedure parameters are widely used and the name of my functions/procedures are fairly detailed.
PL/pgSQL column name the same as variable
Is there a way to define an alias for a function or procedure name - to be used inside its body?
Current code:
CREATE OR REPLACE PROCEDURE dbo.PR_DeleteCrazyNamedItemByCrazyNamedID(in NamedID UUID)
LANGUAGE plpgsql AS
$BODY$
DECLARE
BEGIN
Delete from dbo.Table t where PR_DeleteCrazyNamedItemByCrazyNamedID.NamedID = t.NamedID;
...
Desired code:
CREATE OR REPLACE PROCEDURE dbo.PR_DeleteCrazyNamedItemByCrazyNamedID(in NamedID UUID) as proc
LANGUAGE plpgsql AS
$BODY$
DECLARE
BEGIN
Delete from dbo.Table t where proc.NamedID = t.NamedID;
...
Not directly. The function name seems to be visible as record containing input parameters inside the function body, but it is not accessible for an ALIAS as suggested in my referenced answer because it actually serves as outer label. The manual:
Note
There is actually a hidden “outer block” surrounding the body of any
PL/pgSQL function. This block provides the declarations of the
function's parameters (if any), as well as some special variables such
as FOUND (see Section 42.5.5). The outer block is labeled with the
function's name, meaning that parameters and special variables can be
qualified with the function's name.
But you can combine an ALIAS for function parameters with an outer block label (one nesting level below the built-in outer block labeled with the function name) like this:
General example with a function:
CREATE OR REPLACE FUNCTION weird_procedure_name(named_id int)
RETURNS TABLE (referenced_how text, input_value int) LANGUAGE plpgsql AS
$func$
<< proc >> -- outer label!
DECLARE
named_id ALIAS FOR named_id; -- sic!
BEGIN
RETURN QUERY VALUES
('weird_procedure_name.named_id', weird_procedure_name.named_id)
, ('proc.named_id', proc.named_id)
, ('named_id', named_id)
;
END
$func$;
SELECT * FROM weird_procedure_name(666);
referenced_how | input_value
:---------------------------- | ----------:
weird_procedure_name.named_id | 666
proc.named_id | 666
named_id | 666
db<>fiddle here
named_id ALIAS FOR named_id; seems to be pointless noise, but now the input parameter is accessible via block label - effectively doing what you ask for. (You might chose a different name while being at it.)
And I would certainly add a code comment explaining why label and alias are needed, lest the next smart developer should be tempted to remove either.
Applied to your example:
CREATE OR REPLACE PROCEDURE PR_DeleteCrazyNamedItemByCrazyNamedID(in NamedID UUID)
LANGUAGE plpgsql AS
$BODY$
<< proc >> -- !
DECLARE
NamedID ALIAS FOR NamedID; -- sic!
BEGIN
DELETE FROM dbo.tbl t WHERE t.NamedID = proc.NamedID; -- what you wanted !
END
$BODY$;
I would still much rather work with unique parameter names to begin with, so no qualification is required at all. I like to prefix all parameter names with underscore (like: _named_id) - and never do the same for other object names.
I create a postgres function like this:
CREATE OR REPLACE FUNCTION delete_annotation_f(
IN cmtid uuid)
RETURNS bool AS $$
DECLARE
pid uuid;
cmt_cnt int4;
BEGIN
SELECT get_comment_cnt_f(cmtid) INTO cmt_cnt;
UPDATE detail_t SET ann_cmt_cnt=ann_cmt_cnt - cmt_cnt;
RETURN TRUE;
END
$$
LANGUAGE plpgsql;
But when I run this function I get this error:
ERROR: column reference "cmt_cnt" is ambiguous
LINE 1: ...detail_t SET ann_cmt_cnt=ann_cmt_cnt-cmt_cnt WH...
I find this link On Inset: column reference "score" is ambiguous but it could not help me solve the problem. Anyone have solutions?
You are using a variable that have the same name of a column. The query parser could not decide which it must chose, change your variable name then the ambiguity will vanish.
https://www.postgresql.org/docs/current/static/plpgsql-implementation.html
By default, PL/pgSQL will report an error if a name in a SQL statement
could refer to either a variable or a table column. You can fix such a
problem by renaming the variable or column, or by qualifying the
ambiguous reference, or by telling PL/pgSQL which interpretation to
prefer. The simplest solution is to rename the variable or column. A common
coding rule is to use a different naming convention for PL/pgSQL
variables than you use for column names. For example, if you
consistently name function variables v_something while none of your
column names start with v_, no conflicts will occur.
and further:
You can also set the behavior on a function-by-function basis, by
inserting one of these special commands at the start of the function
text:
#variable_conflict error
#variable_conflict use_variable
#variable_conflict use_column
Can you have an SQL block that accepts a variable for input, uses that variable in a join and returns a result set.
The kicker is that I have been asked to do this outside a function - i.e. within an SQL block.
So, for example, I want to pass the value 1234567890 to the variable v_hold:
DO $$
declare
v_hold integer;
BEGIN
select * from t_table_A where ID = v_hold ;
--return alert_mesg;
END$$;
--$$ LANGUAGE plpgsql
The testing I've done says that in order to return a result set, you have to define that in a RETURN TABLE declaration. I've tried to define this outside a function, but I haven't figured it out.
Can this be done outside a function - i.e pass a variable and return a result set based on a select statement which references a variable in the where clause?
You could try using a prepared statement. For example:
PREPARE myStatement (int) AS SELECT * FROM t_table_A where ID = $1;
To then run the statement use the execute command:
EXECUTE myStatement(1234567890);
From the documentation:
DO executes an anonymous code block, or in other words a transient anonymous function in a procedural language.
The code block is treated as though it were the body of a function with no parameters, returning void. It is parsed and executed a single time.
You could generate your code block with a shell script and get the sort of effect you are looking for.
Does the function that returns the trigger automagically have the ability to reference the NEW and OLD pseudo-rows in a before-update trigger designated for each row?
CREATE TRIGGER foo_trigger BEFORE UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE foo();
CREATE FUNCTION foo() RETURNS trigger AS $foo_trigger$
BEGIN
NEW.taxrate := 5.5;
RETURN NEW;
END;
$foo_trigger$ LANGUAGE plpgsql;
And must the string inside $....$ in the final line of the function $foo_trigger$ LANGUAGE plpgsql; exactly match the name of the trigger in the CREATE TRIGGER statement, or is it just a placeholder?
automagically have the ability to reference the NEW and OLD pseudo-rows in a before-update trigger designated for each row
Yes.
Quote from the manual http://www.postgresql.org/docs/current/static/plpgsql-trigger.html
When a PL/pgSQL function is called as a trigger, several special variables are created automatically in the top-level block.
Must the string [...] exactly match the name of the trigger in the CREATE TRIGGER statement, or is it just a placeholder
No.
The $..$ thing is called "dollar quoting" and simply replaces single quotes to make handling large strings (with embedded quotes) easier. The only requirement is that you use the same "key" at the start and at the end. You can use $body$ (which is very common) or anything you like.
See the manual for details: http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING