Repository navigation
[jdbc-v2] Bind DatabaseMetaData patterns instead of concatenating them into SQL - #3171
Merged
chernser merged 2 commits intoSep 30, 2026
Merged
Conversation
Closes ClickHouse#3169. getSchemas(String, String), getPrimaryKeys and getFunctions in jdbc-v2 DatabaseMetaDataImpl built their WHERE clause by concatenating the caller's pattern into the SQL text as "... LIKE '" + pattern + "'". A single quote in the argument closed the literal early, so a name that ClickHouse accepts (for example a database created as `a'b`) failed with Code 62 SYNTAX_ERROR, and the remainder of the argument was parsed as SQL - getSchemas(null, "nomatch' OR '1'='1") returned every database instead of none. Bind each pattern as a PreparedStatement parameter, the way the sibling getTables already does. PreparedStatementImpl encodes String parameters with SQLUtils.escapeSingleQuotes, which escapes the backslash before the quote, so this path is correct for backslashes too and does not depend on the enquoteLiteral fix in ClickHouse#3063. The LIKE / ILIKE operators, the null to "%" default and the JDBC search escape are unchanged. Because the PreparedStatement is now closed with the try-with-resources block, the rows are returned through DetachedResultSet, matching getTables and getTableTypes. This also stops the three methods leaking the Statement they used to create and never close. Add a regression test to DatabaseMetaDataTest with a @dataProvider over the three methods, covering a database and table whose names contain a single quote (must be found) and the pattern "nomatch' OR '1'='1" (must return no rows).
Contributor
|
Thank you! LGTM |
Contributor
Author
|
@chernser Merged |
chernser
approved these changes
Sep 30, 2026
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 #3169.
What was wrong
Three
DatabaseMetaDatamethods in jdbc-v2 built theirWHEREclause by concatenating thecaller's pattern into the SQL text as
... LIKE '" + pattern + "':getSchemas(String catalog, String schemaPattern)getPrimaryKeys(String catalog, String schema, String table)getFunctions(String catalog, String schemaPattern, String functionNamePattern)A single quote in the argument closes the literal early, which has two consequences:
CREATE DATABASE `a'b`is valid, butgetSchemas(null, "a'b")throwsCode: 62 ... Single quoted string is not closed (SYNTAX_ERROR).filtering it.
The sibling methods already get this right:
getTablesbinds the pattern as aPreparedStatementparameter, and
getColumnsusesSQLUtils.enquoteLiteral.Change
Bind each pattern as a
PreparedStatementparameter, the waygetTablesdoes. TheLIKE/ILIKEoperators, the
null→%default and the JDBC search escape (getSearchStringEscape()returns\) are all unchanged.I chose binding over
SQLUtils.enquoteLiteralfor the reason given in the issue.PreparedStatementImpl.encodeObjectencodes String parameters withSQLUtils.escapeSingleQuotes,which escapes the backslash before the quote, so this path is already correct for backslashes.
This makes the fix independent of #3063 / #3146 — it does not need the
enquoteLiteralbackslash fix to land first, and it will not need revisiting once that one does.
Because the
PreparedStatementis now closed by try-with-resources, the rows are returned throughDetachedResultSet, matchinggetTablesandgetTableTypes. As a side effect this stops the threemethods leaking the
Statementthey previously created and never closed.Testing
Added
testMetadataPatternIsUsedAsValueNotSqltoDatabaseMetaDataTest: a@DataProviderover thethree methods, covering a database
mdquotea'band tablet'1whose names contain a quote (must befound) and the pattern
nomatch' OR '1'='1(must return no rows). The database name deliberatelycontains no
_, sinceLIKEtreats_as a single-character wildcard and the row-count assertionwould otherwise be non-deterministic.
Run against ClickHouse 25.8 via Testcontainers:
DatabaseMetaDataImplreverted tomainDatabaseMetaDataTestclass, with this fixThe reverted run reproduces the issue exactly, so the new cases genuinely pin the bug:
I pinned
-DclickhouseVersion=25.8(the versionnightly.ymluses) because onlatestthe runfails with
Invalid LZ4 magic bytefor every test, patched or not — that is #3105 and unrelated tothis change. I did not add a
compress=falseworkaround to the test, since it would mask realfailures.
Note on overlap
Open PR #3168 makes
getSchemasuseSHOW DATABASESby default, but keeps thissystem.databasesquery for
jdbc_metadata_use_show_statements=false, and does not touchgetPrimaryKeysorgetFunctions. Happy to rebase on it if it lands first.