Postgres PERFORM COUNT(*) always returning 0 - postgresql

I had a query in SQL for postgres to get the count
SELECT COUNT(*) AS count_variable FROM user WHERE something=something;
When I executed, it was returning the count. Then as per requirement this query was required inside a Postgres Function.When I used this query inside Function, Postgres replied
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function myfuntion(integer,integer) line 11 at SQL statement
I searched for this error and found that, this happens if query returned null while using SELECT, But already I was getting value when executed directly on command line.
And also some posts told to discard it we should use PERFORM instead of SELECT. So my new query inside the same function was
PERFORM COUNT(*) AS count_variable FROM user WHERE something=something;
After this function started working, but count_variable is always zero. I am checking it using raise after PERFORM query.
raise notice 'count_variable: %', count_variable;
count_variable is declared as
DECLARE
count_variable int;
...
BEGIN
count_variable := 0;
Is there anything I am missing or doing wrong, or COUNT() function doesn't works inside function. If count() is not available, is their any alternative for counting rows. Somewhere I saw ##ROWCOUNT is also a variable to get row count, but it gives error.
Help will be highly appreciated.

You don't want to discard the result of the select, so perform is the wrong choice. You want to store the result of the query in a variable, so you need an INTO clause:
DECLARE
count_variable int;
...
BEGIN
SELECT COUNT(*) INTO count_variable
FROM "user"
WHERE something=something;
...
Just because you give the column an alias that is the same name as a variable, does not mean the result is stored in that variable. The column names have no relation with variables.
This is all explained in detail in the manual. Especially the chapter Executing a Query with a Single-row Result

Related

Is it possible to write polymorphic Postgres functions using RECORD parameters?

