Skip to content

Commit 5a567ed

Browse files
authored
feat: Support for diffs on Create Search Index (#104)
1 parent 4a4c0ad commit 5a567ed

24 files changed

+821
-11
lines changed

src/main/java/com/google/cloud/solutions/spannerddl/diff/AstTreeUtils.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,18 @@ public static void validateChildrenClasses(
139139
}
140140
}
141141
}
142+
143+
/** Verifies that each child is the specified class. */
144+
public static void validateChildrenClass(
145+
Node[] children, Class<? extends SimpleNode> validChildClass) {
146+
for (Node child : children) {
147+
if (validChildClass != child.getClass()) {
148+
throw new IllegalArgumentException(
149+
"Unexpected child node "
150+
+ child.getClass().getSimpleName()
151+
+ " in parent "
152+
+ child.jjtGetParent().getClass().getSimpleName());
153+
}
154+
}
155+
}
142156
}

src/main/java/com/google/cloud/solutions/spannerddl/diff/DatbaseDefinition.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.cloud.solutions.spannerddl.parser.ASTcheck_constraint;
2424
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_change_stream_statement;
2525
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_index_statement;
26+
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_search_index_statement;
2627
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_table_statement;
2728
import com.google.cloud.solutions.spannerddl.parser.ASTddl_statement;
2829
import com.google.cloud.solutions.spannerddl.parser.ASTforeign_key;
@@ -49,6 +50,7 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {
4950
// Use LinkedHashMap to preserve creation order in original DDL.
5051
LinkedHashMap<String, ASTcreate_table_statement> tablesInCreationOrder = new LinkedHashMap<>();
5152
LinkedHashMap<String, ASTcreate_index_statement> indexes = new LinkedHashMap<>();
53+
LinkedHashMap<String, ASTcreate_search_index_statement> searchIndexes = new LinkedHashMap<>();
5254
LinkedHashMap<String, ConstraintWrapper> constraints = new LinkedHashMap<>();
5355
LinkedHashMap<String, ASTrow_deletion_policy_clause> ttls = new LinkedHashMap<>();
5456
LinkedHashMap<String, ASTcreate_change_stream_statement> changeStreams = new LinkedHashMap<>();
@@ -76,6 +78,11 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {
7678
createTable.getRowDeletionPolicyClause();
7779
rowDeletionPolicyClause.ifPresent(rdp -> ttls.put(createTable.getTableName(), rdp));
7880
break;
81+
case DdlParserTreeConstants.JJTCREATE_SEARCH_INDEX_STATEMENT:
82+
searchIndexes.put(
83+
((ASTcreate_search_index_statement) statement).getName(),
84+
(ASTcreate_search_index_statement) statement);
85+
break;
7986
case DdlParserTreeConstants.JJTCREATE_INDEX_STATEMENT:
8087
indexes.put(
8188
((ASTcreate_index_statement) statement).getIndexName(),
@@ -118,6 +125,7 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {
118125
}
119126
return new AutoValue_DatbaseDefinition(
120127
ImmutableMap.copyOf(tablesInCreationOrder),
128+
ImmutableMap.copyOf(searchIndexes),
121129
ImmutableMap.copyOf(indexes),
122130
ImmutableMap.copyOf(constraints),
123131
ImmutableMap.copyOf(ttls),
@@ -127,6 +135,8 @@ static DatbaseDefinition create(List<ASTddl_statement> statements) {
127135

128136
abstract ImmutableMap<String, ASTcreate_table_statement> tablesInCreationOrder();
129137

138+
abstract ImmutableMap<String, ASTcreate_search_index_statement> searchIndexes();
139+
130140
abstract ImmutableMap<String, ASTcreate_index_statement> indexes();
131141

132142
abstract ImmutableMap<String, ConstraintWrapper> constraints();

src/main/java/com/google/cloud/solutions/spannerddl/diff/DdlDiff.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.cloud.solutions.spannerddl.parser.ASTcolumn_type;
2828
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_change_stream_statement;
2929
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_index_statement;
30+
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_search_index_statement;
3031
import com.google.cloud.solutions.spannerddl.parser.ASTcreate_table_statement;
3132
import com.google.cloud.solutions.spannerddl.parser.ASTddl_statement;
3233
import com.google.cloud.solutions.spannerddl.parser.ASTforeign_key;
@@ -96,6 +97,7 @@ public class DdlDiff {
9697
private final MapDifference<String, ASTrow_deletion_policy_clause> ttlDifferences;
9798
private final MapDifference<String, String> alterDatabaseOptionsDifferences;
9899
private final MapDifference<String, ASTcreate_change_stream_statement> changeStreamDifferences;
100+
private final MapDifference<String, ASTcreate_search_index_statement> searchIndexDifferences;
99101
private final String databaseName; // for alter Database
100102

101103
private DdlDiff(DatbaseDefinition originalDb, DatbaseDefinition newDb, String databaseName)
@@ -113,6 +115,8 @@ private DdlDiff(DatbaseDefinition originalDb, DatbaseDefinition newDb, String da
113115
Maps.difference(originalDb.alterDatabaseOptions(), newDb.alterDatabaseOptions());
114116
this.changeStreamDifferences =
115117
Maps.difference(originalDb.changeStreams(), newDb.changeStreams());
118+
this.searchIndexDifferences =
119+
Maps.difference(originalDb.searchIndexes(), newDb.searchIndexes());
116120

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

189+
// drop deleted search indexes.
190+
if (options.get(ALLOW_DROP_STATEMENTS_OPT)) {
191+
for (String searchIndexName : searchIndexDifferences.entriesOnlyOnLeft().keySet()) {
192+
LOG.info("Dropping deleted search index: {}", searchIndexName);
193+
output.add("DROP SEARCH INDEX " + searchIndexName);
194+
}
195+
}
196+
185197
// Drop modified indexes that need to be re-created...
186198
for (ValueDifference<ASTcreate_index_statement> difference :
187199
indexDifferences.entriesDiffering().values()) {
@@ -215,6 +227,12 @@ public List<String> generateDifferenceStatements(Map<String, Boolean> options)
215227
output.add("ALTER TABLE " + tableName + " DROP ROW DELETION POLICY");
216228
}
217229

230+
// For each changed search index, apply the drop column statements
231+
SchemaUpdateStatements searchIndexUpdateStatements =
232+
ASTcreate_search_index_statement.generateAlterStatementsFor(
233+
searchIndexDifferences.entriesDiffering(), options.get(ALLOW_DROP_STATEMENTS_OPT));
234+
output.addAll(searchIndexUpdateStatements.dropStatements());
235+
218236
if (options.get(ALLOW_DROP_STATEMENTS_OPT)) {
219237
// Drop tables that have been deleted -- need to do it in reverse creation order.
220238
List<String> reverseOrderedTableNames =
@@ -370,6 +388,17 @@ public List<String> generateDifferenceStatements(Map<String, Boolean> options)
370388
}
371389
}
372390

391+
for (ASTcreate_search_index_statement searchIndex :
392+
searchIndexDifferences.entriesOnlyOnRight().values()) {
393+
LOG.info("Creating new search index: {}", searchIndex.getName());
394+
output.add(searchIndex.toString());
395+
}
396+
397+
// For each changed search index, apply the add column statements
398+
output.addAll(searchIndexUpdateStatements.createStatements());
399+
400+
// Add all new search indexes
401+
373402
return output.build();
374403
}
375404

@@ -722,6 +751,7 @@ static List<ASTddl_statement> parseDdl(String original) throws DdlDiffException
722751
case DdlParserTreeConstants.JJTCREATE_INDEX_STATEMENT:
723752
case DdlParserTreeConstants.JJTALTER_DATABASE_STATEMENT:
724753
case DdlParserTreeConstants.JJTCREATE_CHANGE_STREAM_STATEMENT:
754+
case DdlParserTreeConstants.JJTCREATE_SEARCH_INDEX_STATEMENT:
725755
// no-op
726756
break;
727757
default:
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.solutions.spannerddl.diff;
18+
19+
import com.google.auto.value.AutoValue;
20+
import com.google.common.collect.ImmutableList;
21+
import java.util.List;
22+
23+
/** Simple container class to hold a set of drop, modify and create statements. */
24+
@AutoValue
25+
public abstract class SchemaUpdateStatements {
26+
27+
/** Creates an instance storing the specified statements. */
28+
public static SchemaUpdateStatements create(
29+
List<String> dropStatements, List<String> modifyStatements, List<String> createStatements) {
30+
return new AutoValue_SchemaUpdateStatements(
31+
ImmutableList.copyOf(dropStatements),
32+
ImmutableList.copyOf(modifyStatements),
33+
ImmutableList.copyOf(createStatements));
34+
}
35+
36+
public abstract ImmutableList<String> dropStatements();
37+
38+
public abstract ImmutableList<String> modifyStatements();
39+
40+
public abstract ImmutableList<String> createStatements();
41+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.solutions.spannerddl.parser;
17+
18+
public class ASTalter_search_index_statement extends SimpleNode {
19+
public ASTalter_search_index_statement(int id) {
20+
super(id);
21+
throw new UnsupportedOperationException("Not Implemented");
22+
}
23+
24+
public ASTalter_search_index_statement(DdlParser p, int id) {
25+
super(p, id);
26+
throw new UnsupportedOperationException("Not Implemented");
27+
}
28+
}

src/main/java/com/google/cloud/solutions/spannerddl/parser/ASTcolumn_type.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public String toString() {
5555
case "DATE":
5656
case "NUMERIC":
5757
case "JSON":
58+
case "TOKENLIST":
5859
return typeName;
5960
case "STRING":
6061
case "BYTES":
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.solutions.spannerddl.parser;
17+
18+
import static com.google.cloud.solutions.spannerddl.diff.AstTreeUtils.validateChildrenClass;
19+
20+
import com.google.cloud.solutions.spannerddl.diff.AstTreeUtils;
21+
import com.google.common.base.Joiner;
22+
import java.util.List;
23+
import java.util.stream.Collectors;
24+
25+
public class ASTcreate_index_where_clause extends SimpleNode {
26+
public ASTcreate_index_where_clause(int id) {
27+
super(id);
28+
}
29+
30+
public ASTcreate_index_where_clause(DdlParser p, int id) {
31+
super(p, id);
32+
}
33+
34+
/**
35+
* void create_index_where_clause() : {} { path() <IS> <NOT> <NULLL> (<AND> path() <IS> <NOT>
36+
* <NULLL>)* }
37+
*/
38+
@Override
39+
public String toString() {
40+
validateChildrenClass(children, ASTpath.class);
41+
42+
// convert paths object to "pathName IS NOT NULL" to make joining easier.
43+
List<String> paths =
44+
AstTreeUtils.getChildrenAssertType(children, ASTpath.class).stream()
45+
.map((ASTpath path) -> path.toString() + " IS NOT NULL")
46+
.collect(Collectors.toList());
47+
48+
return "WHERE " + Joiner.on(" AND ").join(paths);
49+
}
50+
51+
@Override
52+
public int hashCode() {
53+
return toString().hashCode();
54+
}
55+
}

0 commit comments

Comments
 (0)