Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tZ6tU-0015k3-4O for pgsql-general@arkaria.postgresql.org; Sat, 18 Jan 2025 11:21:36 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1tZ6tS-00HDqr-Jl for pgsql-general@arkaria.postgresql.org; Sat, 18 Jan 2025 11:21:35 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tZ6tS-00HDqi-6e for pgsql-general@lists.postgresql.org; Sat, 18 Jan 2025 11:21:34 +0000 Received: from mail-ed1-x532.google.com ([2a00:1450:4864:20::532]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1tZ6tP-000BUk-2F for pgsql-general@postgresql.org; Sat, 18 Jan 2025 11:21:34 +0000 Received: by mail-ed1-x532.google.com with SMTP id 4fb4d7f45d1cf-5d9f06f8cf2so5590714a12.3 for ; Sat, 18 Jan 2025 03:21:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1737199290; x=1737804090; darn=postgresql.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=yWEytDlTTgdBV6rxX8YSMSQ79P8cxpFaTypHQrK9YC4=; b=jivL4roCqlmwFA6c5YPcxBK2zF0BIAdU1VciaanhVh1PG/cZVua1zJBEzdBOgZpS4E GzlHgRdFn0lm/ZdqjmDldsh6Vz5ifA2hb5kg6SHlj74k39grOUGNHL2yN0Fv8Dg6fgrR vDzg89yx8X4nrlgSwj+T71hUMyjNfRbX0U4+VuCiS6GZbRx9YslLDgKkAGHa7UE2E8LW OyclRJokuYY7iAvCz4grWOdI7C7K64sR1/RxMkyqP6c3S7LhZy1qsjWl2ThxqdzoDj+0 D6bWb0/BgCF1Sy5/fIpeVg8k20yYojCLbAbTVe5cKg/we9dUgg63Dc+NV5B7TS07wojI JwcA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1737199290; x=1737804090; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=yWEytDlTTgdBV6rxX8YSMSQ79P8cxpFaTypHQrK9YC4=; b=rddkn9H/UR47bcdKDGKtrN9PihY1gZMesxeBLc0AZjPY1Vhg2trKTQUGHYePnhd0Pw DYXq9i16kPN1n1ZKCx/3O96A1gaGWqTxKhGHNwoLbzJzz0FwkQNQ/rkK3mgVHfpWQ98H BbIhRcRlVa5ty2NkpdT+mNhn4DzNxeivI3WOiHW1afIZ3jq6fMUh+1IxvYDW+TIvMv5M wrgct9pFivEHqQtmHQdseJu7Z4AB1dj0U7uGVOMXH6THt48uRZxg4Mp957COhGhO/M/9 cSAfkeZcazYnWDsEEe/YPygw/edsKD1aDK6DAqoD6zOquon2//nZaZpJiZWz+/b14RqB 1Ykg== X-Gm-Message-State: AOJu0YxfYqZBDrT4rBJ7VUANERXvk1H6j3afkNRY1pPrhj+Z3KMdXapQ oKHT5WRlrt6fUgHodJc72MoX+ibkxR9mL2UJiqp4KiHI5v9sJDvfTYBD1xpn64YZ0FEZlK5eruC 1e9XA8YkTn7ZYx9MGps22unylMF3ZZRXH X-Gm-Gg: ASbGncs53dqPOOEt7eLjHanaLHqh3dn863pnqsDlz6joZVMKltg//h5QbDsU+n5jzsv gN1bH8hPaPhf9CP2GO/NkeCvWPswhF4Uo+Ak26B8rknVlzeGBc2g= X-Google-Smtp-Source: AGHT+IEQlJFbYGP0eGamt4MGFyhlrHsiobVlIFL2XyEatGQ2R0ycw6JBs1THJSIPxJ2RZO9WZEDAWXF0Wfc2h1IFk14= X-Received: by 2002:a05:6402:210c:b0:5d9:cde9:2b7f with SMTP id 4fb4d7f45d1cf-5db7d3550d5mr5202073a12.23.1737199289776; Sat, 18 Jan 2025 03:21:29 -0800 (PST) MIME-Version: 1.0 From: Alexander Farber Date: Sat, 18 Jan 2025 12:21:18 +0100 X-Gm-Features: AbW1kvbYilxB0i2a9rznf1VPtBicYrB-lN1HwSpyJ4GSwPTKP2iKK4pvQxFzaNw Message-ID: Subject: Why is DELETE FROM vehicle_data WHERE NOW, > expires_at, failing in my stored function? To: pgsql-general Content-Type: multipart/alternative; boundary="000000000000924312062bf93af1" List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --000000000000924312062bf93af1 Content-Type: text/plain; charset="UTF-8" Hi fellow PostgreSQL users, I have prepared a DB Fiddle https://dbfiddle.uk/BCXD_Bo2 for my question and I will also show my complete SQL code below - I am trying to store data in a vehicle_data table and each data record has an expires_at column: -- Create table for customer IDs CREATE TABLE customer_ids ( id INTEGER PRIMARY KEY CHECK (id > 0), label TEXT NOT NULL CHECK (label ~ '\S') ); -- Insert valid customer IDs INSERT INTO customer_ids (id, label) VALUES (1, 'Customer 1'), (2, 'Customer 2'), (3, 'Customer 3'), (4, 'Customer 4'), (5, 'Customer 5'); -- Create table for use case IDs CREATE TABLE use_case_ids ( id INTEGER PRIMARY KEY CHECK (id > 0), label TEXT NOT NULL CHECK (label ~ '\S') ); -- Insert valid use case IDs INSERT INTO use_case_ids (id, label) VALUES (1, 'Use Case 1'), (2, 'Use Case 2'), (3, 'Use Case 3'), (4, 'Use Case 4'), (5, 'Use Case 5'); -- Create table for uploaded vehicle data CREATE TABLE vehicle_data ( -- the triple is counted when comparing against node_limit osm_node_id BIGINT NOT NULL CHECK (osm_node_id > 0), customer_id INTEGER NOT NULL, use_case_id INTEGER NOT NULL, container_id BIGINT NOT NULL CHECK (container_id > 0), expires_at TIMESTAMPTZ NOT NULL, FOREIGN KEY (customer_id) REFERENCES customer_ids(id), FOREIGN KEY (use_case_id) REFERENCES use_case_ids(id), id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY ); -- Add a triple-column index to improve search performance CREATE INDEX idx_vehicle_data ON vehicle_data (osm_node_id, customer_id, use_case_id); To fill the vehicle_data table with data I have defined a simple stored procedure: CREATE OR REPLACE FUNCTION store_vehicle_data( _container_id BIGINT, _osm_node_ids BIGINT[], _customer_id INTEGER, _use_case_id INTEGER, _node_limit INTEGER, _retention_time INTERVAL ) RETURNS BOOLEAN AS $$ DECLARE _osm_node_id BIGINT; _row_count INTEGER; _should_send_pull_container BOOLEAN := TRUE; BEGIN -- Delete records with expired retention time - NOT WORKING DELETE FROM vehicle_data WHERE NOW() > expires_at; -- Insert new records FOREACH _osm_node_id IN ARRAY _osm_node_ids LOOP BEGIN INSERT INTO vehicle_data ( osm_node_id, customer_id, use_case_id, container_id, expires_at ) VALUES ( _osm_node_id, _customer_id, _use_case_id, _container_id, NOW() + _retention_time ); EXCEPTION WHEN foreign_key_violation THEN RAISE EXCEPTION 'Invalid customer_id % or use_case_id % for osm_node_id % container_id: %', _customer_id, _use_case_id, _osm_node_id, _container_id; END; -- Check if the number of records exceeds the node limit SELECT COUNT(*) INTO STRICT _row_count FROM vehicle_data WHERE osm_node_id = _osm_node_id AND customer_id = _customer_id AND use_case_id = _use_case_id; -- There is enough up-to-date vehicle data for this triple, -- so tell the vehicle not to send any PULL containers IF _row_count > _node_limit THEN _should_send_pull_container := FALSE; END IF; END LOOP; RETURN _should_send_pull_container; END; $$ LANGUAGE plpgsql; Finally, I have prepared a smoke test for my code and run it twice: CREATE OR REPLACE FUNCTION test_store_vehicle_data( num_runs INTEGER, OUT count_true INTEGER, OUT count_false INTEGER ) RETURNS RECORD AS $$ DECLARE test_result BOOLEAN; BEGIN count_true := 0; count_false := 0; FOR i IN 1..num_runs LOOP -- Store OSM node ids (2 are same, 2 are changing) -- with node limit 10 and retention time 5 seconds test_result := store_vehicle_data( 100 + i, ARRAY[1000, 2000, 3000 + i, 4000 + i], 1, 5, 10, INTERVAL '5 seconds' ); IF test_result THEN count_true := count_true + 1; ELSE count_false := count_false + 1; END IF; END LOOP; RETURN; END $$ LANGUAGE plpgsql; -- Run 2 smoke tests DO $$ DECLARE test_result RECORD; BEGIN -- Test 1: store 15x4 records expiring in 5 seconds SELECT * INTO test_result FROM test_store_vehicle_data(15); IF test_result.count_true != 10 OR test_result.count_false != 5 THEN RAISE EXCEPTION 'Test 1 failed: expected 10 TRUE, 5 FALSE'; END IF; -- Sleep for 10 seconds, so that all records in vehicle_data expire PERFORM pg_sleep(10); -- Test 2: store 15x4 records expiring in 5 seconds SELECT * INTO test_result FROM test_store_vehicle_data(15); IF test_result.count_true != 10 OR test_result.count_false != 5 THEN RAISE EXCEPTION 'Test 2 failed: expected 10 TRUE, 5 FALSE'; END IF; END $$; -- Print all records in the vehicle_data table SELECT expires_at < NOW() AS expired, * FROM vehicle_data ORDER BY container_id; My problem is that the DELETE FROM vehicle_data WHERE NOW() > expires_at; statement in my store_vehicle_data() function does not see to delete anything. And then the SELECT expires_at < NOW() AS expired, * FROM vehicle_data ORDER BY container_id; prints the records in the table and yes, they are all expired there. I run the above SQL code (creating tables, creating functions, running smoke test) in a Dockerfile based on the official Postgres Dockerfile and the smoke test (the Test 2) just always fails. Then I connect using psql to my docker container and run the DELETE command at the psql prompt and voila - it works there as expected and deletes all the expired records. Thank you for any ideas and I have also asked this question at https://stackoverflow.com/q/79367012/165071 Best regards Alex --000000000000924312062bf93af1 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
=C2=A0 =C2=A0 id INTEGER PRIMARY KEY CHECK (id > 0),
=C2=A0 =C2=A0 label TEXT NOT NULL CHECK (label ~ '\S')
);

