Skip to content
Open
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 @@ -22,6 +22,12 @@
type = RosettaModuleTypes.DDL_GENERATOR
)
public class SpannerDDLGenerator implements DDL {
private final static String TABLE_CREATE_TEMPLATE = "spanner/table/create";
private final static String TABLE_DROP_TEMPLATE = "spanner/table/drop";
private final static String FOREIGN_KEY_CREATE_TEMPLATE = "spanner/foreignkey/create";
private final static String FOREIGN_KEY_DROP_TEMPLATE = "spanner/foreignkey/drop";
private final static String COLUMN_ADD_TEMPLATE = "spanner/column/add";
private final static String COLUMN_DROP_TEMPLATE = "spanner/column/drop";
private static String VIEW_CREATE_TEMPLATE = "spanner/view/create";
private static String VIEW_ALTER_TEMPLATE = "spanner/view/alter";
private static String VIEW_DROP_TEMPLATE = "spanner/view/drop";
Expand All @@ -39,44 +45,28 @@ public String createTable(Table table, boolean dropTableIfExists) {
throw new RuntimeException(String.format("Table %s has no primary key. Spanner requires tables to have primary key.", table.getName()));
}
List<String> definitions = table.getColumns().stream().map(this::createColumn).collect(Collectors.toList());

Map<String, Object> createParams = new HashMap<>();
Optional<String> primaryKeysForTable = createPrimaryKeysForTable(table);
String definitionAsString = String.join(", ", definitions);

StringBuilder stringBuilder = new StringBuilder();
if (dropTableIfExists) {
stringBuilder.append("DROP TABLE IF EXISTS ");
if (table.getSchema() != null && !table.getSchema().isBlank()) {
stringBuilder.append("`").append(table.getSchema()).append("`.");
}
stringBuilder.append(DEFAULT_WRAPPER).append(table.getName()).append(DEFAULT_WRAPPER).append("; \n");
stringBuilder.append(dropTable(table));
}

stringBuilder.append("CREATE TABLE ");

if (table.getSchema() != null && !table.getSchema().isBlank()) {
stringBuilder.append(DEFAULT_WRAPPER)
.append(table.getSchema()).append(DEFAULT_WRAPPER).append(".");
}

stringBuilder.append(DEFAULT_WRAPPER).append(table.getName()).append(DEFAULT_WRAPPER)
.append("(").append(definitionAsString).append(")");
if(primaryKeysForTable.isPresent()) {
stringBuilder.append(" ");
stringBuilder.append(primaryKeysForTable.get());
}

if (table.getInterleave() != null) {
stringBuilder.append(",\r");
stringBuilder.append("INTERLEAVE IN PARENT ")
.append(table.getInterleave().getParentName());

final String onDeleteAction = table.getInterleave().getOnDeleteAction();
if (onDeleteAction != null && !onDeleteAction.isEmpty()) {
stringBuilder.append(" ON DELETE ").append(table.getInterleave().getOnDeleteAction());
createParams.put("deleteRule", String.format("ON DELETE %s", table.getInterleave().getOnDeleteAction()));
}
createParams.put("interleavedTable", table.getInterleave().getParentName());
}
stringBuilder.append(";");
createParams.put("schemaName", table.getSchema());
createParams.put("tableName", table.getName());
createParams.put("tableCode", definitionAsString);
primaryKeysForTable.ifPresent(s -> createParams.put("primaryKeyDefinition", s));
stringBuilder.append(TemplateEngine.process(TABLE_CREATE_TEMPLATE, createParams));
return stringBuilder.toString();
}

