RelSpec script-parser bug #22

Closed
opened 2026-08-29 19:54:27 +00:00 by warkanum · 1 comment
Owner

RelSpec script-parser bug: it reports SQL EOF syntax failure for unchanged 401_1_concurrency_trigger.sql ending in FI;, which prevents the full fresh-stack harness from completing.

CREATE OR REPLACE FUNCTION root.tf_concurrency_trigger(
)
  RETURNS trigger
  LANGUAGE plpgsql
  VOLATILE
AS
$FI$
DECLARE
  has_updatecnt boolean;
  has_updatedat boolean;
  v_new_int     integer;
  v_old_int     integer;
  v_new_ts      timestamptz;
  v_old_ts      timestamptz;
  v_now_ts      timestamptz := clock_timestamp();
  v_change_cnt  integer;
BEGIN
  IF TG_WHEN <> 'BEFORE'
  THEN
    RAISE EXCEPTION 'This trigger function must only be used as a BEFORE trigger.';
  END IF;

  SELECT EXISTS (
    SELECT 1
    FROM pg_attribute
    WHERE
        attrelid = TG_RELID
    AND attname = 'updatecnt'
    AND attnum > 0
    AND NOT attisdropped
    )
  INTO has_updatecnt;

  SELECT EXISTS (
    SELECT 1
    FROM pg_attribute
    WHERE
        attrelid = TG_RELID
    AND attname = 'updatedat'
    AND attnum > 0
    AND NOT attisdropped
    )
  INTO has_updatedat;

  IF NOT has_updatecnt AND NOT has_updatedat
  THEN
    RETURN NEW;
  END IF;

  IF TG_OP = 'UPDATE'
  THEN
    IF has_updatecnt
    THEN
      EXECUTE 'SELECT ($1).updatecnt, ($2).updatecnt'
        INTO v_new_int, v_old_int
        USING NEW, OLD;

      IF coalesce(v_old_int, 0) >= 214748364
        AND coalesce(v_new_int, 0) < coalesce(v_old_int, 0)
      THEN
        NEW := jsonb_populate_record(
            NEW,
            jsonb_build_object('updatecnt', 1)
               );
        RETURN NEW;
      END IF;

      IF coalesce(v_new_int, 0) < coalesce(v_old_int, 0)
      THEN
        v_change_cnt := greatest(coalesce(v_old_int, 0) - coalesce(v_new_int, 0), 1);

        RAISE EXCEPTION
          USING
            ERRCODE = 'P0001',
            MESSAGE = format(
                'This record was modified by another user since your last refresh. There have been %s change %s. Reload to get the latest values, then re-enter and save your changes.',
                v_change_cnt,
                CASE WHEN v_change_cnt = 1 THEN '' ELSE 's' END
                      );
      END IF;

      NEW := jsonb_populate_record(
          NEW,
          jsonb_build_object(
              'updatecnt',
              CASE
                WHEN coalesce(v_old_int, 0) >= 214748364 THEN 1
                ELSE coalesce(v_old_int, 0) + 1
              END
          )
             );

    ELSIF has_updatedat
    THEN
      EXECUTE 'SELECT ($1).updatedat, ($2).updatedat'
        INTO v_new_ts, v_old_ts
        USING NEW, OLD;

      -- Conflict detection tolerates clients that drop sub-second precision from
      -- `updatedat` on a JSON round-trip (e.g. ResolveSpec's spectypes.SqlDateTime,
      -- which serialises as 'YYYY-MM-DDThh24:mi:ss'). When the incoming value still
      -- carries a fractional second (mobile sync, direct SQL) the check stays exact
      -- so a same-second stale write is still caught; when it has been truncated to
      -- a whole second, only a full-second gap counts as a conflict.
      IF v_new_ts > '1900-01-01'::timestamptz
        AND CASE
              WHEN v_new_ts <> date_trunc('second', v_new_ts)
                THEN v_old_ts > v_new_ts
              ELSE date_trunc('second', v_old_ts) > v_new_ts
            END
      THEN
        RAISE EXCEPTION
          USING
            ERRCODE = 'P0001',
            MESSAGE = format(
                'This record was modified by another user since your last refresh. Last change saved at %s. Reload for latest values, then re-enter and save your changes. Your changes were made at %s',
                coalesce(to_char(v_old_ts, 'YYYY-MM-DD HH24:MI:SS'), 'an unknown time'), coalesce(to_char(v_new_ts, 'YYYY-MM-DD HH24:MI:SS'), 'an unknown time')
                      );
      END IF;

      NEW := jsonb_populate_record(
          NEW,
          jsonb_build_object('updatedat', v_now_ts)
             );
    END IF;

  ELSIF TG_OP = 'INSERT'
  THEN
    IF has_updatecnt
    THEN
      EXECUTE 'SELECT ($1).updatecnt'
        INTO v_new_int
        USING NEW;

      NEW := jsonb_populate_record(
          NEW,
          jsonb_build_object('updatecnt', coalesce(v_new_int, 1))
             );
    ELSIF has_updatedat
    THEN
      NEW := jsonb_populate_record(
          NEW,
          jsonb_build_object('updatedat', v_now_ts)
             );
    END IF;
  END IF;

  RETURN NEW;