I want to write a PL/pgSQL function that can take records of different types, check the type of record provided, then do something with the record. Example:
CREATE FUNCTION polymorphic_input(arg_rec RECORD) RETURNS TEXT LANGUAGE plpgsql AS
$plpgsql$
BEGIN
IF pg_typeof(arg_rec)::text = 'information_schema.tables' THEN
RETURN (arg_rec::information_schema.tables).table_name;
ELSIF pg_typeof(arg_rec)::text = 'information_schema.columns' THEN
RETURN (arg_rec::information_schema.columns).column_name;
ELSE
RETURN 'unknown';
END IF;
END;
$plpgsql$;
When you call the function with a row from the information_schema.tables table, it should return the name of the table and it does so when you call it like this:
-- this returns table name "pg_type"
SELECT polymorphic_input((SELECT t FROM information_schema.tables t WHERE table_name = 'pg_type' LIMIT 1));
When you call the function with a row from the information_schema.columns table, it should return the name of the column and it does so when you call it like this:
-- this returns column name "objsubid"
SELECT polymorphic_input((SELECT t FROM information_schema.columns t WHERE t.column_name = 'objsubid' LIMIT 1));
The problem is you CAN'T call the function twice in a row with different row types. For example, if you call it with a row from information_schema.columns it works, then when you call it with a row form information_schema.tables, you get an error like this:
type of parameter 1 (information_schema.tables) does not match that when preparing the plan (information_schema.columns)
The words "when preparing the plan" gave me a hint that Postgres is caching the plans, so I figured running DISCARD PLANS; before each call to the function would work, and indeed it does when you run this entire query:
DISCARD PLANS; SELECT polymorphic_input((SELECT t FROM information_schema.tables t WHERE table_name = 'pg_type' LIMIT 1));
DISCARD PLANS; SELECT polymorphic_input((SELECT t FROM information_schema.columns t WHERE t.column_name = 'objsubid' LIMIT 1));
Running DISCARD PLANS; seems like the nuclear option and would no doubt affect performance in a real-world scenario. After some experimentation, I saw that using the pg_typeof function is what forces the plans to be cached. We can rewrite the function to avoid pg_typeof by adding a parameter that specifies what record type to expect:
CREATE FUNCTION polymorphic_input2(arg_rec RECORD, arg_type text) RETURNS TEXT LANGUAGE plpgsql AS
$plpgsql$
BEGIN
IF arg_type = 'tables' THEN
RETURN (arg_rec::information_schema.tables).table_name;
ELSIF arg_type = 'columns' THEN
RETURN (arg_rec::information_schema.columns).column_name;
ELSE
RETURN 'unknown';
END IF;
END;
$plpgsql$;
You can then call polymorphic_input2 multiple times in a row with different row types without error as follows:
-- no need for DISCARD PLANS here...these calls work fine.
SELECT polymorphic_input2((SELECT t FROM information_schema.tables t WHERE table_name = 'pg_type' LIMIT 1), 'tables');
SELECT polymorphic_input2((SELECT t FROM information_schema.columns t WHERE t.column_name = 'objsubid' LIMIT 1), 'columns');
The problem with polymorphic_input2 is that you have to manually give it a hint as to the type of the record to expect. My question: is it possible to implement a polymorphic function that can figure out the type of record passed to it, without the cached plan errors?
The docs mention the plan_cache_mode setting:
Prepared statements (either explicitly prepared or implicitly generated, for example by PL/pgSQL) can be executed using custom or generic plans. Custom plans are made afresh for each execution using its specific set of parameter values, while generic plans do not rely on the parameter values and can be re-used across executions....The allowed values are auto (the default), force_custom_plan and force_generic_plan...
I tried removing the error by running SET plan_cache_mode = force_custom_plan; but that didn't help (which is probably a bug because the docs imply it should force a custom plan in each call, but Postgres is still caching the plan and causing errors). Only DISCARD PLANS worked.
The docs on plan caching seem to recognize this issue and say:
The mutable nature of record variables presents another problem in this connection. When fields of a record variable are used in expressions or statements, the data types of the fields must not change from one call of the function to the next, since each expression will be analyzed using the data type that is present when the expression is first reached. EXECUTE can be used to get around this problem when necessary.
...and a little further down the docs indicate this shouldn't be happening:
Likewise, functions having polymorphic argument types have a separate statement cache for each combination of actual argument types they have been invoked for, so that data type differences do not cause unexpected failures.
This is further confirmed by the docs EXECUTE which say:
Also, there is no plan caching for commands executed via EXECUTE. Instead, the command is always planned each time the statement is run. Thus the command string can be dynamically created within the function to perform actions on different tables and columns.
So I tried another variant that tries to run pg_typeof via EXECUTE:
CREATE FUNCTION polymorphic_input3(arg_rec RECORD) RETURNS TEXT LANGUAGE plpgsql AS
$plpgsql$
DECLARE
rec_type text;
BEGIN
EXECUTE 'SELECT pg_typeof($1)' INTO rec_type USING arg_rec;
IF rec_type = 'information_schema.tables' THEN
RETURN (arg_rec::information_schema.tables).table_name;
ELSIF rec_type = 'information_schema.columns' THEN
RETURN (arg_rec::information_schema.columns).column_name;
ELSE
RETURN 'unknown';
END IF;
END;
$plpgsql$;
...but that still produces the same error as the variant which calls pg_typeof directly.
My question once again: is it possible (in Postgres 14) to implement a polymorphic function that can figure out the type of record passed to it, without the cached plan errors?

plpgsql : how to reference variable in sql statement

