How to access customized object value inside postgresql function? - postgresql

I'm new to postgresql function, wondering how to we access the value from a customized type inside postgresql?
For example the following example, BreakoutMatchingsDecrementProps is the customized type and I'm not able to use record.communityId to get the value...
CREATE TYPE BreakoutMatchingsDecrementProps AS (
weekId int,
communityId varchar,
timeSlot int,
groupSize breakoutsize,
day weekday
);
create or replace function Test (obj BreakoutMatchingsDecrementProps[])
returns void as
$$
DECLARE
record BreakoutMatchingsDecrementProps;
BEGIN
FOREACH record IN ARRAY obj LOOP
update breakout_matching
set number_of_registration = number_of_registration - 1
where week_id = 1
AND community_id = record.communityId
AND time_slot = 1
AND group_size = 'Two'
AND day = 'Monday'
AND number_of_registration > 0;
END
END
$$
language plpgsql;
I'm using Supabase and client side looks like:
const { data, error } = await adminClient.rpc(
"test",
{
obj:
{
timeSlot: 1,
groupSize: "Two",
day: "Monday",
communityId: "dshjdfdfak",
weekId: 1,
},
}
);
When I changed the record.communityId to "dshjdfdfak" then it works...
Wondering how do we access customized type object in plpgsql? Thanks!

Related

default date value to store in a table in snowflake

