Skip to content

feat(webui): Add support for building queries from guided SQL input boxes.#1304

Merged
hoophalab merged 9 commits into
y-scope:mainfrom
hoophalab:buildsql
Sep 19, 2025
Merged

feat(webui): Add support for building queries from guided SQL input boxes.#1304
hoophalab merged 9 commits into
y-scope:mainfrom
hoophalab:buildsql

Conversation

@hoophalab

@hoophalab hoophalab commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

Description

  1. Add a buildSqlQuery function to the sql-parser that takes SQL query component strings as arguments and returns a valid SQL query.
  2. Add testing input boxes. @davemarco said he will replace those testing input boxes with real ones when the UI is done.
sqlbuilder

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

  1. Run a few SQL queries.
  2. The generated SQL queries execute correctly. The webui shows the results from Presto.

Summary by CodeRabbit

  • New Features

    • Added an experimental SQL testing UI in guided Presto mode to compose SELECT, FROM, WHERE, ORDER BY, LIMIT, log the generated SQL and submit queries.
    • Exposed a new buildSearchQuery utility for assembling and validating generated SQL before execution; validation failures show contextual errors.
  • Refactor

    • Separated parser creation from validation to improve parsing reliability and support guided query features.

@hoophalab hoophalab requested a review from a team as a code owner September 16, 2025 00:24
@coderabbitai

coderabbitai Bot commented Sep 16, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a temporary React component for composing and submitting Presto SQL in guided mode, integrates it into SearchControls, and extends the SQL parser with a parser-construction helper, an exported buildSearchQuery function and BuildSearchQueryProps type; plus a minor syntax formatting tweak.

Changes

Cohort / File(s) Summary of changes
Presto testing UI component
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx
New named-export React component BuildSqlTestingInputs that renders inputs for select/from/where/order/limit, normalizes empty strings via a TransformEmptyStringSchema, parses form data into a typed props object (TypeBox), calls buildSearchQuery to produce an SQL string, logs it, and calls handlePrestoQuerySubmit({ queryString }).
Search controls integration
components/webui/client/src/pages/SearchPage/SearchControls/index.tsx
Imports and conditionally renders BuildSqlTestingInputs (after the existing form) when isPrestoGuided is true; return value changed to a fragment to include the new component, existing submit/control logic unchanged.
SQL parser enhancements
components/webui/client/src/sql-parser/index.ts
Adds buildParser(input: string) helper and refactors validate to use it; introduces and exports buildSearchQuery(props) and exported type BuildSearchQueryProps; buildSearchQuery assembles SQL (SELECT/FROM with optional WHERE/ORDER BY/LIMIT), validates via parser, and rethrows parse errors with the constructed SQL as context; minor trailing-comma formatting tweak in SyntaxErrorListener.syntaxError.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as BuildSqlTestingInputs
  participant Builder as buildSearchQuery
  participant Parser as buildParser / validate
  participant Submit as handlePrestoQuerySubmit

  User->>UI: Enter select/from/where/order/limit\nClick Run
  UI->>Builder: buildSearchQuery(props)
  Builder->>Parser: buildParser(sqlString).singleStatement()
  alt valid SQL
    Parser-->>Builder: success
    Builder-->>UI: sqlString
    UI->>Submit: handlePrestoQuerySubmit({ queryString: sqlString })
  else parse error
    Parser-->>Builder: throw SyntaxError
    Builder-->>UI: throw Error(with constructed SQL + cause)
    UI-->>User: surface/log error
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
Title Check ✅ Passed The title succinctly and accurately describes the primary change: adding guided web UI inputs to build SQL queries (which aligns with the added BuildSqlTestingInputs component and the new buildSearchQuery API in the sql-parser). It is concise, focused on the main user-visible feature, and does not include noise or misleading details. The scope "webui" and the phrase "guided SQL input boxes" correctly reflect the changes in the diff and PR objectives.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/webui/client/src/sql-parser/index.ts (1)

24-37: Avoid shadowing the built‑in SyntaxError. Rename to SqlSyntaxError.

Shadowing can confuse consumers and stack traces.

Apply:

