-- Coverage for previously-untested paths: start_proc failure, DML/utility -- SPI results, trigger arguments, cursor/transaction interactions, UTF-8, -- TOASTed values, PHP exception handling, DO runtime errors. -- start_proc failure: the first call of the session fails, but the session -- is already marked initialized, so after clearing the GUC things work. CREATE FUNCTION cov_ident(int) RETURNS int LANGUAGE plphp AS $$ return $args[0]; $$; SET plphp.start_proc = 'cov_no_such_fn'; SELECT cov_ident(1); RESET plphp.start_proc; SELECT cov_ident(1); -- DML and utility statements through spi_exec: status and row counts CREATE FUNCTION cov_dml() RETURNS text LANGUAGE plphp AS $$ $out = array(); $r = spi_exec("create table cov_t (a int, b text)"); $out[] = spi_status($r); $r = spi_exec("insert into cov_t select g, 'row ' || g from generate_series(1, 3) g"); $out[] = spi_status($r) . ":" . spi_processed($r); $r = spi_exec("update cov_t set b = b || '!' where a < 3"); $out[] = spi_status($r) . ":" . spi_processed($r); $r = spi_exec("delete from cov_t where a = 1"); $out[] = spi_status($r) . ":" . spi_processed($r); // fetching from a non-SELECT result is refused $row = spi_fetch_row($r); $out[] = $row === false ? "no-fetch" : "fetched?"; return implode(" ", $out); $$; SELECT cov_dml(); SELECT * FROM cov_t ORDER BY a; -- spi_rewind on a non-SELECT result is harmless CREATE FUNCTION cov_rewind_dml() RETURNS text LANGUAGE plphp AS $$ $r = spi_exec("insert into cov_t values (9, 'nine')"); spi_rewind($r); return spi_status($r) . ":" . spi_processed($r); $$; SELECT cov_rewind_dml(); -- Trigger arguments: $_TD['args'] carries CREATE TRIGGER's literal arguments CREATE FUNCTION cov_trigargs() RETURNS trigger LANGUAGE plphp AS $$ pg_raise('notice', "trigger args: argc=" . $_TD['argc'] . " [" . implode(",", $_TD['args']) . "]"); return; $$; CREATE TRIGGER cov_trg BEFORE INSERT ON cov_t FOR EACH ROW EXECUTE PROCEDURE cov_trigargs('alpha', '42'); INSERT INTO cov_t VALUES (10, 'ten'); DROP TRIGGER cov_trg ON cov_t; -- A cursor does not survive spi_commit: the portal dies with the -- transaction and further fetches just return false. CREATE PROCEDURE cov_cursor_commit() LANGUAGE plphp AS $$ $c = spi_query("select a from cov_t order by a"); $row = spi_fetchrow($c); pg_raise('notice', "before commit: a=" . $row['a']); spi_commit(); $row = spi_fetchrow($c); pg_raise('notice', "after commit: " . ($row === false ? "false" : "a row")); $$; CALL cov_cursor_commit(); -- A cursor opened inside a rolled-back subtransaction() disappears with it CREATE FUNCTION cov_cursor_subxact() RETURNS text LANGUAGE plphp AS $$ $c = null; try { subtransaction(function() use (&$c) { $c = spi_query("select 1 as x"); throw new Exception("abort the subxact"); }); } catch (Exception $e) { pg_raise('notice', "caught: " . $e->getMessage()); } $row = spi_fetchrow($c); return $row === false ? "cursor gone" : "cursor survived"; $$; SELECT cov_cursor_subxact(); -- Multibyte UTF-8 round trip (strlen counts bytes) CREATE FUNCTION cov_utf8(text) RETURNS text LANGUAGE plphp AS $$ return $args[0] . " (" . strlen($args[0]) . " bytes)"; $$; SELECT cov_utf8('héllo wörld 日本語'); -- A TOAST-sized value passes through intact CREATE FUNCTION cov_toast(text) RETURNS text LANGUAGE plphp AS $$ return strlen($args[0]) . ":" . md5($args[0]); $$; SELECT cov_toast(repeat('abcdefghij', 1000)); -- PHP exceptions are catchable in plain function code CREATE FUNCTION cov_catch() RETURNS text LANGUAGE plphp AS $$ try { throw new RuntimeException("boom"); } catch (RuntimeException $e) { return "caught " . $e->getMessage(); } $$; SELECT cov_catch(); -- A DO block can fail at runtime (not just at parse time) DO $$ pg_raise('error', 'do block runtime error'); $$ LANGUAGE plphp; DROP TABLE cov_t; -- Redefinition churn: each CREATE OR REPLACE (and ALTER) discards the old -- compiled function and its memory context and recompiles on next use CREATE FUNCTION churn() RETURNS int LANGUAGE plphp AS $$ return 0; $$; SELECT churn(); DO $$ for ($i = 1; $i <= 50; $i++) { spi_exec("create or replace function churn() returns int language plphp as 'return $i;'"); $r = spi_exec("select churn() as v"); $row = spi_fetch_row($r); if ($row['v'] != $i) pg_raise('error', "recompile returned {$row['v']}, expected $i"); } pg_raise('notice', 'redefined 50 times, all correct'); $$ LANGUAGE plphp; -- ALTER also invalidates the cached compilation ALTER FUNCTION churn() COST 42; SELECT churn(); -- a body that fails to compile poisons the cache entry; fixing it recovers CREATE OR REPLACE FUNCTION churn() RETURNS int LANGUAGE plphp AS $$ return $$; SELECT churn(); CREATE OR REPLACE FUNCTION churn() RETURNS int LANGUAGE plphp AS $$ return 99; $$; SELECT churn(); DROP FUNCTION churn();