tsvector as a column not updated automatically in postgresql - postgresql

Hi postgresql experts,
I have already searched my problem, some of them seem relevant but actually not the same. I apologize first if there is a dup.
I am doing a text search with postgresql. Since I cant change the already defined sql files (limitations from the project), all the updates or changes need to be done in a new file to alter the previous definition. In order to make it clear, I will list all the code I have which are related to the table which am doing full text search. I will list the codes in their execute order.
My problem is, whenever I insert a row to mytable, the search_vector is not updated automatically. Only when I execute the 5th sql after the insertion, the search_vector is updated according to its definition. Wired thing is that, even though I change the code in 3th or 4th sql to change the search_vector definition, eg. to make
search_vector = to_tsvector('english', name);
the insertion after that is still not updating their search_vector, still keeps the same as the previous definition.
1. created a table:
CREATE TABLE mytable {
id VARCHAR(20) PRIMARY KEY,
name VARCHAR NOT NULL,
label VARCHAR,
color VARCHAR NOT NULL
};
2. update the search_vector as a column and also use trigger and index:
BEGIN;
DROP TRIGGER IF EXISTS mytable_search_vector_trigger ON mytable;
DROP INDEX IF EXISTS search_vector_index;
ALTER TABLE mytable DROP COLUMN IF EXISTS search_vector;
ALTER TABLE mytable ADD COLUMN search_vector tsvector;
UPDATE mytable
SET search_vector = to_tsvector('english', id || ' ' || name || ' ' ||
coalesce(label, ''));
CREATE INDEX search_vector_index ON opportunity USING gin(search_vector);
CREATE TRIGGER mytable_search_vector_trigger BEFORE INSERT OR UPDATE ON
mytable
FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(search_vector,
'pg_catalog.english', id, name, label);
ANALYZE mytable;
COMMIT;
3. add another column and update the search_vector and the trigger:
BEGIN;
ALTER TABLE mytable ADD COLUMN product_number CHAR(6);
DROP TRIGGER IF EXISTS mytable_search_vector_trigger ON mytable;
DROP INDEX IF EXISTS search_vector_index;
ALTER TABLE mytable DROP COLUMN IF EXISTS search_vector;
ALTER TABLE mytable ADD COLUMN search_vector tsvector;
UPDATE mytable
SET search_vector = to_tsvector('english', id || ' ' || name || ' ' ||
product_number || ' ' || coalesce(label, ''));
CREATE INDEX search_vector_index ON mytable USING gin(search_vector);
CREATE TRIGGER mytable_search_vector_trigger
BEFORE INSERT OR UPDATE ON mytable
FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(search_vector,
'pg_catalog.english', id, name, product_number, label);
ANALYZE mytable;
COMMIT;
4. change the primary key type:
BEGIN;
ALTER TABLE mytable
ALTER COLUMN id SET DATA TYPE CHAR(18);
COMMIT;
5. update the search_vector:
BEGIN;
DROP TRIGGER IF EXISTS mytable_search_vector_trigger ON mytable;
DROP INDEX IF EXISTS search_vector_index;
CREATE TRIGGER mytable_search_vector_trigger BEFORE INSERT OR UPDATE
ON mytable FOR EACH ROW EXECUTE PROCEDURE
tsvector_update_trigger(search_vector, 'pg_catalog.english', id, name,
product_number, label);
UPDATE mytable
SET search_vector = to_tsvector('english', substring(id from 1 for 15) || ' '
|| id || ' ' || name || ' ' || coalesce(product_number, '') ||
coalesce(application_name, ''))
WHERE search_vector IS NOT NULL; -- tried delete this, but not working, also tried to change the 'english' to 'pg_catalog.english', not working as well.
CREATE INDEX search_vector_idx ON mytable USING gin(search_vector);
ANALYZE mytable;
COMMIT;
Thanks in advance for any advice.

Related

I'm getting column "my_column" contains null values' when adding a composite primary key

