Skip to content

Commit f1a935d

Browse files
committed
Fix
1 parent 754d7ee commit f1a935d

File tree

15 files changed

+513
-113
lines changed

15 files changed

+513
-113
lines changed

core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminIntegrationTest.java

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
11
package com.scalar.db.storage.jdbc;
22

3+
import static org.assertj.core.api.Assertions.assertThat;
34
import static org.assertj.core.api.Assertions.assertThatCode;
5+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
6+
import static org.assertj.core.api.Assertions.catchThrowable;
47

8+
import com.scalar.db.api.DistributedStorage;
59
import com.scalar.db.api.DistributedStorageAdminIntegrationTestBase;
10+
import com.scalar.db.api.Put;
11+
import com.scalar.db.api.PutBuilder;
12+
import com.scalar.db.api.Result;
13+
import com.scalar.db.api.Scan;
14+
import com.scalar.db.api.Scanner;
615
import com.scalar.db.api.TableMetadata;
716
import com.scalar.db.config.DatabaseConfig;
817
import com.scalar.db.exception.storage.ExecutionException;
918
import com.scalar.db.io.DataType;
19+
import com.scalar.db.io.Key;
1020
import com.scalar.db.util.AdminTestUtils;
21+
import java.io.IOException;
22+
import java.nio.charset.StandardCharsets;
23+
import java.time.Instant;
24+
import java.time.LocalDate;
25+
import java.time.LocalDateTime;
26+
import java.time.LocalTime;
27+
import java.time.ZoneId;
28+
import java.time.ZoneOffset;
29+
import java.util.List;
1130
import java.util.Map;
1231
import java.util.Properties;
1332
import org.junit.jupiter.api.Test;
@@ -37,11 +56,28 @@ protected boolean isCreateIndexOnTextColumnEnabled() {
3756
return !JdbcTestUtils.isDb2(rdbEngine);
3857
}
3958

59+
@SuppressWarnings("unused")
60+
private boolean isOracle() {
61+
return JdbcEnv.isOracle();
62+
}
63+
4064
@SuppressWarnings("unused")
4165
private boolean isDb2() {
4266
return JdbcEnv.isDb2();
4367
}
4468

69+
@SuppressWarnings("unused")
70+
private boolean isSqlite() {
71+
return JdbcEnv.isSqlite();
72+
}
73+
74+
@SuppressWarnings("unused")
75+
private boolean isColumnTypeConversionToTextNotFullySupported() {
76+
return JdbcTestUtils.isDb2(rdbEngine)
77+
|| JdbcTestUtils.isOracle(rdbEngine)
78+
|| JdbcTestUtils.isSqlite(rdbEngine);
79+
}
80+
4581
@Test
4682
@Override
4783
@DisabledIf("isDb2")
@@ -97,6 +133,228 @@ public void renameColumn_Db2_ForPrimaryOrIndexKeyColumn_ShouldThrowUnsupportedOp
97133
}
98134
}
99135

