I have a stored procedure in the database (PostgreSql) and it receives a record as a parameter. When an FdStoredProc is prepared with this function, the parameter is created with the following definitions:
DataType = ftADT
FDDataType = dtRowRef
ParamType = ptInput
I would like to know how to fill this parameter in execution mode.
See a function example
CREATE TYPE t_row_param AS
(
one_field INTEGER,
other_field NUMERIC,
another_field TEXT
--...
);
CREATE FUNCTION my_function(_asuper_row t_row_param)
RETURNS void
LANGUAGE sql
STABLE
AS
$$
--...process row
$$;
and that is the sample Delphi code:
type
TMyRow = record
one_field: integer;
other_field Double;
another_field : String;
end;
procedure DB_MyFunction(_arow: TMyRow);
var
aproc := TFDStoredProc;
begin
aproc := TFDStoredProc.Create(nil);
aproc.Connection := aconnection;
aproc.StoredProcName := 'my_function';
aproc.Prepare;
//aproc.params.ParamByName('_asuper_row')AsXXX := ???; how do it?
//...finalize
end;
Related
I am new to PostgreSQL. I have the query:
---------
DO
$$
DECLARE
l_pin INT;
l_pin1 int;
BEGIN
l_pin := 3;
l_pin1 := 4;
select l_pin,l_pin1;
END;
$$
LANGUAGE PLPGSQL;
--------------------------
from above query am getting an error as
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 inline_code_block line 9 at SQL statement
SQL state: 42601
I need to get the values of l_pin and l_pin1 as output.
What you have there is a DO statement, not a "query" nor a "function". DO statements cannot return anything at all.
How to perform a select query in a DO block?
The displayed error is because you cannot call SELECT in a plpgsql code block without assigning the result. To actually return values from a plpgsql function, use some form of RETURN (explicitly or implicitly).
As minimal example:
CREATE OR REPLACE FUNCTION foo(OUT l_pin int, OUT l_pin1 int)
RETURNS record AS -- RETURNS record is optional because of OUT parameters
$func$
BEGIN
l_pin := 3;
l_pin1 := 4;
RETURN; -- RETURN is optional here because of OUT parameters
END
$func$ LANGUAGE plpgsql;
SELECT * FROM foo();
Related:
Can I make a plpgsql function return an integer without using a variable?
SELECT or PERFORM in a PL/pgSQL function
Returning from a function with OUT parameter
First you can create a new type that can hold multiple values:
CREATE TYPE type_name AS (l_pin INTEGER, l_pin1 INTEGER);
Then you can do something like:
CREATE OR REPLACE FUNCTION function_name()
RETURNS type_name AS $$
DECLARE
result type_name;
BEGIN
/* Code that puts those values into type_name object
i.e.
result.l_pin := 3;
result.l_pin1 := 4;
*/
return result ;
END
$$ language plpgsql
I am working on a migration from Oracle 9i to PostgreSQL 9.3 and I need to migrate some packages with their content.
I am using Ora2PG for most of the work, but packages need to be migrated manually considering how specific to Oracle the code is.
I have a user defined Type called RelevCaspTyp with the following definition :
CREATE TYPE pkgstesa5152com.relevcasptyp AS (
-- SOME OTHER DATA
d1 timestamp,
d2 timestamp,
d1min timestamp,
d2min timestamp,
d1max timestamp,
d2max timestamp,
-- SOME OTHER DATA
);
Then, I create an array of objects of this type, in another Type :
CREATE TYPE pkgstesa5152com.relevcasptabtyp AS (relevcasptabtyp pkgstesa5152com.relevcasptyp[]);
And finally here's the function that triggers a syntax error :
CREATE OR REPLACE FUNCTION PkgStesa5152Com.calculd1d2 (RelevCaspTab INOUT pkgstesa5152com.RelevCaspTabTyp, i integer) AS $body$
BEGIN
IF RelevCaspTab[i].d1max IS NULL OR RelevCaspTab[i].d1min >= RelevCaspTab[i].d1max THEN
RelevCaspTab[i].d1 := RelevCaspTab[i].d1min;
ELSE
RelevCaspTab[i].d1 := RelevCaspTab[i].d1max;
END IF;
IF RelevCaspTab[i].d2max IS NULL OR RelevCaspTab[i].d2min >= RelevCaspTab[i].d2max THEN
RelevCaspTab[i].d2 := RelevCaspTab[i].d2min;
ELSE
RelevCaspTab[i].d2 := RelevCaspTab[i].d2max;
END IF;
END;
$body$
LANGUAGE PLPGSQL
;
When I try to import this function in my PostgreSQL database, I get this message :
ERROR: syntax error at or near "."
LINE 5: RelevCaspTab[i].d1 := RelevCaspTab[i].d1min;
^
As odd as it looks, it seems that LINE 4 (with the IF/THEN) passes correctly, unless this is yet another false positive and the issue is actually elsewhere.
EDIT : it looks like the problem actually comes from the affectation of a value to RelevCaspTab[i].d1. If I comment all the affectations in the function, it is created successfully.
if r[i].d1max is null or r[i].d1min >= r[i].d1max then
r[i] := (r[i].d1min,r[i].d2,r[i].d1min,r[i].d2min,r[i].d1max,r[i].d2max);
Edit
As suggested in the comments:
create type relevCaspTyp as (
d1 timestamp,
d1min timestamp,
d1max timestamp
);
create or replace function calculd1d2 (
r inout relevCaspTyp[], i integer
) as $body$
declare a relevCaspTyp;
begin
a := r[i];
if a.d1max is null or a.d1min >= a.d1max then
a.d1 := a.d1min;
else
a.d1 := a.d1max;
end if;
r[i] := a;
end;
$body$
language plpgsql
;
with r (r) as (values (array[('2017-01-01','2017-01-04','2017-01-03')::relevCaspTyp]))
select calculd1d2(r,1)
from r;
calculd1d2
-------------------------------------------------------------------------------
{"(\"2017-01-04 00:00:00\",\"2017-01-04 00:00:00\",\"2017-01-03 00:00:00\")"}
I have a stored procedure where I declare three variables as records and initialize them later as below:
CREATE OR REPLACE FUNCTION calculateUnitPrice()
RETURNS VOID AS
$$
DECLARE
amenities RECORD;
paramValues RECORD;
logsumAccessebility RECORD;
propertyType integer;
unitPrice float;
freehold integer;
tazId bigint;
unit RECORD;
BEGIN
FOR unit IN (SELECT * from main2012.fm_unit_res)
LOOP
amenities := getAmenitiesById(unit.sla_address_id);
tazId := toBigint(amenities.taz_id);
logsumAccessebility := getLogsumByTazId(tazId);
propertyType := getPropertyTypeFromUnitType(unit.unit_type);
paramValues := getParamValuesByPropertyType(propertyType);
freehold := 0;
unitPrice := paramValues.intercept + (paramValues.floor_area * ln(unit.floor_area)) + (paramValues.freehold * freehold) + (paramValues.logsum_accessebility * logsumAccessebility.accessibility);
UPDATE main2012.fm_unit_res SET rent = unitPrice WHERE fm_unit_id = unit.fm_unit_id;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
But when I run the function I am getting an error like this:
ERROR: record "paramvalues" is not assigned yet
SQL state: 55000
Detail: The tuple structure of a not-yet-assigned record is indeterminate.
Context: PL/pgSQL function calculateunitprice() line 20 at assignment
Please give me your ideas. Am I doing anything wrong here (syntax) or is there a limit in the number of records I can initialize within a stored procedure?
Debug the output, by adding
raise info '%',getPropertyTypeFromUnitType(unit.unit_type);
raise info '%',getPropertyTypeFromUnitType(unit.unit_type);
before declaring paramValues
Is there any way I can set some value to OUT parameter in Postgres stored procedure? Below is the example what I want to do. In oracle PL/SQL you can use out parameters and set it to any value and return it. After that u can use that value and manipulate it.
CREATE OR REPLACE FUNCTION demo_procedure(p_invoice text, OUT p_out1 integer, OUT p_out2 integer)
returns SETOF record
as
$func$
DECLARE
s_inv_rest integer;
s_state integer;
BEGIN
BEGIN
SELECT "REST_TO_PAY", "STATE"
INTO s_inv_rest, s_state
FROM "INVOICE"
WHERE "INVOICE_REFERENCE" = p_invoice;
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_out1 := 1;
p_out2 := 2;
RETURN;
END;
END;
$func$
language plpgsql;
So in this example for instance if no data is found I want to return some out code and out message.
I want to write a stored procedure that returns a 'flattened' object. By 'flattening', I am essentially selecting a set of rows, and returning specific fields in the rows, into the data returned from the function.
The code below explains what I am trying to do
CREATE TABLE user (id int, school_id int, name varchar(32));
CREATE TYPE my_type (user1_id int, user1_name varchar(32), user2_id int, user2_name varchar(32));
CREATE OR REPLACE FUNCTION get_two_users_from_school(schoolid int)
RETURNS my_type AS $$
DECLARE
result my_type
temp_result user
BEGIN
-- for purpose of this question assume 2 rows returned
SELECT id, name INTO temp_result FROM user where school_id = schoolid LIMIT 2;
-- Will the (pseudo)code below work?:
result.user1_id := temp_result[0].id ;
result.user1_name := temp_result[0].name ;
result.user2_id := temp_result[1].id ;
result.user2_name := temp_result[1].name ;
return result ;
END
$$ language plpgsql
I have two questions:
Am I using the correct data type for variable temp_result
Am I accessing the rows correctly (using array indexing)?
Am I accessing the rows correctly (using array indexing)?
No, you need to loop through the result using a cursor which is nicely explained in the manual:
http://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-RECORDS-ITERATING
Something like this should work:
DECLARE
temp_result RECORD;
row_counter integer;
BEGIN
row_counter := 1;
FOR temp_result IN SELECT id, name FROM user where school_id = schoolid LIMIT 2 LOOP
IF row_counter = 1 THEN
result.user1_id := temp_result.id;
result.user1_name = temp_result.name;
END IF;
IF row_counter = 2 THEN
result.user2_id := temp_result.id;
result.user2_name = temp_result.name;
END IF;
row_counter := row_counter + 1;
END LOOP;
return result;
END;
Btw: having a table named "user" is not a really good idea as user is a reserved word and might cause some problems in the long run.