blob: d1bdebeb658aeeca8ceb4f9b610e868eda4ca7e5 [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.internal.jdbc2;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcColumnMeta;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcIndexMeta;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcPrimaryKeyMeta;
import org.apache.ignite.internal.processors.odbc.jdbc.JdbcTableMeta;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.util.typedef.F;
import static java.sql.DatabaseMetaData.columnNullable;
import static java.sql.DatabaseMetaData.tableIndexOther;
import static java.sql.ResultSetMetaData.columnNoNulls;
import static java.sql.Types.BIGINT;
import static java.sql.Types.BINARY;
import static java.sql.Types.BOOLEAN;
import static java.sql.Types.DATE;
import static java.sql.Types.DECIMAL;
import static java.sql.Types.DOUBLE;
import static java.sql.Types.FLOAT;
import static java.sql.Types.INTEGER;
import static java.sql.Types.OTHER;
import static java.sql.Types.SMALLINT;
import static java.sql.Types.TIME;
import static java.sql.Types.TIMESTAMP;
import static java.sql.Types.TINYINT;
import static java.sql.Types.VARCHAR;
import static org.apache.ignite.internal.processors.query.QueryUtils.PRIMARY_KEY_INDEX;
/**
* Utility methods for JDBC driver.
*/
public class JdbcUtils {
/** The only possible name for catalog. */
public static final String CATALOG_NAME = "IGNITE";
/** Name of TABLE type. */
public static final String TYPE_TABLE = "TABLE";
/** Name of VIEW type. */
public static final String TYPE_VIEW = "VIEW";
/**
* Converts Java class name to type from {@link Types}.
*
* @param cls Java class name.
* @return Type from {@link Types}.
*/
public static int type(String cls) {
if (Boolean.class.getName().equals(cls) || boolean.class.getName().equals(cls))
return BOOLEAN;
else if (Byte.class.getName().equals(cls) || byte.class.getName().equals(cls))
return TINYINT;
else if (Short.class.getName().equals(cls) || short.class.getName().equals(cls))
return SMALLINT;
else if (Integer.class.getName().equals(cls) || int.class.getName().equals(cls))
return INTEGER;
else if (Long.class.getName().equals(cls) || long.class.getName().equals(cls))
return BIGINT;
else if (Float.class.getName().equals(cls) || float.class.getName().equals(cls))
return FLOAT;
else if (Double.class.getName().equals(cls) || double.class.getName().equals(cls))
return DOUBLE;
else if (String.class.getName().equals(cls))
return VARCHAR;
else if (byte[].class.getName().equals(cls))
return BINARY;
else if (Time.class.getName().equals(cls))
return TIME;
else if (Timestamp.class.getName().equals(cls))
return TIMESTAMP;
else if (Date.class.getName().equals(cls) || java.sql.Date.class.getName().equals(cls))
return DATE;
else if (BigDecimal.class.getName().equals(cls))
return DECIMAL;
else
return OTHER;
}
/**
* Converts Java class name to SQL type name.
*
* @param cls Java class name.
* @return SQL type name.
*/
public static String typeName(String cls) {
if (Boolean.class.getName().equals(cls) || boolean.class.getName().equals(cls))
return "BOOLEAN";
else if (Byte.class.getName().equals(cls) || byte.class.getName().equals(cls))
return "TINYINT";
else if (Short.class.getName().equals(cls) || short.class.getName().equals(cls))
return "SMALLINT";
else if (Integer.class.getName().equals(cls) || int.class.getName().equals(cls))
return "INTEGER";
else if (Long.class.getName().equals(cls) || long.class.getName().equals(cls))
return "BIGINT";
else if (Float.class.getName().equals(cls) || float.class.getName().equals(cls))
return "FLOAT";
else if (Double.class.getName().equals(cls) || double.class.getName().equals(cls))
return "DOUBLE";
else if (String.class.getName().equals(cls))
return "VARCHAR";
else if (byte[].class.getName().equals(cls))
return "BINARY";
else if (Time.class.getName().equals(cls))
return "TIME";
else if (Timestamp.class.getName().equals(cls))
return "TIMESTAMP";
else if (Date.class.getName().equals(cls) || java.sql.Date.class.getName().equals(cls))
return "DATE";
else if (BigDecimal.class.getName().equals(cls))
return "DECIMAL";
else
return "OTHER";
}
/**
* Determines whether type is nullable.
*
* @param name Column name.
* @param cls Java class name.
* @return {@code True} if nullable.
*/
public static boolean nullable(String name, String cls) {
return !"_KEY".equalsIgnoreCase(name) &&
!"_VAL".equalsIgnoreCase(name) &&
!(boolean.class.getName().equals(cls) ||
byte.class.getName().equals(cls) ||
short.class.getName().equals(cls) ||
int.class.getName().equals(cls) ||
long.class.getName().equals(cls) ||
float.class.getName().equals(cls) ||
double.class.getName().equals(cls));
}
/**
* Checks whether a class is SQL-compliant.
*
* @param cls Class.
* @return Whether given type is SQL-compliant.
*/
static boolean isSqlType(Class<?> cls) {
return QueryUtils.isSqlType(cls) || cls == URL.class;
}
/**
* Convert exception to {@link SQLException}.
*
* @param e Converted Exception.
* @param msgForUnknown Message non-convertable exception.
* @return JDBC {@link SQLException}.
* @see IgniteQueryErrorCode
*/
public static SQLException convertToSqlException(Exception e, String msgForUnknown) {
return convertToSqlException(e, msgForUnknown, null);
}
/**
* Convert exception to {@link SQLException}.
*
* @param e Converted Exception.
* @param msgForUnknown Message for non-convertable exception.
* @param sqlStateForUnknown SQLSTATE for non-convertable exception.
* @return JDBC {@link SQLException}.
* @see IgniteQueryErrorCode
*/
public static SQLException convertToSqlException(Exception e, String msgForUnknown, String sqlStateForUnknown) {
SQLException sqlEx = null;
Throwable t = e;
while (sqlEx == null && t != null) {
if (t instanceof SQLException)
return (SQLException)t;
else if (t instanceof IgniteSQLException)
return ((IgniteSQLException)t).toJdbcException();
t = t.getCause();
}
return new SQLException(msgForUnknown, sqlStateForUnknown, e);
}
/**
* @param colMeta Column metadata.
* @param pos Ordinal position.
* @return Column metadata row.
*/
public static List<Object> columnRow(JdbcColumnMeta colMeta, int pos) {
List<Object> row = new ArrayList<>(24);
row.add(CATALOG_NAME); // 1. TABLE_CAT
row.add(colMeta.schemaName()); // 2. TABLE_SCHEM
row.add(colMeta.tableName()); // 3. TABLE_NAME
row.add(colMeta.columnName()); // 4. COLUMN_NAME
row.add(colMeta.dataType()); // 5. DATA_TYPE
row.add(colMeta.dataTypeName()); // 6. TYPE_NAME
row.add(colMeta.precision() == -1 ? null : colMeta.precision()); // 7. COLUMN_SIZE
row.add((Integer)null); // 8. BUFFER_LENGTH
row.add(colMeta.scale() == -1 ? null : colMeta.scale()); // 9. DECIMAL_DIGITS
row.add(10); // 10. NUM_PREC_RADIX
row.add(colMeta.isNullable() ? columnNullable : columnNoNulls); // 11. NULLABLE
row.add((String)null); // 12. REMARKS
row.add(colMeta.defaultValue()); // 13. COLUMN_DEF
row.add(colMeta.dataType()); // 14. SQL_DATA_TYPE
row.add((Integer)null); // 15. SQL_DATETIME_SUB
row.add(Integer.MAX_VALUE); // 16. CHAR_OCTET_LENGTH
row.add(pos); // 17. ORDINAL_POSITION
row.add(colMeta.isNullable() ? "YES" : "NO"); // 18. IS_NULLABLE
row.add((String)null); // 19. SCOPE_CATALOG
row.add((String)null); // 20. SCOPE_SCHEMA
row.add((String)null); // 21. SCOPE_TABLE
row.add((Short)null); // 22. SOURCE_DATA_TYPE
row.add("NO"); // 23. IS_AUTOINCREMENT
row.add("NO"); // 23. IS_GENERATEDCOLUMN
return row;
}
/**
* @param idxMeta Index metadata.
* @return List of result rows correspond to index.
*/
public static List<List<Object>> indexRows(JdbcIndexMeta idxMeta) {
List<List<Object>> rows = new ArrayList<>(idxMeta.fields().size());
for (int i = 0; i < idxMeta.fields().size(); ++i) {
List<Object> row = new ArrayList<>(13);
// Now, only primary key index (and PK proxy) can be unique.
boolean nonUnique = !idxMeta.indexName().startsWith(PRIMARY_KEY_INDEX);
row.add(CATALOG_NAME); // TABLE_CAT
row.add(idxMeta.schemaName()); // TABLE_SCHEM
row.add(idxMeta.tableName()); // TABLE_NAME
row.add(nonUnique); // NON_UNIQUE
row.add(null); // INDEX_QUALIFIER (index catalog)
row.add(idxMeta.indexName()); // INDEX_NAME
row.add(tableIndexOther); // TYPE
row.add(i + 1); // ORDINAL_POSITION
row.add(idxMeta.fields().get(i)); // COLUMN_NAME
row.add(idxMeta.fieldsAsc().get(i) ? "A" : "D"); // ASC_OR_DESC
row.add((Integer)0); // CARDINALITY
row.add((Integer)0); // PAGES
row.add((String)null); // FILTER_CONDITION
rows.add(row);
}
return rows;
}
/**
* @param pkMeta Primary key metadata.
* @return Result set rows for primary key.
*/
public static List<List<Object>> primaryKeyRows(JdbcPrimaryKeyMeta pkMeta) {
List<List<Object>> rows = new ArrayList<>(pkMeta.fields().size());
for (int i = 0; i < pkMeta.fields().size(); ++i) {
List<Object> row = new ArrayList<>(6);
row.add(CATALOG_NAME); // table catalog
row.add(pkMeta.schemaName());
row.add(pkMeta.tableName());
row.add(pkMeta.fields().get(i));
row.add(i + 1); // sequence number
row.add(pkMeta.name());
rows.add(row);
}
return rows;
}
/**
* @param tblMeta Table metadata.
* @return Table metadata row.
*/
public static List<Object> tableRow(JdbcTableMeta tblMeta) {
List<Object> row = new ArrayList<>(10);
row.add(CATALOG_NAME);
row.add(tblMeta.schemaName());
row.add(tblMeta.tableName());
row.add(tblMeta.tableType());
row.add(null);
row.add(null);
row.add(null);
row.add(null);
row.add(null);
row.add(null);
return row;
}
/**
* Normalize schema name. If it is quoted - unquote and leave as is, otherwise - convert to upper case.
*
* @param schemaName Schema name.
* @return Normalized schema name.
*/
public static String normalizeSchema(String schemaName) {
if (F.isEmpty(schemaName))
return QueryUtils.DFLT_SCHEMA;
String res;
if (schemaName.startsWith("\"") && schemaName.endsWith("\""))
res = schemaName.substring(1, schemaName.length() - 1);
else
res = schemaName.toUpperCase();
return res;
}
}