Skip to content

fix(export): don't treat a plain-text default ending in (…) as a function - #1125

Open
NgoQuocViet2001 wants to merge 1 commit into
drawdb-io:mainfrom
NgoQuocViet2001:fix-isfunction-anchor
Open

fix(export): don't treat a plain-text default ending in (…) as a function#1125
NgoQuocViet2001 wants to merge 1 commit into
drawdb-io:mainfrom
NgoQuocViet2001:fix-isfunction-anchor

Conversation

@NgoQuocViet2001

Copy link
Copy Markdown
Contributor

The bug

A column default that is ordinary text but happens to end in a parenthesised word is exported as if it were a SQL function call — unquoted — which produces invalid SQL.

default value        exported as
"Call now(free)"  →  DEFAULT Call now(free)    -- syntax error
"see item(s)"     →  DEFAULT see item(s)       -- syntax error

Why

isFunction is anchored only at the end:

export function isFunction(str) {
  return /\w+\([^)]*\)$/.test(str);
}

parseDefault consults it before the quoting branch, so anything it accepts is emitted verbatim:

if (isFunction(field.default) || isKeyword(field.default) || (typeInfo && !typeInfo.hasQuotes)) {
  return field.default;          // no quotes
}
return `'${escapeQuotes(field.default)}'`;

Nothing upstream constrains the value — the Default box in FieldDetails.jsx is free text.

Anchoring at the start fixes it. The . in the class keeps qualified names like pg_catalog.now() matching, and the non-string guard mirrors isKeyword immediately above it.

Verified

Through the real parseDefault:

                        before                  after
"Call now(free)"    →   Call now(free)          'Call now(free)'
"see item(s)"       →   see item(s)             'see item(s)'
"now()"             →   now()                   now()
"pg_catalog.now()"  →   pg_catalog.now()        pg_catalog.now()
"hello world"       →   'hello world'           'hello world'

And on isFunction directly, over a battery of real function defaults and plain-text lookalikes — 2 fixed, 0 regressions:

value before after wanted
now() true true true
gen_random_uuid() true true true
CURRENT_TIMESTAMP() true true true
nextval('s') true true true
pg_catalog.now() true true true
Call now(free) true false false
see item(s) true false false
Item (s) false false false
now () false false false
hello world false false false

Deliberately not fixed

A default that is itself shaped like a call — a(b), text_(1) — is still treated as a function. That ambiguity is inherent to guessing from a free-text field and can only be settled by asking the user which they meant, so it is out of scope here.

I also tried allowing whitespace before the paren (now ()), but it makes Item (s) match, which is worse than the case it fixes. now () was already false before this change, so leaving it is not a regression.

Checks

  • npm run lint — clean (--max-warnings 0).
  • npm run build — succeeds.

The repo has no test suite, so the tables above come from scratch scripts run against the real modules; they are not part of the diff.

…tion

isFunction was anchored only at the end:

    /\w+\([^)]*\)$/

so any default that merely finishes with a parenthesised word matched.
parseDefault consults it before the quoting branch, so those defaults
were written into the exported SQL unquoted:

    Call now(free)   ->  DEFAULT Call now(free)     -- invalid
    see item(s)      ->  DEFAULT see item(s)        -- invalid

Anchor the pattern at the start as well. The dot in the character class
keeps qualified names such as pg_catalog.now() matching, and the
non-string guard mirrors isKeyword directly above.
@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

@NgoQuocViet2001 is attempting to deploy a commit to the dottle's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant