blob: c8cb24d653f10c7152131c0aee3ec5be0fd01f26 [file] [log] [blame]
/*
* 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.console.agent.db.dialect;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.ignite.cache.QueryIndex;
import org.apache.ignite.console.agent.db.DbColumn;
import org.apache.ignite.console.agent.db.DbTable;
/**
* Metadata dialect that uses standard JDBC for reading metadata.
*/
public class JdbcMetadataDialect extends DatabaseMetadataDialect {
/** */
private static final String[] TABLES_ONLY = {"TABLE"};
/** */
private static final String[] TABLES_AND_VIEWS = {"TABLE", "VIEW"};
/** Schema catalog index. */
private static final int TBL_CATALOG_IDX = 1;
/** Schema name index. */
private static final int TBL_SCHEMA_IDX = 2;
/** Table name index. */
private static final int TBL_NAME_IDX = 3;
/** Primary key column name index. */
private static final int PK_COL_NAME_IDX = 4;
/** Column name index. */
private static final int COL_NAME_IDX = 4;
/** Column data type index. */
private static final int COL_DATA_TYPE_IDX = 5;
/** Column type name index. */
private static final int COL_TYPE_NAME_IDX = 6;
/** Column nullable index. */
private static final int COL_NULLABLE_IDX = 11;
/** Index name index. */
private static final int IDX_NAME_IDX = 6;
/** Index column name index. */
private static final int IDX_COL_NAME_IDX = 9;
/** Index column descend index. */
private static final int IDX_ASC_OR_DESC_IDX = 10;
/** {@inheritDoc} */
@Override public Collection<String> schemas(Connection conn) throws SQLException {
Collection<String> schemas = new ArrayList<>();
ResultSet rs = conn.getMetaData().getSchemas();
Set<String> sys = systemSchemas();
while (rs.next()) {
String schema = rs.getString(1);
// Skip system schemas.
if (sys.contains(schema))
continue;
schemas.add(schema);
}
return schemas;
}
/**
* @return If {@code true} use catalogs for table division.
*/
protected boolean useCatalog() {
return false;
}
/**
* @return If {@code true} use schemas for table division.
*/
protected boolean useSchema() {
return true;
}
/** {@inheritDoc} */
@Override public Collection<DbTable> tables(Connection conn, List<String> schemas, boolean tblsOnly)
throws SQLException {
DatabaseMetaData dbMeta = conn.getMetaData();
Set<String> sys = systemSchemas();
Collection<String> unsignedTypes = unsignedTypes(dbMeta);
if (schemas.isEmpty())
schemas.add(null);
Collection<DbTable> tbls = new ArrayList<>();
for (String toSchema: schemas) {
try (ResultSet tblsRs = dbMeta.getTables(useCatalog() ? toSchema : null, useSchema() ? toSchema : null, "%",
tblsOnly ? TABLES_ONLY : TABLES_AND_VIEWS)) {
while (tblsRs.next()) {
String tblCatalog = tblsRs.getString(TBL_CATALOG_IDX);
String tblSchema = tblsRs.getString(TBL_SCHEMA_IDX);
String tblName = tblsRs.getString(TBL_NAME_IDX);
// In case of MySql we should use catalog.
String schema = tblSchema != null ? tblSchema : tblCatalog;
// Skip system schemas.
if (sys.contains(schema))
continue;
Set<String> pkCols = new LinkedHashSet<>();
try (ResultSet pkRs = dbMeta.getPrimaryKeys(tblCatalog, tblSchema, tblName)) {
while (pkRs.next())
pkCols.add(pkRs.getString(PK_COL_NAME_IDX));
}
Map.Entry<String, Set<String>> uniqueIdxAsPk = null;
// If PK not found, trying to use first UNIQUE index as key.
if (pkCols.isEmpty()) {
Map<String, Set<String>> uniqueIdxs = new LinkedHashMap<>();
try (ResultSet idxRs = dbMeta.getIndexInfo(tblCatalog, tblSchema, tblName, true, true)) {
while (idxRs.next()) {
String idxName = idxRs.getString(IDX_NAME_IDX);
String colName = idxRs.getString(IDX_COL_NAME_IDX);
if (idxName == null || colName == null)
continue;
Set<String> idxCols = uniqueIdxs.get(idxName);
if (idxCols == null) {
idxCols = new LinkedHashSet<>();
uniqueIdxs.put(idxName, idxCols);
}
idxCols.add(colName);
}
}
uniqueIdxAsPk = uniqueIndexAsPk(uniqueIdxs);
if (uniqueIdxAsPk != null)
pkCols.addAll(uniqueIdxAsPk.getValue());
}
Collection<DbColumn> cols = new ArrayList<>();
try (ResultSet colsRs = dbMeta.getColumns(tblCatalog, tblSchema, tblName, null)) {
while (colsRs.next()) {
String colName = colsRs.getString(COL_NAME_IDX);
cols.add(new DbColumn(
colName,
colsRs.getInt(COL_DATA_TYPE_IDX),
pkCols.contains(colName),
colsRs.getInt(COL_NULLABLE_IDX) == DatabaseMetaData.columnNullable,
unsignedTypes.contains(colsRs.getString(COL_TYPE_NAME_IDX))));
}
}
String uniqueIdxAsPkName = uniqueIdxAsPk != null ? uniqueIdxAsPk.getKey() : null;
Map<String, QueryIndex> idxs = new LinkedHashMap<>();
try (ResultSet idxRs = dbMeta.getIndexInfo(tblCatalog, tblSchema, tblName, false, true)) {
while (idxRs.next()) {
String idxName = idxRs.getString(IDX_NAME_IDX);
String colName = idxRs.getString(IDX_COL_NAME_IDX);
// Skip {@code null} names and unique index used as PK.
if (idxName == null || colName == null || idxName.equals(uniqueIdxAsPkName))
continue;
QueryIndex idx = idxs.get(idxName);
if (idx == null) {
idx = index(idxName);
idxs.put(idxName, idx);
}
String askOrDesc = idxRs.getString(IDX_ASC_OR_DESC_IDX);
Boolean asc = askOrDesc == null || "A".equals(askOrDesc);
idx.getFields().put(colName, asc);
}
}
// Remove index that is equals to primary key.
if (!pkCols.isEmpty()) {
for (Map.Entry<String, QueryIndex> entry : idxs.entrySet()) {
QueryIndex idx = entry.getValue();
if (pkCols.equals(idx.getFields().keySet())) {
idxs.remove(entry.getKey());
break;
}
}
}
tbls.add(table(schema, tblName, cols, idxs.values()));
}
}
}
return tbls;
}
}