You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2022/06/22 11:10:07 UTC
[ignite-3] branch main updated: IGNITE-17052 Java thin: Implement query metadata (#891)
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 4119f80b6 IGNITE-17052 Java thin: Implement query metadata (#891)
4119f80b6 is described below
commit 4119f80b6ddcb56af522ba61f038b82c64382da7
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Wed Jun 22 14:10:01 2022 +0300
IGNITE-17052 Java thin: Implement query metadata (#891)
* Pass result set metadata to the client.
* Optimize row serialization using known column types.
---
.../internal/sql/SqlColumnTypeConverter.java | 104 +++++++++++++
.../client/proto/ClientMessageUnpacker.java | 39 +++++
.../client/proto/ClientSqlColumnTypeConverter.java | 165 +++++++++++++++++++++
.../proto/ClientMessagePackerUnpackerTest.java | 32 ++++
.../proto/ClientSqlColumnTypeConverterTest.java | 38 +++++
.../client/handler/JdbcQueryEventHandlerImpl.java | 4 +-
.../handler/requests/sql/ClientSqlCommon.java | 109 +++++++++++++-
.../requests/sql/ClientSqlExecuteRequest.java | 88 ++++++++---
.../internal/client/sql/ClientAsyncResultSet.java | 77 +++++++++-
.../internal/client/sql/ClientColumnMetadata.java | 52 ++++---
.../internal/client/sql/ClientColumnOrigin.java | 85 +++++++++++
.../client/sql/ClientResultSetMetadata.java | 3 +-
.../ignite/internal/client/sql/ClientSession.java | 10 +-
.../ignite/internal/client/sql/ClientSqlRow.java | 20 ++-
.../org/apache/ignite/client/ClientSqlTest.java | 124 ++++++++++++++--
.../ignite/client/fakes/FakeAsyncResultSet.java | 103 +++++++++++--
.../ignite/client/fakes/FakeColumnMetadata.java | 45 ++++--
.../runner/app/client/ItThinClientSqlTest.java | 37 ++++-
.../sql/api/ItSqlClientAsynchronousApiTest.java | 12 --
.../internal/sql/engine/util/QueryChecker.java | 3 +-
.../internal/sql/api/ColumnMetadataImpl.java | 4 +-
.../ignite/internal/sql/engine/util/Commons.java | 71 ---------
22 files changed, 1027 insertions(+), 198 deletions(-)
diff --git a/modules/api/src/main/java/org/apache/ignite/internal/sql/SqlColumnTypeConverter.java b/modules/api/src/main/java/org/apache/ignite/internal/sql/SqlColumnTypeConverter.java
new file mode 100644
index 000000000..dadf62d4d
--- /dev/null
+++ b/modules/api/src/main/java/org/apache/ignite/internal/sql/SqlColumnTypeConverter.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.ignite.internal.sql;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.BitSet;
+import java.util.UUID;
+import org.apache.ignite.sql.SqlColumnType;
+
+/**
+ * Column type converter.
+ */
+public class SqlColumnTypeConverter {
+ /**
+ * Column type to Java class.
+ */
+ public static Class<?> columnTypeToClass(SqlColumnType type) {
+ assert type != null;
+
+ switch (type) {
+ case BOOLEAN:
+ return Boolean.class;
+
+ case INT8:
+ return Byte.class;
+
+ case INT16:
+ return Short.class;
+
+ case INT32:
+ return Integer.class;
+
+ case INT64:
+ return Long.class;
+
+ case FLOAT:
+ return Float.class;
+
+ case DOUBLE:
+ return Double.class;
+
+ case NUMBER:
+ return BigInteger.class;
+
+ case DECIMAL:
+ return BigDecimal.class;
+
+ case UUID:
+ return UUID.class;
+
+ case STRING:
+ return String.class;
+
+ case BYTE_ARRAY:
+ return byte[].class;
+
+ case BITMASK:
+ return BitSet.class;
+
+ case DATE:
+ return LocalDate.class;
+
+ case TIME:
+ return LocalTime.class;
+
+ case DATETIME:
+ return LocalDateTime.class;
+
+ case TIMESTAMP:
+ return Instant.class;
+
+ case PERIOD:
+ return Period.class;
+
+ case DURATION:
+ return Duration.class;
+
+ default:
+ throw new IllegalArgumentException("Unsupported type " + type);
+ }
+ }
+}
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
index 973dca047..3201a9a75 100644
--- a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientMessageUnpacker.java
@@ -145,6 +145,45 @@ public class ClientMessageUnpacker implements AutoCloseable {
}
}
+ /**
+ * Reads an int.
+ *
+ * @param defaultValue Default value to return when type is not from int family.
+ * @return the int value.
+ * @throws MessageTypeException when value is not MessagePack Integer type.
+ */
+ public int tryUnpackInt(int defaultValue) {
+ assert refCnt > 0 : "Unpacker is closed";
+
+ byte code = buf.readByte();
+
+ if (Code.isFixInt(code)) {
+ return code;
+ }
+
+ switch (code) {
+ case Code.UINT8:
+ return buf.readUnsignedByte();
+
+ case Code.INT8:
+ return buf.readByte();
+
+ case Code.UINT16:
+ return buf.readUnsignedShort();
+
+ case Code.INT16:
+ return buf.readShort();
+
+ case Code.UINT32:
+ case Code.INT32:
+ return buf.readInt();
+
+ default:
+ buf.readerIndex(buf.readerIndex() - 1);
+ return defaultValue;
+ }
+ }
+
/**
* Reads a string.
*
diff --git a/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientSqlColumnTypeConverter.java b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientSqlColumnTypeConverter.java
new file mode 100644
index 000000000..cd2030d70
--- /dev/null
+++ b/modules/client-common/src/main/java/org/apache/ignite/internal/client/proto/ClientSqlColumnTypeConverter.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.ignite.internal.client.proto;
+
+import org.apache.ignite.sql.SqlColumnType;
+
+/**
+ * SQL column type utils.
+ */
+public class ClientSqlColumnTypeConverter {
+ /**
+ * Converts column type to wire code.
+ *
+ * @param columnType Column type.
+ * @return Wire code.
+ */
+ public static int columnTypeToOrdinal(SqlColumnType columnType) {
+ switch (columnType) {
+ case BOOLEAN:
+ return 0;
+
+ case INT8:
+ return 1;
+
+ case INT16:
+ return 2;
+
+ case INT32:
+ return 3;
+
+ case INT64:
+ return 4;
+
+ case FLOAT:
+ return 5;
+
+ case DOUBLE:
+ return 6;
+
+ case DECIMAL:
+ return 7;
+
+ case DATE:
+ return 8;
+
+ case TIME:
+ return 9;
+
+ case DATETIME:
+ return 10;
+
+ case TIMESTAMP:
+ return 11;
+
+ case UUID:
+ return 12;
+
+ case BITMASK:
+ return 13;
+
+ case STRING:
+ return 14;
+
+ case BYTE_ARRAY:
+ return 15;
+
+ case PERIOD:
+ return 16;
+
+ case DURATION:
+ return 17;
+
+ case NUMBER:
+ return 18;
+
+ default:
+ throw new IllegalArgumentException("Invalid column type: " + columnType);
+ }
+ }
+
+ /**
+ * Converts wire type code to column type.
+ *
+ * @param ordinal Type code.
+ * @return Column type.
+ */
+ public static SqlColumnType ordinalToColumnType(int ordinal) {
+ switch (ordinal) {
+ case 0:
+ return SqlColumnType.BOOLEAN;
+
+ case 1:
+ return SqlColumnType.INT8;
+
+ case 2:
+ return SqlColumnType.INT16;
+
+ case 3:
+ return SqlColumnType.INT32;
+
+ case 4:
+ return SqlColumnType.INT64;
+
+ case 5:
+ return SqlColumnType.FLOAT;
+
+ case 6:
+ return SqlColumnType.DOUBLE;
+
+ case 7:
+ return SqlColumnType.DECIMAL;
+
+ case 8:
+ return SqlColumnType.DATE;
+
+ case 9:
+ return SqlColumnType.TIME;
+
+ case 10:
+ return SqlColumnType.DATETIME;
+
+ case 11:
+ return SqlColumnType.TIMESTAMP;
+
+ case 12:
+ return SqlColumnType.UUID;
+
+ case 13:
+ return SqlColumnType.BITMASK;
+
+ case 14:
+ return SqlColumnType.STRING;
+
+ case 15:
+ return SqlColumnType.BYTE_ARRAY;
+
+ case 16:
+ return SqlColumnType.PERIOD;
+
+ case 17:
+ return SqlColumnType.DURATION;
+
+ case 18:
+ return SqlColumnType.NUMBER;
+
+ default:
+ throw new IllegalArgumentException("Invalid column type code: " + ordinal);
+ }
+ }
+}
diff --git a/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
index db97c9c35..6346a8de1 100644
--- a/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
+++ b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientMessagePackerUnpackerTest.java
@@ -346,4 +346,36 @@ public class ClientMessagePackerUnpackerTest {
}
}
}
+
+ @Test
+ public void testTryUnpackInt() {
+ try (var packer = new ClientMessagePacker(PooledByteBufAllocator.DEFAULT.directBuffer())) {
+ packer.packInt(1);
+ packer.packInt(Byte.MAX_VALUE);
+ packer.packInt(Short.MAX_VALUE);
+ packer.packInt(Integer.MAX_VALUE);
+ packer.packNoValue();
+ packer.packString("s");
+
+ var buf = packer.getBuffer();
+
+ byte[] data = new byte[buf.readableBytes()];
+ buf.readBytes(data);
+
+ try (var unpacker = new ClientMessageUnpacker(Unpooled.wrappedBuffer(data))) {
+ unpacker.skipValues(4);
+
+ assertEquals(1, unpacker.tryUnpackInt(-1));
+ assertEquals(Byte.MAX_VALUE, unpacker.tryUnpackInt(-1));
+ assertEquals(Short.MAX_VALUE, unpacker.tryUnpackInt(-1));
+ assertEquals(Integer.MAX_VALUE, unpacker.tryUnpackInt(-1));
+
+ assertEquals(-1, unpacker.tryUnpackInt(-1));
+ assertTrue(unpacker.tryUnpackNoValue());
+
+ assertEquals(-2, unpacker.tryUnpackInt(-2));
+ assertEquals("s", unpacker.unpackString());
+ }
+ }
+ }
}
diff --git a/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientSqlColumnTypeConverterTest.java b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientSqlColumnTypeConverterTest.java
new file mode 100644
index 000000000..3655ce46a
--- /dev/null
+++ b/modules/client-common/src/test/java/org/apache/ignite/internal/client/proto/ClientSqlColumnTypeConverterTest.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.ignite.internal.client.proto;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.apache.ignite.sql.SqlColumnType;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests column type converter.
+ */
+public class ClientSqlColumnTypeConverterTest {
+ @Test
+ public void testConvertAllTypes() {
+ for (SqlColumnType columnType : SqlColumnType.values()) {
+ int ordinal = ClientSqlColumnTypeConverter.columnTypeToOrdinal(columnType);
+ SqlColumnType resColumnType = ClientSqlColumnTypeConverter.ordinalToColumnType(ordinal);
+
+ assertEquals(columnType, resColumnType);
+ }
+ }
+}
diff --git a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
index f055b7d21..ea5d18201 100644
--- a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
+++ b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/JdbcQueryEventHandlerImpl.java
@@ -57,6 +57,7 @@ import org.apache.ignite.internal.jdbc.proto.event.QueryFetchRequest;
import org.apache.ignite.internal.jdbc.proto.event.QueryFetchResult;
import org.apache.ignite.internal.jdbc.proto.event.QuerySingleResult;
import org.apache.ignite.internal.jdbc.proto.event.Response;
+import org.apache.ignite.internal.sql.SqlColumnTypeConverter;
import org.apache.ignite.internal.sql.engine.AsyncSqlCursor;
import org.apache.ignite.internal.sql.engine.QueryContext;
import org.apache.ignite.internal.sql.engine.QueryProcessor;
@@ -64,7 +65,6 @@ import org.apache.ignite.internal.sql.engine.QueryValidator;
import org.apache.ignite.internal.sql.engine.exec.QueryValidationException;
import org.apache.ignite.internal.sql.engine.prepare.QueryPlan;
import org.apache.ignite.internal.sql.engine.prepare.QueryPlan.Type;
-import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.lang.IgniteInternalException;
import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.ColumnMetadata.ColumnOrigin;
@@ -336,7 +336,7 @@ public class JdbcQueryEventHandlerImpl implements JdbcQueryEventHandler {
schemaName,
tblName,
colName,
- Commons.columnTypeToClass(fldMeta.type()),
+ SqlColumnTypeConverter.columnTypeToClass(fldMeta.type()),
fldMeta.precision(),
fldMeta.scale(),
fldMeta.nullable()
diff --git a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCommon.java b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCommon.java
index 697c7f200..f5797ad66 100644
--- a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCommon.java
+++ b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlCommon.java
@@ -17,9 +17,14 @@
package org.apache.ignite.client.handler.requests.sql;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Period;
import java.util.List;
import org.apache.ignite.internal.client.proto.ClientMessagePacker;
import org.apache.ignite.sql.ColumnMetadata;
+import org.apache.ignite.sql.ResultSetMetadata;
+import org.apache.ignite.sql.SqlColumnType;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.async.AsyncResultSet;
@@ -28,14 +33,16 @@ import org.apache.ignite.sql.async.AsyncResultSet;
*/
class ClientSqlCommon {
static void packCurrentPage(ClientMessagePacker out, AsyncResultSet asyncResultSet) {
- List<ColumnMetadata> cols = asyncResultSet.metadata().columns();
+ ResultSetMetadata meta = asyncResultSet.metadata();
+ assert meta != null : "Metadata can't be null when row set is present.";
+
+ List<ColumnMetadata> cols = meta.columns();
out.packArrayHeader(asyncResultSet.currentPageSize());
for (SqlRow row : asyncResultSet.currentPage()) {
for (int i = 0; i < cols.size(); i++) {
- // TODO: IGNITE-17052 pack only the value according to the known type.
- out.packObjectWithType(row.value(i));
+ packValue(out, cols.get(i).type(), row, i);
}
}
@@ -43,4 +50,100 @@ class ClientSqlCommon {
asyncResultSet.closeAsync();
}
}
+
+ private static void packValue(ClientMessagePacker out, SqlColumnType colType, SqlRow row, int idx) {
+ if (row.value(idx) == null) {
+ out.packNil();
+ return;
+ }
+
+ switch (colType) {
+ case BOOLEAN:
+ out.packBoolean(row.value(idx));
+ break;
+
+ case INT8:
+ out.packByte(row.byteValue(idx));
+ break;
+
+ case INT16:
+ out.packShort(row.shortValue(idx));
+ break;
+
+ case INT32:
+ out.packInt(row.intValue(idx));
+ break;
+
+ case INT64:
+ out.packLong(row.longValue(idx));
+ break;
+
+ case FLOAT:
+ out.packFloat(row.floatValue(idx));
+ break;
+
+ case DOUBLE:
+ out.packDouble(row.doubleValue(idx));
+ break;
+
+ case DECIMAL:
+ out.packDecimal(row.value(idx));
+ break;
+
+ case DATE:
+ out.packDate(row.dateValue(idx));
+ break;
+
+ case TIME:
+ out.packTime(row.timeValue(idx));
+ break;
+
+ case DATETIME:
+ out.packDateTime(row.datetimeValue(idx));
+ break;
+
+ case TIMESTAMP:
+ out.packTimestamp(row.timestampValue(idx));
+ break;
+
+ case UUID:
+ out.packUuid(row.uuidValue(idx));
+ break;
+
+ case BITMASK:
+ out.packBitSet(row.bitmaskValue(idx));
+ break;
+
+ case STRING:
+ out.packString(row.stringValue(idx));
+ break;
+
+ case BYTE_ARRAY:
+ byte[] bytes = row.value(idx);
+ out.packBinaryHeader(bytes.length);
+ out.writePayload(bytes);
+ break;
+
+ case PERIOD:
+ Period period = row.value(idx);
+ out.packInt(period.getYears());
+ out.packInt(period.getMonths());
+ out.packInt(period.getDays());
+ break;
+
+ case DURATION:
+ Duration duration = row.value(idx);
+ out.packLong(duration.getSeconds());
+ out.packInt(duration.getNano());
+ break;
+
+ case NUMBER:
+ BigInteger number = row.value(idx);
+ out.packBigInteger(number);
+ break;
+
+ default:
+ throw new UnsupportedOperationException("Unsupported column type: " + colType);
+ }
+ }
}
diff --git a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
index f9997e47a..b213f87d0 100644
--- a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
+++ b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/sql/ClientSqlExecuteRequest.java
@@ -20,7 +20,9 @@ package org.apache.ignite.client.handler.requests.sql;
import static org.apache.ignite.client.handler.requests.sql.ClientSqlCommon.packCurrentPage;
import static org.apache.ignite.client.handler.requests.table.ClientTableCommon.readTx;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
@@ -28,11 +30,14 @@ import org.apache.ignite.client.handler.ClientResource;
import org.apache.ignite.client.handler.ClientResourceRegistry;
import org.apache.ignite.internal.client.proto.ClientMessagePacker;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
+import org.apache.ignite.internal.client.proto.ClientSqlColumnTypeConverter;
import org.apache.ignite.internal.util.ArrayUtils;
import org.apache.ignite.lang.IgniteInternalCheckedException;
import org.apache.ignite.lang.IgniteInternalException;
import org.apache.ignite.sql.ColumnMetadata;
+import org.apache.ignite.sql.ColumnMetadata.ColumnOrigin;
import org.apache.ignite.sql.IgniteSql;
+import org.apache.ignite.sql.ResultSetMetadata;
import org.apache.ignite.sql.Session;
import org.apache.ignite.sql.Session.SessionBuilder;
import org.apache.ignite.sql.Statement;
@@ -93,24 +98,7 @@ public class ClientSqlExecuteRequest {
out.packBoolean(asyncResultSet.wasApplied());
out.packLong(asyncResultSet.affectedRows());
- // Pack metadata.
- if (asyncResultSet.metadata() == null || asyncResultSet.metadata().columns() == null) {
- out.packArrayHeader(0);
- } else {
- List<ColumnMetadata> cols = asyncResultSet.metadata().columns();
- out.packArrayHeader(cols.size());
-
- for (int i = 0; i < cols.size(); i++) {
- ColumnMetadata col = cols.get(i);
- out.packString(col.name());
- out.packBoolean(col.nullable());
-
- // TODO: IGNITE-17052 Implement query metadata.
- // Ideally we only need the type code here.
- out.packString(col.valueClass().getName());
- out.packObjectWithType(null /*col.type()*/);
- }
- }
+ packMeta(out, asyncResultSet.metadata());
// Pack first page.
if (asyncResultSet.hasRowSet()) {
@@ -155,6 +143,10 @@ public class ClientSqlExecuteRequest {
}
private static Object[] readArguments(ClientMessageUnpacker in) {
+ if (in.tryUnpackNil()) {
+ return null;
+ }
+
int size = in.unpackArrayHeader();
if (size == 0) {
@@ -169,4 +161,64 @@ public class ClientSqlExecuteRequest {
return res;
}
+
+ private static void packMeta(ClientMessagePacker out, ResultSetMetadata meta) {
+ // TODO IGNITE-17179 metadata caching - avoid sending same meta over and over.
+ if (meta == null || meta.columns() == null) {
+ out.packArrayHeader(0);
+ return;
+ }
+
+ List<ColumnMetadata> cols = meta.columns();
+ out.packArrayHeader(cols.size());
+
+ // In many cases there are multiple columns from the same table.
+ // Schema is the same for all columns in most cases.
+ // When table or schema name was packed before, pack index instead of string.
+ Map<String, Integer> schemas = new HashMap<>();
+ Map<String, Integer> tables = new HashMap<>();
+
+ for (int i = 0; i < cols.size(); i++) {
+ ColumnMetadata col = cols.get(i);
+
+ out.packString(col.name());
+ out.packBoolean(col.nullable());
+ out.packInt(ClientSqlColumnTypeConverter.columnTypeToOrdinal(col.type()));
+ out.packInt(col.scale());
+ out.packInt(col.precision());
+
+ ColumnOrigin origin = col.origin();
+
+ if (origin == null) {
+ out.packBoolean(false);
+ continue;
+ }
+
+ out.packBoolean(true);
+
+ if (col.name().equals(origin.columnName())) {
+ out.packNil();
+ } else {
+ out.packString(origin.columnName());
+ }
+
+ Integer schemaIdx = schemas.get(origin.schemaName());
+
+ if (schemaIdx == null) {
+ schemas.put(origin.schemaName(), i);
+ out.packString(origin.schemaName());
+ } else {
+ out.packInt(schemaIdx);
+ }
+
+ Integer tableIdx = tables.get(origin.tableName());
+
+ if (tableIdx == null) {
+ tables.put(origin.tableName(), i);
+ out.packString(origin.tableName());
+ } else {
+ out.packInt(tableIdx);
+ }
+ }
+ }
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
index b643a8cd7..af037d75c 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientAsyncResultSet.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.client.sql;
+import java.time.Duration;
+import java.time.Period;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -28,6 +30,7 @@ import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
import org.apache.ignite.internal.client.proto.ClientOp;
import org.apache.ignite.sql.NoRowSetExpectedException;
import org.apache.ignite.sql.ResultSetMetadata;
+import org.apache.ignite.sql.SqlColumnType;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.async.AsyncResultSet;
import org.jetbrains.annotations.Nullable;
@@ -188,13 +191,81 @@ class ClientAsyncResultSet implements AsyncResultSet {
var row = new ArrayList<>(rowSize);
for (int j = 0; j < rowSize; j++) {
- // TODO: IGNITE-17052 Unpack according to metadata type.
- row.add(in.unpackObjectWithType());
+ var col = metadata.columns().get(j);
+ row.add(readValue(in, col.type()));
}
- res.add(new ClientSqlRow(row));
+ res.add(new ClientSqlRow(row, metadata));
}
rows = Collections.unmodifiableList(res);
}
+
+ private static Object readValue(ClientMessageUnpacker in, SqlColumnType colType) {
+ if (in.tryUnpackNil()) {
+ return null;
+ }
+
+ switch (colType) {
+ case BOOLEAN:
+ return in.unpackBoolean();
+
+ case INT8:
+ return in.unpackByte();
+
+ case INT16:
+ return in.unpackShort();
+
+ case INT32:
+ return in.unpackInt();
+
+ case INT64:
+ return in.unpackLong();
+
+ case FLOAT:
+ return in.unpackFloat();
+
+ case DOUBLE:
+ return in.unpackDouble();
+
+ case DECIMAL:
+ return in.unpackDecimal();
+
+ case DATE:
+ return in.unpackDate();
+
+ case TIME:
+ return in.unpackTime();
+
+ case DATETIME:
+ return in.unpackDateTime();
+
+ case TIMESTAMP:
+ return in.unpackTimestamp();
+
+ case UUID:
+ return in.unpackUuid();
+
+ case BITMASK:
+ return in.unpackBitSet();
+
+ case STRING:
+ return in.unpackString();
+
+ case BYTE_ARRAY:
+ return in.readPayload(in.unpackBinaryHeader());
+
+ case PERIOD:
+ return Period.of(in.unpackInt(), in.unpackInt(), in.unpackInt());
+
+ case DURATION:
+ return Duration.ofSeconds(in.unpackLong(), in.unpackInt());
+
+ case NUMBER:
+ return in.unpackBigInteger();
+
+ default:
+ throw new UnsupportedOperationException("Unsupported column type: " + colType);
+ }
+ }
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientColumnMetadata.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientColumnMetadata.java
index dd9dfaf7a..654d5a26c 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientColumnMetadata.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientColumnMetadata.java
@@ -17,8 +17,10 @@
package org.apache.ignite.internal.client.sql;
-import org.apache.ignite.client.IgniteClientException;
+import java.util.List;
import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
+import org.apache.ignite.internal.client.proto.ClientSqlColumnTypeConverter;
+import org.apache.ignite.internal.sql.SqlColumnTypeConverter;
import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.SqlColumnType;
@@ -29,29 +31,38 @@ public class ClientColumnMetadata implements ColumnMetadata {
/** Name. */
private final String name;
- /** Value class. */
- private final Class<?> valueClass;
-
/** Type. */
- private final Object type;
+ private final SqlColumnType type;
/** Nullable flag. */
private final boolean nullable;
+ /** Column precision. */
+ private final int precision;
+
+ /** Column scale. */
+ private final int scale;
+
+ /** Origin of the result's column. */
+ private final ColumnOrigin origin;
+
/**
* Constructor.
*
* @param unpacker Unpacker.
+ * @param prevColumns Previous columns.
*/
- public ClientColumnMetadata(ClientMessageUnpacker unpacker) {
- try {
- name = unpacker.unpackString();
- nullable = unpacker.unpackBoolean();
- valueClass = Class.forName(unpacker.unpackString());
- // TODO: IGNITE-17052 Unpack according to metadata type.
- type = unpacker.unpackObjectWithType();
- } catch (ClassNotFoundException e) {
- throw new IgniteClientException(e.getMessage(), e);
+ public ClientColumnMetadata(ClientMessageUnpacker unpacker, List<ColumnMetadata> prevColumns) {
+ name = unpacker.unpackString();
+ nullable = unpacker.unpackBoolean();
+ type = ClientSqlColumnTypeConverter.ordinalToColumnType(unpacker.unpackInt());
+ scale = unpacker.unpackInt();
+ precision = unpacker.unpackInt();
+
+ if (unpacker.unpackBoolean()) {
+ origin = new ClientColumnOrigin(unpacker, name, prevColumns);
+ } else {
+ origin = null;
}
}
@@ -64,27 +75,25 @@ public class ClientColumnMetadata implements ColumnMetadata {
/** {@inheritDoc} */
@Override
public Class<?> valueClass() {
- return valueClass;
+ return SqlColumnTypeConverter.columnTypeToClass(type);
}
/** {@inheritDoc} */
@Override
public SqlColumnType type() {
- return (SqlColumnType) type;
+ return type;
}
/** {@inheritDoc} */
@Override
public int precision() {
- // TODO: IGNITE-17052
- return -1;
+ return precision;
}
/** {@inheritDoc} */
@Override
public int scale() {
- // TODO: IGNITE-17052
- return -1;
+ return scale;
}
/** {@inheritDoc} */
@@ -96,7 +105,6 @@ public class ClientColumnMetadata implements ColumnMetadata {
/** {@inheritDoc} */
@Override
public ColumnOrigin origin() {
- // TODO: IGNITE-17052
- return null;
+ return origin;
}
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientColumnOrigin.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientColumnOrigin.java
new file mode 100644
index 000000000..e9050595b
--- /dev/null
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientColumnOrigin.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *
+ * http://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 org.apache.ignite.internal.client.sql;
+
+import java.util.List;
+import org.apache.ignite.internal.client.proto.ClientMessageUnpacker;
+import org.apache.ignite.internal.tostring.S;
+import org.apache.ignite.sql.ColumnMetadata;
+
+/**
+ * Column origin.
+ */
+public class ClientColumnOrigin implements ColumnMetadata.ColumnOrigin {
+ /** Schema name. */
+ private final String schemaName;
+
+ /** Table name. */
+ private final String tableName;
+
+ /** Column name. */
+ private final String columnName;
+
+ /**
+ * Constructor.
+ */
+ public ClientColumnOrigin(
+ ClientMessageUnpacker unpacker,
+ String cursorColumnName,
+ List<ColumnMetadata> prevColumns) {
+ this.columnName = unpacker.tryUnpackNil() ? cursorColumnName : unpacker.unpackString();
+
+ int schemaNameIdx = unpacker.tryUnpackInt(-1);
+
+ //noinspection ConstantConditions
+ this.schemaName = schemaNameIdx == -1
+ ? unpacker.unpackString()
+ : prevColumns.get(schemaNameIdx).origin().schemaName();
+
+ int tableNameIdx = unpacker.tryUnpackInt(-1);
+
+ //noinspection ConstantConditions
+ this.tableName = tableNameIdx == -1
+ ? unpacker.unpackString()
+ : prevColumns.get(tableNameIdx).origin().tableName();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String schemaName() {
+ return schemaName;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String tableName() {
+ return tableName;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String columnName() {
+ return columnName;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return S.toString(ClientColumnOrigin.class, this);
+ }
+}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientResultSetMetadata.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientResultSetMetadata.java
index a6d01bfc3..7b81945e3 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientResultSetMetadata.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientResultSetMetadata.java
@@ -43,12 +43,13 @@ class ClientResultSetMetadata implements ResultSetMetadata {
*/
public ClientResultSetMetadata(ClientMessageUnpacker unpacker) {
var size = unpacker.unpackArrayHeader();
+ assert size > 0 : "ResultSetMetadata should not be empty.";
var columns = new ArrayList<ColumnMetadata>(size);
columnIndices = new HashMap<>(size);
for (int i = 0; i < size; i++) {
- ClientColumnMetadata column = new ClientColumnMetadata(unpacker);
+ ClientColumnMetadata column = new ClientColumnMetadata(unpacker, columns);
columns.add(column);
columnIndices.put(column.name(), i);
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSession.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSession.java
index c0de66705..d6de231b9 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSession.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSession.java
@@ -114,15 +114,7 @@ public class ClientSession implements Session {
w.out().packObject(clientStatement.query());
w.out().packBoolean(clientStatement.prepared());
- if (arguments == null) {
- w.out().packArrayHeader(0);
- } else {
- w.out().packArrayHeader(arguments.length);
-
- for (int i = 0; i < arguments.length; i++) {
- w.out().packObjectWithType(arguments[i]);
- }
- }
+ w.out().packObjectArray(arguments);
}, r -> new ClientAsyncResultSet(r.clientChannel(), r.in()));
}
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSqlRow.java b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSqlRow.java
index 99f5865b2..e489cceda 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSqlRow.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/sql/ClientSqlRow.java
@@ -34,18 +34,27 @@ import org.jetbrains.annotations.NotNull;
/**
* Client SQL row.
*/
+@SuppressWarnings("unchecked")
public class ClientSqlRow implements SqlRow {
/** Row. */
private final List<Object> row;
+ /** Meta. */
+ private final ResultSetMetadata metadata;
+
/**
* Constructor.
*
* @param row Row.
+ * @param meta Meta.
*/
- ClientSqlRow(List<Object> row) {
+ public ClientSqlRow(List<Object> row, ResultSetMetadata meta) {
+ assert row != null;
+ assert meta != null;
+
//noinspection AssignmentOrReturnOfFieldWithMutableType
this.row = row;
+ this.metadata = meta;
}
/** {@inheritDoc} */
@@ -57,15 +66,13 @@ public class ClientSqlRow implements SqlRow {
/** {@inheritDoc} */
@Override
public String columnName(int columnIndex) {
- // TODO: IGNITE-17052
- throw new UnsupportedOperationException("Not implemented yet.");
+ return metadata.columns().get(columnIndex).name();
}
/** {@inheritDoc} */
@Override
public int columnIndex(@NotNull String columnName) {
- // TODO: IGNITE-17052
- throw new UnsupportedOperationException("Not implemented yet.");
+ return metadata.indexOf(columnName);
}
private int columnIndexChecked(@NotNull String columnName) {
@@ -282,7 +289,6 @@ public class ClientSqlRow implements SqlRow {
/** {@inheritDoc} */
@Override
public ResultSetMetadata metadata() {
- // TODO: IGNITE-17052
- throw new UnsupportedOperationException("Not implemented yet.");
+ return metadata;
}
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientSqlTest.java b/modules/client/src/test/java/org/apache/ignite/client/ClientSqlTest.java
index 30b58fe77..660ba32aa 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientSqlTest.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/ClientSqlTest.java
@@ -19,14 +19,29 @@ package org.apache.ignite.client;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
+import java.util.BitSet;
import java.util.Map;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
+import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.ResultSet;
+import org.apache.ignite.sql.ResultSetMetadata;
import org.apache.ignite.sql.Session;
+import org.apache.ignite.sql.SqlColumnType;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.Statement;
import org.apache.ignite.sql.async.AsyncResultSet;
@@ -67,8 +82,8 @@ public class ClientSqlTest extends AbstractClientTableTest {
.defaultSchema("SCHEMA1")
.defaultTimeout(123, TimeUnit.SECONDS)
.defaultPageSize(234)
- .property("prop1", 1)
- .property("prop2", 2)
+ .property("prop1", "1")
+ .property("prop2", "2")
.build();
AsyncResultSet resultSet = session.executeAsync(null, "SELECT PROPS").join();
@@ -77,10 +92,10 @@ public class ClientSqlTest extends AbstractClientTableTest {
.collect(Collectors.toMap(x -> x.stringValue(0), x -> x.value(1)));
assertEquals("SCHEMA1", props.get("schema"));
- assertEquals(123000L, props.get("timeout"));
- assertEquals(234, props.get("pageSize"));
- assertEquals(1, props.get("prop1"));
- assertEquals(2, props.get("prop2"));
+ assertEquals("123000", props.get("timeout"));
+ assertEquals("234", props.get("pageSize"));
+ assertEquals("1", props.get("prop1"));
+ assertEquals("2", props.get("prop2"));
}
@Test
@@ -89,8 +104,8 @@ public class ClientSqlTest extends AbstractClientTableTest {
.defaultSchema("SCHEMA1")
.defaultTimeout(123, TimeUnit.SECONDS)
.defaultPageSize(234)
- .property("prop1", 1)
- .property("prop2", 2)
+ .property("prop1", "1")
+ .property("prop2", "2")
.build();
Statement statement = client.sql().statementBuilder()
@@ -98,8 +113,8 @@ public class ClientSqlTest extends AbstractClientTableTest {
.defaultSchema("SCHEMA2")
.queryTimeout(124, TimeUnit.SECONDS)
.pageSize(235)
- .property("prop2", 22)
- .property("prop3", 3)
+ .property("prop2", "22")
+ .property("prop3", "3")
.build();
AsyncResultSet resultSet = session.executeAsync(null, statement).join();
@@ -108,10 +123,89 @@ public class ClientSqlTest extends AbstractClientTableTest {
.collect(Collectors.toMap(x -> x.stringValue(0), x -> x.value(1)));
assertEquals("SCHEMA2", props.get("schema"));
- assertEquals(124000L, props.get("timeout"));
- assertEquals(235, props.get("pageSize"));
- assertEquals(1, props.get("prop1"));
- assertEquals(22, props.get("prop2"));
- assertEquals(3, props.get("prop3"));
+ assertEquals("124000", props.get("timeout"));
+ assertEquals("235", props.get("pageSize"));
+ assertEquals("1", props.get("prop1"));
+ assertEquals("22", props.get("prop2"));
+ assertEquals("3", props.get("prop3"));
+ }
+
+ @Test
+ public void testMetadata() {
+ Session session = client.sql().createSession();
+ ResultSet resultSet = session.execute(null, "SELECT META");
+ ResultSetMetadata meta = resultSet.metadata();
+ SqlRow row = resultSet.next();
+
+ assertNotNull(meta);
+ assertSame(meta, row.metadata());
+
+ for (int i = 0; i < meta.columns().size(); i++) {
+ ColumnMetadata col = meta.columns().get(i);
+ assertEquals(i, meta.indexOf(col.name()));
+ assertEquals(row.<Object>value(i), row.value(col.name()));
+ }
+
+ assertTrue((boolean) row.value(0));
+ assertEquals(SqlColumnType.BOOLEAN, meta.columns().get(0).type());
+
+ assertEquals(Byte.MIN_VALUE, row.byteValue(1));
+ assertEquals(SqlColumnType.INT8, meta.columns().get(1).type());
+
+ assertEquals(Short.MIN_VALUE, row.shortValue(2));
+ assertEquals(SqlColumnType.INT16, meta.columns().get(2).type());
+
+ assertEquals(Integer.MIN_VALUE, row.intValue(3));
+ assertEquals(SqlColumnType.INT32, meta.columns().get(3).type());
+
+ assertEquals(Long.MIN_VALUE, row.longValue(4));
+ assertEquals(SqlColumnType.INT64, meta.columns().get(4).type());
+
+ assertEquals(1.3f, row.floatValue(5));
+ assertEquals(SqlColumnType.FLOAT, meta.columns().get(5).type());
+
+ assertEquals(1.4d, row.doubleValue(6));
+ assertEquals(SqlColumnType.DOUBLE, meta.columns().get(6).type());
+
+ assertEquals(BigDecimal.valueOf(145), row.value(7));
+ ColumnMetadata decimalCol = meta.columns().get(7);
+ assertEquals(SqlColumnType.DECIMAL, decimalCol.type());
+ assertEquals(1, decimalCol.precision());
+ assertEquals(2, decimalCol.scale());
+ assertTrue(decimalCol.nullable());
+ assertNotNull(decimalCol.origin());
+ assertEquals("SCHEMA1", decimalCol.origin().schemaName());
+ assertEquals("TBL2", decimalCol.origin().tableName());
+ assertEquals("BIG_DECIMAL", decimalCol.origin().columnName());
+
+ assertEquals(LocalDate.of(2001, 2, 3), row.dateValue(8));
+ assertEquals(SqlColumnType.DATE, meta.columns().get(8).type());
+
+ assertEquals(LocalTime.of(4, 5), row.timeValue(9));
+ assertEquals(SqlColumnType.TIME, meta.columns().get(9).type());
+
+ assertEquals(LocalDateTime.of(2001, 3, 4, 5, 6), row.datetimeValue(10));
+ assertEquals(SqlColumnType.DATETIME, meta.columns().get(10).type());
+
+ assertEquals(Instant.ofEpochSecond(987), row.timestampValue(11));
+ assertEquals(SqlColumnType.TIMESTAMP, meta.columns().get(11).type());
+
+ assertEquals(new UUID(0, 0), row.uuidValue(12));
+ assertEquals(SqlColumnType.UUID, meta.columns().get(12).type());
+
+ assertEquals(BitSet.valueOf(new byte[0]), row.bitmaskValue(13));
+ assertEquals(SqlColumnType.BITMASK, meta.columns().get(13).type());
+
+ assertEquals(0, ((byte[]) row.value(14))[0]);
+ assertEquals(SqlColumnType.BYTE_ARRAY, meta.columns().get(14).type());
+
+ assertEquals(Period.of(10, 9, 8), row.value(15));
+ assertEquals(SqlColumnType.PERIOD, meta.columns().get(15).type());
+
+ assertEquals(Duration.ofDays(11), row.value(16));
+ assertEquals(SqlColumnType.DURATION, meta.columns().get(16).type());
+
+ assertEquals(BigInteger.valueOf(42), row.value(17));
+ assertEquals(SqlColumnType.NUMBER, meta.columns().get(17).type());
}
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeAsyncResultSet.java b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeAsyncResultSet.java
index 4e94b4833..0087b071c 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeAsyncResultSet.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeAsyncResultSet.java
@@ -17,22 +17,31 @@
package org.apache.ignite.client.fakes;
-import static org.mockito.Mockito.mock;
-
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.Period;
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.List;
+import java.util.UUID;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
+import org.apache.ignite.internal.client.sql.ClientSqlRow;
import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.ResultSetMetadata;
import org.apache.ignite.sql.Session;
+import org.apache.ignite.sql.SqlColumnType;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.Statement;
import org.apache.ignite.sql.async.AsyncResultSet;
import org.apache.ignite.tx.Transaction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import org.mockito.Mockito;
/**
* Fake result set.
@@ -72,8 +81,8 @@ public class FakeAsyncResultSet implements AsyncResultSet {
rows = new ArrayList<>();
rows.add(getRow("schema", session.defaultSchema()));
- rows.add(getRow("timeout", session.defaultTimeout(TimeUnit.MILLISECONDS)));
- rows.add(getRow("pageSize", session.defaultPageSize()));
+ rows.add(getRow("timeout", String.valueOf(session.defaultTimeout(TimeUnit.MILLISECONDS))));
+ rows.add(getRow("pageSize", String.valueOf(session.defaultPageSize())));
var props = ((FakeSession) session).properties();
@@ -83,14 +92,55 @@ public class FakeAsyncResultSet implements AsyncResultSet {
columns = new ArrayList<>();
- columns.add(new FakeColumnMetadata("name"));
- columns.add(new FakeColumnMetadata("val"));
- } else {
- rows = new ArrayList<>();
- rows.add(getRow(1));
-
+ columns.add(new FakeColumnMetadata("name", SqlColumnType.STRING));
+ columns.add(new FakeColumnMetadata("val", SqlColumnType.STRING));
+ } else if ("SELECT META".equals(statement.query())) {
columns = new ArrayList<>();
- columns.add(new FakeColumnMetadata("col1"));
+
+ columns.add(new FakeColumnMetadata("bool", SqlColumnType.BOOLEAN));
+ columns.add(new FakeColumnMetadata("int8", SqlColumnType.INT8));
+ columns.add(new FakeColumnMetadata("int16", SqlColumnType.INT16));
+ columns.add(new FakeColumnMetadata("int32", SqlColumnType.INT32));
+ columns.add(new FakeColumnMetadata("int64", SqlColumnType.INT64));
+ columns.add(new FakeColumnMetadata("float", SqlColumnType.FLOAT));
+ columns.add(new FakeColumnMetadata("double", SqlColumnType.DOUBLE));
+ columns.add(new FakeColumnMetadata("decimal", SqlColumnType.DECIMAL, 1, 2,
+ true, new ColumnOrigin("SCHEMA1", "TBL2", "BIG_DECIMAL")));
+ columns.add(new FakeColumnMetadata("date", SqlColumnType.DATE));
+ columns.add(new FakeColumnMetadata("time", SqlColumnType.TIME));
+ columns.add(new FakeColumnMetadata("datetime", SqlColumnType.DATETIME));
+ columns.add(new FakeColumnMetadata("timestamp", SqlColumnType.TIMESTAMP));
+ columns.add(new FakeColumnMetadata("uuid", SqlColumnType.UUID));
+ columns.add(new FakeColumnMetadata("bitmask", SqlColumnType.BITMASK));
+ columns.add(new FakeColumnMetadata("byte_array", SqlColumnType.BYTE_ARRAY));
+ columns.add(new FakeColumnMetadata("period", SqlColumnType.PERIOD));
+ columns.add(new FakeColumnMetadata("duration", SqlColumnType.DURATION));
+ columns.add(new FakeColumnMetadata("number", SqlColumnType.NUMBER));
+
+ var row = getRow(
+ true,
+ Byte.MIN_VALUE,
+ Short.MIN_VALUE,
+ Integer.MIN_VALUE,
+ Long.MIN_VALUE,
+ 1.3f,
+ 1.4d,
+ BigDecimal.valueOf(145),
+ LocalDate.of(2001, 2, 3),
+ LocalTime.of(4, 5),
+ LocalDateTime.of(2001, 3, 4, 5, 6),
+ Instant.ofEpochSecond(987),
+ new UUID(0, 0),
+ BitSet.valueOf(new byte[0]),
+ new byte[1],
+ Period.of(10, 9, 8),
+ Duration.ofDays(11),
+ BigInteger.valueOf(42));
+
+ rows = List.of(row);
+ } else {
+ rows = List.of(getRow(1));
+ columns = List.of(new FakeColumnMetadata("col1", SqlColumnType.INT32));
}
}
@@ -160,12 +210,33 @@ public class FakeAsyncResultSet implements AsyncResultSet {
@NotNull
private SqlRow getRow(Object... vals) {
- var row = mock(SqlRow.class);
+ return new ClientSqlRow(List.of(vals), metadata());
+ }
- for (int i = 0; i < vals.length; i++) {
- Mockito.when(row.value(i)).thenReturn(vals[i]);
+ private static class ColumnOrigin implements ColumnMetadata.ColumnOrigin {
+ private final String schemaName;
+ private final String tableName;
+ private final String columnName;
+
+ public ColumnOrigin(String schemaName, String tableName, String columnName) {
+ this.schemaName = schemaName;
+ this.tableName = tableName;
+ this.columnName = columnName;
+ }
+
+ @Override
+ public String schemaName() {
+ return schemaName;
}
- return row;
+ @Override
+ public String tableName() {
+ return tableName;
+ }
+
+ @Override
+ public String columnName() {
+ return columnName;
+ }
}
}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeColumnMetadata.java b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeColumnMetadata.java
index 53042e10a..5e82b5764 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeColumnMetadata.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeColumnMetadata.java
@@ -26,13 +26,36 @@ import org.apache.ignite.sql.SqlColumnType;
class FakeColumnMetadata implements ColumnMetadata {
private final String name;
- /**
- * Constructor.
- *
- * @param name Column name.
- */
- FakeColumnMetadata(String name) {
+ private final SqlColumnType type;
+
+ private final int precision;
+
+ private final int scale;
+
+ private final boolean nullable;
+
+ private final ColumnOrigin origin;
+
+ FakeColumnMetadata(String name, SqlColumnType type) {
+ this(name, type, -1, -1, false, null);
+ }
+
+ FakeColumnMetadata(
+ String name,
+ SqlColumnType type,
+ int precision,
+ int scale,
+ boolean nullable,
+ ColumnOrigin origin) {
+ assert name != null;
+ assert type != null;
+
this.name = name;
+ this.type = type;
+ this.precision = precision;
+ this.scale = scale;
+ this.nullable = nullable;
+ this.origin = origin;
}
/** {@inheritDoc} */
@@ -50,30 +73,30 @@ class FakeColumnMetadata implements ColumnMetadata {
/** {@inheritDoc} */
@Override
public SqlColumnType type() {
- return null;
+ return type;
}
/** {@inheritDoc} */
@Override
public int precision() {
- return -1;
+ return precision;
}
/** {@inheritDoc} */
@Override
public int scale() {
- return -1;
+ return scale;
}
/** {@inheritDoc} */
@Override
public boolean nullable() {
- return false;
+ return nullable;
}
/** {@inheritDoc} */
@Override
public ColumnOrigin origin() {
- return null;
+ return origin;
}
}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
index 99b15200d..6a4092291 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/client/ItThinClientSqlTest.java
@@ -20,6 +20,7 @@ package org.apache.ignite.internal.runner.app.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -31,7 +32,9 @@ import org.apache.ignite.client.IgniteClientException;
import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.NoRowSetExpectedException;
import org.apache.ignite.sql.ResultSet;
+import org.apache.ignite.sql.ResultSetMetadata;
import org.apache.ignite.sql.Session;
+import org.apache.ignite.sql.SqlColumnType;
import org.apache.ignite.sql.SqlRow;
import org.apache.ignite.sql.Statement;
import org.apache.ignite.sql.async.AsyncResultSet;
@@ -61,7 +64,10 @@ public class ItThinClientSqlTest extends ItAbstractThinClientTest {
assertEquals(1, row.intValue(0));
assertEquals("hello", row.stringValue(1));
- List<ColumnMetadata> columns = resultSet.metadata().columns();
+ ResultSetMetadata metadata = resultSet.metadata();
+ assertNotNull(metadata);
+
+ List<ColumnMetadata> columns = metadata.columns();
assertEquals(2, columns.size());
assertEquals("NUM", columns.get(0).name());
assertEquals("STR", columns.get(1).name());
@@ -156,13 +162,15 @@ public class ItThinClientSqlTest extends ItAbstractThinClientTest {
assertTrue(deleteRes.wasApplied());
}
+ @SuppressWarnings("ConstantConditions")
@Test
void testExecuteDdlDml() {
Session session = client().sql().createSession();
// Create table.
- ResultSet createRes = session
- .execute(null, "CREATE TABLE testExecuteDdlDml(ID INT PRIMARY KEY, VAL VARCHAR)");
+ ResultSet createRes = session.execute(
+ null,
+ "CREATE TABLE testExecuteDdlDml(ID INT NOT NULL PRIMARY KEY, VAL VARCHAR)");
assertFalse(createRes.hasRowSet());
assertNull(createRes.metadata());
@@ -171,8 +179,9 @@ public class ItThinClientSqlTest extends ItAbstractThinClientTest {
// Insert data.
for (int i = 0; i < 10; i++) {
- ResultSet insertRes = session
- .execute(null, "INSERT INTO testExecuteDdlDml VALUES (?, ?)", i, "hello " + i);
+ ResultSet insertRes = session.execute(
+ null,
+ "INSERT INTO testExecuteDdlDml VALUES (?, ?)", i, "hello " + i);
assertFalse(insertRes.hasRowSet());
assertNull(insertRes.metadata());
@@ -190,9 +199,27 @@ public class ItThinClientSqlTest extends ItAbstractThinClientTest {
List<ColumnMetadata> columns = selectRes.metadata().columns();
assertEquals(3, columns.size());
+
assertEquals("MYVALUE", columns.get(0).name());
+ assertEquals("VAL", columns.get(0).origin().columnName());
+ assertEquals("PUBLIC", columns.get(0).origin().schemaName());
+ assertEquals("TESTEXECUTEDDLDML", columns.get(0).origin().tableName());
+ assertTrue(columns.get(0).nullable());
+ assertEquals(String.class, columns.get(0).valueClass());
+ assertEquals(SqlColumnType.STRING, columns.get(0).type());
+
+ // TODO IGNITE-17203
+ // assertEquals(-1, columns.get(0).scale());
+ // assertEquals(-1, columns.get(0).precision());
+
assertEquals("ID", columns.get(1).name());
+ assertEquals("ID", columns.get(1).origin().columnName());
+ assertEquals("PUBLIC", columns.get(1).origin().schemaName());
+ assertEquals("TESTEXECUTEDDLDML", columns.get(1).origin().tableName());
+ assertFalse(columns.get(1).nullable());
+
assertEquals("ID + 1", columns.get(2).name());
+ assertNull(columns.get(2).origin());
var rows = new ArrayList<SqlRow>();
selectRes.forEachRemaining(rows::add);
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlClientAsynchronousApiTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlClientAsynchronousApiTest.java
index bf8c5c95b..9da9d1dcb 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlClientAsynchronousApiTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/api/ItSqlClientAsynchronousApiTest.java
@@ -47,18 +47,6 @@ public class ItSqlClientAsynchronousApiTest extends ItSqlAsynchronousApiTest {
return client.sql();
}
- @Override
- @Disabled("IGNITE-17052")
- public void metadata() throws ExecutionException, InterruptedException {
- super.metadata();
- }
-
- @Override
- @Disabled("IGNITE-17052")
- public void sqlRow() throws ExecutionException, InterruptedException {
- super.sqlRow();
- }
-
@Override
@Disabled("IGNITE-17134")
public void closeSession() throws ExecutionException, InterruptedException {
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
index 43d99888a..1af6711c8 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/util/QueryChecker.java
@@ -35,6 +35,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
+import org.apache.ignite.internal.sql.SqlColumnTypeConverter;
import org.apache.ignite.internal.sql.engine.QueryProcessor;
import org.apache.ignite.internal.util.CollectionUtils;
import org.apache.ignite.sql.ColumnMetadata;
@@ -370,7 +371,7 @@ public abstract class QueryChecker {
if (expectedColumnTypes != null) {
List<Type> colTypes = cur.metadata().columns().stream()
.map(ColumnMetadata::type)
- .map(Commons::columnTypeToClass)
+ .map(SqlColumnTypeConverter::columnTypeToClass)
.collect(Collectors.toList());
assertThat("Column types don't match", colTypes, equalTo(expectedColumnTypes));
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/ColumnMetadataImpl.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/ColumnMetadataImpl.java
index e9fdf264c..cb42cd65a 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/ColumnMetadataImpl.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/api/ColumnMetadataImpl.java
@@ -18,7 +18,7 @@
package org.apache.ignite.internal.sql.api;
import java.util.List;
-import org.apache.ignite.internal.sql.engine.util.Commons;
+import org.apache.ignite.internal.sql.SqlColumnTypeConverter;
import org.apache.ignite.internal.tostring.S;
import org.apache.ignite.sql.ColumnMetadata;
import org.apache.ignite.sql.SqlColumnType;
@@ -110,7 +110,7 @@ public class ColumnMetadataImpl implements ColumnMetadata {
/** {@inheritDoc} */
@Override
public Class<?> valueClass() {
- return Commons.columnTypeToClass(type);
+ return SqlColumnTypeConverter.columnTypeToClass(type);
}
/** {@inheritDoc} */
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
index 93766d528..c968add06 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/Commons.java
@@ -27,12 +27,10 @@ import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
-import java.time.Period;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
@@ -114,7 +112,6 @@ import org.apache.ignite.lang.IgniteInternalException;
import org.apache.ignite.lang.IgniteLogger;
import org.apache.ignite.lang.IgniteSystemProperties;
import org.apache.ignite.sql.ResultSetMetadata;
-import org.apache.ignite.sql.SqlColumnType;
import org.codehaus.commons.compiler.CompilerFactoryFactory;
import org.codehaus.commons.compiler.IClassBodyEvaluator;
import org.codehaus.commons.compiler.ICompilerFactory;
@@ -702,74 +699,6 @@ public final class Commons {
}
}
- /**
- * Column type to Java class.
- */
- public static Class<?> columnTypeToClass(SqlColumnType type) {
- assert type != null;
-
- switch (type) {
- case BOOLEAN:
- return Boolean.class;
- case INT8:
- return Byte.class;
-
- case INT16:
- return Short.class;
-
- case INT32:
- return Integer.class;
-
- case INT64:
- return Long.class;
-
- case FLOAT:
- return Float.class;
-
- case DOUBLE:
- return Double.class;
-
- case NUMBER:
- return BigInteger.class;
-
- case DECIMAL:
- return BigDecimal.class;
-
- case UUID:
- return UUID.class;
-
- case STRING:
- return String.class;
-
- case BYTE_ARRAY:
- return byte[].class;
-
- case BITMASK:
- return BitSet.class;
-
- case DATE:
- return LocalDate.class;
-
- case TIME:
- return LocalTime.class;
-
- case DATETIME:
- return LocalDateTime.class;
-
- case TIMESTAMP:
- return Instant.class;
-
- case PERIOD:
- return Period.class;
-
- case DURATION:
- return Duration.class;
-
- default:
- throw new IllegalArgumentException("Unsupported type " + type);
- }
- }
-
/**
* NativeTypePrecision.
* TODO Documentation https://issues.apache.org/jira/browse/IGNITE-15859