136+
@Test
137+
@Override
138+
@DisabledIf("isColumnTypeConversionToTextNotFullySupported")
139+
public void
140+
alterColumnType_AlterColumnTypeFromEachExistingDataTypeToText_ShouldAlterColumnTypesCorrectly()
141+
throws ExecutionException, IOException {
142+
super
143+
.alterColumnType_AlterColumnTypeFromEachExistingDataTypeToText_ShouldAlterColumnTypesCorrectly();
144+
}
145+
146+
@Test
147+
@EnabledIf("isOracle")
148+
public void
149+
alterColumnType_Oracle_AlterColumnTypeFromEachExistingDataTypeToText_ShouldThrowUnsupportedOperationException()
150+
throws ExecutionException {
151+
try (DistributedStorage storage = storageFactory.getStorage()) {
152+
// Arrange
153+
Map<String, String> options = getCreationOptions();
154+
TableMetadata.Builder currentTableMetadataBuilder =
155+
TableMetadata.newBuilder()
156+
.addColumn(getColumnName1(), DataType.INT)
157+
.addColumn(getColumnName2(), DataType.INT)
158+
.addColumn(getColumnName3(), DataType.INT)
159+
.addColumn(getColumnName4(), DataType.BIGINT)
160+
.addColumn(getColumnName5(), DataType.FLOAT)
161+
.addColumn(getColumnName6(), DataType.DOUBLE)
162+
.addColumn(getColumnName7(), DataType.TEXT)
163+
.addColumn(getColumnName8(), DataType.BLOB)
164+
.addColumn(getColumnName9(), DataType.DATE)
165+
.addColumn(getColumnName10(), DataType.TIME)
166+
.addPartitionKey(getColumnName1())
167+
.addClusteringKey(getColumnName2(), Scan.Ordering.Order.ASC);
168+
if (isTimestampTypeSupported()) {
169+
currentTableMetadataBuilder
170+
.addColumn(getColumnName11(), DataType.TIMESTAMP)
171+
.addColumn(getColumnName12(), DataType.TIMESTAMPTZ);
172+
}
173+
TableMetadata currentTableMetadata = currentTableMetadataBuilder.build();
174+
admin.createTable(getNamespace1(), getTable4(), currentTableMetadata, options);
175+
PutBuilder.Buildable put =
176+
Put.newBuilder()
177+
.namespace(getNamespace1())
178+
.table(getTable4())
179+
.partitionKey(Key.ofInt(getColumnName1(), 1))
180+
.clusteringKey(Key.ofInt(getColumnName2(), 2))
181+
.intValue(getColumnName3(), 1)
182+
.bigIntValue(getColumnName4(), 2L)
183+
.floatValue(getColumnName5(), 3.0f)
184+
.doubleValue(getColumnName6(), 4.0d)
185+
.textValue(getColumnName7(), "5")
186+
.blobValue(getColumnName8(), "6".getBytes(StandardCharsets.UTF_8))
187+
.dateValue(getColumnName9(), LocalDate.now(ZoneId.of("UTC")))
188+
.timeValue(getColumnName10(), LocalTime.now(ZoneId.of("UTC")));
189+
if (isTimestampTypeSupported()) {
190+
put.timestampValue(getColumnName11(), LocalDateTime.now(ZoneOffset.UTC));
191+
put.timestampTZValue(getColumnName12(), Instant.now());
192+
}
193+
storage.put(put.build());
194+
storage.close();
195+
196+
// Act Assert
197+
assertThatThrownBy(
198+
() ->
199+
admin.alterColumnType(
200+
getNamespace1(), getTable4(), getColumnName3(), DataType.TEXT))
201+
.isInstanceOf(UnsupportedOperationException.class);
202+
assertThatThrownBy(
203+
() ->
204+
admin.alterColumnType(
205+
getNamespace1(), getTable4(), getColumnName4(), DataType.TEXT))
206+
.isInstanceOf(UnsupportedOperationException.class);
207+
assertThatThrownBy(
208+
() ->
209+
admin.alterColumnType(
210+
getNamespace1(), getTable4(), getColumnName5(), DataType.TEXT))
211+
.isInstanceOf(UnsupportedOperationException.class);
212+
assertThatThrownBy(
213+
() ->
214+
admin.alterColumnType(
215+
getNamespace1(), getTable4(), getColumnName6(), DataType.TEXT))
216+
.isInstanceOf(UnsupportedOperationException.class);
217+
admin.alterColumnType(getNamespace1(), getTable4(), getColumnName7(), DataType.TEXT);
218+
assertThatThrownBy(
219+
() ->
220+
admin.alterColumnType(
221+
getNamespace1(), getTable4(), getColumnName8(), DataType.TEXT))
222+
.isInstanceOf(UnsupportedOperationException.class);
223+
assertThatThrownBy(
224+
() ->
225+
admin.alterColumnType(
226+
getNamespace1(), getTable4(), getColumnName9(), DataType.TEXT))
227+
.isInstanceOf(UnsupportedOperationException.class);
228+
assertThatThrownBy(
229+
() ->
230+
admin.alterColumnType(
231+
getNamespace1(), getTable4(), getColumnName10(), DataType.TEXT))
232+
.isInstanceOf(UnsupportedOperationException.class);
233+
if (isTimestampTypeSupported()) {
234+
assertThatThrownBy(
235+
() ->
236+
admin.alterColumnType(
237+
getNamespace1(), getTable4(), getColumnName11(), DataType.TEXT))
238+
.isInstanceOf(UnsupportedOperationException.class);
239+
assertThatThrownBy(
240+
() ->
241+
admin.alterColumnType(
242+
getNamespace1(), getTable4(), getColumnName12(), DataType.TEXT))
243+
.isInstanceOf(UnsupportedOperationException.class);
244+
}
245+
} finally {
246+
admin.dropTable(getNamespace1(), getTable4(), true);
247+
}
248+
}
249+
250+
@Test
251+
@Override
252+
@DisabledIf("isOracle")
253+
public void alterColumnType_WideningConversion_ShouldAlterColumnTypesCorrectly()
254+
throws ExecutionException, IOException {
255+
super.alterColumnType_WideningConversion_ShouldAlterColumnTypesCorrectly();
256+
}
257+
258+
@Test
259+
@EnabledIf("isOracle")
260+
public void alterColumnType_Oracle_WideningConversion_ShouldAlterColumnTypesCorrectly()
261+
throws ExecutionException, IOException {
262+
try {
263+
// Arrange
264+
Map<String, String> options = getCreationOptions();
265+
TableMetadata.Builder currentTableMetadataBuilder =
266+
TableMetadata.newBuilder()
267+
.addColumn(getColumnName1(), DataType.INT)
268+
.addColumn(getColumnName2(), DataType.INT)
269+
.addColumn(getColumnName3(), DataType.INT)
270+
.addColumn(getColumnName4(), DataType.FLOAT)
271+
.addPartitionKey(getColumnName1())
272+
.addClusteringKey(getColumnName2(), Scan.Ordering.Order.ASC);
273+
TableMetadata currentTableMetadata = currentTableMetadataBuilder.build();
274+
admin.createTable(getNamespace1(), getTable4(), currentTableMetadata, options);
275+
DistributedStorage storage = storageFactory.getStorage();
276+
int expectedColumn3Value = 1;
277+
float expectedColumn4Value = 4.0f;
278+
279+
PutBuilder.Buildable put =
280+
Put.newBuilder()
281+
.namespace(getNamespace1())
282+
.table(getTable4())
283+
.partitionKey(Key.ofInt(getColumnName1(), 1))
284+
.clusteringKey(Key.ofInt(getColumnName2(), 2))
285+
.intValue(getColumnName3(), expectedColumn3Value)
286+
.floatValue(getColumnName4(), expectedColumn4Value);
287+
storage.put(put.build());
288+
storage.close();
289+
290+
// Act
291+
admin.alterColumnType(getNamespace1(), getTable4(), getColumnName3(), DataType.BIGINT);
292+
Throwable exception =
293+
catchThrowable(
294+
() ->
295+
admin.alterColumnType(
296+
getNamespace1(), getTable4(), getColumnName4(), DataType.DOUBLE));
297+
298+
// Assert
299+
assertThat(exception).isInstanceOf(UnsupportedOperationException.class);
300+
TableMetadata.Builder expectedTableMetadataBuilder =
301+
TableMetadata.newBuilder()
302+
.addColumn(getColumnName1(), DataType.INT)
303+
.addColumn(getColumnName2(), DataType.INT)
304+
.addColumn(getColumnName3(), DataType.BIGINT)
305+
.addColumn(getColumnName4(), DataType.FLOAT)
306+
.addPartitionKey(getColumnName1())
307+
.addClusteringKey(getColumnName2(), Scan.Ordering.Order.ASC);
308+
TableMetadata expectedTableMetadata = expectedTableMetadataBuilder.build();
309+
assertThat(admin.getTableMetadata(getNamespace1(), getTable4()))
310+
.isEqualTo(expectedTableMetadata);
311+
storage = storageFactory.getStorage();
312+
Scan scan =
313+
Scan.newBuilder()
314+
.namespace(getNamespace1())
315+
.table(getTable4())
316+
.partitionKey(Key.ofInt(getColumnName1(), 1))
317+
.build();
318+
try (Scanner scanner = storage.scan(scan)) {
319+
List<Result> results = scanner.all();
320+
assertThat(results).hasSize(1);
321+
Result result = results.get(0);
322+
assertThat(result.getBigInt(getColumnName3())).isEqualTo(expectedColumn3Value);
323+
}
324+
storage.close();
325+
} finally {
326+
admin.dropTable(getNamespace1(), getTable4(), true);
327+
}
328+
}
329+
330+
@Test
331+
@EnabledIf("isSqlite")
332+
public void alterColumnType_Sqlite_AlterColumnType_ShouldThrowUnsupportedOperationException()
333+
throws ExecutionException {
334+
try {
335+
// Arrange
336+
Map<String, String> options = getCreationOptions();
337+
TableMetadata.Builder currentTableMetadataBuilder =
338+
TableMetadata.newBuilder()
339+
.addColumn(getColumnName1(), DataType.INT)
340+
.addColumn(getColumnName2(), DataType.INT)
341+
.addColumn(getColumnName3(), DataType.INT)
342+
.addPartitionKey(getColumnName1())
343+
.addClusteringKey(getColumnName2(), Scan.Ordering.Order.ASC);
344+
TableMetadata currentTableMetadata = currentTableMetadataBuilder.build();
345+
admin.createTable(getNamespace1(), getTable4(), currentTableMetadata, options);
346+
347+
// Act Assert
348+
assertThatThrownBy(
349+
() ->
350+
admin.alterColumnType(
351+
getNamespace1(), getTable4(), getColumnName3(), DataType.TEXT))
352+
.isInstanceOf(UnsupportedOperationException.class);
353+
} finally {
354+
admin.dropTable(getNamespace1(), getTable4(), true);
355+
}
356+
}
357+
100358
@Override
101359
protected boolean isIndexOnBlobColumnSupported() {
102360
return !JdbcTestUtils.isDb2(rdbEngine);

core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcEnv.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ public static Properties getPropertiesForNormalUser(String testName) {
5151
return properties;
5252
}
5353

54+
public static boolean isOracle() {
55+
return System.getProperty(PROP_JDBC_URL, DEFAULT_JDBC_URL).startsWith("jdbc:oracle:");
56+
}
57+
5458
public static boolean isSqlite() {
5559
return System.getProperty(PROP_JDBC_URL, DEFAULT_JDBC_URL).startsWith("jdbc:sqlite:");
5660
}

core/src/main/java/com/scalar/db/common/CommonDistributedStorageAdmin.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ public void alterColumnType(
383383
if (currentColumnType == newColumnType) {
384384
return;
385385
}
386-
if (!ScalarDbUtils.isTypeConversionSupported(currentColumnType, newColumnType)) {
386+
if (!isTypeConversionSupported(currentColumnType, newColumnType)) {
387387
throw new IllegalArgumentException(
388388
CoreError.INVALID_COLUMN_TYPE_CONVERSION.buildMessage(
389389
currentColumnType, newColumnType, columnName));
@@ -522,4 +522,29 @@ public StorageInfo getStorageInfo(String namespace) throws ExecutionException {
522522
public void close() {
523523
admin.close();
524524
}
525+
526+
private boolean isTypeConversionSupported(DataType from, DataType to) {
527+
if (from == to) {
528+
return true;
529+
}
530+
switch (from) {
531+
case BOOLEAN:
532+
case BIGINT:
533+
case DOUBLE:
534+
case BLOB:
535+
case DATE:
536+
case TIME:
537+
case TIMESTAMP:
538+
case TIMESTAMPTZ:
539+
return to == DataType.TEXT;
540+
case INT:
541+
return to == DataType.BIGINT || to == DataType.TEXT;
542+
case FLOAT:
543+
return to == DataType.DOUBLE || to == DataType.TEXT;
544+
case TEXT:
545+
return false;
546+
default:
547+
throw new AssertionError("Unknown data type: " + from);
548+
}
549+
}
525550
}

core/src/main/java/com/scalar/db/common/CoreError.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,12 @@ public enum CoreError implements ScalarDbError {
790790
"SQLite does not support the altering column type feature",
791791
"",
792792
""),
793+
JDBC_UNSUPPORTED_COLUMN_TYPE_CONVERSION(
794+
Category.USER_ERROR,
795+
"0237",
796+
"The storage does not support column type conversion from %s to %s. Column: %s",
797+
"",
798+
""),
793799

794800
//
795801
// Errors for the concurrency error category

0 commit comments

Comments
 (0)