Is it not supposed to delete null values before altering the table? I'm confused...
My query looks roughly like this:
BEGIN;
DELETE FROM my_table
WHERE my_column IS NULL;
ALTER TABLE my_table DROP CONSTRAINT my_table_pk;
ALTER TABLE my_table ADD PRIMARY KEY (id, my_column);
-- this is to repopulate the data afterwards
INSERT INTO my_table (name, other_table_id, my_column)
SELECT
ya.name,
ot.id,
my_column
FROM other_table ot
LEFT JOIN yet_another ya
ON ya.id = ot."fileId"
WHERE NOT EXISTS (
SELECT
1
FROM my_table mt
WHERE ot.id = mt.other_table_id AND ot.my_column = mt.my_column
) AND my_column IS NOT NULL;
COMMIT;
sorry for naming
There are two possible explanations:
A concurrent session inserted a new row with a NULL value between the start of the DELETE and the start of ALTER TABLE.
To avoid that, lock the table in SHARE mode before you DELETE.
There is a row where id has a NULL value.

How to remove Firebird's triggers created automatically [duplicate]

I have a Firebird table like this:
CREATE TABLE events (
event VARCHAR(6) NOT NULL
CHECK (event IN ('deploy', 'revert', 'fail')),
change_id CHAR(40) NOT NULL,
change VARCHAR(512) NOT NULL
);
Now I need to add another value to the IN() list in the CHECK constraint. How do I do that?
Things I've tried so far:
Updating the value in RDB$TRIGGERS.RDB$TRIGGER_SOURCE:
UPDATE RDB$TRIGGERS
SET RDB$TRIGGER_SOURCE = 'CHECK (event IN (''deploy'', ''revert'', ''fail'', ''merge''))'
WHERE RDB$TRIGGER_SOURCE = 'CHECK (event IN (''deploy'', ''revert'', ''fail''))';
Does not seem to work, as the trigger is compiled in RDB$TRIGGERS.RDB$TRIGGER_BLR.
Creating a new table with a new check, copying the data over, dropping the old table and renaming the new table. However, it seems that one cannot rename a Firebird table, so I can't make the new table have the same name as the old one.
I suspect updating RDB$TRIGGERS is the way to go (idk!), if only I could get Firebird to recompile the code. But maybe there's a better way?
You need to drop and the re-create the check constraint.
As you didn't specify a name for your constraint, Firebird created one, so you first need to find that name:
select trim(cc.rdb$constraint_name), trg.rdb$trigger_source
from rdb$relation_constraints rc
join rdb$check_constraints cc on rc.rdb$constraint_name = cc.rdb$constraint_name
join rdb$triggers trg on cc.rdb$trigger_name = trg.rdb$trigger_name
where rc.rdb$relation_name = 'EVENTS'
and rc.rdb$constraint_type = 'CHECK'
and trg.rdb$trigger_type = 1;
I just added the trigger source for informational reasons.
Once you have the name, you can drop it, e.g.
alter table events drop constraint integ_27;
and then add the new constraint:
alter table events
add constraint check_event_type
CHECK (event IN ('deploy', 'revert', 'fail', 'merge'));
In the future you don't need to look for the constraint name because you already it.
Here's how to do it dynamically:
SET AUTOddl OFF;
SET TERM ^;
EXECUTE BLOCK AS
DECLARE trig VARCHAR(64);
BEGIN
SELECT TRIM(cc.rdb$constraint_name)
FROM rdb$relation_constraints rc
JOIN rdb$check_constraints cc ON rc.rdb$constraint_name = cc.rdb$constraint_name
JOIN rdb$triggers trg ON cc.rdb$trigger_name = trg.rdb$trigger_name
WHERE rc.rdb$relation_name = 'EVENTS'
AND rc.rdb$constraint_type = 'CHECK'
AND trg.rdb$trigger_type = 1
INTO trig;
EXECUTE STATEMENT 'ALTER TABLE EVENTS DROP CONSTRAINT ' || trig;
END^
SET TERM ;^
COMMIT;
ALTER TABLE events ADD CONSTRAINT check_event_type CHECK (
event IN ('deploy', 'revert', 'fail', 'merge')
);
COMMIT;
I had to disable AUTOddl and put in explicit commits or else I got a deadlock on the ALTER TABLE ADD CONSTRAINT statement.
Here's how to do it dynamically:
EXECUTE BLOCK RETURNS (STMT VARCHAR(1000)) AS
BEGIN
SELECT TRIM(R.RDB$CONSTRAINT_NAME)
FROM RDB$RELATION_CONSTRAINTS R
WHERE R.RDB$RELATION_NAME = 'TABLE_NAME'
AND UPPER(R.RDB$CONSTRAINT_TYPE) = UPPER('PRIMARY KEY')
INTO :STMT;
IF (:STMT IS NOT NULL) THEN
BEGIN
EXECUTE STATEMENT 'ALTER TABLE TABLE_NAME DROP CONSTRAINT ' || :STMT || ';';
EXECUTE STATEMENT 'ALTER TABLE TABLE_NAME ADD CONSTRAINT ' || :STMT || ' PRIMARY KEY (FIELD1, FIELD2, FIELD3);';
END
ELSE
BEGIN
EXECUTE STATEMENT 'ALTER TABLE FIELD1 ADD CONSTRAINT PK_PRIMARY_NAME PRIMARY KEY (FIELD1, FIELD2, FIELD3);';
END
END;