Expand Down Expand Up @@ -158,12 +148,16 @@ public String createForeignKey(ForeignKey foreignKey) {
if (foreignKey.getName() == null) {
return "";
}
return "ALTER TABLE" + handleNullSchema(foreignKey.getSchema(), foreignKey.getTableName()) + " ADD CONSTRAINT "
+ foreignKey.getName() + " FOREIGN KEY ("+ DEFAULT_WRAPPER + foreignKey.getColumnName() + DEFAULT_WRAPPER +") REFERENCES "
+ foreignKey.getPrimaryTableName()
// + handleNullSchema(foreignKey.getPrimaryTableSchema(), foreignKey.getPrimaryTableName())
+ "("+ DEFAULT_WRAPPER + foreignKey.getPrimaryColumnName()+ DEFAULT_WRAPPER + ")"
+ foreignKeyDeleteRuleSanitation(foreignKeyDeleteRule(foreignKey)) + ";\r";
Map<String, Object> params = new HashMap<>();
params.put("schemaName", foreignKey.getSchema());
params.put("tableName", foreignKey.getTableName());
params.put("foreignkeyColumn", foreignKey.getColumnName());
params.put("primaryTableSchema", foreignKey.getPrimaryTableSchema());
params.put("primaryTableName", foreignKey.getPrimaryTableName());
params.put("foreignKeyPrimaryColumnName", foreignKey.getPrimaryColumnName());
params.put("foreignkeyName", foreignKey.getName());
params.put("deleteRule", foreignKeyDeleteRule(foreignKey));
return TemplateEngine.process(FOREIGN_KEY_CREATE_TEMPLATE, params);
}

@Override
Expand Down Expand Up @@ -200,25 +194,31 @@ public String dropColumn(ColumnChange change) {
Table table = change.getTable();
Column actual = change.getActual();

return "ALTER TABLE" +
handleNullSchema(table.getSchema(), table.getName()) + " DROP COLUMN "+ DEFAULT_WRAPPER +
actual.getName() + DEFAULT_WRAPPER +";";
Map<String, Object> params = new HashMap<>();
params.put("schemaName", table.getSchema());
params.put("tableName", table.getName());
params.put("columnName", actual.getName());
return TemplateEngine.process(COLUMN_DROP_TEMPLATE, params);
}

@Override
public String addColumn(ColumnChange change) {
Table table = change.getTable();
Column expected = change.getExpected();

return "ALTER TABLE" +
handleNullSchema(table.getSchema(), table.getName()) +
" ADD COLUMN " +
columnSQLDecoratorFactory.decoratorFor(expected).expressSQl() + ";";
Map<String, Object> params = new HashMap<>();
params.put("schemaName", table.getSchema());
params.put("tableName", table.getName());
params.put("columnDefinition", columnSQLDecoratorFactory.decoratorFor(expected).expressSQl());
return TemplateEngine.process(COLUMN_ADD_TEMPLATE, params);
}

@Override
public String dropTable(Table actual) {
return "DROP TABLE" + handleNullSchema(actual.getSchema(), actual.getName()) + ";";
Map<String, Object> params = new HashMap<>();
params.put("schemaName", actual.getSchema());
params.put("tableName", actual.getName());
return TemplateEngine.process(TABLE_DROP_TEMPLATE, params);
}

