fix: apply the partition limit through the SQL AST instead of appending it to the query text - #188
Open
eastagiletracker wants to merge 1 commit into
Conversation
…ng it to the query text
Passing limit_partitions to query() appended "WHERE partition_date IN (...)" to the end of
the SQL string, so any query that does not end where a WHERE clause belongs became invalid:
ORDER BY, LIMIT and GROUP BY all produced ParserError("Expected end of statement, found:
WHERE"). Detecting an existing clause by searching the text for "WHERE" also misfired on
string literals and subqueries, and appending "AND ..." to a condition ending in OR let rows
from excluded partitions through.
The filter is now built as an InList expression and injected into the WHERE clause of every
SELECT that reads a stored table, using the sqlparser already used for table extraction. An
existing condition is wrapped before the AND is added, CTE and derived-table bodies get the
filter pushed into them, and both branches of a UNION are covered.
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.
This PR proposes applying the
limit_partitionsfilter through the SQL AST instead of appending it to the query text, so thatquery(..., limit_partitions)stops failing on statements that end inORDER BY,LIMITorGROUP BY. We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/428. You can sign in with your GitHub ID to claim ownership of the project.What is wrong today
DatabaseManager::querybuilds the partition restriction by concatenating it onto the end of the SQL string:A
WHEREclause only belongs directly afterFROM, so any query whose text continues past that point becomes invalid SQL.SELECT * FROM t ORDER BY dateturns intoSELECT * FROM t ORDER BY date WHERE partition_date IN (...), and the same happens with a trailingLIMIT,GROUP BYorHAVING. SincelimitPartitionsis part of the documented mobile surface (queryandqueryDfin the README, andnativeQueryon both the JNI and the iOS side), an app that passes it together with an ordinary sorted or paged query gets an error back instead of rows.There are two quieter failures in the same three lines. Searching the raw text for
WHEREalso matches a string literal, a column alias or a subquery, soSELECT 'nowhere' AS label FROM tis treated as already having aWHEREclause and getsAND ...appended to a statement that has none. And becauseANDbinds tighter thanOR, appending the partition condition to a filter that ends in anORonly restricts that last branch:WHERE id = 1 OR id = 3with a one-partition limit still returns the row from the excluded partition, with no error to notice.Reproducing it on
developAt
760ce86, with three records written into three consecutive 30-minute partitions:The one test that passes there is the control: a plain
SELECT * FROM test_tablehas nothing after the table name, which is the single shape the string concatenation gets right.The change
inject_partition_filterinsql_query_parser.rsbuilds the restriction as anInListexpression and injects it into theWHEREclause of eachSELECTthat reads a stored table, reusing thesqlparserthis module already parses with, then renders the statement back to SQL. Trailing clauses keep their position because they are separate AST nodes; an existing condition is wrapped in parentheses before theANDis added, so anORstill binds first; and theWHERE-in-the-text guess disappears entirely. ASELECTthat reads only a CTE or a derived table is skipped and the filter is pushed into that body instead, where the partition column is still available, and both sides of aUNIONare covered. An empty partition list returns the query untouched, which keeps the existing early-exit behaviour.The public signature of
query/query_dfis unchanged, and calls that passNonenever reach this code at all.Verification
19 tests come with the change: 14 unit tests on
inject_partition_filtercoveringORDER BY,LIMIT,GROUP BY/HAVING, an existingWHERE, theORprecedence case, the string-literal false positive, CTEs, derived tables,UNION,JOIN, an empty partition list, aSELECTwith no table, and malformed SQL; plus 5 tests that drive the realDatabaseManager::querypath end to end over a three-partition table.Reverting only the call site in
db_manager.rswhile keeping the new function is what produced the failures quoted above — 4 of the 5 end-to-end tests go red, the unit tests stay green, which is what pins the failures to the injection rather than to the tests.Same single failure before and after:
cloud_sync_test::test_process_sink_parquet_file_*, which is order-dependent — running that module alone on an untoucheddevelopreproduces it, and which member of the family fails changes between runs. Nothing this change touches is involved.cargo fmt --checkis clean, andcargo clippy --all-targetsreports exactly the same five pre-existing errors inhelpers_test.rsandsql_query_parser_test.rsas it does ondevelop.One thing noticed while reading the surrounding code, left alone here because it is a separate decision:
all_partitionsis collected from every table in the database rather than from the tables the query actually references, so a limit applied to a database with unevenly-partitioned tables can select dates that do not exist for the table being read.How this was managed
This work was tracked as a story on a board imported from this repository's own issues and pull requests — 187 stories in all — and the board stays live: the story for this fix and the board itself.
If you'd rather not receive contributions like this, reply
no-more-prson this pull request and we won't open any further ones on your repositories.Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/
eastagile.com