Merge branches 'ignite-843' and 'master' of https://git-wip-us.apache.org/repos/asf/incubator-ignite into ignite-843
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/b4321d89 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/b4321d89 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/b4321d89 Branch: refs/heads/ignite-843 Commit: b4321d895d51009d5a7b22af26a07961404f8806 Parents: eaf880b e9a0d06 Author: Alexey Kuznetsov <akuznet...@apache.org> Authored: Wed Aug 19 15:08:20 2015 +0700 Committer: Alexey Kuznetsov <akuznet...@apache.org> Committed: Wed Aug 19 15:08:20 2015 +0700 ---------------------------------------------------------------------- .../parser/dialect/OracleMetadataDialect.java | 101 ++++++++++--------- .../ignite/schema/model/PojoDescriptor.java | 6 +- 2 files changed, 57 insertions(+), 50 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/b4321d89/modules/schema-import-db/src/main/java/org/apache/ignite/schema/parser/dialect/OracleMetadataDialect.java ---------------------------------------------------------------------- diff --cc modules/schema-import-db/src/main/java/org/apache/ignite/schema/parser/dialect/OracleMetadataDialect.java index 860ff68,0000000..30dda5d mode 100644,000000..100644 --- a/modules/schema-import-db/src/main/java/org/apache/ignite/schema/parser/dialect/OracleMetadataDialect.java +++ b/modules/schema-import-db/src/main/java/org/apache/ignite/schema/parser/dialect/OracleMetadataDialect.java @@@ -1,281 -1,0 +1,284 @@@ +/* + * 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.schema.parser.dialect; + +import org.apache.ignite.schema.parser.*; + +import java.sql.*; +import java.util.*; + +import static java.sql.Types.*; + +/** + * Oracle specific metadata dialect. + */ +public class OracleMetadataDialect extends DatabaseMetadataDialect { + /** SQL to get columns metadata. */ + private static final String SQL_COLUMNS = "SELECT a.owner, a.table_name, a.column_name, a.nullable," + + " a.data_type, a.data_precision, a.data_scale " + + "FROM all_tab_columns a %s" + + " WHERE a.owner = '%s'" + + " ORDER BY a.owner, a.table_name, a.column_id"; + + /** SQL to get list of PRIMARY KEYS columns. */ + private static final String SQL_PRIMARY_KEYS = "SELECT b.column_name" + + " FROM all_constraints a" + + " INNER JOIN all_cons_columns b ON a.owner = b.owner AND a.constraint_name = b.constraint_name" + + " WHERE a.owner = ? and a.table_name = ? AND a.constraint_type = 'P'"; + + /** SQL to get indexes metadata. */ + private static final String SQL_INDEXES = "SELECT i.index_name, u.column_expression, i.column_name, i.descend" + + " FROM all_ind_columns i" + + " LEFT JOIN user_ind_expressions u on u.index_name = i.index_name and i.table_name = u.table_name" + + " WHERE i.index_owner = ? and i.table_name = ?" + + " ORDER BY i.index_name, i.column_position"; + + /** Owner index. */ + private static final int OWNER_IDX = 1; + + /** Table name index. */ + private static final int TBL_NAME_IDX = 2; + + /** Column name index. */ + private static final int COL_NAME_IDX = 3; + + /** Nullable index. */ + private static final int NULLABLE_IDX = 4; + + /** Data type index. */ + private static final int DATA_TYPE_IDX = 5; + + /** Numeric precision index. */ + private static final int DATA_PRECISION_IDX = 6; + + /** Numeric scale index. */ + private static final int DATA_SCALE_IDX = 7; + + /** Index name index. */ + private static final int IDX_NAME_IDX = 1; + + /** Index name index. */ + private static final int IDX_EXPR_IDX = 2; + + /** Index column name index. */ + private static final int IDX_COL_NAME_IDX = 3; + + /** Index column sort order index. */ + private static final int IDX_COL_DESCEND_IDX = 4; + + /** + * @param rs Result set with column type metadata from Oracle database. + * @return JDBC type. + * @throws SQLException If failed to decode type. + */ + private int decodeType(ResultSet rs) throws SQLException { - switch (rs.getString(DATA_TYPE_IDX)) { - case "CHAR": - case "NCHAR": - return CHAR; ++ String type = rs.getString(DATA_TYPE_IDX); + - case "VARCHAR2": - case "NVARCHAR2": - return VARCHAR; ++ if (type.startsWith("TIMESTAMP")) ++ return TIMESTAMP; ++ else { ++ switch (type) { ++ case "CHAR": ++ case "NCHAR": ++ return CHAR; + - case "LONG": - return LONGVARCHAR; ++ case "VARCHAR2": ++ case "NVARCHAR2": ++ return VARCHAR; + - case "LONG RAW": - return LONGVARBINARY; ++ case "LONG": ++ return LONGVARCHAR; + - case "FLOAT": - return FLOAT; ++ case "LONG RAW": ++ return LONGVARBINARY; + - case "NUMBER": - int precision = rs.getInt(DATA_PRECISION_IDX); - int scale = rs.getInt(DATA_SCALE_IDX); ++ case "FLOAT": ++ return FLOAT; + - if (scale > 0) { - if (scale < 4 && precision < 19) - return FLOAT; ++ case "NUMBER": ++ int precision = rs.getInt(DATA_PRECISION_IDX); ++ int scale = rs.getInt(DATA_SCALE_IDX); + - if (scale > 4 || precision > 19) - return DOUBLE; ++ if (scale > 0) { ++ if (scale < 4 && precision < 19) ++ return FLOAT; + - return NUMERIC; - } - else { - if (precision < 1) - return INTEGER; ++ if (scale > 4 || precision > 19) ++ return DOUBLE; + - if (precision < 2) - return BOOLEAN; ++ return NUMERIC; ++ } ++ else { ++ if (precision < 1) ++ return INTEGER; + - if (precision < 4) - return TINYINT; ++ if (precision < 2) ++ return BOOLEAN; + - if (precision < 6) - return SMALLINT; ++ if (precision < 4) ++ return TINYINT; + - if (precision < 11) - return INTEGER; ++ if (precision < 6) ++ return SMALLINT; + - if (precision < 20) - return BIGINT; ++ if (precision < 11) ++ return INTEGER; + - return NUMERIC; - } ++ if (precision < 20) ++ return BIGINT; + - case "DATE": - return DATE; ++ return NUMERIC; ++ } + - case "TIMESTAMP": - return TIMESTAMP; ++ case "DATE": ++ return DATE; + - case "BFILE": - case "BLOB": - return BLOB; ++ case "BFILE": ++ case "BLOB": ++ return BLOB; + - case "CLOB": - case "NCLOB": - case "XMLTYPE": - return CLOB; ++ case "CLOB": ++ case "NCLOB": ++ case "XMLTYPE": ++ return CLOB; ++ } + } + + return OTHER; + } + + /** + * Retrieve primary key columns. + * + * @param stmt Prepared SQL statement to execute. + * @param owner DB owner. + * @param tbl Table name. + * @return Primary key columns. + * @throws SQLException If failed to retrieve primary key columns. + */ + private Set<String> primaryKeys(PreparedStatement stmt, String owner, String tbl) throws SQLException { + Set<String> pkCols = new HashSet<>(); + + stmt.setString(1, owner); + stmt.setString(2, tbl); + + try (ResultSet pkRs = stmt.executeQuery()) { + while(pkRs.next()) + pkCols.add(pkRs.getString(1)); + } + + return pkCols; + } + + /** + * Retrieve index columns. + * + * @param stmt Prepared SQL statement to execute. + * @param owner DB owner. + * @param tbl Table name. + * @return Index columns. + * @throws SQLException If failed to retrieve indexes columns. + */ + private Map<String, Map<String, Boolean>> indexes(PreparedStatement stmt, String owner, String tbl) + throws SQLException { + Map<String, Map<String, Boolean>> idxs = new LinkedHashMap<>(); + + stmt.setString(1, owner); + stmt.setString(2, tbl); + + try (ResultSet idxsRs = stmt.executeQuery()) { + while (idxsRs.next()) { + String idxName = idxsRs.getString(IDX_NAME_IDX); + + Map<String, Boolean> idx = idxs.get(idxName); + + if (idx == null) { + idx = new LinkedHashMap<>(); + + idxs.put(idxName, idx); + } + + String expr = idxsRs.getString(IDX_EXPR_IDX); + + String col = expr == null ? idxsRs.getString(IDX_COL_NAME_IDX) : expr.replaceAll("\"", ""); + + idx.put(col, "DESC".equals(idxsRs.getString(IDX_COL_DESCEND_IDX))); + } + } + + return idxs; + } + + /** {@inheritDoc} */ + @Override public Collection<DbTable> tables(Connection conn, boolean tblsOnly) throws SQLException { + Collection<DbTable> tbls = new ArrayList<>(); + + PreparedStatement pkStmt = conn.prepareStatement(SQL_PRIMARY_KEYS); + + PreparedStatement idxStmt = conn.prepareStatement(SQL_INDEXES); + + try (Statement colsStmt = conn.createStatement()) { + Collection<DbColumn> cols = new ArrayList<>(); + + Set<String> pkCols = Collections.emptySet(); + Map<String, Map<String, Boolean>> idxs = Collections.emptyMap(); + + String user = conn.getMetaData().getUserName().toUpperCase(); + + String sql = String.format(SQL_COLUMNS, + tblsOnly ? "INNER JOIN all_tables b on a.table_name = b.table_name and a.owner = b.owner" : "", user); + + try (ResultSet colsRs = colsStmt.executeQuery(sql)) { + String prevSchema = ""; + String prevTbl = ""; + + boolean first = true; + + while (colsRs.next()) { + String owner = colsRs.getString(OWNER_IDX); + String tbl = colsRs.getString(TBL_NAME_IDX); + + boolean changed = !owner.equals(prevSchema) || !tbl.equals(prevTbl); + + if (changed) { + if (first) + first = false; + else + tbls.add(table(prevSchema, prevTbl, cols, idxs)); + + prevSchema = owner; + prevTbl = tbl; + cols = new ArrayList<>(); + pkCols = primaryKeys(pkStmt, owner, tbl); + idxs = indexes(idxStmt, owner, tbl); + } + + String colName = colsRs.getString(COL_NAME_IDX); + + cols.add(new DbColumn(colName, decodeType(colsRs), pkCols.contains(colName), + !"N".equals(colsRs.getString(NULLABLE_IDX)))); + } + + if (!cols.isEmpty()) + tbls.add(table(prevSchema, prevTbl, cols, idxs)); + } + } + + return tbls; + } +}