postgresql create function dynamically insert value into different table - postgresql

I want to create a function that can dynamically insert any data type value into any table and multiple columns. But my function can only insert one value to one column:
CREATE OR REPLACE FUNCTION public.dynamic_insert(
tablename character varying,
columname character varying,
datatype character varying,
valuee text
)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE AS
$BODY$
begin
Execute format('insert into %I ('||columname||')
values (cast($1 as '||datatype||'))',tablename,columname);
end;
$BODY$;
Can any one help?

I don't understand your idea clearly. I guess that you want to write one function to insert into table with list of column and values you specify.
If I guess correctly. I suggest some function like this:
CREATE OR REPLACE FUNCTION public.dynamic_insert(
tablename character varying,
columname character varying[],
datatype character varying[],
valuee character varying[]
)
RETURNS void AS
$BODY$
DECLARE
var_sql varchar;
var_sql_column varchar := '';
var_sql_values varchar := '';
begin
var_sql_column := CONCAT('("',ARRAY_TO_STRING(columname, '","'),'")');
FOR i IN 1..ARRAY_LENGTH(columname,1)
LOOP
var_sql_values := var_sql_values || quote_literal(valuee[i]) || '::' || datatype[i] || ',';
END LOOP;
var_sql_values := CONCAT('(',regexp_replace(var_sql_values, ',$', '', 'g'),')');
var_sql := CONCAT('INSERT INTO ', quote_ident(tablename), var_sql_column, ' VALUES ', var_sql_values);
IF (var_sql IS NOT NULL) THEN
EXECUTE(var_sql);
END IF;
end;
$BODY$;
LANGUAGE 'plpgsql'
COST 100
VOLATILE;
Hopefully it matches your require.
p/s: with my experience, just choose one solution between Format, and String concatenation, don't use both in one query, it makes me hard to view and find bug.
------------- Update for question below -----------------------------------
For the Loop please refer the document here
Basically, it will run from 1 to n (with n is the number elements in array) and build a query.
One simple thing to view how it run is you can put some print out data into you function, such as RAISE. Please refer it document here

Related

How can I omit records when returning a table in postgresql?

I have a problem when I try to execute a function where, depending on the result of another function, it returns a record or not. What I want to do is that when the function ("function_typecar" in the example) returns 1 as a result, it returns the record, otherwise no. The problem is when the condition is not met. It repeats the same record until there is one that meets the condition again. Is there a way to skip returning the records when the condition is not met? I leave an example of this situation
CREATE OR REPLACE FUNCTION schema.function_test
RETURNS TABLE(id_r INTEGER, name_r CHARACTER VARYING, year_r INTEGER) AS
$BODY$
DECLARE
sql_record RECORD;
type_car INTEGER;
BEGIN
SELECT id, name, year
FROM car
FOR sql_record IN
SELECT id, name, year
FROM car
LOOP
SELECT type INTO type_car FROM function_typecar(sql_record.id);
IF type_car = 1 THEN
id_r := sql_record.id;
name_r := sql_record.name;
year_r := sql_record.year;
END IF;
RETURN NEXT;
END LOOP;
END; $BODY$
This is the result that I want to avoid and that only brings me those that meet the IF condition:
Something like this should work, without a LOOP and in plain SQL:
CREATE OR REPLACE FUNCTION function_test()
RETURNS TABLE(id_r INTEGER, name_r CHARACTER VARYING, year_r INTEGER)
LANGUAGE SQL
AS
$BODY$
SELECT id, name, year
FROM car
JOIN function_typecar(id) ON type = 1;
$BODY$;

Invoke one stored procedure from another stored procedure by passing array values in postgresql

I'm unable to pass the array values as input to another stored procedure. However when I try to insert(table kvstore populated without issues) the array values, the table get loaded. Please advice me on how to pass array values dynamically from one stored procedure to another.
The raise notice prints values like below
NOTICE: Value 1:
NOTICE: Value 2:
CREATE OR REPLACE FUNCTION test_schema.test_insert_choice(
p_member_crn character varying,
p_preferencearray text[][],
p_created timestamp without time zone,
p_modified timestamp without time zone,
p_created_system smallint,
p_created_by character varying,
p_modified_by character varying,
p_modified_system smallint
)
RETURNS text AS
$BODY$
DECLARE
lv_n_rtn INTEGER ;
lv_t_err_msg TEXT ;
lv_t_err_dtl TEXT ;
BEGIN
/*FOR i IN 1 .. array_upper(p_preferencearray, 1) LOOP
INSERT INTO lcdm_main.kvstore (key, value)
VALUES (p_preferencearray[i][1], p_preferencearray[i][2]);
END LOOP;*/
FOR i IN 1 .. array_upper(p_preferencearray, 1) LOOP
raise notice 'Value 1: %', p_preferencearray[i][1];
raise notice 'Value 2: %', p_preferencearray[i][2];
perform test_schema.test_insert_master(p_member_crn,
p_preferencearray[i][1],
p_preferencearray[i][2],
p_created,
p_modified,
p_created_system,
p_created_by,
p_modified_by,
p_modified_system);
END LOOP;
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;```
Found the solution and replacing FOR with FOREACH helped to resolve the issue
CREATE OR REPLACE FUNCTION test_schema.test_insert_choice(
p_member_crn character varying,
p_preferencearray text[][],
p_created timestamp without time zone,
p_modified timestamp without time zone,
p_created_system smallint,
p_created_by character varying,
p_modified_by character varying,
p_modified_system smallint
)
RETURNS text AS
$BODY$
DECLARE
lv_n_rtn INTEGER ;
lv_t_err_msg TEXT ;
lv_t_err_dtl TEXT ;
m text[];
arr text[] := p_preferencearray;
BEGIN
FOREACH m SLICE 1 IN ARRAY arr
LOOP
perform test_schema.test_insert_master(p_member_crn,
m[1], --Preference Id
m[2], --Preference Value
p_created,
p_modified,
p_created_system,
p_created_by,
p_modified_by,
p_modified_system);
END LOOP;
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;
Thanks for everyone

In clause in postgres

Need Output from table with in clause in PostgreSQL
I tried to make loop or ids passed from my code. I did same to update the rows dynamically, but for select I m not getting values from DB
CREATE OR REPLACE FUNCTION dashboard.rspgetpendingdispatchbyaccountgroupidandbranchid(
IN accountgroupIdCol numeric(8,0),
IN branchidcol character varying
)
RETURNS void
AS
$$
DECLARE
ArrayText text[];
i int;
BEGIN
select string_to_array(branchidcol, ',') into ArrayText;
i := 1;
loop
if i > array_upper(ArrayText, 1) then
exit;
else
SELECT
pd.branchid,pd.totallr,pd.totalarticle,pd.totalweight,
pd.totalamount
FROM dashboard.pendingdispatch AS pd
WHERE
pd.accountgroupid = accountgroupIdCol AND pd.branchid IN(ArrayText[i]::numeric);
i := i + 1;
end if;
END LOOP;
END;
$$ LANGUAGE 'plpgsql' VOLATILE;
There is no need for a loop (or PL/pgSQL actually)
You can use the array directly in the query, e.g.:
where pd.branchid = any (string_to_array(branchidcol, ','));
But your function does not return anything, so obviously you won't get a result.
If you want to return the result of that SELECT query, you need to define the function as returns table (...) and then use return query - or even better make it a SQL function:
CREATE OR REPLACE FUNCTION dashboard.rspgetpendingdispatchbyaccountgroupidandbranchid(
IN accountgroupIdCol numeric(8,0),
IN branchidcol character varying )
RETURNS table(branchid integer, totallr integer, totalarticle integer, totalweight numeric, totalamount integer)
AS
$$
SELECT pd.branchid,pd.totallr,pd.totalarticle,pd.totalweight, pd.totalamount
FROM dashboard.pendingdispatch AS pd
WHERE pd.accountgroupid = accountgroupIdCol
AND pd.branchid = any (string_to_array(branchidcol, ',')::numeric[]);
$$
LANGUAGE sql
VOLATILE;
Note that I guessed the data types for the columns of the query based on their names. You have to adjust the line with returns table (...) to match the data types of the select columns.

write a function in pl/pgsql

This is my query.
select origindept, `count(am_course_name)` as total_course
from am_courseoffered
group by origindept;
I am trying to create a function who will return this query.
CREATE OR REPLACE FUNCTION getcourse ()
RETURNS TABLE (
course_origindept character varying,
course_ count(am_course_name) character varying
)
AS $$
BEGIN
RETURN QUERY select origindept, count(am_course_name) as number_total_course
from am_courseoffered
group by origindept;
END; $$
LANGUAGE 'plpgsql';
There are some error in my function.
ERROR: syntax error at or near "character"
LINE 4: course_ count(am_course_name) character varying
How i create function who will return this query.
Off the top of my head, the count function should return a datatype of bigint, so I would think declaring the count as a varchar would cause a datatype mismatch. Something like this should fix that:
CREATE OR REPLACE FUNCTION getcourse ()
RETURNS TABLE (
course_origindept character varying,
course_count bigint -- change here
) AS $$
BEGIN
RETURN QUERY
select origindept, count(am_course_name) as number_total_course
from am_courseoffered
group by origindept;
END;
$$
LANGUAGE plpgsql;
I do have to ask, though... is this an academic exercise to understand functions? The use case is questionable enough, as a view would be more appropriate.

Function to DELETE rows taking the tablename as parameter

I want create a function:
CREATE OR REPLACE FUNCTION medibv.delAuto(tableName nvarchar(50), columnName nvarchar(100),value
nvarchar(100))
RETURNS void AS
$BODY$
begin
DELETE from tableName where columnName=value
end;
$BODY$
LANGUAGE plpgsql VOLATILE;
I have these parameters: tableName, columnName, value.
I want tableName as table in PostgreSQL.
CREATE OR REPLACE FUNCTION medibv.delauto(tbl regclass, col text, val text
,OUT success bool)
RETURNS bool AS
$func$
BEGIN
EXECUTE format('
DELETE FROM %s
WHERE %I = $1
RETURNING TRUE', tbl, col)
USING val
INTO success;
RETURN; -- optional in this case
END
$func$ LANGUAGE plpgsql;
Call:
SELECT medibv.delauto('myschema.mytbl', 'my_txt_col', 'foo');
Returns TRUE or NULL.
There is no nvarchar type in Postgres. You may be thinking of SQL Server. The equivalent would be varchar, but most of the time you can simply use text.
regclass is a specialized type for registered table names. It's perfect for the case an prevents SQL injection for the table name automatically and most effectively. More in the related answer below.
The column name is still prone to SQL injection. I sanitize the function with format(%I).
format() requires PostgreSQL 9.1+.
Your function did not report what happened. One or more rows may be found and deleted. Or none at all. As a bare minimum I added a boolean OUT column which will be TRUE if one or more rows were deleted. Because (quoting the manual here):
If multiple rows are returned, only the first will be assigned to the INTO variable.
Lastly, use USING with EXECUTE to pass in values. Don't cast back and forth. This is inefficient and prone to errors and to SQLi once more.
Find more explanation and links in this closely related answer:
Table name as a PostgreSQL function parameter
Use EXECUTE to run dynamic commands:
CREATE OR REPLACE FUNCTION medibv.delAuto(tableName nvarchar(50), columnName nvarchar(100),value
nvarchar(100))
RETURNS void AS
$BODY$
begin
EXECUTE 'DELETE FROM ' || tableName || ' WHERE ' || columnName || '=' || value;
end;
$BODY$
LANGUAGE plpgsql VOLATILE;