Skip to content

Commit

Permalink
feat: support graph and pipe queries in Connection API (#3586)
Browse files Browse the repository at this point in the history
Adds support for GRAPH and pipe-syntax queries to the Connection API. This will make these types of queries usable in the JDBC driver.

This also fixes an issue with regular GoogleSQL queries that start with a bracket. These queries were not correctly recognized as valid queries.
  • Loading branch information
olavloite authored Jan 26, 2025
1 parent c9f41f7 commit 71c3063
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 77 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file:
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>libraries-bom</artifactId>
<version>26.50.0</version>
<version>26.53.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,10 @@ ClientSideStatement getClientSideStatement() {

static final Set<String> ddlStatements =
ImmutableSet.of("CREATE", "DROP", "ALTER", "ANALYZE", "GRANT", "REVOKE", "RENAME");
static final Set<String> selectStatements = ImmutableSet.of("SELECT", "WITH", "SHOW");
static final Set<String> selectStatements =
ImmutableSet.of("SELECT", "WITH", "SHOW", "FROM", "GRAPH");
static final Set<String> SELECT_STATEMENTS_ALLOWING_PRECEDING_BRACKETS =
ImmutableSet.of("SELECT", "FROM");
static final Set<String> dmlStatements = ImmutableSet.of("INSERT", "UPDATE", "DELETE");
private final Set<ClientSideStatementImpl> statements;

Expand Down Expand Up @@ -581,6 +584,10 @@ public boolean isQuery(String sql) {
if (sql.startsWith("@")) {
sql = removeStatementHint(sql);
}
if (sql.startsWith("(")) {
sql = removeOpeningBrackets(sql);
return statementStartsWith(sql, SELECT_STATEMENTS_ALLOWING_PRECEDING_BRACKETS);
}
return statementStartsWith(sql, selectStatements);
}

Expand Down Expand Up @@ -658,6 +665,18 @@ public String removeCommentsAndTrim(String sql) {
/** Removes any statement hints at the beginning of the statement. */
abstract String removeStatementHint(String sql);

private String removeOpeningBrackets(String sql) {
int index = 0;
while (index < sql.length()) {
if (sql.charAt(index) == '(' || Character.isWhitespace(sql.charAt(index))) {
index++;
} else {
return sql.substring(index);
}
}
return sql;
}

@VisibleForTesting
static final ReadQueryUpdateTransactionOption[] EMPTY_OPTIONS =
new ReadQueryUpdateTransactionOption[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,87 +585,110 @@ public void testIsDdlStatement() {

@Test
public void testIsQuery() {
assertThat(parser.isQuery("")).isFalse();
assertThat(parser.isQuery("random text")).isFalse();
assertThat(parser.isQuery("SELECT1")).isFalse();
assertThat(parser.isQuery("SSELECT 1")).isFalse();
assertFalse(parser.isQuery(""));
assertFalse(parser.isQuery("random text"));
assertFalse(parser.isQuery("SELECT1"));
assertFalse(parser.isQuery("SSELECT 1"));

assertThat(parser.isQuery("SELECT 1")).isTrue();
assertThat(parser.isQuery("select 1")).isTrue();
assertThat(parser.isQuery("SELECT foo FROM bar WHERE id=@id")).isTrue();
assertTrue(parser.isQuery("SELECT 1"));
assertTrue(parser.isQuery("select 1"));
assertTrue(parser.isQuery("SELECT foo FROM bar WHERE id=@id"));

assertThat(parser.isQuery("INSERT INTO FOO (ID, NAME) VALUES (1, 'NAME')")).isFalse();
assertThat(parser.isQuery("UPDATE FOO SET NAME='NAME' WHERE ID=1")).isFalse();
assertThat(parser.isQuery("DELETE FROM FOO")).isFalse();
assertThat(parser.isQuery("CREATE TABLE FOO (ID INT64, NAME STRING(100)) PRIMARY KEY (ID)"))
.isFalse();
assertThat(parser.isQuery("alter table foo add Description string(100)")).isFalse();
assertThat(parser.isQuery("drop table foo")).isFalse();
assertThat(parser.isQuery("Create index BAR on foo (name)")).isFalse();
assertFalse(parser.isQuery("INSERT INTO FOO (ID, NAME) VALUES (1, 'NAME')"));
assertFalse(parser.isQuery("UPDATE FOO SET NAME='NAME' WHERE ID=1"));
assertFalse(parser.isQuery("DELETE FROM FOO"));
assertFalse(parser.isQuery("CREATE TABLE FOO (ID INT64, NAME STRING(100)) PRIMARY KEY (ID)"));
assertFalse(parser.isQuery("alter table foo add Description string(100)"));
assertFalse(parser.isQuery("drop table foo"));
assertFalse(parser.isQuery("Create index BAR on foo (name)"));

assertThat(parser.isQuery("select * from foo")).isTrue();
assertTrue(parser.isQuery("select * from foo"));

assertThat(parser.isQuery("INSERT INTO FOO (ID, NAME) SELECT ID+1, NAME FROM FOO")).isFalse();
assertFalse(parser.isQuery("INSERT INTO FOO (ID, NAME) SELECT ID+1, NAME FROM FOO"));

assertThat(
parser.isQuery(
"WITH subQ1 AS (SELECT SchoolID FROM Roster),\n"
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
+ "SELECT * FROM subQ1\n"
+ "UNION ALL\n"
+ "SELECT * FROM subQ2"))
.isTrue();
assertThat(
parser.isQuery(
"with subQ1 AS (SELECT SchoolID FROM Roster),\n"
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
+ "select * FROM subQ1\n"
+ "UNION ALL\n"
+ "SELECT * FROM subQ2"))
.isTrue();
assertThat(
parser
.parse(
Statement.of(
"-- this is a comment\nwith foo as (select * from bar)\nselect * from foo"))
.isQuery())
.isTrue();
assertTrue(
parser.isQuery(
"WITH subQ1 AS (SELECT SchoolID FROM Roster),\n"
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
+ "SELECT * FROM subQ1\n"
+ "UNION ALL\n"
+ "SELECT * FROM subQ2"));
assertTrue(
parser.isQuery(
"with subQ1 AS (SELECT SchoolID FROM Roster),\n"
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
+ "select * FROM subQ1\n"
+ "UNION ALL\n"
+ "SELECT * FROM subQ2"));
assertTrue(
parser
.parse(
Statement.of(
"-- this is a comment\nwith foo as (select * from bar)\nselect * from foo"))
.isQuery());

assertThat(parser.parse(Statement.of("-- this is a comment\nselect * from foo")).isQuery())
.isTrue();
assertThat(
parser
.parse(
Statement.of(
"/* multi line comment\n* with more information on the next line\n*/\nSELECT ID, NAME\nFROM\tTEST\n\tWHERE ID=1"))
.isQuery())
.isTrue();
assertThat(
parser
.parse(
Statement.of(
"/** java doc comment\n* with more information on the next line\n*/\nselect max(id) from test"))
.isQuery())
.isTrue();
assertThat(
parser
.parse(Statement.of("-- INSERT in a single line comment \n select 1"))
.isQuery())
.isTrue();
assertThat(
parser
.parse(
Statement.of(
"/* UPDATE in a multi line comment\n* with more information on the next line\n*/\nSELECT 1"))
.isQuery())
.isTrue();
assertThat(
parser
.parse(
Statement.of(
"/** DELETE in a java doc comment\n* with more information on the next line\n*/\n\n\n\n -- UPDATE test\nSELECT 1"))
.isQuery())
.isTrue();
assertTrue(parser.parse(Statement.of("-- this is a comment\nselect * from foo")).isQuery());
assertTrue(
parser
.parse(
Statement.of(
"/* multi line comment\n* with more information on the next line\n*/\nSELECT ID, NAME\nFROM\tTEST\n\tWHERE ID=1"))
.isQuery());
assertTrue(
parser
.parse(
Statement.of(
"/** java doc comment\n* with more information on the next line\n*/\nselect max(id) from test"))
.isQuery());
assertTrue(
parser.parse(Statement.of("-- INSERT in a single line comment \n select 1")).isQuery());
assertTrue(
parser
.parse(
Statement.of(
"/* UPDATE in a multi line comment\n* with more information on the next line\n*/\nSELECT 1"))
.isQuery());
assertTrue(
parser
.parse(
Statement.of(
"/** DELETE in a java doc comment\n* with more information on the next line\n*/\n\n\n\n -- UPDATE test\nSELECT 1"))
.isQuery());

assertTrue(
parser
.parse(
Statement.of(
"GRAPH FinGraph\n" + "MATCH (n)\n" + "RETURN LABELS(n) AS label, n.id"))
.isQuery());
assertTrue(
parser.parse(Statement.of("FROM Produce\n" + "|> WHERE item != 'bananas'")).isQuery());

assertTrue(
parser
.parse(
Statement.of(
"(\n"
+ " SELECT * FROM Foo\n"
+ " EXCEPT ALL\n"
+ " SELECT 1\n"
+ ")\n"
+ "EXCEPT ALL\n"
+ "SELECT 2"))
.isQuery());
assertTrue(
parser
.parse(
Statement.of(
"(\n"
+ " (SELECT * FROM Foo)\n"
+ " EXCEPT ALL\n"
+ " SELECT 1\n"
+ ")\n"
+ "EXCEPT ALL\n"
+ "SELECT 2"))
.isQuery());
assertFalse(parser.parse(Statement.of("(show variable autocommit;\n")).isQuery());
}

@Test
Expand Down

0 comments on commit 71c3063

Please sign in to comment.