File tree

2 files changed

+71
-6
lines changed

2 files changed

+71
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import java.util.Set;
4343
import java.util.regex.Matcher;
4444
import java.util.regex.Pattern;
45+
import javax.annotation.Nullable;
4546

4647
/**
4748
* Internal connection API for Google Cloud Spanner. This class may introduce breaking changes
@@ -152,6 +153,7 @@ public String[] getValidValues() {
152153
private static final String DEFAULT_NUM_CHANNELS = null;
153154
private static final String DEFAULT_USER_AGENT = null;
154155
private static final String DEFAULT_OPTIMIZER_VERSION = "";
156+
private static final boolean DEFAULT_LENIENT = false;
155157

156158
private static final String PLAIN_TEXT_PROTOCOL = "http:";
157159
private static final String HOST_PROTOCOL = "https:";
@@ -176,6 +178,8 @@ public String[] getValidValues() {
176178
private static final String USER_AGENT_PROPERTY_NAME = "userAgent";
177179
/** Query optimizer version to use for a connection. */
178180
private static final String OPTIMIZER_VERSION_PROPERTY_NAME = "optimizerVersion";
181+
/** Name of the 'lenientMode' connection property. */
182+
public static final String LENIENT_PROPERTY_NAME = "lenient";
179183

180184
/** All valid connection properties. */
181185
public static final Set<ConnectionProperty> VALID_PROPERTIES =
@@ -212,7 +216,11 @@ public String[] getValidValues() {
212216
"The custom user-agent property name to use when communicating with Cloud Spanner. This property is intended for internal library usage, and should not be set by applications."),
213217
ConnectionProperty.createStringProperty(
214218
OPTIMIZER_VERSION_PROPERTY_NAME,
215-
"Sets the default query optimizer version to use for this connection."))));
219+
"Sets the default query optimizer version to use for this connection."),
220+
ConnectionProperty.createBooleanProperty(
221+
LENIENT_PROPERTY_NAME,
222+
"Silently ignore unknown properties in the connection string/properties (true/false)",
223+
DEFAULT_LENIENT))));
216224

217225
private static final Set<ConnectionProperty> INTERNAL_PROPERTIES =
218226
Collections.unmodifiableSet(
@@ -416,6 +424,7 @@ public static Builder newBuilder() {
416424
}
417425

418426
private final String uri;
427+
private final String warnings;
419428
private final String credentialsUrl;
420429
private final String oauthToken;
421430
private final Credentials fixedCredentials;
@@ -441,7 +450,7 @@ private ConnectionOptions(Builder builder) {
441450
Matcher matcher = Builder.SPANNER_URI_PATTERN.matcher(builder.uri);
442451
Preconditions.checkArgument(
443452
matcher.find(), String.format("Invalid connection URI specified: %s", builder.uri));
444-
checkValidProperties(builder.uri);
453+
this.warnings = checkValidProperties(builder.uri);
445454

446455
this.uri = builder.uri;
447456
this.sessionPoolOptions = builder.sessionPoolOptions;
@@ -574,6 +583,12 @@ static String parseOptimizerVersion(String uri) {
574583
return value != null ? value : DEFAULT_OPTIMIZER_VERSION;
575584
}
576585

586+
@VisibleForTesting
587+
static boolean parseLenient(String uri) {
588+
String value = parseUriProperty(uri, LENIENT_PROPERTY_NAME);
589+
return value != null ? Boolean.valueOf(value) : DEFAULT_LENIENT;
590+
}
591+
577592
@VisibleForTesting
578593
static String parseUriProperty(String uri, String property) {
579594
Pattern pattern = Pattern.compile(String.format("(?is)(?:;|\\?)%s=(.*?)(?:;|$)", property));
@@ -586,9 +601,10 @@ static String parseUriProperty(String uri, String property) {
586601

587602
/** Check that only valid properties have been specified. */
588603
@VisibleForTesting
589-
static void checkValidProperties(String uri) {
604+
static String checkValidProperties(String uri) {
590605
String invalidProperties = "";
591606
List<String> properties = parseProperties(uri);
607+
boolean lenient = parseLenient(uri);
592608
for (String property : properties) {
593609
if (!INTERNAL_VALID_PROPERTIES.contains(ConnectionProperty.createEmptyProperty(property))) {
594610
if (invalidProperties.length() > 0) {
@@ -597,9 +613,17 @@ static void checkValidProperties(String uri) {
597613
invalidProperties = invalidProperties + property;
598614
}
599615
}
600-
Preconditions.checkArgument(
601-
invalidProperties.isEmpty(),
602-
"Invalid properties found in connection URI: " + invalidProperties.toString());
616+
if (lenient) {
617+
return String.format(
618+
"Invalid properties found in connection URI: %s", invalidProperties.toString());
619+
} else {
620+
Preconditions.checkArgument(
621+
invalidProperties.isEmpty(),
622+
String.format(
623+
"Invalid properties found in connection URI. Add lenient=true to the connection string to ignore unknown properties. Invalid properties: %s",
624+
invalidProperties.toString()));
625+
return null;
626+
}
603627
}
604628

605629
@VisibleForTesting
@@ -706,6 +730,12 @@ public boolean isRetryAbortsInternally() {
706730
return retryAbortsInternally;
707731
}
708732

733+
/** Any warnings that were generated while creating the {@link ConnectionOptions} instance. */
734+
@Nullable
735+
public String getWarnings() {
736+
return warnings;
737+
}
738+
709739
/** Use http instead of https. Only valid for (local) test servers. */
710740
boolean isUsePlainText() {
711741
return usePlainText;
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,39 @@ public void testSetOAuthTokenAndCredentials() {
386386
assertThat(e.getMessage()).contains("Cannot specify both credentials and an OAuth token");
387387
}
388388
}
389+
390+
@Test
391+
public void testLenient() {
392+
ConnectionOptions options =
393+
ConnectionOptions.newBuilder()
394+
.setUri(
395+
"cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database?lenient=true;foo=bar")
396+
.setCredentialsUrl(FILE_TEST_PATH)
397+
.build();
398+
assertThat(options.getWarnings()).isNotNull();
399+
assertThat(options.getWarnings()).contains("foo");
400+
assertThat(options.getWarnings()).doesNotContain("lenient");
401+
402+
options =
403+
ConnectionOptions.newBuilder()
404+
.setUri(
405+
"cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database?bar=foo;lenient=true")
406+
.setCredentialsUrl(FILE_TEST_PATH)
407+
.build();
408+
assertThat(options.getWarnings()).isNotNull();
409+
assertThat(options.getWarnings()).contains("bar");
410+
assertThat(options.getWarnings()).doesNotContain("lenient");
411+
412+
try {
413+
options =
414+
ConnectionOptions.newBuilder()
415+
.setUri(
416+
"cloudspanner:/projects/test-project-123/instances/test-instance/databases/test-database?bar=foo")
417+
.setCredentialsUrl(FILE_TEST_PATH)
418+
.build();
419+
fail("missing expected exception");
420+
} catch (IllegalArgumentException e) {
421+
assertThat(e.getMessage()).contains("bar");
422+
}
423+
}
389424
}

0 commit comments

Comments
 (0)