@consiliency/agent-board-schema 1.50.0 → 1.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fixtures/already-applied.json +4 -0
- package/migrations/20261002000100_agent_board_session_challenge_supersede.sql +383 -0
- package/package.json +1 -1
- package/schema-manifest.json +12 -3
- package/src/tooling.test.mjs +10 -2
- package/tests/agent_board_agent_liveness.test.sql +3 -1
- package/tests/agent_board_atomic_fleet_onboarding.test.sql +33 -36
- package/tests/agent_board_authbound_boundary.test.sql +4 -3
- package/tests/agent_board_board_claim_policy.test.sql +17 -6
- package/tests/agent_board_claim_subject_gate.test.sql +7 -8
- package/tests/agent_board_claimkey.test.sql +9 -6
- package/tests/agent_board_claimpolicy_race.test.sql +1466 -342
- package/tests/agent_board_consent_emission.test.sql +5 -4
- package/tests/agent_board_consent_gating.test.sql +20 -9
- package/tests/agent_board_enroll_resumption.test.sql +1 -1
- package/tests/agent_board_enrollment_audit_content.test.sql +5 -4
- package/tests/agent_board_federated_identity_injective_slug.test.sql +3 -1
- package/tests/agent_board_federated_lock_order_class.test.sql +69 -52
- package/tests/agent_board_fedsession.test.sql +3 -1
- package/tests/agent_board_fedsession_provider_absence_gate.test.sql +9 -4
- package/tests/agent_board_fedsession_stored_identity_gate.test.sql +18 -6
- package/tests/agent_board_fk_lock_edges.test.sql +84 -5
- package/tests/agent_board_fleet_domain_reclaim.test.sql +2 -4
- package/tests/agent_board_fleet_machine_hijack.test.sql +2 -4
- package/tests/agent_board_fleet_machine_reactivation.test.sql +5 -1
- package/tests/agent_board_fleet_machine_reclaim.test.sql +2 -4
- package/tests/agent_board_invite_no_downgrade.test.sql +24 -18
- package/tests/agent_board_ledger_authority.test.sql +29 -21
- package/tests/agent_board_mint_harden.test.sql +5 -5
- package/tests/agent_board_outbox_wake_ordering.test.sql +4 -3
- package/tests/agent_board_ownerless_renew_quota.test.sql +7 -2
- package/tests/agent_board_portal_offline_invariant.test.sql +5 -4
- package/tests/agent_board_receipt_subject_gate.test.sql +7 -8
- package/tests/agent_board_rolereset.test.sql +5 -3
- package/tests/agent_board_rowid.test.sql +34 -15
- package/tests/agent_board_signer_lifecycle.test.sql +24 -13
- package/tests/agent_board_signing_challenge_pending_cap.test.sql +391 -36
- package/tests/agent_board_source_strip_helper.test.sql +2 -1
- package/tests/agent_board_task_event_origin_posture.test.sql +21 -9
- package/tests/agent_board_wake_recovery.test.sql +53 -26
- package/tests/agent_board_write_enforcement_class.test.sql +130 -12
- package/tests/catalog/function_execute_privileges.json +1923 -0
- package/tests/fixtures/test-prereqs.sql +644 -237
- package/tests/isolation/expected/challenge_supersede_lock_order.out +213 -0
- package/tests/isolation/specs/challenge_supersede_lock_order.spec +183 -0
|
@@ -83,7 +83,7 @@ set search_path = extensions, public, agent_board;
|
|
|
83
83
|
|
|
84
84
|
begin;
|
|
85
85
|
|
|
86
|
-
select plan(
|
|
86
|
+
select plan(45);
|
|
87
87
|
|
|
88
88
|
-- ---------------------------------------------------------------------------
|
|
89
89
|
-- Fixture: one board, one thread on it, one thread with NO board (the constitutive-'inherit'
|
|
@@ -135,6 +135,1370 @@ begin
|
|
|
135
135
|
insert into _cpr(board, thread, free_thread, board_b) values (v_board, v_thread, v_free, v_board_b);
|
|
136
136
|
end $$;
|
|
137
137
|
|
|
138
|
+
-- ===========================================================================
|
|
139
|
+
-- THE MATCHERS FIRST. CPR-0..0b gate the regexes every population assertion below is built from,
|
|
140
|
+
-- so they run BEFORE any production use: a matcher that no longer compiles is named here by its
|
|
141
|
+
-- behaviour-table row (`raises: invalid regular expression …`), where the same defect met first by
|
|
142
|
+
-- a bare `~` in CPR-6 or CPR-14a would only abort the file (#113) and name nothing. Measured under
|
|
143
|
+
-- mutation m25 (a `(?c)` director spliced mid-pattern): with this block after the census, CPR-14a
|
|
144
|
+
-- aborted at its first `write_re('claims')` and 16 of 45 assertions ran.
|
|
145
|
+
-- ===========================================================================
|
|
146
|
+
-- CPR-0 / CPR-0a. THE MATCHERS' BEHAVIOUR TABLE, AND A GATE ON THE TABLE ITSELF.
|
|
147
|
+
--
|
|
148
|
+
-- WHY BOTH. The identifier matchers drifted four times across three review rounds, and every
|
|
149
|
+
-- version passed its accept cases. So a table was added. Then the SAME defect appeared INSIDE the
|
|
150
|
+
-- table three times: rows that pass for a reason unrelated to the rule they are named for — first
|
|
151
|
+
-- r06 (tested `$` through an adjacency that rejected it earlier), then a12/r13 (compiled a
|
|
152
|
+
-- reconstruction rather than the production object), then a13/a14/a15 (keyword branches that the
|
|
153
|
+
-- subject text could never exercise). Each was found by a different reviewer, after the previous
|
|
154
|
+
-- fix, by the same procedure: revert a rule and see which rows move.
|
|
155
|
+
--
|
|
156
|
+
-- THREE OCCURRENCES IS ENOUGH EVIDENCE THAT A HAND-COMPILED REVERT MAP CANNOT BE TRUSTED, because
|
|
157
|
+
-- compiling it is the same act as writing the rows. So the map is mechanical now: CPR-0a reverts
|
|
158
|
+
-- each rule in turn, re-evaluates every row, and requires at least one row to flip. A rule with no
|
|
159
|
+
-- flipping row FAILS AND NAMES ITSELF — which is what would have caught all three, and none of
|
|
160
|
+
-- which a human reading the table did.
|
|
161
|
+
--
|
|
162
|
+
-- The rows are DATA rather than inline expressions so both assertions evaluate the same list: a
|
|
163
|
+
-- row CPR-0 checks and CPR-0a cannot re-evaluate would be back to two lists that agree today.
|
|
164
|
+
-- ---------------------------------------------------------------------------
|
|
165
|
+
-- WANT IS TEXT, NOT BOOLEAN (Consiliency/message-board#416 PR 1): a row may now expect the matcher to
|
|
166
|
+
-- RAISE (`writes_any_frag` on an empty list), and a raise must be asserted by its MESSAGE rather than
|
|
167
|
+
-- by "it did not return true" — otherwise a typo in the expression would satisfy the row. `costly`
|
|
168
|
+
-- marks the rows that run `lock_edges()`/`fn_call_closure()` (seconds each); CPR-0a re-evaluates
|
|
169
|
+
-- them only when the rule being reverted belongs to one of those two functions.
|
|
170
|
+
create temp table _mrow (label text primary key, expr text, want text, costly boolean not null default false) on commit drop;
|
|
171
|
+
|
|
172
|
+
-- A fixture in agent_board for the two costly rows: a caller whose callee is spelled with a QUOTED
|
|
173
|
+
-- identifier. Created inside this rolled-back transaction, so it never outlives the test.
|
|
174
|
+
create function agent_board._probe416_quoted_callee(p_task uuid) returns void
|
|
175
|
+
language plpgsql as $f$
|
|
176
|
+
begin
|
|
177
|
+
perform agent_board."acquire_claim"(p_task);
|
|
178
|
+
end $f$;
|
|
179
|
+
|
|
180
|
+
insert into _mrow(label, expr, want) values
|
|
181
|
+
-- ACCEPTS: spellings PostgreSQL resolves to OUR object
|
|
182
|
+
('a01-qualified', $x$'update agent_board.claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
183
|
+
('a02-unqualified', $x$'update claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
184
|
+
('a03-upper-unquoted', $x$'UPDATE AGENT_BOARD.CLAIMS SET x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
185
|
+
('a04-quoted-lower', $x$'update "agent_board"."claims" set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
186
|
+
('a05-verb-newline', $x$'insert' || chr(10) || 'into claims v' ~ agent_board_test.stmt_re('insert into','claims')$x$, true),
|
|
187
|
+
('a06-verb-2spaces', $x$'delete from claims where x' ~ agent_board_test.stmt_re('delete from','claims')$x$, true),
|
|
188
|
+
('a07-verb-tab', $x$'merge' || chr(9) || 'into claims m' ~ agent_board_test.stmt_re('merge into','claims')$x$, true),
|
|
189
|
+
-- a08 gates ci_re's OWN space rule, which a05-a07 do NOT: they exercise whitespace inside a verb
|
|
190
|
+
-- through stmt_re, which would still work if ci_re emitted a literal space and stmt_re rewrote it.
|
|
191
|
+
('a08-cire-phrase', $x$'no' || chr(10) || 'key' ~ ('(?c)' || agent_board_test.ci_re('no key'))$x$, true),
|
|
192
|
+
('a09-inheritance', $x$'update agent_board.boards* set x' ~ agent_board_test.stmt_re('update','boards')$x$, true),
|
|
193
|
+
('a10-call-space', $x$'perform _claimgate_lock_board_policy (v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, true),
|
|
194
|
+
('a11-call-quoted', $x$'perform agent_board."_claimgate_lock_board_policy"(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, true),
|
|
195
|
+
('a12-col-upper', $x$'set CLAIM_POLICY = x' ~ agent_board_test.col_frag('claim_policy')$x$, true),
|
|
196
|
+
('a13-ins-novals', $x$'insert into agent_board.boards values (1,2)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
197
|
+
('a14-ins-select', $x$'insert into agent_board.boards select (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
198
|
+
-- a15/a16 cover two INSERT FORMS the rule must census. They do NOT gate a keyword exclusion, and
|
|
199
|
+
-- that is stated rather than implied: DEFAULT is only ever followed by VALUES and OVERRIDING by
|
|
200
|
+
-- SYSTEM/USER, so neither can be mistaken for an alias-before-`(`. The exclusions for them were
|
|
201
|
+
-- therefore DEAD CODE and have been removed rather than kept with rows that cannot gate them.
|
|
202
|
+
('a15-ins-default', $x$'insert into agent_board.boards default values' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
203
|
+
('a16-ins-overriding', $x$'insert into agent_board.boards overriding user value values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
204
|
+
('a17-ins-re-eq-frag', $x$agent_board_test.insert_all_columns_re('boards') = '(?c)' || agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
205
|
+
-- REJECTS: spellings PostgreSQL resolves ELSEWHERE
|
|
206
|
+
('a18-verb-rel-spaces', $x$'update agent_board.claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
207
|
+
('r01-other-schema', $x$'update other.claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, false),
|
|
208
|
+
('r02-dot-space', $x$'perform other . _claimgate_lock_board_policy(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, false),
|
|
209
|
+
('r03-quoted-upper', $x$'update "AGENT_BOARD"."claims" set x' ~ agent_board_test.stmt_re('update','claims')$x$, false),
|
|
210
|
+
('r04-prefix-word', $x$'update dashboards set x' ~ agent_board_test.stmt_re('update','boards')$x$, false),
|
|
211
|
+
('r05-dollar-lead', $x$'perform x$_claimgate_lock_board_policy(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, false),
|
|
212
|
+
('r06-dollar-rel', $x$'from x$claims c' ~ agent_board_test.qname_re('claims')$x$, false),
|
|
213
|
+
('r07-nonascii-lead', $x$'perform ' || chr(233) || '_claimgate_lock_board_policy(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, false),
|
|
214
|
+
('r08-nonascii-trail', $x$'from claims' || chr(233) || ' c' ~ agent_board_test.qname_re('claims')$x$, false),
|
|
215
|
+
('r09-col-nonascii', $x$'set claim_policy' || chr(233) || ' = x' ~ agent_board_test.col_frag('claim_policy')$x$, false),
|
|
216
|
+
('r10-col-dollar', $x$'set x$claim_policy = x' ~ agent_board_test.col_frag('claim_policy')$x$, false),
|
|
217
|
+
('r11-i-flag', $x$regexp_instr('x "AGENT_BOARD"."claims" y', agent_board_test.qname_re('claims'), 1, 1, 0, 'i') > 0$x$, false),
|
|
218
|
+
('r12-ci-operator', $x$'x "AGENT_BOARD"."claims" y' ~* agent_board_test.qname_re('claims')$x$, false),
|
|
219
|
+
('r13-ins-collist', $x$'insert into agent_board.boards (slug) values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, false),
|
|
220
|
+
('r14-ins-alias', $x$'insert into agent_board.boards as b(slug) values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, false),
|
|
221
|
+
('r16-call-i-flag', $x$regexp_instr('perform "AGENT_BOARD"."_claimgate_lock_board_policy"(v)', agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0, 'i') > 0$x$, false),
|
|
222
|
+
('r15-ins-qalias', $x$'insert into agent_board.boards as "b"(slug) values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, false),
|
|
223
|
+
-- #416/#417/#424 PR 1: the STATEMENT-SPAN, ASSIGNMENT-POSITION and WRITE-SHAPE rules. Every
|
|
224
|
+
-- row below was run against the family before it was written down.
|
|
225
|
+
('a19-span-lit-semicolon', $x$'update boards set note = ''a;b'', claim_policy = x' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
226
|
+
('r17-span-stmt-boundary', $x$'update boards set x = 1; update other set claim_policy = 2' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
227
|
+
('a20-merge-ins-novals', $x$'merge into agent_board.boards b using s on b.id = s.id when not matched then insert values (1)' ~ ('(?c)' || agent_board_test.merge_insert_all_columns_frag('boards'))$x$, true),
|
|
228
|
+
('a20b-merge-ins-novals-via-col-write', $x$'merge into agent_board.boards b using s on b.id = s.id when not matched then insert values (1)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
229
|
+
('r18-merge-ins-collist', $x$'merge into agent_board.boards b using s on b.id = s.id when not matched then insert (slug) values (1)' ~ ('(?c)' || agent_board_test.merge_insert_all_columns_frag('boards'))$x$, false),
|
|
230
|
+
('r19-merge-do-nothing', $x$'merge into agent_board.boards b using s on b.id = s.id when not matched then do nothing' ~ ('(?c)' || agent_board_test.merge_insert_all_columns_frag('boards'))$x$, false),
|
|
231
|
+
('r20-merge-matched-update', $x$'merge into agent_board.boards b using s on b.id = s.id when matched then update set x = 1' ~ ('(?c)' || agent_board_test.merge_insert_all_columns_frag('boards'))$x$, false),
|
|
232
|
+
('a21-three-part-name', $x$'update mb_db.agent_board.boards set x' ~ agent_board_test.stmt_re('update','boards')$x$, true),
|
|
233
|
+
('a21b-three-part-quoted-db', $x$'update "mb db".agent_board.boards set x' ~ agent_board_test.stmt_re('update','boards')$x$, true),
|
|
234
|
+
('r21-three-part-other-schema', $x$'update mb_db.other.boards set x' ~ agent_board_test.stmt_re('update','boards')$x$, false),
|
|
235
|
+
('a22-copy-from', $x$'copy agent_board.boards from stdin' ~ agent_board_test.write_re('boards')$x$, true),
|
|
236
|
+
('a22b-copy-collist-from', $x$'copy agent_board.boards (slug, claim_policy) from stdin' ~ agent_board_test.write_re('boards')$x$, true),
|
|
237
|
+
('r22-copy-to', $x$'copy agent_board.boards to stdout' ~ agent_board_test.write_re('boards')$x$, false),
|
|
238
|
+
('a23-truncate-table-only', $x$'truncate table only agent_board.boards' ~ agent_board_test.write_re('boards')$x$, true),
|
|
239
|
+
('a36-truncate-list-second', $x$'truncate agent_board.claims, agent_board.boards' ~ agent_board_test.write_re('boards')$x$, true),
|
|
240
|
+
('r34-truncate-other-only', $x$'truncate agent_board.claims' ~ agent_board_test.write_re('boards')$x$, false),
|
|
241
|
+
('r23-write-suffix-word', $x$'update boards_archive set x' ~ agent_board_test.write_re('boards')$x$, false),
|
|
242
|
+
('r24-bare-when-required', $x$'update boards set x' ~ agent_board_test.stmt_re('update','boards', false)$x$, false),
|
|
243
|
+
('a24-qualified-when-required', $x$'update agent_board.boards set x' ~ agent_board_test.stmt_re('update','boards', false)$x$, true),
|
|
244
|
+
('a39-other-schema-explicit', $x$'update auth.users set x' ~ agent_board_test.stmt_re('update','auth.users')$x$, true),
|
|
245
|
+
('r35-other-schema-wrong-schema', $x$'update agent_board.users set x' ~ agent_board_test.stmt_re('update','auth.users')$x$, false),
|
|
246
|
+
('a25-set-row-form', $x$'update boards set (slug, claim_policy) = (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
247
|
+
('a33-mixed-scalar-row-form', $x$'update agent_board.boards set updated_at = now(), (slug, claim_policy) = (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
248
|
+
('r25-col-in-where', $x$'update agent_board.boards set slug = x where claim_policy = y' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
249
|
+
('r26-col-in-returning', $x$'update agent_board.boards set slug = x returning claim_policy' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
250
|
+
('r27-merge-col-in-on-only', $x$'merge into agent_board.boards b using s on b.claim_policy = s.x when matched then update set slug = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
251
|
+
('r28-insert-select-col', $x$'insert into agent_board.boards (slug) select claim_policy from x' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
252
|
+
('r29-set-rhs-read', $x$'update agent_board.boards set slug = (select claim_policy from x)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
253
|
+
('a26-set-subquery-then-col', $x$'update agent_board.boards set slug = (select 1 from t where q = 1), claim_policy = 2' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
254
|
+
('a27-set-where-in-literal', $x$'update agent_board.boards set note = ''where'', claim_policy = 2' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
255
|
+
('a28-span-estring', $x$'update boards set note = E''a\'';b'', claim_policy = x' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
256
|
+
('r30-estring-guard', $x$'update boards set line = ''x\''; update other set claim_policy = ''z''' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
257
|
+
('a29-quoted-callee', $x$'perform agent_board."acquire_claim"(1)' ~ agent_board_test.call_re('acquire_claim')$x$, true),
|
|
258
|
+
('r31-bare-callee-when-required', $x$'perform acquire_claim(1)' ~ agent_board_test.call_re('acquire_claim', false)$x$, false),
|
|
259
|
+
('a30-case-when-then-col', $x$'update agent_board.boards set slug = case when x then y else z end, claim_policy = p' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
260
|
+
('a31-merge-when-matched-and-pred', $x$'merge into agent_board.boards b using s on b.id = s.id when matched and b.x > 1 then update set claim_policy = s.p' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
261
|
+
('a32-merge-not-matched-by-source-update', $x$'merge into agent_board.boards b using s on b.id = s.id when not matched by source then update set claim_policy = s.p' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
262
|
+
('a34-merge-case-when-then-col', $x$'merge into agent_board.boards b using s on b.id = s.id when matched then update set slug = case when s.x then 1 else 2 end, claim_policy = s.p' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
263
|
+
('r33-merge-col-in-later-clause', $x$'merge into agent_board.boards b using s on b.id = s.id when matched then update set slug = 1 when not matched then do nothing' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
264
|
+
('r32-copy-other-cols-from', $x$'copy agent_board.boards (slug) from stdin' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
265
|
+
('a38-copy-collist-col-from', $x$'copy agent_board.boards (slug, claim_policy) from stdin' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
266
|
+
('a37-insert-collist-overriding', $x$'insert into agent_board.boards (slug, claim_policy) overriding system value values (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
267
|
+
('a40-insert-alias-collist', $x$'insert into agent_board.boards as b (slug, claim_policy) values (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
268
|
+
('a41-merge-ins-collist-col', $x$'merge into agent_board.boards b using s on b.id = s.id when not matched then insert (slug, claim_policy) values (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
269
|
+
('a42-select-into-table', $x$'select 1 into temp table agent_board.boards from x' ~ agent_board_test.write_re('boards')$x$, true),
|
|
270
|
+
('r36-select-into-var-from', $x$'select claim_policy into v_policy from agent_board.boards' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
271
|
+
('a35-union-compiles', $x$'update agent_board.boards set x = 1' ~ agent_board_test.re(agent_board_test.call_frag('a') || '|' || agent_board_test.write_frag('boards'))$x$, true),
|
|
272
|
+
('a43-writes-any', $x$'delete from agent_board.claims where x' ~ agent_board_test.writes_any_re(array['boards','claims'])$x$, true),
|
|
273
|
+
('r37-writes-any-empty-refused', $x$'x' ~ agent_board_test.writes_any_re(array[]::text[])$x$, 'raises: writes_any_frag: the relation list is empty'),
|
|
274
|
+
('a44-re-eq-frag', $x$agent_board_test.write_re('boards') = '(?c)' || agent_board_test.write_frag('boards')$x$, true),
|
|
275
|
+
('a45-col-write-re-eq-frag', $x$agent_board_test.col_write_re('boards','claim_policy') = '(?c)' || agent_board_test.col_write_frag('boards','claim_policy')$x$, true),
|
|
276
|
+
('a46-stmt-re-eq-frag', $x$agent_board_test.stmt_re('update','boards') = '(?c)' || agent_board_test.stmt_frag('update','boards')$x$, true),
|
|
277
|
+
('a47-writes-any-re-eq-frag', $x$agent_board_test.writes_any_re(array['boards']) = '(?c)' || agent_board_test.writes_any_frag(array['boards'])$x$, true),
|
|
278
|
+
('a48-call-re-two-eq-frag', $x$agent_board_test.call_re('acquire_claim', false) = '(?c)' || agent_board_test.call_frag('acquire_claim', false)$x$, true),
|
|
279
|
+
('a49-set-run-fold-escape', $x$'update agent_board.boards set slug = 1, claim_policy = 2 WHERE x' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
280
|
+
('r38-set-run-upper-terminator', $x$'update agent_board.boards set slug = x WHERE claim_policy = y' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
281
|
+
('r39-set-run-from-terminator', $x$'update agent_board.boards set slug = x from other o where o.claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
282
|
+
('k1-three-level-paren-KNOWN-MISS', $x$'update agent_board.boards set slug = (select (select (select 1 from a where b) from c) from d), claim_policy = 2' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
283
|
+
('a50-qname-re-bare', $x$'from claims c' ~ agent_board_test.qname_re('claims')$x$, true),
|
|
284
|
+
('a51-ins-novals-bare', $x$'insert into boards values (1)' ~ ('(?c)' || agent_board_test.insert_all_columns_frag('boards'))$x$, true),
|
|
285
|
+
('a52-merge-span-lit-semicolon', $x$'merge into agent_board.boards b using (select ''a;b'' as x) s on b.id = s.id when matched then update set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
286
|
+
('a53-merge-span-estring', $x$'merge into agent_board.boards b using (select E''a\'';b'' as x) s on b.id = s.id when matched then update set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
287
|
+
('a54-estring-guard-ident-prefix', $x$'update boards set note = line''x\'', claim_policy = 1; update other set y = ''z''' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
288
|
+
('a55-set-two-level-paren', $x$'update agent_board.boards set slug = (select (select 1 from a where b) from c), claim_policy = 2' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
289
|
+
('a56-same-stmt-estring-guard', $x$'merge into agent_board.boards b using (select line''x\'' as q, ''y'' as r) s on b.id = s.id when matched then update set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
290
|
+
('a57-write-re-bare', $x$'delete from boards where x' ~ agent_board_test.write_re('boards')$x$, true),
|
|
291
|
+
('a58-writes-any-bare', $x$'delete from boards where x' ~ agent_board_test.writes_any_re(array['claims','boards'])$x$, true),
|
|
292
|
+
('a59-set-scalar-col', $x$'update agent_board.boards set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
293
|
+
('a60-insert-collist-col', $x$'insert into agent_board.boards (id, claim_policy) values (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
294
|
+
('a61-ins-novals-via-col-write', $x$'insert into agent_board.boards values (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
295
|
+
('a62-select-into-via-col-write', $x$'select 1 into agent_board.boards' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
296
|
+
('a20c-merge-ins-novals-bare', $x$'merge into boards b using s on b.id = s.id when not matched then insert values (1)' ~ ('(?c)' || agent_board_test.merge_insert_all_columns_frag('boards'))$x$, true),
|
|
297
|
+
('a64-stmt-frag-bare', $x$'update boards set x' ~ agent_board_test.re(agent_board_test.stmt_frag('update','boards'))$x$, true),
|
|
298
|
+
('a65-call-frag-bare', $x$'perform acquire_claim(1)' ~ agent_board_test.re(agent_board_test.call_frag('acquire_claim'))$x$, true),
|
|
299
|
+
('r40-stmt-re-i-flag', $x$regexp_instr('UPDATE "AGENT_BOARD"."claims" set x', agent_board_test.stmt_re('update','claims'), 1, 1, 0, 'i') > 0$x$, false),
|
|
300
|
+
('r42-merge-col-in-next-when-pred', $x$'merge into agent_board.boards b using s on b.id = s.id when matched then update set slug = 1 when not matched by source and claim_policy = 3 then delete' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
301
|
+
('r43-re-i-flag', $x$regexp_instr('x "AGENT_BOARD"."boards" y', agent_board_test.re(agent_board_test.qname_frag('boards')), 1, 1, 0, 'i') > 0$x$, false),
|
|
302
|
+
('r44-re-groups-the-frag', $x$'a' ~ (agent_board_test.re('a|b') || 'x')$x$, false),
|
|
303
|
+
('r45-select-into-var-from-write', $x$'select 1 into v from agent_board.boards' ~ agent_board_test.write_re('boards')$x$, false),
|
|
304
|
+
('r46-write-re-i-flag', $x$regexp_instr('DELETE FROM "AGENT_BOARD"."boards"', agent_board_test.write_re('boards'), 1, 1, 0, 'i') > 0$x$, false),
|
|
305
|
+
('r47-writes-any-re-i-flag', $x$regexp_instr('DELETE FROM "AGENT_BOARD"."boards"', agent_board_test.writes_any_re(array['boards']), 1, 1, 0, 'i') > 0$x$, false),
|
|
306
|
+
('r48-col-write-re-i-flag', $x$regexp_instr('UPDATE "AGENT_BOARD"."boards" set claim_policy = 1', agent_board_test.col_write_re('boards','claim_policy'), 1, 1, 0, 'i') > 0$x$, false),
|
|
307
|
+
('r49-col-write-bare-when-required', $x$'update boards set claim_policy = 1' ~ agent_board_test.re(agent_board_test.col_write_frag('boards','claim_policy', false))$x$, false),
|
|
308
|
+
('r50-write-bare-when-required', $x$'delete from boards where x' ~ agent_board_test.re(agent_board_test.write_frag('boards', false))$x$, false),
|
|
309
|
+
('r51-writes-any-bare-when-required', $x$'delete from boards where x' ~ agent_board_test.re(agent_board_test.writes_any_frag(array['boards'], false))$x$, false),
|
|
310
|
+
('r52-merge-ins-bare-when-required', $x$'merge into boards b using s on b.id = s.id when not matched then insert values (1)' ~ agent_board_test.re(agent_board_test.merge_insert_all_columns_frag('boards', false))$x$, false),
|
|
311
|
+
('r53-ins-novals-bare-when-required', $x$'insert into boards values (1)' ~ agent_board_test.re(agent_board_test.insert_all_columns_frag('boards', false))$x$, false),
|
|
312
|
+
('a68-dotted-name-quoted-parts', $x$'update "auth"."users" set x' ~ agent_board_test.stmt_re('update','auth.users')$x$, true),
|
|
313
|
+
('r54-set-run-from-terminator-no-where', $x$'update agent_board.boards set slug = x from other o join p on claim_policy = o.id' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
314
|
+
('r55-set-run-returning-terminator-eq', $x$'update agent_board.boards set slug = x returning claim_policy = 1 as is_one' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
315
|
+
('r56-qname-prefix-word', $x$'from dashboards d' ~ agent_board_test.qname_re('boards')$x$, false),
|
|
316
|
+
('r57-same-stmt-estring-guard', $x$'merge into agent_board.boards b using (select line''a\'' as q, ''b'' as r, ''c'' as t, ''d) s on b.id = s.id when matched then update set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
317
|
+
('r58-set-run-estring-guard', $x$'update agent_board.boards set note = line''a\'', slug = ''b'', x = ''c'', y = ''d claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
318
|
+
('r59-dotted-name-dot-is-literal', $x$'update authxusers set x' ~ agent_board_test.stmt_re('update','auth.users')$x$, false),
|
|
319
|
+
('a70-row-lock-for-update', $x$'select 1 from agent_board.claims c where c.id = x for update' ~ agent_board_test.row_lock_re('claims')$x$, true),
|
|
320
|
+
('a71-row-lock-no-key-update', $x$'select 1 from claims for no key update of c' ~ agent_board_test.row_lock_re('claims')$x$, true),
|
|
321
|
+
('a72-row-lock-key-share', $x$'select 1 from "agent_board"."claims" FOR KEY SHARE' ~ agent_board_test.row_lock_re('claims')$x$, true),
|
|
322
|
+
('a73-row-lock-share', $x$'select 1 from claims for share nowait' ~ agent_board_test.row_lock_re('claims')$x$, true),
|
|
323
|
+
('r60-row-lock-in-literal', $x$'select 1 from agent_board.claims where note = ''for update''' ~ agent_board_test.row_lock_re('claims')$x$, false),
|
|
324
|
+
('r61-row-lock-next-stmt', $x$'select 1 from agent_board.claims; select 1 from boards for update' ~ agent_board_test.row_lock_re('claims')$x$, false),
|
|
325
|
+
('r62-row-lock-suffix-word', $x$'select 1 from agent_board.claims for updates' ~ agent_board_test.row_lock_re('claims')$x$, false),
|
|
326
|
+
('r63-row-lock-other-rel', $x$'select 1 from agent_board.boards for update' ~ agent_board_test.row_lock_re('claims')$x$, false),
|
|
327
|
+
('r64-row-lock-prefix-word', $x$'select 1 from agent_board.claims therefor update' ~ agent_board_test.row_lock_re('claims')$x$, false),
|
|
328
|
+
-- Consiliency/message-board#435 codex round 1: an identifier that ENDS in a verb is not the verb.
|
|
329
|
+
('r65-verb-prefix-word', $x$'xupdate agent_board.boards set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
330
|
+
-- Consiliency/message-board#435 codex round 2: the ON CONFLICT upsert assigns the column too.
|
|
331
|
+
('a74-upsert-set-col', $x$'insert into agent_board.boards (id) values (1) on conflict (id) do update set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
332
|
+
('a74b-upsert-set-row-form', $x$'insert into boards (id) values (1) on conflict (id) do update set (slug, claim_policy) = (1, 2)' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
333
|
+
('a74c-upsert-excluded-rhs', $x$'insert into agent_board.boards as b (id) values (1) on conflict on constraint boards_pkey do update set slug = excluded.slug, claim_policy = excluded.claim_policy' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, true),
|
|
334
|
+
('r66-upsert-col-in-where', $x$'insert into agent_board.boards (id) values (1) on conflict (id) do update set slug = 1 where claim_policy = 2' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
335
|
+
('r66b-upsert-do-nothing', $x$'insert into agent_board.boards (id) values (1) on conflict (id) do nothing returning claim_policy' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
336
|
+
-- and the same seat's `"b;x"`: a `;` inside a DOUBLE-QUOTED identifier ends same_stmt()'s span.
|
|
337
|
+
-- Labelled, not fixed: no live body spells an identifier that way, and closing it means a
|
|
338
|
+
-- quoted-identifier arm in same_stmt AND set_run AND every one of their eleven revert bodies.
|
|
339
|
+
('k2-quoted-ident-semicolon-KNOWN-MISS', $x$'update boards as "b;x" set claim_policy = 1' ~ agent_board_test.col_write_re('boards','claim_policy')$x$, false),
|
|
340
|
+
-- col_ref_frag: a column READ, qualified or bare (#417 PR 3). Each arm has its own row so that a
|
|
341
|
+
-- revert dropping one arm is named by one row, not concealed by the other.
|
|
342
|
+
('a75-colref-qualified', $x$'where bm.membership_state in (1)' ~ ('(?c)' || agent_board_test.col_ref_frag('membership_state'))$x$, true),
|
|
343
|
+
('a75b-colref-qualified-quoted-alias', $x$'order by "M" . created_at desc' ~ ('(?c)' || agent_board_test.col_ref_frag('created_at'))$x$, true),
|
|
344
|
+
('a76-colref-bare', $x$'set membership_state = 1' ~ ('(?c)' || agent_board_test.col_ref_frag('membership_state'))$x$, true),
|
|
345
|
+
('r67-colref-suffix-word', $x$'where bm.membership_states in (1)' ~ ('(?c)' || agent_board_test.col_ref_frag('membership_state'))$x$, false),
|
|
346
|
+
('r67b-colref-prefix-word', $x$'where bm.xmembership_state in (1) or xmembership_state = 2' ~ ('(?c)' || agent_board_test.col_ref_frag('membership_state'))$x$, false);
|
|
347
|
+
|
|
348
|
+
insert into _mrow(label, expr, want, costly) values
|
|
349
|
+
('a66-lock-edges-quoted-callee', $x$exists (select 1 from agent_board_test.lock_edges() where fn_name = '_probe416_quoted_callee' and rel_name = 'agent_board.claims' and lock_mode = 'update' and via = 'callee:acquire_claim')$x$, true, true),
|
|
350
|
+
('a67-closure-quoted-callee', $x$exists (select 1 from agent_board_test.fn_call_closure() where root_name = '_probe416_quoted_callee' and reached_name = 'acquire_claim')$x$, true, true);
|
|
351
|
+
|
|
352
|
+
create function pg_temp.mverdict(p_expr text) returns text
|
|
353
|
+
language plpgsql as $f$
|
|
354
|
+
declare v boolean;
|
|
355
|
+
begin
|
|
356
|
+
execute format('select (%s)', p_expr) into v;
|
|
357
|
+
return coalesce(v::text, 'null');
|
|
358
|
+
exception when others then
|
|
359
|
+
-- A row whose expression cannot compile, or whose matcher raises, reports the MESSAGE. A row that
|
|
360
|
+
-- expects a raise names the message it expects, so a raise for the wrong reason still fails; a
|
|
361
|
+
-- row that expects true/false cannot be satisfied by any raise.
|
|
362
|
+
return 'raises: ' || sqlerrm;
|
|
363
|
+
end $f$;
|
|
364
|
+
|
|
365
|
+
select is(
|
|
366
|
+
(select coalesce(string_agg(label || '=' || pg_temp.mverdict(expr), ', ' order by label), '<none>')
|
|
367
|
+
from _mrow),
|
|
368
|
+
(select coalesce(string_agg(label || '=' || want, ', ' order by label), '<none>') from _mrow),
|
|
369
|
+
'CPR-0 MATCHER BEHAVIOUR: over PLAIN and DOUBLE-QUOTED identifiers, the matchers accept the spellings PostgreSQL resolves to our object and reject those it resolves elsewhere; a write matcher sees only the STATEMENT it started (literals and comments are spans, not boundaries), an assignment matcher sees only the SET list, and the write shapes (INSERT without a column list, MERGE ... THEN INSERT, COPY FROM, SELECT INTO, multi-relation TRUNCATE) are each a named row — Unicode-escape identifiers (U&"...") are OUT OF SCOPE and unhandled, three-level SET-list parentheses (k1) and a `;` inside a double-quoted identifier (k2) are labelled KNOWN-MISSes, so this is not a claim about every spelling');
|
|
370
|
+
|
|
371
|
+
-- CPR-0a. EVERY RULE HAS A ROW THAT FAILS WITHOUT IT — compiled by the database, not by hand.
|
|
372
|
+
-- ONE BEHAVIOUR PER ENTRY, AND EACH NAMES THE ROW THAT MUST FLIP. Both are corrections.
|
|
373
|
+
--
|
|
374
|
+
-- THE SPLIT PAID FOR ITSELF ON ITS FIRST RUN, which is the answer to "why are these so finely
|
|
375
|
+
-- divided": separating the behaviours immediately exposed FOUR matcher rules the coarser hand list
|
|
376
|
+
-- had never reverted — ident_re's quoted alternative, qname_frag's OPTIONAL qualifier, call_re's own
|
|
377
|
+
-- director, and stmt_re's `\s+` between verb and relation. Two of them had no row at all (a18, r16
|
|
378
|
+
-- were added for them). A reviewer tempted to recombine entries should read that as the cost.
|
|
379
|
+
--
|
|
380
|
+
-- NO DESCRIPTION CARRIES A COUNT, and the rule that replaced is worth recording because it was
|
|
381
|
+
-- WRONG in a way that took a reviewer to see. It said a count is "safe" when it sits beside its own
|
|
382
|
+
-- assertion's expected value, since the two would fail together. They do not. PROXIMITY IS NOT
|
|
383
|
+
-- DERIVATION: nothing computes the count from the expected value, so the assertion fails on drift
|
|
384
|
+
-- only until a maintainer updates the expected value — which is the normal maintenance path — and
|
|
385
|
+
-- from that moment the count is silently wrong with nothing failing.
|
|
386
|
+
--
|
|
387
|
+
-- Measured rather than argued: add a function to agent_board_test, and CPR-0b fails; update its
|
|
388
|
+
-- expected value the way a maintainer would, and it passes again while any count in its prose is now
|
|
389
|
+
-- false. So the "safe" category did not exist. There are only counts that are easier to notice and
|
|
390
|
+
-- counts that are harder, and the durable move is the one this package already made when its runner
|
|
391
|
+
-- dropped "28 tests" and "42-migration union": delete them. The expected values enumerate every
|
|
392
|
+
-- member by name; a reader who wants a number can count one that cannot drift.
|
|
393
|
+
--
|
|
394
|
+
-- THE RECURSION IS THE POINT, not an aside: the rule written to prevent stale counts made a claim
|
|
395
|
+
-- broader than its own mechanism supported — the same shape as every finding in this review, in the
|
|
396
|
+
-- sentence codifying the lesson from the previous one.
|
|
397
|
+
--
|
|
398
|
+
-- IN THESE TWO ASSERTIONS the expected values enumerate every entry by name, so no count appears in
|
|
399
|
+
-- either description: it would add nothing a reader cannot see and one more thing that can go stale.
|
|
400
|
+
--
|
|
401
|
+
-- COMPOUND ENTRIES LET A COVERED BRANCH CONCEAL AN UNCOVERED SIBLING: `keyword exclusions` reverted
|
|
402
|
+
-- VALUES and SELECT together, and `lead_guard` reverted its dot-whitespace and non-ASCII guards
|
|
403
|
+
-- together, so deleting one row still left the rule reporting as gated through the other. That is
|
|
404
|
+
-- the r06 defect at the RULE level — the map certifying a rule it only half tests.
|
|
405
|
+
--
|
|
406
|
+
-- AND "SOME ROW FLIPPED" IS NOT "THE ROW FOR THIS RULE FLIPPED". The gate now requires the NAMED
|
|
407
|
+
-- row, so a rule cannot borrow coverage from an unrelated one.
|
|
408
|
+
-- ONE RULE PER SIGNATURE, INCLUDING EACH DELEGATE (Consiliency/message-board#416 PR 1). The family
|
|
409
|
+
-- now has 2-arity cores (`…(name, p_bare_ok)`) and 1-arity delegates that pass `true`. A revert of
|
|
410
|
+
-- a delegate no longer touches a path that goes through the core, so the rules below target the
|
|
411
|
+
-- CORE for a behaviour and the DELEGATE for exactly one thing — that it passes `true` — with a row
|
|
412
|
+
-- that calls the delegate directly. `fn` is a regprocedure spelling; CPR-0b keys on the OID, so a
|
|
413
|
+
-- rule that names a signature which does not exist fails there rather than silently gating nothing.
|
|
414
|
+
-- The revert must recreate the SAME signature: a revert that dropped or narrowed one would be
|
|
415
|
+
-- restored by CPR-0a's `pg_get_functiondef` round-trip and read as gated.
|
|
416
|
+
create temp table _mrule (rule_name text primary key, fn text, expect_row text, revert_sql text) on commit drop;
|
|
417
|
+
|
|
418
|
+
insert into _mrule(rule_name, fn, expect_row, revert_sql) values
|
|
419
|
+
('ci_re: a space becomes \s+', 'agent_board_test.ci_re(text)', 'a08-cire-phrase', $r$
|
|
420
|
+
create or replace function agent_board_test.ci_re(p_text text) returns text
|
|
421
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
422
|
+
select coalesce(string_agg(case when c ~ '[A-Za-z]' then '[' || upper(c) || lower(c) || ']' else c end, ''), '')
|
|
423
|
+
from regexp_split_to_table(p_text, '') as c $b$; $r$),
|
|
424
|
+
('ident_chars: $ is an identifier character', 'agent_board_test.ident_chars()', 'r05-dollar-lead', $r$
|
|
425
|
+
create or replace function agent_board_test.ident_chars() returns text
|
|
426
|
+
language sql immutable set search_path to pg_catalog as $b$ select 'A-Za-z0-9_' $b$; $r$),
|
|
427
|
+
('ident_re: the quoted alternative', 'agent_board_test.ident_re(text)', 'a04-quoted-lower', $r$
|
|
428
|
+
create or replace function agent_board_test.ident_re(p_name text) returns text
|
|
429
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
430
|
+
select '(?:' || agent_board_test.ci_re(p_name) || ')' $b$; $r$),
|
|
431
|
+
('ident_any_frag: the quoted alternative', 'agent_board_test.ident_any_frag()', 'a21b-three-part-quoted-db', $r$
|
|
432
|
+
create or replace function agent_board_test.ident_any_frag() returns text
|
|
433
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
434
|
+
select '(?:[A-Za-z_][' || agent_board_test.ident_chars() || ']*)' $b$; $r$),
|
|
435
|
+
('lead_guard: reject a dot with whitespace', 'agent_board_test.lead_guard()', 'r02-dot-space', $r$
|
|
436
|
+
create or replace function agent_board_test.lead_guard() returns text
|
|
437
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
438
|
+
select '(?<![' || agent_board_test.ident_chars() || '."])(?<![^\x00-\x7F])' $b$; $r$),
|
|
439
|
+
('lead_guard: reject a leading non-ASCII letter', 'agent_board_test.lead_guard()', 'r07-nonascii-lead', $r$
|
|
440
|
+
create or replace function agent_board_test.lead_guard() returns text
|
|
441
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
442
|
+
select '(?<![' || agent_board_test.ident_chars() || '."])(?<!\.\s*)' $b$; $r$),
|
|
443
|
+
('lead_guard: reject a leading identifier character', 'agent_board_test.lead_guard()', 'r56-qname-prefix-word', $r$
|
|
444
|
+
create or replace function agent_board_test.lead_guard() returns text
|
|
445
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
446
|
+
select '(?<![."])(?<![^\x00-\x7F])(?<!\.\s*)' $b$; $r$),
|
|
447
|
+
('trail_guard: reject a trailing non-ASCII letter', 'agent_board_test.trail_guard()', 'r08-nonascii-trail', $r$
|
|
448
|
+
create or replace function agent_board_test.trail_guard() returns text
|
|
449
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
450
|
+
select '(?![' || agent_board_test.ident_chars() || '])' $b$; $r$),
|
|
451
|
+
('trail_guard: reject a trailing identifier character', 'agent_board_test.trail_guard()', 'r23-write-suffix-word', $r$
|
|
452
|
+
create or replace function agent_board_test.trail_guard() returns text
|
|
453
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
454
|
+
select '(?![^\x00-\x7F])' $b$; $r$),
|
|
455
|
+
('same_stmt: a literal is a span, not a boundary', 'agent_board_test.same_stmt()', 'a52-merge-span-lit-semicolon', $r$
|
|
456
|
+
create or replace function agent_board_test.same_stmt() returns text
|
|
457
|
+
language sql immutable set search_path to pg_catalog as $b$ select '[^;]*' $b$; $r$),
|
|
458
|
+
('same_stmt: the E-string arm', 'agent_board_test.same_stmt()', 'a53-merge-span-estring', $r$
|
|
459
|
+
create or replace function agent_board_test.same_stmt() returns text
|
|
460
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
461
|
+
select $s$(?:[^;']|'(?:[^']|'')*')*$s$ $b$; $r$),
|
|
462
|
+
('same_stmt: E must be a standalone token', 'agent_board_test.same_stmt()', 'r57-same-stmt-estring-guard', $r$
|
|
463
|
+
create or replace function agent_board_test.same_stmt() returns text
|
|
464
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
465
|
+
select $s$(?:[^;']|[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*')*$s$ $b$; $r$),
|
|
466
|
+
-- set_run: every revert keeps the CTE shape so that the one changed piece is the behaviour named.
|
|
467
|
+
('set_run: a terminator ends the run (the lookahead)', 'agent_board_test.set_run(text)', 'r25-col-in-where', $r$
|
|
468
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
469
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
470
|
+
with lit as (select $s$(?<![A-Za-z0-9_$])[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*'$s$ as l),
|
|
471
|
+
p0 as (select '\((?:[^();'']|' || l || ')*\)' as p from lit),
|
|
472
|
+
p1 as (select '\((?:[^();'']|' || lit.l || '|' || p0.p || ')*\)' as p from lit, p0)
|
|
473
|
+
select '(?:(?:[^;''()]|' || lit.l || '|' || p1.p || '))*' from lit, p1 $b$; $r$),
|
|
474
|
+
('set_run: a parenthesised span is whole', 'agent_board_test.set_run(text)', 'a26-set-subquery-then-col', $r$
|
|
475
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
476
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
477
|
+
with chars as (select c, ord, coalesce(lag(c) over (order by ord), '') as prev
|
|
478
|
+
from regexp_split_to_table(p_terms, '') with ordinality as x(c, ord)),
|
|
479
|
+
t as (select string_agg(case when c ~ '[A-Za-z]' and prev <> '\' then '[' || upper(c) || lower(c) || ']' else c end, '' order by ord) as folded from chars),
|
|
480
|
+
lit as (select $s$(?<![A-Za-z0-9_$])[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*'$s$ as l)
|
|
481
|
+
select '(?:(?!\m(?:' || t.folded || ')\M)(?:[^;'']|' || lit.l || '))*' from t, lit $b$; $r$),
|
|
482
|
+
('set_run: parentheses nest two levels', 'agent_board_test.set_run(text)', 'a55-set-two-level-paren', $r$
|
|
483
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
484
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
485
|
+
with chars as (select c, ord, coalesce(lag(c) over (order by ord), '') as prev
|
|
486
|
+
from regexp_split_to_table(p_terms, '') with ordinality as x(c, ord)),
|
|
487
|
+
t as (select string_agg(case when c ~ '[A-Za-z]' and prev <> '\' then '[' || upper(c) || lower(c) || ']' else c end, '' order by ord) as folded from chars),
|
|
488
|
+
lit as (select $s$(?<![A-Za-z0-9_$])[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*'$s$ as l),
|
|
489
|
+
p0 as (select '\((?:[^();'']|' || l || ')*\)' as p from lit)
|
|
490
|
+
select '(?:(?!\m(?:' || t.folded || ')\M)(?:[^;''()]|' || lit.l || '|' || p0.p || '))*' from t, lit, p0 $b$; $r$),
|
|
491
|
+
('set_run: terminator letters are case-folded', 'agent_board_test.set_run(text)', 'r38-set-run-upper-terminator', $r$
|
|
492
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
493
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
494
|
+
with lit as (select $s$(?<![A-Za-z0-9_$])[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*'$s$ as l),
|
|
495
|
+
p0 as (select '\((?:[^();'']|' || l || ')*\)' as p from lit),
|
|
496
|
+
p1 as (select '\((?:[^();'']|' || lit.l || '|' || p0.p || ')*\)' as p from lit, p0)
|
|
497
|
+
select '(?:(?!\m(?:' || p_terms || ')\M)(?:[^;''()]|' || lit.l || '|' || p1.p || '))*' from lit, p1 $b$; $r$),
|
|
498
|
+
('set_run: a backslash-escaped letter is NOT folded', 'agent_board_test.set_run(text)', 'r42-merge-col-in-next-when-pred', $r$
|
|
499
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
500
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
501
|
+
with t as (select string_agg(case when c ~ '[A-Za-z]' then '[' || upper(c) || lower(c) || ']' else c end, '') as folded
|
|
502
|
+
from regexp_split_to_table(p_terms, '') as c),
|
|
503
|
+
lit as (select $s$(?<![A-Za-z0-9_$])[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*'$s$ as l),
|
|
504
|
+
p0 as (select '\((?:[^();'']|' || l || ')*\)' as p from lit),
|
|
505
|
+
p1 as (select '\((?:[^();'']|' || lit.l || '|' || p0.p || ')*\)' as p from lit, p0)
|
|
506
|
+
select '(?:(?!\m(?:' || t.folded || ')\M)(?:[^;''()]|' || lit.l || '|' || p1.p || '))*' from t, lit, p1 $b$; $r$),
|
|
507
|
+
('set_run: a literal in the SET list is a span', 'agent_board_test.set_run(text)', 'a19-span-lit-semicolon', $r$
|
|
508
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
509
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
510
|
+
with chars as (select c, ord, coalesce(lag(c) over (order by ord), '') as prev
|
|
511
|
+
from regexp_split_to_table(p_terms, '') with ordinality as x(c, ord)),
|
|
512
|
+
t as (select string_agg(case when c ~ '[A-Za-z]' and prev <> '\' then '[' || upper(c) || lower(c) || ']' else c end, '' order by ord) as folded from chars),
|
|
513
|
+
p0 as (select '\((?:[^();])*\)' as p),
|
|
514
|
+
p1 as (select '\((?:[^();]|' || p0.p || ')*\)' as p from p0)
|
|
515
|
+
select '(?:(?!\m(?:' || t.folded || ')\M)(?:[^;()]|' || p1.p || '))*' from t, p1 $b$; $r$),
|
|
516
|
+
('set_run: the E-string arm', 'agent_board_test.set_run(text)', 'a28-span-estring', $r$
|
|
517
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
518
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
519
|
+
with chars as (select c, ord, coalesce(lag(c) over (order by ord), '') as prev
|
|
520
|
+
from regexp_split_to_table(p_terms, '') with ordinality as x(c, ord)),
|
|
521
|
+
t as (select string_agg(case when c ~ '[A-Za-z]' and prev <> '\' then '[' || upper(c) || lower(c) || ']' else c end, '' order by ord) as folded from chars),
|
|
522
|
+
lit as (select $s$'(?:[^']|'')*'$s$ as l),
|
|
523
|
+
p0 as (select '\((?:[^();'']|' || l || ')*\)' as p from lit),
|
|
524
|
+
p1 as (select '\((?:[^();'']|' || lit.l || '|' || p0.p || ')*\)' as p from lit, p0)
|
|
525
|
+
select '(?:(?!\m(?:' || t.folded || ')\M)(?:[^;''()]|' || lit.l || '|' || p1.p || '))*' from t, lit, p1 $b$; $r$),
|
|
526
|
+
('set_run: E must be a standalone token', 'agent_board_test.set_run(text)', 'r58-set-run-estring-guard', $r$
|
|
527
|
+
create or replace function agent_board_test.set_run(p_terms text) returns text
|
|
528
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
529
|
+
with chars as (select c, ord, coalesce(lag(c) over (order by ord), '') as prev
|
|
530
|
+
from regexp_split_to_table(p_terms, '') with ordinality as x(c, ord)),
|
|
531
|
+
t as (select string_agg(case when c ~ '[A-Za-z]' and prev <> '\' then '[' || upper(c) || lower(c) || ']' else c end, '' order by ord) as folded from chars),
|
|
532
|
+
lit as (select $s$[Ee]'(?:[^'\\]|\\.|'')*'|'(?:[^']|'')*'$s$ as l),
|
|
533
|
+
p0 as (select '\((?:[^();'']|' || l || ')*\)' as p from lit),
|
|
534
|
+
p1 as (select '\((?:[^();'']|' || lit.l || '|' || p0.p || ')*\)' as p from lit, p0)
|
|
535
|
+
select '(?:(?!\m(?:' || t.folded || ')\M)(?:[^;''()]|' || lit.l || '|' || p1.p || '))*' from t, lit, p1 $b$; $r$),
|
|
536
|
+
('re: the (?c) director', 'agent_board_test.re(text)', 'r43-re-i-flag', $r$
|
|
537
|
+
create or replace function agent_board_test.re(p_frag text) returns text
|
|
538
|
+
language sql immutable set search_path to pg_catalog as $b$ select '(?:' || p_frag || ')' $b$; $r$),
|
|
539
|
+
('re: the frag is grouped', 'agent_board_test.re(text)', 'r44-re-groups-the-frag', $r$
|
|
540
|
+
create or replace function agent_board_test.re(p_frag text) returns text
|
|
541
|
+
language sql immutable set search_path to pg_catalog as $b$ select '(?c)' || p_frag $b$; $r$),
|
|
542
|
+
('qname_frag(core): the qualifier is OPTIONAL when bare is ok', 'agent_board_test.qname_frag(text,boolean)', 'a02-unqualified', $r$
|
|
543
|
+
create or replace function agent_board_test.qname_frag(p_name text, p_bare_ok boolean) returns text
|
|
544
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
545
|
+
select agent_board_test.lead_guard()
|
|
546
|
+
|| '(?:(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*)?'
|
|
547
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 1) else 'agent_board' end)
|
|
548
|
+
|| '\s*\.\s*)'
|
|
549
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 2) else p_name end)
|
|
550
|
+
|| agent_board_test.trail_guard() $b$; $r$),
|
|
551
|
+
('qname_frag(core): the qualifier is REQUIRED when bare is not ok', 'agent_board_test.qname_frag(text,boolean)', 'r24-bare-when-required', $r$
|
|
552
|
+
create or replace function agent_board_test.qname_frag(p_name text, p_bare_ok boolean) returns text
|
|
553
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
554
|
+
select agent_board_test.lead_guard()
|
|
555
|
+
|| '(?:(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*)?'
|
|
556
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 1) else 'agent_board' end)
|
|
557
|
+
|| '\s*\.\s*)?'
|
|
558
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 2) else p_name end)
|
|
559
|
+
|| agent_board_test.trail_guard() $b$; $r$),
|
|
560
|
+
('qname_frag(core): an optional database component', 'agent_board_test.qname_frag(text,boolean)', 'a21-three-part-name', $r$
|
|
561
|
+
create or replace function agent_board_test.qname_frag(p_name text, p_bare_ok boolean) returns text
|
|
562
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
563
|
+
select agent_board_test.lead_guard()
|
|
564
|
+
|| '(?:' || agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 1) else 'agent_board' end)
|
|
565
|
+
|| '\s*\.\s*)' || case when p_bare_ok then '?' else '' end
|
|
566
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 2) else p_name end)
|
|
567
|
+
|| agent_board_test.trail_guard() $b$; $r$),
|
|
568
|
+
('qname_frag(core): the database component is bound to the schema', 'agent_board_test.qname_frag(text,boolean)', 'r01-other-schema', $r$
|
|
569
|
+
create or replace function agent_board_test.qname_frag(p_name text, p_bare_ok boolean) returns text
|
|
570
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
571
|
+
select agent_board_test.lead_guard()
|
|
572
|
+
|| '(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*)?(?:'
|
|
573
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 1) else 'agent_board' end)
|
|
574
|
+
|| '\s*\.\s*)' || case when p_bare_ok then '?' else '' end
|
|
575
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 2) else p_name end)
|
|
576
|
+
|| agent_board_test.trail_guard() $b$; $r$),
|
|
577
|
+
('qname_frag(core): a dotted name is split at the dot', 'agent_board_test.qname_frag(text,boolean)', 'a68-dotted-name-quoted-parts', $r$
|
|
578
|
+
create or replace function agent_board_test.qname_frag(p_name text, p_bare_ok boolean) returns text
|
|
579
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
580
|
+
select agent_board_test.lead_guard()
|
|
581
|
+
|| '(?:(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*)?'
|
|
582
|
+
|| agent_board_test.ident_re('agent_board')
|
|
583
|
+
|| '\s*\.\s*)' || case when p_bare_ok then '?' else '' end
|
|
584
|
+
|| agent_board_test.ident_re(p_name)
|
|
585
|
+
|| agent_board_test.trail_guard() $b$; $r$),
|
|
586
|
+
('qname_frag(delegate): bare is ok', 'agent_board_test.qname_frag(text)', 'a50-qname-re-bare', $r$
|
|
587
|
+
create or replace function agent_board_test.qname_frag(p_name text) returns text
|
|
588
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.qname_frag(p_name, false) $b$; $r$),
|
|
589
|
+
('qname_re: the (?c) director', 'agent_board_test.qname_re(text)', 'r11-i-flag', $r$
|
|
590
|
+
create or replace function agent_board_test.qname_re(p_name text) returns text
|
|
591
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.qname_frag(p_name) $b$; $r$),
|
|
592
|
+
('call_frag(core): whitespace may precede the paren', 'agent_board_test.call_frag(text,boolean)', 'a10-call-space', $r$
|
|
593
|
+
create or replace function agent_board_test.call_frag(p_name text, p_bare_ok boolean) returns text
|
|
594
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
595
|
+
select agent_board_test.lead_guard()
|
|
596
|
+
|| '(?:(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*)?'
|
|
597
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 1) else 'agent_board' end)
|
|
598
|
+
|| '\s*\.\s*)' || case when p_bare_ok then '?' else '' end
|
|
599
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 2) else p_name end)
|
|
600
|
+
|| '\(' $b$; $r$),
|
|
601
|
+
('call_frag(core): the qualifier is REQUIRED when bare is not ok', 'agent_board_test.call_frag(text,boolean)', 'r31-bare-callee-when-required', $r$
|
|
602
|
+
create or replace function agent_board_test.call_frag(p_name text, p_bare_ok boolean) returns text
|
|
603
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
604
|
+
select agent_board_test.lead_guard()
|
|
605
|
+
|| '(?:(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*)?'
|
|
606
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 1) else 'agent_board' end)
|
|
607
|
+
|| '\s*\.\s*)?'
|
|
608
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 2) else p_name end)
|
|
609
|
+
|| '\s*\(' $b$; $r$),
|
|
610
|
+
('call_frag(core): the callee may be quoted', 'agent_board_test.call_frag(text,boolean)', 'a29-quoted-callee', $r$
|
|
611
|
+
create or replace function agent_board_test.call_frag(p_name text, p_bare_ok boolean) returns text
|
|
612
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
613
|
+
select agent_board_test.lead_guard()
|
|
614
|
+
|| '(?:(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*)?'
|
|
615
|
+
|| agent_board_test.ident_re(case when p_name like '%.%' then split_part(p_name, '.', 1) else 'agent_board' end)
|
|
616
|
+
|| '\s*\.\s*)' || case when p_bare_ok then '?' else '' end
|
|
617
|
+
|| agent_board_test.ci_re(case when p_name like '%.%' then split_part(p_name, '.', 2) else p_name end)
|
|
618
|
+
|| '\s*\(' $b$; $r$),
|
|
619
|
+
('call_frag(delegate): bare is ok', 'agent_board_test.call_frag(text)', 'a65-call-frag-bare', $r$
|
|
620
|
+
create or replace function agent_board_test.call_frag(p_name text) returns text
|
|
621
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.call_frag(p_name, false) $b$; $r$),
|
|
622
|
+
('call_re(core): the (?c) director', 'agent_board_test.call_re(text,boolean)', 'r16-call-i-flag', $r$
|
|
623
|
+
create or replace function agent_board_test.call_re(p_name text, p_bare_ok boolean) returns text
|
|
624
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.call_frag(p_name, p_bare_ok) $b$; $r$),
|
|
625
|
+
('call_re(core): the flag is passed through', 'agent_board_test.call_re(text,boolean)', 'a48-call-re-two-eq-frag', $r$
|
|
626
|
+
create or replace function agent_board_test.call_re(p_name text, p_bare_ok boolean) returns text
|
|
627
|
+
language sql immutable set search_path to pg_catalog as $b$ select '(?c)' || agent_board_test.call_frag(p_name, true) $b$; $r$),
|
|
628
|
+
('call_re(delegate): bare is ok', 'agent_board_test.call_re(text)', 'a10-call-space', $r$
|
|
629
|
+
create or replace function agent_board_test.call_re(p_name text) returns text
|
|
630
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.call_re(p_name, false) $b$; $r$),
|
|
631
|
+
('col_frag: the column token carries identifier rules', 'agent_board_test.col_frag(text)', 'a12-col-upper', $r$
|
|
632
|
+
create or replace function agent_board_test.col_frag(p_name text) returns text
|
|
633
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
634
|
+
select agent_board_test.lead_guard() || p_name || agent_board_test.trail_guard() $b$; $r$),
|
|
635
|
+
('col_ref_frag: the qualified arm', 'agent_board_test.col_ref_frag(text)', 'a75-colref-qualified', $r$
|
|
636
|
+
create or replace function agent_board_test.col_ref_frag(p_name text) returns text
|
|
637
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
638
|
+
select '(?:' || agent_board_test.col_frag(p_name) || ')' $b$; $r$),
|
|
639
|
+
('col_ref_frag: the bare arm', 'agent_board_test.col_ref_frag(text)', 'a76-colref-bare', $r$
|
|
640
|
+
create or replace function agent_board_test.col_ref_frag(p_name text) returns text
|
|
641
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
642
|
+
select '(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*' || agent_board_test.ident_re(p_name) || agent_board_test.trail_guard() || ')' $b$; $r$),
|
|
643
|
+
('col_ref_frag: the qualified arm ends at an identifier boundary', 'agent_board_test.col_ref_frag(text)', 'r67-colref-suffix-word', $r$
|
|
644
|
+
create or replace function agent_board_test.col_ref_frag(p_name text) returns text
|
|
645
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
646
|
+
select '(?:' || agent_board_test.ident_any_frag() || '\s*\.\s*' || agent_board_test.ident_re(p_name)
|
|
647
|
+
|| '|' || agent_board_test.col_frag(p_name) || ')' $b$; $r$),
|
|
648
|
+
('stmt_frag(core): \s+ between the verb and the relation', 'agent_board_test.stmt_frag(text,text,boolean)', 'a18-verb-rel-spaces', $r$
|
|
649
|
+
create or replace function agent_board_test.stmt_frag(p_verbs text, p_rel text, p_bare_ok boolean) returns text
|
|
650
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
651
|
+
select '\m(?:' || (select string_agg(agent_board_test.ci_re(v), '|') from regexp_split_to_table(p_verbs, '\|') as v)
|
|
652
|
+
|| ') (?:' || agent_board_test.ci_re('only') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok) $b$; $r$),
|
|
653
|
+
('stmt_frag(core): the flag reaches the relation', 'agent_board_test.stmt_frag(text,text,boolean)', 'r24-bare-when-required', $r$
|
|
654
|
+
create or replace function agent_board_test.stmt_frag(p_verbs text, p_rel text, p_bare_ok boolean) returns text
|
|
655
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
656
|
+
select '\m(?:' || (select string_agg(agent_board_test.ci_re(v), '|') from regexp_split_to_table(p_verbs, '\|') as v)
|
|
657
|
+
|| ')\s+(?:' || agent_board_test.ci_re('only') || '\s+)?' || agent_board_test.qname_frag(p_rel, true) $b$; $r$),
|
|
658
|
+
('stmt_frag(core): the verb starts a word', 'agent_board_test.stmt_frag(text,text,boolean)', 'r65-verb-prefix-word', $r$
|
|
659
|
+
create or replace function agent_board_test.stmt_frag(p_verbs text, p_rel text, p_bare_ok boolean) returns text
|
|
660
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
661
|
+
select '(?:' || (select string_agg(agent_board_test.ci_re(v), '|') from regexp_split_to_table(p_verbs, '\|') as v)
|
|
662
|
+
|| ')\s+(?:' || agent_board_test.ci_re('only') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok) $b$; $r$),
|
|
663
|
+
('stmt_frag(delegate): bare is ok', 'agent_board_test.stmt_frag(text,text)', 'a64-stmt-frag-bare', $r$
|
|
664
|
+
create or replace function agent_board_test.stmt_frag(p_verbs text, p_rel text) returns text
|
|
665
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.stmt_frag(p_verbs, p_rel, false) $b$; $r$),
|
|
666
|
+
('stmt_re(core): the (?c) director', 'agent_board_test.stmt_re(text,text,boolean)', 'r40-stmt-re-i-flag', $r$
|
|
667
|
+
create or replace function agent_board_test.stmt_re(p_verbs text, p_rel text, p_bare_ok boolean) returns text
|
|
668
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.stmt_frag(p_verbs, p_rel, p_bare_ok) $b$; $r$),
|
|
669
|
+
('stmt_re(core): the flag is passed through', 'agent_board_test.stmt_re(text,text,boolean)', 'r24-bare-when-required', $r$
|
|
670
|
+
create or replace function agent_board_test.stmt_re(p_verbs text, p_rel text, p_bare_ok boolean) returns text
|
|
671
|
+
language sql immutable set search_path to pg_catalog as $b$ select '(?c)' || agent_board_test.stmt_frag(p_verbs, p_rel, true) $b$; $r$),
|
|
672
|
+
('stmt_re(delegate): bare is ok', 'agent_board_test.stmt_re(text,text)', 'a02-unqualified', $r$
|
|
673
|
+
create or replace function agent_board_test.stmt_re(p_verbs text, p_rel text) returns text
|
|
674
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.stmt_re(p_verbs, p_rel, false) $b$; $r$),
|
|
675
|
+
('insert rule(core): the VALUES exclusion', 'agent_board_test.insert_all_columns_frag(text,boolean)', 'a13-ins-novals', $r$
|
|
676
|
+
create or replace function agent_board_test.insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
677
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
678
|
+
select agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
679
|
+
|| '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
|
|
680
|
+
|| agent_board_test.ci_re('select') || '(?![A-Za-z0-9_$]))'
|
|
681
|
+
|| agent_board_test.ident_any_frag() || ')?\s*\()' $b$; $r$),
|
|
682
|
+
('insert rule(core): the SELECT exclusion', 'agent_board_test.insert_all_columns_frag(text,boolean)', 'a14-ins-select', $r$
|
|
683
|
+
create or replace function agent_board_test.insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
684
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
685
|
+
select agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
686
|
+
|| '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
|
|
687
|
+
|| agent_board_test.ci_re('values') || '(?![A-Za-z0-9_$]))'
|
|
688
|
+
|| agent_board_test.ident_any_frag() || ')?\s*\()' $b$; $r$),
|
|
689
|
+
('insert rule(core): no backtracking before the paren', 'agent_board_test.insert_all_columns_frag(text,boolean)', 'r13-ins-collist', $r$
|
|
690
|
+
create or replace function agent_board_test.insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
691
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
692
|
+
select agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || '\s*(?!\()' $b$; $r$),
|
|
693
|
+
('insert rule(core): the quoted-alias alternative', 'agent_board_test.insert_all_columns_frag(text,boolean)', 'r15-ins-qalias', $r$
|
|
694
|
+
create or replace function agent_board_test.insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
695
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
696
|
+
select agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
697
|
+
|| '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
|
|
698
|
+
|| agent_board_test.ci_re('values') || '(?![A-Za-z0-9_$])|'
|
|
699
|
+
|| agent_board_test.ci_re('select') || '(?![A-Za-z0-9_$]))'
|
|
700
|
+
|| '[A-Za-z_][A-Za-z0-9_$]*)?\s*\()' $b$; $r$),
|
|
701
|
+
('insert rule(core): the flag reaches the relation', 'agent_board_test.insert_all_columns_frag(text,boolean)', 'r53-ins-novals-bare-when-required', $r$
|
|
702
|
+
create or replace function agent_board_test.insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
703
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
704
|
+
select agent_board_test.stmt_frag('insert into', p_rel, true)
|
|
705
|
+
|| '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
|
|
706
|
+
|| agent_board_test.ci_re('values') || '(?![A-Za-z0-9_$])|'
|
|
707
|
+
|| agent_board_test.ci_re('select') || '(?![A-Za-z0-9_$]))'
|
|
708
|
+
|| agent_board_test.ident_any_frag() || ')?\s*\()' $b$; $r$),
|
|
709
|
+
('insert rule(delegate): bare is ok', 'agent_board_test.insert_all_columns_frag(text)', 'a51-ins-novals-bare', $r$
|
|
710
|
+
create or replace function agent_board_test.insert_all_columns_frag(p_rel text) returns text
|
|
711
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.insert_all_columns_frag(p_rel, false) $b$; $r$),
|
|
712
|
+
('insert re: composed from the frag', 'agent_board_test.insert_all_columns_re(text)', 'a17-ins-re-eq-frag', $r$
|
|
713
|
+
create or replace function agent_board_test.insert_all_columns_re(p_rel text) returns text
|
|
714
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.insert_all_columns_frag(p_rel) $b$; $r$),
|
|
715
|
+
('merge insert rule(core): a column list is refused', 'agent_board_test.merge_insert_all_columns_frag(text,boolean)', 'r18-merge-ins-collist', $r$
|
|
716
|
+
create or replace function agent_board_test.merge_insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
717
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
718
|
+
select agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
719
|
+
|| '\m' || agent_board_test.ci_re('then') || '\s+' || agent_board_test.ci_re('insert') || '(?![A-Za-z0-9_$])' $b$; $r$),
|
|
720
|
+
('merge insert rule(core): THEN INSERT is required', 'agent_board_test.merge_insert_all_columns_frag(text,boolean)', 'r19-merge-do-nothing', $r$
|
|
721
|
+
create or replace function agent_board_test.merge_insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
722
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
723
|
+
select agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) $b$; $r$),
|
|
724
|
+
('merge insert rule(core): the flag reaches the relation', 'agent_board_test.merge_insert_all_columns_frag(text,boolean)', 'r52-merge-ins-bare-when-required', $r$
|
|
725
|
+
create or replace function agent_board_test.merge_insert_all_columns_frag(p_rel text, p_bare_ok boolean) returns text
|
|
726
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
727
|
+
select agent_board_test.stmt_frag('merge into', p_rel, true) || agent_board_test.same_stmt()
|
|
728
|
+
|| '\m' || agent_board_test.ci_re('then') || '\s+' || agent_board_test.ci_re('insert')
|
|
729
|
+
|| '\s*(?:' || agent_board_test.ci_re('default values') || '|' || agent_board_test.ci_re('values')
|
|
730
|
+
|| '|' || agent_board_test.ci_re('overriding') || ')(?![A-Za-z0-9_$])' $b$; $r$),
|
|
731
|
+
('merge insert rule(delegate): bare is ok', 'agent_board_test.merge_insert_all_columns_frag(text)', 'a20c-merge-ins-novals-bare', $r$
|
|
732
|
+
create or replace function agent_board_test.merge_insert_all_columns_frag(p_rel text) returns text
|
|
733
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.merge_insert_all_columns_frag(p_rel, false) $b$; $r$),
|
|
734
|
+
-- write_frag: each revert drops or narrows ONE arm of the write grammar.
|
|
735
|
+
('write_frag(core): the DML verbs', 'agent_board_test.write_frag(text,boolean)', 'a57-write-re-bare', $r$
|
|
736
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
737
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
738
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update', p_rel, p_bare_ok)
|
|
739
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate')
|
|
740
|
+
|| '(?:\s+' || agent_board_test.ci_re('table') || ')?\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
741
|
+
|| '(?:[A-Za-z_"][A-Za-z0-9_."]*\s*\*?\s*,\s*(?:' || agent_board_test.ci_re('only') || '\s+)?)*'
|
|
742
|
+
|| agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
743
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
744
|
+
|| '\s*(?:\([^)]*\))?\s*' || agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
745
|
+
|| '|\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
746
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
747
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
748
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
749
|
+
|| ')' $b$; $r$),
|
|
750
|
+
('write_frag(core): TRUNCATE names a list', 'agent_board_test.write_frag(text,boolean)', 'a36-truncate-list-second', $r$
|
|
751
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
752
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
753
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update|delete from', p_rel, p_bare_ok)
|
|
754
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate')
|
|
755
|
+
|| '(?:\s+' || agent_board_test.ci_re('table') || ')?\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
756
|
+
|| agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
757
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
758
|
+
|| '\s*(?:\([^)]*\))?\s*' || agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
759
|
+
|| '|\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
760
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
761
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
762
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
763
|
+
|| ')' $b$; $r$),
|
|
764
|
+
('write_frag(core): TRUNCATE [TABLE] [ONLY]', 'agent_board_test.write_frag(text,boolean)', 'a23-truncate-table-only', $r$
|
|
765
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
766
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
767
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update|delete from', p_rel, p_bare_ok)
|
|
768
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate') || '\s+'
|
|
769
|
+
|| '(?:[A-Za-z_"][A-Za-z0-9_."]*\s*\*?\s*,\s*(?:' || agent_board_test.ci_re('only') || '\s+)?)*'
|
|
770
|
+
|| agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
771
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
772
|
+
|| '\s*(?:\([^)]*\))?\s*' || agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
773
|
+
|| '|\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
774
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
775
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
776
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
777
|
+
|| ')' $b$; $r$),
|
|
778
|
+
('write_frag(core): COPY only writes when FROM', 'agent_board_test.write_frag(text,boolean)', 'r22-copy-to', $r$
|
|
779
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
780
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
781
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update|delete from', p_rel, p_bare_ok)
|
|
782
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate')
|
|
783
|
+
|| '(?:\s+' || agent_board_test.ci_re('table') || ')?\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
784
|
+
|| '(?:[A-Za-z_"][A-Za-z0-9_."]*\s*\*?\s*,\s*(?:' || agent_board_test.ci_re('only') || '\s+)?)*'
|
|
785
|
+
|| agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
786
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
787
|
+
|| '|\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
788
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
789
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
790
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
791
|
+
|| ')' $b$; $r$),
|
|
792
|
+
('write_frag(core): COPY may carry a column list', 'agent_board_test.write_frag(text,boolean)', 'a22b-copy-collist-from', $r$
|
|
793
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
794
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
795
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update|delete from', p_rel, p_bare_ok)
|
|
796
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate')
|
|
797
|
+
|| '(?:\s+' || agent_board_test.ci_re('table') || ')?\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
798
|
+
|| '(?:[A-Za-z_"][A-Za-z0-9_."]*\s*\*?\s*,\s*(?:' || agent_board_test.ci_re('only') || '\s+)?)*'
|
|
799
|
+
|| agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
800
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
801
|
+
|| '\s*' || agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
802
|
+
|| '|\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
803
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
804
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
805
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
806
|
+
|| ')' $b$; $r$),
|
|
807
|
+
('write_frag(core): SELECT INTO a table is a write', 'agent_board_test.write_frag(text,boolean)', 'a42-select-into-table', $r$
|
|
808
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
809
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
810
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update|delete from', p_rel, p_bare_ok)
|
|
811
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate')
|
|
812
|
+
|| '(?:\s+' || agent_board_test.ci_re('table') || ')?\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
813
|
+
|| '(?:[A-Za-z_"][A-Za-z0-9_."]*\s*\*?\s*,\s*(?:' || agent_board_test.ci_re('only') || '\s+)?)*'
|
|
814
|
+
|| agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
815
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
816
|
+
|| '\s*(?:\([^)]*\))?\s*' || agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
817
|
+
|| ')' $b$; $r$),
|
|
818
|
+
('write_frag(core): INTO must name the relation, not a variable', 'agent_board_test.write_frag(text,boolean)', 'r45-select-into-var-from-write', $r$
|
|
819
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
820
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
821
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update|delete from', p_rel, p_bare_ok)
|
|
822
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate')
|
|
823
|
+
|| '(?:\s+' || agent_board_test.ci_re('table') || ')?\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
824
|
+
|| '(?:[A-Za-z_"][A-Za-z0-9_."]*\s*\*?\s*,\s*(?:' || agent_board_test.ci_re('only') || '\s+)?)*'
|
|
825
|
+
|| agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
826
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
827
|
+
|| '\s*(?:\([^)]*\))?\s*' || agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
828
|
+
|| '|\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
829
|
+
|| '\m' || agent_board_test.ci_re('into') || '\M' || agent_board_test.same_stmt() || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
830
|
+
|| ')' $b$; $r$),
|
|
831
|
+
('write_frag(core): the flag reaches every arm', 'agent_board_test.write_frag(text,boolean)', 'r50-write-bare-when-required', $r$
|
|
832
|
+
create or replace function agent_board_test.write_frag(p_rel text, p_bare_ok boolean) returns text
|
|
833
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
834
|
+
select '(?:' || agent_board_test.stmt_frag('insert into|merge into|update|delete from', p_rel, true)
|
|
835
|
+
|| '|' || agent_board_test.lead_guard() || agent_board_test.ci_re('truncate')
|
|
836
|
+
|| '(?:\s+' || agent_board_test.ci_re('table') || ')?\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
837
|
+
|| '(?:[A-Za-z_"][A-Za-z0-9_."]*\s*\*?\s*,\s*(?:' || agent_board_test.ci_re('only') || '\s+)?)*'
|
|
838
|
+
|| agent_board_test.qname_frag(p_rel, true)
|
|
839
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, true)
|
|
840
|
+
|| '\s*(?:\([^)]*\))?\s*' || agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
841
|
+
|| '|\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
842
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
843
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
844
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, true)
|
|
845
|
+
|| ')' $b$; $r$),
|
|
846
|
+
('write_frag(delegate): bare is ok', 'agent_board_test.write_frag(text)', 'a57-write-re-bare', $r$
|
|
847
|
+
create or replace function agent_board_test.write_frag(p_rel text) returns text
|
|
848
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.write_frag(p_rel, false) $b$; $r$),
|
|
849
|
+
('write_re: the (?c) director', 'agent_board_test.write_re(text)', 'r46-write-re-i-flag', $r$
|
|
850
|
+
create or replace function agent_board_test.write_re(p_rel text) returns text
|
|
851
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.write_frag(p_rel) $b$; $r$),
|
|
852
|
+
('writes_any_frag(core): an empty list is refused', 'agent_board_test.writes_any_frag(text[],boolean)', 'r37-writes-any-empty-refused', $r$
|
|
853
|
+
create or replace function agent_board_test.writes_any_frag(p_rels text[], p_bare_ok boolean) returns text
|
|
854
|
+
language plpgsql immutable set search_path to pg_catalog as $b$
|
|
855
|
+
begin
|
|
856
|
+
return '(?:' || coalesce((select string_agg(agent_board_test.write_frag(r, p_bare_ok), '|') from unnest(p_rels) as r), 'x') || ')';
|
|
857
|
+
end $b$; $r$),
|
|
858
|
+
('writes_any_frag(core): the union covers every relation', 'agent_board_test.writes_any_frag(text[],boolean)', 'a43-writes-any', $r$
|
|
859
|
+
create or replace function agent_board_test.writes_any_frag(p_rels text[], p_bare_ok boolean) returns text
|
|
860
|
+
language plpgsql immutable set search_path to pg_catalog as $b$
|
|
861
|
+
begin
|
|
862
|
+
if p_rels is null or cardinality(p_rels) = 0 then
|
|
863
|
+
raise exception 'writes_any_frag: the relation list is empty';
|
|
864
|
+
end if;
|
|
865
|
+
return '(?:' || agent_board_test.write_frag(p_rels[1], p_bare_ok) || ')';
|
|
866
|
+
end $b$; $r$),
|
|
867
|
+
('writes_any_frag(core): the flag reaches every relation', 'agent_board_test.writes_any_frag(text[],boolean)', 'r51-writes-any-bare-when-required', $r$
|
|
868
|
+
create or replace function agent_board_test.writes_any_frag(p_rels text[], p_bare_ok boolean) returns text
|
|
869
|
+
language plpgsql immutable set search_path to pg_catalog as $b$
|
|
870
|
+
begin
|
|
871
|
+
if p_rels is null or cardinality(p_rels) = 0 then
|
|
872
|
+
raise exception 'writes_any_frag: the relation list is empty';
|
|
873
|
+
end if;
|
|
874
|
+
return '(?:' || (select string_agg(agent_board_test.write_frag(r, true), '|') from unnest(p_rels) as r) || ')';
|
|
875
|
+
end $b$; $r$),
|
|
876
|
+
('writes_any_frag(delegate): bare is ok', 'agent_board_test.writes_any_frag(text[])', 'a58-writes-any-bare', $r$
|
|
877
|
+
create or replace function agent_board_test.writes_any_frag(p_rels text[]) returns text
|
|
878
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.writes_any_frag(p_rels, false) $b$; $r$),
|
|
879
|
+
('writes_any_re: the (?c) director', 'agent_board_test.writes_any_re(text[])', 'r47-writes-any-re-i-flag', $r$
|
|
880
|
+
create or replace function agent_board_test.writes_any_re(p_rels text[]) returns text
|
|
881
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.writes_any_frag(p_rels) $b$; $r$),
|
|
882
|
+
('writes_any_re: composed from the frag', 'agent_board_test.writes_any_re(text[])', 'a47-writes-any-re-eq-frag', $r$
|
|
883
|
+
create or replace function agent_board_test.writes_any_re(p_rels text[]) returns text
|
|
884
|
+
language sql immutable set search_path to pg_catalog as $b$ select '(?c)(?:' || agent_board_test.writes_any_frag(p_rels) || ')' $b$; $r$),
|
|
885
|
+
-- row_lock_frag / row_lock_re: the explicit locking clause, and the clause bound to a relation
|
|
886
|
+
-- within one statement.
|
|
887
|
+
('row_lock_frag: NO KEY is an admitted qualifier', 'agent_board_test.row_lock_frag()', 'a71-row-lock-no-key-update', $r$
|
|
888
|
+
create or replace function agent_board_test.row_lock_frag() returns text
|
|
889
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
890
|
+
select '\m' || agent_board_test.ci_re('for') || '\s+(?:'
|
|
891
|
+
|| agent_board_test.ci_re('update') || '|' || agent_board_test.ci_re('share') || '|' || agent_board_test.ci_re('key share') || ')'
|
|
892
|
+
|| agent_board_test.trail_guard() $b$; $r$),
|
|
893
|
+
('row_lock_frag: the lock word is whole', 'agent_board_test.row_lock_frag()', 'r62-row-lock-suffix-word', $r$
|
|
894
|
+
create or replace function agent_board_test.row_lock_frag() returns text
|
|
895
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
896
|
+
select '\m' || agent_board_test.ci_re('for') || '\s+(?:' || agent_board_test.ci_re('no key') || '\s+)?(?:'
|
|
897
|
+
|| agent_board_test.ci_re('update') || '|' || agent_board_test.ci_re('share') || '|' || agent_board_test.ci_re('key share') || ')' $b$; $r$),
|
|
898
|
+
('row_lock_frag: FOR starts a word', 'agent_board_test.row_lock_frag()', 'r64-row-lock-prefix-word', $r$
|
|
899
|
+
create or replace function agent_board_test.row_lock_frag() returns text
|
|
900
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
901
|
+
select agent_board_test.ci_re('for') || '\s+(?:' || agent_board_test.ci_re('no key') || '\s+)?(?:'
|
|
902
|
+
|| agent_board_test.ci_re('update') || '|' || agent_board_test.ci_re('share') || '|' || agent_board_test.ci_re('key share') || ')'
|
|
903
|
+
|| agent_board_test.trail_guard() $b$; $r$),
|
|
904
|
+
('row_lock_re: the clause is bound to ONE statement', 'agent_board_test.row_lock_re(text)', 'r61-row-lock-next-stmt', $r$
|
|
905
|
+
create or replace function agent_board_test.row_lock_re(p_rel text) returns text
|
|
906
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
907
|
+
select '(?c)' || agent_board_test.qname_frag(p_rel) || '(?:.|\n)*' || agent_board_test.row_lock_frag() $b$; $r$),
|
|
908
|
+
('row_lock_re: a literal is stepped over, not a boundary', 'agent_board_test.row_lock_re(text)', 'r60-row-lock-in-literal', $r$
|
|
909
|
+
create or replace function agent_board_test.row_lock_re(p_rel text) returns text
|
|
910
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
911
|
+
select '(?c)' || agent_board_test.qname_frag(p_rel) || '[^;]*' || agent_board_test.row_lock_frag() $b$; $r$),
|
|
912
|
+
('row_lock_re: bound to the named relation', 'agent_board_test.row_lock_re(text)', 'r63-row-lock-other-rel', $r$
|
|
913
|
+
create or replace function agent_board_test.row_lock_re(p_rel text) returns text
|
|
914
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
915
|
+
select '(?c)' || agent_board_test.same_stmt() || agent_board_test.row_lock_frag() $b$; $r$),
|
|
916
|
+
-- col_assign_frag: arm (1) UPDATE ... SET, arm (3) MERGE ... THEN UPDATE SET. The row form and the
|
|
917
|
+
-- terminators are each one revert.
|
|
918
|
+
('col_assign_frag: the scalar SET arm', 'agent_board_test.col_assign_frag(text,text,boolean)', 'a59-set-scalar-col', $r$
|
|
919
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
920
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
921
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
922
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from|returning')
|
|
923
|
+
|| '\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)\s*='
|
|
924
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
925
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
926
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
927
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
928
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
929
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
930
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
931
|
+
|| ')' $b$; $r$),
|
|
932
|
+
('col_assign_frag: the row-form SET arm', 'agent_board_test.col_assign_frag(text,text,boolean)', 'a25-set-row-form', $r$
|
|
933
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
934
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
935
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
936
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from|returning')
|
|
937
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
938
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
939
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
940
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
941
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
942
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
943
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
944
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
945
|
+
|| ')' $b$; $r$),
|
|
946
|
+
('col_assign_frag: the MERGE arm', 'agent_board_test.col_assign_frag(text,text,boolean)', 'a31-merge-when-matched-and-pred', $r$
|
|
947
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
948
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
949
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
950
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from|returning')
|
|
951
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
952
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
953
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
954
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
955
|
+
|| ')' $b$; $r$),
|
|
956
|
+
('col_assign_frag: the column is in ASSIGNMENT position (not merely after SET)', 'agent_board_test.col_assign_frag(text,text,boolean)', 'r25-col-in-where', $r$
|
|
957
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
958
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
959
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
960
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.same_stmt()
|
|
961
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
962
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
963
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
964
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
965
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
966
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
967
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
968
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
969
|
+
|| ')' $b$; $r$),
|
|
970
|
+
('col_assign_frag: FROM ends the SET list', 'agent_board_test.col_assign_frag(text,text,boolean)', 'r54-set-run-from-terminator-no-where', $r$
|
|
971
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
972
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
973
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
974
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|returning')
|
|
975
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
976
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
977
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
978
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
979
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
980
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
981
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
982
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
983
|
+
|| ')' $b$; $r$),
|
|
984
|
+
('col_assign_frag: RETURNING ends the SET list', 'agent_board_test.col_assign_frag(text,text,boolean)', 'r55-set-run-returning-terminator-eq', $r$
|
|
985
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
986
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
987
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
988
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from')
|
|
989
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
990
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
991
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
992
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
993
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
994
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
995
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
996
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
997
|
+
|| ')' $b$; $r$),
|
|
998
|
+
('col_assign_frag: WHEN ends the MERGE SET list', 'agent_board_test.col_assign_frag(text,text,boolean)', 'r42-merge-col-in-next-when-pred', $r$
|
|
999
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1000
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1001
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1002
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from|returning')
|
|
1003
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
1004
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1005
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
1006
|
+
|| agent_board_test.set_run('returning')
|
|
1007
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
1008
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1009
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
1010
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
1011
|
+
|| ')' $b$; $r$),
|
|
1012
|
+
('col_assign_frag: the flag reaches the relation', 'agent_board_test.col_assign_frag(text,text,boolean)', 'r49-col-write-bare-when-required', $r$
|
|
1013
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1014
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1015
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, true) || agent_board_test.same_stmt()
|
|
1016
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from|returning')
|
|
1017
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
1018
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, true) || agent_board_test.same_stmt()
|
|
1019
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
1020
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
1021
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
1022
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1023
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('where|returning')
|
|
1024
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
1025
|
+
|| ')' $b$; $r$),
|
|
1026
|
+
('col_assign_frag: the upsert arm', 'agent_board_test.col_assign_frag(text,text,boolean)', 'a74-upsert-set-col', $r$
|
|
1027
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1028
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1029
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1030
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from|returning')
|
|
1031
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
1032
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1033
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
1034
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
1035
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
1036
|
+
|| ')' $b$; $r$),
|
|
1037
|
+
('col_assign_frag: WHERE ends the upsert SET list', 'agent_board_test.col_assign_frag(text,text,boolean)', 'r66-upsert-col-in-where', $r$
|
|
1038
|
+
create or replace function agent_board_test.col_assign_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1039
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1040
|
+
select '(?:' || agent_board_test.stmt_frag('update', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1041
|
+
|| '\m' || agent_board_test.ci_re('set') || '\M' || agent_board_test.set_run('where|from|returning')
|
|
1042
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
1043
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1044
|
+
|| '\m' || agent_board_test.ci_re('then update set') || '\M'
|
|
1045
|
+
|| agent_board_test.set_run('when\s+(?:not\s+)?matched|returning')
|
|
1046
|
+
|| agent_board_test.col_frag(p_col) || '\s*='
|
|
1047
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1048
|
+
|| '\m' || agent_board_test.ci_re('do update set') || '\M' || agent_board_test.set_run('returning')
|
|
1049
|
+
|| '(?:' || agent_board_test.col_frag(p_col) || '|\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\))\s*='
|
|
1050
|
+
|| ')' $b$; $r$),
|
|
1051
|
+
-- col_write_frag: the union of the assignment arms and every write SHAPE. Each revert drops or
|
|
1052
|
+
-- narrows exactly one arm.
|
|
1053
|
+
('col_write_frag(core): the INSERT column-list arm', 'agent_board_test.col_write_frag(text,text,boolean)', 'a60-insert-collist-col', $r$
|
|
1054
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1055
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1056
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1057
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1058
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1059
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1060
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1061
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1062
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1063
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1064
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1065
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1066
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1067
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1068
|
+
|| ')' $b$; $r$),
|
|
1069
|
+
('col_write_frag(core): the INSERT alias before the column list', 'agent_board_test.col_write_frag(text,text,boolean)', 'a40-insert-alias-collist', $r$
|
|
1070
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1071
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1072
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1073
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1074
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1075
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1076
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1077
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1078
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1079
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1080
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1081
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1082
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1083
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1084
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1085
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1086
|
+
|| ')' $b$; $r$),
|
|
1087
|
+
('col_write_frag(core): the column must be INSIDE the INSERT column list', 'agent_board_test.col_write_frag(text,text,boolean)', 'r28-insert-select-col', $r$
|
|
1088
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1089
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1090
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1091
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok) || agent_board_test.same_stmt() || agent_board_test.col_frag(p_col)
|
|
1092
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1093
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1094
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1095
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1096
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1097
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1098
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1099
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1100
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1101
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1102
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1103
|
+
|| ')' $b$; $r$),
|
|
1104
|
+
('col_write_frag(core): the MERGE column-list arm', 'agent_board_test.col_write_frag(text,text,boolean)', 'a41-merge-ins-collist-col', $r$
|
|
1105
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1106
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1107
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1108
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1109
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1110
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1111
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1112
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1113
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1114
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1115
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1116
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1117
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1118
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1119
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1120
|
+
|| ')' $b$; $r$),
|
|
1121
|
+
('col_write_frag(core): the INSERT-without-column-list arm', 'agent_board_test.col_write_frag(text,text,boolean)', 'a61-ins-novals-via-col-write', $r$
|
|
1122
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1123
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1124
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1125
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1126
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1127
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1128
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1129
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1130
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1131
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1132
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1133
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1134
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1135
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1136
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1137
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1138
|
+
|| ')' $b$; $r$),
|
|
1139
|
+
('col_write_frag(core): the MERGE-without-column-list arm', 'agent_board_test.col_write_frag(text,text,boolean)', 'a20b-merge-ins-novals-via-col-write', $r$
|
|
1140
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1141
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1142
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1143
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1144
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1145
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1146
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1147
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1148
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1149
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1150
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1151
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1152
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1153
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1154
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1155
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1156
|
+
|| ')' $b$; $r$),
|
|
1157
|
+
('col_write_frag(core): the COPY arm names the column', 'agent_board_test.col_write_frag(text,text,boolean)', 'a38-copy-collist-col-from', $r$
|
|
1158
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1159
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1160
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1161
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1162
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1163
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1164
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1165
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1166
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1167
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1168
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1169
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1170
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1171
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1172
|
+
|| ')' $b$; $r$),
|
|
1173
|
+
('col_write_frag(core): a COPY column list must NAME the column', 'agent_board_test.col_write_frag(text,text,boolean)', 'r32-copy-other-cols-from', $r$
|
|
1174
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1175
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1176
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1177
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1178
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1179
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1180
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1181
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1182
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1183
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1184
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1185
|
+
|| '\s*(?:\([^)]*\))?\s*'
|
|
1186
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1187
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1188
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1189
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1190
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1191
|
+
|| ')' $b$; $r$),
|
|
1192
|
+
('col_write_frag(core): the SELECT INTO arm', 'agent_board_test.col_write_frag(text,text,boolean)', 'a62-select-into-via-col-write', $r$
|
|
1193
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1194
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1195
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1196
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1197
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1198
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1199
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1200
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1201
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1202
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1203
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1204
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1205
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1206
|
+
|| ')' $b$; $r$),
|
|
1207
|
+
('col_write_frag(core): INTO must name the relation, not a variable', 'agent_board_test.col_write_frag(text,text,boolean)', 'r36-select-into-var-from', $r$
|
|
1208
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1209
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1210
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, p_bare_ok)
|
|
1211
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, p_bare_ok)
|
|
1212
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1213
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1214
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, p_bare_ok) || agent_board_test.same_stmt()
|
|
1215
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1216
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1217
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, p_bare_ok)
|
|
1218
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, p_bare_ok)
|
|
1219
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1220
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1221
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1222
|
+
|| '\m' || agent_board_test.ci_re('into') || '\M' || agent_board_test.same_stmt() || agent_board_test.qname_frag(p_rel, p_bare_ok)
|
|
1223
|
+
|| ')' $b$; $r$),
|
|
1224
|
+
('col_write_frag(core): the flag reaches every arm', 'agent_board_test.col_write_frag(text,text,boolean)', 'r49-col-write-bare-when-required', $r$
|
|
1225
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text, p_bare_ok boolean) returns text
|
|
1226
|
+
language sql immutable set search_path to pg_catalog as $b$
|
|
1227
|
+
select '(?:' || agent_board_test.col_assign_frag(p_rel, p_col, true)
|
|
1228
|
+
|| '|' || agent_board_test.stmt_frag('insert into', p_rel, true)
|
|
1229
|
+
|| '(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?' || agent_board_test.ident_any_frag() || ')?'
|
|
1230
|
+
|| '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1231
|
+
|| '|' || agent_board_test.stmt_frag('merge into', p_rel, true) || agent_board_test.same_stmt()
|
|
1232
|
+
|| '\m' || agent_board_test.ci_re('then insert') || '\s*\([^)]*' || agent_board_test.col_frag(p_col) || '[^)]*\)'
|
|
1233
|
+
|| '|' || agent_board_test.insert_all_columns_frag(p_rel, true)
|
|
1234
|
+
|| '|' || agent_board_test.merge_insert_all_columns_frag(p_rel, true)
|
|
1235
|
+
|| '|' || agent_board_test.stmt_frag('copy', p_rel, true)
|
|
1236
|
+
|| '\s*(?:\((?:[^)]*' || agent_board_test.col_frag(p_col) || '[^)]*)\))?\s*'
|
|
1237
|
+
|| agent_board_test.ci_re('from') || agent_board_test.trail_guard()
|
|
1238
|
+
|| '|' || '\m' || agent_board_test.ci_re('select') || '\M' || agent_board_test.same_stmt()
|
|
1239
|
+
|| '\m' || agent_board_test.ci_re('into') || '\s+(?:(?:' || agent_board_test.ci_re('temporary')
|
|
1240
|
+
|| '|' || agent_board_test.ci_re('temp') || '|' || agent_board_test.ci_re('unlogged') || ')\s+)?'
|
|
1241
|
+
|| '(?:' || agent_board_test.ci_re('table') || '\s+)?' || agent_board_test.qname_frag(p_rel, true)
|
|
1242
|
+
|| ')' $b$; $r$),
|
|
1243
|
+
('col_write_frag(delegate): bare is ok', 'agent_board_test.col_write_frag(text,text)', 'a19-span-lit-semicolon', $r$
|
|
1244
|
+
create or replace function agent_board_test.col_write_frag(p_rel text, p_col text) returns text
|
|
1245
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.col_write_frag(p_rel, p_col, false) $b$; $r$),
|
|
1246
|
+
('col_write_re: the (?c) director', 'agent_board_test.col_write_re(text,text)', 'r48-col-write-re-i-flag', $r$
|
|
1247
|
+
create or replace function agent_board_test.col_write_re(p_rel text, p_col text) returns text
|
|
1248
|
+
language sql immutable set search_path to pg_catalog as $b$ select agent_board_test.col_write_frag(p_rel, p_col) $b$; $r$),
|
|
1249
|
+
('col_write_re: composed from the frag', 'agent_board_test.col_write_re(text,text)', 'a45-col-write-re-eq-frag', $r$
|
|
1250
|
+
create or replace function agent_board_test.col_write_re(p_rel text, p_col text) returns text
|
|
1251
|
+
language sql immutable set search_path to pg_catalog as $b$ select '(?c)(?:' || agent_board_test.col_write_frag(p_rel, p_col) || ')' $b$; $r$),
|
|
1252
|
+
-- The two body readers are GATED rather than exempt: their fixture is the quoted-callee function
|
|
1253
|
+
-- created above, and the revert empties each of them while keeping its signature.
|
|
1254
|
+
('lock_edges: a quoted callee is an edge', 'agent_board_test.lock_edges()', 'a66-lock-edges-quoted-callee', $r$
|
|
1255
|
+
create or replace function agent_board_test.lock_edges()
|
|
1256
|
+
returns table(fn_oid oid, fn_name text, fn_sig text, rel_oid oid, rel_name text, lock_mode text, via text, path integer[], own boolean, origin_oid oid, origin_name text)
|
|
1257
|
+
language sql stable set search_path to pg_catalog as $b$
|
|
1258
|
+
select null::oid, null::text, null::text, null::oid, null::text, null::text, null::text, null::integer[], null::boolean, null::oid, null::text where false $b$; $r$),
|
|
1259
|
+
('fn_call_closure: a quoted callee is reached', 'agent_board_test.fn_call_closure()', 'a67-closure-quoted-callee', $r$
|
|
1260
|
+
create or replace function agent_board_test.fn_call_closure()
|
|
1261
|
+
returns table(root_oid oid, root_name text, reached_oid oid, reached_name text)
|
|
1262
|
+
language sql stable as $b$
|
|
1263
|
+
select null::oid, null::text, null::oid, null::text where false $b$; $r$);
|
|
1264
|
+
|
|
1265
|
+
create temp table _mflip (rule_name text, expect_row text, flipped text, named_row_flipped boolean) on commit drop;
|
|
1266
|
+
|
|
1267
|
+
do $mut$
|
|
1268
|
+
declare
|
|
1269
|
+
r record;
|
|
1270
|
+
v_save text;
|
|
1271
|
+
v_hits text;
|
|
1272
|
+
begin
|
|
1273
|
+
for r in select * from _mrule order by rule_name loop
|
|
1274
|
+
v_save := pg_get_functiondef(r.fn::regprocedure);
|
|
1275
|
+
execute r.revert_sql;
|
|
1276
|
+
select coalesce(string_agg(m.label, ' ' order by m.label), '<NONE>')
|
|
1277
|
+
into v_hits
|
|
1278
|
+
from _mrow m
|
|
1279
|
+
where (not m.costly or r.fn in ('agent_board_test.lock_edges()', 'agent_board_test.fn_call_closure()'))
|
|
1280
|
+
and pg_temp.mverdict(m.expr) <> m.want;
|
|
1281
|
+
execute v_save; -- restore before the next rule
|
|
1282
|
+
insert into _mflip
|
|
1283
|
+
values (r.rule_name, r.expect_row, v_hits,
|
|
1284
|
+
v_hits ~ ('(^| )' || r.expect_row || '( |$)'));
|
|
1285
|
+
end loop;
|
|
1286
|
+
end $mut$;
|
|
1287
|
+
|
|
1288
|
+
select is(
|
|
1289
|
+
(select coalesce(string_agg(rule_name || ' (wanted ' || expect_row || ', got ' || flipped || ')',
|
|
1290
|
+
'; ' order by rule_name), '<none>')
|
|
1291
|
+
from _mflip where not named_row_flipped),
|
|
1292
|
+
'<none>',
|
|
1293
|
+
'CPR-0a REVERT MAP (mechanical): reverting each rule makes THE ROW NAMED FOR THAT RULE fail — one behaviour per entry and a named row per entry, so a compound revert cannot let a covered branch conceal an uncovered sibling and no rule can borrow coverage from an unrelated row — the map is compiled by reverting each rule and re-evaluating the rows, not by hand, because three separate rows have now certified axes they did not test and a human reading the table caught none of them');
|
|
1294
|
+
|
|
1295
|
+
-- CPR-0b. AND THE RULE INVENTORY IS RECONCILED AGAINST THE CATALOG, because _mrule is still written
|
|
1296
|
+
-- by hand and a matcher added without an entry would be SILENTLY UNGATED — which contradicts
|
|
1297
|
+
-- CPR-0a's own sentence. Deriving "every rule a regex-building function implements" from its source
|
|
1298
|
+
-- is not mechanical; deriving "every FUNCTION in this schema is either gated or positively NOT a
|
|
1299
|
+
-- matcher" is, and it closes the exposure that matters: a new matcher cannot arrive unnoticed.
|
|
1300
|
+
--
|
|
1301
|
+
-- THE EXEMPTION IS NOW ASSERTED, NOT LISTED (Consiliency/message-board#416 PR 1). The previous
|
|
1302
|
+
-- version named the ungated functions `NOT-GATED` and called them "body readers and lock helpers,
|
|
1303
|
+
-- which build no regex" — prose nothing checked. A helper that RETURNS `'\m' || p_name || '\M'`,
|
|
1304
|
+
-- or one that builds a pattern from a variable, would have joined that list with the same label.
|
|
1305
|
+
-- Every function without an `_mrule` row now has to satisfy, over its literal-STRIPPED source:
|
|
1306
|
+
-- (2a) every regex-operator right-hand side in its body (`~`, `~*`, `!~`, `!~*`, the pattern
|
|
1307
|
+
-- argument of `regexp_*(…)`, and `substring(… from …)`) is a constant — a literal collapses
|
|
1308
|
+
-- to whitespace under strip_sql — or a call to a GATED function, with nothing else on that
|
|
1309
|
+
-- side (a variable, a column, a parameter, an ungated call);
|
|
1310
|
+
-- (2b) when it RETURNS text, it is not called on the pattern side of any regex operator in this
|
|
1311
|
+
-- schema, nor inside the body of any GATED text-returning function.
|
|
1312
|
+
-- The second half of (2b) is narrower than "any gated function" on purpose: `lock_edges()` and
|
|
1313
|
+
-- `fn_call_closure()` are gated (rows a66/a67) and READ bodies through `stripped_body`, which
|
|
1314
|
+
-- returns text; calling a body reader from a gated matcher is the thing (2b) exists to catch only
|
|
1315
|
+
-- when the gated function is itself building a pattern. `bare_ok(oid)` returns boolean and is
|
|
1316
|
+
-- outside (2b) by construction. The third conjunct — not called on the pattern side of any
|
|
1317
|
+
-- comparison in a TEST FILE — is asserted by scripts/check-test-source-scan-literals.mjs, because
|
|
1318
|
+
-- pg_proc cannot see the test files.
|
|
1319
|
+
--
|
|
1320
|
+
-- A function failing (2a) reads `UNGATED-BUILDS-PATTERN <token>`; one failing (2b) reads
|
|
1321
|
+
-- `UNGATED-CALLED-IN-PATTERN <where>`. Both fail by name; the expected value names every
|
|
1322
|
+
-- signature, so a new function of any kind appears here and must be classified deliberately.
|
|
1323
|
+
create function pg_temp.pattern_operands(p_body text) returns setof text
|
|
1324
|
+
language plpgsql as $f$
|
|
1325
|
+
declare
|
|
1326
|
+
v_pos int := 1; v_len int := length(p_body); v_start int; v_depth int; v_i int; v_c text;
|
|
1327
|
+
v_rhs text; v_args text; v_k int; v_hit text;
|
|
1328
|
+
begin
|
|
1329
|
+
loop
|
|
1330
|
+
v_hit := substring(p_body from v_pos);
|
|
1331
|
+
-- source-scan: loud-literal CPR-0b's own tokenizer over the FAMILY's bodies; the family cannot be its own scanner
|
|
1332
|
+
v_start := regexp_instr(v_hit, '(!?~\*?|(?<![A-Za-z0-9_])(?:regexp_[A-Za-z_]+|substring)\s*\()', 1, 1, 0, 'i');
|
|
1333
|
+
exit when v_start = 0;
|
|
1334
|
+
v_start := v_pos + v_start - 1;
|
|
1335
|
+
if substr(p_body, v_start, 1) in ('~', '!') then
|
|
1336
|
+
-- an operator: the operand runs to the next depth-0 `,` `)` `;` or clause keyword
|
|
1337
|
+
v_i := v_start;
|
|
1338
|
+
while substr(p_body, v_i, 1) in ('!', '~', '*') loop v_i := v_i + 1; end loop;
|
|
1339
|
+
v_rhs := ''; v_depth := 0;
|
|
1340
|
+
while v_i <= v_len loop
|
|
1341
|
+
v_c := substr(p_body, v_i, 1);
|
|
1342
|
+
if v_c = '(' then v_depth := v_depth + 1;
|
|
1343
|
+
elsif v_c = ')' then
|
|
1344
|
+
exit when v_depth = 0;
|
|
1345
|
+
v_depth := v_depth - 1;
|
|
1346
|
+
elsif v_depth = 0 and v_c in (',', ';') then exit;
|
|
1347
|
+
-- source-scan: loud-literal CPR-0b's own tokenizer over the FAMILY's bodies; the family cannot be its own scanner
|
|
1348
|
+
elsif v_depth = 0 and v_rhs ~ '\s$'
|
|
1349
|
+
-- source-scan: loud-literal CPR-0b's own tokenizer over the FAMILY's bodies; the family cannot be its own scanner
|
|
1350
|
+
and substring(p_body from v_i) ~* '^(and|or|then|loop|else|end|when|into|as|from|where|order|group|limit)(?![A-Za-z0-9_])' then exit;
|
|
1351
|
+
end if;
|
|
1352
|
+
v_rhs := v_rhs || v_c; v_i := v_i + 1;
|
|
1353
|
+
end loop;
|
|
1354
|
+
return next btrim(v_rhs);
|
|
1355
|
+
v_pos := v_i;
|
|
1356
|
+
else
|
|
1357
|
+
-- regexp_*( or substring(: the pattern is the second depth-0 argument, or the FROM operand
|
|
1358
|
+
v_i := v_start + position('(' in substr(p_body, v_start));
|
|
1359
|
+
v_depth := 0; v_args := ''; v_k := 1; v_rhs := null;
|
|
1360
|
+
while v_i <= v_len loop
|
|
1361
|
+
v_c := substr(p_body, v_i, 1);
|
|
1362
|
+
if v_c = '(' then v_depth := v_depth + 1;
|
|
1363
|
+
elsif v_c = ')' then
|
|
1364
|
+
exit when v_depth = 0;
|
|
1365
|
+
v_depth := v_depth - 1;
|
|
1366
|
+
-- source-scan: loud-literal CPR-0b's own tokenizer over the FAMILY's bodies; the family cannot be its own scanner
|
|
1367
|
+
elsif v_depth = 0 and (v_c = ',' or (v_args ~ '\s$' and substring(p_body from v_i) ~* '^from(?![A-Za-z0-9_])')) then
|
|
1368
|
+
if v_k = 2 then v_rhs := v_args; end if;
|
|
1369
|
+
v_k := v_k + 1; v_args := '';
|
|
1370
|
+
v_i := v_i + case when v_c = ',' then 1 else 4 end;
|
|
1371
|
+
continue;
|
|
1372
|
+
-- source-scan: loud-literal CPR-0b's own tokenizer over the FAMILY's bodies; the family cannot be its own scanner
|
|
1373
|
+
elsif v_depth = 0 and v_args ~ '\s$' and substring(p_body from v_i) ~* '^for(?![A-Za-z0-9_])' then
|
|
1374
|
+
exit;
|
|
1375
|
+
end if;
|
|
1376
|
+
v_args := v_args || v_c; v_i := v_i + 1;
|
|
1377
|
+
end loop;
|
|
1378
|
+
if v_k = 2 then v_rhs := v_args; end if;
|
|
1379
|
+
return next btrim(coalesce(v_rhs, '<no pattern argument>'));
|
|
1380
|
+
v_pos := v_i + 1;
|
|
1381
|
+
end if;
|
|
1382
|
+
end loop;
|
|
1383
|
+
end $f$;
|
|
1384
|
+
|
|
1385
|
+
-- The operand with every GATED call (and its argument list) removed; what remains must be empty
|
|
1386
|
+
-- or a number — anything else is a token that BUILDS the pattern.
|
|
1387
|
+
create function pg_temp.pattern_residue(p_operand text) returns text
|
|
1388
|
+
language plpgsql as $f$
|
|
1389
|
+
declare v text := p_operand; v_pos int; v_i int; v_depth int; v_c text; v_gated text;
|
|
1390
|
+
begin
|
|
1391
|
+
loop
|
|
1392
|
+
select string_agg(distinct regexp_replace(r.fn, '^agent_board_test\.([^(]+)\(.*$', '\1'), '|') into v_gated from _mrule r;
|
|
1393
|
+
-- source-scan: loud-literal CPR-0b's own residue scanner over the FAMILY's operands; the family cannot be its own scanner
|
|
1394
|
+
v_pos := regexp_instr(v, '(?<![A-Za-z0-9_])agent_board_test\s*\.\s*(?:' || v_gated || ')\s*\(', 1, 1, 0, 'c');
|
|
1395
|
+
exit when v_pos = 0;
|
|
1396
|
+
v_i := v_pos + position('(' in substr(v, v_pos)); v_depth := 0;
|
|
1397
|
+
while v_i <= length(v) loop
|
|
1398
|
+
v_c := substr(v, v_i, 1);
|
|
1399
|
+
if v_c = '(' then v_depth := v_depth + 1;
|
|
1400
|
+
elsif v_c = ')' then
|
|
1401
|
+
exit when v_depth = 0;
|
|
1402
|
+
v_depth := v_depth - 1;
|
|
1403
|
+
end if;
|
|
1404
|
+
v_i := v_i + 1;
|
|
1405
|
+
end loop;
|
|
1406
|
+
v := substr(v, 1, v_pos - 1) || ' ' || substr(v, v_i + 1);
|
|
1407
|
+
end loop;
|
|
1408
|
+
return btrim(regexp_replace(regexp_replace(v, '\|\||[()]', ' ', 'g'), '(?<![A-Za-z0-9_$.])[0-9]+(?![A-Za-z0-9_$.])', ' ', 'g'));
|
|
1409
|
+
end $f$;
|
|
1410
|
+
|
|
1411
|
+
create temp table _mfn on commit drop as
|
|
1412
|
+
select p.oid, p.proname || '(' || array_to_string(p.proargtypes::oid[]::regtype[], ',') || ')' as sig,
|
|
1413
|
+
p.proname, p.prorettype = 'text'::regtype as returns_text,
|
|
1414
|
+
exists (select 1 from _mrule r where r.fn::regprocedure = p.oid) as gated,
|
|
1415
|
+
agent_board_test.stripped_body(p.oid, false) as body
|
|
1416
|
+
from pg_proc p where p.pronamespace = 'agent_board_test'::regnamespace;
|
|
1417
|
+
|
|
1418
|
+
create temp table _moperand on commit drop as
|
|
1419
|
+
select f.sig, o.operand, pg_temp.pattern_residue(o.operand) as residue
|
|
1420
|
+
from _mfn f, lateral pg_temp.pattern_operands(f.body) as o(operand);
|
|
1421
|
+
|
|
1422
|
+
select is(
|
|
1423
|
+
(select string_agg(f.sig || '=' ||
|
|
1424
|
+
case
|
|
1425
|
+
when f.gated then 'gated'
|
|
1426
|
+
when exists (select 1 from _moperand o where o.sig = f.sig and o.residue <> '')
|
|
1427
|
+
then 'UNGATED-BUILDS-PATTERN ' || (select string_agg(o.residue, ' ') from _moperand o where o.sig = f.sig and o.residue <> '')
|
|
1428
|
+
when f.returns_text and exists (select 1 from _moperand o where o.operand ~ agent_board_test.call_re('agent_board_test.' || f.proname, true))
|
|
1429
|
+
then 'UNGATED-CALLED-IN-PATTERN ' || (select string_agg(o.sig, ' ') from _moperand o where o.operand ~ agent_board_test.call_re('agent_board_test.' || f.proname, true))
|
|
1430
|
+
when f.returns_text and exists (select 1 from _mfn g where g.gated and g.returns_text and g.body ~ agent_board_test.call_re('agent_board_test.' || f.proname, true))
|
|
1431
|
+
then 'UNGATED-CALLED-IN-PATTERN ' || (select string_agg(g.sig, ' ') from _mfn g where g.gated and g.returns_text and g.body ~ agent_board_test.call_re('agent_board_test.' || f.proname, true))
|
|
1432
|
+
-- A gated body that returns ROWS (`lock_edges`, `fn_call_closure`) builds its patterns
|
|
1433
|
+
-- into COLUMNS and applies them by column reference, so neither the operand rule nor
|
|
1434
|
+
-- the text-body rule above sees a helper it calls. Every ungated text function such a
|
|
1435
|
+
-- body calls is convicted, EXCEPT a subject producer — identified by what it does
|
|
1436
|
+
-- (it is `strip_sql`, or its body calls it), never by a name list. A helper that both
|
|
1437
|
+
-- strips and builds a pattern would pass this conjunct: recorded, not closed
|
|
1438
|
+
-- (codex, PR #435 round 1, m11d).
|
|
1439
|
+
when f.returns_text and (f.sig = 'strip_sql(text,boolean)' or f.body ~ agent_board_test.call_re('agent_board_test.strip_sql', false))
|
|
1440
|
+
then 'subject-producer'
|
|
1441
|
+
when f.returns_text
|
|
1442
|
+
and exists (select 1 from _mfn g where g.gated and not g.returns_text and g.body ~ agent_board_test.call_re('agent_board_test.' || f.proname, true))
|
|
1443
|
+
then 'UNGATED-CALLED-IN-GATED-BODY ' || (select string_agg(g.sig, ' ') from _mfn g where g.gated and not g.returns_text and g.body ~ agent_board_test.call_re('agent_board_test.' || f.proname, true))
|
|
1444
|
+
else 'not-a-matcher'
|
|
1445
|
+
end, '; ' order by f.sig)
|
|
1446
|
+
from _mfn f),
|
|
1447
|
+
'bare_ok(oid)=not-a-matcher; '
|
|
1448
|
+
'call_frag(text)=gated; '
|
|
1449
|
+
'call_frag(text,boolean)=gated; '
|
|
1450
|
+
'call_re(text)=gated; '
|
|
1451
|
+
'call_re(text,boolean)=gated; '
|
|
1452
|
+
'ci_re(text)=gated; '
|
|
1453
|
+
'col_assign_frag(text,text,boolean)=gated; '
|
|
1454
|
+
'col_frag(text)=gated; '
|
|
1455
|
+
'col_ref_frag(text)=gated; '
|
|
1456
|
+
'col_write_frag(text,text)=gated; '
|
|
1457
|
+
'col_write_frag(text,text,boolean)=gated; '
|
|
1458
|
+
'col_write_re(text,text)=gated; '
|
|
1459
|
+
'fn_call_closure()=gated; '
|
|
1460
|
+
'ident_any_frag()=gated; '
|
|
1461
|
+
'ident_chars()=gated; '
|
|
1462
|
+
'ident_re(text)=gated; '
|
|
1463
|
+
'insert_all_columns_frag(text)=gated; '
|
|
1464
|
+
'insert_all_columns_frag(text,boolean)=gated; '
|
|
1465
|
+
'insert_all_columns_re(text)=gated; '
|
|
1466
|
+
'lead_guard()=gated; '
|
|
1467
|
+
'lock_edges()=gated; '
|
|
1468
|
+
'merge_insert_all_columns_frag(text)=gated; '
|
|
1469
|
+
'merge_insert_all_columns_frag(text,boolean)=gated; '
|
|
1470
|
+
'qname_frag(text)=gated; '
|
|
1471
|
+
'qname_frag(text,boolean)=gated; '
|
|
1472
|
+
'qname_re(text)=gated; '
|
|
1473
|
+
're(text)=gated; '
|
|
1474
|
+
'row_lock_frag()=gated; '
|
|
1475
|
+
'row_lock_rank(text)=not-a-matcher; '
|
|
1476
|
+
'row_lock_re(text)=gated; '
|
|
1477
|
+
'row_locks_conflict(text,text)=not-a-matcher; '
|
|
1478
|
+
'same_stmt()=gated; '
|
|
1479
|
+
'set_run(text)=gated; '
|
|
1480
|
+
'stmt_frag(text,text)=gated; '
|
|
1481
|
+
'stmt_frag(text,text,boolean)=gated; '
|
|
1482
|
+
'stmt_re(text,text)=gated; '
|
|
1483
|
+
'stmt_re(text,text,boolean)=gated; '
|
|
1484
|
+
'strip_sql(text,boolean)=subject-producer; '
|
|
1485
|
+
'stripped_body(oid,boolean)=subject-producer; '
|
|
1486
|
+
'stripped_def(oid,boolean)=subject-producer; '
|
|
1487
|
+
'stripped_viewdef(oid,boolean)=subject-producer; '
|
|
1488
|
+
'trail_guard()=gated; '
|
|
1489
|
+
'write_frag(text)=gated; '
|
|
1490
|
+
'write_frag(text,boolean)=gated; '
|
|
1491
|
+
'write_re(text)=gated; '
|
|
1492
|
+
'writes_any_frag(text[])=gated; '
|
|
1493
|
+
'writes_any_frag(text[],boolean)=gated; '
|
|
1494
|
+
'writes_any_re(text[])=gated',
|
|
1495
|
+
'CPR-0b RULE INVENTORY: every function in agent_board_test is either covered by a CPR-0a rule (keyed by OID, so a rule naming a missing signature gates nothing and fails here) or POSITIVELY not a matcher — every regex operand in its body is a constant or a gated call, and if it returns text it is called on no pattern side, in no gated matcher body, and in no gated ROW-returning body unless it is a subject producer (it strips); the expected value names every signature, so a new function appears here by name and a helper that merely returns a pattern for others to use is convicted where it is used');
|
|
1496
|
+
|
|
1497
|
+
-- The fixture is DROPPED here, not left to the transaction's rollback: the census assertions below
|
|
1498
|
+
-- read agent_board's catalog, and a probe that calls acquire_claim would otherwise appear in
|
|
1499
|
+
-- CPR-14c's callee direction as one more caller of the claims row lock.
|
|
1500
|
+
drop function agent_board._probe416_quoted_callee(uuid);
|
|
1501
|
+
|
|
138
1502
|
-- ===========================================================================
|
|
139
1503
|
-- THE CLASS (rule 1). Catalog-derived on every run.
|
|
140
1504
|
-- ===========================================================================
|
|
@@ -183,16 +1547,23 @@ select is(
|
|
|
183
1547
|
select is(
|
|
184
1548
|
(select string_agg(x.proname || '=' ||
|
|
185
1549
|
case when regexp_instr(b.body, agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0) = 0 then 'NO-LOCK'
|
|
186
|
-
when regexp_instr(b.body, x.decision, 1, 1, 0
|
|
1550
|
+
when regexp_instr(b.body, x.decision, 1, 1, 0) = 0 then 'NO-DECISION'
|
|
187
1551
|
when regexp_instr(b.body, agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0)
|
|
188
|
-
< regexp_instr(b.body, x.decision, 1, 1, 0
|
|
1552
|
+
< regexp_instr(b.body, x.decision, 1, 1, 0)
|
|
189
1553
|
then 'lock-first'
|
|
190
1554
|
else 'DECISION-FIRST' end, ', ' order by x.proname)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
1555
|
+
-- Each row binds `decision` BY NAME (not a VALUES alias), so the lint traces the pattern
|
|
1556
|
+
-- `regexp_instr(b.body, x.decision, …)` reads to these three expressions (#437 round 1). No
|
|
1557
|
+
-- 'i' flag: the family carries its own case classes and its header forbids the flag (round 2).
|
|
1558
|
+
from (select 'acquire_claim'::text as proname,
|
|
1559
|
+
agent_board_test.call_re('_claimgate_board_claim_policy_of') as decision
|
|
1560
|
+
union all
|
|
1561
|
+
select 'renew_claim'::text as proname,
|
|
1562
|
+
agent_board_test.call_re('_claimgate_board_claim_policy_of') as decision
|
|
1563
|
+
union all
|
|
1564
|
+
select 'set_board_claim_policy'::text as proname,
|
|
1565
|
+
agent_board_test.re(agent_board_test.ci_re('into') || '\s+' || agent_board_test.ident_re('v_prev_policy')) as decision
|
|
1566
|
+
) as x
|
|
196
1567
|
join pg_proc p on p.proname = x.proname
|
|
197
1568
|
join pg_namespace n on n.oid = p.pronamespace and n.nspname = 'agent_board'
|
|
198
1569
|
cross join lateral (select agent_board_test.stripped_body(p.oid, true) as body) b),
|
|
@@ -226,10 +1597,15 @@ select is(
|
|
|
226
1597
|
regexp_instr(agent_board_test.stripped_body(p.oid, true),
|
|
227
1598
|
agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0) as board_pos,
|
|
228
1599
|
regexp_instr(agent_board_test.stripped_body(p.oid, true),
|
|
229
|
-
'
|
|
1600
|
+
agent_board_test.re('(?:' || agent_board_test.call_frag('pg_catalog.pg_advisory_xact_lock', true)
|
|
1601
|
+
|| '|' || agent_board_test.call_frag('pg_catalog.pg_try_advisory_xact_lock', true)
|
|
1602
|
+
|| ')\s*' || agent_board_test.call_frag('pg_catalog.hashtext', true)), 1, 1, 0) as quota_pos
|
|
230
1603
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
231
1604
|
where n.nspname = 'agent_board'
|
|
232
|
-
and agent_board_test.stripped_body(p.oid, true)
|
|
1605
|
+
and agent_board_test.stripped_body(p.oid, true)
|
|
1606
|
+
~ agent_board_test.re('(?:' || agent_board_test.call_frag('pg_catalog.pg_advisory_xact_lock', true)
|
|
1607
|
+
|| '|' || agent_board_test.call_frag('pg_catalog.pg_try_advisory_xact_lock', true)
|
|
1608
|
+
|| ')\s*' || agent_board_test.call_frag('pg_catalog.hashtext', true))
|
|
233
1609
|
)
|
|
234
1610
|
select coalesce(string_agg(proname || '=' ||
|
|
235
1611
|
(case when board_pos = 0 then 'no-board-lock'
|
|
@@ -260,41 +1636,20 @@ select is(
|
|
|
260
1636
|
-- asserted, because an advisory lock only excludes callers that also take it.
|
|
261
1637
|
-- ===========================================================================
|
|
262
1638
|
|
|
263
|
-
-- CPR-6. Source bound.
|
|
1639
|
+
-- CPR-6. Source bound. The population is `col_write_re('boards','claim_policy')` — a statement
|
|
1640
|
+
-- that ASSIGNS the column, positionally, over the LITERAL-KEEPING body so a `set claim_policy`
|
|
1641
|
+
-- assembled inside an `execute format(...)` string is still seen. It used to be a site-local
|
|
1642
|
+
-- composition, `stmt_re(...) || '[^;]*' || col_frag(...)`, which read `update boards set slug = x
|
|
1643
|
+
-- where claim_policy = y` as a writer (R1a) and a literal `'a;b'` as a statement boundary (P2a);
|
|
1644
|
+
-- the COPY, INSERT-with-no-column-list and SELECT-INTO arms it carried are inside the matcher now,
|
|
1645
|
+
-- pinned by CPR-0's rows rather than by this comment.
|
|
264
1646
|
select is(
|
|
265
1647
|
(select coalesce(string_agg(p.proname, ', ' order by p.proname), '')
|
|
266
1648
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
267
1649
|
where n.nspname not in ('pg_catalog', 'information_schema', 'agent_board_test')
|
|
268
1650
|
and n.nspname !~ '^pg_'
|
|
269
1651
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
270
|
-
and agent_board_test.stripped_body(p.oid, true)
|
|
271
|
-
~ (agent_board_test.stmt_re('update|merge into|insert into', 'boards')
|
|
272
|
-
|| '[^;]*' || agent_board_test.col_frag('claim_policy')
|
|
273
|
-
-- COPY, parsed by POSITION rather than by token. Three things this gets right that a
|
|
274
|
-
-- verb-plus-token test did not:
|
|
275
|
-
-- * `copy boards from …` with NO column list writes EVERY column, claim_policy
|
|
276
|
-
-- included, and contains no claim_policy token — the widest form of the verb.
|
|
277
|
-
-- * the direction word must follow the relation and its optional column list, so
|
|
278
|
-
-- `copy boards to '/tmp/from.csv'` cannot match on a PATH containing "from".
|
|
279
|
-
-- (Literals are kept by this scan, so the path really is in the text.)
|
|
280
|
-
-- * a column list that does NOT name claim_policy (`copy boards (slug) from …`)
|
|
281
|
-
-- writes only that column and is correctly NOT a member: the optional group fails
|
|
282
|
-
-- to match it, and the direction word is then not where the pattern requires.
|
|
283
|
-
|| '|' || agent_board_test.ci_re('copy') || '\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
284
|
-
|| agent_board_test.qname_frag('boards')
|
|
285
|
-
|| '\s*(?:\([^)]*' || agent_board_test.col_frag('claim_policy') || '[^)]*\)\s*)?'
|
|
286
|
-
|| agent_board_test.ci_re('from') || '(?![A-Za-z0-9_])'
|
|
287
|
-
-- INSERT WITH NO COLUMN LIST, the same shape as COPY without one and a PRE-EXISTING
|
|
288
|
-
-- hole rather than one this change introduced: `insert into boards values (…)` and
|
|
289
|
-
-- `insert into boards select …` write EVERY column, claim_policy included, and carry
|
|
290
|
-
-- no claim_policy token. COPY was special-cased and INSERT was not.
|
|
291
|
-
-- The INSERT-without-column-list rule is a NAMED OBJECT, embedded VERBATIM — the frag,
|
|
292
|
-
-- not `replace(re, '(?c)', '')`. CPR-0's rows compile this same frag, so row and
|
|
293
|
-
-- production are one object rather than two strings that agree today.
|
|
294
|
-
|| '|' || agent_board_test.insert_all_columns_frag('boards')
|
|
295
|
-
|| '|\m' || agent_board_test.ci_re('select') || '(?![A-Za-z0-9_])[^;]*\m'
|
|
296
|
-
|| agent_board_test.ci_re('into') || '(?![A-Za-z0-9_])[^;]*'
|
|
297
|
-
|| agent_board_test.qname_frag('boards') || '[^;]*' || agent_board_test.col_frag('claim_policy'))),
|
|
1652
|
+
and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.col_write_re('boards', 'claim_policy')),
|
|
298
1653
|
'set_board_claim_policy',
|
|
299
1654
|
'CPR-6 SOLE WRITER (source): across EVERY non-system schema, exactly one function has a statement targeting boards that mentions claim_policy — a STATEMENT test rather than an enumeration of assignment spellings, so the multi-column form `set (slug, claim_policy) = (…)` lands here too, and a SECURITY DEFINER writer in another schema cannot bypass the premise by living outside agent_board');
|
|
300
1655
|
|
|
@@ -334,7 +1689,7 @@ select is(
|
|
|
334
1689
|
(agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.qname_re('claims'))::text, ', ' order by p.proname)
|
|
335
1690
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
336
1691
|
where n.nspname = 'agent_board'
|
|
337
|
-
and agent_board_test.stripped_body(p.oid, true)
|
|
1692
|
+
and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.re('\m' || agent_board_test.ci_re('execute') || '\M')),
|
|
338
1693
|
'get_bundle_compatibility:mentions_boards=false/mentions_threads=false/mentions_claims=false, mark_retention_metadata:mentions_boards=false/mentions_threads=false/mentions_claims=false',
|
|
339
1694
|
'CPR-9 SOLE WRITER (EXECUTE token): the functions in agent_board that contain an `execute` token are these and neither names boards, threads or claims — a third, or either one gaining any of the three, fails here. The range is that token, not dynamic SQL in general: a statement assembled and run some other way is outside it. The claims field is what makes a dynamic claims writer visible, since CPR-14a strips literals and cannot see one');
|
|
340
1695
|
|
|
@@ -378,10 +1733,12 @@ select is(
|
|
|
378
1733
|
-- valid forms were unmatched: the multi-column form `set (board_id, …) = (…)`, a
|
|
379
1734
|
-- `MERGE … WHEN MATCHED THEN UPDATE SET board_id`, and a plain INSERT naming board_id in its COLUMN
|
|
380
1735
|
-- LIST rather than an assignment. The population is now "a statement targeting threads that mentions
|
|
381
|
-
-- board_id at all", which
|
|
382
|
-
--
|
|
383
|
-
--
|
|
384
|
-
--
|
|
1736
|
+
-- board_id at all", which admitted every spelling of assignment — and every READ of the column in
|
|
1737
|
+
-- a WHERE clause (R1a), which is a false member with a label, not a writer. The population is now
|
|
1738
|
+
-- `col_write_re('threads','board_id')`: a statement that ASSIGNS the column, positionally — scalar
|
|
1739
|
+
-- SET, the multi-column SET row, MERGE's UPDATE SET, an INSERT naming it in its column list, and
|
|
1740
|
+
-- the no-column-list INSERT / COPY / SELECT INTO forms that write every column — pinned by CPR-0's
|
|
1741
|
+
-- rows. Measured: today's population is unchanged.
|
|
385
1742
|
--
|
|
386
1743
|
-- CPR-12. And the only one that assigns `board_id` does it FIRST-BOARD-WINS. That guard is what
|
|
387
1744
|
-- makes the transition null -> board and never board -> board, which is why locking the board
|
|
@@ -391,14 +1748,16 @@ select is(
|
|
|
391
1748
|
select is(
|
|
392
1749
|
(select coalesce(string_agg(p.proname || '[' ||
|
|
393
1750
|
(case when agent_board_test.stripped_body(p.oid, true)
|
|
394
|
-
~ '
|
|
1751
|
+
~ agent_board_test.re(agent_board_test.col_assign_frag('threads', 'board_id', agent_board_test.bare_ok(p.oid))
|
|
1752
|
+
|| '\s*' || agent_board_test.ident_re('p_board_id') || agent_board_test.same_stmt()
|
|
1753
|
+
|| '\m' || agent_board_test.ci_re('where') || '\s+' || agent_board_test.col_ref_frag('board_id')
|
|
1754
|
+
|| '\s+' || agent_board_test.ci_re('is null') || '\M')
|
|
395
1755
|
then 'first-board-wins' else 'UNGUARDED' end) || ']', ', ' order by p.proname), '<none>')
|
|
396
1756
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
397
1757
|
where n.nspname not in ('pg_catalog', 'information_schema', 'agent_board_test')
|
|
398
1758
|
and n.nspname !~ '^pg_'
|
|
399
1759
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
400
|
-
and agent_board_test.stripped_body(p.oid, true)
|
|
401
|
-
~ (agent_board_test.stmt_re('update|merge into|insert into', 'threads') || '[^;]*' || agent_board_test.col_frag('board_id'))),
|
|
1760
|
+
and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.col_write_re('threads', 'board_id')),
|
|
402
1761
|
'_upsert_delivery_recipient[first-board-wins]',
|
|
403
1762
|
'CPR-12 BOARD IMMUTABILITY: the sole writer of threads.board_id stamps only where it is null (OBSGATE first-board-wins), so a board can be gained but never moved');
|
|
404
1763
|
|
|
@@ -485,7 +1844,7 @@ select is(
|
|
|
485
1844
|
(select coalesce(string_agg(p.proname, ', ' order by p.proname), '<none>')
|
|
486
1845
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
487
1846
|
where n.nspname = 'agent_board'
|
|
488
|
-
and agent_board_test.stripped_body(p.oid, true) ~
|
|
1847
|
+
and agent_board_test.stripped_body(p.oid, true) ~ agent_board_test.row_lock_re('claims')),
|
|
489
1848
|
'<none>',
|
|
490
1849
|
'CPR-14 LOCK ORDER: no function in agent_board takes a claims row lock through an EXPLICIT locking clause (for update / for no key update / for share / for key share) — the spelling that could sit anywhere in a body; the UPDATE-shaped row locks, which CPR-14a enumerates and orders, are not this assertion''s subject');
|
|
491
1850
|
|
|
@@ -563,15 +1922,8 @@ select is(
|
|
|
563
1922
|
select proname,
|
|
564
1923
|
regexp_instr(body, agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0) as lock_pos,
|
|
565
1924
|
least(
|
|
566
|
-
coalesce(nullif(regexp_instr(body,
|
|
567
|
-
|
|
568
|
-
1, 1, 0, 'i'), 0), 2147483647),
|
|
569
|
-
coalesce(nullif(regexp_instr(body,
|
|
570
|
-
agent_board_test.qname_re('claims') || '[^;]*\m' || agent_board_test.ci_re('for') || '\s+(?:' || agent_board_test.ci_re('no key') || '\s+)?(?:' || agent_board_test.ci_re('update') || '|' || agent_board_test.ci_re('share') || '|' || agent_board_test.ci_re('key share') || ')(?![A-Za-z0-9_])',
|
|
571
|
-
1, 1, 0, 'i'), 0), 2147483647),
|
|
572
|
-
coalesce(nullif(regexp_instr(body,
|
|
573
|
-
agent_board_test.stmt_re('insert into', 'claims'),
|
|
574
|
-
1, 1, 0, 'i'), 0), 2147483647)
|
|
1925
|
+
coalesce(nullif(regexp_instr(body, agent_board_test.write_re('claims'), 1, 1, 0), 0), 2147483647),
|
|
1926
|
+
coalesce(nullif(regexp_instr(body, agent_board_test.row_lock_re('claims'), 1, 1, 0), 0), 2147483647)
|
|
575
1927
|
) as row_pos
|
|
576
1928
|
from bodies
|
|
577
1929
|
)
|
|
@@ -623,12 +1975,16 @@ select is(
|
|
|
623
1975
|
select oid, proname, body from bodies where body ~ agent_board_test.qname_re('claims')
|
|
624
1976
|
), censused as (
|
|
625
1977
|
select oid from mentioners
|
|
626
|
-
where body ~ agent_board_test.
|
|
627
|
-
or body ~
|
|
628
|
-
|
|
1978
|
+
where body ~ agent_board_test.write_re('claims')
|
|
1979
|
+
or body ~ agent_board_test.row_lock_re('claims')
|
|
1980
|
+
), class_rels as (
|
|
1981
|
+
-- every relation in agent_board, from pg_class: a write to ANY of them is the verb.
|
|
1982
|
+
select array_agg(c.relname order by c.relname) as rels
|
|
1983
|
+
from pg_class c join pg_namespace n on n.oid = c.relnamespace
|
|
1984
|
+
where n.nspname = 'agent_board' and c.relkind in ('r', 'p', 'v', 'm', 'f')
|
|
629
1985
|
)
|
|
630
1986
|
select coalesce(string_agg(m.oid::regprocedure::text || '[' ||
|
|
631
|
-
(case when m.body
|
|
1987
|
+
(case when m.body ~ agent_board_test.writes_any_re((select rels from class_rels))
|
|
632
1988
|
then 'HAS-WRITE-VERB' else 'no-write-verb' end) || ']', ', ' order by m.oid::regprocedure::text), '<none>')
|
|
633
1989
|
from mentioners m
|
|
634
1990
|
where m.oid not in (select oid from censused)),
|
|
@@ -665,9 +2021,11 @@ select is(
|
|
|
665
2021
|
', ' order by x.proname)
|
|
666
2022
|
from (select p.proname,
|
|
667
2023
|
substring(agent_board_test.stripped_body(p.oid)
|
|
668
|
-
from
|
|
2024
|
+
from agent_board_test.re(agent_board_test.call_frag('_claimgate_lock_board_policy')
|
|
2025
|
+
|| '\s*(' || agent_board_test.ident_any_frag() || ')\s*\)')) as locked,
|
|
669
2026
|
substring(agent_board_test.stripped_body(p.oid)
|
|
670
|
-
from
|
|
2027
|
+
from agent_board_test.re(agent_board_test.call_frag('_claimgate_board_claim_policy_of')
|
|
2028
|
+
|| '\s*(' || agent_board_test.ident_any_frag() || ')\s*\)')) as resolved
|
|
671
2029
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
672
2030
|
where n.nspname = 'agent_board'
|
|
673
2031
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
@@ -706,7 +2064,8 @@ select is(
|
|
|
706
2064
|
(select count(*) from regexp_matches(agent_board_test.stripped_body(p.oid),
|
|
707
2065
|
agent_board_test.call_re('_claimgate_board_claim_policy_of'), 'g')) as resolvers,
|
|
708
2066
|
agent_board_test.stripped_body(p.oid)
|
|
709
|
-
|
|
2067
|
+
~ agent_board_test.re(agent_board_test.call_frag('_claimgate_board_claim_policy_of')
|
|
2068
|
+
|| '\s*' || agent_board_test.ident_any_frag() || '\s*\)') as bare
|
|
710
2069
|
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
|
|
711
2070
|
where n.nspname = 'agent_board'
|
|
712
2071
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
@@ -780,20 +2139,19 @@ select is(
|
|
|
780
2139
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
781
2140
|
), rowlockers as (
|
|
782
2141
|
select oid from fns
|
|
783
|
-
where body ~ agent_board_test.
|
|
784
|
-
or body ~ agent_board_test.
|
|
785
|
-
or body ~ (agent_board_test.qname_re('claims') || '[^;]*\m' || agent_board_test.ci_re('for') || '\s+(?:' || agent_board_test.ci_re('no key') || '\s+)?(?:' || agent_board_test.ci_re('update') || '|' || agent_board_test.ci_re('share') || '|' || agent_board_test.ci_re('key share') || ')(?![A-Za-z0-9_])')
|
|
2142
|
+
where body ~ agent_board_test.write_re('claims')
|
|
2143
|
+
or body ~ agent_board_test.row_lock_re('claims')
|
|
786
2144
|
), reaches_rowlock as (
|
|
787
2145
|
select distinct c.root_oid as oid
|
|
788
2146
|
from agent_board_test.fn_call_closure() c
|
|
789
2147
|
where c.reached_oid in (select oid from rowlockers)
|
|
790
2148
|
), edges as (
|
|
791
2149
|
select r.proname as caller, e.proname as callee,
|
|
792
|
-
regexp_instr(r.body,
|
|
2150
|
+
regexp_instr(r.body, agent_board_test.call_re(e.proname, agent_board_test.bare_ok(r.oid)), 1, 1, 0) as call_pos,
|
|
793
2151
|
regexp_instr(r.body, agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0) as lock_pos
|
|
794
2152
|
from fns r join fns e on e.oid <> r.oid
|
|
795
2153
|
where e.oid in (select oid from reaches_rowlock)
|
|
796
|
-
and r.body ~ agent_board_test.call_re(e.proname)
|
|
2154
|
+
and r.body ~ agent_board_test.call_re(e.proname, agent_board_test.bare_ok(r.oid))
|
|
797
2155
|
and regexp_instr(r.body, agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0) > 0
|
|
798
2156
|
)
|
|
799
2157
|
select coalesce(string_agg(distinct caller || '->' || callee, ', '
|
|
@@ -817,9 +2175,8 @@ select is(
|
|
|
817
2175
|
and p.prolang <> (select oid from pg_language where lanname = 'internal')
|
|
818
2176
|
), rowlockers as (
|
|
819
2177
|
select oid from fns
|
|
820
|
-
where body ~ agent_board_test.
|
|
821
|
-
or body ~ agent_board_test.
|
|
822
|
-
or body ~ (agent_board_test.qname_re('claims') || '[^;]*\m' || agent_board_test.ci_re('for') || '\s+(?:' || agent_board_test.ci_re('no key') || '\s+)?(?:' || agent_board_test.ci_re('update') || '|' || agent_board_test.ci_re('share') || '|' || agent_board_test.ci_re('key share') || ')(?![A-Za-z0-9_])')
|
|
2178
|
+
where body ~ agent_board_test.write_re('claims')
|
|
2179
|
+
or body ~ agent_board_test.row_lock_re('claims')
|
|
823
2180
|
)
|
|
824
2181
|
select coalesce(string_agg(distinct c.root_name, ', ' order by c.root_name), '<none>')
|
|
825
2182
|
from agent_board_test.fn_call_closure() c
|
|
@@ -830,267 +2187,6 @@ select is(
|
|
|
830
2187
|
'CPR-14d LOCK ORDER (callee direction, membership half): these are the functions that reach BOTH a claims row-locker and the board lock through their call closures — a fourth, wrapper or composer alike, is named here and must be decided about rather than exempted by a predicate');
|
|
831
2188
|
|
|
832
2189
|
-- ---------------------------------------------------------------------------
|
|
833
|
-
-- CPR-0 / CPR-0a. THE MATCHERS' BEHAVIOUR TABLE, AND A GATE ON THE TABLE ITSELF.
|
|
834
|
-
--
|
|
835
|
-
-- WHY BOTH. The identifier matchers drifted four times across three review rounds, and every
|
|
836
|
-
-- version passed its accept cases. So a table was added. Then the SAME defect appeared INSIDE the
|
|
837
|
-
-- table three times: rows that pass for a reason unrelated to the rule they are named for — first
|
|
838
|
-
-- r06 (tested `$` through an adjacency that rejected it earlier), then a12/r13 (compiled a
|
|
839
|
-
-- reconstruction rather than the production object), then a13/a14/a15 (keyword branches that the
|
|
840
|
-
-- subject text could never exercise). Each was found by a different reviewer, after the previous
|
|
841
|
-
-- fix, by the same procedure: revert a rule and see which rows move.
|
|
842
|
-
--
|
|
843
|
-
-- THREE OCCURRENCES IS ENOUGH EVIDENCE THAT A HAND-COMPILED REVERT MAP CANNOT BE TRUSTED, because
|
|
844
|
-
-- compiling it is the same act as writing the rows. So the map is mechanical now: CPR-0a reverts
|
|
845
|
-
-- each rule in turn, re-evaluates every row, and requires at least one row to flip. A rule with no
|
|
846
|
-
-- flipping row FAILS AND NAMES ITSELF — which is what would have caught all three, and none of
|
|
847
|
-
-- which a human reading the table did.
|
|
848
|
-
--
|
|
849
|
-
-- The rows are DATA rather than inline expressions so both assertions evaluate the same list: a
|
|
850
|
-
-- row CPR-0 checks and CPR-0a cannot re-evaluate would be back to two lists that agree today.
|
|
851
|
-
-- ---------------------------------------------------------------------------
|
|
852
|
-
create temp table _mrow (label text primary key, expr text, want boolean) on commit drop;
|
|
853
|
-
|
|
854
|
-
insert into _mrow(label, expr, want) values
|
|
855
|
-
-- ACCEPTS: spellings PostgreSQL resolves to OUR object
|
|
856
|
-
('a01-qualified', $x$'update agent_board.claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
857
|
-
('a02-unqualified', $x$'update claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
858
|
-
('a03-upper-unquoted', $x$'UPDATE AGENT_BOARD.CLAIMS SET x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
859
|
-
('a04-quoted-lower', $x$'update "agent_board"."claims" set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
860
|
-
('a05-verb-newline', $x$'insert' || chr(10) || 'into claims v' ~ agent_board_test.stmt_re('insert into','claims')$x$, true),
|
|
861
|
-
('a06-verb-2spaces', $x$'delete from claims where x' ~ agent_board_test.stmt_re('delete from','claims')$x$, true),
|
|
862
|
-
('a07-verb-tab', $x$'merge' || chr(9) || 'into claims m' ~ agent_board_test.stmt_re('merge into','claims')$x$, true),
|
|
863
|
-
-- a08 gates ci_re's OWN space rule, which a05-a07 do NOT: they exercise whitespace inside a verb
|
|
864
|
-
-- through stmt_re, which would still work if ci_re emitted a literal space and stmt_re rewrote it.
|
|
865
|
-
('a08-cire-phrase', $x$'no' || chr(10) || 'key' ~ ('(?c)' || agent_board_test.ci_re('no key'))$x$, true),
|
|
866
|
-
('a09-inheritance', $x$'update agent_board.boards* set x' ~ agent_board_test.stmt_re('update','boards')$x$, true),
|
|
867
|
-
('a10-call-space', $x$'perform _claimgate_lock_board_policy (v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, true),
|
|
868
|
-
('a11-call-quoted', $x$'perform agent_board."_claimgate_lock_board_policy"(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, true),
|
|
869
|
-
('a12-col-upper', $x$'set CLAIM_POLICY = x' ~ agent_board_test.col_frag('claim_policy')$x$, true),
|
|
870
|
-
('a13-ins-novals', $x$'insert into agent_board.boards values (1,2)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
871
|
-
('a14-ins-select', $x$'insert into agent_board.boards select (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
872
|
-
-- a15/a16 cover two INSERT FORMS the rule must census. They do NOT gate a keyword exclusion, and
|
|
873
|
-
-- that is stated rather than implied: DEFAULT is only ever followed by VALUES and OVERRIDING by
|
|
874
|
-
-- SYSTEM/USER, so neither can be mistaken for an alias-before-`(`. The exclusions for them were
|
|
875
|
-
-- therefore DEAD CODE and have been removed rather than kept with rows that cannot gate them.
|
|
876
|
-
('a15-ins-default', $x$'insert into agent_board.boards default values' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
877
|
-
('a16-ins-overriding', $x$'insert into agent_board.boards overriding user value values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
878
|
-
('a17-ins-re-eq-frag', $x$agent_board_test.insert_all_columns_re('boards') = '(?c)' || agent_board_test.insert_all_columns_frag('boards')$x$, true),
|
|
879
|
-
-- REJECTS: spellings PostgreSQL resolves ELSEWHERE
|
|
880
|
-
('a18-verb-rel-spaces', $x$'update agent_board.claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, true),
|
|
881
|
-
('r01-other-schema', $x$'update other.claims set x' ~ agent_board_test.stmt_re('update','claims')$x$, false),
|
|
882
|
-
('r02-dot-space', $x$'perform other . _claimgate_lock_board_policy(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, false),
|
|
883
|
-
('r03-quoted-upper', $x$'update "AGENT_BOARD"."claims" set x' ~ agent_board_test.stmt_re('update','claims')$x$, false),
|
|
884
|
-
('r04-prefix-word', $x$'update dashboards set x' ~ agent_board_test.stmt_re('update','boards')$x$, false),
|
|
885
|
-
('r05-dollar-lead', $x$'perform x$_claimgate_lock_board_policy(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, false),
|
|
886
|
-
('r06-dollar-rel', $x$'from x$claims c' ~ agent_board_test.qname_re('claims')$x$, false),
|
|
887
|
-
('r07-nonascii-lead', $x$'perform ' || chr(233) || '_claimgate_lock_board_policy(v)' ~ agent_board_test.call_re('_claimgate_lock_board_policy')$x$, false),
|
|
888
|
-
('r08-nonascii-trail', $x$'from claims' || chr(233) || ' c' ~ agent_board_test.qname_re('claims')$x$, false),
|
|
889
|
-
('r09-col-nonascii', $x$'set claim_policy' || chr(233) || ' = x' ~ agent_board_test.col_frag('claim_policy')$x$, false),
|
|
890
|
-
('r10-col-dollar', $x$'set x$claim_policy = x' ~ agent_board_test.col_frag('claim_policy')$x$, false),
|
|
891
|
-
('r11-i-flag', $x$regexp_instr('x "AGENT_BOARD"."claims" y', agent_board_test.qname_re('claims'), 1, 1, 0, 'i') > 0$x$, false),
|
|
892
|
-
('r12-ci-operator', $x$'x "AGENT_BOARD"."claims" y' ~* agent_board_test.qname_re('claims')$x$, false),
|
|
893
|
-
('r13-ins-collist', $x$'insert into agent_board.boards (slug) values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, false),
|
|
894
|
-
('r14-ins-alias', $x$'insert into agent_board.boards as b(slug) values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, false),
|
|
895
|
-
('r16-call-i-flag', $x$regexp_instr('perform "AGENT_BOARD"."_claimgate_lock_board_policy"(v)', agent_board_test.call_re('_claimgate_lock_board_policy'), 1, 1, 0, 'i') > 0$x$, false),
|
|
896
|
-
('r15-ins-qalias', $x$'insert into agent_board.boards as "b"(slug) values (1)' ~ agent_board_test.insert_all_columns_frag('boards')$x$, false);
|
|
897
|
-
|
|
898
|
-
create function pg_temp.mverdict(p_expr text) returns boolean
|
|
899
|
-
language plpgsql as $f$
|
|
900
|
-
declare v boolean;
|
|
901
|
-
begin
|
|
902
|
-
execute format('select (%s)', p_expr) into v;
|
|
903
|
-
return coalesce(v, false);
|
|
904
|
-
exception when others then
|
|
905
|
-
-- A row whose expression cannot compile is a FAILED row, not a skipped one: returning null here
|
|
906
|
-
-- would let a broken pattern read as agreement with a `false` expectation.
|
|
907
|
-
return not (select want from _mrow where expr = p_expr);
|
|
908
|
-
end $f$;
|
|
909
|
-
|
|
910
|
-
select is(
|
|
911
|
-
(select coalesce(string_agg(label || '=' || pg_temp.mverdict(expr)::text, ', ' order by label), '<none>')
|
|
912
|
-
from _mrow),
|
|
913
|
-
(select coalesce(string_agg(label || '=' || want::text, ', ' order by label), '<none>') from _mrow),
|
|
914
|
-
'CPR-0 MATCHER BEHAVIOUR: over PLAIN and DOUBLE-QUOTED identifiers, the matchers accept the spellings PostgreSQL resolves to our object and reject those it resolves elsewhere — Unicode-escape identifiers (U&"...") are OUT OF SCOPE and unhandled, so this is not a claim about every spelling');
|
|
915
|
-
|
|
916
|
-
-- CPR-0a. EVERY RULE HAS A ROW THAT FAILS WITHOUT IT — compiled by the database, not by hand.
|
|
917
|
-
-- ONE BEHAVIOUR PER ENTRY, AND EACH NAMES THE ROW THAT MUST FLIP. Both are corrections.
|
|
918
|
-
--
|
|
919
|
-
-- THE SPLIT PAID FOR ITSELF ON ITS FIRST RUN, which is the answer to "why are these so finely
|
|
920
|
-
-- divided": separating the behaviours immediately exposed FOUR matcher rules the coarser hand list
|
|
921
|
-
-- had never reverted — ident_re's quoted alternative, qname_frag's OPTIONAL qualifier, call_re's own
|
|
922
|
-
-- director, and stmt_re's `\s+` between verb and relation. Two of them had no row at all (a18, r16
|
|
923
|
-
-- were added for them). A reviewer tempted to recombine entries should read that as the cost.
|
|
924
|
-
--
|
|
925
|
-
-- NO DESCRIPTION CARRIES A COUNT, and the rule that replaced is worth recording because it was
|
|
926
|
-
-- WRONG in a way that took a reviewer to see. It said a count is "safe" when it sits beside its own
|
|
927
|
-
-- assertion's expected value, since the two would fail together. They do not. PROXIMITY IS NOT
|
|
928
|
-
-- DERIVATION: nothing computes the count from the expected value, so the assertion fails on drift
|
|
929
|
-
-- only until a maintainer updates the expected value — which is the normal maintenance path — and
|
|
930
|
-
-- from that moment the count is silently wrong with nothing failing.
|
|
931
|
-
--
|
|
932
|
-
-- Measured rather than argued: add a function to agent_board_test, and CPR-0b fails; update its
|
|
933
|
-
-- expected value the way a maintainer would, and it passes again while any count in its prose is now
|
|
934
|
-
-- false. So the "safe" category did not exist. There are only counts that are easier to notice and
|
|
935
|
-
-- counts that are harder, and the durable move is the one this package already made when its runner
|
|
936
|
-
-- dropped "28 tests" and "42-migration union": delete them. The expected values enumerate every
|
|
937
|
-
-- member by name; a reader who wants a number can count one that cannot drift.
|
|
938
|
-
--
|
|
939
|
-
-- THE RECURSION IS THE POINT, not an aside: the rule written to prevent stale counts made a claim
|
|
940
|
-
-- broader than its own mechanism supported — the same shape as every finding in this review, in the
|
|
941
|
-
-- sentence codifying the lesson from the previous one.
|
|
942
|
-
--
|
|
943
|
-
-- IN THESE TWO ASSERTIONS the expected values enumerate every entry by name, so no count appears in
|
|
944
|
-
-- either description: it would add nothing a reader cannot see and one more thing that can go stale.
|
|
945
|
-
--
|
|
946
|
-
-- COMPOUND ENTRIES LET A COVERED BRANCH CONCEAL AN UNCOVERED SIBLING: `keyword exclusions` reverted
|
|
947
|
-
-- VALUES and SELECT together, and `lead_guard` reverted its dot-whitespace and non-ASCII guards
|
|
948
|
-
-- together, so deleting one row still left the rule reporting as gated through the other. That is
|
|
949
|
-
-- the r06 defect at the RULE level — the map certifying a rule it only half tests.
|
|
950
|
-
--
|
|
951
|
-
-- AND "SOME ROW FLIPPED" IS NOT "THE ROW FOR THIS RULE FLIPPED". The gate now requires the NAMED
|
|
952
|
-
-- row, so a rule cannot borrow coverage from an unrelated one.
|
|
953
|
-
create temp table _mrule (rule_name text primary key, fn text, expect_row text, revert_sql text) on commit drop;
|
|
954
|
-
|
|
955
|
-
insert into _mrule(rule_name, fn, expect_row, revert_sql) values
|
|
956
|
-
('ci_re: a space becomes \s+', 'agent_board_test.ci_re(text)', 'a08-cire-phrase', $r$
|
|
957
|
-
create or replace function agent_board_test.ci_re(p_text text) returns text
|
|
958
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
959
|
-
select coalesce(string_agg(case when c ~ '[A-Za-z]' then '[' || upper(c) || lower(c) || ']' else c end, ''), '')
|
|
960
|
-
from regexp_split_to_table(p_text, '') as c $b$; $r$),
|
|
961
|
-
('ident_chars: $ is an identifier character', 'agent_board_test.ident_chars()', 'r05-dollar-lead', $r$
|
|
962
|
-
create or replace function agent_board_test.ident_chars() returns text
|
|
963
|
-
language sql immutable set search_path to pg_catalog as $b$ select 'A-Za-z0-9_' $b$; $r$),
|
|
964
|
-
('ident_re: the quoted alternative', 'agent_board_test.ident_re(text)', 'a04-quoted-lower', $r$
|
|
965
|
-
create or replace function agent_board_test.ident_re(p_name text) returns text
|
|
966
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
967
|
-
select '(?:' || agent_board_test.ci_re(p_name) || ')' $b$; $r$),
|
|
968
|
-
('lead_guard: reject a dot with whitespace', 'agent_board_test.lead_guard()', 'r02-dot-space', $r$
|
|
969
|
-
create or replace function agent_board_test.lead_guard() returns text
|
|
970
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
971
|
-
select '(?<![' || agent_board_test.ident_chars() || '."])(?<![^\x00-\x7F])' $b$; $r$),
|
|
972
|
-
('lead_guard: reject a leading non-ASCII letter', 'agent_board_test.lead_guard()', 'r07-nonascii-lead', $r$
|
|
973
|
-
create or replace function agent_board_test.lead_guard() returns text
|
|
974
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
975
|
-
select '(?<![' || agent_board_test.ident_chars() || '."])(?<!\.\s*)' $b$; $r$),
|
|
976
|
-
('trail_guard: reject a trailing non-ASCII letter', 'agent_board_test.trail_guard()', 'r08-nonascii-trail', $r$
|
|
977
|
-
create or replace function agent_board_test.trail_guard() returns text
|
|
978
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
979
|
-
select '(?![' || agent_board_test.ident_chars() || '])' $b$; $r$),
|
|
980
|
-
('qname_frag: the qualifier is OPTIONAL', 'agent_board_test.qname_frag(text)', 'a02-unqualified', $r$
|
|
981
|
-
create or replace function agent_board_test.qname_frag(p_name text) returns text
|
|
982
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
983
|
-
select agent_board_test.lead_guard() || agent_board_test.ident_re('agent_board')
|
|
984
|
-
|| '\s*\.\s*' || agent_board_test.ident_re(p_name) || agent_board_test.trail_guard() $b$; $r$),
|
|
985
|
-
('qname_re: the (?c) director', 'agent_board_test.qname_re(text)', 'r11-i-flag', $r$
|
|
986
|
-
create or replace function agent_board_test.qname_re(p_name text) returns text
|
|
987
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
988
|
-
select agent_board_test.qname_frag(p_name) $b$; $r$),
|
|
989
|
-
('call_frag: whitespace may precede the paren', 'agent_board_test.call_frag(text)', 'a10-call-space', $r$
|
|
990
|
-
create or replace function agent_board_test.call_frag(p_name text) returns text
|
|
991
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
992
|
-
select agent_board_test.lead_guard() || '(?:' || agent_board_test.ident_re('agent_board')
|
|
993
|
-
|| '\s*\.\s*)?' || agent_board_test.ident_re(p_name) || '\(' $b$; $r$),
|
|
994
|
-
('call_re: the (?c) director', 'agent_board_test.call_re(text)', 'r16-call-i-flag', $r$
|
|
995
|
-
create or replace function agent_board_test.call_re(p_name text) returns text
|
|
996
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
997
|
-
select agent_board_test.call_frag(p_name) $b$; $r$),
|
|
998
|
-
('col_frag: the column token carries identifier rules', 'agent_board_test.col_frag(text)', 'a12-col-upper', $r$
|
|
999
|
-
create or replace function agent_board_test.col_frag(p_name text) returns text
|
|
1000
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
1001
|
-
select agent_board_test.lead_guard() || p_name || agent_board_test.trail_guard() $b$; $r$),
|
|
1002
|
-
('stmt_re: \s+ between the verb and the relation', 'agent_board_test.stmt_re(text,text)', 'a18-verb-rel-spaces', $r$
|
|
1003
|
-
create or replace function agent_board_test.stmt_re(p_verbs text, p_rel text) returns text
|
|
1004
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
1005
|
-
select '(?c)(?:' || (select string_agg(agent_board_test.ci_re(v), '|')
|
|
1006
|
-
from regexp_split_to_table(p_verbs, '\|') as v)
|
|
1007
|
-
|| ') (?:' || agent_board_test.ci_re('only') || '\s+)?' || agent_board_test.qname_frag(p_rel) $b$; $r$),
|
|
1008
|
-
('insert rule: the VALUES exclusion', 'agent_board_test.insert_all_columns_frag(text)', 'a13-ins-novals', $r$
|
|
1009
|
-
create or replace function agent_board_test.insert_all_columns_frag(p_rel text) returns text
|
|
1010
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
1011
|
-
select agent_board_test.ci_re('insert into') || '\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
1012
|
-
|| agent_board_test.qname_frag(p_rel)
|
|
1013
|
-
|| '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
|
|
1014
|
-
|| agent_board_test.ci_re('select') || '(?![A-Za-z0-9_$]))'
|
|
1015
|
-
|| '(?:"[^"]*"|[A-Za-z_][A-Za-z0-9_$]*))?\s*\()' $b$; $r$),
|
|
1016
|
-
('insert rule: the SELECT exclusion', 'agent_board_test.insert_all_columns_frag(text)', 'a14-ins-select', $r$
|
|
1017
|
-
create or replace function agent_board_test.insert_all_columns_frag(p_rel text) returns text
|
|
1018
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
1019
|
-
select agent_board_test.ci_re('insert into') || '\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
1020
|
-
|| agent_board_test.qname_frag(p_rel)
|
|
1021
|
-
|| '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
|
|
1022
|
-
|| agent_board_test.ci_re('values') || '(?![A-Za-z0-9_$]))'
|
|
1023
|
-
|| '(?:"[^"]*"|[A-Za-z_][A-Za-z0-9_$]*))?\s*\()' $b$; $r$),
|
|
1024
|
-
('insert rule: no backtracking before the paren', 'agent_board_test.insert_all_columns_frag(text)', 'r13-ins-collist', $r$
|
|
1025
|
-
create or replace function agent_board_test.insert_all_columns_frag(p_rel text) returns text
|
|
1026
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
1027
|
-
select agent_board_test.ci_re('insert into') || '\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
1028
|
-
|| agent_board_test.qname_frag(p_rel) || '\s*(?!\()' $b$; $r$),
|
|
1029
|
-
('insert rule: the quoted-alias alternative', 'agent_board_test.insert_all_columns_frag(text)', 'r15-ins-qalias', $r$
|
|
1030
|
-
create or replace function agent_board_test.insert_all_columns_frag(p_rel text) returns text
|
|
1031
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
1032
|
-
select agent_board_test.ci_re('insert into') || '\s+(?:' || agent_board_test.ci_re('only') || '\s+)?'
|
|
1033
|
-
|| agent_board_test.qname_frag(p_rel)
|
|
1034
|
-
|| '(?!(?:\s+(?:' || agent_board_test.ci_re('as') || '\s+)?(?!'
|
|
1035
|
-
|| agent_board_test.ci_re('values') || '(?![A-Za-z0-9_$])|'
|
|
1036
|
-
|| agent_board_test.ci_re('select') || '(?![A-Za-z0-9_$]))'
|
|
1037
|
-
|| '[A-Za-z_][A-Za-z0-9_$]*)?\s*\()' $b$; $r$),
|
|
1038
|
-
('insert re: composed from the frag', 'agent_board_test.insert_all_columns_re(text)', 'a17-ins-re-eq-frag', $r$
|
|
1039
|
-
create or replace function agent_board_test.insert_all_columns_re(p_rel text) returns text
|
|
1040
|
-
language sql immutable set search_path to pg_catalog as $b$
|
|
1041
|
-
select agent_board_test.insert_all_columns_frag(p_rel) $b$; $r$);
|
|
1042
|
-
|
|
1043
|
-
create temp table _mflip (rule_name text, expect_row text, flipped text, named_row_flipped boolean) on commit drop;
|
|
1044
|
-
|
|
1045
|
-
do $mut$
|
|
1046
|
-
declare
|
|
1047
|
-
r record;
|
|
1048
|
-
v_save text;
|
|
1049
|
-
v_hits text;
|
|
1050
|
-
begin
|
|
1051
|
-
for r in select * from _mrule order by rule_name loop
|
|
1052
|
-
v_save := pg_get_functiondef(r.fn::regprocedure);
|
|
1053
|
-
execute r.revert_sql;
|
|
1054
|
-
select coalesce(string_agg(m.label, ' ' order by m.label), '<NONE>')
|
|
1055
|
-
into v_hits
|
|
1056
|
-
from _mrow m
|
|
1057
|
-
where pg_temp.mverdict(m.expr) <> m.want;
|
|
1058
|
-
execute v_save; -- restore before the next rule
|
|
1059
|
-
insert into _mflip
|
|
1060
|
-
values (r.rule_name, r.expect_row, v_hits,
|
|
1061
|
-
v_hits ~ ('(^| )' || r.expect_row || '( |$)'));
|
|
1062
|
-
end loop;
|
|
1063
|
-
end $mut$;
|
|
1064
|
-
|
|
1065
|
-
select is(
|
|
1066
|
-
(select coalesce(string_agg(rule_name || ' (wanted ' || expect_row || ', got ' || flipped || ')',
|
|
1067
|
-
'; ' order by rule_name), '<none>')
|
|
1068
|
-
from _mflip where not named_row_flipped),
|
|
1069
|
-
'<none>',
|
|
1070
|
-
'CPR-0a REVERT MAP (mechanical): reverting each rule makes THE ROW NAMED FOR THAT RULE fail — one behaviour per entry and a named row per entry, so a compound revert cannot let a covered branch conceal an uncovered sibling and no rule can borrow coverage from an unrelated row — the map is compiled by reverting each rule and re-evaluating the rows, not by hand, because three separate rows have now certified axes they did not test and a human reading the table caught none of them');
|
|
1071
|
-
|
|
1072
|
-
-- CPR-0b. AND THE RULE INVENTORY IS RECONCILED AGAINST THE CATALOG, because _mrule is still written
|
|
1073
|
-
-- by hand and a matcher added without an entry would be SILENTLY UNGATED — which contradicts
|
|
1074
|
-
-- CPR-0a's own sentence. Deriving "every rule a regex-building function implements" from its source
|
|
1075
|
-
-- is not mechanical; deriving "every FUNCTION in this schema is either gated or positively exempted"
|
|
1076
|
-
-- is, and it closes the exposure that matters: a new matcher cannot arrive unnoticed.
|
|
1077
|
-
--
|
|
1078
|
-
-- Every function in agent_board_test is classified. A new matcher appears as `NOT-GATED` and fails;
|
|
1079
|
-
-- a new helper appears too and must be exempted deliberately. That is rule 1 applied to the gate's
|
|
1080
|
-
-- own inventory: the classification comes from pg_proc, and the exemptions are asserted positively
|
|
1081
|
-
-- rather than by absence.
|
|
1082
|
-
select is(
|
|
1083
|
-
(select string_agg(p.proname || '=' ||
|
|
1084
|
-
(case when exists (select 1 from _mrule r where r.fn like '%.' || p.proname || '(%')
|
|
1085
|
-
then 'gated' else 'NOT-GATED' end), ', ' order by p.proname)
|
|
1086
|
-
from pg_proc p where p.pronamespace = 'agent_board_test'::regnamespace),
|
|
1087
|
-
'call_frag=gated, call_re=gated, ci_re=gated, col_frag=gated, fn_call_closure=NOT-GATED, '
|
|
1088
|
-
'ident_chars=gated, ident_re=gated, insert_all_columns_frag=gated, insert_all_columns_re=gated, '
|
|
1089
|
-
'lead_guard=gated, lock_edges=NOT-GATED, qname_frag=gated, qname_re=gated, '
|
|
1090
|
-
'row_lock_rank=NOT-GATED, row_locks_conflict=NOT-GATED, stmt_re=gated, strip_sql=NOT-GATED, '
|
|
1091
|
-
'stripped_body=NOT-GATED, stripped_def=NOT-GATED, stripped_viewdef=NOT-GATED, trail_guard=gated',
|
|
1092
|
-
'CPR-0b RULE INVENTORY: every function in agent_board_test is either covered by a CPR-0a rule or positively listed as not a pattern matcher — the NOT-GATED entries are body readers and lock helpers, which build no regex, and the expected value names every one of them; a new matcher added without a rule appears here as NOT-GATED and fails by name');
|
|
1093
|
-
|
|
1094
2190
|
-- CPR-15/16. And no session can take one either — the OPERATION, not a privilege proxy for it.
|
|
1095
2191
|
-- `select ... for update` on claims is refused with 42501 to both roles that can read the table,
|
|
1096
2192
|
-- because a row lock needs UPDATE/DELETE privilege and neither holds it at table or column level.
|
|
@@ -1116,35 +2212,63 @@ select throws_ok(
|
|
|
1116
2212
|
-- THE HELPERS: contract and authorization.
|
|
1117
2213
|
-- ===========================================================================
|
|
1118
2214
|
|
|
2215
|
+
-- CPR-17..21. EACH IS lives_ok + A SEPARATE STATE CHECK (rule 3 / #113), not a bare `is((select
|
|
2216
|
+
-- rpc()), …)`: a resolver that RAISES inside a bare `is` aborts the transaction, pgTAP emits no
|
|
2217
|
+
-- `not ok` for it or for anything after it, and the file "passes" with fewer assertions than its
|
|
2218
|
+
-- plan. The result is captured by name into `_cpr_out` under lives_ok, so a raise is a NAMED `not
|
|
2219
|
+
-- ok` and the paired check then fails BY COMPARISON (no row -> null, never equal to the sentinel)
|
|
2220
|
+
-- rather than raising again; the file runs to plan(N). A null result is stored as the sentinel
|
|
2221
|
+
-- '<null>', so "no row because it raised" and "returned null" are different values.
|
|
2222
|
+
create temp table _cpr_out(site text primary key, v text);
|
|
2223
|
+
|
|
1119
2224
|
-- CPR-17/18. The null fallbacks, which are what make the "board stamped mid-wait" drift
|
|
1120
2225
|
-- CONSERVATIVE rather than a hole: an absent board resolves to the ENFORCING policy.
|
|
2226
|
+
select lives_ok(
|
|
2227
|
+
$$ insert into _cpr_out select 'CPR-17', coalesce(agent_board._claimgate_board_of_thread(null)::text, '<null>') $$,
|
|
2228
|
+
'CPR-17 NULL FALLBACK (runs): the thread-to-board walk accepts a null thread without raising');
|
|
1121
2229
|
select is(
|
|
1122
|
-
(select
|
|
1123
|
-
null
|
|
2230
|
+
(select v from _cpr_out where site = 'CPR-17'),
|
|
2231
|
+
'<null>',
|
|
1124
2232
|
'CPR-17 NULL FALLBACK: a null thread has no board — so nothing is locked and nothing can go stale');
|
|
1125
2233
|
|
|
2234
|
+
select lives_ok(
|
|
2235
|
+
$$ insert into _cpr_out select 'CPR-18', coalesce(agent_board._claimgate_board_claim_policy_of(null)::text, '<null>') $$,
|
|
2236
|
+
'CPR-18 NULL FALLBACK (runs): the board-to-policy half accepts a null board without raising');
|
|
1126
2237
|
select is(
|
|
1127
|
-
(select
|
|
2238
|
+
(select v from _cpr_out where site = 'CPR-18'),
|
|
1128
2239
|
'inherit',
|
|
1129
2240
|
'CPR-18 NULL FALLBACK: an absent board resolves to inherit, the ENFORCING answer — a board stamped onto a null-board thread mid-wait can therefore only make this path stricter, never permissive');
|
|
1130
2241
|
|
|
1131
2242
|
-- CPR-19/20. The composition is the SAME function it replaced. `_claimgate_board_claim_policy` is
|
|
1132
2243
|
-- what the census view resolves through and what BCP-5 pins; decomposing it into the two halves
|
|
1133
|
-
-- the enforcement paths call separately must not have moved its answer on either case.
|
|
2244
|
+
-- the enforcement paths call separately must not have moved its answer on either case. Both sides
|
|
2245
|
+
-- of CPR-19 are captured under lives_ok — the composed resolver AND the two halves — so a raise in
|
|
2246
|
+
-- either is named rather than aborting.
|
|
2247
|
+
select lives_ok(
|
|
2248
|
+
$$ insert into _cpr_out
|
|
2249
|
+
select 'CPR-19', coalesce((select agent_board._claimgate_board_claim_policy(c.thread) from _cpr c)::text, '<null>')
|
|
2250
|
+
union all
|
|
2251
|
+
select 'CPR-19-halves', coalesce((select agent_board._claimgate_board_claim_policy_of(agent_board._claimgate_board_of_thread(c.thread)) from _cpr c)::text, '<null>') $$,
|
|
2252
|
+
'CPR-19 COMPOSITION (runs): the thread-keyed resolver and the composed halves both resolve the board-linked fixture thread without raising');
|
|
1134
2253
|
select is(
|
|
1135
|
-
(select
|
|
1136
|
-
(select
|
|
2254
|
+
(select v from _cpr_out where site = 'CPR-19'),
|
|
2255
|
+
(select v from _cpr_out where site = 'CPR-19-halves'),
|
|
1137
2256
|
'CPR-19 COMPOSITION: for a board-linked thread the thread-keyed resolver and the two halves agree — one definition of the walk, not two');
|
|
1138
2257
|
|
|
2258
|
+
select lives_ok(
|
|
2259
|
+
$$ insert into _cpr_out select 'CPR-20', coalesce((select agent_board._claimgate_board_claim_policy(c.free_thread) from _cpr c)::text, '<null>') $$,
|
|
2260
|
+
'CPR-20 COMPOSITION (runs): the thread-keyed resolver accepts a thread with no board without raising');
|
|
1139
2261
|
select is(
|
|
1140
|
-
(select
|
|
2262
|
+
(select v from _cpr_out where site = 'CPR-20'),
|
|
1141
2263
|
'inherit',
|
|
1142
2264
|
'CPR-20 COMPOSITION: a thread with NO board still resolves inherit — a claim cannot opt itself out by omitting the board (BCP-5''s rule, unchanged by the decomposition)');
|
|
1143
2265
|
|
|
1144
2266
|
-- CPR-21. And the board-linked fixture really is advisory, so CPR-19 is comparing something.
|
|
1145
|
-
-- Without this CPR-19 could agree on 'inherit' twice and prove nothing.
|
|
2267
|
+
-- Without this CPR-19 could agree on 'inherit' twice and prove nothing. Read from the SAME capture
|
|
2268
|
+
-- CPR-19 compared, so it is that comparison's discrimination that is pinned; and CPR-19 itself
|
|
2269
|
+
-- cannot pass on two absent rows, because the sentinel check here names the value.
|
|
1146
2270
|
select is(
|
|
1147
|
-
(select
|
|
2271
|
+
(select v from _cpr_out where site = 'CPR-19'),
|
|
1148
2272
|
'advisory',
|
|
1149
2273
|
'CPR-21 DISCRIMINATION: the fixture board is advisory, so CPR-19 compares a non-default answer rather than agreeing on the fallback twice');
|
|
1150
2274
|
|