@Override
Expand All @@ -228,7 +228,11 @@ public String alterForeignKey(ForeignKeyChange change) {

@Override
public String dropForeignKey(ForeignKey actual) {
return "ALTER TABLE" + handleNullSchema(actual.getSchema(), actual.getTableName()) + " DROP CONSTRAINT " + DEFAULT_WRAPPER + actual.getName() + DEFAULT_WRAPPER + ";";
Map<String, Object> params = new HashMap<>();
params.put("schemaName", actual.getSchema());
params.put("tableName", actual.getTableName());
params.put("foreignkeyName", actual.getName());
return TemplateEngine.process(FOREIGN_KEY_DROP_TEMPLATE, params);
}

@Override
Expand Down
1 change: 1 addition & 0 deletions ddl/src/main/resources/templates/spanner/column/add.sqlt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE [(${tableName})] ADD COLUMN [(${columnDefinition})];
Empty file.
Empty file.
1 change: 1 addition & 0 deletions ddl/src/main/resources/templates/spanner/column/drop.sqlt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE [(${tableName})] DROP COLUMN [(${columnName})];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE [(${tableName})] ADD CONSTRAINT [(${foreignkeyName})] FOREIGN KEY ("[(${foreignkeyColumn})]") REFERENCES [(${primaryTableName})]("[(${foreignKeyPrimaryColumnName})]") [(${deleteRule})];
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE [(${tableName})] DROP CONSTRAINT [(${foreignkeyName})];
Empty file.
7 changes: 7 additions & 0 deletions ddl/src/main/resources/templates/spanner/table/create.sqlt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[# th:if="${interleavedTable} == null or ${interleavedTable} == ''"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to handle schema here as well? In the examples above we are checking if schema exists.

CREATE TABLE [(${tableName})]([(${tableCode})]) [(${primaryKeyDefinition})];
[/]
[# th:if="${interleavedTable} != null and ${interleavedTable} != ''"]
CREATE TABLE [(${tableName})]([(${tableCode})]) [(${primaryKeyDefinition})],
INTERLEAVE IN PARENT [(${interleavedTable})] [(${deleteRule})];
[/]
1 change: 1 addition & 0 deletions ddl/src/main/resources/templates/spanner/table/drop.sqlt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE [(${tableName})];
Original file line number Diff line number Diff line change
Expand Up @@ -21,39 +21,39 @@ public class SpannerDDLTest {
@Test
public void addInterleaveTable() throws IOException {
String ddl = generateDDL("add_interleave_table");
Assertions.assertEquals("CREATE TABLE AlbumsNew(SingerId INT64 NOT NULL , AlbumId INT64 NOT NULL , AlbumTitle STRING(MAX)) PRIMARY KEY (SingerId, AlbumId),\r" +
"INTERLEAVE IN PARENT Singers ON DELETE CASCADE;", ddl);
Assertions.assertEquals("CREATE TABLE AlbumsNew(SingerId INT64 NOT NULL , AlbumId INT64 NOT NULL , AlbumTitle STRING(MAX)) PRIMARY KEY (SingerId, AlbumId),\n" +
"INTERLEAVE IN PARENT Singers ON DELETE CASCADE;\n", ddl.replaceAll("(?m)^[ \t]*\r?\n", ""));
}

@Test
public void createDB() throws IOException {
String ddl = generateDDL("clean_database");
Assertions.assertEquals("CREATE TABLE Singers(SingerId STRING(1024) NOT NULL , FirstName STRING(1024), LastName STRING(1024)) PRIMARY KEY (SingerId);CREATE VIEW `SingerNames` SQL SECURITY INVOKER AS SELECT Singers.SingerId AS SingerId, Singers.FirstName || ' ' || Singers.LastName AS Name FROM Singers ;CREATE VIEW `NamesSinger` SQL SECURITY INVOKER AS SELECT Singers.SingerId AS SingerId, Singers.FirstName, Singers.LastName FROM Singers ;",
Assertions.assertEquals(" CREATE TABLE Singers(SingerId STRING(1024) NOT NULL , FirstName STRING(1024), LastName STRING(1024)) PRIMARY KEY (SingerId); CREATE VIEW `SingerNames` SQL SECURITY INVOKER AS SELECT Singers.SingerId AS SingerId, Singers.FirstName || ' ' || Singers.LastName AS Name FROM Singers ;CREATE VIEW `NamesSinger` SQL SECURITY INVOKER AS SELECT Singers.SingerId AS SingerId, Singers.FirstName, Singers.LastName FROM Singers ;",
ddl.replaceAll("(\\n)", " ").replaceAll("(\\r)", ""));
}

@Test
public void addTable() throws IOException {
String ddl = generateDDL("add_table");
Assertions.assertEquals("CREATE TABLE Logs(LogId INT64 NOT NULL , Description STRING(1024)) PRIMARY KEY (LogId);", ddl);
Assertions.assertEquals("CREATE TABLE Logs(LogId INT64 NOT NULL , Description STRING(1024)) PRIMARY KEY (LogId);\n", ddl.replaceAll("(?m)^[ \t]*\r?\n", ""));
}

@Test
public void dropTable() throws IOException {
String ddl = generateDDL("drop_table");
Assertions.assertEquals("DROP TABLE Logs;", ddl);
Assertions.assertEquals("DROP TABLE Logs;\n", ddl.replaceAll("(?m)^[ \t]*\r?\n", ""));
}

@Test
public void addColumn() throws IOException {
String ddl = generateDDL("add_column");
Assertions.assertEquals("ALTER TABLE Logs ADD COLUMN Status STRING(1024);", ddl);
Assertions.assertEquals("ALTER TABLE Logs ADD COLUMN Status STRING(1024);\n", ddl.replaceAll("(?m)^[ \t]*\r?\n", ""));
}

@Test
public void dropColumn() throws IOException {
String ddl = generateDDL("drop_column");
Assertions.assertEquals("ALTER TABLE Logs DROP COLUMN Status;", ddl);
Assertions.assertEquals("ALTER TABLE Logs DROP COLUMN Status;\n", ddl.replaceAll("(?m)^[ \t]*\r?\n", ""));
}

@Test
Expand Down