From 44db92489ff5a457ea701e197aacfed193a91bdf Mon Sep 17 00:00:00 2001 From: jkee <jkee@yandex-team.ru> Date: Thu, 19 Mar 2015 00:03:33 +0300 Subject: [PATCH] METR-15511: different metadata, some types support --- .../clickhouse/CHDatabaseMetadata.java | 207 ++++++++++++++++-- .../copypaste/AbstractResultSet.java | 5 - .../clickhouse/copypaste/CHResultSet.java | 129 +++++++++-- 3 files changed, 298 insertions(+), 43 deletions(-) diff --git a/src/main/java/ru/yandex/metrika/clickhouse/CHDatabaseMetadata.java b/src/main/java/ru/yandex/metrika/clickhouse/CHDatabaseMetadata.java index 5322e29c..a33ab50f 100644 --- a/src/main/java/ru/yandex/metrika/clickhouse/CHDatabaseMetadata.java +++ b/src/main/java/ru/yandex/metrika/clickhouse/CHDatabaseMetadata.java @@ -1,6 +1,11 @@ package ru.yandex.metrika.clickhouse; +import ru.yandex.metrika.clickhouse.copypaste.CHResultBuilder; +import ru.yandex.metrika.clickhouse.copypaste.CHResultSet; + import java.sql.*; +import java.util.ArrayList; +import java.util.List; /** * Created by jkee on 14.03.15. @@ -612,12 +617,42 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { - return request("SELECT 'bullshit_procedure'"); + CHResultBuilder builder = CHResultBuilder.builder(9); + builder.names( + "PROCEDURE_CAT", + "PROCEDURE_SCHEM", + "PROCEDURE_NAME", + "RES_1", + "RES_2", + "RES_3", + "REMARKS", + "PROCEDURE_TYPE", + "SPECIFIC_NAME" + ); + + builder.types( + "String", + "String", + "String", + "String", + "String", + "String", + "String", + "UInt8", + "String" + ); + + return builder.build(); } @Override public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { - return request("SELECT 'bullshit_procedure_columns'"); + CHResultBuilder builder = CHResultBuilder.builder(20); + builder.names("1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20"); + + builder.types("UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32","UInt32"); + + return builder.build(); } @Override @@ -638,16 +673,45 @@ public class CHDatabaseMetadata implements DatabaseMetaData { catalog = "default"; } String sql = "select " + - "database, name, name, 'TABLE', '', '', '', '', '', '' " + + "database, name, name " + "from system.tables " + "where database = '" + catalog + "' " + "order by name"; - return request(sql); + ResultSet result = request(sql); + + CHResultBuilder builder = CHResultBuilder.builder(10); + builder.names("TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "TABLE_TYPE", "REMARKS", "TYPE_CAT", "TYPE_SCHEM", "TYPE_NAME", "SELF_REFERENCING_COL_NAME", "REF_GENERATION"); + builder.types("String", "String", "String", "String", "String", "String", "String", "String", "String", "String"); + + while(result.next()) { + List<String> row = new ArrayList<String>(); + row.add(result.getString(1)); + row.add(result.getString(2)); + row.add(result.getString(3)); + row.add("TABLE"); // можно Ñделать точнее + for (int i = 4; i < 10; i++) { + row.add(null); + } + builder.addRow(row); + } + return builder.build(); } @Override public ResultSet getSchemas() throws SQLException { - return request("select name, database from system.tables"); + return getSchemas(null, null); + } + + @Override + public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { + String sql = "select name, database from system.tables"; + if (catalog != null) sql += " where database = '" + catalog + '\''; + if (schemaPattern != null) { + if (catalog != null) sql += " and "; + else sql += " where "; + sql += "name = '" + schemaPattern + '\''; + } + return request(sql); } @Override @@ -662,7 +726,107 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { - return request("desc table " + catalog + '.' + tableNamePattern); + CHResultBuilder builder = CHResultBuilder.builder(23); + builder.names( + "TABLE_CAT", + "TABLE_SCHEM", + "TABLE_NAME", + "COLUMN_NAME", + "DATA_TYPE", + "TYPE_NAME", + "COLUMN_SIZE", + "BUFFER_LENGTH", + "DECIMAL_DIGITS", + "NUM_PREC_RADIX", + "NULLABLE", + "REMARKS", + "COLUMN_DEF", + "SQL_DATA_TYPE", + "SQL_DATETIME_SUB", + "CHAR_OCTET_LENGTH", + "ORDINAL_POSITION", + "IS_NULLABLE", + "SCOPE_CATLOG", + "SCOPE_SCHEMA", + "SCOPE_TABLE", + "SOURCE_DATA_TYPE", + "IS_AUTOINCREMENT" + ); + builder.types( + "String", + "String", + "String", + "String", + "Int32", + "String", + "Int32", + "Int32", + "Int32", + "Int32", + "Int32", + "String", + "String", + "Int32", + "Int32", + "Int32", + "Int32", + "String", + "String", + "String", + "String", + "Int32", + "String" + ); + ResultSet descTable = request("desc table " + catalog + '.' + tableNamePattern); + int colNum = 1; + while (descTable.next()) { + List<String> row = new ArrayList<String>(); + row.add(catalog); + row.add(tableNamePattern); + row.add(tableNamePattern); + row.add(descTable.getString(1)); + String type = descTable.getString(2); + row.add(Integer.toString(CHResultSet.toSqlType(type))); + row.add(type); + + // column size ? + row.add("0"); + row.add("0"); + + // decimal digits + if (type.contains("Int")) { + String bits = type.substring(type.indexOf("Int") + "Int".length()); + row.add(bits); //bullshit + } + + // radix + row.add("10"); + // nullable + row.add(String.valueOf(columnNoNulls)); + + row.add(null); + row.add(null); + row.add(null); + row.add(null); + + // char octet length + row.add("0"); + // ordinal + row.add(String.valueOf(colNum)); + colNum += 1; + row.add("NO"); + + row.add(null); + row.add(null); + row.add(null); + row.add(null); + row.add(null); + + builder.addRow(row); + + } + + return builder.build(); } @Override @@ -717,6 +881,10 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public boolean supportsResultSetType(int type) throws SQLException { + int[] types = CHResultSet.supportedTypes(); + for (int i : types) { + if (i == type) return true; + } return false; } @@ -727,32 +895,32 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public boolean ownUpdatesAreVisible(int type) throws SQLException { - return false; + return true; } @Override public boolean ownDeletesAreVisible(int type) throws SQLException { - return false; + return true; } @Override public boolean ownInsertsAreVisible(int type) throws SQLException { - return false; + return true; } @Override public boolean othersUpdatesAreVisible(int type) throws SQLException { - return false; + return true; } @Override public boolean othersDeletesAreVisible(int type) throws SQLException { - return false; + return true; } @Override public boolean othersInsertsAreVisible(int type) throws SQLException { - return false; + return true; } @Override @@ -782,7 +950,7 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public Connection getConnection() throws SQLException { - return null; + return connection; } @Override @@ -837,7 +1005,7 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public int getDatabaseMinorVersion() throws SQLException { - return 0; + return 1; } @Override @@ -847,12 +1015,12 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public int getJDBCMinorVersion() throws SQLException { - return 0; + return 1; } @Override public int getSQLStateType() throws SQLException { - return 0; + return sqlStateSQL; } @Override @@ -867,12 +1035,7 @@ public class CHDatabaseMetadata implements DatabaseMetaData { @Override public RowIdLifetime getRowIdLifetime() throws SQLException { - return null; - } - - @Override - public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { - return null; + return RowIdLifetime.ROWID_UNSUPPORTED; } @Override diff --git a/src/main/java/ru/yandex/metrika/clickhouse/copypaste/AbstractResultSet.java b/src/main/java/ru/yandex/metrika/clickhouse/copypaste/AbstractResultSet.java index 603cb6fc..d753caa4 100644 --- a/src/main/java/ru/yandex/metrika/clickhouse/copypaste/AbstractResultSet.java +++ b/src/main/java/ru/yandex/metrika/clickhouse/copypaste/AbstractResultSet.java @@ -284,11 +284,6 @@ public abstract class AbstractResultSet implements ResultSet { throw new UnsupportedOperationException(); } - @Override - public int getRow() throws SQLException { - throw new UnsupportedOperationException(); - } - @Override public boolean absolute(int row) throws SQLException { throw new UnsupportedOperationException(); diff --git a/src/main/java/ru/yandex/metrika/clickhouse/copypaste/CHResultSet.java b/src/main/java/ru/yandex/metrika/clickhouse/copypaste/CHResultSet.java index 2f7ef804..0c82b9f0 100644 --- a/src/main/java/ru/yandex/metrika/clickhouse/copypaste/CHResultSet.java +++ b/src/main/java/ru/yandex/metrika/clickhouse/copypaste/CHResultSet.java @@ -2,10 +2,7 @@ package ru.yandex.metrika.clickhouse.copypaste; import java.io.IOException; import java.io.InputStream; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.sql.Types; +import java.sql.*; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -29,6 +26,7 @@ public class CHResultSet extends AbstractResultSet { private final String[] types; private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // + private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // // current line private ByteFragment[] values; @@ -38,6 +36,9 @@ public class CHResultSet extends AbstractResultSet { // next line private ByteFragment nextLine; + // row counter + private int rowNumber; + public CHResultSet(InputStream is, int bufferSize) throws IOException { bis = new StreamSplitter(is, (byte) 0x0A, bufferSize); /// \n ByteFragment headerFragment = bis.next(); @@ -90,6 +91,7 @@ public class CHResultSet extends AbstractResultSet { if (hasNext()) { values = nextLine.split((byte) 0x09); nextLine = null; + rowNumber += 1; return true; } else return false; } @@ -129,7 +131,7 @@ public class CHResultSet extends AbstractResultSet { @Override public boolean wasNull() throws SQLException { if (lastReadColumn == 0) throw new IllegalStateException("You should get something before check nullability"); - return values[lastReadColumn - 1].isNull(); + return getValue(lastReadColumn).isNull(); } @Override @@ -182,57 +184,126 @@ public class CHResultSet extends AbstractResultSet { } + @Override + public double getDouble(String columnLabel) throws SQLException { + return getDouble(asColNum(columnLabel)); + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + return getFloat(asColNum(columnLabel)); + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + return getDate(asColNum(columnLabel)); + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + return getTime(asColNum(columnLabel)); + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + return getObject(asColNum(columnLabel)); + } + ///////////////////////////////////////////////////////// @Override public String getString(int colNum) { - return toString(values[colNum - 1]); + return toString(getValue(colNum)); } + @Override public int getInt(int colNum) { - return ByteFragmentUtils.parseInt(values[colNum - 1]); + return ByteFragmentUtils.parseInt(getValue(colNum)); } @Override public boolean getBoolean(int colNum) { - return toBoolean(values[colNum - 1]); + return toBoolean(getValue(colNum)); } @Override public long getLong(int colNum) { - return ByteFragmentUtils.parseLong(values[colNum - 1]); + return ByteFragmentUtils.parseLong(getValue(colNum)); } @Override public byte[] getBytes(int colNum) { - return toBytes(values[colNum - 1]); + return toBytes(getValue(colNum)); } public long getTimestampAsLong(int colNum) { - return toTimestamp(values[colNum - 1]); + return toTimestamp(getValue(colNum)); } @Override public Timestamp getTimestamp(int columnIndex) throws SQLException { - if (values[columnIndex - 1].isNull()) return null; + if (getValue(columnIndex).isNull()) return null; return new Timestamp(getTimestampAsLong(columnIndex)); } @Override public short getShort(int colNum) { - return toShort(values[colNum - 1]); + return toShort(getValue(colNum)); } @Override public byte getByte(int colNum) { - return toByte(values[colNum - 1]); + return toByte(getValue(colNum)); } public long[] getLongArray(int colNum) { - return toLongArray(values[colNum - 1]); + return toLongArray(getValue(colNum)); + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + return (float) getDouble(columnIndex); + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + String string = getString(columnIndex); + if (string == null) return 0; + return Double.parseDouble(string); + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + // дата из кликхауÑа приходит в виде Ñтроки + ByteFragment value = getValue(columnIndex); + if (value.isNull()) return null; + try { + return new Date(dateFormat.parse(value.asString()).getTime()); + } catch (ParseException e) { + return null; + } } + @Override + public Time getTime(int columnIndex) throws SQLException { + return new Time(getTimestamp(columnIndex).getTime()); + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + int type = toSqlType(types[columnIndex - 1]); + switch (type) { + case Types.BIGINT: return getLong(columnIndex); + case Types.INTEGER: return getInt(columnIndex); + case Types.VARCHAR: return getString(columnIndex); + case Types.FLOAT: return getFloat(columnIndex); + case Types.DATE: return getDate(columnIndex); + case Types.TIMESTAMP: return getTime(columnIndex); + case Types.BLOB: return getString(columnIndex); + } + return getString(columnIndex); + } ///////////////////////////////////////////////////////// @@ -282,6 +353,20 @@ public class CHResultSet extends AbstractResultSet { } } + ////// + + @Override + public int getType() throws SQLException { + return TYPE_FORWARD_ONLY; + } + + @Override + public int getRow() throws SQLException { + return rowNumber + 1; + } + + ///// + // 1-based insex in column list private int asColNum(String column) { if (col.containsKey(column)) { @@ -291,7 +376,12 @@ public class CHResultSet extends AbstractResultSet { } } - int toSqlType(String type) { + private ByteFragment getValue(int colNum) { + lastReadColumn = colNum; + return values[colNum - 1]; + } + + public static int toSqlType(String type) { if (type.startsWith("Int") || type.startsWith("UInt")) { if (type.endsWith("64")) return Types.BIGINT; @@ -308,4 +398,11 @@ public class CHResultSet extends AbstractResultSet { } + public static int[] supportedTypes() { + return new int[] { + Types.BIGINT, Types.INTEGER, Types.VARCHAR, Types.FLOAT, + Types.DATE, Types.TIMESTAMP, Types.BLOB + }; + } + } -- GitLab