Make duplicate row in Postgresql

I am writing migration script to migrate database. I have to duplicate the row by incrementing primary key considering that different database can have n number of different columns in the table. I can't write each and every column in query. If i simply just copy the row then, I am getting duplicate key error.
Query: INSERT INTO table_name SELECT * FROM table_name WHERE id=255;
ERROR: duplicate key value violates unique constraint "table_name_pkey"
DETAIL: Key (id)=(255) already exist
Here, It's good that I don't have to mention all column names. I can select all columns by giving *. But, same time I am also getting duplicate key error.
What's the solution of this problem? Any help would be appreciated. Thanks in advance.
If you are willing to type all column names, you may write
INSERT INTO table_name (
pri_key
,col2
,col3
)
SELECT (
SELECT MAX(pri_key) + 1
FROM table_name
)
,col2
,col3
FROM table_name
WHERE id = 255;
Other option (without typing all columns , but you know the primary key ) is to CREATE a temp table, update it and re-insert within a transaction.
BEGIN;
CREATE TEMP TABLE temp_tab ON COMMIT DROP AS SELECT * FROM table_name WHERE id=255;
UPDATE temp_tab SET pri_key_col = ( select MAX(pri_key_col) + 1 FROM table_name );
INSERT INTO table_name select * FROM temp_tab;
COMMIT;
This is just a DO block but you could create a function that takes things like the table name etc as parameters.
Setup:
CREATE TABLE public.t1 (a TEXT, b TEXT, c TEXT, id SERIAL PRIMARY KEY, e TEXT, f TEXT);
INSERT INTO public.t1 (e) VALUES ('x'), ('y'), ('z');
Code to duplicate values without the primary key column:
DO $$
DECLARE
_table_schema TEXT := 'public';
_table_name TEXT := 't1';
_pk_column_name TEXT := 'id';
_columns TEXT;
BEGIN
SELECT STRING_AGG(column_name, ',')
INTO _columns
FROM information_schema.columns
WHERE table_name = _table_name
AND table_schema = _table_schema
AND column_name <> _pk_column_name;
EXECUTE FORMAT('INSERT INTO %1$s.%2$s (%3$s) SELECT %3$s FROM %1$s.%2$s', _table_schema, _table_name, _columns);
END $$
The query it creates and runs is: INSERT INTO public.t1 (a,b,c,e,f) SELECT a,b,c,e,f FROM public.t1. It's selected all the columns apart from the PK one. You could put this code in a function and use it for any table you wanted, or just use it like this and edit it for whatever table.

IF Exists doesn't seem to work for a Table Drop if already exists

