1-- test error handling, i forgot to restore Warn_restart in 2-- the trigger handler once. the errors and subsequent core dump were 3-- interesting. 4 5/* Flat out Python syntax error 6 */ 7CREATE FUNCTION python_syntax_error() RETURNS text 8 AS 9'.syntaxerror' 10 LANGUAGE plpythonu; 11 12/* With check_function_bodies = false the function should get defined 13 * and the error reported when called 14 */ 15SET check_function_bodies = false; 16 17CREATE FUNCTION python_syntax_error() RETURNS text 18 AS 19'.syntaxerror' 20 LANGUAGE plpythonu; 21 22SELECT python_syntax_error(); 23/* Run the function twice to check if the hashtable entry gets cleaned up */ 24SELECT python_syntax_error(); 25 26RESET check_function_bodies; 27 28/* Flat out syntax error 29 */ 30CREATE FUNCTION sql_syntax_error() RETURNS text 31 AS 32'plpy.execute("syntax error")' 33 LANGUAGE plpythonu; 34 35SELECT sql_syntax_error(); 36 37 38/* check the handling of uncaught python exceptions 39 */ 40CREATE FUNCTION exception_index_invalid(text) RETURNS text 41 AS 42'return args[1]' 43 LANGUAGE plpythonu; 44 45SELECT exception_index_invalid('test'); 46 47 48/* check handling of nested exceptions 49 */ 50CREATE FUNCTION exception_index_invalid_nested() RETURNS text 51 AS 52'rv = plpy.execute("SELECT test5(''foo'')") 53return rv[0]' 54 LANGUAGE plpythonu; 55 56SELECT exception_index_invalid_nested(); 57 58 59/* a typo 60 */ 61CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text 62 AS 63'if "plan" not in SD: 64 q = "SELECT fname FROM users WHERE lname = $1" 65 SD["plan"] = plpy.prepare(q, [ "test" ]) 66rv = plpy.execute(SD["plan"], [ a ]) 67if len(rv): 68 return rv[0]["fname"] 69return None 70' 71 LANGUAGE plpythonu; 72 73SELECT invalid_type_uncaught('rick'); 74 75 76/* for what it's worth catch the exception generated by 77 * the typo, and return None 78 */ 79CREATE FUNCTION invalid_type_caught(a text) RETURNS text 80 AS 81'if "plan" not in SD: 82 q = "SELECT fname FROM users WHERE lname = $1" 83 try: 84 SD["plan"] = plpy.prepare(q, [ "test" ]) 85 except plpy.SPIError, ex: 86 plpy.notice(str(ex)) 87 return None 88rv = plpy.execute(SD["plan"], [ a ]) 89if len(rv): 90 return rv[0]["fname"] 91return None 92' 93 LANGUAGE plpythonu; 94 95SELECT invalid_type_caught('rick'); 96 97 98/* for what it's worth catch the exception generated by 99 * the typo, and reraise it as a plain error 100 */ 101CREATE FUNCTION invalid_type_reraised(a text) RETURNS text 102 AS 103'if "plan" not in SD: 104 q = "SELECT fname FROM users WHERE lname = $1" 105 try: 106 SD["plan"] = plpy.prepare(q, [ "test" ]) 107 except plpy.SPIError, ex: 108 plpy.error(str(ex)) 109rv = plpy.execute(SD["plan"], [ a ]) 110if len(rv): 111 return rv[0]["fname"] 112return None 113' 114 LANGUAGE plpythonu; 115 116SELECT invalid_type_reraised('rick'); 117 118 119/* no typo no messing about 120 */ 121CREATE FUNCTION valid_type(a text) RETURNS text 122 AS 123'if "plan" not in SD: 124 SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ]) 125rv = plpy.execute(SD["plan"], [ a ]) 126if len(rv): 127 return rv[0]["fname"] 128return None 129' 130 LANGUAGE plpythonu; 131 132SELECT valid_type('rick'); 133 134/* error in nested functions to get a traceback 135*/ 136CREATE FUNCTION nested_error() RETURNS text 137 AS 138'def fun1(): 139 plpy.error("boom") 140 141def fun2(): 142 fun1() 143 144def fun3(): 145 fun2() 146 147fun3() 148return "not reached" 149' 150 LANGUAGE plpythonu; 151 152SELECT nested_error(); 153 154/* raising plpy.Error is just like calling plpy.error 155*/ 156CREATE FUNCTION nested_error_raise() RETURNS text 157 AS 158'def fun1(): 159 raise plpy.Error("boom") 160 161def fun2(): 162 fun1() 163 164def fun3(): 165 fun2() 166 167fun3() 168return "not reached" 169' 170 LANGUAGE plpythonu; 171 172SELECT nested_error_raise(); 173 174/* using plpy.warning should not produce a traceback 175*/ 176CREATE FUNCTION nested_warning() RETURNS text 177 AS 178'def fun1(): 179 plpy.warning("boom") 180 181def fun2(): 182 fun1() 183 184def fun3(): 185 fun2() 186 187fun3() 188return "you''ve been warned" 189' 190 LANGUAGE plpythonu; 191 192SELECT nested_warning(); 193 194/* AttributeError at toplevel used to give segfaults with the traceback 195*/ 196CREATE FUNCTION toplevel_attribute_error() RETURNS void AS 197$$ 198plpy.nonexistent 199$$ LANGUAGE plpythonu; 200 201SELECT toplevel_attribute_error(); 202 203/* Calling PL/Python functions from SQL and vice versa should not lose context. 204 */ 205CREATE OR REPLACE FUNCTION python_traceback() RETURNS void AS $$ 206def first(): 207 second() 208 209def second(): 210 third() 211 212def third(): 213 plpy.execute("select sql_error()") 214 215first() 216$$ LANGUAGE plpythonu; 217 218CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$ 219begin 220 select 1/0; 221end 222$$ LANGUAGE plpgsql; 223 224CREATE OR REPLACE FUNCTION python_from_sql_error() RETURNS void AS $$ 225begin 226 select python_traceback(); 227end 228$$ LANGUAGE plpgsql; 229 230CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$ 231plpy.execute("select sql_error()") 232$$ LANGUAGE plpythonu; 233 234SELECT python_traceback(); 235SELECT sql_error(); 236SELECT python_from_sql_error(); 237SELECT sql_from_python_error(); 238 239/* check catching specific types of exceptions 240 */ 241CREATE TABLE specific ( 242 i integer PRIMARY KEY 243); 244 245CREATE FUNCTION specific_exception(i integer) RETURNS void AS 246$$ 247from plpy import spiexceptions 248try: 249 plpy.execute("insert into specific values (%s)" % (i or "NULL")); 250except spiexceptions.NotNullViolation, e: 251 plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate) 252except spiexceptions.UniqueViolation, e: 253 plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate) 254$$ LANGUAGE plpythonu; 255 256SELECT specific_exception(2); 257SELECT specific_exception(NULL); 258SELECT specific_exception(2); 259 260/* SPI errors in PL/Python functions should preserve the SQLSTATE value 261 */ 262CREATE FUNCTION python_unique_violation() RETURNS void AS $$ 263plpy.execute("insert into specific values (1)") 264plpy.execute("insert into specific values (1)") 265$$ LANGUAGE plpythonu; 266 267CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$ 268begin 269 begin 270 perform python_unique_violation(); 271 exception when unique_violation then 272 return 'ok'; 273 end; 274 return 'not reached'; 275end; 276$$ language plpgsql; 277 278SELECT catch_python_unique_violation(); 279 280/* manually starting subtransactions - a bad idea 281 */ 282CREATE FUNCTION manual_subxact() RETURNS void AS $$ 283plpy.execute("savepoint save") 284plpy.execute("create table foo(x integer)") 285plpy.execute("rollback to save") 286$$ LANGUAGE plpythonu; 287 288SELECT manual_subxact(); 289 290/* same for prepared plans 291 */ 292CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$ 293save = plpy.prepare("savepoint save") 294rollback = plpy.prepare("rollback to save") 295plpy.execute(save) 296plpy.execute("create table foo(x integer)") 297plpy.execute(rollback) 298$$ LANGUAGE plpythonu; 299 300SELECT manual_subxact_prepared(); 301 302/* raising plpy.spiexception.* from python code should preserve sqlstate 303 */ 304CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$ 305raise plpy.spiexceptions.DivisionByZero() 306$$ LANGUAGE plpythonu; 307 308DO $$ 309BEGIN 310 SELECT plpy_raise_spiexception(); 311EXCEPTION WHEN division_by_zero THEN 312 -- NOOP 313END 314$$ LANGUAGE plpgsql; 315 316/* setting a custom sqlstate should be handled 317 */ 318CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$ 319exc = plpy.spiexceptions.DivisionByZero() 320exc.sqlstate = 'SILLY' 321raise exc 322$$ LANGUAGE plpythonu; 323 324DO $$ 325BEGIN 326 SELECT plpy_raise_spiexception_override(); 327EXCEPTION WHEN SQLSTATE 'SILLY' THEN 328 -- NOOP 329END 330$$ LANGUAGE plpgsql; 331 332/* test the context stack trace for nested execution levels 333 */ 334CREATE FUNCTION notice_innerfunc() RETURNS int AS $$ 335plpy.execute("DO LANGUAGE plpythonu $x$ plpy.notice('inside DO') $x$") 336return 1 337$$ LANGUAGE plpythonu; 338 339CREATE FUNCTION notice_outerfunc() RETURNS int AS $$ 340plpy.execute("SELECT notice_innerfunc()") 341return 1 342$$ LANGUAGE plpythonu; 343 344\set SHOW_CONTEXT always 345 346SELECT notice_outerfunc(); 347