org.apache.kylin.jdbc.KylinMeta Maven / Gradle / Ivy
The newest version!
/*
* 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.kylin.jdbc;
import static org.apache.kylin.jdbc.LoggerUtils.entry;
import static org.apache.kylin.jdbc.LoggerUtils.exit;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import org.apache.calcite.avatica.AvaticaUtils;
import org.apache.calcite.avatica.ColumnMetaData;
import org.apache.calcite.avatica.MetaImpl;
import org.apache.calcite.avatica.NoSuchStatementException;
import org.apache.calcite.avatica.QueryState;
import org.apache.calcite.avatica.remote.TypedValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of Avatica interface
*/
public class KylinMeta extends MetaImpl {
private static final Logger logger = LoggerFactory.getLogger(KylinMeta.class);
private KMetaProject metaProject;
public KylinMeta(KylinConnection connection) {
super(connection);
entry(logger);
exit(logger);
}
private KylinConnection connection() {
return (KylinConnection) connection;
}
// insert/update/delete go this path, ignorable for Kylin
@Override
public StatementHandle prepare(ConnectionHandle ch, String sql, long maxRowCount) {
entry(logger);
StatementHandle result = super.createStatement(ch);
result.signature = connection().mockPreparedSignature(sql);
exit(logger);
return result;
}
@Override
public ExecuteBatchResult prepareAndExecuteBatch(StatementHandle sh, List sqlCommands) {
entry(logger);
ExecuteBatchResult result = new ExecuteBatchResult(new long[] {});
exit(logger);
return result;
}
@Override
public ExecuteBatchResult executeBatch(StatementHandle sh, List> parameterValues)
throws NoSuchStatementException {
entry(logger);
ExecuteBatchResult result = new ExecuteBatchResult(new long[] {});
exit(logger);
return result;
}
/**
* @deprecated (2022-11-20, real execution happens in KylinResultSet.execute())
*/
@Override
@Deprecated
public ExecuteResult execute(StatementHandle sh, List parameterValues, long maxRowCount)
throws NoSuchStatementException {
entry(logger);
final MetaResultSet metaResultSet = MetaResultSet.create(sh.connectionId, sh.id, false, sh.signature, null);
ExecuteResult result = new ExecuteResult(Collections.singletonList(metaResultSet));
exit(logger);
return result;
}
@Override
public ExecuteResult execute(StatementHandle sh, List parameterValues, int maxRowsInFirstFrame)
throws NoSuchStatementException {
entry(logger);
final MetaResultSet metaResultSet = MetaResultSet.create(sh.connectionId, sh.id, false, sh.signature, null);
ExecuteResult result = new ExecuteResult(Collections.singletonList(metaResultSet));
exit(logger);
return result;
}
/**
* @deprecated (2022-11-20, mimic from CalciteMetaImpl, real execution happens via callback in KylinResultSet.execute())
*/
@Override
@Deprecated
public ExecuteResult prepareAndExecute(StatementHandle sh, String sql, long maxRowCount, PrepareCallback callback) {
entry(logger);
try {
synchronized (callback.getMonitor()) {
callback.clear();
sh.signature = connection().mockPreparedSignature(sql);
callback.assign(sh.signature, null, -1);
}
callback.execute();
final MetaResultSet metaResultSet = MetaResultSet.create(sh.connectionId, sh.id, false, sh.signature, null);
ExecuteResult result = new ExecuteResult(Collections.singletonList(metaResultSet));
exit(logger);
return result;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public ExecuteResult prepareAndExecute(StatementHandle sh, String sql, long maxRowCount, int maxRowsInFirstFrame,
PrepareCallback callback) throws NoSuchStatementException {
entry(logger);
try {
synchronized (callback.getMonitor()) {
callback.clear();
sh.signature = connection().mockPreparedSignature(sql);
callback.assign(sh.signature, null, -1);
}
callback.execute();
final MetaResultSet metaResultSet = MetaResultSet.create(sh.connectionId, sh.id, false, sh.signature, null);
ExecuteResult result = new ExecuteResult(Collections.singletonList(metaResultSet));
exit(logger);
return result;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Override
public void closeStatement(StatementHandle h) {
entry(logger);
// nothing to do
exit(logger);
}
private KMetaProject getMetaProject() {
entry(logger);
try {
if (metaProject == null) {
KylinConnection conn = connection();
metaProject = conn.getRemoteClient().retrieveMetaData(conn.getProject());
}
exit(logger);
return metaProject;
} catch (IOException e) {
logger.error(e.getMessage(), e);
throw new RuntimeException(e);
}
}
@Override
public MetaResultSet getTableTypes(ConnectionHandle ch) {
entry(logger);
MetaResultSet resultSet = createResultSet(metaTableTypes, MetaTableType.class, "TABLE_TYPE");
exit(logger);
return resultSet;
}
@Override
public MetaResultSet getCatalogs(ConnectionHandle ch) {
entry(logger);
List catalogs = getMetaProject().catalogs;
MetaResultSet resultSet = createResultSet(catalogs, KMetaCatalog.class, "TABLE_CAT");
exit(logger);
return resultSet;
}
@Override
public MetaResultSet getSchemas(ConnectionHandle ch, String catalog, Pat schemaPattern) {
entry(logger);
List schemas = getMetaProject().getSchemas(catalog, schemaPattern);
MetaResultSet resultSet = createResultSet(schemas, KMetaSchema.class, "TABLE_SCHEM", "TABLE_CATALOG");
entry(logger);
return resultSet;
}
@Override
public MetaResultSet getTables(ConnectionHandle ch, String catalog, Pat schemaPattern, Pat tableNamePattern,
List typeList) {
entry(logger);
List tables = getMetaProject().getTables(catalog, schemaPattern, tableNamePattern, typeList);
MetaResultSet resultSet = createResultSet(tables, KMetaTable.class, //
"TABLE_CAT", //
"TABLE_SCHEM", //
"TABLE_NAME", //
"TABLE_TYPE", //
"REMARKS", //
"TYPE_CAT", //
"TYPE_SCHEM", //
"TYPE_NAME", //
"SELF_REFERENCING_COL_NAME", //
"REF_GENERATION");
exit(logger);
return resultSet;
}
@Override
public MetaResultSet getColumns(ConnectionHandle ch, String catalog, Pat schemaPattern, Pat tableNamePattern,
Pat columnNamePattern) {
entry(logger);
List columns = getMetaProject().getColumns(catalog, schemaPattern, tableNamePattern,
columnNamePattern);
MetaResultSet resultSet = createResultSet(columns, KMetaColumn.class, //
"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_CATALOG", //
"SCOPE_TABLE", //
"SOURCE_DATA_TYPE", //
"IS_AUTOINCREMENT", //
"IS_GENERATEDCOLUMN");
exit(logger);
return resultSet;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private MetaResultSet createResultSet(List iterable, Class clazz, String... names) {
entry(logger);
final List columns = new ArrayList<>();
final List fields = new ArrayList<>();
final List fieldNames = new ArrayList<>();
for (String name : names) {
final int index = fields.size();
final String fieldName = AvaticaUtils.toCamelCase(name);
final Field field;
try {
field = clazz.getField(fieldName);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
columns.add(columnMetaData(name, index, field.getType(), true));
fields.add(field);
fieldNames.add(fieldName);
}
CursorFactory cursorFactory = CursorFactory.record(clazz, fields, fieldNames);
Signature signature = new Signature(columns, "", null, Collections. emptyMap(), cursorFactory,
StatementType.SELECT);
StatementHandle sh = this.createStatement(connection().handle);
Frame frame = new Frame(0, true, iterable);
MetaResultSet resultSet = MetaResultSet.create(connection().id, sh.id, true, signature, frame);
exit(logger);
return resultSet;
}
// ============================================================================
public static interface NamedWithChildren extends Named {
List extends NamedWithChildren> getChildren();
}
public static List extends NamedWithChildren> searchByPatterns(NamedWithChildren parent, Pat... patterns) {
if (patterns == null || patterns.length <= 0) {
throw new AssertionError();
}
List extends NamedWithChildren> children = findChildren(parent, patterns[0]);
if (patterns.length == 1) {
return children;
} else {
List result = new ArrayList<>();
Pat[] subPatterns = Arrays.copyOfRange(patterns, 1, patterns.length);
for (NamedWithChildren c : children) {
result.addAll(searchByPatterns(c, subPatterns));
}
return result;
}
}
private static List extends NamedWithChildren> findChildren(NamedWithChildren parent, Pat pattern) {
if (null == pattern.s || pattern.s.equals("%")) {
return parent.getChildren();
}
List result = new ArrayList<>();
Pattern regex = likeToRegex(pattern);
for (NamedWithChildren c : parent.getChildren()) {
if (regex.matcher(c.getName()).matches()) {
result.add(c);
}
}
return result;
}
/**
* Converts a LIKE-style pattern (where '%' represents a wild-card,
* escaped using '\') to a Java regex.
*/
private static Pattern likeToRegex(Pat pattern) {
StringBuilder buf = new StringBuilder("^");
char[] charArray = pattern.s.toCharArray();
int slash = -2;
for (int i = 0; i < charArray.length; i++) {
char c = charArray[i];
if (slash == i - 1) {
buf.append('[').append(c).append(']');
} else {
switch (c) {
case '\\':
slash = i;
break;
case '%':
buf.append(".*");
break;
case '[':
buf.append("\\[");
break;
case ']':
buf.append("\\]");
break;
default:
buf.append('[').append(c).append(']');
}
}
}
buf.append("$");
return Pattern.compile(buf.toString());
}
// ============================================================================
private static final List metaTableTypes = new ArrayList();
static {
metaTableTypes.add(new MetaTableType("TABLE"));
}
public static class KMetaProject implements NamedWithChildren {
public final String projectName;
public final List catalogs;
public KMetaProject(String projectName, List catalogs) {
this.projectName = projectName;
this.catalogs = catalogs;
}
@SuppressWarnings("unchecked")
public List getSchemas(String catalog, Pat schemaPattern) {
return (List) searchByPatterns(this, Pat.of(catalog), schemaPattern);
}
@SuppressWarnings("unchecked")
public List getTables(String catalog, Pat schemaPattern, Pat tableNamePattern,
List typeList) {
return (List) searchByPatterns(this, Pat.of(catalog), schemaPattern, tableNamePattern);
}
@SuppressWarnings("unchecked")
public List getColumns(String catalog, Pat schemaPattern, Pat tableNamePattern,
Pat columnNamePattern) {
return (List) searchByPatterns(this, Pat.of(catalog), schemaPattern, tableNamePattern,
columnNamePattern);
}
@Override
public String getName() {
return projectName;
}
@Override
public List extends NamedWithChildren> getChildren() {
return catalogs;
}
}
public static class KMetaCatalog implements NamedWithChildren {
public final String tableCat;
public final List schemas;
public KMetaCatalog(String tableCatalog, List schemas) {
this.tableCat = tableCatalog;
this.schemas = schemas;
}
@Override
public String getName() {
return tableCat;
}
@Override
public List extends NamedWithChildren> getChildren() {
return schemas;
}
}
public static class KMetaSchema extends MetaSchema implements NamedWithChildren {
public final List tables;
public KMetaSchema(String tableCatalog, String tableSchem, List tables) {
super(tableCatalog, tableSchem);
this.tables = tables;
}
@Override
public List extends NamedWithChildren> getChildren() {
return tables;
}
}
public static class KMetaTable extends MetaTable implements NamedWithChildren {
public final List columns;
public KMetaTable(String tableCat, String tableSchem, String tableName, String tableType,
List columns) {
super(tableCat, tableSchem, tableName, tableType);
this.columns = columns;
}
@Override
public List extends NamedWithChildren> getChildren() {
return columns;
}
}
public static class KMetaColumn implements NamedWithChildren {
public final String tableCat;
public final String tableSchem;
@MetaImpl.ColumnNoNulls
public final String tableName;
@MetaImpl.ColumnNoNulls
public final String columnName;
public final int dataType;
@MetaImpl.ColumnNoNulls
public final String typeName;
public final Integer columnSize;
@MetaImpl.ColumnNullableUnknown
public final Integer bufferLength = null;
public final Integer decimalDigits;
public final Integer numPrecRadix;
public final int nullable;
public final String remarks;
public final String columnDef = null;
@MetaImpl.ColumnNullableUnknown
public final Integer sqlDataType = null;
@MetaImpl.ColumnNullableUnknown
public final Integer sqlDatetimeSub = null;
public final Integer charOctetLength;
public final int ordinalPosition;
@MetaImpl.ColumnNoNulls
public final String isNullable;
public final String scopeCatalog = null;
public final String scopeSchema = null;
public final String scopeTable = null;
public final Short sourceDataType = null;
@MetaImpl.ColumnNoNulls
public final String isAutoincrement = "";
@MetaImpl.ColumnNoNulls
public final String isGeneratedcolumn = "";
public KMetaColumn(String tableCat, String tableSchem, String tableName, String columnName, int dataType,
String typeName, int columnSize, Integer decimalDigits, int numPrecRadix, int nullable,
int charOctetLength, int ordinalPosition, String isNullable, String remarks) {
this.tableCat = tableCat;
this.tableSchem = tableSchem;
this.tableName = tableName;
this.columnName = columnName;
this.dataType = dataType;
this.typeName = typeName;
this.columnSize = columnSize;
this.decimalDigits = decimalDigits;
this.numPrecRadix = numPrecRadix;
this.nullable = nullable;
this.charOctetLength = charOctetLength;
this.ordinalPosition = ordinalPosition;
this.isNullable = isNullable;
this.remarks = remarks;
}
@Override
public String getName() {
return this.columnName;
}
@Override
public List getChildren() {
return Collections.emptyList();
}
}
@Override
public Frame fetch(StatementHandle h, long offset, int fetchMaxRowCount) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean syncResults(StatementHandle sh, QueryState state, long offset) {
// TODO Auto-generated method stub
return false;
}
@Override
public void commit(ConnectionHandle ch) {
// TODO Auto-generated method stub
}
@Override
public void rollback(ConnectionHandle ch) {
// TODO Auto-generated method stub
}
}