Skip to content

feat: support SQL Server OPTION query hints (#161) - #2472

Open
fudianchn wants to merge 1 commit into
JSQLParser:masterfrom
fudianchn:feat/sqlserver-query-hints-161
Open

feat: support SQL Server OPTION query hints (#161)#2472
fudianchn wants to merge 1 commit into
JSQLParser:masterfrom
fudianchn:feat/sqlserver-query-hints-161

Conversation

@fudianchn

Copy link
Copy Markdown
Contributor

What

Adds support for the SQL Server (T-SQL) OPTION (...) query hint clause at the end of SELECT (including after set operations and FOR XML), UPDATE and DELETE statements, modelled as a new OptionClause holding a list of OptionHints.

Why

The grammar has no production for query hints (#161), so statements like SELECT CustomerID, PersonID, StoreID FROM cte OPTION (MAXRECURSION 2) fail to parse, although this is everyday T-SQL for query tuning.

How

  • New OptionClause() production attached in four places (after ForClause in PlainSelect, before LIMIT in the Select wrapper, after LIMIT in Update/Delete), following the Hints (Transact-SQL) - Query Hints documentation.
  • OptionHintName() collects (possibly multi-word) keyword names (RECOMPILE, HASH JOIN, OPTIMIZE FOR UNKNOWN, ...) from identifiers, the non-reserved word range and an explicit list of reserved words usable inside hint keywords (JOIN, FOR, ORDER, UNION, EXCEPT, INTERSECT, USE, OPTIMIZE, FORCE, UNKNOWN).
  • A hint takes no argument, a single value (FAST 100, MAXDOP 4, MAX_GRANT_PERCENT = 25) or a parenthesized argument list (USE HINT ('...'), OPTIMIZE FOR (@p = 1), TABLE HINT (t, INDEX (i))), kept generically as name + value/parameters because SQL Server keeps adding new hints with every release.
  • K_OPTION is a non-reserved keyword; isAliasAhead() treats K_OPTION followed by ( as a clause start rather than a table alias, so option remains usable as a column and table name.
  • Like ORDER BY/LIMIT, the clause is hoisted from the last PlainSelect onto the SetOperationList, so SELECT ... UNION SELECT ... ORDER BY ... OPTION (...) deparse in the right order.
  • AST classes OptionClause/OptionHint plus wiring in Select, Update, Delete, the three deparsers and the three validators.

Root cause

Missing feature: the grammar has no production for the T-SQL query hint clause, so such statements always threw a ParseException.

Testing

  • New OptionClauseTest (13 cases): the issue's SQL plus AST assertions, all hint shapes (keyword / value / equals / parameter list), UNION, FOR XML, UPDATE, DELETE, and option as column/table name; ./gradlew test --tests ...OptionClauseTest passes.
  • Full local gate ./gradlew spotlessApply check on the branch: 4816 tests, 0 failures.

Verification of the original issue

Before (master 406a4d4): CCJSqlParserUtil.parse("SELECT CustomerID, PersonID, StoreID FROM cte OPTION (MAXRECURSION 2)") throws a ParseException.
After: parses into PlainSelect with getOption().getOptionHints().get(0) = name MAXRECURSION, value 2, and the deparse round-trips unchanged. All other query hint forms currently documented by Microsoft parse and round-trip as well.

Grammar change, paired gradle jmh runs (JSQLParserBenchmark.parseSQLStatements on performance.sql, version=latest, 10 forks x 10 iterations = 100 samples each, 32-core host), two interleaved pairs:

build ms/op (run 1) ms/op (run 2)
master 406a4d4 3.724 ± 0.028 3.740 ± 0.030
this PR 3.747 ± 0.025 3.761 ± 0.030

In both pairs the delta (+0.6%) is smaller than the combined confidence interval -> not statistically significant, no regression.

Limitation: T-SQL also allows OPTION (...) after INSERT and MERGE statements, and the OPTIMIZE FOR (@var UNKNOWN) parameter form; these are not covered yet (the latter needs an AST representation for a variable followed by the bare UNKNOWN keyword, e.g. a dedicated expression type or a nullable-value VariableAssignment; both can be added in follow-ups without changing the modeling introduced here).

Fixes #161

Parse the T-SQL OPTION (...) clause at the end of SELECT (including after
set operations and FOR XML), UPDATE and DELETE statements, following the
Query Hints documentation: multi-word keyword hints (RECOMPILE, HASH
JOIN, FORCE ORDER, OPTIMIZE FOR UNKNOWN, ...), single value arguments
(FAST 100, MAXDOP 4, MAX_GRANT_PERCENT = 25) and parenthesized argument
lists (USE HINT ('...'), OPTIMIZE FOR (@p = 1), TABLE HINT (t, INDEX
(i))).

Hints are kept in a generic OptionHint (keyword name + optional value or
ExpressionList parameters) attached via a new OptionClause, mirroring
how SQL Server keeps adding new hints without enum churn. OPTION is a
non-reserved keyword and K_OPTION followed by ( is excluded from alias
positions, so option remains usable as a column and table name. The
union hoisting lifts the clause from the last PlainSelect onto the
SetOperationList like ORDER BY and LIMIT.

Signed-off-by: 付典 <fudianchn@gmail.com>
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.

add support for SQL Server Query Hints

1 participant