Was getting this error each and every time tried to execute a DROP Table if exists
Step 1: Created a Table
CREATE TABLE Work_Tables.dbo.Drop_Table_Test (RowID INT IDENTITY(1,1), Data VARCHAR(50))
INSERT INTO Work_Tables.dbo.Drop_Table_Test
SELECT 'Test' UNION
SELECT 'Test1' UNION
SELECT 'Test2' UNION
SELECT 'Test3'
Step 2: Wrote a IF Exists block to check if the Table exists.
IF EXISTS (SELECT 1 FROM Work_Tables.dbo.SysObjects WHERE NAME LIKE 'Drop_Table_Test' AND XType = 'U')
BEGIN
PRINT 'IN'
DROP TABLE Work_Tables.dbo.Drop_Table_Test
END
CREATE TABLE Work_Tables.dbo.Drop_Table_Test (RowID INT IDENTITY(1,1), Data VARCHAR(50), NAME VARCHAR(20), PreCheck INT)
INSERT INTO Work_Tables.dbo.Drop_Table_Test (Data, Name, PreCheck)
SELECT 'Test','SRK',1 UNION
SELECT 'Test1','Daya',2 UNION
SELECT 'Test2','Dinesh',3 UNION
SELECT 'Test3','Suresh',4
On running the Step 2 Code its obvious the Table has to be Dropped and recreated with the same name but it didn't even enter the Begin End block.
I feel that its because have added few more columns in the second try, but still not clear why it should have problems as we are to DROP the table.
You can not drop and create the same table in the same batch in SQL Server.
Break your code up into separate batches so the table can be dropped before you try and recreate it. Add GO after END in your BEGIN / END statement.
IF EXISTS (SELECT 1 FROM Work_Tables.dbo.SysObjects WHERE NAME LIKE 'Drop_Table_Test' AND XType = 'U')
BEGIN
PRINT 'IN'
DROP TABLE Work_Tables.dbo.Drop_Table_Test
END
GO --Add this...
....
Straight from Microsoft's Documentation:
DROP TABLE and CREATE TABLE should not be executed on the same table in the same batch. Otherwise an unexpected error may occur.
You can try to use this syntax:
IF OBJECT_ID('dbo.Drop_Table_Test', 'U') IS NOT NULL
DROP TABLE dbo.Drop_Table_Test;
IF EXISTS will drop the table only when your table Drop_Table_Test does not contain any row. In case if it contains the data then it will not drop the table.

Update column of a inserted row with with its generated id in a single query

Say I have a table, created as follows:
CREATE TABLE test_table (id serial, unique_id varchar(50) primary key, name varchar(50));
test_table
----------
id | unique_id | name
In that table, I would like to update the unique_id field with the newly inserted id concatenated with the inserted name in a single go.
Usually this is accomplished by two queries. (PHP way)
$q = "INSERT INTO table (unique_id,name) values ('uid','abc') returning id||name as unique_id;";
$r = pg_query($dbconn,$q);
$row = pg_fetch_array($r);
$q1 = "UPDATE test_table set unique_id =".$row['unique_id']." where unique_id='uid'";
$r1 = pg_query($dbconn,$q1);
Is there any way to do the above in a single query?
You can have several options here, you could create a AFTER trigger which uses the generated ID for an direct update of the same row:
CREATE TRIGGER test_table_insert ON AFTER INSERT ON test_table FOR EACH ROW EXECUTE PROCEDURE test_table_insert();
And in your function you update the value:
CREATE FUNCTION test_table_insert() RETURNS TRIGGER AS $$
BEGIN
UPDATE test_table SET uniqid = NEW.id::text || NEW.name WHERE id = NEW.id;
END;
$$ LANGUAGE plpgsql;
You need to add the function before the trigger.
An other option would be to do it directly in the insert:
INSERT INTO table (id, unique_id, name) values (nextval('test_table_id_seq'), 'abc', currval('test_table_id_seq')::text || 'abc') returning id;
But as a_horse_with_no_name pointed out, I think you may have a problem in your database design.