-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsqlstate.out
More file actions
141 lines (134 loc) · 4.5 KB
/
Copy pathsqlstate.out
File metadata and controls
141 lines (134 loc) · 4.5 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
--
-- 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();
ERROR: custom failure
CONTEXT: PL/Ruby function "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();
es_caught
---------------------------------------
std=true names_it=true sqlstate=42P01
(1 row)
-- 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();
es_sqlstate
----------------------------------
db=22012 elog=nil ruby=no method
(1 row)
-- 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);
es_ensure
------------
ok:ensured
(1 row)
SELECT es_ensure(true);
es_ensure
-----------------
rescued:ensured
(1 row)
-- 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();
ERROR: order rejected
DETAIL: order 42 exceeds the credit limit
HINT: raise the limit or split the order
CONTEXT: PL/Ruby function "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();
es_catch_structured
----------------------------------------------------------------------------------------------
state=P0001 detail=order 42 exceeds the credit limit hint=raise the limit or split the order
(1 row)
-- 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();
NOTICE: heads up
DETAIL: the details
HINT: the hint
es_notice_fields
------------------
(1 row)
-- 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();
ERROR: pg_raise: invalid sqlstate "abc"
CONTEXT: PL/Ruby function "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();
ERROR: pg_raise: incorrect log level "BOGUS"
CONTEXT: PL/Ruby function "es_badraise"
CREATE FUNCTION es_badelog() RETURNS void LANGUAGE plruby AS $$
elog('BOGUS', 'nope')
$$;
SELECT es_badelog();
ERROR: elog: unrecognized level "BOGUS"
CONTEXT: PL/Ruby function "es_badelog"