Postgres inserting value issue - column "insert" does not exists - postgresql

I have a table in DB:
<createTable tableName="api_consumer">
<column name="id"
type="INTEGER">
<constraints primaryKey="true"
primaryKeyName="PK_api_consumer_id"/>
</column>
<column name="business_id"
type="VARCHAR(50)"/>
<column name="type"
type="VARCHAR(32)"/>
<column name="status"
type="VARCHAR(32)"/>
<column name="description"
type="VARCHAR(256)"/>
<column name="created_by"
type="VARCHAR(64)"/>
<column name="created_date"
type="DATETIME"/>
<column name="modified_by"
type="VARCHAR(64)"/>
<column name="modified_date"
type="DATETIME"/>
<!--TTP below-->
<column name="name"
type="VARCHAR(64)">
</column>
<column name="origin_country"
type="VARCHAR(64)"/>
<column name="license_authority_name"
type="VARCHAR(64)"/>
<column name="registration_date"
type="DATETIME"/>
<column name="api_callback_url"
type="VARCHAR(256)"/>
<column name="application_base_url"
type="VARCHAR(256)"/>
<column name="authorization_callback_url"
type="VARCHAR(256)"/>
<column name="address"
type="INTEGER">
<constraints foreignKeyName="FK_api_consumer_address_id"
references="address(id)"/>
</column>
<column name="local_address"
type="INTEGER">
<constraints foreignKeyName="FK_api_consumer_local_address_id"
references="address(id)"/>
</column>
<column name="license"
type="INTEGER">
<constraints foreignKeyName="FK_api_consumer_license_id"
references="license(id)"/>
</column>
</createTable>
Generated in SQL dialect:
CREATE TABLE api_consumer
(
id INTEGER NOT NULL
CONSTRAINT "PK_api_consumer_id"
PRIMARY KEY,
business_id VARCHAR(50),
type VARCHAR(32),
status VARCHAR(32),
description VARCHAR(256),
created_by VARCHAR(64),
created_date TIMESTAMP,
modified_by VARCHAR(64),
modified_date TIMESTAMP,
name VARCHAR(64),
origin_country VARCHAR(64),
license_authority_name VARCHAR(64),
registration_date TIMESTAMP,
api_callback_url VARCHAR(256),
application_base_url VARCHAR(256),
authorization_callback_url VARCHAR(256),
address INTEGER
CONSTRAINT FK_api_consumer_address_id
REFERENCES address,
local_address INTEGER
CONSTRAINT FK_api_consumer_local_address_id
REFERENCES address,
license INTEGER
CONST
RAINT FK_api_consumer_license_id
REFERENCES license
);
and from the IntelliJ DB console I am trying to insert value to this table and test my triggers on it.
My insert is like:
INSERT INTO api_consumer VALUES ('11', '101', 'PSD2', 'NEW', 'TEST OF TRIGGER ON INSERT', 'kkwiatkowski', CURRENT_TIMESTAMP, null, null, 'SPRAWDZAM_COS', 'PL', null, null, null, null, null, 1, 1, 1);
And I am getting an error like:
sql> INSERT INTO api_consumer VALUES ('11', '101', 'PSD2', 'NEW', 'TEST OF TRIGGER ON INSERT', 'kkwiatkowski', CURRENT_TIMESTAMP, null, null, 'SPRAWDZAM_COS', 'PL', null, null, null, null, null, 1, 1, 1)
[2017-10-16 10:30:43] [42703] ERROR: column "insert" does not exist
[2017-10-16 10:30:43] Gdzie: PL/pgSQL function proc_subscription_audit_insert() line 3 at SQL statement
Word: "Gdzie" means Where in eng.
If I delete this id value: '11', then the insert query is invalid, cause the argument scope is moving on the right. Understandable.
[2017-10-16 10:39:08] [22007] ERROR: invalid input syntax for type timestamp: "SPRAWDZAM_COS"
[2017-10-16 10:39:08] Pozycja: 133
Word: "Pozycja" means Position in eng.
1) So how can I fix that?
2) How can I implement auto-increment value in here?
I have tried adding an autoIncrement="true" in the xml structure and then invoke this insert query without id argument. Neither this nor adding nextval(id) to the insert query works.
Edit:
procedures.xml
<changeSet id="1.0-procedures" author="blab">
<sql>
DROP FUNCTION IF EXISTS proc_api_consumer_audit_insert();
DROP FUNCTION IF EXISTS proc_api_consumer_audit_update();
DROP FUNCTION IF EXISTS proc_subscription_audit_insert();
DROP FUNCTION IF EXISTS proc_subscription_audit_update();
</sql>
<createProcedure>
CREATE FUNCTION proc_api_consumer_audit_insert()
RETURNS TRIGGER AS $api_consumer$
BEGIN
INSERT INTO api_consumer_audit(api_consumer_id, change_type, changed_by, changed_date, business_id_old, business_id_new, name_old, name_new, api_callback_url_old, api_callback_url_new, application_base_url_old, application_base_url_new, authorization_callback_url_old, authorization_callback_url_new, status_old, status_new) VALUES(NEW.id, 'INSERT', CURRENT_USER, CURRENT_TIMESTAMP, null, NEW.business_id, null, NEW.name, null, NEW.api_callback_url, null, NEW.application_base_url, null, NEW.authorization_callback_url, null, NEW.status);
RETURN NEW;
END;
$api_consumer$ LANGUAGE plpgsql;
</createProcedure>
<createProcedure>
CREATE FUNCTION proc_api_consumer_audit_update()
RETURNS TRIGGER AS $api_consumer$
BEGIN
INSERT INTO api_consumer_audit(api_consumer_id, change_type, changed_by, changed_date, business_id_old, business_id_new, name_old, name_new, api_callback_url_old, api_callback_url_new, application_base_url_old, application_base_url_new, authorization_callback_url_old, authorization_callback_url_new, status_old, status_new) VALUES(NEW.id, 'UPDATE', CURRENT_USER, CURRENT_TIMESTAMP, OLD.business_id, NEW.business_id, OLD.name, NEW.name, OLD.api_callback_url, NEW.api_callback_url, OLD.application_base_url, NEW.application_base_url, OLD.authorization_callback_url, NEW.authorization_callback_url, OLD.status, NEW.status);
RETURN NEW;
END;
$api_consumer$ LANGUAGE plpgsql;
</createProcedure>
<createProcedure>
CREATE FUNCTION proc_subscription_audit_insert()
RETURNS TRIGGER AS $subscription_audit$
BEGIN
INSERT INTO subscription_audit(subscription_id, change_type, status_old, status_new, valid_from_old, valid_from_new, valid_through_old, valid_through_new, changed_by, changed_date) VALUES (NEW.id, INSERT, null, NEW.status, null, NEW.valid_from, null, NEW.valid_through, CURRENT_USER, CURRENT_TIMESTAMP);
RETURN NEW;
END;
$subscription_audit$ LANGUAGE plpgsql;
</createProcedure>
<createProcedure>
CREATE FUNCTION proc_subscription_audit_update()
RETURNS TRIGGER AS $subscription_audit$
BEGIN
INSERT INTO subscription_audit(subscription_id, change_type, status_old, status_new, valid_from_old, valid_from_new, valid_through_old, valid_through_new, changed_by, changed_date) VALUES (NEW.id, 'UPDATE', OLD.status, NEW.status, OLD.valid_from, NEW.valid_from, OLD.valid_through, NEW.valid_through, CURRENT_USER, CURRENT_TIMESTAMP);
RETURN NEW;
END;
$subscription_audit$ LANGUAGE plpgsql;
</createProcedure>
<rollback>
DROP FUNCTION IF EXISTS proc_api_consumer_audit_insert();
DROP FUNCTION IF EXISTS proc_api_consumer_audit_update();
DROP FUNCTION IF EXISTS proc_subscription_audit_insert();
DROP FUNCTION IF EXISTS proc_subscription_audit_update();
</rollback>
</changeSet>
triggers.xml
<changeSet id="1.0-triggers" author="blab">
<sql>
DROP TRIGGER IF EXISTS trg_api_consumer_audit_insert ON api_consumer;
DROP TRIGGER IF EXISTS trg_api_consumer_audit_update ON api_consumer;
<!-- DROP TRIGGER IF EXISTS trg_api_consumer_audit_delete ON api_consumer-->
DROP TRIGGER IF EXISTS trg_subscription_audit_insert ON subscription;
DROP TRIGGER IF EXISTS trg_subscription_audit_update ON subscription;
<!-- DROP TRIGGER IF EXISTS trg_subscription_audit_delete ON subscription-->
</sql>
<createProcedure>
CREATE TRIGGER trg_api_consumer_audit_insert
AFTER INSERT ON api_consumer
FOR EACH ROW EXECUTE PROCEDURE proc_api_consumer_audit_insert();
</createProcedure>
<createProcedure>
CREATE TRIGGER trg_api_consumer_audit_update
AFTER UPDATE ON api_consumer
FOR EACH ROW EXECUTE PROCEDURE proc_api_consumer_audit_update();
</createProcedure>
<createProcedure>
CREATE TRIGGER trg_subscription_audit_insert
AFTER INSERT ON api_consumer
FOR EACH ROW EXECUTE PROCEDURE proc_subscription_audit_insert();
</createProcedure>
<createProcedure>
CREATE TRIGGER trg_subscription_audit_update
AFTER UPDATE ON api_consumer
FOR EACH ROW EXECUTE PROCEDURE proc_subscription_audit_update();
</createProcedure>
<rollback>
DROP TRIGGER IF EXISTS trg_api_consumer_audit_insert;
DROP TRIGGER IF EXISTS trg_api_consumer_audit_update;
DROP TRIGGER IF EXISTS trg_subscription_audit_insert;
DROP TRIGGER IF EXISTS trg_subscription_audit_update;
</rollback>
</changeSet>

