You might need to add explicit type casts - postgresql

I am trying to create function which returns last inserted id, but its showing error while function call:
Will you please help me to identify my error.
ERROR: function public.insert_voucher(integer, unknown, unknown, integer, integer, unknown) does not exist
LINE 1: select public.insert_voucher(1, 'P', '20180909', 1, 1, 'txt');
Function:
CREATE OR REPLACE FUNCTION public.insert_voucher(
in_orgid smallint
,in_transtype character
,in_date character
,in_partnerid smallint
,in_quantity smallint
,in_remarks character varying)
RETURNS integer AS
$BODY$
BEGIN
insert into
public.transaction_header(
org_id
,trans_type
,fiscal_year
,date
,partner_id
,quantity
,remarks
,create_by
,create_ts)
values (
in_orgid
,in_transtype
,1819
,in_date
,in_partnerid
,in_quantity
,in_remarks
,1
,now())
returning trans_header_id;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 1;
Table Schema:
trans_header_id integer NOT NULL DEFAULT nextval('transaction_header_trans_header_id_seq'::regclass)
,org_id smallint NOT NULL
,trans_type character(1) NOT NULL DEFAULT 'P'::bpchar
,fiscal_year smallint NOT NULL DEFAULT '1819'::smallint
,date date NOT NULL
,partner_id smallint NOT NULL
,quantity smallint NOT NULL
,remarks character varying(100)
,create_by smallint NOT NULL
,create_ts timestamp without time zone NOT NULL DEFAULT now()
,update_by smallint
,update_ts timestamp without time zone
,CONSTRAINT transaction_header_pk PRIMARY KEY (trans_header_id)
,CONSTRAINT create_by FOREIGN KEY (create_by)
REFERENCES public.app_user (user_id) MATCH FULL
ON UPDATE NO ACTION ON DELETE NO ACTION
,CONSTRAINT org_id FOREIGN KEY (org_id)
REFERENCES public.organization (org_id) MATCH FULL
ON UPDATE NO ACTION ON DELETE NO ACTION
,CONSTRAINT partner_id FOREIGN KEY (partner_id)
REFERENCES public.partners (partner_id) MATCH FULL
ON UPDATE NO ACTION ON DELETE NO ACTION
,CONSTRAINT update_by FOREIGN KEY (update_by)
REFERENCES public.app_user (user_id) MATCH FULL
ON UPDATE NO ACTION ON DELETE NO ACTION
,CONSTRAINT org_fy_transtype_transno UNIQUE (org_id, trans_type, fiscal_year)
With reference #muistooshort and #stickybit, I am updating previous function.
Hope it will give more clarity and returns desired result.
CREATE OR REPLACE FUNCTION public.insert_voucher(
IN in_orgid smallint
,IN in_transtype character
,IN in_date date
,IN in_partnerid smallint
,IN in_quantity smallint
,IN in_remarks character varying
,OUT out_id smallint)
RETURNS smallint AS
$BODY$
BEGIN
insert into
public.transaction_header(
org_id
,trans_type
,fiscal_year
,date
,partner_id
,quantity
,remarks
,create_by
,create_ts)
values (
in_orgid
,in_transtype
,1819
,in_date
,in_partnerid
,in_quantity
,in_remarks
,1
,now())
RETURNING trans_header_id
INTO out_id;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 1;

The smallints are the problem. A cast from integer to smallint can mean losing some information. The engine won't do implicit casts, where information can get lost. Therefore it considers public.insert_voucher(integer, unknown, unknown, integer, integer, unknown) as not an option.
If you explicitly cast the numbers to smallint, the call should work.
SELECT public.insert_voucher(1::smallint, 'P', '20180909', 1::smallint, 1::smallint, 'txt');
There are some other things like why you pass a date as a string and '1819'::smallint (why the string first?). And some other problem may arise once the call works. But that's beyond the current question.

Related

postgresql insert with integer PK nextval

