Skip to content

Commit

Permalink
feat: Support for diffs on Create Search Index (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
nielm authored May 14, 2024
1 parent 4a4c0ad commit 5a567ed
Show file tree
Hide file tree
Showing 24 changed files with 821 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,18 @@ public static void validateChildrenClasses(
}
}
}

/** Verifies that each child is the specified class. */
public static void validateChildrenClass(
Node[] children, Class<? extends SimpleNode> validChildClass) {
for (Node child : children) {
if (validChildClass != child.getClass()) {
throw new IllegalArgumentException(
"Unexpected child node "
+ child.getClass().getSimpleName()
+ " in parent "
+ child.jjtGetParent().getClass().getSimpleName());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.cloud.solutions.spannerddl.parser.ASTcheck_constraint;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_change_stream_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_index_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_search_index_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_table_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTddl_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTforeign_key;
Expand All @@ -49,6 +50,7 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {
// Use LinkedHashMap to preserve creation order in original DDL.
LinkedHashMap<String, ASTcreate_table_statement> tablesInCreationOrder = new LinkedHashMap<>();
LinkedHashMap<String, ASTcreate_index_statement> indexes = new LinkedHashMap<>();
LinkedHashMap<String, ASTcreate_search_index_statement> searchIndexes = new LinkedHashMap<>();
LinkedHashMap<String, ConstraintWrapper> constraints = new LinkedHashMap<>();
LinkedHashMap<String, ASTrow_deletion_policy_clause> ttls = new LinkedHashMap<>();
LinkedHashMap<String, ASTcreate_change_stream_statement> changeStreams = new LinkedHashMap<>();
Expand Down Expand Up @@ -76,6 +78,11 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {
createTable.getRowDeletionPolicyClause();
rowDeletionPolicyClause.ifPresent(rdp -> ttls.put(createTable.getTableName(), rdp));
break;
case DdlParserTreeConstants.JJTCREATE_SEARCH_INDEX_STATEMENT:
searchIndexes.put(
((ASTcreate_search_index_statement) statement).getName(),
(ASTcreate_search_index_statement) statement);
break;
case DdlParserTreeConstants.JJTCREATE_INDEX_STATEMENT:
indexes.put(
((ASTcreate_index_statement) statement).getIndexName(),
Expand Down Expand Up @@ -118,6 +125,7 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {
}
return new AutoValue_DatbaseDefinition(
ImmutableMap.copyOf(tablesInCreationOrder),
ImmutableMap.copyOf(searchIndexes),
ImmutableMap.copyOf(indexes),
ImmutableMap.copyOf(constraints),
ImmutableMap.copyOf(ttls),
Expand All @@ -127,6 +135,8 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {

abstract ImmutableMap<String, ASTcreate_table_statement> tablesInCreationOrder();

abstract ImmutableMap<String, ASTcreate_search_index_statement> searchIndexes();

abstract ImmutableMap<String, ASTcreate_index_statement> indexes();

abstract ImmutableMap<String, ConstraintWrapper> constraints();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.cloud.solutions.spannerddl.parser.ASTcolumn_type;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_change_stream_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_index_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_search_index_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_table_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTddl_statement;
import com.google.cloud.solutions.spannerddl.parser.ASTforeign_key;
Expand Down Expand Up @@ -96,6 +97,7 @@ public class DdlDiff {
private final MapDifference<String, ASTrow_deletion_policy_clause> ttlDifferences;
private final MapDifference<String, String> alterDatabaseOptionsDifferences;
private final MapDifference<String, ASTcreate_change_stream_statement> changeStreamDifferences;
private final MapDifference<String, ASTcreate_search_index_statement> searchIndexDifferences;
private final String databaseName; // for alter Database

private DdlDiff(DatbaseDefinition originalDb, DatbaseDefinition newDb, String databaseName)
Expand All @@ -113,6 +115,8 @@ private DdlDiff(DatbaseDefinition originalDb, DatbaseDefinition newDb, String da
Maps.difference(originalDb.alterDatabaseOptions(), newDb.alterDatabaseOptions());
this.changeStreamDifferences =
Maps.difference(originalDb.changeStreams(), newDb.changeStreams());
this.searchIndexDifferences =
Maps.difference(originalDb.searchIndexes(), newDb.searchIndexes());

if (!alterDatabaseOptionsDifferences.areEqual() && Strings.isNullOrEmpty(databaseName)) {
// should never happen, but...
Expand Down Expand Up @@ -182,6 +186,14 @@ public List<String> generateDifferenceStatements(Map<String, Boolean> options)
}
}

// drop deleted search indexes.
if (options.get(ALLOW_DROP_STATEMENTS_OPT)) {
for (String searchIndexName : searchIndexDifferences.entriesOnlyOnLeft().keySet()) {
LOG.info("Dropping deleted search index: {}", searchIndexName);
output.add("DROP SEARCH INDEX " + searchIndexName);
}
}

// Drop modified indexes that need to be re-created...
for (ValueDifference<ASTcreate_index_statement> difference :
indexDifferences.entriesDiffering().values()) {
Expand Down Expand Up @@ -215,6 +227,12 @@ public List<String> generateDifferenceStatements(Map<String, Boolean> options)
output.add("ALTER TABLE " + tableName + " DROP ROW DELETION POLICY");
}

// For each changed search index, apply the drop column statements
SchemaUpdateStatements searchIndexUpdateStatements =
ASTcreate_search_index_statement.generateAlterStatementsFor(
searchIndexDifferences.entriesDiffering(), options.get(ALLOW_DROP_STATEMENTS_OPT));
output.addAll(searchIndexUpdateStatements.dropStatements());

if (options.get(ALLOW_DROP_STATEMENTS_OPT)) {
// Drop tables that have been deleted -- need to do it in reverse creation order.
List<String> reverseOrderedTableNames =
Expand Down Expand Up @@ -370,6 +388,17 @@ public List<String> generateDifferenceStatements(Map<String, Boolean> options)
}
}

for (ASTcreate_search_index_statement searchIndex :
searchIndexDifferences.entriesOnlyOnRight().values()) {
LOG.info("Creating new search index: {}", searchIndex.getName());
output.add(searchIndex.toString());
}

// For each changed search index, apply the add column statements
output.addAll(searchIndexUpdateStatements.createStatements());

// Add all new search indexes

return output.build();
}

Expand Down Expand Up @@ -722,6 +751,7 @@ static List<ASTddl_statement> parseDdl(String original) throws DdlDiffException
case DdlParserTreeConstants.JJTCREATE_INDEX_STATEMENT:
case DdlParserTreeConstants.JJTALTER_DATABASE_STATEMENT:
case DdlParserTreeConstants.JJTCREATE_CHANGE_STREAM_STATEMENT:
case DdlParserTreeConstants.JJTCREATE_SEARCH_INDEX_STATEMENT:
// no-op
break;
default:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2024 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
*
* https://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.solutions.spannerddl.diff;

import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import java.util.List;

/** Simple container class to hold a set of drop, modify and create statements. */
@AutoValue
public abstract class SchemaUpdateStatements {

/** Creates an instance storing the specified statements. */
public static SchemaUpdateStatements create(
List<String> dropStatements, List<String> modifyStatements, List<String> createStatements) {
return new AutoValue_SchemaUpdateStatements(
ImmutableList.copyOf(dropStatements),
ImmutableList.copyOf(modifyStatements),
ImmutableList.copyOf(createStatements));
}

public abstract ImmutableList<String> dropStatements();

public abstract ImmutableList<String> modifyStatements();

public abstract ImmutableList<String> createStatements();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2024 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
*
* https://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.solutions.spannerddl.parser;

public class ASTalter_search_index_statement extends SimpleNode {
public ASTalter_search_index_statement(int id) {
super(id);
throw new UnsupportedOperationException("Not Implemented");
}

public ASTalter_search_index_statement(DdlParser p, int id) {
super(p, id);
throw new UnsupportedOperationException("Not Implemented");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public String toString() {
case "DATE":
case "NUMERIC":
case "JSON":
case "TOKENLIST":
return typeName;
case "STRING":
case "BYTES":
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2024 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
*
* https://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.solutions.spannerddl.parser;

import static com.google.cloud.solutions.spannerddl.diff.AstTreeUtils.validateChildrenClass;

import com.google.cloud.solutions.spannerddl.diff.AstTreeUtils;
import com.google.common.base.Joiner;
import java.util.List;
import java.util.stream.Collectors;

public class ASTcreate_index_where_clause extends SimpleNode {
public ASTcreate_index_where_clause(int id) {
super(id);
}

public ASTcreate_index_where_clause(DdlParser p, int id) {
super(p, id);
}

/**
* void create_index_where_clause() : {} { path() <IS> <NOT> <NULLL> (<AND> path() <IS> <NOT>
* <NULLL>)* }
*/
@Override
public String toString() {
validateChildrenClass(children, ASTpath.class);

// convert paths object to "pathName IS NOT NULL" to make joining easier.
List<String> paths =
AstTreeUtils.getChildrenAssertType(children, ASTpath.class).stream()
.map((ASTpath path) -> path.toString() + " IS NOT NULL")
.collect(Collectors.toList());

return "WHERE " + Joiner.on(" AND ").join(paths);
}

@Override
public int hashCode() {
return toString().hashCode();
}
}
Loading

0 comments on commit 5a567ed

Please sign in to comment.