package com.github.housepower.jdbc.data.type.complex; import com.github.housepower.jdbc.ClickHouseArray; import com.github.housepower.jdbc.connect.PhysicalInfo; import com.github.housepower.jdbc.data.DataTypeFactory; import com.github.housepower.jdbc.data.IDataType; import com.github.housepower.jdbc.misc.SQLLexer; import com.github.housepower.jdbc.misc.Validate; import com.github.housepower.jdbc.serializer.BinaryDeserializer; import com.github.housepower.jdbc.serializer.BinarySerializer; import java.io.IOException; import java.math.BigInteger; import java.sql.Array; import java.sql.SQLException; import java.sql.Types; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class DataTypeArray implements IDataType { private final String name; private final Array defaultValue; private final IDataType elemDataType; private final IDataType offsetIDataType; public DataTypeArray(String name, IDataType elemDataType, IDataType offsetIDataType) throws SQLException { this.name = name; this.elemDataType = elemDataType; this.offsetIDataType = offsetIDataType; this.defaultValue = new ClickHouseArray(new Object[] {elemDataType.defaultValue()}); } @Override public String name() { return name; } @Override public int sqlTypeId() { return Types.ARRAY; } @Override public Object defaultValue() { return defaultValue; } @Override public Class javaTypeClass() { return Array.class; } @Override public boolean nullable() { return false; } @Override public Object deserializeTextQuoted(SQLLexer lexer) throws SQLException { Validate.isTrue(lexer.character() == '['); List<Object> arrayData = new ArrayList<Object>(); for (; ; ) { if (lexer.isCharacter(']')) { lexer.character(); break; } if (lexer.isCharacter(',')) { lexer.character(); } arrayData.add(elemDataType.deserializeTextQuoted(lexer)); } return new ClickHouseArray(arrayData.toArray()); } public void serializeBinary(Object data, BinarySerializer dataBinarySerializer, List<List<Integer>> offsets, int level) throws SQLException, IOException { int dataOffset = ((Object[]) ((Array) data).getArray()).length; if (offsets.size() < level) { List<Integer> offset = new ArrayList<>(); offset.add(dataOffset); offsets.add(offset); } else { int lastIdx = offsets.get(level - 1).size() - 1; int lastOffset = offsets.get(level - 1).get(lastIdx); offsets.get(level - 1).add(lastOffset + dataOffset); } for (Object v : ((Object[]) ((Array) data).getArray())) { if (elemDataType.sqlTypeId() == Types.ARRAY) { ((DataTypeArray)(elemDataType)).serializeBinary(v, dataBinarySerializer, offsets, level + 1); } else { elemDataType.serializeBinary(v, dataBinarySerializer); } } } @Override public void serializeBinary(Object data, BinarySerializer serializer) throws SQLException, IOException{ throw new SQLException("DataTypeArray serializeBinary not supported"); } @Override public void serializeBinaryBulk(Iterator<Object> data, BinarySerializer serializer) throws SQLException, IOException { throw new SQLException("DataTypeArray serializeBinaryBulk not supported"); } @Override public void serializeBinaryBulk(Object[] data, BinarySerializer serializer) throws SQLException, IOException { throw new SQLException("DataTypeArray serializeBinaryBulk not supported"); } @Override public Object deserializeBinary(BinaryDeserializer deserializer) throws SQLException, IOException { Long offset = (Long) offsetIDataType.deserializeBinary(deserializer); return elemDataType.deserializeBinaryBulk(offset.intValue(), deserializer); } @Override public Object[] deserializeBinaryBulk(int rows, BinaryDeserializer deserializer) throws IOException, SQLException { ClickHouseArray[] data = new ClickHouseArray[rows]; if (rows == 0) { return data; } Object[] offsets = offsetIDataType.deserializeBinaryBulk(rows, deserializer); ClickHouseArray res = new ClickHouseArray( elemDataType.deserializeBinaryBulk(((BigInteger) offsets[rows - 1]).intValue() , deserializer)); for (int row = 0, lastOffset = 0; row < rows; row++) { BigInteger offset = (BigInteger) offsets[row]; data[row] = res.slice(lastOffset, offset.intValue() - lastOffset); lastOffset = offset.intValue(); } return data; } public static IDataType createArrayType(SQLLexer lexer, PhysicalInfo.ServerInfo serverInfo) throws SQLException { Validate.isTrue(lexer.character() == '('); IDataType arrayNestedType = DataTypeFactory.get(lexer, serverInfo); Validate.isTrue(lexer.character() == ')'); return new DataTypeArray("Array(" + arrayNestedType.name() + ")", arrayNestedType, DataTypeFactory.get("UInt64", serverInfo)); } }