Related

Postgresql update multiple rows with same name and id, and update the consecutive rows vrersion

I have a table where insertion is in this form.
Table
I want the verion to get update by 1 whenever there is a new row with same name and id.
Required output
I tried using a function and trigger.
CREATE OR REPLACE FUNCTION update_ver()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
update version
set ver = ver + 1
where new.name = 'A' and new.id ='1';
RETURN new;
END;
$$
-- create table
CREATE TABLE mytable (
"name" varchar NULL,
id int4 NULL,
phone varchar NULL,
email varchar NULL,
ver int4 NULL
);
-- create trigger function
CREATE OR REPLACE FUNCTION before_insert()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
begin
new.ver = (select coalesce(max(ver), 0) + 1 from mytable where name = new.name and id = new.id);
return new;
end;
$$
-- set trigger function to table
create trigger trg_before_insert before
insert
on
mytable for each row execute function before_insert();
-- inserting sample data
INSERT INTO mytable ("name", id, phone, email) VALUES('A', 1, '123', '123#email.com');
INSERT INTO mytable ("name", id, phone, email) VALUES('B', 2, '345', '345#email.com');
INSERT INTO mytable ("name", id, phone, email) VALUES('A', 1, '456', '456#email.com');
-- select data and view
select * from mytable;
Result:
name|id|phone|email |ver|
----+--+-----+-------------+---+
A | 1|123 |123#email.com| 1|
B | 2|345 |345#email.com| 1|
A | 1|456 |456#email.com| 2|

