PostgreSQL reports a key violation, but that is not possible - postgresql

I have wrote a stored-procedure in PostgreSQL as an API to create contract and user in a same time (by postgrest).
CREATE OR REPLACE FUNCTION
auth.create_contracts(
username text,
password text,
services json,
province text,
region text,
type user_group,
telephone text,
mobile text,
email text,
start_date text,
end_date text,
organization_name text,
contract_number text,
name text,
economy_code INTEGER,
t_name text,
t_phone text,
t_ip text,
t_email text,
t_telephone text,
t_prefer_contract text,
area_state text,
area_city text,
area_county text
) RETURNS json
LANGUAGE plpgsql
AS $$
DECLARE
_user_id INTEGER;
_res json;
_now DOUBLE PRECISION;
_app_token text;
_username text;
BEGIN
SELECT random_string(64) INTO _app_token;
INSERT INTO auth.users(
username,
password,
role,
services,
app_token,
province,
region,
type,
phone,
organization_name,
mobile,
email
)
VALUES (
username,
password,
username,
services,
_app_token,
province,
region,
type,
telephone,
organization_name,
mobile,
email
);
SELECT username INTO _username;
SELECT id FROM auth.users WHERE users.username=_username
INTO _user_id;
INSERT INTO auth.contracts(
user_id,
start_date,
end_date,
organization_name,
contract_number,
economy_code,
t_name,
t_phone,
t_ip,
t_email,
t_telephone,
t_prefer_contract,
name,
area_state,
area_city,
area_county
)
VALUES(
_user_id,
start_date::TIMESTAMP,
end_date::TIMESTAMP,
organization_name,
contract_number,
economy_code,
t_name,
t_phone,
t_ip,
t_email,
t_telephone,
t_prefer_contract,
name,
area_state,
area_city,
area_county
);
SET TIME ZONE 'UTC-3:32';
PERFORM (
SELECT auth.contract_redis_function(end_date::TIMESTAMP, now()::TIMESTAMP,
_app_token, username, 100000)
);
SELECT json_build_object(
'status', 'ok',
'app_token', _app_token
)
INTO _res;
RETURN _res;
END;
$$;
users definition:
create table auth.users
(
id serial not null
constraint users_pkey
primary key,
username text,
password text,
role text,
services json,
app_token text,
province text,
region text,
type user_group,
contract_id integer,
phone text,
picture text,
is_first_login boolean default true,
is_locked boolean default false,
is_active boolean default true,
organization_name text,
mobile text,
email text,
prefer_contact text
)
;
create unique index users_username_uindex
on users (username);
contracts definition:
create table auth.contracts
(
user_id integer
constraint unique_contract
unique
constraint contract_user_01
references auth.users,
start_date timestamp,
end_date timestamp,
organization_name text,
contract_number text,
economy_code integer,
t_name text,
t_phone text,
t_ip text,
t_email text,
t_telephone text,
t_prefer_contract text,
area_state text,
area_city text,
area_county text,
name text,
id serial not null,
is_active boolean default true
);
I have a unique constraint on a key of users relation (id that is auto incremented)
sometimes when call the end-point raise an exception that key exists, but doesn't, and create record.
I don't know why!?
I have a similar problem when creating policy in a stored-procedure.

username is not unique in auth.users, so there is no guarantee that this query gets the correct id:
SELECT id FROM auth.users WHERE users.username=_username
INTO _user_id;
You will get the id of one of the rows with that username.
If there are several such rows, you could get the wrong one, and the UNIQUE constraint on contracts will be violated when you insert the same number a second time.
The solution is to always get the correct id like this:
INSERT INTO auth.users (...) VALUES (...)
RETURNING id INTO _user_id;
Then you will always get the id of the record you just inserted.

Related

POSTGRESQL: inserting multiple values in second table using uuid from first insert