END;
$FI$;
RelSpec script-parser bug: it reports SQL EOF syntax failure for unchanged 401_1_concurrency_trigger.sql ending in $FI$;, which prevents the full fresh-stack harness from completing. ```sql CREATE OR REPLACE FUNCTION root.tf_concurrency_trigger( ) RETURNS trigger LANGUAGE plpgsql VOLATILE AS $FI$ DECLARE has_updatecnt boolean; has_updatedat boolean; v_new_int integer; v_old_int integer; v_new_ts timestamptz; v_old_ts timestamptz; v_now_ts timestamptz := clock_timestamp(); v_change_cnt integer; BEGIN IF TG_WHEN <> 'BEFORE' THEN RAISE EXCEPTION 'This trigger function must only be used as a BEFORE trigger.'; END IF; SELECT EXISTS ( SELECT 1 FROM pg_attribute WHERE attrelid = TG_RELID AND attname = 'updatecnt' AND attnum > 0 AND NOT attisdropped ) INTO has_updatecnt; SELECT EXISTS ( SELECT 1 FROM pg_attribute WHERE attrelid = TG_RELID AND attname = 'updatedat' AND attnum > 0 AND NOT attisdropped ) INTO has_updatedat; IF NOT has_updatecnt AND NOT has_updatedat THEN RETURN NEW; END IF; IF TG_OP = 'UPDATE' THEN IF has_updatecnt THEN EXECUTE 'SELECT ($1).updatecnt, ($2).updatecnt' INTO v_new_int, v_old_int USING NEW, OLD; IF coalesce(v_old_int, 0) >= 214748364 AND coalesce(v_new_int, 0) < coalesce(v_old_int, 0) THEN NEW := jsonb_populate_record( NEW, jsonb_build_object('updatecnt', 1) ); RETURN NEW; END IF; IF coalesce(v_new_int, 0) < coalesce(v_old_int, 0) THEN v_change_cnt := greatest(coalesce(v_old_int, 0) - coalesce(v_new_int, 0), 1); RAISE EXCEPTION USING ERRCODE = 'P0001', MESSAGE = format( 'This record was modified by another user since your last refresh. There have been %s change %s. Reload to get the latest values, then re-enter and save your changes.', v_change_cnt, CASE WHEN v_change_cnt = 1 THEN '' ELSE 's' END ); END IF; NEW := jsonb_populate_record( NEW, jsonb_build_object( 'updatecnt', CASE WHEN coalesce(v_old_int, 0) >= 214748364 THEN 1 ELSE coalesce(v_old_int, 0) + 1 END ) ); ELSIF has_updatedat THEN EXECUTE 'SELECT ($1).updatedat, ($2).updatedat' INTO v_new_ts, v_old_ts USING NEW, OLD; -- Conflict detection tolerates clients that drop sub-second precision from -- `updatedat` on a JSON round-trip (e.g. ResolveSpec's spectypes.SqlDateTime, -- which serialises as 'YYYY-MM-DDThh24:mi:ss'). When the incoming value still -- carries a fractional second (mobile sync, direct SQL) the check stays exact -- so a same-second stale write is still caught; when it has been truncated to -- a whole second, only a full-second gap counts as a conflict. IF v_new_ts > '1900-01-01'::timestamptz AND CASE WHEN v_new_ts <> date_trunc('second', v_new_ts) THEN v_old_ts > v_new_ts ELSE date_trunc('second', v_old_ts) > v_new_ts END THEN RAISE EXCEPTION USING ERRCODE = 'P0001', MESSAGE = format( 'This record was modified by another user since your last refresh. Last change saved at %s. Reload for latest values, then re-enter and save your changes. Your changes were made at %s', coalesce(to_char(v_old_ts, 'YYYY-MM-DD HH24:MI:SS'), 'an unknown time'), coalesce(to_char(v_new_ts, 'YYYY-MM-DD HH24:MI:SS'), 'an unknown time') ); END IF; NEW := jsonb_populate_record( NEW, jsonb_build_object('updatedat', v_now_ts) ); END IF; ELSIF TG_OP = 'INSERT' THEN IF has_updatecnt THEN EXECUTE 'SELECT ($1).updatecnt' INTO v_new_int USING NEW; NEW := jsonb_populate_record( NEW, jsonb_build_object('updatecnt', coalesce(v_new_int, 1)) ); ELSIF has_updatedat THEN NEW := jsonb_populate_record( NEW, jsonb_build_object('updatedat', v_now_ts) ); END IF; END IF; RETURN NEW; END; $FI$; ```
Member

