Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@
import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;

/**
* {@link UnitOfWork} that is used when a DDL batch is started. These batches only accept DDL
Expand Down Expand Up @@ -188,10 +191,16 @@ public UpdateDatabaseDdlMetadata call() throws Exception {
operation.get();
// Return metadata.
return operation.getMetadata().get();
} catch (Exception e) {
} catch (ExecutionException e) {
SpannerException spannerException = extractSpannerCause(e);
long[] updateCounts = extractUpdateCounts(operation.getMetadata().get());
throw SpannerExceptionFactory.newSpannerBatchUpdateException(
ErrorCode.INVALID_ARGUMENT, e.getMessage(), updateCounts);
spannerException == null ? ErrorCode.UNKNOWN : spannerException.getErrorCode(),
e.getMessage(), updateCounts);
} catch (InterruptedException e) {
long[] updateCounts = extractUpdateCounts(operation.getMetadata().get());
throw SpannerExceptionFactory.newSpannerBatchUpdateException(
ErrorCode.CANCELLED, e.getMessage(), updateCounts);
}
}
};
Expand All @@ -207,6 +216,19 @@ public UpdateDatabaseDdlMetadata call() throws Exception {
}
}

private SpannerException extractSpannerCause(ExecutionException e) {
Throwable cause = e.getCause();
Set<Throwable> causes = new HashSet<>();
while (cause != null && !causes.contains(cause)) {
if (cause instanceof SpannerException) {
return (SpannerException) cause;
}
causes.add(cause);
cause = cause.getCause();
}
return null;
}

@VisibleForTesting
long[] extractUpdateCounts(UpdateDatabaseDdlMetadata metadata) {
long[] updateCounts = new long[metadata.getStatementsCount()];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ protected void verifyExpectedException(String statement, Exception e, String cod
String messagePrefix) {
assertThat(e instanceof SpannerException, is(true));
SpannerException spannerException = (SpannerException) e;
assertThat(statement, spannerException.getErrorCode(), is(equalTo(ErrorCode.valueOf(code))));
assertThat(statement + " resulted in " + spannerException.toString(), spannerException.getErrorCode(), is(equalTo(ErrorCode.valueOf(code))));
if (messagePrefix != null) {
assertThat(statement, e.getMessage(),
startsWith(messagePrefix.substring(1, messagePrefix.length() - 1)));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.spanner.jdbc.it;

import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.google.cloud.spanner.IntegrationTest;
import com.google.cloud.spanner.jdbc.ITAbstractSpannerTest;
import com.google.cloud.spanner.jdbc.SqlScriptVerifier;

/** Execute DDL statements using the generic connection API. */
@Category(IntegrationTest.class)
@RunWith(JUnit4.class)
public class ITDdlTest extends ITAbstractSpannerTest {

@Test
public void testSqlScript() throws Exception {
SqlScriptVerifier verifier = new SqlScriptVerifier(new ITConnectionProvider());
verifier.verifyStatementsInFile("ITDdlTest.sql", SqlScriptVerifier.class, true);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.spanner.jdbc.it;

import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.google.cloud.spanner.IntegrationTest;
import com.google.cloud.spanner.jdbc.ITAbstractJdbcTest;
import com.google.cloud.spanner.jdbc.JdbcSqlScriptVerifier;
import com.google.cloud.spanner.jdbc.SqlScriptVerifier;

/** Execute DDL statements using JDBC. */
@Category(IntegrationTest.class)
@RunWith(JUnit4.class)
public class ITJdbcDdlTest extends ITAbstractJdbcTest {

@Test
public void testSqlScript() throws Exception {
JdbcSqlScriptVerifier verifier = new JdbcSqlScriptVerifier(new ITJdbcConnectionProvider());
verifier.verifyStatementsInFile("ITDdlTest.sql", SqlScriptVerifier.class, true);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
* Copyright 2019 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

NEW_CONNECTION;
-- Create table in autocommit mode

@EXPECT RESULT_SET 'AUTOCOMMIT',true
SHOW VARIABLE AUTOCOMMIT;
@EXPECT RESULT_SET 'READONLY',false
SHOW VARIABLE READONLY;

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='VALID_DDL_AUTOCOMMIT';

CREATE TABLE VALID_DDL_AUTOCOMMIT (ID INT64 NOT NULL, BAR STRING(100)) PRIMARY KEY (ID);

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 1 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='VALID_DDL_AUTOCOMMIT';


NEW_CONNECTION;
-- Try to create a table with an invalid SQL statement

@EXPECT RESULT_SET 'AUTOCOMMIT',true
SHOW VARIABLE AUTOCOMMIT;
@EXPECT RESULT_SET 'READONLY',false
SHOW VARIABLE READONLY;

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='INVALID_DDL_AUTOCOMMIT';

@EXPECT EXCEPTION INVALID_ARGUMENT
CREATE TABLE INVALID_DDL_AUTOCOMMIT (ID INT64 NOT NULL, BAZ STRING(100), MISSING_DATA_TYPE_COL) PRIMARY KEY (ID);

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='INVALID_DDL_AUTOCOMMIT';


NEW_CONNECTION;
-- Try to create a new table in a DDL_BATCH

-- Check that the table is not present
@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='VALID_SINGLE_DDL_IN_DDL_BATCH';

-- Change to DDL batch mode
SET AUTOCOMMIT = FALSE;
START BATCH DDL;

-- Execute the create table statement, but do not commit yet
CREATE TABLE VALID_SINGLE_DDL_IN_DDL_BATCH (ID INT64 NOT NULL, BAR STRING(100)) PRIMARY KEY (ID);

NEW_CONNECTION;
-- Transaction has not been committed, so the table should not be present
-- We do this in a new transaction, as selects are not allowed in a DDL_BATCH
@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='VALID_SINGLE_DDL_IN_DDL_BATCH';

-- Change to DDL batch mode again
SET AUTOCOMMIT = FALSE;
START BATCH DDL;

-- Execute the create table statement and do a commit
CREATE TABLE VALID_SINGLE_DDL_IN_DDL_BATCH (ID INT64 NOT NULL, BAR STRING(100)) PRIMARY KEY (ID);
RUN BATCH;

-- Go back to AUTOCOMMIT mode and check that the table was created
SET AUTOCOMMIT = TRUE;

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 1 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='VALID_SINGLE_DDL_IN_DDL_BATCH';


NEW_CONNECTION;
-- Create two tables in one batch

-- First ensure that the tables do not exist
@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='VALID_MULTIPLE_DDL_IN_DDL_BATCH_1' OR TABLE_NAME='VALID_MULTIPLE_DDL_IN_DDL_BATCH_2';

-- Change to DDL batch mode
SET AUTOCOMMIT = FALSE;
START BATCH DDL;

-- Create two tables
CREATE TABLE VALID_MULTIPLE_DDL_IN_DDL_BATCH_1 (ID INT64 NOT NULL, BAR STRING(100)) PRIMARY KEY (ID);
CREATE TABLE VALID_MULTIPLE_DDL_IN_DDL_BATCH_2 (ID INT64 NOT NULL, BAR STRING(100)) PRIMARY KEY (ID);
-- Run the batch
RUN BATCH;

-- Switch to autocommit and verify that both tables exist
SET AUTOCOMMIT = TRUE;

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 2 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='VALID_MULTIPLE_DDL_IN_DDL_BATCH_1' OR TABLE_NAME='VALID_MULTIPLE_DDL_IN_DDL_BATCH_2';


NEW_CONNECTION;
/*
* Do a test that shows that a DDL batch might only execute some of the statements,
* for example if data in a table prevents a unique index from being created.
*/
SET AUTOCOMMIT = FALSE;
START BATCH DDL;

CREATE TABLE TEST1 (ID INT64 NOT NULL, NAME STRING(100)) PRIMARY KEY (ID);
CREATE TABLE TEST2 (ID INT64 NOT NULL, NAME STRING(100)) PRIMARY KEY (ID);
RUN BATCH;

SET AUTOCOMMIT = TRUE;

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 2 AS EXPECTED
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME='TEST1' OR TABLE_NAME='TEST2';

-- Fill the second table with some data that will prevent us from creating a unique index on
-- the name column.
INSERT INTO TEST2 (ID, NAME) VALUES (1, 'TEST');
INSERT INTO TEST2 (ID, NAME) VALUES (2, 'TEST');

-- Ensure the indices that we are to create do not exist
@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.INDEXES
WHERE (TABLE_NAME='TEST1' AND INDEX_NAME='IDX_TEST1')
OR (TABLE_NAME='TEST2' AND INDEX_NAME='IDX_TEST2');

-- Try to create two unique indices in one batch
SET AUTOCOMMIT = FALSE;
START BATCH DDL;

CREATE UNIQUE INDEX IDX_TEST1 ON TEST1 (NAME);
CREATE UNIQUE INDEX IDX_TEST2 ON TEST2 (NAME);

@EXPECT EXCEPTION FAILED_PRECONDITION
RUN BATCH;

SET AUTOCOMMIT = TRUE;

-- Ensure that IDX_TEST1 was created and IDX_TEST2 was not.
@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 1 AS EXPECTED
FROM INFORMATION_SCHEMA.INDEXES
WHERE TABLE_NAME='TEST1' AND INDEX_NAME='IDX_TEST1';

@EXPECT RESULT_SET
SELECT COUNT(*) AS ACTUAL, 0 AS EXPECTED
FROM INFORMATION_SCHEMA.INDEXES
WHERE TABLE_NAME='TEST2' AND INDEX_NAME='IDX_TEST2';

NEW_CONNECTION;
/* Verify that empty DDL batches are accepted. */
START BATCH DDL;
RUN BATCH;

START BATCH DDL;
ABORT BATCH;