@consiliency/agent-board-schema 1.51.0 → 1.53.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.
Files changed (44) hide show
  1. package/fixtures/already-applied.json +4 -0
  2. package/migrations/20261003000100_agent_board_room_roster.sql +115 -0
  3. package/package.json +1 -1
  4. package/schema-manifest.json +12 -3
  5. package/src/tooling.test.mjs +4 -2
  6. package/tests/agent_board_agent_liveness.test.sql +3 -1
  7. package/tests/agent_board_atomic_fleet_onboarding.test.sql +33 -36
  8. package/tests/agent_board_authbound_boundary.test.sql +4 -3
  9. package/tests/agent_board_board_claim_policy.test.sql +17 -6
  10. package/tests/agent_board_claim_subject_gate.test.sql +7 -8
  11. package/tests/agent_board_claimkey.test.sql +9 -6
  12. package/tests/agent_board_claimpolicy_race.test.sql +1466 -342
  13. package/tests/agent_board_consent_emission.test.sql +5 -4
  14. package/tests/agent_board_consent_gating.test.sql +20 -9
  15. package/tests/agent_board_enroll_resumption.test.sql +1 -1
  16. package/tests/agent_board_enrollment_audit_content.test.sql +5 -4
  17. package/tests/agent_board_federated_identity_injective_slug.test.sql +3 -1
  18. package/tests/agent_board_federated_lock_order_class.test.sql +69 -52
  19. package/tests/agent_board_fedsession.test.sql +3 -1
  20. package/tests/agent_board_fedsession_provider_absence_gate.test.sql +9 -4
  21. package/tests/agent_board_fedsession_stored_identity_gate.test.sql +18 -6
  22. package/tests/agent_board_fk_lock_edges.test.sql +64 -5
  23. package/tests/agent_board_fleet_domain_reclaim.test.sql +2 -4
  24. package/tests/agent_board_fleet_machine_hijack.test.sql +2 -4
  25. package/tests/agent_board_fleet_machine_reactivation.test.sql +5 -1
  26. package/tests/agent_board_fleet_machine_reclaim.test.sql +2 -4
  27. package/tests/agent_board_invite_no_downgrade.test.sql +24 -18
  28. package/tests/agent_board_ledger_authority.test.sql +29 -21
  29. package/tests/agent_board_mint_harden.test.sql +5 -5
  30. package/tests/agent_board_outbox_wake_ordering.test.sql +4 -3
  31. package/tests/agent_board_ownerless_renew_quota.test.sql +7 -2
  32. package/tests/agent_board_portal_offline_invariant.test.sql +5 -4
  33. package/tests/agent_board_receipt_subject_gate.test.sql +7 -8
  34. package/tests/agent_board_rolereset.test.sql +5 -3
  35. package/tests/agent_board_room_roster.test.sql +466 -0
  36. package/tests/agent_board_rowid.test.sql +39 -19
  37. package/tests/agent_board_signer_lifecycle.test.sql +24 -13
  38. package/tests/agent_board_source_strip_helper.test.sql +2 -1
  39. package/tests/agent_board_task_event_origin_posture.test.sql +21 -9
  40. package/tests/agent_board_wake_recovery.test.sql +53 -26
  41. package/tests/agent_board_write_enforcement_class.test.sql +130 -12
  42. package/tests/catalog/function_execute_privileges.json +1932 -0
  43. package/tests/catalog/p_metadata_parameters.json +4 -0
  44. package/tests/fixtures/test-prereqs.sql +644 -237
@@ -83,7 +83,7 @@ set search_path = extensions, public, agent_board;
83
83
 
84
84
  begin;
85
85
 
86
- select plan(41);
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, 'i') = 0 then 'NO-DECISION'
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, 'i')
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
- from (values
192
- ('acquire_claim', agent_board_test.call_re('_claimgate_board_claim_policy_of')),
193
- ('renew_claim', agent_board_test.call_re('_claimgate_board_claim_policy_of')),
194
- ('set_board_claim_policy', 'into\s+v_prev_policy')
195
- ) as x(proname, decision)
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
- 'pg_(try_)?advisory_xact_lock\s*\(\s*hashtext\s*\(', 1, 1, 0, 'i') as quota_pos
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) ~* 'pg_(try_)?advisory_xact_lock\s*\(\s*hashtext\s*\('
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) ~* '\mexecute\M'),
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 admits every spelling of assignment including ones nobody has thought of;
382
- -- it is deliberately WIDER than assignment (an UPDATE merely reading board_id in its WHERE lands
383
- -- here too), because a false member is labelled and must be decided about, while a missed one is
384
- -- invisible. Measured: the widening leaves today's population unchanged.
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
- ~ 'update\s+agent_board\.threads\s+t\s+set\s+board_id\s*=\s*p_board_id\s+where\s+t\.board_id\s+is\s+null'
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) ~ (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_])')),
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
- agent_board_test.stmt_re('update|delete from|merge into', 'claims'),
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.stmt_re('update|delete from|merge into', 'claims')
627
- 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_])')
628
- or body ~ agent_board_test.stmt_re('insert into', 'claims')
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 ~* '\m(update|delete|insert|merge|truncate|copy)\M|\mselect\M[^;]*\minto\M'
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 '"?_claimgate_lock_board_policy"?\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)') as locked,
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 '"?_claimgate_board_claim_policy_of"?\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)') as resolved
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
- ~* '"?_claimgate_board_claim_policy_of"?\s*\(\s*[A-Za-z_][A-Za-z0-9_]*\s*\)' as bare
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.stmt_re('update|delete from|merge into', 'claims')
784
- or body ~ agent_board_test.stmt_re('insert into', 'claims')
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, '\m' || e.proname || '\M\s*\(', 1, 1, 0, 'i') as call_pos,
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.stmt_re('update|delete from|merge into', 'claims')
821
- or body ~ agent_board_test.stmt_re('insert into', 'claims')
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 agent_board._claimgate_board_of_thread(null)),
1123
- null::uuid,
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 agent_board._claimgate_board_claim_policy_of(null)),
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 agent_board._claimgate_board_claim_policy(c.thread) from _cpr c),
1136
- (select agent_board._claimgate_board_claim_policy_of(agent_board._claimgate_board_of_thread(c.thread)) from _cpr c),
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 agent_board._claimgate_board_claim_policy(c.free_thread) from _cpr c),
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 agent_board._claimgate_board_claim_policy(c.thread) from _cpr c),
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