I am rather new to PL/pgSQL and don't know how to reference a variable in a SELECT statement.
In this following function the SELECT INTO always returns NULL:
$body$
DECLARE
r RECORD;
c CURSOR FOR select name from t_people;
nb_bills integer;
BEGIN
OPEN c;
LOOP
FETCH c INTO r;
EXIT WHEN NOT FOUND;
RAISE NOTICE 'name found: %', r.name;
SELECT count(bill_id) INTO nb_bills from t_bills where name = r.name;
END LOOP;
END;
$body$
RAISE NOTICE allows me to verify that my CURSOR is working well: names are properly retrieved, but for some reason still unknown to me, not properly passed to the SELECT INTO statement.
For debugging purpose, I tried to replace the variable in SELECT INTO with a constant value and it worked:
SELECT count( bill_id) INTO nb_bills from t_bills where name = 'joe';
I don't know how to reference r.name in the SELECT INTO statement.
I tried r.name, I tried to create another variable containing quotes, it is always returning NULL.
I am stuck. If anyone knows ...
the SELECT INTO always returns NULL
not properly passed to the SELECT INTO statement.
it is always returning NULL.
None of this makes sense.
SELECT statements do not return anything by itself in PL/pgSQL. You have to either assign results to variables or explicitly return results with one of the available RETURN variants.
SELECT INTO is only used for variable assignment and does not return anything, either. Not to be confused with SELECT INTO in plain SQL - which is generally discouraged:
Combine two tables into a new one so that select rows from the other one are ignored
It's not clear what's supposed to be returned in your example. You did not disclose the return type of the function and you did not actually return anything.
Start by reading the chapter Returning From a Function in the manual.
Here are some related answers with examples:
Can I make a plpgsql function return an integer without using a variable?
Return a query from a function?
How to return multiple rows from PL/pgSQL function?
Return setof record (virtual table) from function
And there may be naming conflicts with parameter names. We can't tell unless you post the complete function definition.
Better approach
That said, explicit cursors are only actually needed on rare occasions. Typically, the implicit cursor of a FOR loop is simpler to handle and cheaper:
Truncating all tables in a Postgres database
And most of the time you don't even need any cursors or loops at all. Set-based solutions are typically simpler and faster.

What command should I use to make my void function display a paticular table using a variable from the function in the where clause?

