Skip to content

Commit 12a0acb

Browse files
chore: add namespace for client side statements in PostgreSQL dialect (#1737)
* chore: namespace Spanner statements for PostgreSQL dialect * feat: add namespace for client side statements in PostgreSQL * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * feat: add transaction statements * chore: address review comments * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * test: separate statement timeout tests for PG * chore: address review comments Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 2f5ac2d commit 12a0acb

File tree

43 files changed

+59963
-5489
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+59963
-5489
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ implementation 'com.google.cloud:google-cloud-spanner'
5656
If you are using Gradle without BOM, add this to your dependencies
5757

5858
```Groovy
59-
implementation 'com.google.cloud:google-cloud-spanner:6.21.1'
59+
implementation 'com.google.cloud:google-cloud-spanner:6.21.2'
6060
```
6161

6262
If you are using SBT, add this to your dependencies
6363

6464
```Scala
65-
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.21.1"
65+
libraryDependencies += "com.google.cloud" % "google-cloud-spanner" % "6.21.2"
6666
```
6767

6868
## Authentication

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

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,11 @@
2222
import com.google.cloud.spanner.SpannerException;
2323
import com.google.cloud.spanner.SpannerExceptionFactory;
2424
import com.google.cloud.spanner.Statement;
25-
import com.google.cloud.spanner.connection.ClientSideStatementImpl.CompileException;
2625
import com.google.common.annotations.VisibleForTesting;
2726
import com.google.common.base.Preconditions;
2827
import com.google.common.collect.ImmutableMap;
2928
import com.google.common.collect.ImmutableSet;
3029
import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
31-
import java.util.Collections;
3230
import java.util.HashMap;
3331
import java.util.Map;
3432
import java.util.Objects;
@@ -322,14 +320,9 @@ ClientSideStatement getClientSideStatement() {
322320
private final Dialect dialect;
323321
private final Set<ClientSideStatementImpl> statements;
324322

325-
AbstractStatementParser(Dialect dialect) {
323+
AbstractStatementParser(Dialect dialect, Set<ClientSideStatementImpl> statements) {
326324
this.dialect = dialect;
327-
try {
328-
statements =
329-
Collections.unmodifiableSet(ClientSideStatements.INSTANCE.getCompiledStatements());
330-
} catch (CompileException e) {
331-
throw new RuntimeException(e);
332-
}
325+
this.statements = statements;
333326
}
334327

335328
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2022 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.spanner.connection;
18+
19+
import com.google.cloud.spanner.ErrorCode;
20+
import com.google.cloud.spanner.SpannerExceptionFactory;
21+
import com.google.cloud.spanner.connection.ClientSideStatementImpl.CompileException;
22+
import com.google.cloud.spanner.connection.ClientSideStatementValueConverters.PgTransactionModeConverter;
23+
import java.lang.reflect.Method;
24+
import java.util.regex.Matcher;
25+
26+
/** Specific executor for the BEGIN statement for PostgreSQL. */
27+
class ClientSideStatementPgBeginExecutor implements ClientSideStatementExecutor {
28+
private final ClientSideStatementImpl statement;
29+
private final Method method;
30+
private final PgTransactionModeConverter converter;
31+
32+
ClientSideStatementPgBeginExecutor(ClientSideStatementImpl statement) throws CompileException {
33+
try {
34+
this.statement = statement;
35+
this.converter = new PgTransactionModeConverter();
36+
this.method =
37+
ConnectionStatementExecutor.class.getDeclaredMethod(
38+
statement.getMethodName(), converter.getParameterClass());
39+
} catch (Exception e) {
40+
throw new CompileException(e, statement);
41+
}
42+
}
43+
44+
@Override
45+
public StatementResult execute(ConnectionStatementExecutor connection, String sql)
46+
throws Exception {
47+
return (StatementResult) method.invoke(connection, getParameterValue(sql));
48+
}
49+
50+
PgTransactionMode getParameterValue(String sql) {
51+
Matcher matcher = statement.getPattern().matcher(sql);
52+
if (matcher.find() && matcher.groupCount() >= 1) {
53+
String value = matcher.group(1);
54+
if (value != null) {
55+
PgTransactionMode res = converter.convert(value.trim());
56+
if (res != null) {
57+
return res;
58+
}
59+
throw SpannerExceptionFactory.newSpannerException(
60+
ErrorCode.INVALID_ARGUMENT, String.format("Unknown transaction mode: %s", value));
61+
}
62+
}
63+
return null;
64+
}
65+
}

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.EnumSet;
3030
import java.util.HashMap;
3131
import java.util.Map;
32+
import java.util.concurrent.TimeUnit;
3233
import java.util.regex.Matcher;
3334
import java.util.regex.Pattern;
3435

@@ -121,6 +122,48 @@ public Duration convert(String value) {
121122
}
122123
}
123124

125+
/** Converter from string to {@link Duration}. */
126+
static class PgDurationConverter implements ClientSideStatementValueConverter<Duration> {
127+
private final Pattern allowedValues;
128+
129+
public PgDurationConverter(String allowedValues) {
130+
// Remove the parentheses from the beginning and end.
131+
this.allowedValues =
132+
Pattern.compile(
133+
"(?is)\\A" + allowedValues.substring(1, allowedValues.length() - 1) + "\\z");
134+
}
135+
136+
@Override
137+
public Class<Duration> getParameterClass() {
138+
return Duration.class;
139+
}
140+
141+
@Override
142+
public Duration convert(String value) {
143+
Matcher matcher = allowedValues.matcher(value);
144+
if (matcher.find()) {
145+
Duration duration;
146+
if (matcher.group(0).equalsIgnoreCase("default")) {
147+
return Durations.fromNanos(0L);
148+
} else if (matcher.group(2) == null) {
149+
duration =
150+
ReadOnlyStalenessUtil.createDuration(
151+
Long.parseLong(matcher.group(0)), TimeUnit.MILLISECONDS);
152+
} else {
153+
duration =
154+
ReadOnlyStalenessUtil.createDuration(
155+
Long.parseLong(matcher.group(1)),
156+
ReadOnlyStalenessUtil.parseTimeUnit(matcher.group(2)));
157+
}
158+
if (duration.getSeconds() == 0L && duration.getNanos() == 0) {
159+
return null;
160+
}
161+
return duration;
162+
}
163+
return null;
164+
}
165+
}
166+
124167
/** Converter from string to possible values for read only staleness ({@link TimestampBound}). */
125168
static class ReadOnlyStalenessConverter
126169
implements ClientSideStatementValueConverter<TimestampBound> {
@@ -243,6 +286,33 @@ public TransactionMode convert(String value) {
243286
}
244287
}
245288

289+
/**
290+
* Converter for converting string values to {@link PgTransactionMode} values. Includes no-op
291+
* handling of setting the isolation level of the transaction to default or serializable.
292+
*/
293+
static class PgTransactionModeConverter
294+
implements ClientSideStatementValueConverter<PgTransactionMode> {
295+
private final CaseInsensitiveEnumMap<PgTransactionMode> values =
296+
new CaseInsensitiveEnumMap<>(
297+
PgTransactionMode.class, PgTransactionMode::getStatementString);
298+
299+
PgTransactionModeConverter() {}
300+
301+
public PgTransactionModeConverter(String allowedValues) {}
302+
303+
@Override
304+
public Class<PgTransactionMode> getParameterClass() {
305+
return PgTransactionMode.class;
306+
}
307+
308+
@Override
309+
public PgTransactionMode convert(String value) {
310+
// Transaction mode may contain multiple spaces.
311+
String valueWithSingleSpaces = value.replaceAll("\\s+", " ");
312+
return values.get(valueWithSingleSpaces);
313+
}
314+
}
315+
246316
/** Converter for converting strings to {@link RpcPriority} values. */
247317
static class RpcPriorityConverter implements ClientSideStatementValueConverter<Priority> {
248318
private final CaseInsensitiveEnumMap<Priority> values =

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.google.cloud.spanner.connection;
1818

19+
import com.google.cloud.spanner.Dialect;
20+
import com.google.cloud.spanner.ErrorCode;
21+
import com.google.cloud.spanner.SpannerExceptionFactory;
1922
import com.google.cloud.spanner.connection.ClientSideStatementImpl.CompileException;
2023
import com.google.gson.Gson;
2124
import java.io.InputStreamReader;
@@ -24,7 +27,21 @@
2427
/** This class reads and parses the {@link ClientSideStatement}s from the json file. */
2528
class ClientSideStatements {
2629
private static final String STATEMENTS_DEFINITION_FILE = "ClientSideStatements.json";
27-
static final ClientSideStatements INSTANCE = importStatements();
30+
private static final String PG_STATEMENTS_DEFINITION_FILE = "PG_ClientSideStatements.json";
31+
private static final ClientSideStatements INSTANCE = importStatements();
32+
private static final ClientSideStatements PG_INSTANCE = pgImportStatements();
33+
34+
static ClientSideStatements getInstance(Dialect dialect) {
35+
switch (dialect) {
36+
case GOOGLE_STANDARD_SQL:
37+
return INSTANCE;
38+
case POSTGRESQL:
39+
return PG_INSTANCE;
40+
default:
41+
throw SpannerExceptionFactory.newSpannerException(
42+
ErrorCode.INVALID_ARGUMENT, "Unknown or unsupported dialect: " + dialect);
43+
}
44+
}
2845

2946
/**
3047
* Reads statement definitions from ClientSideStatements.json and parses these as Java objects.
@@ -37,6 +54,18 @@ private static ClientSideStatements importStatements() {
3754
ClientSideStatements.class);
3855
}
3956

57+
/**
58+
* Reads statement definitions from PG_ClientSideStatements.json and parses these as Java objects.
59+
*/
60+
private static ClientSideStatements pgImportStatements() {
61+
Gson gson = new Gson();
62+
return gson.fromJson(
63+
new InputStreamReader(
64+
ClientSideStatements.class.getResourceAsStream(PG_STATEMENTS_DEFINITION_FILE)),
65+
ClientSideStatements.class);
66+
}
67+
68+
// This field is set automatically by the importStatements / pgImportStatements methods.
4069
private Set<ClientSideStatementImpl> statements;
4170

4271
private ClientSideStatements() {}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,19 @@ interface ConnectionStatementExecutor {
8585

8686
StatementResult statementBeginTransaction();
8787

88+
StatementResult statementBeginPgTransaction(PgTransactionMode transactionMode);
89+
8890
StatementResult statementCommit();
8991

9092
StatementResult statementRollback();
9193

9294
StatementResult statementSetTransactionMode(TransactionMode mode);
9395

96+
StatementResult statementSetPgTransactionMode(PgTransactionMode transactionMode);
97+
98+
StatementResult statementSetPgSessionCharacteristicsTransactionMode(
99+
PgTransactionMode transactionMode);
100+
94101
StatementResult statementStartBatchDdl();
95102

96103
StatementResult statementStartBatchDml();

0 commit comments

Comments
 (0)