Postgres function update column ambiguous - postgresql

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

Related

Alias for function or procedure name

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.

Use custom function with HugSQL

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());

Errors with an easy PL/pgSQL Function

I'm trying to write my first PL/pgSQL function. For right now it is simply supposed to return the number of characters in a value that is passed to it.
CREATE OR REPLACE FUNCTION public.cents(money)
RETURNS int
LANGUAGE plpgsql
LEAKPROOF
AS $function$
DECLARE
new_price money;
size int;
BEGIN
size := char_length(money);
RETURN size;
END;
$function$;
When I try to test with $66.66 I get one error:
select cents($66.66);
ERROR: syntax error at or near ".66"
LINE 1: select cents($66.66);
^
And if I use $66 I get a different error:
select cents($66);
ERROR: there is no parameter $66
LINE 1: select cents($66);
^
Using just the integer 66 gives me a third error:
select cents(66);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
What am I doing wrong here?
Are you sure you want to use the data type money? Consider:
PostgreSQL: Which Datatype should be used for Currency?
If you need the type money, be sure to understand the role of locale settings for this type. Read the manual.
You can't enter literals without single quotes (like numeric constants) - unless you cast them, which would be inefficient. But that's not the only error. Your function would work like this:
CREATE OR REPLACE FUNCTION public.cents(_my_money_parameter money)
RETURNS int AS
$func$
BEGIN
RETURN char_length(_my_money_parameter::text);
END
$func$ LANGUAGE plpgsql LEAKPROOF;
Call:
SELECT public.cents(money '66.66');
SELECT public.cents('66.66'::money);
SELECT public.cents(66.66::money);
The 1st call variant is the most efficient, but it depends on locale settings. The dot in my example is interpreted as thousands separator and ignored (not as decimal point) in some locales.
Notes
You treat money like a parameter name in the function body, but it's just the data type. If you want to use parameter names, you have to declare them like demonstrated. Or refer to parameters with positional references: $1, $2 etc.
char_length() expects a character data type, you cannot use it for data type money without casting. Just length() is equivalent.
If you include the dollar sign, you need single quotes for the string literal: '$66.66' - and the format must match your locale setting to work for money.
If you just supply the numeric constant 66, Postgres won't find the function with a money parameter due to the rules of function type resolution. Details:
Is there a way to disable function overloading in Postgres
Start by reading the chapter Constants in the manual.
Continue with the page on CREATE FUNCTION.
You need to put single-quotes around your input:
SELECT cents('$66.66');
If your settings don't allow this (still throw an error) you can try casting:
SELECT cents('66.66'::float8::numeric::money);
Be sure to reference the docs as they provide a good overview:
https://www.postgresql.org/docs/current/static/datatype-money.html

Ambiguous function arguments

The example function:
CREATE OR REPLACE FUNCTION update_a_table(id int, name text)
RETURNS void AS $$
BEGIN
UPDATE a_table
SET name = name
WHERE id = id;
END;
$$ LANGUAGE plpgsql;
cause this error:
ERROR: column reference "name" is ambiguous
LINE 2: SET name = name
^
DETAIL: It could refer to either a PL/pgSQL variable or a table column.
It is clear that I can correct this by changing the names of arguments. Is there any alternative solution?
Generally it is a bad practice in programming to use the same name for two different objects. You should not do it and the change of the arguments names is the best solution. However, Postgres leaves the door open (for the compatibility with older versions). You can set the configuration parameter:
set plpgsql.variable_conflict to use_variable;
Possible values of the parameter: error (default), use_variable or use_column.
It is also possible to set the parameter only for a given function:
CREATE OR REPLACE FUNCTION update_a_table(id int, name text)
RETURNS void AS $$
#variable_conflict use_variable
BEGIN
UPDATE a_table
SET name = name
WHERE id = id;
END;
$$ LANGUAGE plpgsql;
Alternatively, you can explicitly qualify ambiguous names, which is a better solution than the above one. The qualified names are of the shape <function_name>.<parameter_name>, for example update_a_table.id.
CREATE OR REPLACE FUNCTION update_a_table(id int, name text)
RETURNS void AS $$
BEGIN
UPDATE a_table
SET name = update_a_table.name
WHERE a_table.id = update_a_table.id;
END;
$$ LANGUAGE plpgsql;
This error is safer against to terribly difficult detectable bugs. The PLpgSQL good style requires
using qualified names everywhere in any embedded SQL - for access to function's parameters use a pattern function_name.parameter_name
previous versions of Postgres didn't this detection, and only one protection was using variables with special prefix. Usually is used '_' as prefix. The rule is simple - when your function contains embedded SQL, then all of the variable and parameter names should start with '_'. This is safer against name collision and it increase a readability, because you can see quickly what is a variable and what is a SQL identifier.
CREATE OR REPLACE FUNCTION update_a_table_1(int,text)
RETURNS void AS $$
BEGIN
UPDATE a_table
SET name = $2
WHERE id =$1;
END;
$$ LANGUAGE plpgsql;
You can create a function w/o specifying argument(s) name, in your
case you have 2 args so you just need to access them by providing its
index ($1 and $2) but IMO its not a good idea when you have many
args to pass(for example a function with 10 or 10+ args it may
confuse you)

String passed into cursor.callproc becomes unknown (psycopg2, python 2.7, postgres 9.3)

For some reason, passing a string from Python into a Postgres function and calling it using a psycopg2 cursor causes the function to be unrecognized because the argument is unknown.
The function is very simple like so:
CREATE OR REPLACE FUNCTION text_test (some_text text)
RETURNS void AS $$
BEGIN
END;
$$ LANGUAGE plpgsql;
On the python side, I have:
cursor.callproc("text_test", ("test",))
And I get the following error:
psycopg2.ProgrammingError: function text_test(unknown) does not exist
LINE 1: SELECT * FROM text_test('test')
^
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Why does this only happen with strings and what do I need to do to have a function successfully accept a string? For some reason numeric data types are unaffected by this problem.
This happens because there is no way to cast the string to the "correct" text type. Is it a char(N)? A varchar(N)? A text?
Unfortunately .callproc() doesn't provide an easy way to specify the argument types but you can always use .execute() casting the arguments explicitly and everything works:
curs.execute("SELECT * FROM text_test(%s::text)", ("test",))
You could also make a list with the parameters you need to send:
param_list = ["test"]
curs.callproc(proc_name, param_list)
Here is a good answer about it:
python + psycopg2 = unknown types?