Skip to content

Commit 71c3063

Browse files
authored
feat: support graph and pipe queries in Connection API (#3586)
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.
1 parent c9f41f7 commit 71c3063

File tree

3 files changed

+119
-77
lines changed

3 files changed

+119
-77
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ If you are using Maven with [BOM][libraries-bom], add this to your pom.xml file:
1919
<dependency>
2020
<groupId>com.google.cloud</groupId>
2121
<artifactId>libraries-bom</artifactId>
22-
<version>26.50.0</version>
22+
<version>26.53.0</version>
2323
<type>pom</type>
2424
<scope>import</scope>
2525
</dependency>

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/AbstractStatementParser.java

+20-1
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,10 @@ ClientSideStatement getClientSideStatement() {
418418

419419
static final Set<String> ddlStatements =
420420
ImmutableSet.of("CREATE", "DROP", "ALTER", "ANALYZE", "GRANT", "REVOKE", "RENAME");
421-
static final Set<String> selectStatements = ImmutableSet.of("SELECT", "WITH", "SHOW");
421+
static final Set<String> selectStatements =
422+
ImmutableSet.of("SELECT", "WITH", "SHOW", "FROM", "GRAPH");
423+
static final Set<String> SELECT_STATEMENTS_ALLOWING_PRECEDING_BRACKETS =
424+
ImmutableSet.of("SELECT", "FROM");
422425
static final Set<String> dmlStatements = ImmutableSet.of("INSERT", "UPDATE", "DELETE");
423426
private final Set<ClientSideStatementImpl> statements;
424427

@@ -581,6 +584,10 @@ public boolean isQuery(String sql) {
581584
if (sql.startsWith("@")) {
582585
sql = removeStatementHint(sql);
583586
}
587+
if (sql.startsWith("(")) {
588+
sql = removeOpeningBrackets(sql);
589+
return statementStartsWith(sql, SELECT_STATEMENTS_ALLOWING_PRECEDING_BRACKETS);
590+
}
584591
return statementStartsWith(sql, selectStatements);
585592
}
586593

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

668+
private String removeOpeningBrackets(String sql) {
669+
int index = 0;
670+
while (index < sql.length()) {
671+
if (sql.charAt(index) == '(' || Character.isWhitespace(sql.charAt(index))) {
672+
index++;
673+
} else {
674+
return sql.substring(index);
675+
}
676+
}
677+
return sql;
678+
}
679+
661680
@VisibleForTesting
662681
static final ReadQueryUpdateTransactionOption[] EMPTY_OPTIONS =
663682
new ReadQueryUpdateTransactionOption[0];

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/StatementParserTest.java

+98-75
Original file line numberDiff line numberDiff line change
@@ -585,87 +585,110 @@ public void testIsDdlStatement() {
585585

586586
@Test
587587
public void testIsQuery() {
588-
assertThat(parser.isQuery("")).isFalse();
589-
assertThat(parser.isQuery("random text")).isFalse();
590-
assertThat(parser.isQuery("SELECT1")).isFalse();
591-
assertThat(parser.isQuery("SSELECT 1")).isFalse();
588+
assertFalse(parser.isQuery(""));
589+
assertFalse(parser.isQuery("random text"));
590+
assertFalse(parser.isQuery("SELECT1"));
591+
assertFalse(parser.isQuery("SSELECT 1"));
592592

593-
assertThat(parser.isQuery("SELECT 1")).isTrue();
594-
assertThat(parser.isQuery("select 1")).isTrue();
595-
assertThat(parser.isQuery("SELECT foo FROM bar WHERE id=@id")).isTrue();
593+
assertTrue(parser.isQuery("SELECT 1"));
594+
assertTrue(parser.isQuery("select 1"));
595+
assertTrue(parser.isQuery("SELECT foo FROM bar WHERE id=@id"));
596596

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

606-
assertThat(parser.isQuery("select * from foo")).isTrue();
605+
assertTrue(parser.isQuery("select * from foo"));
607606

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

610-
assertThat(
611-
parser.isQuery(
612-
"WITH subQ1 AS (SELECT SchoolID FROM Roster),\n"
613-
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
614-
+ "SELECT * FROM subQ1\n"
615-
+ "UNION ALL\n"
616-
+ "SELECT * FROM subQ2"))
617-
.isTrue();
618-
assertThat(
619-
parser.isQuery(
620-
"with subQ1 AS (SELECT SchoolID FROM Roster),\n"
621-
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
622-
+ "select * FROM subQ1\n"
623-
+ "UNION ALL\n"
624-
+ "SELECT * FROM subQ2"))
625-
.isTrue();
626-
assertThat(
627-
parser
628-
.parse(
629-
Statement.of(
630-
"-- this is a comment\nwith foo as (select * from bar)\nselect * from foo"))
631-
.isQuery())
632-
.isTrue();
609+
assertTrue(
610+
parser.isQuery(
611+
"WITH subQ1 AS (SELECT SchoolID FROM Roster),\n"
612+
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
613+
+ "SELECT * FROM subQ1\n"
614+
+ "UNION ALL\n"
615+
+ "SELECT * FROM subQ2"));
616+
assertTrue(
617+
parser.isQuery(
618+
"with subQ1 AS (SELECT SchoolID FROM Roster),\n"
619+
+ " subQ2 AS (SELECT OpponentID FROM PlayerStats)\n"
620+
+ "select * FROM subQ1\n"
621+
+ "UNION ALL\n"
622+
+ "SELECT * FROM subQ2"));
623+
assertTrue(
624+
parser
625+
.parse(
626+
Statement.of(
627+
"-- this is a comment\nwith foo as (select * from bar)\nselect * from foo"))
628+
.isQuery());
633629

634-
assertThat(parser.parse(Statement.of("-- this is a comment\nselect * from foo")).isQuery())
635-
.isTrue();
636-
assertThat(
637-
parser
638-
.parse(
639-
Statement.of(
640-
"/* multi line comment\n* with more information on the next line\n*/\nSELECT ID, NAME\nFROM\tTEST\n\tWHERE ID=1"))
641-
.isQuery())
642-
.isTrue();
643-
assertThat(
644-
parser
645-
.parse(
646-
Statement.of(
647-
"/** java doc comment\n* with more information on the next line\n*/\nselect max(id) from test"))
648-
.isQuery())
649-
.isTrue();
650-
assertThat(
651-
parser
652-
.parse(Statement.of("-- INSERT in a single line comment \n select 1"))
653-
.isQuery())
654-
.isTrue();
655-
assertThat(
656-
parser
657-
.parse(
658-
Statement.of(
659-
"/* UPDATE in a multi line comment\n* with more information on the next line\n*/\nSELECT 1"))
660-
.isQuery())
661-
.isTrue();
662-
assertThat(
663-
parser
664-
.parse(
665-
Statement.of(
666-
"/** DELETE in a java doc comment\n* with more information on the next line\n*/\n\n\n\n -- UPDATE test\nSELECT 1"))
667-
.isQuery())
668-
.isTrue();
630+
assertTrue(parser.parse(Statement.of("-- this is a comment\nselect * from foo")).isQuery());
631+
assertTrue(
632+
parser
633+
.parse(
634+
Statement.of(
635+
"/* multi line comment\n* with more information on the next line\n*/\nSELECT ID, NAME\nFROM\tTEST\n\tWHERE ID=1"))
636+
.isQuery());
637+
assertTrue(
638+
parser
639+
.parse(
640+
Statement.of(
641+
"/** java doc comment\n* with more information on the next line\n*/\nselect max(id) from test"))
642+
.isQuery());
643+
assertTrue(
644+
parser.parse(Statement.of("-- INSERT in a single line comment \n select 1")).isQuery());
645+
assertTrue(
646+
parser
647+
.parse(
648+
Statement.of(
649+
"/* UPDATE in a multi line comment\n* with more information on the next line\n*/\nSELECT 1"))
650+
.isQuery());
651+
assertTrue(
652+
parser
653+
.parse(
654+
Statement.of(
655+
"/** DELETE in a java doc comment\n* with more information on the next line\n*/\n\n\n\n -- UPDATE test\nSELECT 1"))
656+
.isQuery());
657+
658+
assertTrue(
659+
parser
660+
.parse(
661+
Statement.of(
662+
"GRAPH FinGraph\n" + "MATCH (n)\n" + "RETURN LABELS(n) AS label, n.id"))
663+
.isQuery());
664+
assertTrue(
665+
parser.parse(Statement.of("FROM Produce\n" + "|> WHERE item != 'bananas'")).isQuery());
666+
667+
assertTrue(
668+
parser
669+
.parse(
670+
Statement.of(
671+
"(\n"
672+
+ " SELECT * FROM Foo\n"
673+
+ " EXCEPT ALL\n"
674+
+ " SELECT 1\n"
675+
+ ")\n"
676+
+ "EXCEPT ALL\n"
677+
+ "SELECT 2"))
678+
.isQuery());
679+
assertTrue(
680+
parser
681+
.parse(
682+
Statement.of(
683+
"(\n"
684+
+ " (SELECT * FROM Foo)\n"
685+
+ " EXCEPT ALL\n"
686+
+ " SELECT 1\n"
687+
+ ")\n"
688+
+ "EXCEPT ALL\n"
689+
+ "SELECT 2"))
690+
.isQuery());
691+
assertFalse(parser.parse(Statement.of("(show variable autocommit;\n")).isQuery());
669692
}
670693

671694
@Test

0 commit comments

Comments
 (0)