I created below snowflake procedure and from that procedure I want to
insert default date value into a table. Below is the script.
create or replace procedure test_dt() returns string not null language
javascript //execute as owner
as
$$
try {
var c_dt=`select current_date()`;
snowflake.execute({sqlText:c_dt});
var sql_query = `insert into test_date values (:1)`;
var resultSet = snowflake.execute( {sqlText: sql_query, binds:c_dt});
}
catch(err) {
return err.message;
}
$$;
call test_dt();
while executing the procedure I am getting below error.
"Date 'select current_date()' is not recognized"
Please help me on this.
you are binding the "date" in the second query, to the input sql that "gets the current_date()" and not the actual result, thus it's the same as
insert into test_date values ('select current_date()');
so you ether want to save the result of the first query OR just use the current date aka CURRENT_DATE
insert into test_date values (CURRENT_DATE);
create or replace procedure test_dt() returns string not null language
javascript //execute as owner
as
$$
try {
var sql_query = `insert into test_date values (current_date)`;
var resultSet = snowflake.execute( {sqlText: sql_query});
}
catch(err) {
return err.message;
}
$$;```

PostgreSql - calling stored proc to bulk upsert (insert on conflict) data isn't working via npgsql

I am trying bulk upsert data to via a stored proc. Following a few threads, I ended up creating a stored procs passing input records as json and using json_populate_recorset internally like this
CREATE OR REPLACE PROCEDURE public.test_proc3(IN par_records character varying)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
insert into firsttable
select * from json_populate_recordset(NULL::uttfirsttable, (par_records)::json)
on conflict(id)
do update set
createddate = excluded.createddate,
name = excluded.name;
END;
$BODY$;
btw uttfirsttable is a type declared as
CREATE TYPE public.uttfirsttable AS
(
id integer,
name character varying(100),
createddate timestamp without time zone
);
This works fine if I call it directly from PgAdmin as
call test_proc3(
'[
{
"id": 1,
"name": "newfirst",
"createddate": "2021-03-10T10:03:19.3589317+11:00"
},
{
"id": 6,
"name": "sixth",
"createddate": "2021-03-10T10:03:19.3689052+11:00"
},
{
"id": 2,
"name": "newsecond",
"createddate": "2021-03-10T10:03:19.3689052+11:00"
}
]'
);
However my .net code is somehow failing to insert/update anything despite passing exactly same json to the stored proc
string recordsJson = "[{\"id\": 1,\"name\": \"newfirst\",\"createddate\": \"2021-03-10T10:03:19.3589317+11:00\"},{\"id\": 6,\"name\": \"sixth\",\"createddate\": \"2021-03-10T10:03:19.3689052+11:00\"},{\"id\": 2,\"name\": \"newsecond\",\"createddate\": \"2021-03-10T10:03:19.3689052+11:00\"}]";// JsonConvert.SerializeObject(records, Formatting.Indented);
using (var lConn = GetConnection())
{
using (var trans = lConn.BeginTransaction())
{
using (var cmd = GetCommand("call test_proc3(#par_records)", lConn, trans, System.Data.CommandType.Text))
{
cmd.Parameters.Add("#par_records", NpgsqlDbType.Varchar, recordsJson.Length).Value = recordsJson;
int result = cmd.ExecuteNonQuery();
}
}
}
result has -1 value in it. Just for testing if I replace the 'call test_proc3(#par_records) with the full query from the stored proc, then cmd.ExecuteNonQuery returns 3, however still no records are added/updated.
Any idea what I could be doing wrong here?
Took me hours to notice a careless mistake of missing
trans.Commit
before exiting the function.

Error: cannot insert multiple commands into a prepared statement

I am trying to move a row from one table to another.
The problem is that if I put both queries together, I get "error: cannot insert multiple commands into a prepared statement". What can I do?
exports.deletePost = function(id) {
return db.query(`INSERT INTO deletedjobs
SELECT *
FROM jobs
WHERE id = $1;
DELETE FROM jobs WHERE id = $1;`, [id]).then(results => {
console.log("succesfull transfer");
return results.rows[0];
});
};
EDIT: Following Docs v7.0.0, I found out db.multi can execute a multi-query string, you can try this:
db.multi(`INSERT INTO deletedjobs
SELECT *
FROM jobs
WHERE id = $1;DELETE FROM jobs WHERE id = $1`, [id])
Other way I think the better solution is that you should wrap the query into a function for insert-delete at same time, like below:
CREATE FUNCTION moveJob(id character varying) RETURNs void AS
$BODY$
BEGIN
INSERT INTO deletedjobs
SELECT *
FROM jobs
WHERE id = id;
DELETE FROM jobs WHERE id = id;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;
And call it as postgresql function in your js:
db.any('select moveJob($1)', [id]);
You can also use a WITH ... AS clause (https://www.postgresql.org/docs/9.1/queries-with.html) to execute both queries in one go. So it could look something like that:
exports.deletePost = function(id) {
return db.query(`
WITH
A AS (SELECT * FROM jobs WHERE id = $1),
B AS (INSERT INTO deletedjobs FROM A),
DELETE FROM jobs WHERE id IN (SELECT id FROM A);
`, [id]).then(results => {
console.log("succesfull transfer");
return results.rows[0];
});
};
Maybe you should not use AND clause, try semicolon ;.
You used select * you should enter columns name, because in later
times you may add a new column, your function does not execute.

{pg-promise} postgres - how to convert type int[] to be compatible with POINT()

So I'm using pg-promise to insert into a type POINT column. But it's giving me the following error:
function point(integer[]) does not exist
I'm passing the values as an array. What should I change to make it work?
Some code (not sure if useful):
simplified_query = `$${counter++}:name = POINT($${counter++})`
fields =
[
"geolocation",
[10, 10]
]
As per Custom Type Formatting, if your field is ['geolocation', [10, 10]], with the first value being the column name, you can use the following function:
function asPoint(field) {
return {
rawType: true,
toPostgres: () => pgp.as.format('$1:name = POINT($2:csv)', field)
};
}
Then you can use asPoint(field) as a query-formatting parameter:
const field = ['geolocation', [10, 10]];
db.any('SELECT * FROM table WHERE $1', [asPoint(field)])
//=> SELECT * FROM table WHERE "geolocation" = POINT(10, 10)
Alternatively, your field can be a custom-type class that implements Custom Type Formatting either explicitly or via the prototype, in which case it can be used as a formatting parameter directly.

pg-promise update where in custom array

How can the following postgresql query be written using the npm pg-promise package?
update schedule
set student_id = 'a1ef71bc6d02124977d4'
where teacher_id = '6b33092f503a3ddcc34' and (start_day_of_week, start_time) in (VALUES ('M', (cast('17:00:00' as time))), ('T', (cast('19:00:00' as time))));
I didn't see anything in the formatter namespace that can help accomplish this. https://vitaly-t.github.io/pg-promise/formatting.html
I cannot inject the 'cast' piece into the '17:00:00' value without it being considered part of the time string itself.
The first piece of the query is easy. It's the part after VALUES that i can't figure out.
First piece:
var query = `update schedule
set student_id = $1
where teacher_id = $2 and (start_day_of_week, start_time) in (VALUES $3)`;
var inserts = [studentId, teacherId, values];
I'm using this messiness right now for $3 (not working yet), but it completely bypasses all escaping/security built into pg-promise:
const buildPreparedParams = function(arr, colNames){
let newArr = [];
let rowNumber = 0
arr.forEach((row) => {
const rowVal = (rowNumber > 0 ? ', ' : '') +
`('${row.startDayOfWeek}', (cast('${row.startTime}' as time)))`;
newArr.push(rowVal);
});
return newArr;
};
The structure I am trying to convert into this sql query is:
[{
"startTime":"17:00:00",
"startDayOfWeek":"U"
},
{
"startTime":"16:00:00",
"startDayOfWeek":"T"
}]
Use CSV Filter for the last part: IN (VALUES $3:csv).
And to make each item in the array format itself correctly, apply Custom Type Formatting:
const data = [{
startTime: '17:00:00',
startDayOfWeek: 'U'
},
{
startTime: '16:00:00',
startDayOfWeek: 'T'
}];
const values = data.map(d => ({
toPostgres: () => pgp.as.format('(${startDayOfWeek},(cast(${startTime} as time))', d),
rawType: true
}));
Now passing in values for $3:csv will format your values correctly:
('U',(cast('17:00:00' as time)),('T',(cast('16:00:00' as time))