Tables
CREATE TABLE “transactions” (
“uuid” uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
“user_uuid” uuid,
“chain_uuid” uuid,
“block_number” int,
“timestamp” int,
“hash” text,
“from” text,
“to” text,
“address” text,
“value” numeric,
“value_fiat” numeric
“fees” numeric,
“fees_fiat” numeric,
“is_error” int,
“input” text,
“protocol” text,
“type” text,
“reviewed_status”,
“is_manual” boolean,
“is_transferring_native_asset” boolean
);
CREATE TABLE “transaction_assets” (
“uuid” uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
“transaction_uuid” uuid,
“asset_uuid” uuid,
“type” text,
“is_assetin”, boolean
“balance” numeric,
“price” numeric,
“value” numeric
--CONSTRAINT transaction_assets_pkey PRIMARY KEY (uuid)
);
My Query
Begin;
with tx as (
INSERT INTO
transactions (user_uuid, chain_uuid, ..blah blah blah)
VALUES($ 1, $ 2, $ 3,.... $ 16 ) RETURNING uuid)
INSERT INTO
transaction_assets (transaction_uuid, asset_uuid, type, is_assetin, balance, price,value
)
VALUES
((SELECT tx.uuid FROM tx),$ 17,$ 18,$ 19,$ 20,$ 21),
((SELECT tx.uuid FROM tx),$ 22,$ 23,$ 24,$ 25,$ 26);
// repeat with many transactions
Commit;
I am using pg-promise. I have an array of transaction objects in javascript, each transaction has an array of assets associated with it. I would like to insert the transactions into the transaction table and the assets associated with that transaction into the transaction_assets table, using the transaction UUID from the first insert. I have many transactions to insert. which is why I am using the begin/commit. I would like to insert many transactions at the same time but if it fails be able to role back all of them
my current query is getting the error
syntax error at or near "from"

Running A FOREACH after a CTE in a PostgreSQL procedure?

I have a PLPGSQL procedure that:
A) Inserts an employee into an Employee table.
B) Also insert the generated serial eid into another Manager table.
C) Finally, the procedure also dictates an array of course_areas that I would like to insert in a specialize table.
Procedure where C and D array are the course_areas
CALL add_employee('Athena', '22222222', 'athena#outlook.com', '2012-12-12', '111', 'instructor', 300.0, NULL,
'{C,D}'::text[]);
Employees
CREATE TABLE Employees (
eid SERIAL PRIMARY KEY,
name TEXT NOT NULL,
phone TEXT NOT NULL,
email TEXT NOT NULL,
join_date DATE NOT NULL,
address TEXT NOT NULL,
depart_date DATE
);
Managers
CREATE TABLE Managers (
eid INT PRIMARY KEY,
monthly_salary DECIMAL(10,2) NOT NULL,
FOREIGN KEY (eid) REFERENCES Employees(eid)
ON DELETE CASCADE
);
Specializes
CREATE TABLE Specializes (
eid INT NOT NULL,
name TEXT NOT NULL,
PRIMARY KEY (eid, name),
FOREIGN KEY (eid) REFERENCES Employees(eid)
on DELETE CASCADE,
FOREIGN KEY (name) REFERENCES Course_areas(name)
on DELETE CASCADE
);
procedure.sql
CREATE OR REPLACE PROCEDURE add_employee (name TEXT, phone TEXT, email TEXT, joinDate DATE, address TEXT, category TEXT, monthlySalary DECIMAL(10,2) default NULL, hourlySalary DECIMAL(10,2) default NULL, courseAreas TEXT[] default NULL)
...
WITH FIRST AS (
INSERT INTO Employees(
name, phone, email, join_date, address
)
VALUES
(
name, phone, email, joinDate, address
) RETURNING eid
),
SECOND AS (
INSERT INTO Full_time_instructors(eid, monthly_salary)
SELECT
eid,
monthlySalary
FROM
ROWS RETURNING eid
)
FOREACH area IN ARRAY courseAreas
LOOP
RAISE NOTICE '%', area; -- should print "C" and "D"
END LOOP;
Error
ERROR: syntax error at or near "FOREACH"
LINE 27: FOREACH area IN ARRAY courseAreas
I can get A) and B) to work.
How can I use FOREACH to iterate through my courseAreas that I pass to the procedure such that I can insert each course area and the eid into a Specialize table?
The FOREACH is a PLPGSQL control structure whereas the CTE is a SQL feature. You can't mix these. Instead of using CTE you can simply perform the first statement, using the RETURNING clause to retrieve the eid to a local variable. You can then use this variable with the subsequent statement and inside your FOREACH loop.
This question gives an overview of getting the new serial value using returning: Get default serial value after INSERT inside PL/pgSQL.

PostgreSQL ERROR: relation "XXX" does not exist and unsure why

I run my code and get the error in PostgreSQL. I have created multiple tables and the first seems to work while the others don't.
create table people (
id serial,
family_names text,
given_names text,
displayed_names text,
email_address text,
primary key (id)
);
create table users (
userid integer,
website text,
date_registered date,
gender GenderValue,
birthday date,
password text,
portrait integer,
primary key (userid),
foreign key (userid) references people(id),
foreign key (portrait) references photos(photoid)
);
create table photos (
photoid serial,
title TitleValue,
date_uploaded date,
date_taken date,
description text,
technical_details text,
safety_level safetyLevel,
visibility visibilityLevel,
owns integer,
primary key (photoid),
foreign key (owns) references users(userid)
);

Cassandra Select query returns allow filtering even if the column is in primary key