-- Insert valid use case IDs
I= NSERT INTO use_case_ids (id, label) VALUES
(1, 'Use Case 1= 9;),
(2, 'Use Case 2'),
(3, 'Use Case 3'= ;),
(4, 'Use Case 4'),
(5, 'Use Case 5'= );

-- Create table for uploaded vehicle data
=
CREATE TABLE vehicle_data (
=C2=A0 =C2=A0 -- the triple is c= ounted when comparing against node_limit
=C2=A0 =C2=A0 osm_node_i= d BIGINT NOT NULL CHECK (osm_node_id > 0),
=C2=A0 =C2=A0 custo= mer_id INTEGER NOT NULL,
=C2=A0 =C2=A0 use_case_id INTEGER NOT NU= LL,

=C2=A0 =C2=A0 container_id BIGINT NOT NULL CHE= CK (container_id > 0),
=C2=A0 =C2=A0 expires_at TIMESTAMPTZ NO= T NULL,

=C2=A0 =C2=A0 FOREIGN KEY (customer_id) RE= FERENCES customer_ids(id),
=C2=A0 =C2=A0 FOREIGN KEY (use_case_id= ) REFERENCES use_case_ids(id),
=C2=A0 =C2=A0 id BIGINT GENERATED = ALWAYS AS IDENTITY PRIMARY KEY
);

-- Add= a triple-column index to improve search performance
CREATE INDEX= idx_vehicle_data ON vehicle_data (osm_node_id, customer_id, use_case_id);<= /div>

To fill the vehicle_data table with data I have de= fined a simple stored procedure:

CREATE OR REPLACE= FUNCTION store_vehicle_data(
=C2=A0 =C2=A0 _container_id BIGINT,=
=C2=A0 =C2=A0 _osm_node_ids BIGINT[],
=C2=A0 =C2=A0 _c= ustomer_id INTEGER,
=C2=A0 =C2=A0 _use_case_id INTEGER,
=C2=A0 =C2=A0 _node_limit INTEGER,
=C2=A0 =C2=A0 _retention_time= INTERVAL
)
RETURNS BOOLEAN AS $$
DECLARE
=C2=A0 =C2=A0 _osm_node_id BIGINT;
=C2=A0 =C2=A0 _row_coun= t INTEGER;
=C2=A0 =C2=A0 _should_send_pull_container BOOLEAN :=3D= TRUE;
BEGIN
=C2=A0 =C2=A0 -- Delete records with expir= ed retention time - NOT WORKING
=C2=A0 =C2=A0 DELETE FROM vehicle= _data
=C2=A0 =C2=A0 WHERE NOW() > expires_at;

=C2=A0 =C2=A0 -- Insert new records
=C2=A0 =C2=A0 FOREAC= H _osm_node_id IN ARRAY _osm_node_ids LOOP
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 BEGIN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 INSERT INT= O vehicle_data (
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 osm_node_id,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 customer_id,=C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 use_case_id,=C2=A0
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 container_id,=C2=A0
=C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 expires_at
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ) VALUES (
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 _osm_node_id,=C2=A0
=C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 _customer_id,=C2=A0
=
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 _use_case_id,= =C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 _c= ontainer_id,
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 NOW() + _retention_time
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 );
=C2=A0 =C2=A0 =C2=A0 =C2=A0 EXCEPTION WHEN foreign_key_= violation THEN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 RAISE EX= CEPTION 'Invalid customer_id % or use_case_id % for osm_node_id % conta= iner_id: %',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 _customer_id, _use_case_id, _osm_node_id, _container_id;
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 END;

=C2=A0 =C2=A0 =C2= =A0 =C2=A0 -- Check if the number of records exceeds the node limit
=C2=A0 =C2=A0 =C2=A0 =C2=A0 SELECT COUNT(*)
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 INTO STRICT _row_count
=C2=A0 =C2=A0 =C2=A0 =C2=A0 FRO= M vehicle_data
=C2=A0 =C2=A0 =C2=A0 =C2=A0 WHERE osm_node_id =3D = _osm_node_id
=C2=A0 =C2=A0 =C2=A0 =C2=A0 AND customer_id =3D _cus= tomer_id
=C2=A0 =C2=A0 =C2=A0 =C2=A0 AND use_case_id =3D _use_cas= e_id;

=C2=A0 =C2=A0 =C2=A0 =C2=A0 -- There is enou= gh up-to-date vehicle data for this triple,
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 -- so tell the vehicle not to send any PULL containers
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 IF _row_count > _node_limit THEN
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 _should_send_pull_container :=3D FAL= SE;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 END IF;
=C2=A0 =C2=A0 E= ND LOOP;

=C2=A0 =C2=A0 RETURN _should_send_pull_co= ntainer;
END;
$$ LANGUAGE plpgsql;

=
Finally, I have prepared a smoke test for my code and run it twice:

CREATE OR REPLACE FUNCTION test_store_vehicle_data(<= /div>
=C2=A0 =C2=A0 num_runs INTEGER,
=C2=A0 =C2=A0 OUT count= _true INTEGER,
=C2=A0 =C2=A0 OUT count_false INTEGER
)<= /div>
RETURNS RECORD AS $$
DECLARE
=C2=A0 =C2=A0 te= st_result BOOLEAN;
BEGIN
=C2=A0 =C2=A0 count_true :=3D = 0;
=C2=A0 =C2=A0 count_false :=3D 0;

=C2= =A0 =C2=A0 FOR i IN 1..num_runs LOOP
=C2=A0 =C2=A0 =C2=A0 =C2=A0 = -- Store OSM node ids (2 are same, 2 are changing)
=C2=A0 =C2=A0 = =C2=A0 =C2=A0 -- with node limit 10 and retention time 5 seconds
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 test_result :=3D store_vehicle_data(
= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 100 + i,=C2=A0
=C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 ARRAY[1000, 2000, 3000 + i, 4000 + i],
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 1,=C2=A0
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 5,=C2=A0
=C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 10,=C2=A0
=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 INTERVAL '5 seconds'
=C2=A0 =C2=A0 =C2=A0 = =C2=A0 );

=C2=A0 =C2=A0 =C2=A0 =C2=A0 IF test_resu= lt THEN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 count_true :=3D= count_true + 1;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 ELSE
=C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 count_false :=3D count_false + 1;
=C2=A0 =C2=A0 =C2=A0 =C2=A0 END IF;
=C2=A0 =C2=A0 END LOO= P;

=C2=A0 =C2=A0 RETURN;
END $$ LANGUAGE= plpgsql;

-- Run 2 smoke tests
DO $$
DECLARE
=C2=A0 =C2=A0 test_result RECORD;
BEGIN<= /div>
=C2=A0 =C2=A0 -- Test 1: store 15x4 records expiring in 5 seconds=
=C2=A0 =C2=A0 SELECT * INTO test_result FROM test_store_vehicle_= data(15);
=C2=A0 =C2=A0 IF test_result.count_true !=3D 10 OR test= _result.count_false !=3D 5 THEN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 RAISE= EXCEPTION 'Test 1 failed: expected 10 TRUE, 5 FALSE';
= =C2=A0 =C2=A0 END IF;

=C2=A0 =C2=A0 -- Sleep for 1= 0 seconds, so that all records in vehicle_data expire
=C2=A0 =C2= =A0 PERFORM pg_sleep(10);

=C2=A0 =C2=A0 -- Test 2:= store 15x4 records expiring in 5 seconds
=C2=A0 =C2=A0 SELECT * = INTO test_result FROM test_store_vehicle_data(15);
=C2=A0 =C2=A0 = IF test_result.count_true !=3D 10 OR test_result.count_false !=3D 5 THEN
=C2=A0 =C2=A0 =C2=A0 =C2=A0 RAISE EXCEPTION 'Test 2 failed: exp= ected 10 TRUE, 5 FALSE';
=C2=A0 =C2=A0 END IF;
END = $$;

-- Print all records in the vehicle_data table=
SELECT expires_at < NOW() AS expired, * FROM vehicle_data ORD= ER BY container_id;

My problem is that the DELETE = FROM vehicle_data WHERE NOW() > expires_at; statement in my store_vehicl= e_data() function does not see to delete anything.

And then the SELECT expires_at < NOW() AS expired, * FROM vehicle_data = ORDER BY container_id; prints the records in the table and yes, they are al= l expired there.

I run the above SQL code (creatin= g tables, creating functions, running smoke test) in a Dockerfile based on = the official Postgres Dockerfile and the smoke test (the Test 2) just alway= s fails.

Then I connect using psql to my docker co= ntainer and run the DELETE command at the psql prompt and voila - it works = there as expected and deletes all the expired records.

=
Thank you for any ideas and I have also asked this question at https://stackoverflow.com/= q/79367012/165071

Best regards
Alex<= /div>

<= /div>
--000000000000924312062bf93af1--