When trying to join two tables and update one of them, I'm receiving an unexpected error from this function right here:
CREATE OR REPLACE FUNCTION tsi.update_data(_creation_time int)
RETURNS VOID
AS $$
BEGIN
EXECUTE format($sql$
UPDATE tsi.forecasts_%s a SET
a."incidents # 01:00" = b.n_incid,
a."road # 01:00" = b.n_roads
FROM tgi_tmp b WHERE a.link_ix = b.link_id;
$sql$,_creation_time);
END $$ LANGUAGE plpgsql;
It gives me this error message:
syntax error at or near "#"
cidents # 01:00" = n_incid,
^
Do anyone know why I'm getting this error? The tables do contain the mentioned columns, so that is not the problem. Is postgres having a hard time dealing with string-columns in an Execute-format?
Postgres version: 10.5
Simplified table structure:
CREATE TABLE tsi.forecasts_%s (
link_ix int PRIMARY KEY,
"slipincidents # 00:00" SMALLINT NOT NULL,
"roadcoverage # 00:00" SMALLINT NOT NULL,
);
and tgi_tmp:
CREATE TEMPORARY TABLE tgi_tmp (
link_id TEXT,
n_road SMALLINT,
n_incid SMALLINT
CONSTRAINT tgi_tmp_pkey PRIMARY KEY (link_id)
);
Weird it complaints about the # doesn't do that for me. What however is wrong is specifying the table (alias) for the columns you are assigning to in the set. You should only specify the column names.
CREATE OR REPLACE FUNCTION tsi.update_data(_creation_time int)
RETURNS VOID
AS $$
BEGIN
EXECUTE format($sql$
UPDATE tsi.forecasts_%s a SET
"incidents # 01:00" = b.n_incid,
"road # 01:00" = b.n_roads
FROM tgi_tmp b WHERE a.link_ix = b.link_id;
$sql$,_creation_time);
END $$ LANGUAGE plpgsql;
Trying to debug your function, I get these error messages, one after the other:
ERROR: operator does not exist: integer = text
ERROR: column b.n_roads does not exist
ERROR: column "a" of relation "tsi_forecasts_1" does not exist
ERROR: column "incidents # 01:00" of relation "tsi_forecasts_1" does not exist
Each after fixing the previous error.
I arrive at this working version:
CREATE OR REPLACE FUNCTION tsi_update_data(_creation_time int)
RETURNS VOID AS
$func$
BEGIN
EXECUTE format($sql$
UPDATE tsi_forecasts_%s a
SET "slipincidents # 00:00" = b.n_incid -- don't table-qualify target cols
, "roadcoverage # 00:00" = b.n_road -- col names in q don't match
FROM tgi_tmp b
WHERE a.link_ix = b.link_id::int; -- your db design forces a cast
$sql$, _creation_time);
END
$func$ LANGUAGE plpgsql;
But I cannot reproduce your error:
syntax error at or near "#"
cidents # 01:00" = n_incid,
^
Which must be invoked by something that's not in the question, like outer double-quoting or special meaning of characters in your unnamed client program.
All that aside, it might pay to reconsider your naming convention and your db design. Use legal, lower-case, unquoted identifiers and matching data types (link_ix is int while link_ix is text).
Works for some reason when I'm not specifying the offset. Like this:
CREATE OR REPLACE FUNCTION tsi.update_data(_creation_time int)
RETURNS VOID
AS $$
BEGIN
EXECUTE format($sql$
UPDATE tsi.forecasts_%s a SET
"incidents # %s" = b.n_incid,
"road # %s" = b.n_roads
FROM tgi_tmp b WHERE a.link_ix = b.link_id;
$sql$,_creation_time, '01:00', '01:00');
END $$ LANGUAGE plpgsql;
Related
I am trying to update a table in Postgres, but I get below error.
Userid is the primary key.
CREATE OR REPLACE FUNCTION public.update_user(username character varying,
userid character varying,
roleid integer,
deptid integer,
status integer)
RETURNS integer AS
$$
BEGIN
UPDATE public."M_User" SET public."M_User".user_name = public.update_user.username,
public."M_User".user_role_id = public.update_user.roleid,
public."M_User".dept_id = public.update_user.deptid,
public."M_User".status = public.update_user.status
WHERE public."M_User".user_id = userid;
RETURN 0;
END
$$
LANGUAGE 'plpgsql';
Call:
SELECT * FROM public.update_user ('ji','gis', 123, 24, 1);
Error:
ERROR: missing FROM-clause entry for table "update_user"
LINE 1: ...E public."M_User" SET public."M_User".user_name = public.upd...
^
QUERY: UPDATE public."M_User" SET public."M_User".user_name = public.update_user.username,
public."M_User".user_role_id = public.update_user.roleid,
public."M_User".dept_id = public.update_user.deptid,
public."M_User".status = public.update_user.status
WHERE public."M_User".user_id = userid
CONTEXT: PL/pgSQL function update_user(character varying,character varying,integer,integer,integer) line 4 at SQL
statement
SQL state: 42P01
How to resolve the issue?
CREATE OR REPLACE FUNCTION public.update_user(_username varchar,
_userid varchar,
_roleid integer,
_deptid integer,
_status integer)
RETURNS integer
LANGUAGE sql AS
$func$
UPDATE public."M_User" u
SET user_name = _username
, user_role_id = _roleid
, dept_id = _deptid
, status = _status
WHERE u.user_id = _userid
AND (u.user_name, u.user_role_id, u.dept_id, u.status) IS DISTINCT FROM
( _username , _roleid , _deptid , _status) -- optional addition
RETURNING u.user_id;
$func$
You can qualify function parameters with the function name to disambiguate, but that's typically an awkward solution. Breaks when renaming the function. There are other ways. See:
PL/pgSQL column name the same as variable
I like to prefix parameters and variables with an underscore, and never do the same for column names. Not standard, but a widespread convention. Make it a habit to table-qualify all column names, and you are safe. A table alias helps to keep the noise down. Basics in the manual on UPDATE.
But target columns in an UPDATE statement cannot be qualified. (They are never ambiguous.)
I added an optional predicate to skip the UPDATE if it wouldn't change anything. See:
How do I (or can I) SELECT DISTINCT on multiple columns?
You do not need PL/pgSQL for this. (Though you can, of course.) A simpler SQL function does the job. See:
Difference between language sql and language plpgsql in PostgreSQL functions
RETURN 0 does nothing useful. I changed it to return the effective user_id of the updated row. (Typically the same as the input _userid, but it might differ with triggers.) The important difference: you only get the return value if a row is actually updated, making it useful. Else, you might as well declare the function as RETURNS void. Just as useful as an unconditional RETURN 0;, but cheaper.
The doc says one can refer to an input as function_name.parameter_name, so try removing references to the schema_name
UPDATE public."M_User"
SET user_name = update_user.username,
user_role_id = update_user.roleid,
dept_id = update_user.deptid,
status = update_user.status
WHERE user_id = update_user.userid;
I'm trying to cast a string to a varchar from user input on a web application. I want to change the value of one of the columns given a certain ID (Primary Key) and the column name is what I'm casting as the varchar.
CREATE OR REPLACE FUNCTION changeQuantities(productID varchar, warehouseID int, change int)
RETURNS void AS $$
BEGIN
EXECUTE format('UPDATE warehouses SET CAST(%I AS VARCHAR) = %s WHERE warehouseID = %s', productID, change, warehouseID)
USING change, warehouseID;
END;
$$ LANGUAGE plpgsql;
The 'productID' is the column name, 'change' is the new value, and 'warehouseID' is the primary key for the table. 'warehouses' is the table. Here is the error I receive:
SELECT changeQuantities('bg412',1,100);
ERROR: syntax error at or near "CAST"
LINE 1: UPDATE warehouses SET CAST(bg412 AS VARCHAR) = 100 WHERE war...
^
QUERY: UPDATE warehouses SET CAST(bg412 AS VARCHAR) = 100 WHERE warehouseID = 1
CONTEXT: PL/pgSQL function changequantities(character varying,integer,integer) line 3 at EXECUTE statement
I have another function just like it that uses a SELECT statement while casting the column name and it works just fine. Can I just not cast something after SET? I haven't found anything on this particular case, so I'm either going to be humiliated or I will help someone else out with similar issues. Thanks for any help.
You can't have a cast() on the left side of the assignment - and you don't need it, as the data type of a column is known. If at all you would need to cast the right hand side of an assignment to the data type of the left hand side.
Assuming that bg412 is a column name, you need:
CREATE OR REPLACE FUNCTION changeQuantities(productID varchar, warehouseID int, change int)
RETURNS void AS $$
BEGIN
EXECUTE format('UPDATE warehouses SET %I = %s WHERE warehouseID = %s', productID, change, warehouseID)
USING change, warehouseID;
END;
$$ LANGUAGE plpgsql;
Unrelated, but: using the the ID of a product as a column name in a table seems like a horrible design. What do you do if you have a million products?
I'm trying to create a function in postgresql and i get the following error:
ERROR: column "A00" does not exist
LINE 1: SELECT * from "UpdateStatus"(91206618515,"A00")
^
********** Error **********
ERROR: column "A00" does not exist
SQL state: 42703
Character: 50
My function bellow:
CREATE OR REPLACE FUNCTION UpdateStatus(id bigint,status varchar(3))
RETURNS void AS $$
BEGIN
UPDATE tb_test
set
id_status = status
where id_test = id;
END
$$ LANGUAGE plpgsql;
if i put the parameter like this "UpdateStatus"(91206618515,'A00')
i get the following error:
ERROR: function UpdateStatus(bigint, unknown) does not exist
Runnig /df +updatestatus
public | updatestatus | void | cpf bigint, status character
public | updatestatus | void | cpf bigint, status character varying
Thanks in advance.
In SQL you mask strings with single quotes, and you must remove the double quotes around the function name, because you have created it with a case insensitive name:
SELECT * from UpdateStatus(91206618515, 'A00')
My bet is you create multiple functions with similar signature. Check your schema functions and drop duplicate function.
Try changing the name of your function for testing. Because your function should work.
SQL DEMO
CREATE OR REPLACE FUNCTION TestUpdateStatus(id bigint,status varchar(3))
RETURNS varchar AS $$
BEGIN
return status;
END
$$ LANGUAGE plpgsql;
SELECT TestUpdateStatus(1, 'A001')
OUTPUT and see how result show lowercase even when define with CamelCase?
I have a general question. I have a function that creates a file. However, within that function presently I am hard coding the file name pattern based on argument inputs. Now I have come to a point where I need to have more than one file name pattern. I devised a way of using another table as the file name map that the function can simply call if the user inputs that file name pattern id. Here is my example to help better illustrate my point:
Here is the table creation and data insertion for referential purposes:
create table some_schema.file_mapper(
mapper_id integer not null,
file_name_template varchar);
insert into some_schema.file_mapper (mapper_id, file_name_template)
values
(1, '||v_first_name||''_''||v_last_name||')
(2, '||v_last_name||''_''||v_first_name||')
(3, '||v_last_name||''_''||v_last_name||');
Now the function itself
create or replace function some_schema.some_function(integer)
returns varchar as
$body$
Declare
v_filename_config_id alias for $1;
v_filename varchar;
v_first_name varchar;
v_last_name varchar;
cmd text;
Begin
v_first_name :='Joe';
v_last_name :='Shmoe';
cmd := 'select file_name_template
from some_schema.file_mapper
where mapper_id = '||v_filename_config||'';
execute cmd into v_filename;
raise notice 'checking "%"',v_filename;
return 'done';
end;
$body$
LANGUAGE plpgsql;
Now that I have this. I want to be able to mix and match file name patterns. So for instance I wanted to use mapper_id 3, I would expect a returned file of "Shmoe_Shmoe.csv" if I execute the script:
select from some_schema.some_function(2)
The Problem is whenever I get it to read the "v_filename" variable it will not evaluate and return the values from the function's variables. Originally, I believed it to be a quoting issue(and it probably still is). After messing with the quoting I have gotten about as far the error below:
ERROR: syntax error at or near "_"
LINE 4: ...s/dir/someplace/||v_last_name||'_'||v_firs...
^
QUERY: copy(
select *
from some_schema.some_table)
to '/dir/someplace/||v_last_name||'_'||v_first_name||.csv/;
DELIMITER,
csv HEADER;
CONTEXT: PL/pgSQL function some_schema.some_function(integer) line 27 at EXECUTE statement
As you can tell it is pretty much telling me it is a quoting issue. Is there a way I can get the function to properly evaluate the variable and return the proper file name? Let me know if I am not clear and need to elaborate.
This more or less illustrates the usage of format(). (Slightly reduced wrt the original question):
CREATE TABLE file_mapper
( mapper_id INTEGER NOT NULL PRIMARY KEY
, file_name_template TEXT
);
INSERT INTO file_mapper(mapper_id, file_name_template) VALUES (1,'one'), (2, 'two');
CREATE OR REPLACE FUNCTION dump_the_shit(INTEGER)
RETURNS VARCHAR AS
$body$
DECLARE
v_filename_config_id alias for $1;
v_filename VARCHAR;
name_cmd TEXT;
copy_cmd TEXT;
BEGIN
name_cmd := format( e'select file_name_template
from file_mapper
where mapper_id = %L;', v_filename_config_id );
EXECUTE name_cmd into v_filename;
RAISE NOTICE 'V_filename := %', v_filename;
copy_cmd := format( e'copy(
select *
from %I)
to \'/tmp/%s.csv\'
csv HEADER;' , 'file_mapper' , v_filename);
EXECUTE copy_cmd;
RETURN copy_cmd;
END;
$body$
LANGUAGE plpgsql;
SELECT dump_the_shit(1);
format function description
summary:
use %I for identifiers (tablenames and column names) [ FROM %I ]
use %L for literals such as query constants [ WHERE the_date = %L ]
use %s for ordinary strings [ to \'/tmp/%s.csv\' ]
I'm trying to create this function with Goose using a postgres (pq lib) database.
My code is as follows:
CREATE OR REPLACE FUNCTION add_userlocation(user_id INT, location_id INT) RETURNS VOID AS
$BODY$
BEGIN
LOOP
UPDATE userslocations SET count = count+1 WHERE userid = user_id AND locationid = location_id;
IF found THEN
RETURN;
END IF;
BEGIN
INSERT INTO userslocations(userid,locationid, count) VALUES (user_id, location_id, 1);
RETURN;
EXCEPTION WHEN unique_violation THEN
END;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql;
When I try to goose up it provides an error:
(pq: unterminated dollar-quoted string at or near "$BODY$
BEGIN
LOOP
-- first try to update the key
UPDATE userslocations SET count = count+1 WHERE userid = user_id AND locationid = location_id;
"), quitting migration.
Goose basically echo's the pq library error, so I dont think it's in Goose, but rather the pq-library. Query runs succesful on pgAdmin III.
According to the goose documentation, complex statements that include semicolons must be annotated with -- +goose StatementBegin and -- +goose StatementEnd
Your statement contains semicolons embedded within it so you need to use these annotations. Otherwise goose mangles the SQL so that libpq gives errors.