-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy patherror.sql
More file actions
373 lines (308 loc) · 8.6 KB
/
error.sql
File metadata and controls
373 lines (308 loc) · 8.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
SET standard_conforming_strings TO ON;
SET client_encoding TO 'UTF-8';
CREATE OR REPLACE FUNCTION pysyntaxerror() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
# missing ':'
def main()
pass
$python$;
SELECT pysyntaxerror();
CREATE OR REPLACE FUNCTION py_load_failure() RETURNS VOID LANGUAGE python AS
$python$
# raise an exception while loading the module
raise RuntimeError("doa")
def main():
pass
$python$;
SELECT py_load_failure();
CREATE OR REPLACE FUNCTION py_failure() RETURNS VOID LANGUAGE python AS
$python$
def main():
# raise an exception on execution
raise RuntimeError("doa")
$python$;
SELECT py_failure();
-- raises a pg error on load
CREATE OR REPLACE FUNCTION pg_load_failure() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
fun = rp('nosuchfunc(int17,zzz)')
def main():
pass
$python$;
SELECT pg_load_failure();
-- raises a pg error on exec
CREATE OR REPLACE FUNCTION pg_failure() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
def main():
fun = rp('nosuchfunc(int17,zzz)')
$python$;
SELECT pg_failure();
-- suffocates a pg error on load; should see PL complaint
CREATE OR REPLACE FUNCTION pg_load_failure_suf() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
try:
fun = rp('nosuchfunc(int17,zzz)')
except:
pass
def main():
raise RuntimeError("should never see this")
$python$;
SELECT pg_load_failure_suf();
-- suffocates a pg error on exec; should see PL complaint
CREATE OR REPLACE FUNCTION pg_failure_suf() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
def main():
try:
fun = rp('nosuchfunc(int17,zzz)')
except:
pass
$python$;
SELECT pg_failure_suf();
-- suffocates a pg error, and leaves an open xact on load
CREATE OR REPLACE FUNCTION pg_x_load_failure_suf() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
x=xact()
x.__enter__()
try:
fun = rp('nosuchfunc(int17,zzz)')
except:
pass
def main():
raise RuntimeError("should never see this")
$python$;
SELECT pg_x_load_failure_suf();
-- suffocates a pg error, and leaves an open xact
CREATE OR REPLACE FUNCTION pg_x_failure_suf() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
def main():
x=xact()
x.__enter__()
try:
fun = rp('nosuchfunc(int17,zzz)')
except:
pass
$python$;
SELECT pg_x_failure_suf();
-- suffocates a pg error, and attempts to enter a protected area
CREATE OR REPLACE FUNCTION pg_failure_suf_IFTE() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
def main():
try:
fun = rp('nosuchfunc(int17,zzz)')
except:
# Should be valid, but the protection of
# PL_DB_IN_ERROR should keep it from getting called.
rp('pg_x_failure_suf()')
$python$;
SELECT pg_failure_suf_IFTE();
-- suffocates a pg error, and leaves an open xact, attempts to enter a protected area
CREATE OR REPLACE FUNCTION pg_x_failure_suf_IFTE() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
def main():
x=xact()
x.__enter__()
try:
fun = rp('nosuchfunc(int17,zzz)')
except:
# Should be valid, but the protection of
# PL_DB_IN_ERROR should keep it from getting called.
rp('pg_x_failure_suf()')
$python$;
SELECT pg_x_failure_suf_IFTE();
CREATE OR REPLACE FUNCTION check_errordata_access() RETURNS SETOF text LANGUAGE python AS
$python$
import Postgres
rp = Postgres.Type(Postgres.CONST['REGPROCEDUREOID'])
keys = (
'message',
'detail',
'detail_log',
'context',
'elevel',
'sqlerrcode',
'code',
'severity',
)
def main():
try:
with xact():
rp('nosuchfunc(int17,zzz)')
except Postgres.Exception as e:
errdata = e.pg_errordata
# attribute checks
errdata.internalpos
errdata.cursorpos
errdata.lineno
errdata.funcname
errdata.saved_errno
errdata.domain
return (
x + ':' + str(getattr(errdata, x)) for x in keys
)
$python$;
SELECT check_errordata_access();
-- check the attributes available on Postgres.Exception
CREATE OR REPLACE FUNCTION check_exc_access() RETURNS SETOF text LANGUAGE python AS
$python$
import Postgres
keys = (
'code',
'message',
'errno',
'severity',
)
def main():
try:
with xact():
Postgres.ERROR(
code = '22331',
message = 'message',
context = 'context',
detail = 'detail'
)
except Postgres.Exception as e:
err = e
# attribute checks
d = list(err.details.items())
d.sort(key=lambda x: x[0])
return tuple((
x + ':' + str(getattr(err, x)) for x in keys
)) + tuple(d)
$python$;
SELECT check_exc_access();
-- __func__ must exist
CREATE OR REPLACE FUNCTION silly_function() RETURNS VOID LANGUAGE python AS
$python$
del __func__
def main():
raise Exception("haha")
$python$;
-- raised the 'haha' exception.. module is known
SELECT silly_function();
-- raise an Attribute error; we need __func__.
SELECT silly_function();
-- __func__ must be a Postgres.Function
CREATE OR REPLACE FUNCTION evil_function() RETURNS VOID LANGUAGE python AS
$python$
__func__ = None
def main():
raise Exception("muahahahhaha")
$python$;
-- The Exception in main gets raised as we grabbed the reference from
-- our own call to load_module().
SELECT evil_function();
-- This time, fn_extra is NULL and there's a module in sys.modules.
-- Validate that __func__ is actually a Postgres.Function object.
-- This will fail with a TypeError
SELECT evil_function();
-- __func__ must be a Postgres.Function with the Oid of the called function
CREATE OR REPLACE FUNCTION more_evil_function() RETURNS VOID LANGUAGE python AS
$python$
__func__ = proc('evil_function()')
def main():
raise Exception("muahahahhahaHAHAHAHA")
$python$;
-- same as before, we already have the module object
SELECT more_evil_function();
-- It's a function object, but the wrong function object.
-- Raise a value error this time...
SELECT more_evil_function();
CREATE OR REPLACE FUNCTION custom_error(bool) RETURNS VOID LANGUAGE python AS
$python$
import Postgres
def main(with_tb):
Postgres.ERROR(
code = 'AAAAA',
message = 'message',
context = 'context',
detail = 'detail',
hint = 'hint',
inhibit_pl_context = not bool(with_tb),
)
$python$;
\set VERBOSITY verbose
SELECT custom_error(true);
SELECT custom_error(false);
\set VERBOSITY default
-- Ignore the inhibit_pl_context setting when it's not a Postgres.Exception.
CREATE OR REPLACE FUNCTION inhibited_pl_context_pyerr() RETURNS VOID LANGUAGE python AS
$python$
import Postgres
def main():
ve = ValueError("show this traceback")
ve.inhibit_pl_context = True
raise ve
$python$;
SELECT inhibited_pl_context_pyerr();
-- exercise a relayed exception
-- probably better categorized relative to "bool", but there isn't a place..
CREATE OR REPLACE FUNCTION boolean_blowup() RETURNS bool LANGUAGE python AS
$python$
class foo(object):
def __bool__(self):
raise ValueError("bad nonzero implementation, relay this exception")
def main():
return foo()
$python$;
SELECT boolean_blowup();
-- test direct function execution while "in" a Python exception --
-- (no database error hath occurred)
-- XXX: Not the desired effect for CONTEXT
--
-- The following function shows the effect of Python's __context__.
-- For plpython, the global __context__ needs to be stored and restored
-- every time the plhandler is entered. This is necessary to ensure that
-- the traceback print out is not redundant.
--
-- There are a couple ways this could be hacked:
--
-- 1. setting an exception on entry to identify the context and
-- breaking the context chain at that point iff an exception occurs
-- 2. storing and restoring the context from the thread state.
-- (currently a bit too "dangerous", so we'll have to poll the capi sig)
--
CREATE OR REPLACE FUNCTION check_dfc_in_exc(bool) RETURNS VOID LANGUAGE python AS
$python$
from Postgres import WARNING
def main(cont):
try:
if cont:
raise TypeError("lame")
else:
raise ValueError("err")
except ValueError as exc:
__func__(True)
$python$;
SELECT check_dfc_in_exc(FALSE);
-- make sure the linecache is getting cleared
CREATE OR REPLACE FUNCTION check_linecache_clear() RETURNS VOID LANGUAGE python AS
$python$
def main():
raise ValueError
$python$;
SELECT check_linecache_clear();
-- should show the "raise TypeError" in the traceback string.
-- adjust Postgres._pl_eox to *not* clear the linecache to see how it operated before
CREATE OR REPLACE FUNCTION check_linecache_clear() RETURNS VOID LANGUAGE python AS
$python$
def main():
raise TypeError
$python$;
SELECT check_linecache_clear();