-class SyntaxError extends Error {
+class SqlSyntaxError extends Error {
 }
@@
-        throw new SyntaxError(`line ${line}:${column}: ${msg}`);
+        throw new SqlSyntaxError(`line ${line}:${column}: ${msg}`);

Also update all references/JSDoc/exports accordingly (see suggestions below).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb89bfb and 46c4e3c.

⛔ Files ignored due to path filters (2)
  • components/webui/client/src/sql-parser/generated/SqlBaseParser.ts is excluded by !**/generated/**
  • components/webui/client/src/sql-parser/generated/SqlBaseVisitor.ts is excluded by !**/generated/**
📒 Files selected for processing (5)
  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (1 hunks)
  • components/webui/client/src/pages/SearchPage/SearchControls/index.tsx (2 hunks)
  • components/webui/client/src/sql-parser/SqlBase.g4 (3 hunks)
  • components/webui/client/src/sql-parser/index.ts (3 hunks)
  • taskfiles/codegen.yaml (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx
  • components/webui/client/src/pages/SearchPage/SearchControls/index.tsx
  • components/webui/client/src/sql-parser/index.ts
🧠 Learnings (1)
📓 Common learnings
Learnt from: haiqi96
PR: y-scope/clp#0
File: :0-0
Timestamp: 2025-07-29T14:04:13.769Z
Learning: User haiqi96 requested creating a GitHub issue to document a bug fix from PR #1136, which addressed MySQL compatibility issues with invalid SQL CAST operations in the WebUI component.
🧬 Code graph analysis (3)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (2)
components/webui/client/src/sql-parser/index.ts (2)
  • BuildSearchQueryProps (226-226)
  • buildSearchQuery (221-221)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/presto-search-requests.ts (1)
  • handlePrestoQuerySubmit (104-104)
components/webui/client/src/pages/SearchPage/SearchControls/index.tsx (1)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (1)
  • BuildSqlTestingInputs (47-47)
components/webui/client/src/sql-parser/index.ts (4)
components/webui/client/src/sql-parser/generated/SqlBaseParser.ts (27)
  • SqlBaseParser (21-11173)
  • SelectItemListContext (14582-14604)
  • RelationListContext (14607-14629)
  • BooleanExpressionContext (15294-15305)
  • SortItemListContext (14304-14326)
  • QuerySpecificationContext (14525-14579)
  • selectItemList (4740-4782)
  • selectItemList (11255-11257)
  • selectItemList (14535-14537)
  • relationList (4784-4826)
  • relationList (11280-11282)
  • relationList (14544-14546)
  • booleanExpression (5841-6138)
  • booleanExpression (11305-11307)
  • booleanExpression (12871-12873)
  • booleanExpression (12935-12937)
  • booleanExpression (13514-13516)
  • booleanExpression (14565-14567)
  • booleanExpression (15016-15018)
  • booleanExpression (15277-15279)
  • booleanExpression (15314-15316)
  • booleanExpression (15358-15360)
  • booleanExpression (16945-16947)
  • QueryNoWithContext (14237-14301)
  • sortItemList (4339-4379)
  • sortItemList (11330-11332)
  • sortItemList (14254-14256)
components/webui/client/src/sql-parser/generated/SqlBaseLexer.ts (1)
  • SqlBaseLexer (15-1482)
components/webui/common/src/utility-types.ts (1)
  • Nullable (3-3)
components/webui/client/src/sql-parser/generated/SqlBaseVisitor.ts (1)
  • SqlBaseVisitor (252-1832)
🪛 Biome (2.1.2)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx

[error] 30-30: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 32-32: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 34-34: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 36-36: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 38-38: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: musllinux_1_2-x86_64-dynamic-linked-bins
  • GitHub Check: musllinux_1_2-x86_64-static-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-dynamic-linked-bins
  • GitHub Check: ubuntu-jammy-static-linked-bins
  • GitHub Check: manylinux_2_28-x86_64-static-linked-bins
  • GitHub Check: ubuntu-jammy-dynamic-linked-bins
  • GitHub Check: package-image
  • GitHub Check: centos-stream-9-dynamic-linked-bins
  • GitHub Check: ubuntu-jammy-lint
  • GitHub Check: centos-stream-9-static-linked-bins
  • GitHub Check: build-macos (macos-15, true)
  • GitHub Check: lint-check (ubuntu-24.04)
  • GitHub Check: build-macos (macos-15, false)
🔇 Additional comments (3)
taskfiles/codegen.yaml (1)

66-72: Visitor generated and referenced correctly — approved.
SqlBaseVisitor.ts exists at components/webui/client/src/sql-parser/generated/ and imports ParseTreeVisitor from 'antlr4'; SqlBaseParser.ts and SqlBaseLexer.ts import 'antlr4', and SqlBaseParser imports the visitor via "./SqlBaseVisitor.js". No changes required.

components/webui/client/src/sql-parser/SqlBase.g4 (1)

33-51: Regenerate the ANTLR TypeScript parser and verify visitor behaviour + ORDER BY parsing

  • Regenerate the parser (run ANTLR with -visitor) and confirm ANTLR reports no ambiguities/warnings.
  • TypeScript target caveat: it emits a visitor interface only (no generated BaseVisitor with a visitChildren fallback). Implement/forward any required visitor methods or add a small BaseVisitor shim to preserve traversal behaviour.
  • ORDER BY handling is fine for Presto/Trino — use a comma‑separated non‑left‑recursive list (expression (',' expression)*).
  • Check: components/webui/client/src/sql-parser/SqlBase.g4 (lines 33–51; also verify 260–269 and 288–303).
components/webui/client/src/sql-parser/index.ts (1)

66-76: Confirmed: generated parser uses the 'antlr4' JS runtime — no change required

components/webui/client/src/sql-parser/generated/SqlBaseLexer.ts, SqlBaseParser.ts and SqlBaseVisitor.ts import from 'antlr4', and components/webui/client/package.json lists "antlr4": "^4.13.2".

Comment thread components/webui/client/src/sql-parser/index.ts
Comment thread components/webui/client/src/sql-parser/index.ts Outdated
Comment thread components/webui/client/src/sql-parser/index.ts Outdated
Comment thread components/webui/client/src/sql-parser/index.ts
Comment thread components/webui/client/src/sql-parser/index.ts
Comment thread components/webui/client/src/sql-parser/index.ts

@davemarco davemarco left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great that it's works!

Overall i have a high level questions about this comment from the rabbit - #1304 (comment)

What is wrong with this approach, it seems more intuitive and simpler than the template approach? There is also no setting children directly.

Open to template approach, if you have good reason for it.

Also i think you should add more comments to parser code.

{controls}
</form>
<>
<form onSubmit={handleSubmit}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this to Guided Controls

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot because the testing component uses form and a form cannot be nested in another form. Since it's temporarily, I guess it's ok.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is okay, since it still wont render without guided which is default false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may need to modify top of file per license

@hoophalab

hoophalab commented Sep 16, 2025

Copy link
Copy Markdown
Contributor Author

Overall i have a high level questions about this comment from the rabbit - #1304 (comment)
What is wrong with this approach, it seems more intuitive and simpler than the template approach? There is also no setting children directly.
Open to template approach, if you have good reason for it.

I guess both works for our use but template might be more structured? Ideally, we should be able to construct syntax trees directly using components, (e.g. new Query(new QuerySpecification(columns, relations), booleanExpr)), but unfortunately ANTLR doesn't support doing this on a parsing tree. So the template is a workaround.

I'm open to both. @junhaoliao

@junhaoliao

Copy link
Copy Markdown
Member

Overall i have a high level questions about this comment from the rabbit - #1304 (comment)
What is wrong with this approach, it seems more intuitive and simpler than the template approach? There is also no setting children directly.
Open to template approach, if you have good reason for it.

I guess both works for our use but template might be more structured? Ideally, we should be able to construct syntax trees directly using components, (e.g. new Query(new QuerySpecification(columns, relations), booleanExpr)), but unfortunately ANTLR doesn't support doing this on a parsing tree. So the template is a workaround.

I'm open to both. @junhaoliao

Let's pull in some ANTLR experts for this discussion. @gibber9809 @LinZhihao-723 @kirkrodrigues

From the solutions that were presented (templating vs string assembly), I believe the templating approach is easier to maintain. if we hardcode with string assembly method, we lost the AST abstraction. the code might become more complex if the query grammar grows (e.g., GROUP BY, HAVING, etc..)

it does seem that building fragment ASTs for SELECT, FROM, WHERE, etc can be a better approach, though rejected at #1304 (comment)

by the way, do you think this can be a better interface than buildSearchQuery(...)?:

const sql = queryBuilder()
  .select(selectCtx)
  .from(fromCtx)
  .where(optionalWhereCtx)
  .orderBy(optionalOrderCtx)
  .limit(optionalLimitCtx)
  .toSQL();

or can we mimic the interfaces in this library https://www.npmjs.com/package/node-sql-parser ?

@LinZhihao-723

Copy link
Copy Markdown
Member

Overall i have a high level questions about this comment from the rabbit - #1304 (comment)
What is wrong with this approach, it seems more intuitive and simpler than the template approach? There is also no setting children directly.
Open to template approach, if you have good reason for it.

I guess both works for our use but template might be more structured? Ideally, we should be able to construct syntax trees directly using components, (e.g. new Query(new QuerySpecification(columns, relations), booleanExpr)), but unfortunately ANTLR doesn't support doing this on a parsing tree. So the template is a workaround.
I'm open to both. @junhaoliao

Let's pull in some ANTLR experts for this discussion. @gibber9809 @LinZhihao-723 @kirkrodrigues

From the solutions that were presented (templating vs string assembly), I believe the templating approach is easier to maintain. if we hardcode with string assembly method, we lost the AST abstraction. the code might become more complex if the query grammar grows (e.g., GROUP BY, HAVING, etc..)

it does seem that building fragment ASTs for SELECT, FROM, WHERE, etc can be a better approach, though rejected at #1304 (comment)

by the way, do you think this can be a better interface than buildSearchQuery(...)?:

const sql = queryBuilder()
  .select(selectCtx)
  .from(fromCtx)
  .where(optionalWhereCtx)
  .orderBy(optionalOrderCtx)
  .limit(optionalLimitCtx)
  .toSQL();

or can we mimic the interfaces in this library https://www.npmjs.com/package/node-sql-parser ?

I'm not sure if I fully understand what Marco proposed, but I think the correct way to do it here is:

  • Use a format string to construct a complete SQL query.
  • Formally define the grammar to parse the query as a single tree.

Q: Why parsing a complete SQL query string?
A: A few advantages:

  • For completeness: the parsing rule will be a complete subset of SQL query (but not for its components).
  • Grammar-level validation: the validation/parsing is now at the grammar level, end-to-end from a string to the parsed tree.
  • Future proving:
    • Easier to extend the grammar to support more SQL features.
    • Easier to extend so that users can input a complete SQL query string.

Q: Does it mean we need to maintain the format string carefully to concat subcomponents/clusters into a complete SQL query string?
A: Yes. And it's not duplicated work.

  • The grammar should define the standard ordering for main clusters (like SELECT before FROM before WHERE...).
  • Before we support arbitrary SQL query inputs, we carefully construct the query from clusters to follow the standard, which guarantees that the standard ordering is followed.
  • After adding support for arbitrary SQL query inputs, the grammar is still the checker to ensure user inputs follow the standard cluster ordering.

@davemarco

Copy link
Copy Markdown
Contributor

@LinZhihao-723 I think good approach, but how do we send syntax error to user? I guess the errors would be for the full format string, and not the individual input, which may be fine?

@LinZhihao-723

Copy link
Copy Markdown
Member

@LinZhihao-723 I think good approach, but how do we send syntax error to user? I guess the errors would be for the full format string, and not the individual input, which may be fine?

Yeah. Maybe we can do a preview of the generated query string in the UI. And reporting errors to that generated query string would make more sense.

@hoophalab

hoophalab commented Sep 17, 2025

Copy link
Copy Markdown
Contributor Author
  1. @junhaoliao I had another offline discussion with Zhihao and we settled with this very simple format + validation approach.

by the way, do you think this can be a better interface than buildSearchQuery(...)?:

tbh, I don't really like Java-style builders -- they add many boilerplates to the code. I made a few changes using optional fields. Can you do a review again?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/webui/client/src/sql-parser/index.ts (1)

13-14: Rename local SyntaxError to avoid shadowing the built‑in error.

Shadowing the global SyntaxError hinders accurate type checks and tooling. Use a distinct name (e.g., SqlSyntaxError) and propagate the rename.

Apply:

-class SyntaxError extends Error {
+class SqlSyntaxError extends Error {
 }
♻️ Duplicate comments (5)
components/webui/client/src/sql-parser/index.ts (3)

67-75: JSDoc should reference the SQL‑specific error type.

- * @throws {SyntaxError} with line, column, and message details if a syntax error is found.
+ * @throws {SqlSyntaxError} with line, column, and message details if a syntax error is found.

125-129: Update exports to use the renamed error type.

 export {
     buildSearchQuery,
-    SyntaxError,
+    SqlSyntaxError,
     validate,
 };

85-97: Keep original SQL syntax error type when rethrowing; update JSDoc.

Preserve SqlSyntaxError so callers can distinguish parse failures; fix the @throws tag accordingly.

- * @throws {SyntaxError} if any of the input is not valid.
+ * @throws {SqlSyntaxError} if any of the input is not valid.
@@
     try {
         validate(sqlString);
     } catch (err: unknown) {
-        throw new Error(`The constructed SQL is not valid: ${sqlString}`, {cause: err});
+        if (err instanceof SqlSyntaxError) {
+            throw err;
+        }
+        throw new Error(`The constructed SQL is not valid: ${sqlString}`, {cause: err});
     }
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (2)

59-69: Associate labels with inputs and constrain LIMIT to numeric.

Fixes a11y violations and reduces invalid input.

-            <label>select:</label>
-            <input name={"selectItemList"}/>
-            <label>from:</label>
-            <input name={"relationList"}/>
-            <label>where:</label>
-            <input name={"booleanExpression"}/>
-            <label>order:</label>
-            <input name={"sortItemList"}/>
-            <label>limit:</label>
-            <input name={"limitValue"}/>
+            <label>
+                select:
+                <input name="selectItemList" placeholder="col1, col2, t.*" />
+            </label>
+            <label>
+                from:
+                <input name="relationList" placeholder="table1 t, table2" />
+            </label>
+            <label>
+                where:
+                <input name="booleanExpression" placeholder="t.col = 1 AND ..." />
+            </label>
+            <label>
+                order:
+                <input name="sortItemList" placeholder="col1 DESC, col2" />
+            </label>
+            <label>
+                limit:
+                <input name="limitValue" type="number" inputMode="numeric" min="0" />
+            </label>

46-57: Guard submit with try/catch and gate debug logs.

Unhandled exceptions from parsing crash the UI; keep console noise out of production.

             onSubmit={(ev: FormEvent<HTMLFormElement>) => {
                 ev.preventDefault();
-                const formData = new FormData(ev.target as HTMLFormElement);
-                const props: Static<typeof BuildSearchQueryPropsSchema> = Value.Parse(
-                    BuildSearchQueryPropsSchema,
-                    Object.fromEntries(formData),
-                );
-
-                const sqlString = buildSearchQuery(props);
-                console.log(`SQL: ${sqlString}`);
-                handlePrestoQuerySubmit({queryString: sqlString});
+                try {
+                    const formData = new FormData(ev.target as HTMLFormElement);
+                    const props: Static<typeof BuildSearchQueryPropsSchema> = Value.Parse(
+                        BuildSearchQueryPropsSchema,
+                        Object.fromEntries(formData),
+                    );
+                    const sqlString = buildSearchQuery(props);
+                    if ("production" !== process.env.NODE_ENV) {
+                        // eslint-disable-next-line no-console
+                        console.log(`SQL: ${sqlString}`);
+                    }
+                    handlePrestoQuerySubmit({queryString: sqlString});
+                } catch (err) {
+                    // eslint-disable-next-line no-console
+                    console.error(err);
+                    alert("Invalid SQL fragments. Please check your inputs.");
+                }
             }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1127fc and 23eca7e.

📒 Files selected for processing (2)
  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (1 hunks)
  • components/webui/client/src/sql-parser/index.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx
  • components/webui/client/src/sql-parser/index.ts
🧬 Code graph analysis (2)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (2)
components/webui/client/src/sql-parser/index.ts (1)
  • buildSearchQuery (126-126)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/presto-search-requests.ts (1)
  • handlePrestoQuerySubmit (104-104)
components/webui/client/src/sql-parser/index.ts (2)
components/webui/client/src/sql-parser/generated/SqlBaseParser.ts (11)
  • SqlBaseParser (19-10938)
  • booleanExpression (5617-5914)
  • booleanExpression (12151-12153)
  • booleanExpression (12199-12201)
  • booleanExpression (12642-12644)
  • booleanExpression (13413-13415)
  • booleanExpression (13702-13704)
  • booleanExpression (13883-13885)
  • booleanExpression (13912-13914)
  • booleanExpression (13940-13942)
  • booleanExpression (15063-15065)
components/webui/client/src/sql-parser/generated/SqlBaseLexer.ts (1)
  • SqlBaseLexer (15-1482)
🪛 Biome (2.1.2)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx

[error] 59-59: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 61-61: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 63-63: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 65-65: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 67-67: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: package-image
  • GitHub Check: lint-check (ubuntu-24.04)
  • GitHub Check: lint-check (macos-15)
🔇 Additional comments (2)
components/webui/client/src/sql-parser/index.ts (1)

55-66: LGTM: Parser factory separation is clean.

Good encapsulation of lexer/parser setup with error listeners.

components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (1)

16-27: LGTM: Schema transform for empty strings → undefined is solid.

Keeps UI optional fields aligned with the builder’s types.

Also applies to: 28-36

Comment thread components/webui/client/src/sql-parser/index.ts
Comment thread components/webui/client/src/sql-parser/index.ts
@davemarco

Copy link
Copy Markdown
Contributor

Yeah. Maybe we can do a preview of the generated query string in the UI. And reporting errors to that generated query string would make more sense.

Okay i think eventually we can change the error so its shows the full format string

@hoophalab

Copy link
Copy Markdown
Contributor Author

Yeah. Maybe we can do a preview of the generated query string in the UI. And reporting errors to that generated query string would make more sense.

Okay i think eventually we can change the error so its shows the full format string

Or we can still add standaloneXXX back and validate individual components before formatting?

@hoophalab hoophalab requested a review from davemarco September 17, 2025 23:02

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/webui/client/src/sql-parser/index.ts (1)

13-26: Rename custom SyntaxError to SqlSyntaxError; update JSDoc and exports.

Avoid colliding with the global SyntaxError — rename the class, the thrown instance, the @throws JSDoc, and the exported identifier.

Location: components/webui/client/src/sql-parser/index.ts — class: 13–26; JSDoc: 67–72; exports: 124–128

-class SyntaxError extends Error {
+class SqlSyntaxError extends Error {
}
@@
-        throw new SyntaxError(`line ${line}:${column}: ${msg}`);
+        throw new SqlSyntaxError(`line ${line}:${column}: ${msg}`);
- * @throws {SyntaxError} with line, column, and message details if a syntax error is found.
+ * @throws {SqlSyntaxError} with line, column, and message details if a syntax error is found.
-export {
-    buildSearchQuery,
-    SyntaxError,
-    validate,
-};
+export {buildSearchQuery, validate, SqlSyntaxError};
♻️ Duplicate comments (6)
components/webui/client/src/sql-parser/index.ts (4)

115-119: Preserve the specific SQL error type when rethrowing.

Let callers catch SqlSyntaxError reliably; wrap only non‑syntax errors.

-    } catch (err: unknown) {
-        throw new Error(`The constructed SQL is not valid: ${sqlString}`, {cause: err});
-    }
+    } catch (err: unknown) {
+        if (err instanceof SqlSyntaxError) throw err;
+        throw new Error(`The constructed SQL is not valid: ${sqlString}`, {cause: err});
+    }

67-76: JSDoc must reference the renamed error type.

Same as above; update the @throws tag to SqlSyntaxError.


105-113: Prefer direct undefined checks over typeof string comparisons.

Cleaner TS; no behaviour change.

-    if ("undefined" !== typeof booleanExpression) {
+    if (undefined !== booleanExpression) {
@@
-    if ("undefined" !== typeof sortItemList) {
+    if (undefined !== sortItemList) {
@@
-    if ("undefined" !== typeof limitValue) {
+    if (undefined !== limitValue) {

104-113: Defensively reject empty strings for optional fragments (enforce API contract).

If someone bypasses the UI schema, "" will emit broken clauses. Guard early.

-    if ("undefined" !== typeof booleanExpression) {
-        sqlString += ` WHERE ${booleanExpression}`;
-    }
-    if ("undefined" !== typeof sortItemList) {
-        sqlString += ` ORDER BY ${sortItemList}`;
-    }
-    if ("undefined" !== typeof limitValue) {
-        sqlString += ` LIMIT ${limitValue}`;
-    }
+    if (undefined !== booleanExpression) {
+        if ("" === booleanExpression) throw new Error("booleanExpression must be undefined or a valid fragment");
+        sqlString += ` WHERE ${booleanExpression}`;
+    }
+    if (undefined !== sortItemList) {
+        if ("" === sortItemList) throw new Error("sortItemList must be undefined or a valid fragment");
+        sqlString += ` ORDER BY ${sortItemList}`;
+    }
+    if (undefined !== limitValue) {
+        if ("" === limitValue) throw new Error("limitValue must be undefined or a valid fragment");
+        sqlString += ` LIMIT ${limitValue}`;
+    }
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (2)

60-71: Associate labels with inputs and constrain LIMIT to numeric.

Fixes a11y errors (Biome) and prevents non‑numeric limit.

-            <label>select:</label>
-            <input name={"selectItemList"}/>
-            <label>from:</label>
-            <input name={"relationList"}/>
-            <label>where:</label>
-            <input name={"booleanExpression"}/>
-            <label>order:</label>
-            <input name={"sortItemList"}/>
-            <label>limit:</label>
-            <input name={"limitValue"}/>
+            <label>
+                select:
+                <input name="selectItemList" placeholder="col1, col2, t.*" required />
+            </label>
+            <label>
+                from:
+                <input name="relationList" placeholder="table1 t, table2" required />
+            </label>
+            <label>
+                where:
+                <input name="booleanExpression" placeholder="t.col = 1 AND ..." />
+            </label>
+            <label>
+                order:
+                <input name="sortItemList" placeholder="col1 DESC, col2" />
+            </label>
+            <label>
+                limit:
+                <input name="limitValue" type="number" inputMode="numeric" min="1" step="1" />
+            </label>

47-58: Wrap parse/build in try/catch; don’t crash the UI on invalid input.

Uncaught exceptions here will break the page.

-            onSubmit={(ev: FormEvent<HTMLFormElement>) => {
+            onSubmit={(ev: FormEvent<HTMLFormElement>) => {
                 ev.preventDefault();
-                const formData = new FormData(ev.target as HTMLFormElement);
-                const props: Static<typeof BuildSearchQueryPropsSchema> = Value.Parse(
-                    BuildSearchQueryPropsSchema,
-                    Object.fromEntries(formData),
-                );
-
-                const sqlString = buildSearchQuery(props);
-                console.log(`SQL: ${sqlString}`);
-                handlePrestoQuerySubmit({queryString: sqlString});
+                try {
+                    const formData = new FormData(ev.target as HTMLFormElement);
+                    const props = Value.Parse(
+                        BuildSearchQueryPropsSchema,
+                        Object.fromEntries(formData),
+                    );
+                    const sqlString = buildSearchQuery(props);
+                    if ("production" !== process.env.NODE_ENV) {
+                        // eslint-disable-next-line no-console
+                        console.log(`SQL: ${sqlString}`);
+                    }
+                    handlePrestoQuerySubmit({queryString: sqlString});
+                } catch (err) {
+                    // eslint-disable-next-line no-console
+                    console.error(err);
+                    alert("Invalid SQL fragments. Please check your inputs.");
+                }
             }}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 23eca7e and 5d562e3.

📒 Files selected for processing (2)
  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (1 hunks)
  • components/webui/client/src/sql-parser/index.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • components/webui/client/src/sql-parser/index.ts
  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-17T22:51:15.765Z
Learnt from: hoophalab
PR: y-scope/clp#1304
File: components/webui/client/src/sql-parser/index.ts:98-115
Timestamp: 2025-09-17T22:51:15.765Z
Learning: In the CLP webui codebase, when designing APIs that accept optional string parameters, developers should omit the value (pass undefined) rather than pass empty strings. The API contract should be clear that empty strings are not valid inputs - only undefined should be used to indicate omission of optional clauses.

Applied to files:

  • components/webui/client/src/sql-parser/index.ts
📚 Learning: 2025-06-09T17:48:56.024Z
Learnt from: junhaoliao
PR: y-scope/clp#988
File: components/log-viewer-webui/client/src/components/QueryBox/InputWithCaseSensitive/CaseSenstiveToggle.tsx:34-37
Timestamp: 2025-06-09T17:48:56.024Z
Learning: In the y-scope/clp project, prefer `false == <expression>` rather than `!<expression>` for boolean expressions in TypeScript/JavaScript files, as specified in the coding guidelines.

Applied to files:

  • components/webui/client/src/sql-parser/index.ts
🧬 Code graph analysis (2)
components/webui/client/src/sql-parser/index.ts (2)
components/webui/client/src/sql-parser/generated/SqlBaseParser.ts (1)
  • SqlBaseParser (19-10938)
components/webui/client/src/sql-parser/generated/SqlBaseLexer.ts (1)
  • SqlBaseLexer (15-1482)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (2)
components/webui/client/src/sql-parser/index.ts (1)
  • buildSearchQuery (125-125)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/presto-search-requests.ts (1)
  • handlePrestoQuerySubmit (104-104)
🪛 Biome (2.1.2)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx

[error] 60-60: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 62-62: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 64-64: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 66-66: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 68-68: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: package-image
  • GitHub Check: lint-check (macos-15)
  • GitHub Check: lint-check (ubuntu-24.04)
🔇 Additional comments (2)
components/webui/client/src/sql-parser/index.ts (2)

49-66: Parser construction looks solid.

Upper‑casing char stream + shared error listener for lexer/parser is correct and keeps case‑insensitive grammar behaviour.


77-84: Props shape LGTM (optional fragments via undefined).

Matches the agreed contract (omit optional fragments rather than pass empty strings).

Please confirm all call sites (including tests/Playgrounds) omit optional fields instead of sending "".

@davemarco davemarco left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small comments. I am ready to approve after this

{controls}
</form>
<>
<form onSubmit={handleSubmit}>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is okay, since it still wont render without guided which is default false

buildParser(sqlString).singleStatement();
};

interface BuildSearchQueryProps {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to typings.ts?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could also maybe move the ascii types

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually keep function/component props interface in the original file?

@davemarco

davemarco commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

@hoophalab

Or we can still add standaloneXXX back and validate individual components before formatting?

if the errors look weird in testing, maybe we add this as a precheck in a later PR?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (4)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (4)

47-58: Catch and surface parse/build errors; don’t crash the UI.

Wrap Value.Parse/buildSearchQuery in try/catch, dev‑gate logs, and bail out on error. This prevents UX breakage on invalid fragments.

         <form
-            onSubmit={(ev: FormEvent<HTMLFormElement>) => {
+            onSubmit={(ev: FormEvent<HTMLFormElement>) => {
                 ev.preventDefault();
                 const formData = new FormData(ev.target as HTMLFormElement);
-                const props: Static<typeof BuildSearchQueryPropsSchema> = Value.Parse(
-                    BuildSearchQueryPropsSchema,
-                    Object.fromEntries(formData),
-                );
-
-                const sqlString = buildSearchQuery(props);
-                console.log(`SQL: ${sqlString}`);
-                handlePrestoQuerySubmit({queryString: sqlString});
+                try {
+                    const props = Value.Parse(
+                        BuildSearchQueryPropsSchema,
+                        Object.fromEntries(formData),
+                    );
+                    const sqlString = buildSearchQuery(props);
+                    if ("production" !== process.env.NODE_ENV) {
+                        console.log(`SQL: ${sqlString}`);
+                    }
+                    handlePrestoQuerySubmit({queryString: sqlString});
+                } catch (err) {
+                    console.error(err);
+                    alert("Invalid SQL fragments. Please check your inputs.");
+                }
             }}
         >

9-11: Type with the exported API type; drop Static to prevent drift.

Keep UI strictly aligned with the parser’s public surface.

-import {
-    Static,
-    Type,
-} from "@sinclair/typebox";
+import {Type} from "@sinclair/typebox";
 import {Value} from "@sinclair/typebox/value";
 
-import {buildSearchQuery} from "../../../../sql-parser";
+import {buildSearchQuery, type BuildSearchQueryProps} from "../../../../sql-parser";
@@
-                const props: Static<typeof BuildSearchQueryPropsSchema> = Value.Parse(
+                const props: BuildSearchQueryProps = Value.Parse(
                     BuildSearchQueryPropsSchema,
                     Object.fromEntries(formData),
                 );

Also applies to: 50-53, 3-7


60-71: Fix a11y label association; constrain limit as numeric; mark required fields.

Resolves Biome errors and improves UX.

-            <label>select:</label>
-            <input name={"selectItemList"}/>
-            <label>from:</label>
-            <input name={"relationList"}/>
-            <label>where:</label>
-            <input name={"booleanExpression"}/>
-            <label>order:</label>
-            <input name={"sortItemList"}/>
-            <label>limit:</label>
-            <input name={"limitValue"}/>
+            <label>
+                select:
+                <input name="selectItemList" placeholder="col1, col2, t.*" required />
+            </label>
+            <label>
+                from:
+                <input name="relationList" placeholder="table1 t, table2" required />
+            </label>
+            <label>
+                where:
+                <input name="booleanExpression" placeholder="t.col = 1 AND ..." />
+            </label>
+            <label>
+                order:
+                <input name="sortItemList" placeholder="col1 DESC, col2" />
+            </label>
+            <label>
+                limit:
+                <input name="limitValue" type="number" inputMode="numeric" min="0" step="1" />
+            </label>

39-44: Tighten JSDoc phrasing and return tag.

 /**
- * Returns input boxes to test `buildSearchQuery`.
+ * Renders input boxes to test `buildSearchQuery`.
  *
- * @return
+ * @returns JSX.Element
  */
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5d562e3 and 68f2ed0.

📒 Files selected for processing (1)
  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{cpp,hpp,java,js,jsx,tpp,ts,tsx}

⚙️ CodeRabbit configuration file

  • Prefer false == <expression> rather than !<expression>.

Files:

  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx
🧠 Learnings (1)
📚 Learning: 2024-11-21T15:51:33.203Z
Learnt from: junhaoliao
PR: y-scope/clp#596
File: components/log-viewer-webui/client/src/api/query.js:16-23
Timestamp: 2024-11-21T15:51:33.203Z
Learning: In `components/log-viewer-webui/client/src/api/query.js`, the `ExtractJsonResp` type definition is accurate as-is and does not require modification. When suggesting changes to type definitions, ensure they align with the server-side definitions, referencing the source code if necessary.

Applied to files:

  • components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx
🧬 Code graph analysis (1)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (2)
components/webui/client/src/sql-parser/index.ts (1)
  • buildSearchQuery (125-125)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/presto-search-requests.ts (1)
  • handlePrestoQuerySubmit (104-104)
🪛 Biome (2.1.2)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx

[error] 60-60: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 62-62: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 64-64: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 66-66: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)