Postgres exclude using gist across different tables

I have 2 tables like this
drop table if exists public.table_1;
drop table if exists public.table_2;
CREATE TABLE public.table_1 (
id serial NOT NULL,
user_id bigint not null,
status varchar(255) not null,
date_start date NOT NULL,
date_end date NULL
);
CREATE TABLE public.table_2 (
id serial NOT NULL,
user_id bigint not null,
status varchar(255) not null,
date_start date NOT NULL,
date_end date NULL
);
alter table public.table_1
add constraint my_constraint_1
EXCLUDE USING gist (user_id with =, daterange(date_start, date_end, '[]') WITH &&)
where (status != 'deleted');
alter table public.table_2
add constraint my_constraint_2
EXCLUDE USING gist (user_id with =, daterange(date_start, date_end, '[]') WITH &&)
where (status != 'deleted');
Every table contains rows which are related to a user, and all the rows of the same user cannot overlap in range. In addition, some rows may be logically deleted, so I added a where condition.
So far it's working w/o problems, but the 2 constraints work separately for each table.
I need to create a constraint which cover the 2 set of tables, so that a single daterange (of the same user and not deleted), may appaer only once across the 2 different tables.
Does the EXCLUDE notation be extended to work with different tables or do I need to check it with a trigger? If the trigger is the answer, which is the simplier way to do this? Create a temporary table with the union of the 2, add the constraint on it and check if fails?
Starting from #Laurenz Albe suggestion, this is what I made
-- #################### SETUP SAMPLE TABLES ####################
drop table if exists public.table_1;
drop table if exists public.table_2;
CREATE TABLE public.table_1 (
id serial NOT NULL,
user_id bigint not null,
status varchar(255) not null,
date_start date NOT NULL,
date_end date NULL
);
CREATE TABLE public.table_2 (
id serial NOT NULL,
user_id bigint not null,
status varchar(255) not null,
date_start date NOT NULL,
date_end date NULL
);
alter table public.table_1
add constraint my_constraint_1
EXCLUDE USING gist (user_id with =, daterange(date_start, date_end, '[]') WITH &&)
where (status != 'deleted');
alter table public.table_2
add constraint my_constraint_2
EXCLUDE USING gist (user_id with =, daterange(date_start, date_end, '[]') WITH &&)
where (status != 'deleted');
-- #################### SETUP TRIGGER ####################
create or REPLACE FUNCTION check_date_overlap_trigger_hook()
RETURNS trigger as
$body$
DECLARE
l_table text;
l_sql text;
l_row record;
begin
l_table := TG_ARGV[0];
l_sql := format('
select *
from public.%s as t
where
t.user_id = %s -- Include only records of the same user
and t.status != ''deleted'' -- Include only records that are active
', l_table, new.user_id);
for l_row in execute l_sql
loop
IF daterange(l_row.date_start, COALESCE(l_row.date_end, 'infinity'::date)) && daterange(new.date_start, COALESCE(new.date_end, 'infinity'::date))
THEN
RAISE EXCEPTION 'Date interval is overlapping with another one in table %', l_table
USING HINT = 'You can''t have the same interval across table1 AND table2';
END IF;
end loop;
RETURN NEW;
end
$body$
LANGUAGE plpgsql;
-- #################### INSTALL TRIGGER ####################
create trigger check_date_overlap
BEFORE insert or update
ON public.table_1
FOR EACH row
EXECUTE PROCEDURE check_date_overlap_trigger_hook('table_2');
create trigger check_date_overlap
BEFORE insert or update
ON public.table_2
FOR EACH row
EXECUTE PROCEDURE check_date_overlap_trigger_hook('table_1');
-- #################### INSERT DEMO ROWS ####################
insert into public.table_1 (user_id, status, date_start, date_end) values (1, 'active', '2020-12-10', '2020-12-20');
insert into public.table_1 (user_id, status, date_start, date_end) values (1, 'deleted', '2020-12-15', '2020-12-25');
insert into public.table_1 (user_id, status, date_start, date_end) values (2, 'active', '2020-12-10', '2020-12-20');
insert into public.table_1 (user_id, status, date_start, date_end) values (2, 'deleted', '2020-12-15', '2020-12-25');
-- This will fail for overlap on the same table
-- insert into public.table_1 (user_id, status, date_start, date_end) values (1, 'active', '2020-12-15', '2020-12-25');
-- This will fail as the user 1 already has an overlapping period on table 1
-- insert into public.table_2 (user_id, status, date_start, date_end) values (1, 'active', '2020-12-15', '2020-12-25');
-- This will fail as the user 1 already has an overlapping period on table 1
insert into public.table_2 (user_id, status, date_start, date_end) values (1, 'deleted', '2020-12-15', '2020-12-25');
update public.table_2 set status = 'active' where id = 1;
select 'table_1' as src_table, * from public.table_1
union
select 'table_2', * from public.table_2
You can probably use a trigger, but triggers are always vulnerable to race conditions (unless you are using SERIALIZABLE isolation).
If your tables really have the same columns, why don't you use a single table (and perhaps add a type column to disambiguate)?

I'm having an issue with this code when I try to input values into the transactions table

So I'm setting up a schema in which I can input transactions of a journal entry independent of each other but also that rely on each other (mainly to ensure that debits = credits). I set up the tables, function, and trigger. Then, when I try to input values into the transactions table, I get the error below. I'm doing all of this in pgAdmin4.
CREATE TABLE transactions (
transactions_id UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
entry_id INTEGER NOT NULL,
post_date DATE NOT NULL,
account_id INTEGER NOT NULL,
contact_id INTEGER NULL,
description TEXT NOT NULL,
reference_id UUID NULL,
document_id UUID NULL,
amount NUMERIC(12,2) NOT NULL
);
CREATE TABLE entries (
id UUID PRIMARY KEY,
test_date DATE NOT NULL,
balance NUMERIC(12,2)
CHECK (balance = 0.00)
);
CREATE OR REPLACE FUNCTION transactions_biut()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
EXECUTE 'INSERT INTO entries (id,test_date,balance)
SELECT
entry_id,
post_date,
SUM(amount) AS ''balance''
FROM
transactions
GROUP BY
entry_id;';
END;
$$;
CREATE TRIGGER transactions_biut
BEFORE INSERT OR UPDATE ON transactions
FOR EACH ROW EXECUTE PROCEDURE transactions_biut();
INSERT INTO transactions (
entry_id,
post_date,
account_id,
description,
amount
)
VALUES
(
'1',
'2019-10-01',
'101',
'MISC DEBIT: PAID FOR FACEBOOK ADS',
-200.00
),
(
'1',
'2019-10-01',
'505',
'MISC DEBIT: PAID FOR FACEBOOK ADS',
200.00
);
After I execute this input, I get the following error:
ERROR: column "id" of relation "entries" does not exist
LINE 1: INSERT INTO entries (id,test_date,balance)
^
QUERY: INSERT INTO entries (id,test_date,balance)
SELECT
entry_id,
post_date,
SUM(amount) AS "balance"
FROM
transactions
GROUP BY
entry_id;
CONTEXT: PL/pgSQL function transactions_biut() line 2 at EXECUTE
SQL state: 42703
There are a few problems here:
You're not returning anything from the trigger function => should probably be return NEW or return OLD since you're not modifying anything
Since you're executing the trigger before each row, it's bound to fail for any transaction that isn't 0 => maybe you want a deferred constraint trigger?
You're not grouping by post_date, so your select should fail
You've defined entry_id as INTEGER, but entries.id is of type UUID
Also note that this isn't really going to scale (you're summing up all transactions of all days, so this will get slower and slower...)
#chirs I was able to figure out how to create a functioning solution using statement-level triggers:
CREATE TABLE transactions (
transactions_id UUID PRIMARY KEY DEFAULT uuid_generate_v1(),
entry_id INTEGER NOT NULL,
post_date DATE NOT NULL,
account_id INTEGER NOT NULL,
contact_id INTEGER NULL,
description TEXT NOT NULL,
reference_id UUID NULL,
document_id UUID NULL,
amount NUMERIC(12,2) NOT NULL
);
CREATE TABLE entries (
entry_id INTEGER PRIMARY KEY,
post_date DATE NOT NULL,
balance NUMERIC(12,2),
CHECK (balance = 0.00)
);
CREATE OR REPLACE FUNCTION transactions_entries() RETURNS TRIGGER AS $$
BEGIN
IF (TG_OP = 'DELETE') THEN
INSERT INTO entries
SELECT o.entry_id, o.post_date, SUM(o.amount) FROM old_table o GROUP BY o.entry_id, o.post_date;
ELSIF (TG_OP = 'UPDATE') THEN
INSERT INTO entries
SELECT o.entry_id, n.post_date, SUM(n.amount) FROM new_table n, old_table o GROUP BY o.entry_id, n.post_date;
ELSIF (TG_OP = 'INSERT') THEN
INSERT INTO entries
SELECT n.entry_id,n.post_date, SUM(n.amount) FROM new_table n GROUP BY n.entry_id, n.post_date;
END IF;
RETURN NULL; -- result is ignored since this is an AFTER trigger
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER transactions_ins
AFTER INSERT ON transactions
REFERENCING NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE PROCEDURE transactions_entries();
CREATE TRIGGER transactions_upd
AFTER UPDATE ON transactions
REFERENCING OLD TABLE AS old_table NEW TABLE AS new_table
FOR EACH STATEMENT EXECUTE PROCEDURE transactions_entries();
CREATE TRIGGER transactions_del
AFTER DELETE ON transactions
REFERENCING OLD TABLE AS old_table
FOR EACH STATEMENT EXECUTE PROCEDURE transactions_entries();
Any thoughts on optimization?

Why select statement inside stored procedure in PostgreSQL throws error 'column does not exist..?

I have this stored procedure in PostgreSQL where I want to make an insert in a table. I get some parameters from the procedure and using them I have tried to select other attributes on other tables.
This is my stored procedure:
CREATE OR REPLACE FUNCTION "public"."prc_sales_invoice_header_insert"("customercode" varchar, "sales_note" varchar, "automatic_payment_id" int4, "cash_register_code" varchar,...etc)
RETURNS "pg_catalog"."void" AS $BODY$
--declaring variables to store data from the tables
DECLARE
salesdate date;
salesdocumentserial varchar;
currencycode varchar;
currencycode2 varchar;
customername varchar;
warehousecode varchar;
......etc.
BEGIN
--getting values from tables and storing to variables
SELECT "name" into customername from public."customers" where "customer_code" = customercode;
SELECT CURRENT_DATE into salesdate;
SELECT max(sales_invoice_header_id) into salesdocumentserial from public."sales_invoice_header";
.....
--inserting values
INSERT INTO public."sales_invoice_header"("sales_date",
"sales_document_serial",
"currency_code",
"currency_code2",
"customer_code",
....
VALUES(
salesdate,
salesdocumentserial,
currencycode,
currencycode2,
customer_code,
.....)
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
When I try to execute it throws an error saying:
"ERROR: column "customer_code" does not exist", "HINT: There is a column named "customer_code" in table "sales_invoice_header", but it cannot be referenced from this part of the query."
Table customers exists and there is a column named costomer_code but I don't understand why it cannot reference it.
Table customers:
-- ----------------------------
-- Table structure for customers
-- ----------------------------
DROP TABLE IF EXISTS "public"."customers";
CREATE TABLE "public"."customers" (
"customer_id" int4 NOT NULL DEFAULT nextval('"Customers_CustomerId_seq"'::regclass),
"customer_code" varchar COLLATE "pg_catalog"."default",
"barcode" varchar COLLATE "pg_catalog"."default",
"qr_code" varchar COLLATE "pg_catalog"."default",
"tax_id" varchar COLLATE "pg_catalog"."default",
"business_id" varchar COLLATE "pg_catalog"."default",
"city_id" int8,
"mobile" varchar COLLATE "pg_catalog"."default",
"accounting_number" varchar COLLATE "pg_catalog"."default",
"name" varchar COLLATE "pg_catalog"."default"
);
Can anyone help me with this, what I am doing wrong? Or is this the correct way of doing things?
Thanks in advance.
INSERT INTO public."sales_invoice_header"("sales_date",
"sales_document_serial",
"currency_code",
"currency_code2",
"customer_code",
....
VALUES(
salesdate,
salesdocumentserial,
currencycode,
currencycode2,
customer_code,
here customer_code is not a variable, nor literal - it is a column name. yet you don't select it form table - you try to use it in VALUES - won't work. either use
insert into ... select ...,customer_code from sales_invoice_header
or
insert into ... values(..., VAR_customer_code)
or
insert into ... values(..., 'value_customer_code')
edit:
as a_horse_with_no_name noticed my VAR_customer_code, must be your customercode first argument?..

Postgres RLS inconsistency on one table

I'm using RLS in a multi-tenancy model. No problems for the first several tables I enabled. Then I add RLS to a new table and suddenly I cannot insert a record to that table.
Here is a table that works:
CREATE TABLE wtr.adjustment (
id uuid UNIQUE NOT NULL DEFAULT uuid_generate_v1(),
created_at timestamp NOT NULL DEFAULT current_timestamp,
updated_at timestamp NOT NULL DEFAULT current_timestamp,
vendor_id uuid NOT NULL,
reporting_period_id uuid NOT NULL,
inventory_lot_id uuid NOT NULL,
adjustment_date date NOT NULL,
quantity_delta NUMERIC(50,2) NOT NULL,
adjustment_type wtr.adjustment_type NOT NULL,
comments text,
CONSTRAINT pk_adjustment PRIMARY KEY (id)
);
--||--
GRANT select, insert, update, delete ON TABLE wtr.adjustment TO wtr_user;
--||--
ALTER TABLE wtr.adjustment ADD CONSTRAINT fk_adjustment_vendor FOREIGN KEY ( vendor_id ) REFERENCES wtr.vendor( id );
--||--
ALTER TABLE wtr.adjustment ADD CONSTRAINT fk_adjustment_reporting_period FOREIGN KEY ( reporting_period_id ) REFERENCES wtr.reporting_period( id );
--||--
ALTER TABLE wtr.adjustment ADD CONSTRAINT fk_adjustment_inventory_lot FOREIGN KEY ( inventory_lot_id ) REFERENCES wtr.inventory_lot( id );
--||--
ALTER TABLE wtr.adjustment ENABLE ROW LEVEL SECURITY;
--||--
CREATE POLICY select_adjustment ON wtr.adjustment FOR SELECT
USING (vendor_id = wtr.current_vendor_id());
--||--
CREATE FUNCTION wtr.fn_timestamp_update_adjustment() RETURNS trigger AS $$
BEGIN
NEW.updated_at = current_timestamp;
RETURN NEW;
END; $$ LANGUAGE plpgsql;
--||--
CREATE TRIGGER tg_timestamp_update_adjustment
BEFORE UPDATE ON wtr.adjustment
FOR EACH ROW
EXECUTE PROCEDURE wtr.fn_timestamp_update_adjustment();
--||--
and the associated function that works:
CREATE OR REPLACE FUNCTION wtr.build_adjustment(
_reporting_period_id uuid,
_inventory_lot_gov_id text,
_strain_name text,
_room_name text,
_inventory_type_name text,
_adjustment_date text,
_quantity_delta NUMERIC(50,2),
_adjustment_type wtr.adjustment_type,
_comments text
)
RETURNS wtr.adjustment as $$
DECLARE
_vendor_id uuid;
_inventory_lot wtr.inventory_lot;
_adjustment wtr.adjustment;
_inventory_type_id uuid;
BEGIN
_vendor_id := wtr.current_vendor_id();
_inventory_lot := wtr.find_or_build_existing_inventory_lot(
_strain_name,
_room_name,
_inventory_lot_gov_id,
_inventory_type_name
);
INSERT INTO wtr.adjustment(
vendor_id,
reporting_period_id,
inventory_lot_id,
adjustment_date,
quantity_delta,
adjustment_type,
comments
)
SELECT
_vendor_id,
_reporting_period_id,
_inventory_lot.id,
_adjustment_date::DATE,
_quantity_delta,
_adjustment_type,
_comments
RETURNING *
INTO _adjustment;
RETURN _adjustment;
END;
$$ language plpgsql strict security definer;
--||--
GRANT execute ON FUNCTION wtr.build_adjustment(
uuid,
text,
text,
text,
text,
text,
numeric,
wtr.adjustment_type,
text
) TO wtr_user;
here is the table that fails:
CREATE TABLE wtr.received_inventory_transfer (
id uuid UNIQUE NOT NULL DEFAULT uuid_generate_v1(),
created_at timestamp NOT NULL DEFAULT current_timestamp,
updated_at timestamp NOT NULL DEFAULT current_timestamp,
vendor_id uuid NOT NULL,
reporting_period_id uuid NOT NULL,
inventory_lot_id uuid NOT NULL,
transfer_date DATE NOT NULL,
quantity_received NUMERIC(50,2) NOT NULL,
CONSTRAINT pk_received_inventory_transfer PRIMARY KEY (id)
);
--||--
GRANT select, insert, update, delete ON TABLE wtr.received_inventory_transfer TO wtr_user;
--||--
ALTER TABLE wtr.received_inventory_transfer ADD CONSTRAINT fk_received_inventory_transfer_vendor FOREIGN KEY ( vendor_id ) REFERENCES wtr.vendor( id );
--||--
ALTER TABLE wtr.received_inventory_transfer ADD CONSTRAINT fk_received_inventory_transfer_reporting_period FOREIGN KEY ( reporting_period_id ) REFERENCES wtr.reporting_period( id );
--||--
ALTER TABLE wtr.received_inventory_transfer ADD CONSTRAINT fk_received_inventory_transfer_inventory_lot FOREIGN KEY ( inventory_lot_id ) REFERENCES wtr.inventory_lot( id );
--||--
ALTER TABLE wtr.received_inventory_transfer ENABLE ROW LEVEL SECURITY;
--||--
CREATE POLICY select_received_inventory_transfer ON wtr.received_inventory_transfer FOR SELECT USING (vendor_id = wtr.current_vendor_id());
--||--
CREATE FUNCTION wtr.fn_timestamp_update_received_inventory_transfer() RETURNS trigger AS $$
BEGIN
NEW.updated_at = current_timestamp;
RETURN NEW;
END; $$ LANGUAGE plpgsql;
--||--
CREATE TRIGGER tg_timestamp_update_received_inventory_transfer
BEFORE UPDATE ON wtr.received_inventory_transfer
FOR EACH ROW
EXECUTE PROCEDURE wtr.fn_timestamp_update_received_inventory_transfer();
--||--
and the associated failing function:
CREATE OR REPLACE FUNCTION wtr.build_received_inventory_transfers(
_reporting_period_id uuid,
_transfer_date text
-- _received_inventory_transfers jsonb
)
RETURNS wtr.received_inventory_transfer as $$
DECLARE
_vendor_id uuid;
_inventory_lot wtr.inventory_lot;
_received_inventory_transfer_info jsonb;
_received_inventory_transfer wtr.received_inventory_transfer;
_quantity numeric(50,2);
BEGIN
_vendor_id := wtr.current_vendor_id();
-- this call is currently hard coded for debug purposes
_inventory_lot := wtr.find_or_build_existing_inventory_lot(
'tacos',
'N/A',
'1234123412341234',
'Hash'
);
-- again, this is hard-coded for debug purposes
RAISE EXCEPTION 'v: %, rp: %, il: %, td: %, q: %',
_vendor_id,
_reporting_period_id,
_inventory_lot.id,
_transfer_date::DATE,
20
;
-- this is the call that fails
INSERT INTO wtr.received_inventory_transfer(
vendor_id,
reporting_period_id,
inventory_lot_id,
transfer_date,
quantity_received
)
SELECT
_vendor_id,
_reporting_period_id,
_inventory_lot.id,
_transfer_date::DATE,
20
RETURNING *
INTO _received_inventory_transfer;
RETURN _received_inventory_transfer;
END;
$$ language plpgsql;
--||--
GRANT execute ON FUNCTION wtr.build_received_inventory_transfers(
uuid,
text
-- jsonb
) TO wtr_user;
the current_vendor_id function uses a jwt token claim that is passed in by the server, which is postgraphile.
At the insert statement, the call fails with:
new row violates row-level security policy for table
\"received_inventory_transfer\"",
This info is all that the logs will show me.
I think my real question is - how can I further debug RLS policy? If I only enable RLS, but create no select policy, I get the same failure.
of course, the next thing i figured out:
CREATE POLICY insert_received_inventory_transfer
ON wtr.received_inventory_transfer
FOR INSERT
WITH CHECK (vendor_id = wtr.current_vendor_id());