From 9e770f281c03a1e9c034e5ff3ddee44fa20a7b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Mon, 9 Mar 2020 16:25:30 +0100 Subject: [PATCH] feat: add support for foreign keys (#78) Google Cloud Spanner now supports Foreign Key Constraints. These now also show up in the corresponding DatabaseMetaData methods. Fixes #77 --- .../DatabaseMetaData_GetCrossReferences.sql | 50 ++-- .../jdbc/DatabaseMetaData_GetExportedKeys.sql | 53 ++-- .../jdbc/DatabaseMetaData_GetImportedKeys.sql | 53 ++-- .../jdbc/it/ITJdbcDatabaseMetaDataTest.java | 275 +++++++++++++++++- .../spanner/jdbc/it/CreateMusicTables.sql | 14 +- 5 files changed, 383 insertions(+), 62 deletions(-) diff --git a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetCrossReferences.sql b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetCrossReferences.sql index 175edc4e..7d362ca5 100644 --- a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetCrossReferences.sql +++ b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetCrossReferences.sql @@ -14,21 +14,35 @@ * limitations under the License. */ -SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, PARENT.TABLE_NAME AS PKTABLE_NAME, - PARENT.COLUMN_NAME AS PKCOLUMN_NAME, CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, - CHILD.TABLE_NAME AS FKTABLE_NAME, CHILD.COLUMN_NAME AS FKCOLUMN_NAME, CHILD.ORDINAL_POSITION AS KEY_SEQ, 3 AS UPDATE_RULE, - CASE WHEN TABLES.ON_DELETE_ACTION='CASCADE' THEN 0 ELSE 3 END AS DELETE_RULE, NULL AS FK_NAME, 'PRIMARY_KEY' AS PK_NAME, - 7 AS DEFERRABILITY, TABLES.ON_DELETE_ACTION -FROM INFORMATION_SCHEMA.INDEX_COLUMNS PARENT -INNER JOIN INFORMATION_SCHEMA.INDEXES PARENT_INDEX ON PARENT.INDEX_NAME=PARENT_INDEX.INDEX_NAME AND PARENT.TABLE_NAME=PARENT_INDEX.TABLE_NAME AND PARENT.TABLE_SCHEMA=PARENT_INDEX.TABLE_SCHEMA AND PARENT.TABLE_CATALOG=PARENT_INDEX.TABLE_CATALOG AND PARENT_INDEX.INDEX_TYPE='PRIMARY_KEY' -INNER JOIN INFORMATION_SCHEMA.INDEX_COLUMNS CHILD ON NOT PARENT.TABLE_NAME=CHILD.TABLE_NAME AND PARENT.COLUMN_NAME=CHILD.COLUMN_NAME -INNER JOIN INFORMATION_SCHEMA.INDEXES CHILD_INDEX ON CHILD.INDEX_NAME=CHILD_INDEX.INDEX_NAME AND CHILD.TABLE_NAME=CHILD_INDEX.TABLE_NAME AND CHILD.TABLE_SCHEMA=CHILD_INDEX.TABLE_SCHEMA AND CHILD.TABLE_CATALOG=CHILD_INDEX.TABLE_CATALOG AND CHILD_INDEX.INDEX_TYPE='PRIMARY_KEY' -INNER JOIN INFORMATION_SCHEMA.TABLES ON CHILD.TABLE_CATALOG=TABLES.TABLE_CATALOG AND CHILD.TABLE_SCHEMA=TABLES.TABLE_SCHEMA AND CHILD.TABLE_NAME=TABLES.TABLE_NAME AND PARENT.TABLE_NAME=TABLES.PARENT_TABLE_NAME -WHERE PARENT_INDEX.INDEX_TYPE='PRIMARY_KEY' AND CHILD.ORDINAL_POSITION IS NOT NULL -AND UPPER(PARENT.TABLE_CATALOG) LIKE ? -AND UPPER(PARENT.TABLE_SCHEMA) LIKE ? -AND UPPER(PARENT.TABLE_NAME) LIKE ? -AND UPPER(CHILD.TABLE_CATALOG) LIKE ? -AND UPPER(CHILD.TABLE_SCHEMA) LIKE ? -AND UPPER(CHILD.TABLE_NAME) LIKE ? -ORDER BY CHILD.TABLE_CATALOG, CHILD.TABLE_SCHEMA, CHILD.TABLE_NAME, CHILD.ORDINAL_POSITION \ No newline at end of file +SELECT * +FROM ( + SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, PARENT.TABLE_NAME AS PKTABLE_NAME, + PARENT.COLUMN_NAME AS PKCOLUMN_NAME, CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, + CHILD.TABLE_NAME AS FKTABLE_NAME, CHILD.COLUMN_NAME AS FKCOLUMN_NAME, CHILD.ORDINAL_POSITION AS KEY_SEQ, 3 AS UPDATE_RULE, + CASE WHEN TABLES.ON_DELETE_ACTION='CASCADE' THEN 0 ELSE 3 END AS DELETE_RULE, NULL AS FK_NAME, 'PRIMARY_KEY' AS PK_NAME, + 7 AS DEFERRABILITY + FROM INFORMATION_SCHEMA.INDEX_COLUMNS PARENT + INNER JOIN INFORMATION_SCHEMA.INDEXES PARENT_INDEX ON PARENT.INDEX_NAME=PARENT_INDEX.INDEX_NAME AND PARENT.TABLE_NAME=PARENT_INDEX.TABLE_NAME AND PARENT.TABLE_SCHEMA=PARENT_INDEX.TABLE_SCHEMA AND PARENT.TABLE_CATALOG=PARENT_INDEX.TABLE_CATALOG AND PARENT_INDEX.INDEX_TYPE='PRIMARY_KEY' + INNER JOIN INFORMATION_SCHEMA.INDEX_COLUMNS CHILD ON NOT PARENT.TABLE_NAME=CHILD.TABLE_NAME AND PARENT.COLUMN_NAME=CHILD.COLUMN_NAME + INNER JOIN INFORMATION_SCHEMA.INDEXES CHILD_INDEX ON CHILD.INDEX_NAME=CHILD_INDEX.INDEX_NAME AND CHILD.TABLE_NAME=CHILD_INDEX.TABLE_NAME AND CHILD.TABLE_SCHEMA=CHILD_INDEX.TABLE_SCHEMA AND CHILD.TABLE_CATALOG=CHILD_INDEX.TABLE_CATALOG AND CHILD_INDEX.INDEX_TYPE='PRIMARY_KEY' + INNER JOIN INFORMATION_SCHEMA.TABLES ON CHILD.TABLE_CATALOG=TABLES.TABLE_CATALOG AND CHILD.TABLE_SCHEMA=TABLES.TABLE_SCHEMA AND CHILD.TABLE_NAME=TABLES.TABLE_NAME AND PARENT.TABLE_NAME=TABLES.PARENT_TABLE_NAME + WHERE PARENT_INDEX.INDEX_TYPE='PRIMARY_KEY' AND CHILD.ORDINAL_POSITION IS NOT NULL + + UNION ALL + + SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, PARENT.TABLE_NAME AS PKTABLE_NAME, + PARENT.COLUMN_NAME AS PKCOLUMN_NAME, CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, + CHILD.TABLE_NAME AS FKTABLE_NAME, CHILD.COLUMN_NAME AS FKCOLUMN_NAME, CHILD.ORDINAL_POSITION AS KEY_SEQ, 3 AS UPDATE_RULE, + 3 AS DELETE_RULE, CONSTRAINTS.CONSTRAINT_NAME AS FK_NAME, CONSTRAINTS.UNIQUE_CONSTRAINT_NAME AS PK_NAME, + 7 AS DEFERRABILITY + FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS CONSTRAINTS + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CHILD ON CONSTRAINTS.CONSTRAINT_CATALOG=CHILD.CONSTRAINT_CATALOG AND CONSTRAINTS.CONSTRAINT_SCHEMA= CHILD.CONSTRAINT_SCHEMA AND CONSTRAINTS.CONSTRAINT_NAME= CHILD.CONSTRAINT_NAME + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE PARENT ON CONSTRAINTS.UNIQUE_CONSTRAINT_CATALOG=PARENT.CONSTRAINT_CATALOG AND CONSTRAINTS.UNIQUE_CONSTRAINT_SCHEMA=PARENT.CONSTRAINT_SCHEMA AND CONSTRAINTS.UNIQUE_CONSTRAINT_NAME=PARENT.CONSTRAINT_NAME AND PARENT.ORDINAL_POSITION=CHILD.POSITION_IN_UNIQUE_CONSTRAINT +) AS CROSS_REF +WHERE UPPER(PKTABLE_CAT) LIKE ? +AND UPPER(PKTABLE_SCHEM) LIKE ? +AND UPPER(PKTABLE_NAME) LIKE ? +AND UPPER(FKTABLE_CAT) LIKE ? +AND UPPER(FKTABLE_SCHEM) LIKE ? +AND UPPER(FKTABLE_NAME) LIKE ? +ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, KEY_SEQ diff --git a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetExportedKeys.sql b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetExportedKeys.sql index d83bf67d..631886d7 100644 --- a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetExportedKeys.sql +++ b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetExportedKeys.sql @@ -14,21 +14,38 @@ * limitations under the License. */ -SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, - PARENT.TABLE_NAME AS PKTABLE_NAME, PARENT_INDEX_COLUMNS.COLUMN_NAME AS PKCOLUMN_NAME, - CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, - CHILD.TABLE_NAME AS FKTABLE_NAME, PARENT_INDEX_COLUMNS.COLUMN_NAME AS FKCOLUMN_NAME, - PARENT_INDEX_COLUMNS.ORDINAL_POSITION AS KEY_SEQ, - 1 AS UPDATE_RULE, -- 1 = importedKeyRestrict - CASE WHEN CHILD.ON_DELETE_ACTION='CASCADE' THEN 0 ELSE 1 END AS DELETE_RULE, -- 0 = cascade - NULL AS FK_NAME, 'PRIMARY_KEY' AS PK_NAME, - 7 AS DEFERRABILITY -- 7 = importedKeyNotDeferrable -FROM INFORMATION_SCHEMA.TABLES PARENT -INNER JOIN INFORMATION_SCHEMA.TABLES CHILD ON CHILD.PARENT_TABLE_NAME=PARENT.TABLE_NAME -INNER JOIN INFORMATION_SCHEMA.INDEX_COLUMNS PARENT_INDEX_COLUMNS ON - PARENT_INDEX_COLUMNS.TABLE_NAME=PARENT.TABLE_NAME - AND PARENT_INDEX_COLUMNS.INDEX_NAME='PRIMARY_KEY' -WHERE UPPER(PARENT.TABLE_CATALOG) LIKE ? - AND UPPER(PARENT.TABLE_SCHEMA) LIKE ? - AND UPPER(PARENT.TABLE_NAME) LIKE ? -ORDER BY CHILD.TABLE_CATALOG, CHILD.TABLE_SCHEMA, CHILD.TABLE_NAME, PARENT_INDEX_COLUMNS.ORDINAL_POSITION +SELECT * +FROM ( + SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, + PARENT.TABLE_NAME AS PKTABLE_NAME, PARENT_INDEX_COLUMNS.COLUMN_NAME AS PKCOLUMN_NAME, + CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, + CHILD.TABLE_NAME AS FKTABLE_NAME, PARENT_INDEX_COLUMNS.COLUMN_NAME AS FKCOLUMN_NAME, + PARENT_INDEX_COLUMNS.ORDINAL_POSITION AS KEY_SEQ, + 1 AS UPDATE_RULE, -- 1 = importedKeyRestrict + CASE WHEN CHILD.ON_DELETE_ACTION='CASCADE' THEN 0 ELSE 1 END AS DELETE_RULE, -- 0 = cascade + NULL AS FK_NAME, 'PRIMARY_KEY' AS PK_NAME, + 7 AS DEFERRABILITY -- 7 = importedKeyNotDeferrable + FROM INFORMATION_SCHEMA.TABLES PARENT + INNER JOIN INFORMATION_SCHEMA.TABLES CHILD ON CHILD.PARENT_TABLE_NAME=PARENT.TABLE_NAME + INNER JOIN INFORMATION_SCHEMA.INDEX_COLUMNS PARENT_INDEX_COLUMNS ON + PARENT_INDEX_COLUMNS.TABLE_NAME=PARENT.TABLE_NAME + AND PARENT_INDEX_COLUMNS.INDEX_NAME='PRIMARY_KEY' + + UNION ALL + + SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, PARENT.TABLE_NAME AS PKTABLE_NAME, + PARENT.COLUMN_NAME AS PKCOLUMN_NAME, CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, + CHILD.TABLE_NAME AS FKTABLE_NAME, CHILD.COLUMN_NAME AS FKCOLUMN_NAME, + CHILD.ORDINAL_POSITION AS KEY_SEQ, + 1 AS UPDATE_RULE, -- 1 = importedKeyRestrict + 1 AS DELETE_RULE, -- 1 = importedKeyRestrict + CONSTRAINTS.CONSTRAINT_NAME AS FK_NAME, CONSTRAINTS.UNIQUE_CONSTRAINT_NAME AS PK_NAME, + 7 AS DEFERRABILITY -- 7 = importedKeyNotDeferrable + FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS CONSTRAINTS + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CHILD ON CONSTRAINTS.CONSTRAINT_CATALOG=CHILD.CONSTRAINT_CATALOG AND CONSTRAINTS.CONSTRAINT_SCHEMA= CHILD.CONSTRAINT_SCHEMA AND CONSTRAINTS.CONSTRAINT_NAME= CHILD.CONSTRAINT_NAME + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE PARENT ON CONSTRAINTS.UNIQUE_CONSTRAINT_CATALOG=PARENT.CONSTRAINT_CATALOG AND CONSTRAINTS.UNIQUE_CONSTRAINT_SCHEMA=PARENT.CONSTRAINT_SCHEMA AND CONSTRAINTS.UNIQUE_CONSTRAINT_NAME=PARENT.CONSTRAINT_NAME AND PARENT.ORDINAL_POSITION=CHILD.POSITION_IN_UNIQUE_CONSTRAINT +) EXPORTED_KEYS +WHERE UPPER(PKTABLE_CAT) LIKE ? + AND UPPER(PKTABLE_SCHEM) LIKE ? + AND UPPER(PKTABLE_NAME) LIKE ? +ORDER BY FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, KEY_SEQ diff --git a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetImportedKeys.sql b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetImportedKeys.sql index 4e7dc7aa..50994a55 100644 --- a/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetImportedKeys.sql +++ b/src/main/resources/com/google/cloud/spanner/jdbc/DatabaseMetaData_GetImportedKeys.sql @@ -14,21 +14,38 @@ * limitations under the License. */ -SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, - PARENT.TABLE_NAME AS PKTABLE_NAME, COL.COLUMN_NAME AS PKCOLUMN_NAME, - CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, - CHILD.TABLE_NAME AS FKTABLE_NAME, COL.COLUMN_NAME FKCOLUMN_NAME, - COL.ORDINAL_POSITION AS KEY_SEQ, - 1 AS UPDATE_RULE, -- 1 = importedKeyRestrict - CASE WHEN CHILD.ON_DELETE_ACTION = 'CASCADE' THEN 0 ELSE 1 END AS DELETE_RULE, -- 0 = cascade - NULL AS FK_NAME, INDEXES.INDEX_NAME AS PK_NAME, - 7 AS DEFERRABILITY -- 7 = importedKeyNotDeferrable -FROM INFORMATION_SCHEMA.TABLES CHILD -INNER JOIN INFORMATION_SCHEMA.TABLES PARENT ON CHILD.TABLE_CATALOG=PARENT.TABLE_CATALOG AND CHILD.TABLE_SCHEMA=PARENT.TABLE_SCHEMA AND CHILD.PARENT_TABLE_NAME=PARENT.TABLE_NAME -INNER JOIN INFORMATION_SCHEMA.INDEXES ON PARENT.TABLE_CATALOG=INDEXES.TABLE_CATALOG AND PARENT.TABLE_SCHEMA=INDEXES.TABLE_SCHEMA AND PARENT.TABLE_NAME=INDEXES.TABLE_NAME AND INDEXES.INDEX_TYPE='PRIMARY_KEY' -INNER JOIN INFORMATION_SCHEMA.INDEX_COLUMNS COL ON INDEXES.TABLE_CATALOG=COL.TABLE_CATALOG AND INDEXES.TABLE_SCHEMA=COL.TABLE_SCHEMA AND INDEXES.TABLE_NAME=COL.TABLE_NAME AND INDEXES.INDEX_NAME=COL.INDEX_NAME -WHERE CHILD.PARENT_TABLE_NAME IS NOT NULL - AND UPPER(CHILD.TABLE_CATALOG) LIKE ? - AND UPPER(CHILD.TABLE_SCHEMA) LIKE ? - AND UPPER(CHILD.TABLE_NAME) LIKE ? -ORDER BY PARENT.TABLE_CATALOG, PARENT.TABLE_SCHEMA, PARENT.TABLE_NAME, COL.ORDINAL_POSITION \ No newline at end of file +SELECT * +FROM ( + SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, + PARENT.TABLE_NAME AS PKTABLE_NAME, PARENT_INDEX_COLUMNS.COLUMN_NAME AS PKCOLUMN_NAME, + CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, + CHILD.TABLE_NAME AS FKTABLE_NAME, PARENT_INDEX_COLUMNS.COLUMN_NAME AS FKCOLUMN_NAME, + PARENT_INDEX_COLUMNS.ORDINAL_POSITION AS KEY_SEQ, + 1 AS UPDATE_RULE, -- 1 = importedKeyRestrict + CASE WHEN CHILD.ON_DELETE_ACTION='CASCADE' THEN 0 ELSE 1 END AS DELETE_RULE, -- 0 = cascade + NULL AS FK_NAME, 'PRIMARY_KEY' AS PK_NAME, + 7 AS DEFERRABILITY -- 7 = importedKeyNotDeferrable + FROM INFORMATION_SCHEMA.TABLES PARENT + INNER JOIN INFORMATION_SCHEMA.TABLES CHILD ON CHILD.PARENT_TABLE_NAME=PARENT.TABLE_NAME + INNER JOIN INFORMATION_SCHEMA.INDEX_COLUMNS PARENT_INDEX_COLUMNS ON + PARENT_INDEX_COLUMNS.TABLE_NAME=PARENT.TABLE_NAME + AND PARENT_INDEX_COLUMNS.INDEX_NAME='PRIMARY_KEY' + + UNION ALL + + SELECT PARENT.TABLE_CATALOG AS PKTABLE_CAT, PARENT.TABLE_SCHEMA AS PKTABLE_SCHEM, PARENT.TABLE_NAME AS PKTABLE_NAME, + PARENT.COLUMN_NAME AS PKCOLUMN_NAME, CHILD.TABLE_CATALOG AS FKTABLE_CAT, CHILD.TABLE_SCHEMA AS FKTABLE_SCHEM, + CHILD.TABLE_NAME AS FKTABLE_NAME, CHILD.COLUMN_NAME AS FKCOLUMN_NAME, + CHILD.ORDINAL_POSITION AS KEY_SEQ, + 1 AS UPDATE_RULE, -- 1 = importedKeyRestrict + 1 AS DELETE_RULE, -- 1 = importedKeyRestrict + CONSTRAINTS.CONSTRAINT_NAME AS FK_NAME, CONSTRAINTS.UNIQUE_CONSTRAINT_NAME AS PK_NAME, + 7 AS DEFERRABILITY -- 7 = importedKeyNotDeferrable + FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS CONSTRAINTS + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CHILD ON CONSTRAINTS.CONSTRAINT_CATALOG=CHILD.CONSTRAINT_CATALOG AND CONSTRAINTS.CONSTRAINT_SCHEMA= CHILD.CONSTRAINT_SCHEMA AND CONSTRAINTS.CONSTRAINT_NAME= CHILD.CONSTRAINT_NAME + INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE PARENT ON CONSTRAINTS.UNIQUE_CONSTRAINT_CATALOG=PARENT.CONSTRAINT_CATALOG AND CONSTRAINTS.UNIQUE_CONSTRAINT_SCHEMA=PARENT.CONSTRAINT_SCHEMA AND CONSTRAINTS.UNIQUE_CONSTRAINT_NAME=PARENT.CONSTRAINT_NAME AND PARENT.ORDINAL_POSITION=CHILD.POSITION_IN_UNIQUE_CONSTRAINT +) IMPORTED_KEYS +WHERE UPPER(FKTABLE_CAT) LIKE ? + AND UPPER(FKTABLE_SCHEM) LIKE ? + AND UPPER(FKTABLE_NAME) LIKE ? +ORDER BY PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, KEY_SEQ diff --git a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java index a43f83c4..27d7a12f 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/it/ITJdbcDatabaseMetaDataTest.java @@ -18,6 +18,7 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -44,7 +45,9 @@ public class ITJdbcDatabaseMetaDataTest extends ITAbstractJdbcTest { private static final String SINGERS_TABLE = "Singers"; private static final String ALBUMS_TABLE = "Albums"; private static final String SONGS_TABLE = "Songs"; + private static final String CONCERTS_TABLE = "Concerts"; private static final String TABLE_WITH_ALL_COLS = "TableWithAllColumnTypes"; + private static final String TABLE_WITH_REF = "TableWithRef"; @Override protected boolean doCreateMusicTables() { @@ -276,6 +279,109 @@ public void testGetCrossReferences() throws SQLException { rs.getShort("DEFERRABILITY"), is(equalTo((short) DatabaseMetaData.importedKeyNotDeferrable))); } + + try (ResultSet rs = + connection + .getMetaData() + .getCrossReference( + DEFAULT_CATALOG, + DEFAULT_SCHEMA, + SINGERS_TABLE, + DEFAULT_CATALOG, + DEFAULT_SCHEMA, + CONCERTS_TABLE)) { + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo("Singers"))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo("Concerts"))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 1))); + assertThat( + rs.getShort("UPDATE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat( + rs.getShort("DELETE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat(rs.getString("FK_NAME"), is(equalTo("Fk_Concerts_Singer"))); + assertThat(rs.getString("PK_NAME"), is(equalTo("PK_Singers"))); + assertThat( + rs.getShort("DEFERRABILITY"), + is(equalTo((short) DatabaseMetaData.importedKeyNotDeferrable))); + assertThat(rs.next(), is(false)); + } + + try (ResultSet rs = + connection + .getMetaData() + .getCrossReference( + DEFAULT_CATALOG, + DEFAULT_SCHEMA, + TABLE_WITH_ALL_COLS, + DEFAULT_CATALOG, + DEFAULT_SCHEMA, + TABLE_WITH_REF)) { + + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(TABLE_WITH_ALL_COLS))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("ColFloat64"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(TABLE_WITH_REF))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("RefFloat"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 1))); + assertThat( + rs.getShort("UPDATE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat( + rs.getShort("DELETE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat(rs.getString("FK_NAME"), is(equalTo("Fk_TableWithRef_TableWithAllColumnTypes"))); + assertThat( + rs.getShort("DEFERRABILITY"), + is(equalTo((short) DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(TABLE_WITH_ALL_COLS))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("ColString"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(TABLE_WITH_REF))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("RefString"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 2))); + assertThat( + rs.getShort("UPDATE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat( + rs.getShort("DELETE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat(rs.getString("FK_NAME"), is(equalTo("Fk_TableWithRef_TableWithAllColumnTypes"))); + assertThat( + rs.getShort("DEFERRABILITY"), + is(equalTo((short) DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(TABLE_WITH_ALL_COLS))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("ColDate"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(""))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(TABLE_WITH_REF))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("RefDate"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 3))); + assertThat( + rs.getShort("UPDATE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat( + rs.getShort("DELETE_RULE"), is(equalTo((short) DatabaseMetaData.importedKeyNoAction))); + assertThat(rs.getString("FK_NAME"), is(equalTo("Fk_TableWithRef_TableWithAllColumnTypes"))); + assertThat( + rs.getShort("DEFERRABILITY"), + is(equalTo((short) DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(false)); + } // try getting self-references try (ResultSet rs = connection @@ -292,7 +398,7 @@ public void testGetCrossReferences() throws SQLException { // try getting all cross-references in the database try (ResultSet rs = connection.getMetaData().getCrossReference(null, null, null, null, null, null)) { - for (int i = 0; i < 3; i++) { + for (int i = 0; i < 7; i++) { assertThat(rs.next(), is(true)); } assertThat(rs.next(), is(false)); @@ -334,6 +440,13 @@ private IndexInfo( new IndexInfo("Concerts", false, "PRIMARY_KEY", 1, "VenueId", "A"), new IndexInfo("Concerts", false, "PRIMARY_KEY", 2, "SingerId", "A"), new IndexInfo("Concerts", false, "PRIMARY_KEY", 3, "ConcertDate", "A"), + new IndexInfo( + "Concerts", + true, + "FOREIGN_KEY", + 1, + "SingerId", + "A"), // Index automatically added by Cloud Spanner for the referencing column. new IndexInfo("Singers", false, "PRIMARY_KEY", 1, "SingerId", "A"), new IndexInfo("Singers", true, "SingersByFirstLastName", 1, "FirstName", "A"), new IndexInfo("Singers", true, "SingersByFirstLastName", 2, "LastName", "A"), @@ -344,7 +457,20 @@ private IndexInfo( new IndexInfo("Songs", false, "SongsBySingerAlbumSongNameDesc", 2, "AlbumId", "A"), new IndexInfo("Songs", false, "SongsBySingerAlbumSongNameDesc", 3, "SongName", "D"), new IndexInfo("Songs", true, "SongsBySongName", 1, "SongName", "A"), - new IndexInfo("TableWithAllColumnTypes", false, "PRIMARY_KEY", 1, "ColInt64", "A")); + new IndexInfo( + "TableWithAllColumnTypes", + false, + "GENERATED", + 1, + "ColFloat64", + "A"), // Index automatically added by Cloud Spanner for the referencing foreign key. + new IndexInfo("TableWithAllColumnTypes", false, "GENERATED", 2, "ColString", "A"), + new IndexInfo("TableWithAllColumnTypes", false, "GENERATED", 3, "ColDate", "A"), + new IndexInfo("TableWithAllColumnTypes", false, "PRIMARY_KEY", 1, "ColInt64", "A"), + new IndexInfo("TableWithRef", false, "PRIMARY_KEY", 1, "Id", "A"), + new IndexInfo("TableWithRef", true, "FOREIGN_KEY", 1, "RefFloat", "A"), + new IndexInfo("TableWithRef", true, "FOREIGN_KEY", 2, "RefString", "A"), + new IndexInfo("TableWithRef", true, "FOREIGN_KEY", 3, "RefDate", "A")); @Test public void testGetIndexInfo() throws SQLException { @@ -361,7 +487,10 @@ public void testGetIndexInfo() throws SQLException { assertThat(rs.getString("TABLE_NAME"), is(equalTo(index.tableName))); assertThat(rs.getBoolean("NON_UNIQUE"), is(index.nonUnique)); assertThat(rs.getString("INDEX_QUALIFIER"), is(equalTo(DEFAULT_CATALOG))); - assertThat(rs.getString("INDEX_NAME"), is(equalTo(index.indexName))); + // Foreign key index names are automatically generated. + if (!"FOREIGN_KEY".equals(index.indexName) && !"GENERATED".equals(index.indexName)) { + assertThat(rs.getString("INDEX_NAME"), is(equalTo(index.indexName))); + } if (index.indexName.equals("PRIMARY_KEY")) { assertThat(rs.getShort("TYPE"), is(equalTo(DatabaseMetaData.tableIndexClustered))); } else { @@ -390,7 +519,7 @@ public void testGetExportedKeys() throws SQLException { connection .getMetaData() .getExportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, SINGERS_TABLE)) { - assertKeysSingersAlbums(rs); + assertExportedKeysSingers(rs); } try (ResultSet rs = connection.getMetaData().getExportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, ALBUMS_TABLE)) { @@ -402,18 +531,92 @@ public void testGetExportedKeys() throws SQLException { @Test public void testGetImportedKeys() throws SQLException { try (Connection connection = createConnection()) { + try (ResultSet rs = + connection + .getMetaData() + .getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, SINGERS_TABLE)) { + assertImportedKeysSingers(rs); + } try (ResultSet rs = connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, ALBUMS_TABLE)) { - assertKeysSingersAlbums(rs); + assertImportedKeysAlbums(rs); + } + try (ResultSet rs = + connection + .getMetaData() + .getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, CONCERTS_TABLE)) { + assertImportedKeysConcerts(rs); } try (ResultSet rs = connection.getMetaData().getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, SONGS_TABLE)) { assertKeysAlbumsSongs(rs); } + try (ResultSet rs = + connection + .getMetaData() + .getImportedKeys(DEFAULT_CATALOG, DEFAULT_SCHEMA, TABLE_WITH_REF)) { + assertImportedKeysTableWithRef(rs); + } } } - private void assertKeysSingersAlbums(ResultSet rs) throws SQLException { + private void assertImportedKeysSingers(ResultSet rs) throws SQLException { + assertThat(rs.next(), is(false)); + } + + private void assertImportedKeysTableWithRef(ResultSet rs) throws SQLException { + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(TABLE_WITH_ALL_COLS))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("ColFloat64"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(TABLE_WITH_REF))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("RefFloat"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 1))); + assertThat(rs.getInt("UPDATE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getInt("DELETE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getString("FK_NAME"), is("Fk_TableWithRef_TableWithAllColumnTypes")); + assertThat(rs.getString("PK_NAME"), is(notNullValue())); // Index name is generated. + assertThat(rs.getInt("DEFERRABILITY"), is(equalTo(DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(TABLE_WITH_ALL_COLS))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("ColString"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(TABLE_WITH_REF))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("RefString"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 2))); + assertThat(rs.getInt("UPDATE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getInt("DELETE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getString("FK_NAME"), is("Fk_TableWithRef_TableWithAllColumnTypes")); + assertThat(rs.getString("PK_NAME"), is(notNullValue())); // Index name is generated. + assertThat(rs.getInt("DEFERRABILITY"), is(equalTo(DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(TABLE_WITH_ALL_COLS))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("ColDate"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(TABLE_WITH_REF))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("RefDate"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 3))); + assertThat(rs.getInt("UPDATE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getInt("DELETE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getString("FK_NAME"), is("Fk_TableWithRef_TableWithAllColumnTypes")); + assertThat(rs.getString("PK_NAME"), is(notNullValue())); // Index name is generated. + assertThat(rs.getInt("DEFERRABILITY"), is(equalTo(DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(false)); + } + + private void assertImportedKeysAlbums(ResultSet rs) throws SQLException { assertThat(rs.next(), is(true)); assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); @@ -429,6 +632,63 @@ private void assertKeysSingersAlbums(ResultSet rs) throws SQLException { assertThat(rs.getString("FK_NAME"), is(nullValue())); assertThat(rs.getString("PK_NAME"), is(equalTo("PRIMARY_KEY"))); assertThat(rs.getInt("DEFERRABILITY"), is(equalTo(DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(false)); + } + + private void assertImportedKeysConcerts(ResultSet rs) throws SQLException { + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(SINGERS_TABLE))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(CONCERTS_TABLE))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 1))); + assertThat(rs.getInt("UPDATE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getInt("DELETE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getString("FK_NAME"), is("Fk_Concerts_Singer")); + assertThat(rs.getString("PK_NAME"), is(equalTo("PK_Singers"))); + assertThat(rs.getInt("DEFERRABILITY"), is(equalTo(DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(false)); + } + + private void assertExportedKeysSingers(ResultSet rs) throws SQLException { + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(SINGERS_TABLE))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(ALBUMS_TABLE))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 1))); + assertThat(rs.getInt("UPDATE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getInt("DELETE_RULE"), is(equalTo(DatabaseMetaData.importedKeyCascade))); + assertThat(rs.getString("FK_NAME"), is(nullValue())); + assertThat(rs.getString("PK_NAME"), is(equalTo("PRIMARY_KEY"))); + assertThat(rs.getInt("DEFERRABILITY"), is(equalTo(DatabaseMetaData.importedKeyNotDeferrable))); + + assertThat(rs.next(), is(true)); + assertThat(rs.getString("PKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("PKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("PKTABLE_NAME"), is(equalTo(SINGERS_TABLE))); + assertThat(rs.getString("PKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getString("FKTABLE_CAT"), is(equalTo(DEFAULT_CATALOG))); + assertThat(rs.getString("FKTABLE_SCHEM"), is(equalTo(DEFAULT_SCHEMA))); + assertThat(rs.getString("FKTABLE_NAME"), is(equalTo(CONCERTS_TABLE))); + assertThat(rs.getString("FKCOLUMN_NAME"), is(equalTo("SingerId"))); + assertThat(rs.getShort("KEY_SEQ"), is(equalTo((short) 1))); + assertThat(rs.getInt("UPDATE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getInt("DELETE_RULE"), is(equalTo(DatabaseMetaData.importedKeyRestrict))); + assertThat(rs.getString("FK_NAME"), is("Fk_Concerts_Singer")); + assertThat(rs.getString("PK_NAME"), is(equalTo("PK_Singers"))); + assertThat(rs.getInt("DEFERRABILITY"), is(equalTo(DatabaseMetaData.importedKeyNotDeferrable))); + assertThat(rs.next(), is(false)); } @@ -533,7 +793,8 @@ private Table(String name) { new Table("Concerts"), new Table("Singers"), new Table("Songs"), - new Table("TableWithAllColumnTypes")); + new Table("TableWithAllColumnTypes"), + new Table("TableWithRef")); @Test public void testGetTables() throws SQLException { diff --git a/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql b/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql index 896b8b9c..5bbb90f6 100644 --- a/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql +++ b/src/test/resources/com/google/cloud/spanner/jdbc/it/CreateMusicTables.sql @@ -58,7 +58,8 @@ CREATE TABLE Concerts ( ConcertDate DATE NOT NULL, BeginTime TIMESTAMP, EndTime TIMESTAMP, - TicketPrices ARRAY + TicketPrices ARRAY, + CONSTRAINT Fk_Concerts_Singer FOREIGN KEY (SingerId) REFERENCES Singers (SingerId) ) PRIMARY KEY(VenueId, SingerId, ConcertDate); CREATE TABLE TableWithAllColumnTypes ( @@ -85,4 +86,15 @@ CREATE TABLE TableWithAllColumnTypes ( ) PRIMARY KEY (ColInt64) ; +CREATE TABLE TableWithRef ( + Id INT64 NOT NULL, + RefFloat FLOAT64 NOT NULL, + RefString STRING(100) NOT NULL, + RefDate DATE NOT NULL, + CONSTRAINT Fk_TableWithRef_TableWithAllColumnTypes + FOREIGN KEY (RefFloat, RefString, RefDate) + REFERENCES TableWithAllColumnTypes (ColFloat64, ColString, ColDate) +) PRIMARY KEY (Id) +; + RUN BATCH; \ No newline at end of file