So, I have the following table definition with the id as integer PK using a nextval seq as shown below.
'''
CREATE TABLE public.fi_raisedalarms
(
id integer NOT NULL DEFAULT nextval('fi_raisedalarms_id_seq'::regclass),
equipid integer,
alid integer,
isset boolean,
tstamp timestamp without time zone,
create_uid integer,
create_date timestamp without time zone,
write_uid integer,
write_date timestamp without time zone,
CONSTRAINT fi_raisedalarms_pkey PRIMARY KEY (id),
CONSTRAINT fi_raisedalarms_alid_fkey FOREIGN KEY (alid)
REFERENCES public.fi_alarms (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE SET NULL,
CONSTRAINT fi_raisedalarms_create_uid_fkey FOREIGN KEY (create_uid)
REFERENCES public.res_users (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE SET NULL,
CONSTRAINT fi_raisedalarms_write_uid_fkey FOREIGN KEY (write_uid)
REFERENCES public.res_users (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE SET NULL
)
'''
I then want to use a function as shown below to insert omitting the id since it should pull the default value but I am getting the following error. Not sure where to go from here....
'''
CREATE OR REPLACE FUNCTION public.setequipmentalarm(
equipmentid integer,
alarmid integer,
isset boolean,
tstamp timestamp without time zone)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
DECLARE
var integer;
BEGIN
INSERT INTO fi_raisedalarms VALUES(equipmentid, alarmid, isset, tstamp) RETURNING equipmentid into var;
RETURN var;
END;
$BODY$;
ALTER FUNCTION public.setequipmentalarm(integer, integer, boolean, timestamp without time zone)
OWNER TO postgres;
'''
ERROR: column "alid" is of type integer but expression is of type boolean
LINE 1: ...INTO fi_raisedalarms VALUES(equipmentid, alarmid, isset, tst...
^
HINT: You will need to rewrite or cast the expression.
QUERY: INSERT INTO fi_raisedalarms VALUES(equipmentid, alarmid, isset, tstamp) RETURNING equipmentid
CONTEXT: PL/pgSQL function setequipmentalarm(integer,integer,boolean,timestamp without time zone) line 5 at SQL statement
SQL state: 42804
You need to specify column names if you are not setting a value for each table column:
INSERT INTO fi_raisedalarms(equipid, alid , isset, tstamp) VALUES ...
Alternatively, you can insert DEFAULT in place of the column to explicitly choose the default value. But specifying the columns is preferable.

How to automatically fill columns on a row based on the foreign key being the primary key on another table

I'm working on a sql database for QGIS. I have 8 tables: 3 three of them are the base tables and the others are relational tables. One relational table,
"tabela_is_po", has 4 fields:
indice_sequencial_po (integer and Primary Key),
tema (character varying),
subtema (character varying),
designacao (character varying)
And the base table, "tabela_objecto_area", has 7 fields:
identificador integer ,
dtcc character varying(4),
planta_po boolean,
indice_sequencial_po integer (foreign key),
tema_po character varying(254),
subtema_po character varying(254),
designacao_po character varying(254)
In "tabela_objecto area", when I fill, indice_sequencial_po integer (foreign key), I want the table to be able to automatically get the data from the relational table "tabela_is_po", based on the number that is filled in indice_sequencial_po.
I've tried the trigger function but it never worked well.
tabela_objecto_area code:
CREATE TABLE pdm2019.tabela_objecto_area
(
identificador integer NOT NULL DEFAULT nextval('pdm2019.tabela_objecto_area_identificador_seq'::regclass),
dtcc character varying(4) NOT NULL DEFAULT '0101',
planta_po boolean NOT NULL,
indice_sequencial_po integer,
tema_po character varying(254),
subtema_po character varying(254),
designacao_po character varying(254),
CONSTRAINT tabela_objecto_area_pkey PRIMARY KEY (identificador)
)
WITH (
OIDS=FALSE
);
Trigger function:
CREATE OR REPLACE FUNCTION tema_e_subtema_automatico() RETURNS TRIGGER AS $tema_e_subtema_automatico$
BEGIN
IF NEW.pdm2019.tabela_objecto_area.indice_sequencial_po IS NOT NULL THEN
INSERT INTO pdm2019.tabela_objecto_area(tema_po,subtema_po,designacao_po) SELECT tema,subtema,designacao FROM pdm2019.tabela_is_po WHERE indice_sequencial_po = NEW.pdm2019.tabela_objecto_area.indice_sequencial_po;
END IF;
END;
$tema_e_subtema_automatico$ LANGUAGE plpgsql;
CREATE TRIGGER tema_e_subtema_automatico AFTER INSERT OR UPDATE ON pdm2019.tabela_objecto_area
FOR EACH ROW EXECUTE PROCEDURE tema_e_subtema_automatico();
You would probably be better off using a view. See https://en.wikipedia.org/wiki/Relational_model for an overview, basically each item should appear only once in the database.
For data entry, it's often easier to use a framework like django (https://www.djangoproject.com/) to create the structure of your tables and input data, which you can then view in qgis.
CREATE TABLE pdm2019.tabela_objecto_area
(
identificador integer NOT NULL DEFAULT nextval('pdm2019.tabela_objecto_area_identificador_seq'::regclass),
dtcc character varying(4) NOT NULL DEFAULT '0101',
planta_po boolean NOT NULL,
indice_sequencial_po integer,
-- tema_po character varying(254), -- use a view to populate these
-- subtema_po character varying(254), -- use a view to populate these
-- designacao_po character varying(254), -- use a view to populate these
CONSTRAINT tabela_objecto_area_pkey PRIMARY KEY (identificador)
)
WITH (
OIDS=FALSE
);
CREATE VIEW v_tabela_objecto_area as (
select a.*, b.tema_po, b.subtema_po, b.designacao_po
from tabela_objecto_area a, tabela_is_po b
where a.indice_sequencial_po = b.indice_sequencial_po);

Default constraint not being enforced

Given the following table definition:
CREATE TABLE ControlledSubstances.NationalDrugCode
(
NationalDrugCodeID INT NOT NULL
,NationalDrugCode VARCHAR(20) NOT NULL
,Product VARCHAR(100)
,Ingredient VARCHAR(500)
,ClassID VARCHAR(50)
,Class VARCHAR(50)
,DrugEnforcementAgencyClassID VARCHAR(50)
,DrugEnforcementAgencyClass VARCHAR(50)
,GenericDrug VARCHAR(50)
,Form VARCHAR(50)
,Drug VARCHAR(50)
,StrengthPerUnit NUMERIC(6,2)
,UnitOfMeasure VARCHAR(50)
,ConversionFactor NUMERIC(4,2)
,LongOrShortActing VARCHAR(50)
,IsPreventionForStates BIT NOT NULL
)
;
ALTER TABLE ControlledSubstances.NationalDrugCode
ADD CONSTRAINT PK_ControlledSubstances_NationalDrugCode PRIMARY KEY (NationalDrugCodeID)
,CONSTRAINT DF_ControlledSubstances_NationalDrugCode_IsPreventionForStates DEFAULT 0 FOR IsPreventionForStates
;
CREATE UNIQUE INDEX UQ_ControlledSubstances_NationalDrugCode_NationalDrugCode ON ControlledSubstances.NationalDrugCode (NationalDrugCode);
Why would I be receiving an error on insert for the column I defined as NOT NULL and created a default constraint of 0? I know I can handle the logic in the insert statement to not pass in NULL values, but I use this logic in multiple tables and have never gotten an error before. The error I receive is:
Cannot insert the value NULL into column 'IsPreventionForStates', table 'Staging.ControlledSubstances.NationalDrugCode'; column does not allow nulls. INSERT fails.
This will happen if you explicitly provide NULL as its value. The default constraint only kicks in when you don't supply a value at all, or when you use the DEFAULT keyword:
For example, if NationalDrugCodeID and IsPreventionForStates were your only two columns in the table (for illustration), this will fail:
INSERT INTO NationalDrugCode(NationalDrugCodeID, IsPreventionForStates) VALUES (5, NULL);
But either of these would work:
INSERT INTO NationalDrugCode(NationalDrugCodeID) VALUES (5);
INSERT INTO NationalDrugCode(NationalDrugCodeID, IsPreventionForStates) VALUES (5, DEFAULT);
In the edge case where you need ALL columns to have default values inserted, you can use:
INSERT INTO NationalDrugCode DEFAULT VALUES;

How to create a pageable function in PostgreSQL

I have two tables: event and location
CREATE TABLE location
(
location_id bigint NOT NULL,
version bigint NOT NULL,
active boolean NOT NULL,
created timestamp without time zone NOT NULL,
latitude double precision NOT NULL,
longitude double precision NOT NULL,
updated timestamp without time zone,
CONSTRAINT location_pkey PRIMARY KEY (location_id)
)
CREATE TABLE event
(
event_id bigint NOT NULL,
version bigint NOT NULL,
active boolean NOT NULL,
created timestamp without time zone NOT NULL,
end_date date,
entry_fee numeric(19,2),
location_id bigint NOT NULL,
organizer_id bigint NOT NULL,
start_date date NOT NULL,
timetable_id bigint,
updated timestamp without time zone,
CONSTRAINT event_pkey PRIMARY KEY (event_id),
CONSTRAINT fk_organizer FOREIGN KEY (organizer_id)
REFERENCES "user" (user_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_timetable FOREIGN KEY (timetable_id)
REFERENCES timetable (timetable_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_location FOREIGN KEY (location_id)
REFERENCES location (location_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Other tables are of lesser to no importance so they will not be shown (unless explicitly asked).
And for those tables, using cube and earthdistance pgsql extensions I've created the following function for finding all event_ids within a certain radius of a certain point.
CREATE OR REPLACE FUNCTION eventidswithinradius(
lat double precision,
lng double precision,
radius double precision)
RETURNS SETOF bigint AS
$BODY$
BEGIN
RETURN QUERY SELECT event.event_id
FROM event
INNER JOIN location ON location.location_id = event.location_id
WHERE earth_box( ll_to_earth(lat, lng), radius) #> ll_to_earth(location.latitude, location.longitude);
END;
$BODY$
And this works as expected. Now I wish to make it pageable, and am stuck on how to get all the necessary values (the table with paged contents and total count).
So far I've created this:
CREATE OR REPLACE FUNCTION pagedeventidswithinradius(
IN lat double precision,
IN lng double precision,
IN radius double precision,
IN page_size integer,
IN page_offset integer)
RETURNS TABLE( total_size integer , event_id bigint ) AS
$BODY$
DECLARE total integer;
BEGIN
SELECT COUNT(location.*) INTO total FROM location WHERE earth_box( ll_to_earth(lat, lng), radius) #> ll_to_earth(location.latitude, location.longitude);
RETURN QUERY SELECT total, event.event_id as event_id
FROM event
INNER JOIN location ON location.location_id = event.location_id
WHERE earth_box( ll_to_earth(lat, lng), radius) #> ll_to_earth(location.latitude, location.longitude)
ORDER BY event_id
LIMIT page_size OFFSET page_offset;
END;
$BODY$
Here count is called only once and stored in a variable since I assumed that if I placed COUNT into the return query itself it would be called for each row.
This kind of works, but it is difficult to parse on the back-end since the result is in the form of (count, event_id), also count is needlessly repeated over all result rows. I was hoping I could simply add total as an OUT param and have the function return the table and fill the OUT variable with total count, however it seems this is not allowed. I can always have the count be a separate function but I was wondering if there is a better way to approach this issue?
No, there isn't really a better option. You want two different types of quantities so you need two queries. You can improve upon your function, however:
CREATE FUNCTION eventidswithinradius(lat float8, long float8, radius float8) RETURNS SETOF bigint AS $BODY$
SELECT event.event_id
FROM event
JOIN location l USING (location_id)
WHERE earth_box(ll_to_earth(lat, lng), radius) #> ll_to_earth(l.latitude, l.longitude);
$BODY$ LANGUAGE sql STRICT;
As a LANGUAGE sql function it is more efficient than as a PL/pgSQL function, plus you can do your paging on the outside:
SELECT *
FROM eventidswithinradius(121.056, 14.582, 3000)
LIMIT 15 OFFSET 1;
Internally the query planner will resolve the function call to its underlying query and apply the paging directly to that level.
Get the total with the obvious:
SELECT count(id)
FROM eventidswithinradius(121.056, 14.582, 3000);

Postgres before insert trigger using sequence from another table

Using Postgres, what I would like to achieve is to be able to have many different instrument types, with corresponding [TYPE].instrument tables, which all have a unique ID in the table, but also reference a unique ID in the instrument.master table. I have the following:
create schema instrument
CREATE TABLE instrument.type (
id smallserial NOT NULL,
name text not null,
code text not null,
CONSTRAINT pk_instrument_type PRIMARY KEY (id)
);
ALTER TABLE instrument.type ADD CONSTRAINT unq_instrument_type_code UNIQUE(code);
ALTER TABLE instrument.type ADD CONSTRAINT unq_instrument_type_name UNIQUE(name);
insert into instrument.type (name, code) values ('futures', 'f');
CREATE TABLE instrument.master (
id serial NOT NULL,
type smallint not null references instrument.type (id),
timestamp timestamp with time zone not null,
CONSTRAINT pk_instrument_master PRIMARY KEY (id)
);
CREATE TABLE futures.definition (
id smallserial NOT NULL,
code text not null,
CONSTRAINT pk_futures_definition PRIMARY KEY (id)
);
ALTER TABLE futures.definition ADD CONSTRAINT unq_futures_definition_code UNIQUE(code);
insert into futures.definition (code) values ('ED');
CREATE TABLE futures.instrument (
id smallserial NOT NULL,
master serial not null references instrument.master (id),
definition smallint not null references futures.definition (id),
month smallint not null,
year smallint not null,
CONSTRAINT pk_futures_instrument PRIMARY KEY (id),
check (month >= 1),
check (month <= 12),
check (year >= 1900)
);
ALTER TABLE futures.instrument ADD CONSTRAINT unq_futures_instrument UNIQUE(definition, month, year);
CREATE OR REPLACE FUNCTION trigger_master_futures()
RETURNS trigger AS
$BODY$
BEGIN
insert into instrument.master (type, timestamp)
select id, current_timestamp from instrument.type where code = 'f';
NEW.master := currval('instrument.master_id_seq');
RETURN NEW;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
create trigger trg_futures_instrument before insert on futures.instrument
for each row
execute procedure trigger_master_futures();
I then test with:
insert into futures.instrument (definition, month, year)
select id, 3, 2015 from futures.definition where code = 'ED';
Everything works almost as I would like it to. The only issue is that somehow, instrument.master.id ends up being one more than futures.instrument.master. I am not sure what I need to do to achieve the behavior I want, which is that whenever an entry is inserted into futures.instrument, an entry should be inserted into instrument.master, and the id entry of the latter should be inserted into the master entry of the former. I actually think it should have failed since the foreign key relationship is violated somehow.
As it turns out, everything was correct. The issue was that in futures.instrument, the type of the master column is serial, and it should have been int.