Dispatcher reconciliation update — 2026-09-07 06:02 SAST

• Forge/repository: Gitea wdevs/relspecgo
• Issue: #22 — RelSpec script-parser bug
• Assignment state: existing assignment remains BLOCKED; no duplicate worker launched.
• Selected backend for the prior bounded attempt: custom:litellm-warky / ornith-1.5:35b (simple investigation probe succeeded; worker exited after investigation).
• Worker/session: issue-agent-wdevs-relspecgo-22 / ia-relspecgo-22-ornith; no live process or tmux session remains.
• Worktree: /home/hermes/work/issue-agents/wdevs/relspecgo/issue-22
• Branch: issue-22-script-parser-eof; no commit or PR was produced.
• Result: the referenced parser/fixture path could not be reproduced from the available repository state, so implementation would require guessing. The preserved report records the investigation and blocker.
• Plan when clarified: reproduce the exact parser failure, add a focused regression test, implement the smallest fix, run Go verification, push the branch, create a PR, and post exact SHA/evidence here.

No issue close, merge, force-push, history rewrite, or dangerous permission bypass was performed.

Dispatcher reconciliation update — 2026-09-07 06:02 SAST • Forge/repository: Gitea wdevs/relspecgo • Issue: #22 — RelSpec script-parser bug • Assignment state: existing assignment remains BLOCKED; no duplicate worker launched. • Selected backend for the prior bounded attempt: custom:litellm-warky / ornith-1.5:35b (simple investigation probe succeeded; worker exited after investigation). • Worker/session: issue-agent-wdevs-relspecgo-22 / ia-relspecgo-22-ornith; no live process or tmux session remains. • Worktree: /home/hermes/work/issue-agents/wdevs/relspecgo/issue-22 • Branch: issue-22-script-parser-eof; no commit or PR was produced. • Result: the referenced parser/fixture path could not be reproduced from the available repository state, so implementation would require guessing. The preserved report records the investigation and blocker. • Plan when clarified: reproduce the exact parser failure, add a focused regression test, implement the smallest fix, run Go verification, push the branch, create a PR, and post exact SHA/evidence here. No issue close, merge, force-push, history rewrite, or dangerous permission bypass was performed.
Sign in to join this conversation.
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wdevs/relspecgo#22