I basically have a void function that creates a tuple on a existing table. Now at the end of the function I want display the table with the updated tuple. I am running it problems when trying to do this.
This is the statement I am using:
EXECUTE 'SELECT * FROM table WHERE IDNo = idnumber';
-- (idnumber is a variable that is assigned a value in the function)
I get the following error:
ERROR: column "idnumber" does not exist.
Can someone please help me find a solution.
For the actual query, you would want to do something like this:
execute 'select * from table where IDNo = $1' using idnumber;
With the key being the $1 and the USING clause to interpolate the variable.
That should resolve the column error regarding idnumber.
However, I'm not quite sure what you mean by:
display the table with the updated tuple
Do you mean you want to return all the rows in the table including the newly added row? Or just the newly added row? or something else?
Edit in response to comment from OP:
The substitution variables, e.g. $1, $2, $3... are scoped (i.e. unique) to each separate execute statement. So if you had two statements, the first with 3 variables, the second with three, you could use $1, $2, $3 in each and they would refer to the variables mentioned in the USING clause for that individual statement.
See the Postgres Basic Statements doc, specifically the section entitled 40.5.4. Executing Dynamic Commands, for more detail.
Second edit in response to display comments from OP:
When executeing statements, they won't output the way, say, a select statement would if you were doing it within psql or pgadmin. Rather, you have a couple different options, depending on what you ultimately want to do.
First, you could use an INTO clause to put the result into a record (although how you do this depends on whether it's just one row or many rows).
You would need to declare it in that case in the declaration section something like this: foo RECORD;
And then add INTO foo before the USING clause. If it complains about more than one record, you could add LIMIT 1 clause at the end of the query.
You could then do whatever else you wanted to with that record, including RAISE NOTICE with interpolating the record's columns, which would print it to the console.
If you want the entire table, and you want it to "display" more like psql or similar would (that is, return the rows obtained), you would want to have the function return a setof a specific type.
So it may then look something like this:
create function get_table() returns setof table as $$
execute 'select * from table where IDNo = $1' using idnumber;
$$ language 'plpgsql';
Where table is the name of the table you want. If you just want to return the existing rows of the table, this sort of query should work. It would then "display" in a client (e.g. psql, etc.) as the result set.
If you want to modify that (say, by dynamically adding some columns), then you would need to define that new type specifically, and then use that as the type being returned.
See the Postgres Wiki for more details. The wiki content is pretty old (Postgres 7.x vintage), but it generally still applies.

Trying to convert simple execution stored procedure to postgres function - can't get CURRVAL('table_seq') to function

The MySQL Stored Procedure was:
BEGIN
set #sql=_sql;
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
set _ires=LAST_INSERT_ID();
END$$
I tried to convert it to:
BEGIN
EXECUTE _sql;
SELECT INTO _ires CURRVAL('table_seq');
RETURN;
END;
I get the error:
SQL error:
ERROR: relation "table_seq" does not exist
LINE 1: SELECT CURRVAL('table_seq')
^
QUERY: SELECT CURRVAL('table_seq')
CONTEXT: PL/pgSQL function "myexecins" line 4 at SQL statement
In statement:
SELECT myexecins('SELECT * FROM tblbilldate WHERE billid = 2')
The query used is for testing purposes only. I believe this function is used to get the row id of the inserted or created row from the query. Any Suggestions?
When you create tables with serial columns, sequences are, by default, named as tablename_columnname_seq, but it seems that you're trying to access tablename_seq. So with a table called foobar and primary key column foobar_id it ends up being foobar_foobar_id_seq.
By the way, a cleaner way to get the primary key after an insert is using the RETURNING clause in INSERT. E.g.:
_sql = 'INSERT INTO sometable (foobar) VALUES (123) RETURNING sometable_id';
EXECUTE _sql INTO _ires;
PostgreSQL is saying that there is no sequence called "table_seq". Are you sure that that is the right name? The name you would use would depend on what is in _sql as each SERIAL or BIGSERIAL gets its own sequence, you can also define sequences and wire them up by hand.
In any case, lastval() is a closer match to MySQL's LAST_INSERT_ID(), lastval() returns the most recently returned value from any sequence in the current session:
lastval
Return the value most recently returned by nextval in the current session. This
function is identical to currval, except that instead of taking the sequence name
as an argument it fetches the value of the last sequence used by nextval in the
current session. It is an error to call lastval if nextval has not yet been called
in the current session.
Using lastval() also means that you don't have to worry about what's in _sql, unless of course it doesn't use a sequence at all.

Is using the RETURNING clause from an UPDATE as the query clause for an INSERT's query clause possible?

I'm trying to use PostgreSQL's RETURNING clause on an UPDATE within in UPDATE statement, and running into trouble.
Postgres allows a query clause in an INSERT, for example:
INSERT INTO films
SELECT * FROM tmp_films WHERE date_prod < '2004-05-07';
I would like to use the RETURNING clause from an UPDATE as the query clause for an INSERT, for example:
INSERT INTO user_status_history(status)
UPDATE user_status SET status = 'ACTIVE' WHERE status = 'DISABLED' RETURNING status
All of the Postgres references I can find suggest that a RETURNING clause behaved exactly like a SELECT clause,
however when I run something like the above, I get the following:
ERROR: syntax error at or near "UPDATE"
LINE 2: UPDATE user_statuses
Despite being able to execute the UPDATE portion of the above query without error.
Is using the RETURNING clause from an UPDATE as the query clause for an INSERT's query clause possible?
The goal is to update one table and insert into another with a single query, if possible.
With PostgreSQL 9.1 (or higher) you may use the new functionality that allows data-modification commands (INSERT/UPDATE/DELETE) in WITH clauses, such as:
WITH updated_rows AS
(
UPDATE products
SET ...
WHERE ...
RETURNING *
)
INSERT INTO products_log
SELECT * FROM updated_rows;
With PostgreSQL 9.0 (or lower) you may embed the UPDATE command inside one function, and then use that function from another function which performs the INSERT command, such as:
FUNCTION update_rows()
RETURNS TABLE (id integer, descrip varchar)
AS $$
BEGIN
RETURN QUERY
UPDATE products
SET ...
WHERE ...
RETURNING *;
END;
$$ LANGUAGE plpgsql;
FUNCTION insert_rows()
RETURNS void
AS $$
BEGIN
INSERT INTO products_log
SELECT * FROM update_rows() AS x;
END;
$$ LANGUAGE plpgsql;
Right now, no.
There was a feature that almost made it into PostgreSQL 9.0 known as Writeable CTE's that does what you're thinking (although the syntax is different).
Currently, you could either do this via a trigger or as two separate statements.
I think this is not possible the way you are trying to do.
I'd suggest you to write an AFTER UPDATE trigger, which could perform the insert, then.