I have created a orders table which is something like this
create_query1 = CREATE TABLE IF NOT EXISTS orders
(status text, city text, company text, country_id text, created_at timestamp,
customer_address_id text, customer_id text, discount_amount float, discount_invoiced float, email text,
entity_id text, firstname text, free_shipping float, grand_total_final float, is_qty_decimal float,
is_virtual text, item_id text, lastname text, method text, name text, no_discount float,order_id text,
original_price float, parent_id text, postcode text, price float, price_incl_tax float,
product_id text, product_type text, qty_ordered float, qty_shipped float, quote_item_id text,PRIMARY KEY ((order_id), created_at ))
When I query something like
SELECT * from orders where created_at >= '2019-01-31 20:55:43' and created_at <= '2019-05-13 13:58:15'
I get this error
Error from server: code=2200 [Invalid query] message="Cannot execute this query as it might involve data filtering and thus may have unpredictable performance. If you want to execute this query despite the performance unpredictability, use ALLOW FILTERING"
What am I doing wrong here?
Is Cassandra not suited for this type of query?
Query looks correct but you are using a different table name in query than the one you created.

Optimizing pgSQL function performance

I really need advice. I'm trying to use the function : pivot_subrecords_set_table_1
In the following query:
SELECT rec.id AS key,
rec.primaryurn AS urn,
rec.mx_syscreatedby AS creared_by,
(SELECT mf_mxdate_str_to_date (epi.mx_epizoddate)) AS date_of_creation,
epi.mx_epizodenotes AS notes,
epi.mx_effectscountry AS country,
epi.mx_effectsregion AS region,
epi.mx_effectscity AS city,
(SELECT get_epizod_subrecords (epi.id)) AS epizod_type
(pivot_subrecords_set_table_1 (epi.id)).idx,
(pivot_subrecords_set_table_1 (epi.id)).upn_1 AS upn_1,
(pivot_subrecords_set_table_1 (epi.id)).status_1
FROM mxt_recordheader AS rec, mt_epizod AS epi, mt_epizod_subrecord AS eps
WHERE rec.entitytype = 'epizod'
AND rec.logicalserverprefix = 'EA'
AND epi.id = rec.id;
The function looks like this:
CREATE OR REPLACE FUNCTION public.pivot_subrecords_set_table_1(id bigint,
OUT idx bigint, OUT upn_1 text, OUT status_1 text, OUT upn_2 text, OUT
status_2 text, OUT upn_3 text, OUT status_3 text, OUT upn_4 text, OUT
status_4 text, OUT upn_5 text, OUT status_5 text, OUT upn_6 text, OUT
status_6 text, OUT upn_7 text, OUT status_7 text, OUT upn_8 text, OUT
status_8 text, OUT upn_9 text, OUT status_9 text, OUT upn_10 text, OUT
status_10 text)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
DECLARE
key ALIAS FOR $1;
BEGIN
RETURN QUERY
WITH sub_upn AS (SELECT * FROM crosstab
('SELECT id, mx_osobastatus, mx_osobaupn FROM mt_epizod_subrecord
WHERE mt_epizod_subrecord.mx_syscategory = ''tableepizodinfo''
AND mt_epizod_subrecord.id = '|| key ||' GROUP BY id, mx_osobaupn,
mx_osobastatus
ORDER BY id') AS ct
(id bigint, UPN_1 text, UPN_2 text, UPN_3 text, UPN_4 text, UPN_5 text, UPN_6
text, UPN_7 text, UPN_8 text ,UPN_9 text, UPN_10 text)
),
sub_stat AS
(SELECT * FROM crosstab
('SELECT id, mx_osobaupn, mx_osobastatus FROM mt_epizod_subrecord
WHERE mt_epizod_subrecord.mx_syscategory = ''tableepizodinfo''
AND mt_epizod_subrecord.id = '|| key ||' GROUP BY 1,2,3 ORDER BY id ')
AS cn (id bigint, status_1 text, status_2 text, status_3 text, status_4 text,
status_5 text, status_6 text, status_7 text, status_8 text,
status_9 text, status_10 text)
)
SELECT
supn.id,
supn.UPN_1,
sust.status_1,
supn.UPN_2,
sust.status_2,
supn.UPN_3,
sust.status_3,
supn.UPN_4,
sust.status_4,
supn.UPN_5,
sust.status_5,
supn.UPN_6,
sust.status_6,
supn.UPN_7,
sust.status_7,
supn.UPN_8,
sust.status_8,
supn.UPN_9,
sust.status_9,
supn.UPN_10,
sust.status_10
FROM sub_upn supn
JOIN sub_stat sust ON supn.id = sust.id;
END;
$function$
But the performance of the function is very low.
How can I increase the performance of a function or can I rewrite the query in which it is used?
Use plprofiler to figure out what is slow in your function and optimize that.