Add Cloud Spanner dialect support
Example events-log.config update:
[events-log]
storeDriver = com.google.cloud.spanner.jdbc.JdbcDriver
storeUrl = jdbc:cloudspanner:/projects/project-name/instances/instance-name/databases/db-name
As the Spanner jdbc driver does not allow allowMultiQueries it
was necessary to add a Spanner-specific case in index creation.
Spanner does not support AUTOINCREMENT and recommends against
implementing it as it creates hotspots, so we use GENERATE_UUID()
for primary key creation:
https://cloud.google.com/spanner/docs/schema-and-data-model#choosing_a_primary_key
Change-Id: I7c9ead0ff59af681c13d753f13a19e36cc44dd0c
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java
index b554ecd..0949935 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java
@@ -66,7 +66,14 @@
*/
void createDBIfNotCreated() throws SQLException {
execute(SQLTable.createTableQuery(databaseDialect));
- execute(SQLTable.createIndexes(databaseDialect));
+ switch (databaseDialect) {
+ case SPANNER:
+ execute(SQLTable.createSpannerDateIndex());
+ execute(SQLTable.createSpannerProjectIndex());
+ break;
+ default:
+ execute(SQLTable.createIndexes(databaseDialect));
+ }
}
/**
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialect.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialect.java
index 10df147..c6eade5 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialect.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialect.java
@@ -18,7 +18,8 @@
public enum SQLDialect {
H2,
MYSQL,
- POSTGRESQL;
+ POSTGRESQL,
+ SPANNER;
/**
* This attempts to determine the SQL dialect from the JDBC URL. If the URL does not match one of
@@ -32,6 +33,8 @@
return POSTGRESQL;
} else if (jdbcUrl.contains("mysql")) {
return MYSQL;
+ } else if (jdbcUrl.contains("cloudspanner")) {
+ return SPANNER;
}
return H2;
}
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLTable.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLTable.java
index 6093a38..2e9378f 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLTable.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLTable.java
@@ -59,6 +59,13 @@
+ "END IF;\n"
+ "END$$;";
+ /**
+ * This is the Spanner idempotent index-creation query format. Inputs, in order: index-name,
+ * table-name, index-column
+ */
+ private static final String SPANNER_INDEX_CREATION_FORMAT =
+ "CREATE INDEX IF NOT EXISTS %s ON %s (%s)";
+
private SQLTable() {}
static String createTableQuery(SQLDialect databaseDialect) {
@@ -68,14 +75,26 @@
case POSTGRESQL:
query.append(format("%s SERIAL PRIMARY KEY,", PRIMARY_ENTRY));
break;
+ case SPANNER:
+ query.append(format("%s STRING(36) DEFAULT (GENERATE_UUID()), ", PRIMARY_ENTRY));
+ break;
case MYSQL:
case H2:
default:
query.append(format("%s INT AUTO_INCREMENT PRIMARY KEY,", PRIMARY_ENTRY));
}
- query.append(format("%s VARCHAR(255),", PROJECT_ENTRY));
- query.append(format("%s TIMESTAMP DEFAULT NOW(),", DATE_ENTRY));
- query.append(format("%s TEXT)", EVENT_ENTRY));
+ switch (databaseDialect) {
+ case SPANNER:
+ query.append(format("%s STRING(255),", PROJECT_ENTRY));
+ query.append(format("%s TIMESTAMP DEFAULT (CURRENT_TIMESTAMP()),", DATE_ENTRY));
+ query.append(format("%s STRING(MAX))", EVENT_ENTRY));
+ query.append(format(" PRIMARY KEY (%s)", PRIMARY_ENTRY));
+ break;
+ default:
+ query.append(format("%s VARCHAR(255),", PROJECT_ENTRY));
+ query.append(format("%s TIMESTAMP DEFAULT NOW(),", DATE_ENTRY));
+ query.append(format("%s TEXT)", EVENT_ENTRY));
+ }
return query.toString();
}
@@ -140,4 +159,16 @@
query.append(format(H2_INDEX_CREATION_FORMAT, PROJECT_INDEX, TABLE_NAME, PROJECT_ENTRY));
return query.toString();
}
+
+ static String createSpannerDateIndex() {
+ StringBuilder query = new StringBuilder();
+ query.append(format(SPANNER_INDEX_CREATION_FORMAT, CREATED_INDEX, TABLE_NAME, DATE_ENTRY));
+ return query.toString();
+ }
+
+ static String createSpannerProjectIndex() {
+ StringBuilder query = new StringBuilder();
+ query.append(format(SPANNER_INDEX_CREATION_FORMAT, PROJECT_INDEX, TABLE_NAME, PROJECT_ENTRY));
+ return query.toString();
+ }
}
diff --git a/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialectTest.java b/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialectTest.java
index 60b7cb1..96802ac 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialectTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLDialectTest.java
@@ -35,4 +35,9 @@
public void postgresqlIsParsed() throws Exception {
assertThat(SQLDialect.fromJdbcUrl("jdbc:postgresql://")).isEqualTo(SQLDialect.POSTGRESQL);
}
+
+ @Test
+ public void spannerIsParsed() throws Exception {
+ assertThat(SQLDialect.fromJdbcUrl("jdbc:cloudspanner://")).isEqualTo(SQLDialect.SPANNER);
+ }
}