Skip to content

Commit b1f9060

Browse files
authored
Merge pull request #7 from ergon/feature/partial-index
add support for partial index
2 parents 3b3abe5 + b279773 commit b1f9060

File tree

11 files changed

+70
-6
lines changed

11 files changed

+70
-6
lines changed

core/src/main/java/ch/ergon/adam/core/db/SchemaDiffExtractor.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,8 @@ private boolean ignoreFieldSizeChange(DataType dataType) {
232232
private void processIndexes(Index sourceIndex, Index targetIndex, MigrationStrategy strategy) {
233233
if (sourceIndex.isUnique() != targetIndex.isUnique()
234234
|| sourceIndex.isPrimary() != targetIndex.isPrimary()
235-
|| !Arrays.equals(createSchemaItemNameArray(sourceIndex.getFields()), createSchemaItemNameArray(targetIndex.getFields()))) {
235+
|| !Arrays.equals(createSchemaItemNameArray(sourceIndex.getFields()), createSchemaItemNameArray(targetIndex.getFields()))
236+
|| !Objects.equals(sourceIndex.getWhere(), targetIndex.getWhere())) {
236237
strategy.indexUpdated(sourceIndex, targetIndex);
237238
}
238239
}

core/src/main/java/ch/ergon/adam/core/db/schema/Index.java

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class Index extends TableItem {
1010
private boolean isPrimary;
1111
private boolean isUnique;
1212
private List<Field> fields;
13+
private String where;
1314
private final Set<ForeignKey> referencingForeignKeys = new LinkedHashSet<>();
1415

1516
public Index(String name) {
@@ -41,6 +42,14 @@ public void setUnique(boolean unique) {
4142
isUnique = unique;
4243
}
4344

45+
public String getWhere() {
46+
return where;
47+
}
48+
49+
public void setWhere(String where) {
50+
this.where = where;
51+
}
52+
4453
public void addReferencingForeignKey(ForeignKey foreignKey) {
4554
referencingForeignKeys.add(foreignKey);
4655
}

integration-test-db/src/main/resources/adam/schema/test.table.yml

+7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ fields:
1818
- name: "col4"
1919
dataType: "VARCHAR"
2020
length: 10
21+
- name: "col5"
22+
dataType: "VARCHAR"
23+
nullable: true
2124
foreignKeys: []
2225
indexes:
2326
- name: "test_table_col1_key"
@@ -29,4 +32,8 @@ indexes:
2932
- "id"
3033
primary: true
3134
unique: true
35+
- name: "test_table_partial_key"
36+
fields:
37+
- "col5"
38+
where: "col5 is null"
3239
ruleConstraints: []

integration-test/src/test/java/ch/ergon/adam/integrationtest/testcases/IndexTests.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,19 @@
99
import static org.hamcrest.CoreMatchers.is;
1010
import static org.hamcrest.MatcherAssert.assertThat;
1111
import static org.junit.jupiter.api.Assertions.assertFalse;
12+
import static org.junit.jupiter.api.Assertions.assertTrue;
1213

1314
public abstract class IndexTests extends AbstractDbTestBase {
1415

1516
private static final String CREATE_TABLE_SQL =
1617
"create table test_table (" +
17-
"id integer null unique " +
18+
"id integer null unique, " +
19+
"col1 varchar" +
1820
")";
1921

22+
private static final String CREATE_PARTIAL_INDEX_SQL =
23+
"create unique index partial_idx on test_table(id) where col1 = 'test'";
24+
2025
public IndexTests(TestDbUrlProvider testDbUrlProvider) {
2126
super(testDbUrlProvider);
2227
}
@@ -39,6 +44,8 @@ public void testRecreateIndexAfterTableChange() throws Exception {
3944

4045
// Setup db
4146
getSourceDbConnection().createStatement().execute(CREATE_TABLE_SQL);
47+
getSourceDbConnection().createStatement().execute(CREATE_PARTIAL_INDEX_SQL);
48+
4249
sourceToTarget();
4350
DummySink dummySink = targetToDummy();
4451
Schema schema = dummySink.getTargetSchema();
@@ -51,6 +58,9 @@ public void testRecreateIndexAfterTableChange() throws Exception {
5158

5259
// Verify
5360
assertFalse(schema.getTable("test_table").getField("id").isNullable());
54-
assertThat(schema.getTable("test_table").getIndexes().size(), is(1));
61+
assertThat(schema.getTable("test_table").getIndexes().size(), is(2));
62+
if (!this.getClass().getSimpleName().contains("Sqlite")) { // https://github.com/jOOQ/jOOQ/issues/16683
63+
assertThat(schema.getTable("test_table").getIndex("partial_idx").getWhere(), is("(((col1)::text = 'test'::text))"));
64+
}
5565
}
5666
}

jooq/src/main/java/ch/ergon/adam/jooq/JooqSink.java

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package ch.ergon.adam.jooq;
22

33
import ch.ergon.adam.core.db.interfaces.SchemaSink;
4-
import ch.ergon.adam.core.db.schema.*;
54
import ch.ergon.adam.core.db.schema.Constraint;
65
import ch.ergon.adam.core.db.schema.Field;
76
import ch.ergon.adam.core.db.schema.ForeignKey;
87
import ch.ergon.adam.core.db.schema.Index;
98
import ch.ergon.adam.core.db.schema.Schema;
109
import ch.ergon.adam.core.db.schema.Sequence;
1110
import ch.ergon.adam.core.db.schema.Table;
11+
import ch.ergon.adam.core.db.schema.*;
12+
import com.google.common.base.Strings;
1213
import org.jooq.DataType;
1314
import org.jooq.*;
1415
import org.jooq.impl.DSL;
@@ -26,6 +27,7 @@
2627
import static java.util.stream.Collectors.joining;
2728
import static java.util.stream.Collectors.toList;
2829
import static java.util.stream.Stream.concat;
30+
import static org.jooq.impl.DSL.noCondition;
2931
import static org.jooq.impl.SQLDataType.TIMESTAMPWITHTIMEZONE;
3032

3133
public class JooqSink implements SchemaSink {
@@ -115,7 +117,12 @@ public void createIndex(Index index) {
115117
} else {
116118
createIndex = context.createIndex(index.getName());
117119
}
118-
createIndex.on(getTableName(index.getTable()), fieldNames).execute();
120+
CreateIndexIncludeStep indexStep = createIndex.on(getTableName(index.getTable()), fieldNames);
121+
if (Strings.isNullOrEmpty(index.getWhere())) {
122+
indexStep.execute();
123+
} else {
124+
indexStep.where(DSL.condition(index.getWhere())).execute();
125+
}
119126
}
120127
}
121128

jooq/src/main/java/ch/ergon/adam/jooq/JooqSource.java

+3
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ private Index mapIndexFromJooq(Table table, org.jooq.Index jooqIndex) {
150150
Index index = new Index(jooqIndex.getName());
151151
index.setFields(jooqIndex.getFields().stream().map(jooqField -> table.getField(jooqField.getName())).collect(toList()));
152152
index.setUnique(jooqIndex.getUnique());
153+
if (jooqIndex.getWhere() != null) {
154+
index.setWhere(jooqIndex.getWhere().toString());
155+
}
153156
UniqueKey<?> primaryKey = jooqIndex.getTable().getPrimaryKey();
154157
if (primaryKey != null) {
155158
String[] primaryKeyFieldNames = primaryKey.getFields().stream().map(TableField::getName).toArray(String[]::new);

sqlite/src/main/java/ch/ergon/adam/sqlite/SqliteSink.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22

33
import ch.ergon.adam.jooq.JooqSink;
44
import ch.ergon.adam.core.db.schema.Index;
5+
import com.google.common.base.Strings;
6+
import org.jooq.Condition;
7+
import org.jooq.CreateIndexIncludeStep;
58
import org.jooq.CreateIndexStep;
69
import org.jooq.Name;
10+
import org.jooq.impl.DSL;
711

812
import java.sql.Connection;
913
import java.util.Collection;
1014

1115
import static java.util.stream.Collectors.toList;
1216
import static org.jooq.SQLDialect.SQLITE;
17+
import static org.jooq.impl.DSL.trueCondition;
1318

1419
public class SqliteSink extends JooqSink {
1520

@@ -35,7 +40,13 @@ public void createIndex(Index index) {
3540
} else {
3641
createIndex = context.createIndex();
3742
}
38-
createIndex.on(getTableName(index.getTable()), fieldNames).execute();
43+
44+
CreateIndexIncludeStep indexStep = createIndex.on(getTableName(index.getTable()), fieldNames);
45+
if (Strings.isNullOrEmpty(index.getWhere())) {
46+
indexStep.execute();
47+
} else {
48+
indexStep.where(DSL.condition(index.getWhere())).execute();
49+
}
3950
} else {
4051
super.createIndex(index);
4152
}

yml/src/main/java/ch/ergon/adam/yml/YmlSink.java

+1
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ private YmlIndex mapToYml(Index index) {
280280
YmlIndex ymlIndex = new YmlIndex(index.getName());
281281
ymlIndex.setPrimary(index.isPrimary());
282282
ymlIndex.setUnique(index.isUnique());
283+
ymlIndex.setWhere(index.getWhere());
283284
ymlIndex.setFields(createSchemaItemNameArray(index.getFields()));
284285
return ymlIndex;
285286
}

yml/src/main/java/ch/ergon/adam/yml/YmlSource.java

+1
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ private Index mapFromYml(YmlIndex ymlIndex, Table table, int position) {
193193
Index index = new Index(indexName);
194194
index.setPrimary(ymlIndex.isPrimary());
195195
index.setUnique(ymlIndex.isPrimary() ? true : ymlIndex.isUnique());
196+
index.setWhere(ymlIndex.getWhere());
196197
index.setFields(stream(ymlIndex.getFields())
197198
.map(name -> requireNonNull(table.getField(name), format("field %s.%s not found for index %s", table.getName(), name, ymlIndex.getName())))
198199
.collect(toList()));

yml/src/main/java/ch/ergon/adam/yml/schema/YmlIndex.java

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ public class YmlIndex extends YmlSchemaItem {
66

77
private boolean isPrimary;
88
private boolean isUnique;
9+
private String where;
910
private String[] fields = new String[0];
1011

1112
public YmlIndex(@JsonProperty("name")String name) {
@@ -32,6 +33,14 @@ public boolean isUnique() {
3233
return isUnique;
3334
}
3435

36+
public String getWhere() {
37+
return where;
38+
}
39+
40+
public void setWhere(String where) {
41+
this.where = where;
42+
}
43+
3544
public void setUnique(boolean unique) {
3645
isUnique = unique;
3746
}

yml/table-schema.json

+5
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@
199199
]
200200
}
201201
},
202+
"where": {
203+
"$id": "#/properties/indexes/items/properties/where",
204+
"type": "string",
205+
"description": "Partial index condition."
206+
},
202207
"primary": {
203208
"$id": "#/properties/indexes/items/properties/primary",
204209
"type": "boolean",

0 commit comments

Comments
 (0)