-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsqlstate.sql
More file actions
105 lines (96 loc) · 3.32 KB
/
Copy pathsqlstate.sql
File metadata and controls
105 lines (96 loc) · 3.32 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
--
-- Error model: how Ruby exceptions and PostgreSQL errors interact, what is
-- preserved when a database error is caught, and what is not.
--
-- A specific exception class's message is preserved when it propagates out.
CREATE FUNCTION es_custom() RETURNS int LANGUAGE plruby AS $$
raise ArgumentError, 'custom failure'
$$;
SELECT es_custom();
-- A caught database error: it is a StandardError, its message names the
-- offending object, its SQLSTATE is exposed, and the session keeps working
-- afterwards.
CREATE FUNCTION es_caught() RETURNS text LANGUAGE plruby AS $$
begin
spi_exec('select * from missing_relation_xyz')
'no error'
rescue => e
"std=#{e.is_a?(StandardError)} names_it=#{e.message.include?('missing_relation_xyz')} sqlstate=#{e.sqlstate}"
end
$$;
SELECT es_caught();
-- The SQLSTATE reflects the error class (division_by_zero here). A
-- PLRuby::Error not backed by a database error (elog) has a nil sqlstate,
-- and an ordinary Ruby exception has no sqlstate method at all.
CREATE FUNCTION es_sqlstate() RETURNS text LANGUAGE plruby AS $$
db = begin
spi_exec('select 1 / 0')
'no error'
rescue => e
e.sqlstate
end
pl = begin
elog('ERROR', 'synthetic')
rescue PLRuby::Error => e
e.sqlstate.inspect
end
rb = begin
raise 'plain ruby error'
rescue => e
e.respond_to?(:sqlstate) ? 'has method' : 'no method'
end
"db=#{db} elog=#{pl} ruby=#{rb}"
$$;
SELECT es_sqlstate();
-- An ensure block runs on both the success and the rescue path.
CREATE FUNCTION es_ensure(boolean) RETURNS text LANGUAGE plruby AS $$
log = []
r = begin
raise 'boom' if args[0]
'ok'
rescue
'rescued'
ensure
log << 'ensured'
end
"#{r}:#{log.join}"
$$;
SELECT es_ensure(false);
SELECT es_ensure(true);
-- pg_raise can attach DETAIL, HINT, and a specific SQLSTATE...
CREATE FUNCTION es_structured() RETURNS void LANGUAGE plruby AS $$
pg_raise('ERROR', 'order rejected',
detail: 'order 42 exceeds the credit limit',
hint: 'raise the limit or split the order',
sqlstate: 'P0001')
$$;
SELECT es_structured();
-- ...and a caller that rescues the resulting database error sees all three.
CREATE FUNCTION es_catch_structured() RETURNS text LANGUAGE plruby AS $$
begin
spi_exec('select es_structured()')
'no error'
rescue PLRuby::Error => e
"state=#{e.sqlstate} detail=#{e.detail} hint=#{e.hint}"
end
$$;
SELECT es_catch_structured();
-- Non-error levels emit the fields directly.
CREATE FUNCTION es_notice_fields() RETURNS void LANGUAGE plruby AS $$
pg_raise('NOTICE', 'heads up', detail: 'the details', hint: 'the hint')
$$;
SELECT es_notice_fields();
-- An invalid sqlstate is rejected at the call.
CREATE FUNCTION es_badstate() RETURNS void LANGUAGE plruby AS $$
pg_raise('ERROR', 'nope', sqlstate: 'abc')
$$;
SELECT es_badstate();
-- pg_raise and elog both reject an unknown level with a PL/Ruby error.
CREATE FUNCTION es_badraise() RETURNS void LANGUAGE plruby AS $$
pg_raise('BOGUS', 'nope')
$$;
SELECT es_badraise();
CREATE FUNCTION es_badelog() RETURNS void LANGUAGE plruby AS $$
elog('BOGUS', 'nope')
$$;
SELECT es_badelog();