[error] 68-68: A form label must be associated with an input.

Consider adding a for or htmlFor attribute to the label element or moving the input element to inside the label element.

(lint/a11y/noLabelWithoutControl)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: package-image
  • GitHub Check: antlr-code-committed (macos-15)
🔇 Additional comments (1)
components/webui/client/src/pages/SearchPage/SearchControls/Presto/BuildSqlTestingInputs.tsx (1)

47-58: Await the submission if it’s async.

If handlePrestoQuerySubmit returns a Promise, make the handler async and await it to surface network errors consistently.

-            onSubmit={(ev: FormEvent<HTMLFormElement>) => {
+            onSubmit={async (ev: FormEvent<HTMLFormElement>) => {
@@
-                    handlePrestoQuerySubmit({queryString: sqlString});
+                    await handlePrestoQuerySubmit({queryString: sqlString});

@junhaoliao junhaoliao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deferring to @davemarco ’s review

@hoophalab hoophalab changed the title feat(webui): Add support for building SQL queries from guided input boxes. feat(webui): Add support for building SQL queries from guided SQL input boxes. Sep 19, 2025
@hoophalab hoophalab merged commit c38ea39 into y-scope:main Sep 19, 2025
20 checks passed
@hoophalab hoophalab changed the title feat(webui): Add support for building SQL queries from guided SQL input boxes. feat(webui): Add support for building queries from guided SQL input boxes. Sep 19, 2025
@hoophalab hoophalab deleted the buildsql branch October 23, 2025 19:06
junhaoliao pushed a commit to junhaoliao/clp that referenced this pull request May 17, 2026
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.

4 participants