feat(sql,core): FROM-less SELECT handshake executed against Elasticsearch (story 20.9) - #268
Merged
Conversation
fupelaqu
added a commit
that referenced
this pull request
Sep 2, 2026
…ke index The read-only-account guidance showed only the raw Elasticsearch PUTs; an administrator connected through SoftClient4ES itself can equivalently run CREATE TABLE IF NOT EXISTS + INSERT (lead review of PR #268). Both statements parse-probed through the real parser; CREATE TABLE IF NOT EXISTS probes via indexExists (unaffected by the SHOW TABLES filter) and INSERT executes with refresh=true, so the seeded doc is immediately searchable. Refs #251 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fupelaqu
added a commit
that referenced
this pull request
Sep 2, 2026
…s; 403 guidance names both routes Lead review of PR #268, round 2: the SQL pre-creation snippet now pins the same settings the lazy creation applies (1 shard / 0 replicas — without OPTIONS the created index takes ES defaults: 1 replica = yellow on single-node, 5 shards on ES 6.8), and the appended 403 guidance names the SQL route (CREATE TABLE IF NOT EXISTS ... OPTIONS + INSERT) alongside the REST PUTs. Snippet parse-probed; rendered settings verified byte-equal to the lazy creation's ({"index":{"number_of_shards":"1","number_of_replicas":"0"}}). FromlessSelectGatewaySpec 9/9; 2.12 core compile green. Refs #251 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…arch (story 20.9) SELECT 1 is the connection/health idiom of the JDBC/SQLAlchemy ecosystem (Tableau re-issues it on every interaction; Superset's connect test and the Flight sidecar's schema probe send it too). It now parses as the new FromlessSelect statement kind and executes AGAINST the cluster: rewritten to SELECT <items> FROM softclient4es_handshake LIMIT 1 through the unmodified FROM-ful script_fields pipeline behind the HandshakeEvaluator seam, so a green handshake genuinely means connected and connectionTestQuery pools become real tests. The handshake index (1 shard/0 replicas, dummy keyword, one seeded doc, index.hidden on ES >= 7.7) is created lazily, race-safely, once per client, and is never listed by SHOW TABLES (covering jdbc getTables and Flight GET_TABLES through the one TableExecutor seam). Constant-only guards (window > aggregation > star > name > dependencies > placeholder walk) re-run on the programmatic path; the statement's LIMIT/OFFSET apply engine-side on the assembled row and never leak into the rewrite. searchAs now aborts cleanly on a FROM-less statement instead of a macro MatchError. Integration green on real ES 6.8 (rest+jest), 7.17, 8.18, 9.0. Closes #251 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ke index The read-only-account guidance showed only the raw Elasticsearch PUTs; an administrator connected through SoftClient4ES itself can equivalently run CREATE TABLE IF NOT EXISTS + INSERT (lead review of PR #268). Both statements parse-probed through the real parser; CREATE TABLE IF NOT EXISTS probes via indexExists (unaffected by the SHOW TABLES filter) and INSERT executes with refresh=true, so the seeded doc is immediately searchable. Refs #251 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s; 403 guidance names both routes Lead review of PR #268, round 2: the SQL pre-creation snippet now pins the same settings the lazy creation applies (1 shard / 0 replicas — without OPTIONS the created index takes ES defaults: 1 replica = yellow on single-node, 5 shards on ES 6.8), and the appended 403 guidance names the SQL route (CREATE TABLE IF NOT EXISTS ... OPTIONS + INSERT) alongside the REST PUTs. Snippet parse-probed; rendered settings verified byte-equal to the lazy creation's ({"index":{"number_of_shards":"1","number_of_replicas":"0"}}). FromlessSelectGatewaySpec 9/9; 2.12 core compile green. Refs #251 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fupelaqu
force-pushed
the
feature/20.9
branch
from
September 2, 2026 14:45
26894a9 to
1b6d8c1
Compare
fupelaqu
marked this pull request as ready for review
September 2, 2026 15:47
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #251
FROM-less SELECT — the connection handshake (story 20.9,
0.22.0train)SELECT 1is the connection/health idiom of the JDBC/SQLAlchemy ecosystem: Tableau re-issues it on every interaction (measured in the Epic 19 capture — each one aborted browse), Superset's connect test sends it, its engine probe and the Flight sidecar's own schema probe sendSELECT 1 LIMIT 100, and connection pools use it asconnectionTestQuery. Until now every one of these failed with a parse error.What this PR does
FromlessSelect(sql):select ~ limit.?, inserted indqlStatementimmediately aftersearchStatement(|commits to the first succeeding alternative;searchStatementfails on FROM-less input becausesinglerequiresfrom). General FROM-less select-list: literals and Painless-translatable constant scalars, multiple items,AS/bare aliases,LIMIT/OFFSET.SELECT <items> FROM softclient4es_handshake LIMIT 1and rides the unmodified FROM-ful SQL→Painlessscript_fieldspipeline — FROM-less and FROM-ful semantics agree by construction (one code path, oneparams.__now__clock per request). A locally-answered handshake would report "connected" against a dead cluster; with ES unreachable,SELECT 1now fails with the propagated client error — a pool'sconnectionTestQuerybecomes a real test.softclient4es_handshake(1 shard / 0 replicas;index.hidden: trueon ES ≥ 7.7; singledummykeyword mapping; one seeded doc), created lazily at first use — probe-before-act, race-safe, memoized per client, bounded self-heal (one retry onindex_not_found/ empty page). Behind a seam (HandshakeEvaluator) so the future Painless-execute-API backend swaps in without touching the statement kind, executor, or tests.TableExecutor'sShowTablesarm — the ONE seam both BI browse surfaces traverse (jdbcgetTablesand FlightGET_TABLESboth executeSHOW TABLESthroughgateway.run), so zero driver/sidecar changes.DESCRIBE TABLE softclient4es_handshakestill works, deliberately.SELECT *, aggregates, window functions (checked before aggregation —WindowFunction extends AggregateFunction),EXCEPT, duplicate output names, unbound?(top-level and nested — a nestedparams.paramValuewould be a silent NULL at ES), array literals, negative/overflow-negative LIMIT/OFFSET. Guards re-run on the programmatic path (run(statement)never callsvalidate()). Clauses (WHERE/GROUP BY/HAVING/ORDER BY) andUNIONstay grammar-rejected.FromlessSelecttakesCoreDqlExtension's structural no-cap arm (result ≤ 1 row; pinned — noCapHitKind.QueryResultsincrement); the internal rewrite always carriesLIMIT 1, so it can never reach scroll/PIT, and the statement's own LIMIT/OFFSET are applied engine-side on the assembled row (LIMIT 0still connection-checks).documentation/sql/dql_statements.md§"FROM-less SELECT (connection handshake)" — semantics, index disclosure, read-only pre-creation guidance, reject list. Web twin rides the0.22.0doc sweep.Tests
FromlessSelectParserSpec(accept/reject/round-trip — every accepted form's.sqlre-parses to an equal AST, fix(sql): make ALTER COLUMN ... SET|ADD FIELD parse and actually apply #218 fixed point) +FromlessSelectValidateSpec(guard arms parse-first; rewrite shape pins incl. the LIMIT-isolation pin) — sql, Docker-free.FromlessSelectGatewaySpec(core, Docker-free, NopeClientApi fixtures): bare no-op client ⇒ handshake fails (nothing answers locally); stubbed client ⇒ row assembly, array unwrap, ensure-flow order + keyword mapping, memoization, LIMIT semantics, multi-statement; failing client ⇒ connection error propagates; probe-403 AND create-403 ⇒ original status preserved + pre-creation guidance appended; SHOW TABLES filter.CoreDqlExtensionSpec: FromlessSelect no-cap routing pin (the recorded search statement IS the LIMIT-1 rewrite on the handshake index).GatewayApiIntegrationSpec+ReplGatewayIntegrationSpec): value assertions on real ES —SELECT 1⇒ one row, column"1", Integer (class-pinned; Scala's cooperative equality would let a Long passshouldBe 1); UPPER/arithmetic/'125'::BIGINT; NULL; RANDOM fresh per execution; one clock per statement;1/0fails loudly; invisibility on every SHOW TABLES pattern + DESCRIBE still working.sbt17): ES 6.8 (rest + jest), 7.17, 8.18, 9.0 — GatewayApi + REPL legs each.Release notes (
0.22.0)SELECTof constant scalar expressions (Support FROM-less SELECT of literal expressions — SELECT 1 is the connection/health idiom of Tableau and Superset and is rejected #251) — executed against Elasticsearch as Painless (connection-check semantics: with the cluster unreachable,SELECT 1fails). Unblocks Tableau/Superset/DBeaver connection tests andconnectionTestQuerypools — which now genuinely test the connection.softclient4es_handshake(1 shard / 0 replicas, one seeded{"dummy":"dummy"}doc,index.hiddenon ES ≥ 7.7). Never listed bySHOW TABLES(nor JDBC/Flight browse); never auto-deleted. Read-only deployments: pre-create + seed it as an administrator (docs §FROM-less SELECT) and lazy creation becomes a no-op probe — the BI service account then only needsreadonsoftclient4es_handshake. Without pre-creation, a read-only session's firstSELECT 1fails with the cluster's own security error plus the pre-creation guidance appended.SELECT 1that previously failed with a parse error now succeed and return one row (column named"1", INTEGER — matching what the 19.4 shim reported).SELECT <expr>rejections now name the reason (... requires a FROM clause) instead of a grammar-combinator error.*/ aggregates / window functions without FROM;WHERE/GROUP BY/HAVING/ORDER BYwithout FROM; FROM-lessUNION ALL; backtick-quoted aliases (Identifier quoting coverage: backticks rejected in every position; double-quoted table names unparseable in FROM #252);CAST(<bare literal> AS T)— pre-existing grammar gap (CAST/TRY_CAST/CONVERT reject bare literal operands — CAST('125' AS BIGINT) does not parse #267), use'<literal>'::T.Coordination
0.22.0train as feat(help): retire the 13 phantom help entries + HelpCorpusSpec corpus-walk anti-drift guard #263 (story 20.7) and fix(core): parse rejections say SQL statement, not schema DDL (Story 20.4) #264 (story 20.4). Trivial rebase expected against fix(core): parse rejections say SQL statement, not schema DDL (Story 20.4) #264 (disjoint regions ofGatewayApi.scala; this PR pins only its own reason substrings + statusCode 400, never theError parsingwording).BiJdbcToolSELECT 1step flips from "the defect that aborts the harness" to a valid Support FROM-less SELECT of literal expressions — SELECT 1 is the connection/health idiom of Tableau and Superset and is rejected #251 regression check against a 0.22.0-based driver.🤖 Generated with Claude Code