Skip to content

Commit 9f3a6ed

Browse files
o. Re-factor table creation into validation against
existing table and table creation o. Add reason for table mismatch
1 parent e42a983 commit 9f3a6ed

File tree

4 files changed

+158
-124
lines changed

4 files changed

+158
-124
lines changed

src/main/java/com/oracle/nosql/spring/data/core/NosqlTemplate.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,16 @@ public String getTableName(Class<?> domainClass) {
9393
@Override
9494
public boolean createTableIfNotExists(
9595
NosqlEntityInformation<?, ?> entityInformation) {
96-
return doCreateTableIfNotExists(entityInformation);
96+
String ddl = getCreateTableDDL(entityInformation);
97+
try {
98+
doCheckExistingTable(entityInformation);
99+
} catch (IllegalArgumentException iae) {
100+
String msg = String.format("Error executing DDL '%s': Table %s " +
101+
"exists but definitions do not match : %s", ddl,
102+
entityInformation.getTableName(), iae.getMessage());
103+
throw new IllegalArgumentException(msg, iae);
104+
}
105+
return doCreateTable(entityInformation, ddl);
97106
}
98107

99108
@SuppressWarnings("unchecked")

src/main/java/com/oracle/nosql/spring/data/core/NosqlTemplateBase.java

Lines changed: 138 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,8 @@ protected TableResult doTableRequest(NosqlEntityInformation<?, ?> entityInformat
117117
return tableRes;
118118
}
119119

120-
protected boolean doCreateTableIfNotExists(
121-
NosqlEntityInformation<?, ?> entityInformation) {
122-
120+
protected String getCreateTableDDL(NosqlEntityInformation<?, ?> entityInformation) {
123121
String tableName = entityInformation.getTableName();
124-
String autogen = getAutoGenType(entityInformation);
125-
TimeToLive ttl = entityInformation.getTtl();
126122
String sql;
127123

128124
Map<String, FieldValue.Type> shardKeys =
@@ -140,6 +136,7 @@ protected boolean doCreateTableIfNotExists(
140136
if (keyType.equals(FieldValue.Type.TIMESTAMP.toString())) {
141137
keyType += "(" + nosqlDbFactory.getTimestampPrecision() + ")";
142138
}
139+
String autogen = getAutoGenType(entityInformation);
143140
tableBuilder.append(key).append(" ").append(keyType)
144141
.append(" ").append(autogen).append(",");
145142
});
@@ -149,6 +146,7 @@ protected boolean doCreateTableIfNotExists(
149146
if (keyType.equals(FieldValue.Type.TIMESTAMP.toString())) {
150147
keyType += "(" + nosqlDbFactory.getTimestampPrecision() + ")";
151148
}
149+
String autogen = getAutoGenType(entityInformation);
152150
tableBuilder.append(key).append(" ").append(keyType)
153151
.append(" ").append(autogen).append(",");
154152
});
@@ -167,28 +165,19 @@ protected boolean doCreateTableIfNotExists(
167165
tableBuilder.append(")"); //create close )
168166

169167
//ttl
170-
if (ttl != null && ttl.getValue() != 0) {
168+
if (entityInformation.getTtl() != null &&
169+
entityInformation.getTtl().getValue() != 0) {
171170
tableBuilder.append(String.format(TEMPLATE_TTL_CREATE,
172171
entityInformation.getTtl().toString()));
173172
}
174173
sql = tableBuilder.toString();
174+
return sql;
175+
}
175176

176-
TableResult tableResult = doGetTable(entityInformation);
177-
if (tableResult != null) {
178-
/*table already exist in the database. Compare and throw error if
179-
mismatch*/
180-
String schema = tableResult.getSchema();
181-
MapValue jsonSchema = JsonUtils.createValueFromJson(schema,
182-
new JsonOptions().setMaintainInsertionOrder(true)).asMap();
183-
if (!compareTables(shardKeys, nonShardKeys, entityInformation,
184-
jsonSchema)) {
185-
throw new IllegalArgumentException(String.format(
186-
"Error executing DDL '%s': Table %s exists" +
187-
" but definitions do not match", sql,
188-
tableName));
189-
}
190-
}
191-
TableRequest tableReq = new TableRequest().setStatement(sql)
177+
protected boolean doCreateTable(
178+
NosqlEntityInformation<?, ?> entityInformation,
179+
String ddl) {
180+
TableRequest tableReq = new TableRequest().setStatement(ddl)
192181
.setTableLimits(entityInformation.getTableLimits(nosqlDbFactory));
193182

194183
TableResult tableRes = doTableRequest(entityInformation, tableReq);
@@ -197,6 +186,132 @@ protected boolean doCreateTableIfNotExists(
197186
return tableState == TableResult.State.ACTIVE;
198187
}
199188

189+
protected void doCheckExistingTable(NosqlEntityInformation<?, ?> entityInformation) {
190+
TableResult tableResult = doGetTable(entityInformation);
191+
/*If table already exist in the database compare and throw error if
192+
mismatch*/
193+
if (tableResult != null) {
194+
MapValue jsonSchema = JsonUtils.createValueFromJson(
195+
tableResult.getSchema(),
196+
new JsonOptions().setMaintainInsertionOrder(true)).
197+
asMap();
198+
199+
Map<String, FieldValue.Type> shardKeys = entityInformation.
200+
getShardKeys();
201+
Map<String, FieldValue.Type> nonShardKeys = entityInformation.
202+
getNonShardKeys();
203+
204+
ArrayValue columns = jsonSchema.get("fields").asArray();
205+
//check number of columns are same
206+
if (columns.size() != shardKeys.size() + nonShardKeys.size() + 1) {
207+
throw new IllegalArgumentException(
208+
"Number of columns are not same");
209+
}
210+
211+
//lower case maps
212+
Map<String, FieldValue.Type> caseShardKey = new LinkedHashMap<>();
213+
shardKeys.forEach((k, v) -> caseShardKey.put(k.toLowerCase(), v));
214+
215+
Map<String, FieldValue.Type> caseNonShardKey =
216+
new LinkedHashMap<>();
217+
nonShardKeys.forEach((k, v) -> caseNonShardKey.put(k.toLowerCase(), v));
218+
219+
//check column names and types are same
220+
for (int i = 0; i < columns.size(); i++) {
221+
MapValue column = columns.get(i).asMap();
222+
String columnName = column.getString("name").toLowerCase();
223+
String columnType = column.getString("type").toLowerCase();
224+
String msg;
225+
if (i < caseShardKey.size()) {
226+
if (!caseShardKey.containsKey(columnName)) {
227+
msg = String.format("column '%s' mismatch", columnName);
228+
throw new IllegalArgumentException(msg);
229+
}
230+
if (!columnType.equalsIgnoreCase(caseShardKey.get(columnName).name())) {
231+
msg = String.format("type mismatch for " +
232+
"column '%s'", columnName);
233+
throw new IllegalArgumentException(msg);
234+
}
235+
} else if (i < caseShardKey.size() + caseNonShardKey.size()) {
236+
if (!caseNonShardKey.containsKey(columnName)) {
237+
msg = String.format("column '%s' mismatch", columnName);
238+
throw new IllegalArgumentException(msg);
239+
}
240+
if (!columnType.equalsIgnoreCase(caseNonShardKey.get(columnName).name())) {
241+
msg = String.format("type mismatch for " +
242+
"column '%s'", columnName);
243+
throw new IllegalArgumentException(msg);
244+
}
245+
} else {
246+
if (!columnName.equalsIgnoreCase(JSON_COLUMN)) {
247+
msg = String.format("%s column not present",
248+
JSON_COLUMN);
249+
throw new IllegalArgumentException(msg);
250+
}
251+
if (!columnType.equalsIgnoreCase("JSON")) {
252+
msg = String.format("%s column type is not JSON",
253+
JSON_COLUMN);
254+
throw new IllegalArgumentException(msg);
255+
}
256+
}
257+
}
258+
259+
//check order of the shard keys are same
260+
ArrayValue shards = jsonSchema.get("shardKey").asArray();
261+
if (shards.size() != shardKeys.size()) {
262+
throw new IllegalArgumentException("number of shard keys do " +
263+
"not match");
264+
265+
}
266+
int i = 0;
267+
for (String key : shardKeys.keySet()) {
268+
if (!key.equalsIgnoreCase(shards.get(i).getString())) {
269+
throw new IllegalArgumentException("Order of shard keys " +
270+
"do not match");
271+
}
272+
i++;
273+
}
274+
275+
//check order of non shard keys are same
276+
ArrayValue primaryKeys = jsonSchema.get("primaryKey").asArray();
277+
for (String key : nonShardKeys.keySet()) {
278+
if (!key.equalsIgnoreCase(primaryKeys.get(i).getString())) {
279+
throw new IllegalArgumentException("Order of non-shard " +
280+
"keys do not match");
281+
}
282+
i++;
283+
}
284+
285+
//check identity same
286+
FieldValue identity = jsonSchema.get("identity");
287+
if (identity != null && !entityInformation.isAutoGeneratedId()) {
288+
throw new IllegalArgumentException("Identity information " +
289+
"mismatch");
290+
291+
} else if (identity == null && entityInformation.isAutoGeneratedId() &&
292+
entityInformation.getIdNosqlType() != FieldValue.Type.STRING) {
293+
throw new IllegalArgumentException("Identity information " +
294+
"mismatch");
295+
}
296+
297+
//TTL warning
298+
FieldValue ttlValue = jsonSchema.get("ttl");
299+
TimeToLive ttl = entityInformation.getTtl();
300+
//TTL is present in database but not in the entity
301+
if (ttlValue != null && ttl != null &&
302+
!ttl.toString().equalsIgnoreCase(ttlValue.getString())) {
303+
LOG.warn("TTL of the table in database is different from the " +
304+
"TTL " +
305+
"of the entity " + entityInformation.getJavaType().getName());
306+
} else if (ttlValue == null && ttl != null && ttl.getValue() != 0) {
307+
//TTL is present in entity but not in the database
308+
LOG.warn("TTL of the table in database is different from the " +
309+
"TTL " +
310+
"of the entity " + entityInformation.getJavaType().getName());
311+
}
312+
}
313+
}
314+
200315
protected DeleteResult doDelete(
201316
NosqlEntityInformation<?, ?> entityInformation,
202317
MapValue primaryKey) {
@@ -252,7 +367,7 @@ protected PutResult doPut(NosqlEntityInformation<?, ?> entityInformation,
252367
putRes = nosqlClient.put(putReq);
253368
} catch (TableNotFoundException tnfe) {
254369
if (entityInformation.isAutoCreateTable()) {
255-
doCreateTableIfNotExists(entityInformation);
370+
doCreateTable(entityInformation, getCreateTableDDL(entityInformation));
256371
putRes = nosqlClient.put(putReq);
257372
} else {
258373
throw tnfe;
@@ -449,101 +564,4 @@ private String getAutoGenType(NosqlEntityInformation<?, ?> entityInformation) {
449564
}
450565
return "";
451566
}
452-
453-
/**
454-
* Compare table present in the database against the DDL generated from
455-
* entity
456-
*/
457-
private boolean compareTables(Map<String, FieldValue.Type> shardKeys,
458-
Map<String, FieldValue.Type> nonShardKeys,
459-
NosqlEntityInformation<?, ?> entityInformation,
460-
MapValue jsonSchema) {
461-
ArrayValue columns = jsonSchema.get("fields").asArray();
462-
//check number of columns are same
463-
if (columns.size() != shardKeys.size() + nonShardKeys.size() + 1) {
464-
return false;
465-
}
466-
467-
//lower case maps
468-
Map<String, FieldValue.Type> caseShardKey = new LinkedHashMap<>();
469-
shardKeys.forEach((k, v) -> caseShardKey.put(k.toLowerCase(), v));
470-
471-
Map<String, FieldValue.Type> caseNonShardKey = new LinkedHashMap<>();
472-
nonShardKeys.forEach((k, v) -> caseNonShardKey.put(k.toLowerCase(), v));
473-
474-
//check column names and types are same
475-
for (int i = 0; i < columns.size(); i++) {
476-
MapValue column = columns.get(i).asMap();
477-
String columnName = column.getString("name").toLowerCase();
478-
String columnType = column.getString("type").toLowerCase();
479-
if (i < caseShardKey.size()) {
480-
if (!caseShardKey.containsKey(columnName)) {
481-
return false;
482-
}
483-
if (!columnType.equalsIgnoreCase(caseShardKey.get(columnName).name())) {
484-
return false;
485-
}
486-
} else if (i < caseShardKey.size() + caseNonShardKey.size()) {
487-
if (!caseNonShardKey.containsKey(columnName)) {
488-
return false;
489-
}
490-
if (!columnType.equalsIgnoreCase(caseNonShardKey.get(columnName).name())) {
491-
return false;
492-
}
493-
} else {
494-
if (!columnName.equalsIgnoreCase(JSON_COLUMN)) {
495-
return false;
496-
}
497-
if (!columnType.equalsIgnoreCase("JSON")) {
498-
return false;
499-
}
500-
}
501-
}
502-
503-
//check order of the shard keys are sane
504-
ArrayValue shards = jsonSchema.get("shardKey").asArray();
505-
if (shards.size() != shardKeys.size()) {
506-
return false;
507-
}
508-
int i = 0;
509-
for (String key : shardKeys.keySet()) {
510-
if (!key.equalsIgnoreCase(shards.get(i).getString())) {
511-
return false;
512-
}
513-
i++;
514-
}
515-
516-
//check order of non shard keys are same
517-
ArrayValue primaryKeys = jsonSchema.get("primaryKey").asArray();
518-
for (String key : nonShardKeys.keySet()) {
519-
if (!key.equalsIgnoreCase(primaryKeys.get(i).getString())) {
520-
return false;
521-
}
522-
i++;
523-
}
524-
525-
//check identity same
526-
FieldValue identity = jsonSchema.get("identity");
527-
if (identity != null && !entityInformation.isAutoGeneratedId()) {
528-
return false;
529-
} else if (identity == null && entityInformation.isAutoGeneratedId() &&
530-
entityInformation.getIdNosqlType() != FieldValue.Type.STRING) {
531-
return false;
532-
}
533-
534-
//TTL warning
535-
FieldValue ttlValue = jsonSchema.get("ttl");
536-
TimeToLive ttl = entityInformation.getTtl();
537-
//TTL is present in database but not in the entity
538-
if (ttlValue != null && ttl != null &&
539-
!ttl.toString().equalsIgnoreCase(ttlValue.getString())) {
540-
LOG.warn("TTL of the table in database is different from the TTL " +
541-
"of the entity " + entityInformation.getJavaType().getName());
542-
} else if (ttlValue == null && ttl != null && ttl.getValue() != 0) {
543-
//TTL is present in entity but not in the database
544-
LOG.warn("TTL of the table in database is different from the TTL " +
545-
"of the entity " + entityInformation.getJavaType().getName());
546-
}
547-
return true;
548-
}
549567
}

src/main/java/com/oracle/nosql/spring/data/core/ReactiveNosqlTemplate.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,16 @@ public Mono<Boolean> createTableIfNotExists(
7979
NosqlEntityInformation<?, ?> entityInformation) {
8080
Assert.notNull(entityInformation, "Entity information should not be null");
8181

82-
return Mono.just(doCreateTableIfNotExists(entityInformation));
82+
String ddl = getCreateTableDDL(entityInformation);
83+
try {
84+
doCheckExistingTable(entityInformation);
85+
} catch (IllegalArgumentException iae) {
86+
String msg = String.format("Error executing DDL '%s': Table %s " +
87+
"exists but definitions do not match : %s" , ddl,
88+
entityInformation.getTableName(), iae.getMessage());
89+
throw new IllegalArgumentException(msg, iae);
90+
}
91+
return Mono.just(doCreateTable(entityInformation, ddl));
8392
}
8493

8594
/**

src/test/java/com/oracle/nosql/spring/data/test/composite/TestTableCreation.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ public void testCompositeEntityWithRepeatingOrder() {
147147
try {
148148
NosqlEntityInformation<?, ?> entityInformation =
149149
template.getNosqlEntityInformation(domainClass);
150-
template.createTableIfNotExists(entityInformation);
151150
fail("Expecting IllegalArgumentException but didn't get");
152151
} catch (IllegalArgumentException ignored) {
153152

@@ -160,7 +159,6 @@ public void testCompositeEntityWithMissingOrder() {
160159
try {
161160
NosqlEntityInformation<?, ?> entityInformation =
162161
template.getNosqlEntityInformation(domainClass);
163-
template.createTableIfNotExists(entityInformation);
164162
fail("Expecting IllegalArgumentException but didn't get");
165163
} catch (IllegalArgumentException ignored) {
166164

0 commit comments

Comments
 (0)