diff --git a/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/main/java/org/apache/shardingsphere/db/protocol/postgresql/constant/PostgreSQLArrayColumnType.java b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/main/java/org/apache/shardingsphere/db/protocol/postgresql/constant/PostgreSQLArrayColumnType.java new file mode 100644 index 0000000000000..a535931504d42 --- /dev/null +++ b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/main/java/org/apache/shardingsphere/db/protocol/postgresql/constant/PostgreSQLArrayColumnType.java @@ -0,0 +1,135 @@ +/* + * 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.shardingsphere.db.protocol.postgresql.constant; + +import java.util.HashMap; +import java.util.Map; + +/** + * Array column types for PostgreSQL. + */ +public class PostgreSQLArrayColumnType { + + private static final String ORIGINAL_RECORD_LINES = + "_aclitem 1034\n" + + "_array_tc_0 16425\n" + + "_array_tc_1 16433\n" + + "_bit 1561\n" + + "_bool 1000\n" + + "_box 1020\n" + + "_bpchar 1014\n" + + "_bytea 1001\n" + + "_cardinal_number 13031\n" + + "_char 1002\n" + + "_character_data 13034\n" + + "_cid 1012\n" + + "_cidr 651\n" + + "_circle 719\n" + + "_cstring 1263\n" + + "_date 1182\n" + + "_daterange 3913\n" + + "_float4 1021\n" + + "_float8 1022\n" + + "_gtsvector 3644\n" + + "_inet 1041\n" + + "_int2 1005\n" + + "_int2vector 1006\n" + + "_int4 1007\n" + + "_int4range 3905\n" + + "_int8 1016\n" + + "_int8range 3927\n" + + "_interval 1187\n" + + "_json 199\n" + + "_jsonb 3807\n" + + "_jsonpath 4073\n" + + "_line 629\n" + + "_lseg 1018\n" + + "_macaddr 1040\n" + + "_macaddr8 775\n" + + "_money 791\n" + + "_name 1003\n" + + "_numeric 1231\n" + + "_numrange 3907\n" + + "_oid 1028\n" + + "_oidvector 1013\n" + + "_path 1019\n" + + "_pg_foreign_data_wrappers 13265\n" + + "_pg_foreign_servers 13274\n" + + "_pg_foreign_table_columns 13258\n" + + "_pg_foreign_tables 13284\n" + + "_pg_lsn 3221\n" + + "_pg_user_mappings 13294\n" + + "_point 1017\n" + + "_polygon 1027\n" + + "_record 2287\n" + + "_refcursor 2201\n" + + "_regclass 2210\n" + + "_regconfig 3735\n" + + "_regdictionary 3770\n" + + "_regnamespace 4090\n" + + "_regoper 2208\n" + + "_regoperator 2209\n" + + "_regproc 1008\n" + + "_regprocedure 2207\n" + + "_regrole 4097\n" + + "_regtype 2211\n" + + "_sql_identifier 13036\n" + + "_test_array 16395\n" + + "_text 1009\n" + + "_tid 1010\n" + + "_time 1183\n" + + "_time_stamp 13041\n" + + "_timestamp 1115\n" + + "_timestamptz 1185\n" + + "_timetz 1270\n" + + "_tsquery 3645\n" + + "_tsrange 3909\n" + + "_tstzrange 3911\n" + + "_tsvector 3643\n" + + "_txid_snapshot 2949\n" + + "_uuid 2951\n" + + "_varbit 1563\n" + + "_varchar 1015\n" + + "_xid 1011\n" + + "_xml 143\n" + + "_yes_or_no 13043"; + + private static final Map COLUMN_TYPE_NAME_OID_MAP = new HashMap<>(128, 1.0F); + + static { + for (String line : ORIGINAL_RECORD_LINES.split("\n")) { + String[] values = line.split(" "); + COLUMN_TYPE_NAME_OID_MAP.put(values[0], Integer.parseInt(values[1])); + } + } + + /** + * Get type oid by database-specific column type name. + * + * @param columnTypeName PostgreSQL column type name, e.g. int4 + * @return type oid, e.g. 23 for int4 + * @throws IllegalArgumentException if no type oid could be found + */ + public static int getTypeOidByColumnTypeName(final String columnTypeName) throws IllegalArgumentException { + if (COLUMN_TYPE_NAME_OID_MAP.containsKey(columnTypeName)) { + return COLUMN_TYPE_NAME_OID_MAP.get(columnTypeName); + } + throw new IllegalArgumentException(String.format("Cannot find PostgreSQL type oid for columnTypeName '%s'", columnTypeName)); + } + +} diff --git a/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/main/java/org/apache/shardingsphere/db/protocol/postgresql/packet/command/query/PostgreSQLColumnDescription.java b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/main/java/org/apache/shardingsphere/db/protocol/postgresql/packet/command/query/PostgreSQLColumnDescription.java index f8c703dc35e5d..7259ac456a9ae 100644 --- a/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/main/java/org/apache/shardingsphere/db/protocol/postgresql/packet/command/query/PostgreSQLColumnDescription.java +++ b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/main/java/org/apache/shardingsphere/db/protocol/postgresql/packet/command/query/PostgreSQLColumnDescription.java @@ -17,13 +17,19 @@ package org.apache.shardingsphere.db.protocol.postgresql.packet.command.query; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.shardingsphere.db.protocol.postgresql.constant.PostgreSQLArrayColumnType; import org.apache.shardingsphere.db.protocol.postgresql.constant.PostgreSQLColumnType; /** * Column description for PostgreSQL. */ @Getter +@Slf4j public final class PostgreSQLColumnDescription { private final String columnName; @@ -40,10 +46,20 @@ public final class PostgreSQLColumnDescription { private final int dataFormat = 0; - public PostgreSQLColumnDescription(final String columnName, final int columnIndex, final int columnType, final int columnLength) { + public PostgreSQLColumnDescription(final String columnName, final int columnIndex, final int columnType, final int columnLength, final ResultSetMetaData resultSetMetaData) { this.columnName = columnName; this.columnIndex = columnIndex; - this.typeOID = PostgreSQLColumnType.valueOfJDBCType(columnType).getValue(); + if (Types.ARRAY == columnType && null != resultSetMetaData) { + String columnTypeName = null; + try { + columnTypeName = resultSetMetaData.getColumnTypeName(columnIndex); + } catch (final SQLException ex) { + log.error("getColumnTypeName failed, columnName={}, columnIndex={}", columnName, columnIndex, ex); + } + this.typeOID = PostgreSQLArrayColumnType.getTypeOidByColumnTypeName(columnTypeName); + } else { + this.typeOID = PostgreSQLColumnType.valueOfJDBCType(columnType).getValue(); + } this.columnLength = columnLength; } } diff --git a/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/test/java/org/apache/shardingsphere/db/protocol/postgresql/constant/PostgreSQLArrayColumnTypeTest.java b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/test/java/org/apache/shardingsphere/db/protocol/postgresql/constant/PostgreSQLArrayColumnTypeTest.java new file mode 100644 index 0000000000000..352363cb5c23e --- /dev/null +++ b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/test/java/org/apache/shardingsphere/db/protocol/postgresql/constant/PostgreSQLArrayColumnTypeTest.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.shardingsphere.db.protocol.postgresql.constant; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class PostgreSQLArrayColumnTypeTest { + + @Test + public void assertIntegerArrayGot() { + int columnTypeName = PostgreSQLArrayColumnType.getTypeOidByColumnTypeName("_int4"); + assertThat(columnTypeName, is(1007)); + } + + @Test(expected = IllegalArgumentException.class) + public void assertIllegalArgExThrown() { + PostgreSQLArrayColumnType.getTypeOidByColumnTypeName("not_exist_type"); + } + +} diff --git a/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/test/java/org/apache/shardingsphere/db/protocol/postgresql/packet/command/query/PostgreSQLColumnDescriptionTest.java b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/test/java/org/apache/shardingsphere/db/protocol/postgresql/packet/command/query/PostgreSQLColumnDescriptionTest.java new file mode 100644 index 0000000000000..b622cfe872e36 --- /dev/null +++ b/shardingsphere-db-protocol/shardingsphere-db-protocol-postgresql/src/test/java/org/apache/shardingsphere/db/protocol/postgresql/packet/command/query/PostgreSQLColumnDescriptionTest.java @@ -0,0 +1,45 @@ +/* + * 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.shardingsphere.db.protocol.postgresql.packet.command.query; + +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class PostgreSQLColumnDescriptionTest { + + @Test + public void assertIntegerTypeOid() { + PostgreSQLColumnDescription description = new PostgreSQLColumnDescription("age", 1, Types.INTEGER, 4, null); + assertThat(description.getTypeOID(), is(23)); + } + + @Test + public void assertIntegerArrayTypeOid() throws SQLException { + ResultSetMetaData resultSetMetaData = Mockito.mock(ResultSetMetaData.class); + Mockito.when(resultSetMetaData.getColumnTypeName(2)).thenReturn("_int4"); + PostgreSQLColumnDescription description = new PostgreSQLColumnDescription("ages", 2, Types.ARRAY, 12, resultSetMetaData); + assertThat(description.getTypeOID(), is(1007)); + } + +} diff --git a/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/QueryResult.java b/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/QueryResult.java index 84822d46adef0..2509d5225d1a5 100644 --- a/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/QueryResult.java +++ b/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/QueryResult.java @@ -18,6 +18,7 @@ package org.apache.shardingsphere.infra.executor.sql; import java.io.InputStream; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Calendar; @@ -25,7 +26,14 @@ * Query result form SQL. */ public interface QueryResult { - + + /** + * Get JDBC ResultSetMetaData. + * + * @return JDBC ResultSetMetaData + */ + ResultSetMetaData getResultSetMetaData(); + /** * iterate next data. * diff --git a/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/MemoryQueryResult.java b/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/MemoryQueryResult.java index 2e621317bac24..35d4c26f047a4 100644 --- a/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/MemoryQueryResult.java +++ b/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/MemoryQueryResult.java @@ -111,7 +111,12 @@ private Object getRowValue(final ResultSet resultSet, final int columnIndex) thr return resultSet.getObject(columnIndex); } } - + + @Override + public ResultSetMetaData getResultSetMetaData() { + return resultSetMetaData; + } + @Override public boolean next() { if (rows.hasNext()) { diff --git a/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/StreamQueryResult.java b/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/StreamQueryResult.java index 18e0aba8776c6..ee2e1e185c95a 100644 --- a/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/StreamQueryResult.java +++ b/shardingsphere-infra/shardingsphere-infra-executor/src/main/java/org/apache/shardingsphere/infra/executor/sql/resourced/jdbc/queryresult/StreamQueryResult.java @@ -44,7 +44,12 @@ public StreamQueryResult(final ResultSet resultSet) throws SQLException { resultSetMetaData = resultSet.getMetaData(); this.resultSet = resultSet; } - + + @Override + public ResultSetMetaData getResultSetMetaData() { + return resultSetMetaData; + } + @Override public boolean next() throws SQLException { return resultSet.next(); diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/binary/bind/PostgreSQLComBindExecutor.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/binary/bind/PostgreSQLComBindExecutor.java index ea5ab519c5d58..667b16942b465 100644 --- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/binary/bind/PostgreSQLComBindExecutor.java +++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/binary/bind/PostgreSQLComBindExecutor.java @@ -17,6 +17,7 @@ package org.apache.shardingsphere.proxy.frontend.postgresql.command.query.binary.bind; +import java.sql.ResultSetMetaData; import lombok.Getter; import org.apache.shardingsphere.db.protocol.packet.DatabasePacket; import org.apache.shardingsphere.db.protocol.postgresql.constant.PostgreSQLColumnType; @@ -29,6 +30,7 @@ import org.apache.shardingsphere.db.protocol.postgresql.packet.command.query.text.PostgreSQLDataRowPacket; import org.apache.shardingsphere.db.protocol.postgresql.packet.generic.PostgreSQLCommandCompletePacket; import org.apache.shardingsphere.db.protocol.postgresql.packet.generic.PostgreSQLErrorResponsePacket; +import org.apache.shardingsphere.infra.executor.sql.QueryResult; import org.apache.shardingsphere.infra.executor.sql.raw.execute.result.query.QueryHeader; import org.apache.shardingsphere.proxy.backend.communication.DatabaseCommunicationEngine; import org.apache.shardingsphere.proxy.backend.communication.DatabaseCommunicationEngineFactory; @@ -117,9 +119,11 @@ private Optional createQueryPacket(final QueryRe private List getPostgreSQLColumnDescriptions(final QueryResponse queryResponse) { List result = new LinkedList<>(); + List queryResults = queryResponse.getQueryResults(); + ResultSetMetaData resultSetMetaData = queryResults.size() > 0 ? queryResults.get(0).getResultSetMetaData() : null; int columnIndex = 0; for (QueryHeader each : queryResponse.getQueryHeaders()) { - result.add(new PostgreSQLColumnDescription(each.getColumnName(), ++columnIndex, each.getColumnType(), each.getColumnLength())); + result.add(new PostgreSQLColumnDescription(each.getColumnName(), ++columnIndex, each.getColumnType(), each.getColumnLength(), resultSetMetaData)); } return result; } diff --git a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/text/PostgreSQLComQueryExecutor.java b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/text/PostgreSQLComQueryExecutor.java index 9fc8db24d7aae..a6fabfff2d76f 100644 --- a/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/text/PostgreSQLComQueryExecutor.java +++ b/shardingsphere-proxy/shardingsphere-proxy-frontend/shardingsphere-proxy-frontend-postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/text/PostgreSQLComQueryExecutor.java @@ -17,6 +17,7 @@ package org.apache.shardingsphere.proxy.frontend.postgresql.command.query.text; +import java.sql.ResultSetMetaData; import lombok.Getter; import org.apache.shardingsphere.db.protocol.packet.DatabasePacket; import org.apache.shardingsphere.db.protocol.postgresql.packet.PostgreSQLPacket; @@ -27,6 +28,7 @@ import org.apache.shardingsphere.db.protocol.postgresql.packet.generic.PostgreSQLCommandCompletePacket; import org.apache.shardingsphere.db.protocol.postgresql.packet.generic.PostgreSQLErrorResponsePacket; import org.apache.shardingsphere.infra.database.type.DatabaseTypes; +import org.apache.shardingsphere.infra.executor.sql.QueryResult; import org.apache.shardingsphere.infra.executor.sql.raw.execute.result.query.QueryHeader; import org.apache.shardingsphere.proxy.backend.communication.jdbc.connection.BackendConnection; import org.apache.shardingsphere.proxy.backend.response.BackendResponse; @@ -102,9 +104,11 @@ private Optional createQueryPacket(final QueryRe private List getPostgreSQLColumnDescriptions(final QueryResponse queryResponse) { List result = new LinkedList<>(); + List queryResults = queryResponse.getQueryResults(); + ResultSetMetaData resultSetMetaData = queryResults.size() > 0 ? queryResults.get(0).getResultSetMetaData() : null; int columnIndex = 0; for (QueryHeader each : queryResponse.getQueryHeaders()) { - result.add(new PostgreSQLColumnDescription(each.getColumnName(), ++columnIndex, each.getColumnType(), each.getColumnLength())); + result.add(new PostgreSQLColumnDescription(each.getColumnName(), ++columnIndex, each.getColumnType(), each.getColumnLength(), resultSetMetaData)); } return result; }