Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u...
Transcript of Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u...
![Page 2: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/2.jpg)
CREATE PROCEDURE new_customer(name text, address text) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO customers VALUES (name, address); END $$; CALL new_customer('somename', 'someaddress');
![Page 3: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/3.jpg)
CREATE PROCEDURE convert_to_upper(INOUT string text) LANGUAGE plpgsql AS $$ BEGIN string := upper(string); END $$; CALL convert_to_upper('abc'); string -------- ABC (1 row)
![Page 4: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/4.jpg)
CREATE PROCEDURE new_customer(name text, address text) LANGUAGE plperl AS $$ $plan = spi_prepare('INSERT INTO customers VALUES ($1, $2)'); spi_exec_prepare($plan, $_[0], $_[1]); $$; CALL new_customer('somename', 'someaddress');
![Page 5: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/5.jpg)
CREATE PROCEDURE transaction_test1(x int, y text) LANGUAGE plpgsql AS $$ BEGIN FOR i IN 0..x LOOP INSERT INTO test1 (a, b) VALUES (i, y); IF i % 2 = 0 THEN COMMIT; ELSE ROLLBACK; END IF; END LOOP; END $$; CALL transaction_test1(9, 37);
![Page 6: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/6.jpg)
CREATE PROCEDURE transaction_test1() LANGUAGE plperl AS $$ foreach my $i (0..9) { spi_exec_query("INSERT INTO test1 (a) VALUES ($i)"); if ($i % 2 == 0) { spi_commit(); } else { spi_rollback(); } } $$;
![Page 7: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/7.jpg)
CREATE PROCEDURE transaction_test1() LANGUAGE plr AS $$ for(i in 0:9){ pg.spi.exec(paste("INSERT INTO test1 (a) VALUES (", i, ")")) if (i %% 2 == 0) { pg.spi.commit() } else { pg.spi.rollback() } } $$;
https://github.com/petere/plr/tree/procedure-transaction
![Page 8: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/8.jpg)
DO LANGUAGE plpgsql $$ BEGIN FOR i IN 0..9 LOOP INSERT INTO test1 (a) VALUES (i); IF i % 2 = 0 THEN COMMIT; ELSE ROLLBACK; END IF; END LOOP; END $$;
![Page 9: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/9.jpg)
CREATE PROCEDURE transaction_test2() LANGUAGE plpgsql AS $$ DECLARE r RECORD; BEGIN FOR r IN SELECT * FROM test2 ORDER BY x LOOP INSERT INTO test1 (a) VALUES (r.x); COMMIT; END LOOP; END; $$;
![Page 10: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/10.jpg)
CREATE PROCEDURE p1() LANGUAGE plpgsql AS $$ BEGIN CALL p2(); END $$; CREATE PROCEDURE p2() LANGUAGE plpgsql AS $$ BEGIN CALL transaction_test1(9, 37); END $$; CALL p1();
CREATE PROCEDURE p1() LANGUAGE plpgsql AS $$ BEGIN SELECT f2(); END $$; CREATE FUNCTION f2() LANGUAGE plpgsql AS $$ BEGIN CALL transaction_test1(9, 37); END $$; CALL p1();
![Page 11: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/11.jpg)
(non)atomic execution context
![Page 12: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/12.jpg)
top-level ☞ non-atomic
⬇ ⬇
CALL p1 ☞ non-atomic
⬇ ⬇
CALL p2 ☞ non-atomic
⬇ ⬇
CALL transaction_test1 ☞ non-atomic COMMIT -- OK ✅
![Page 13: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/13.jpg)
top-level ☞ non-atomic
⬇ ⬇
CALL p1 ☞ non-atomic
⬇ �
SELECT f2 ☞ atomic
⬇ ⬇
CALL transaction_test1 ☞ atomic COMMIT -- error ❌
![Page 14: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/14.jpg)
other stuff that disallowstransaction control
transaction blockGUC settings attached to proceduresecurity definercursor loop not read-onlyinside subtransaction (block with exceptionhandling)
![Page 15: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/15.jpg)
other stuff that doesn't work(yet)
procedures with OUT parametersVACUUM etc. inside proceduressupport in other PLs
![Page 16: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/16.jpg)
use casesporting (Oracle, DB2)batch stuffaudit logging
![Page 17: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/17.jpg)
CREATE PROCEDURE batch_geocode() LANGUAGE plpgsql AS $$ BEGIN WHILE EXISTS (SELECT 1 FROM addr_to_geocode WHERE pt IS NULL) LOOP WITH a AS (SELECT addid, address FROM addr_to_geocode WHERE pt IS NULL ORDER BY addid LIMIT 5 FOR UPDATE SKIP LOCKED) UPDATE addr_to_geocode SET pt = ST_SetSRID(g.geomout, 4326)::geography FROM (SELECT addid, (gc).rating, (gc).addy, (gc).geomout FROM a LEFT JOIN LATERAL geocode(address, 1) AS gc ON (true) ) AS g WHERE g.addid = addr_to_geocode.addid; COMMIT; END LOOP; END; $$;
http://www.postgresonline.com/journal/index.php?/archives/390-Using-procedures-for-batch-geocoding-and-other-batch-processing.html
![Page 18: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/18.jpg)
CREATE PROCEDURE new_customer(name text, address text) LANGUAGE plpgsql AS $$ BEGIN INSERT INTO audit_log (entry) VALUES ('someone tried to create a new customer'); COMMIT; INSERT INTO customers VALUES (name, address); END; $$; CALL new_customer('somename', 'someaddress');
![Page 19: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/19.jpg)
CREATE PROCEDURE waste_xid(cnt int) LANGUAGE plpgsql AS $$ DECLARE i int; BEGIN FOR i IN 1..cnt LOOP PERFORM txid_current(); COMMIT; END LOOP; END; $$;
![Page 20: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/20.jpg)
implementation details
![Page 21: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/21.jpg)
SPI functionsSPI_connect_ext(SPI_OPT_NONATOMIC)SPI_start_transaction()SPI_commit()SPI_rollback()
![Page 22: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/22.jpg)
tips for PL authorswrite a giant test suiteuse SPI_connect_ext()implement commit/rollback callsuse portal pinning (PinPortal(),HoldPinnedPortals())use PortalContext instead ofTopTransactionContextconsider lifetime of objects carefully
![Page 23: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/23.jpg)
dynamic result sets(not implemented)
CREATE PROCEDURE pdrstest1() LANGUAGE SQL AS $$ DECLARE c1 CURSOR WITH RETURN FOR SELECT * FROM cp_test2; DECLARE c2 CURSOR WITH RETURN FOR SELECT * FROM cp_test3; $$; CALL pdrstest1();
![Page 24: Stored Procedures - PostgreSQL...Stored Procedures What Are They Good For P e t e r E i se n t r a u t p e t e r. e i se n t r a u t @ 2 n d q u a d r a n t . co m @ p e t e r e i](https://reader034.fdocuments.us/reader034/viewer/2022042113/5e8ef5fc4e88054de7130faa/html5/thumbnails/24.jpg)
the end