com.mongodb.jdbc.MongoDatabaseMetaData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mongodb-jdbc Show documentation
Show all versions of mongodb-jdbc Show documentation
JDBC Driver for MongoDB Atlas SQL interface
package com.mongodb.jdbc;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.RowIdLifetime;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Pattern;
import org.bson.BsonBoolean;
import org.bson.BsonInt32;
import org.bson.BsonNull;
import org.bson.BsonString;
import org.bson.BsonValue;
public class MongoDatabaseMetaData implements DatabaseMetaData {
MongoConnection conn;
private String serverVersion;
public MongoDatabaseMetaData(MongoConnection conn) {
this.conn = conn;
}
public static String escapeString(String value) {
String escaped = value.replace("'", "''");
return escaped.replace("\\", "\\\\");
}
// Actual max size is 16777216, we reserve 216 for other bits of encoding,
// since this value is used to set limits on literals and field names.
// This is arbitrary and conservative.
static final int APPROXIMATE_DOC_SIZE = 16777000;
//----------------------------------------------------------------------
// First, a variety of minor information about the target database.
@Override
public boolean allProceduresAreCallable() throws SQLException {
return true;
}
@Override
public boolean allTablesAreSelectable() throws SQLException {
return true;
}
@Override
public String getURL() throws SQLException {
return conn.getURL();
}
@Override
public String getUserName() throws SQLException {
return conn.getUser();
}
@Override
public boolean isReadOnly() throws SQLException {
return true; // we are only read-only for now.
}
@Override
public boolean nullsAreSortedHigh() throws SQLException {
return false; // missing and NULL < all other values
}
@Override
public boolean nullsAreSortedLow() throws SQLException {
return true; // missing and NULL < all other values
}
@Override
public boolean nullsAreSortedAtStart() throws SQLException {
return false; // missing and NULL < all other values
}
@Override
public boolean nullsAreSortedAtEnd() throws SQLException {
return false; // missing and NULL < all other values
}
@Override
public String getDatabaseProductName() throws SQLException {
return "MongoDB Atlas Data Lake";
}
@Override
public String getDatabaseProductVersion() throws SQLException {
if (serverVersion != null) {
return serverVersion;
}
serverVersion = conn.getServerVersion();
return serverVersion;
}
@Override
public String getDriverName() throws SQLException {
return "MongoDB Atlas Data Lake JDBC Driver";
}
@Override
public String getDriverVersion() throws SQLException {
return MongoDriver.VERSION;
}
@Override
public int getDriverMajorVersion() {
return MongoDriver.MAJOR_VERSION;
}
@Override
public int getDriverMinorVersion() {
return MongoDriver.MINOR_VERSION;
}
@Override
public boolean usesLocalFiles() throws SQLException {
// No files are local on Atlas Data Lake
return false;
}
@Override
public boolean usesLocalFilePerTable() throws SQLException {
// No files are local on Atlas Data Lake
return false;
}
@Override
public boolean supportsMixedCaseIdentifiers() throws SQLException {
return true;
}
@Override
public boolean storesUpperCaseIdentifiers() throws SQLException {
return false;
}
@Override
public boolean storesLowerCaseIdentifiers() throws SQLException {
return false;
}
@Override
public boolean storesMixedCaseIdentifiers() throws SQLException {
return supportsMixedCaseIdentifiers();
}
@Override
public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException {
return true;
}
@Override
public boolean storesUpperCaseQuotedIdentifiers() throws SQLException {
return false;
}
@Override
public boolean storesLowerCaseQuotedIdentifiers() throws SQLException {
return false;
}
@Override
public boolean storesMixedCaseQuotedIdentifiers() throws SQLException {
return true;
}
@Override
public String getIdentifierQuoteString() throws SQLException {
return "`";
}
@Override
public String getSQLKeywords() throws SQLException {
// These come directly from the mongosqld keywords file, minus the keywords from SQL2003.
// See resources/keywords.py
return "ADDDATE,"
+ "AUTO_INCREMENT,"
+ "BINLOG,"
+ "BOOL,"
+ "BTREE,"
+ "CHANGE,"
+ "CHANNEL,"
+ "CHARSET,"
+ "CODE,"
+ "COLUMNS,"
+ "COMMENT,"
+ "DATABASE,"
+ "DATABASES,"
+ "DATETIME,"
+ "DATE_ADD,"
+ "DATE_SUB,"
+ "DAY_HOUR,"
+ "DAY_MICROSECOND,"
+ "DAY_MINUTE,"
+ "DAY_SECOND,"
+ "DBS,"
+ "DISABLE,"
+ "DIV,"
+ "DUAL,"
+ "ENABLE,"
+ "ENGINE,"
+ "ENGINES,"
+ "ENUM,"
+ "ERRORS,"
+ "EVENT,"
+ "EVENTS,"
+ "EXPLAIN,"
+ "EXTENDED,"
+ "FIELDS,"
+ "FLUSH,"
+ "FN,"
+ "FORCE,"
+ "FORMAT,"
+ "FULLTEXT,"
+ "GRANTS,"
+ "GROUP_CONCAT,"
+ "HASH,"
+ "HOSTS,"
+ "HOUR_MICROSECOND,"
+ "HOUR_MINUTE,"
+ "HOUR_SECOND,"
+ "IGNORE,"
+ "INDEX,"
+ "INDEXES,"
+ "JSON,"
+ "KEYS,"
+ "KILL,"
+ "LIMIT,"
+ "LOCK,"
+ "LOGS,"
+ "LONGTEXT,"
+ "LOW_PRIORITY,"
+ "MASTER,"
+ "MEDIUMBLOB,"
+ "MEDIUMTEXT,"
+ "MICROSECOND,"
+ "MINUS,"
+ "MINUTE_MICROSECOND,"
+ "MINUTE_SECOND,"
+ "MODIFY,"
+ "MUTEX,"
+ "OBJECTID,"
+ "OFF,"
+ "OFFSET,"
+ "OJ,"
+ "PARTITIONS,"
+ "PLUGINS,"
+ "PROCESSLIST,"
+ "PROFILE,"
+ "PROFILES,"
+ "PROXY,"
+ "QUARTER,"
+ "QUERY,"
+ "REGEXP,"
+ "RELAYLOG,"
+ "RENAME,"
+ "RLIKE,"
+ "SAMPLE,"
+ "SCHEMAS,"
+ "SECOND_MICROSECOND,"
+ "SEPARATOR,"
+ "SERIAL,"
+ "SHOW,"
+ "SIGNED,"
+ "SLAVE,"
+ "SQL_BIGINT,"
+ "SQL_DATE,"
+ "SQL_DOUBLE,"
+ "SQL_TIMESTAMP,"
+ "SQL_TSI_DAY,"
+ "SQL_TSI_HOUR,"
+ "SQL_TSI_MINUTE,"
+ "SQL_TSI_MONTH,"
+ "SQL_TSI_QUARTER,"
+ "SQL_TSI_SECOND,"
+ "SQL_TSI_WEEK,"
+ "SQL_TSI_YEAR,"
+ "SQL_VARCHAR,"
+ "STATUS,"
+ "STORAGE,"
+ "STRAIGHT_JOIN,"
+ "SUBDATE,"
+ "SUBSTR,"
+ "TABLES,"
+ "TEXT,"
+ "TIMESTAMPADD,"
+ "TIMESTAMPDIFF,"
+ "TINYINT,"
+ "TINYTEXT,"
+ "TRADITIONAL,"
+ "TRIGGERS,"
+ "UNLOCK,"
+ "UNSIGNED,"
+ "USE,"
+ "UTC_DATE,"
+ "UTC_TIMESTAMP,"
+ "VARIABLES,"
+ "WARNINGS,"
+ "WEEK,"
+ "XOR,"
+ "YEAR_MONTH";
}
@Override
public String getNumericFunctions() throws SQLException {
return MongoFunction.numericFunctionsString;
}
@Override
public String getStringFunctions() throws SQLException {
return MongoFunction.stringFunctionsString;
}
@Override
public String getSystemFunctions() throws SQLException {
return "DATABASE,USER,SYSTEM_USER,SESSION_USER,VERSION";
}
@Override
public String getTimeDateFunctions() throws SQLException {
return MongoFunction.dateFunctionsString;
}
@Override
public String getSearchStringEscape() throws SQLException {
return "\\";
}
@Override
public String getExtraNameCharacters() throws SQLException {
// Retrieves all the "extra" characters that can be used in unquoted identifier names (those beyond a-z, A-Z, 0-9 and _).
return "";
}
//--------------------------------------------------------------------
// Functions describing which features are supported.
@Override
public boolean supportsAlterTableWithAddColumn() throws SQLException {
return false;
}
@Override
public boolean supportsAlterTableWithDropColumn() throws SQLException {
return false;
}
@Override
public boolean supportsColumnAliasing() throws SQLException {
return true;
}
@Override
public boolean nullPlusNonNullIsNull() throws SQLException {
return true;
}
@Override
public boolean supportsConvert() throws SQLException {
return true;
}
@Override
public boolean supportsConvert(int fromType, int toType) throws SQLException {
switch (toType) {
case Types.ARRAY:
return false;
case Types.BLOB:
case Types.BINARY:
case Types.BIT:
case Types.TIMESTAMP:
case Types.DECIMAL:
case Types.DOUBLE:
case Types.INTEGER:
case Types.LONGVARCHAR:
case Types.NULL:
return true;
}
return false;
}
@Override
public boolean supportsTableCorrelationNames() throws SQLException {
return true;
}
@Override
public boolean supportsDifferentTableCorrelationNames() throws SQLException {
return false;
}
@Override
public boolean supportsExpressionsInOrderBy() throws SQLException {
return true;
}
@Override
public boolean supportsOrderByUnrelated() throws SQLException {
return true;
}
@Override
public boolean supportsGroupBy() throws SQLException {
return true;
}
@Override
public boolean supportsGroupByUnrelated() throws SQLException {
return true;
}
@Override
public boolean supportsGroupByBeyondSelect() throws SQLException {
return true;
}
@Override
public boolean supportsLikeEscapeClause() throws SQLException {
return true;
}
@Override
public boolean supportsMultipleResultSets() throws SQLException {
return false;
}
@Override
public boolean supportsMultipleTransactions() throws SQLException {
// We don't support transactions for now.
return false;
}
@Override
public boolean supportsNonNullableColumns() throws SQLException {
return false;
}
@Override
public boolean supportsMinimumSQLGrammar() throws SQLException {
// If this isn't true, it's a bug.
return true;
}
@Override
public boolean supportsCoreSQLGrammar() throws SQLException {
// If this isn't true, it's a bug.
return true;
}
@Override
public boolean supportsExtendedSQLGrammar() throws SQLException {
return false;
}
@Override
public boolean supportsANSI92EntryLevelSQL() throws SQLException {
// If it does not, this is a bug.
return true;
}
@Override
public boolean supportsANSI92IntermediateSQL() throws SQLException {
// If it does not, this is a bug.
return true;
}
@Override
public boolean supportsANSI92FullSQL() throws SQLException {
return true;
}
@Override
public boolean supportsIntegrityEnhancementFacility() throws SQLException {
return false;
}
@Override
public boolean supportsOuterJoins() throws SQLException {
return true;
}
@Override
public boolean supportsFullOuterJoins() throws SQLException {
return false;
}
@Override
public boolean supportsLimitedOuterJoins() throws SQLException {
return false;
}
@Override
public String getSchemaTerm() throws SQLException {
// We do not support schemata.
return "schema";
}
@Override
public String getProcedureTerm() throws SQLException {
// We do not support procedures.
return "procedure";
}
@Override
public String getCatalogTerm() throws SQLException {
return "database";
}
@Override
public boolean isCatalogAtStart() throws SQLException {
return true;
}
@Override
public String getCatalogSeparator() throws SQLException {
return ".";
}
@Override
public boolean supportsSchemasInDataManipulation() throws SQLException {
return false;
}
@Override
public boolean supportsSchemasInProcedureCalls() throws SQLException {
return false;
}
@Override
public boolean supportsSchemasInTableDefinitions() throws SQLException {
return false;
}
@Override
public boolean supportsSchemasInIndexDefinitions() throws SQLException {
return false;
}
@Override
public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException {
return false;
}
@Override
public boolean supportsCatalogsInDataManipulation() throws SQLException {
// at least when we support data manipulation calls. Also A => B and !A ==> true.
return true;
}
@Override
public boolean supportsCatalogsInProcedureCalls() throws SQLException {
// at least when we support data manipulation calls. Also A => B and !A ==> true.
return true;
}
@Override
public boolean supportsCatalogsInTableDefinitions() throws SQLException {
// at least when we support data manipulation calls. Also A => B and !A ==> true.
return true;
}
@Override
public boolean supportsCatalogsInIndexDefinitions() throws SQLException {
// at least when we support data manipulation calls. Also A => B and !A ==> true.
return true;
}
@Override
public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException {
// at least when we support data manipulation calls. Also A => B and !A ==> true.
return true;
}
@Override
public boolean supportsPositionedDelete() throws SQLException {
return false;
}
@Override
public boolean supportsPositionedUpdate() throws SQLException {
return false;
}
@Override
public boolean supportsSelectForUpdate() throws SQLException {
return false;
}
@Override
public boolean supportsStoredProcedures() throws SQLException {
return false;
}
@Override
public boolean supportsSubqueriesInComparisons() throws SQLException {
return true;
}
@Override
public boolean supportsSubqueriesInExists() throws SQLException {
return true;
}
@Override
public boolean supportsSubqueriesInIns() throws SQLException {
return true;
}
@Override
public boolean supportsSubqueriesInQuantifieds() throws SQLException {
return true;
}
@Override
public boolean supportsCorrelatedSubqueries() throws SQLException {
return true;
}
@Override
public boolean supportsUnion() throws SQLException {
return true;
}
@Override
public boolean supportsUnionAll() throws SQLException {
return true;
}
@Override
public boolean supportsOpenCursorsAcrossCommit() throws SQLException {
// Though we don't support commit.
return true;
}
@Override
public boolean supportsOpenCursorsAcrossRollback() throws SQLException {
// Though we don't support rollback.
return true;
}
@Override
public boolean supportsOpenStatementsAcrossCommit() throws SQLException {
// Though we don't support commit.
return true;
}
@Override
public boolean supportsOpenStatementsAcrossRollback() throws SQLException {
// Though we don't support rollback.
return true;
}
//----------------------------------------------------------------------
// The following group of methods exposes various limitations
// based on the target database with the current driver.
// Unless otherwise specified, a result of zero means there is no
// limit, or the limit is not known.
@Override
public int getMaxBinaryLiteralLength() throws SQLException {
return APPROXIMATE_DOC_SIZE;
}
@Override
public int getMaxCharLiteralLength() throws SQLException {
return APPROXIMATE_DOC_SIZE;
}
@Override
public int getMaxColumnNameLength() throws SQLException {
return APPROXIMATE_DOC_SIZE;
}
@Override
public int getMaxColumnsInGroupBy() throws SQLException {
// No specific max size, though it would be limited by max document size.
return 0;
}
@Override
public int getMaxColumnsInIndex() throws SQLException {
// MongoDB has no limit in 4.2+. Datalake doesn't support indexes, yet,
// but returning 0 is fine.
return 0;
}
@Override
public int getMaxColumnsInOrderBy() throws SQLException {
// The only limit would be based on document size.
return 0;
}
@Override
public int getMaxColumnsInSelect() throws SQLException {
// The only limit would be based on document size.
return 0;
}
@Override
public int getMaxColumnsInTable() throws SQLException {
return 0;
}
@Override
public int getMaxConnections() throws SQLException {
return 0;
}
@Override
public int getMaxCursorNameLength() throws SQLException {
return 0;
}
@Override
public int getMaxIndexLength() throws SQLException {
return 0;
}
@Override
public int getMaxSchemaNameLength() throws SQLException {
return 0;
}
@Override
public int getMaxProcedureNameLength() throws SQLException {
return 0;
}
@Override
public int getMaxCatalogNameLength() throws SQLException {
return 255;
}
@Override
public int getMaxRowSize() throws SQLException {
return APPROXIMATE_DOC_SIZE;
}
@Override
public boolean doesMaxRowSizeIncludeBlobs() throws SQLException {
return true;
}
@Override
public int getMaxStatementLength() throws SQLException {
return APPROXIMATE_DOC_SIZE;
}
@Override
public int getMaxStatements() throws SQLException {
return 0;
}
@Override
public int getMaxTableNameLength() throws SQLException {
return APPROXIMATE_DOC_SIZE;
}
@Override
public int getMaxTablesInSelect() throws SQLException {
return 0;
}
@Override
public int getMaxUserNameLength() throws SQLException {
return APPROXIMATE_DOC_SIZE;
}
//----------------------------------------------------------------------
@Override
public int getDefaultTransactionIsolation() throws SQLException {
return java.sql.Connection.TRANSACTION_NONE;
}
@Override
public boolean supportsTransactions() throws SQLException {
return false;
}
@Override
public boolean supportsTransactionIsolationLevel(int level) throws SQLException {
return level == java.sql.Connection.TRANSACTION_NONE;
}
@Override
public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException {
// at least when we support data manipulation calls. Also A => B and !A ==> true.
return true;
}
@Override
public boolean supportsDataManipulationTransactionsOnly() throws SQLException {
return false;
}
@Override
public boolean dataDefinitionCausesTransactionCommit() throws SQLException {
return false;
}
@Override
public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
return false;
}
@Override
public ResultSet getProcedures(
String catalog, String schemaPattern, String procedureNamePattern) throws SQLException {
// No procedures so we always return an empty result set.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "PROCEDURE_CAT", "PROCEDURE_CAT", "string", n));
doc.values.add(new Column("", "", "", "PROCEDURE_SCHEM", "PROCEDURE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "PROCEDURE_NAME", "PROCEDURE_NAME", "string", n));
doc.values.add(new Column("", "", "", "REMARKS", "REMARKS", "string", n));
doc.values.add(new Column("", "", "", "PROCEDURE_TYPE", "PROCEDURE_TYPE", "int", n));
doc.values.add(new Column("", "", "", "SPECIFIC_NAME", "SPECIFIC_NAME", "string", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public ResultSet getProcedureColumns(
String catalog,
String schemaPattern,
String procedureNamePattern,
String columnNamePattern)
throws SQLException {
// No procedures so we always return an empty result set.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "PROCEDURE_CAT", "PROCEDURE_CAT", "string", n));
doc.values.add(new Column("", "", "", "PROCEDURE_SCHEM", "PROCEDURE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "PROCEDURE_NAME", "PROCEDURE_NAME", "string", n));
doc.values.add(new Column("", "", "", "COLUMN_NAME", "COLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "COLUMN_TYPE", "COLUMN_TYPE", "int", n));
doc.values.add(new Column("", "", "", "DATA_TYPE", "DATA_TYPE", "int", n));
doc.values.add(new Column("", "", "", "TYPE_NAME", "TYPE_NAME", "string", n));
doc.values.add(new Column("", "", "", "PRECISION", "PRECISION", "int", n));
doc.values.add(new Column("", "", "", "LENGTH", "LENGTH", "int", n));
doc.values.add(new Column("", "", "", "SCALE", "SCALE", "int", n));
doc.values.add(new Column("", "", "", "RADIX", "RADIX", "int", n));
doc.values.add(new Column("", "", "", "NULLABLE", "NULLABLE", "int", n));
doc.values.add(new Column("", "", "", "REMARKS", "REMARKS", "string", n));
doc.values.add(new Column("", "", "", "COLUMN_DEF", "COLUMN_DEF", "string", n));
doc.values.add(new Column("", "", "", "SQL_DATA_TYPE", "SQL_DATA_TYPE", "int", n));
doc.values.add(new Column("", "", "", "SQL_DATETIME_SUB", "SQL_DATETIME_SUB", "int", n));
doc.values.add(new Column("", "", "", "CHAR_OCTET_LENGTH", "CHAR_OCTET_LENGTH", "int", n));
doc.values.add(new Column("", "", "", "ORDINAL_POSITION", "ORDINAL_POSITION", "int", n));
doc.values.add(new Column("", "", "", "IS_NULLABLE", "IS_NULLABLE", "string", n));
doc.values.add(new Column("", "", "", "SPECIFIC_NAME", "SPECIFIC_NAME", "string", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
private String patternCond(String colName, String pattern) {
if (pattern == null) {
return " 1 "; //just return a true condition.
}
return " " + colName + " like '" + escapeString(pattern) + "' ";
}
private String equalsCond(String colName, String literal) {
if (literal == null) {
return " 1 "; //just return a true condition.
}
return " " + colName + " = '" + escapeString(literal) + "' ";
}
@Override
public ResultSet getTables(
String catalog, String schemaPattern, String tableNamePattern, String types[])
throws SQLException {
Statement stmt = conn.createStatement();
// What JDBC calls catalog is the SCHEMA column in the TABLES table. It's annoying, but it's
// what works the best with Tableau, and what the MySQL/MariaDB JDBC drivers do. So even
// though we call it SCHEMA in the INFORMATION_SCHEMA, we will use the catalog argument to
// filter here. We ignore types because we only have one kind of table type (e.g., no
// views).
return stmt.executeQuery(
"select "
+ " TABLE_SCHEMA as TABLE_CAT, "
+ " '' as TABLE_SCHEM, "
+ " TABLE_NAME, "
+ " TABLE_TYPE, "
+ " NULL as REMARKS, "
+ " NULL as TYPE_CAT, "
+ " NULL as TYPE_SCHEM, "
+ " NULL as TYPE_NAME, "
+ " NULL as SELF_REFERENCING_COL_NAME, "
+ " NULL as REF_GENERATION "
+ "from INFORMATION_SCHEMA.TABLES "
+ "where"
+ patternCond("TABLE_SCHEMA", catalog)
+ " and"
+ patternCond("TABLE_NAME", tableNamePattern)
+ " order by TABLE_TYPE, TABLE_CAT, TABLE_SCHEMA, TABLE_NAME");
}
@Override
public ResultSet getSchemas() throws SQLException {
Statement stmt = conn.createStatement();
// This is a hack for now. We need an empty resultset. Better would be to
// select from DUAL where 1=0, but that does not work, it creates a batch errror.
return stmt.executeQuery(
"select "
+ " '' as TABLE_SCHEM, "
+ " '' as TABLE_CATALOG "
+ "from INFORMATION_SCHEMA.TABLE_PRIVILEGES ");
}
@Override
public ResultSet getCatalogs() throws SQLException {
Statement stmt = conn.createStatement();
return stmt.executeQuery(
"select "
+ " SCHEMA_NAME as TABLE_CAT "
+ "from INFORMATION_SCHEMA.SCHEMATA "
+ " order by TABLE_CAT");
}
@Override
public ResultSet getTableTypes() throws SQLException {
MongoResultDoc doc = new MongoResultDoc();
ArrayList docs = new ArrayList<>(1);
doc.values = new ArrayList<>(1);
doc.values.add(
new Column(
"", "", "", "TABLE_TYPE", "TABLE_TYPE", "string", new BsonString("TABLE")));
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
public static String[] typeNames =
new String[] {
"null",
"document",
"binData",
"numeric",
"string",
// Keep this for now or Tableau cannot figure out the types properly.
"varchar",
"long",
// Keep this for now or Tableau cannot figure out the types properly.
"bigint",
"tinyint",
"int",
"datetime",
"date",
"double",
"decimal",
};
public static final HashMap typeNums = new HashMap<>();
public static final HashMap typePrecs = new HashMap<>();
public static final HashMap typeScales = new HashMap<>();
public static final HashMap typeBytes = new HashMap<>();
static {
for (String name : typeNames) {
typeNums.put(name, typeNum(name));
typePrecs.put(name, typePrec(name));
typeScales.put(name, typeScale(name));
typeBytes.put(name, typeBytes(name));
}
}
public static int typeNum(String typeName) {
if (typeName == null) {
return Types.NULL;
}
switch (typeName) {
case "null":
return Types.NULL;
case "bool":
return Types.BIT;
case "document":
return Types.NULL;
case "binData":
return Types.BINARY;
case "numeric":
return Types.NUMERIC;
case "string":
case "varchar":
return Types.LONGVARCHAR;
case "long":
case "int":
case "bigint":
case "tinyint":
return Types.INTEGER;
case "date":
case "datetime":
return Types.TIMESTAMP;
case "double":
return Types.DOUBLE;
case "decimal":
return Types.DECIMAL;
}
return 0;
}
public static Integer typePrec(String typeName) {
if (typeName == null) {
return 0;
}
switch (typeName) {
case "numeric":
return 34;
case "double":
return 15;
case "long":
case "bigint":
return 19;
case "int":
return 10;
case "decimal":
return 34;
}
return 0;
}
public static Integer typeScale(String typeName) {
if (typeName == null) {
return 0;
}
switch (typeName) {
case "numeric":
return 34;
case "double":
return 15;
case "decimal":
return 34;
}
return null;
}
public static Integer typeBytes(String typeName) {
if (typeName == null) {
return 0;
}
switch (typeName) {
case "numeric":
return 16;
case "double":
return 8;
case "long":
case "bigint":
case "date":
case "datatime":
return 8;
case "int":
return 4;
case "decimal":
return 16;
case "tinyint":
case "bool":
return 1;
}
return null;
}
public static Integer typeRadix(String typeName) {
if (typeName == null) {
return 0;
}
switch (typeName) {
case "numeric":
return 10;
case "double":
return 2;
case "long":
case "bigint":
return 2;
case "int":
return 2;
case "decimal":
return 10;
}
return 0;
}
private String getTypeCase(String col, HashMap outs) {
StringBuilder ret = new StringBuilder("case ");
ret.append(col);
ret.append("\n");
for (String name : typeNames) {
Integer out = outs.get(name);
String range = out == null ? "NULL" : out.toString();
ret.append("when ");
ret.append("'");
ret.append(name);
ret.append("' then ");
ret.append(range);
ret.append(" \n");
}
ret.append("end");
return ret.toString();
}
private String getDataTypeNumCase(String col) {
return getTypeCase(col, typeNums);
}
private String getDataTypePrecCase(String col) {
return getTypeCase(col, typePrecs);
}
private String getDataTypeScaleCase(String col) {
return getTypeCase(col, typeScales);
}
private String getDataTypeBytesCase(String col) {
return getTypeCase(col, typeBytes);
}
@Override
public ResultSet getColumns(
String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern)
throws SQLException {
final String nullableCase =
""
+ "case IS_NULLABLE "
+ " when 'YES' then "
+ ResultSetMetaData.columnNullable
+ " when 'NO' then "
+ ResultSetMetaData.columnNoNulls
+ " end";
Statement stmt = conn.createStatement();
return stmt.executeQuery(
"select "
+ " TABLE_SCHEMA as TABLE_CAT, "
+ " '' as TABLE_SCHEM, "
+ " TABLE_NAME, "
+ " COLUMN_NAME, "
+ getDataTypeNumCase("DATA_TYPE")
+ " as DATA_TYPE, "
+ " DATA_TYPE as TYPE_NAME, "
+ " CHARACTER_MAXIMUM_LENGTH as COLUMN_SIZE, "
+ " 0 as BUFFER_LENGTH, "
+ getDataTypeScaleCase("DATA_TYPE")
+ " as DECIMAL_DIGITS, "
+ getDataTypePrecCase("DATA_TYPE")
+ " as NUM_PREC_RADIX, "
+ nullableCase
+ " as NULLABLE, "
+ " '' as REMARKS, "
+ " NULL as COLUMN_DEF, "
+ " 0 as SQL_DATA_TYPE, "
+ " 0 as SQL_DATETIME_SUB, "
+ getDataTypeBytesCase("DATA_TYPE")
+ " as CHAR_OCTET_LENGTH, "
+ " ORDINAL_POSITION, "
+ " IS_NULLABLE, "
+ " NULL as SCOPE_CATALOG, "
+ " NULL as SCOPE_SCHEMA, "
+ " NULL as SCOPE_TABLE, "
+ " 0 as SOURCE_DATA_TYPE, "
+ " 0 as IS_AUTOINCREMENT, "
+ " 0 as IS_GENERATEDCOLUMN "
+ "from INFORMATION_SCHEMA.COLUMNS "
+ "where "
+ patternCond("TABLE_SCHEMA", catalog)
+ " and "
+ patternCond("TABLE_NAME", tableNamePattern)
+ " and "
+ patternCond("COLUMN_NAME", columnNamePattern));
}
@Override
public ResultSet getColumnPrivileges(
String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern)
throws SQLException {
Statement stmt = conn.createStatement();
return stmt.executeQuery(
"select "
+ " TABLE_SCHEMA as TABLE_CAT, "
+ " '' as TABLE_SCHEM, "
+ " TABLE_NAME, "
+ " COLUMN_NAME, "
+ " GRANTOR, "
+ " GRANTEE, "
+ " PRIVILEGE_TYPE as PRIVILEGE, "
+ " IS_GRANTABLE "
+ "from INFORMATION_SCHEMA.COLUMN_PRIVILEGES "
+ "where "
+ patternCond("TABLE_SCHEMA", catalog)
+ " and "
+ patternCond("TABLE_NAME", tableNamePattern)
+ " and "
+ patternCond("COLUMN_NAME", columnNamePattern));
}
@Override
public ResultSet getTablePrivileges(
String catalog, String schemaPattern, String tableNamePattern) throws SQLException {
Statement stmt = conn.createStatement();
return stmt.executeQuery(
"select "
+ " TABLE_SCHEMA as TABLE_CAT, "
+ " '' as TABLE_SCHEM, "
+ " TABLE_NAME, "
+ " GRANTOR, "
+ " GRANTEE, "
+ " PRIVILEGE_TYPE as PRIVILEGE, "
+ " IS_GRANTABLE "
+ "from INFORMATION_SCHEMA.TABLE_PRIVILEGES "
+ "where "
+ patternCond("TABLE_SCHEMA", catalog)
+ " and "
+ patternCond("TABLE_NAME", tableNamePattern));
}
@Override
public ResultSet getBestRowIdentifier(
String catalog, String schema, String table, int scope, boolean nullable)
throws SQLException {
Statement stmt = conn.createStatement();
return stmt.executeQuery(
"select "
+ "NULL as SCOPE, "
+ "c.COLUMN_NAME AS COLUMN_NAME, "
+ getDataTypeNumCase("c.DATA_TYPE")
+ " as DATA_TYPE, "
+ "c.DATA_TYPE as TYPE_NAME, "
+ getDataTypePrecCase("c.DATA_TYPE")
+ " as COLUMN_SIZE, "
+ "NULL as BUFFER_LENGTH, "
+ getDataTypeScaleCase("c.DATA_TYPE")
+ " as DECIMAL_DIGITS, "
+ bestRowNotPseudo
+ " as PSEUDO_COLUMN"
+ " from INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu inner join INFORMATION_SCHEMA.COLUMNS as c "
+ "on kcu.TABLE_SCHEMA = c.TABLE_SCHEMA "
+ "and kcu.TABLE_NAME = c.TABLE_NAME "
+ "and kcu.COLUMN_NAME = c.COLUMN_NAME "
+ " inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc "
+ "on c.TABLE_SCHEMA = tc.TABLE_SCHEMA "
+ "and c.TABLE_NAME = tc.TABLE_NAME "
+ "where "
+ equalsCond("kcu.TABLE_SCHEMA", catalog)
+ " and "
+ equalsCond("kcu.TABLE_NAME", table)
+ " and tc.CONSTRAINT_TYPE = 'PRIMARY KEY'");
}
@Override
public ResultSet getVersionColumns(String catalog, String schema, String table)
throws SQLException {
// We do not have updates, so this will always be empty.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "SCOPE", "SCOPE", "string", n));
doc.values.add(new Column("", "", "", "COLUMN_NAME", "COLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "DATA_TYPE", "DATA_TYPE", "int", n));
doc.values.add(new Column("", "", "", "TYPE_NAME", "TYPE_NAME", "string", n));
doc.values.add(new Column("", "", "", "COLUMN_SIZE", "COLUMN_SIZE", "int", n));
doc.values.add(new Column("", "", "", "BUFFER_LENGTH", "BUFFER_LENGTH", "int", n));
doc.values.add(new Column("", "", "", "DECIMAL_DIGITS", "DECIMAL_DIGITS", "int", n));
doc.values.add(new Column("", "", "", "PSEUDO_COLUMN", "PSEUDO_COLUMN", "int", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public ResultSet getPrimaryKeys(String catalog, String schema, String table)
throws SQLException {
Statement stmt = conn.createStatement();
return stmt.executeQuery(
"select "
+ "kcu.TABLE_SCHEMA as TABLE_CAT, "
+ "'' as TABLE_SCHEM, "
+ "kcu.TABLE_NAME as TABLE_NAME, "
+ "kcu.COLUMN_NAME AS COLUMN_NAME, "
+ "kcu.ORDINAL_POSITION as KEY_SEQ, "
+ "kcu.CONSTRAINT_NAME as PK_NAME "
+ "from INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu inner join INFORMATION_SCHEMA.TABLE_CONSTRAINTS as c "
+ "on kcu.TABLE_SCHEMA = c.TABLE_SCHEMA "
+ "and kcu.TABLE_NAME = c.TABLE_NAME "
+ "where "
+ equalsCond("kcu.TABLE_SCHEMA", catalog)
+ " and "
+ equalsCond("kcu.TABLE_NAME", table)
+ " and c.CONSTRAINT_TYPE = 'PRIMARY KEY'");
}
@Override
public ResultSet getImportedKeys(String catalog, String schema, String table)
throws SQLException {
// We do not have foreign keys, so this will always be empty.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "PKTABLE_CAT", "PKTABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "PKTABLE_SCHEM", "PKTABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "PKTABLE_NAME", "PKTABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "PKCOLUMN_NAME", "PKCOLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_CAT", "FKTABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_SCHEM", "FKTABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_NAME", "FKTABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "FKCOLUMN_NAME", "FKCOLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "KEY_SEQ", "KEY_SEQ", "int", n));
doc.values.add(new Column("", "", "", "UPDATE_RULE", "UPDATE_RULE", "int", n));
doc.values.add(new Column("", "", "", "DELETE_RULE", "DELETE_RULE", "int", n));
doc.values.add(new Column("", "", "", "FK_NAME", "FK_NAME", "string", n));
doc.values.add(new Column("", "", "", "PK_NAME", "PK_NAME", "string", n));
doc.values.add(new Column("", "", "", "DEFERRABILITY", "DEFERRABILITY", "int", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public ResultSet getExportedKeys(String catalog, String schema, String table)
throws SQLException {
// We do not have foreign keys, so this will always be empty.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "PKTABLE_CAT", "PKTABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "PKTABLE_SCHEM", "PKTABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "PKTABLE_NAME", "PKTABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "PKCOLUMN_NAME", "PKCOLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_CAT", "FKTABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_SCHEM", "FKTABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_NAME", "FKTABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "FKCOLUMN_NAME", "FKCOLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "KEY_SEQ", "KEY_SEQ", "int", n));
doc.values.add(new Column("", "", "", "UPDATE_RULE", "UPDATE_RULE", "int", n));
doc.values.add(new Column("", "", "", "DELETE_RULE", "DELETE_RULE", "int", n));
doc.values.add(new Column("", "", "", "FK_NAME", "FK_NAME", "string", n));
doc.values.add(new Column("", "", "", "PK_NAME", "PK_NAME", "string", n));
doc.values.add(new Column("", "", "", "DEFERRABILITY", "DEFERRABILITY", "int", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public ResultSet getCrossReference(
String parentCatalog,
String parentSchema,
String parentTable,
String foreignCatalog,
String foreignSchema,
String foreignTable)
throws SQLException {
// We do not have foreign keys, so this will always be empty.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "PKTABLE_CAT", "PKTABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "PKTABLE_SCHEM", "PKTABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "PKTABLE_NAME", "PKTABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "PKCOLUMN_NAME", "PKCOLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_CAT", "FKTABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_SCHEM", "FKTABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "FKTABLE_NAME", "FKTABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "FKCOLUMN_NAME", "FKCOLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "KEY_SEQ", "KEY_SEQ", "int", n));
doc.values.add(new Column("", "", "", "UPDATE_RULE", "UPDATE_RULE", "int", n));
doc.values.add(new Column("", "", "", "DELETE_RULE", "DELETE_RULE", "int", n));
doc.values.add(new Column("", "", "", "FK_NAME", "FK_NAME", "string", n));
doc.values.add(new Column("", "", "", "PK_NAME", "PK_NAME", "string", n));
doc.values.add(new Column("", "", "", "DEFERRABILITY", "DEFERRABILITY", "int", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
private MongoResultDoc getTypeInfoDoc(
String typeName,
int dataType,
int precision,
String literalPrefix,
String literalSuffix,
int nullable,
boolean caseSensitive,
int searchable,
boolean unsigned,
boolean fixedPrecScale,
int minScale,
int maxScale,
int numPrecRadix) {
BsonValue n = new BsonNull();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(
new Column(
"", "", "", "TYPE_NAME", "TYPE_NAME", "string", new BsonString(typeName)));
doc.values.add(
new Column("", "", "", "DATA_TYPE", "DATA_TYPE", "int", new BsonInt32(dataType)));
doc.values.add(
new Column("", "", "", "PRECISION", "PRECISION", "int", new BsonInt32(precision)));
doc.values.add(
new Column(
"",
"",
"",
"LITERAL_PREFIX",
"LITERAL_PREFIX",
"string",
literalPrefix != null ? new BsonString(literalPrefix) : n));
doc.values.add(
new Column(
"",
"",
"",
"LITERAL_SUFFIX",
"LITERAL_SUFFIX",
"string",
literalSuffix != null ? new BsonString(literalSuffix) : n));
doc.values.add(new Column("", "", "", "CREATE_PARAMS", "CREATE_PARAMS", "string", n));
doc.values.add(
new Column("", "", "", "NULLABLE", "NULLABLE", "int", new BsonInt32(nullable)));
doc.values.add(
new Column(
"",
"",
"",
"CASE_SENSITIVE",
"CASE_SENSITIVE",
"bool",
new BsonBoolean(caseSensitive)));
doc.values.add(
new Column(
"", "", "", "SEARCHABLE", "SEARCHABLE", "int", new BsonInt32(searchable)));
doc.values.add(
new Column(
"",
"",
"",
"UNSIGNED_ATTRIBUTE",
"UNSIGNED_ATTRIBUTE",
"bool",
new BsonBoolean(unsigned)));
doc.values.add(
new Column(
"",
"",
"",
"FIXED_PREC_SCALE",
"FIXED_PREC_SCALE",
"bool",
new BsonBoolean(fixedPrecScale)));
doc.values.add(
new Column(
"",
"",
"",
"AUTO_INCREMENT",
"AUTO_INCREMENT",
"bool",
new BsonBoolean(false)));
doc.values.add(new Column("", "", "", "LOCAL_TYPE_NAME", "LOCAL_TYPE_NAME", "string", n));
doc.values.add(
new Column(
"",
"",
"",
"MINIMUM_SCALE",
"MINIMUM_SCALE",
"int",
new BsonInt32(minScale)));
doc.values.add(
new Column(
"",
"",
"",
"MAXIMUM_SCALE",
"MAXIMUM_SCALE",
"int",
new BsonInt32(maxScale)));
doc.values.add(
new Column("", "", "", "SQL_DATA_TYPE", "SQL_DATA_TYPE", "int", new BsonInt32(0)));
doc.values.add(
new Column(
"",
"",
"",
"SQL_DATETIME_SUB",
"SQL_DATETIME_SUB",
"int",
new BsonInt32(0)));
doc.values.add(
new Column(
"",
"",
"",
"NUM_PREC_RADIX",
"NUM_PREC_RADIX",
"int",
new BsonInt32(numPrecRadix)));
return doc;
}
@Override
public ResultSet getTypeInfo() throws SQLException {
ArrayList docs = new ArrayList<>(11);
docs.add(
getTypeInfoDoc(
"binData", //typeName
Types.NULL, //dataType
0, //precision
null, //literalPrefix
null, //literalSuffix
ResultSetMetaData.columnNullable, //nullable
false, //caseSensitive
typePredNone, //seachable
false, //unsigned
false, //fixedPrecScale
0, //minScale
0, //maxScale
0)); //numPrecRadix
docs.add(
getTypeInfoDoc(
"bool", //typeName
Types.BIT, //dataType
1, //precision
null, //literalPrefix
null, //literalSuffix
ResultSetMetaData.columnNullable, //nullable
false, //caseSensitive
typeSearchable, //seachable
true, //unsigned
false, //fixedPrecScale
0, //minScale
0, //maxScale
0)); //numPrecRadix
docs.add(
getTypeInfoDoc(
"date", //typeName
Types.TIMESTAMP, //dataType
24, //precision
"'", //literalPrefix
"'", //literalSuffix
ResultSetMetaData.columnNullable, //nullable
false, //caseSensitive
typeSearchable, //seachable
false, //unsigned
false, //fixedPrecScale
0, //minScale
0, //maxScale
0)); //numPrecRadix
docs.add(
getTypeInfoDoc(
"decimal", //typeName
Types.DECIMAL, //dataType
34, //precision
null, //literalPrefix
null, //literalSuffix
ResultSetMetaData.columnNullable, //nullable
false, //caseSensitive
typeSearchable, //seachable
false, //unsigned
false, //fixedPrecScale
34, //minScale
34, //maxScale
10)); //numPrecRadix
docs.add(
getTypeInfoDoc(
"double", //typeName
Types.DOUBLE, //dataType
15, //precision
null, //literalPrefix
null, //literalSuffix
ResultSetMetaData.columnNullable, //nullable
false, //caseSensitive
typeSearchable, //seachable
false, //unsigned
false, //fixedPrecScale
15, //minScale
15, //maxScale
2)); //numPrecRadix
docs.add(
getTypeInfoDoc(
"int", //typeName
Types.INTEGER, //dataType
10, //precision
null, //literalPrefix
null, //literalSuffix
ResultSetMetaData.columnNullable, //nullable
false, //caseSensitive
typeSearchable, //seachable
false, //unsigned
true, //fixedPrecScale
0, //minScale
0, //maxScale
2)); //numPrecRadix
docs.add(
getTypeInfoDoc(
"long", //typeName
Types.INTEGER, //dataType
19, //precision
null, //literalPrefix
null, //literalSuffix
ResultSetMetaData.columnNullable, //nullable
false, //caseSensitive
typeSearchable, //seachable
false, //unsigned
true, //fixedPrecScale
0, //minScale
0, //maxScale
2)); //numPrecRadix
docs.add(
getTypeInfoDoc(
"string", //typeName
Types.LONGVARCHAR, //dataType
0, //precision
"'", //literalPrefix
"'", //literalSuffix
ResultSetMetaData.columnNullable, //nullable
true, //caseSensitive
typeSearchable, //seachable
false, //unsigned
false, //fixedPrecScale
0, //minScale
0, //maxScale
0)); //numPrecRadix
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public ResultSet getIndexInfo(
String catalog, String schema, String table, boolean unique, boolean approximate)
throws SQLException {
Statement stmt = conn.createStatement();
return stmt.executeQuery(
"select "
+ "tc.TABLE_SCHEMA as TABLE_CAT, "
+ "'' as TABLE_SCHEM, "
+ "tc.TABLE_NAME as TABLE_NAME, "
+ "tc.CONSTRAINT_TYPE not in ('PRIMARY KEY', 'UNIQUE') as NON_UNIQUE, "
+ "tc.CONSTRAINT_CATALOG as INDEX_QUALIFIER, "
+ "tc.CONSTRAINT_NAME as INDEX_NAME, "
+ "tc.CONSTRAINT_TYPE as TYPE, "
+ "kcu.ORDINAL_POSITION as ORDINAL_POSITION, "
+ "kcu.COLUMN_NAME AS COLUMN_NAME, "
+ "'A' as ASC_OR_DESC, "
+ "NULL as CARDINALITY, "
+ "NULL as PAGES, "
+ "NULL as FILTER_CONDITION "
+ "from INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc inner join "
+ " INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu "
+ " on tc.CONSTRAINT_SCHEMA = kcu.CONSTRAINT_SCHEMA "
+ " and tc.CONSTRAINT_NAME = kcu.CONSTRAINT_NAME "
+ " and tc.TABLE_SCHEMA = kcu.TABLE_SCHEMA "
+ " and tc.TABLE_NAME = kcu.TABLE_NAME "
+ " and "
+ equalsCond("tc.TABLE_SCHEMA", catalog)
+ " and "
+ equalsCond("tc.TABLE_NAME", table)
+ ((unique)
? "where tc.CONSTRAINT_TYPE in ('PRIMARY KEY', 'UNIQUE')"
: ""));
}
//--------------------------JDBC 2.0-----------------------------
@Override
public boolean supportsResultSetType(int type) throws SQLException {
return type == ResultSet.TYPE_FORWARD_ONLY;
}
@Override
public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException {
return type == ResultSet.TYPE_FORWARD_ONLY && concurrency == ResultSet.CONCUR_READ_ONLY;
}
@Override
public boolean ownUpdatesAreVisible(int type) throws SQLException {
// We do not have updates.
return false;
}
@Override
public boolean ownDeletesAreVisible(int type) throws SQLException {
// We do not have deletes.
return false;
}
@Override
public boolean ownInsertsAreVisible(int type) throws SQLException {
// We do not have inserts.
return false;
}
@Override
public boolean othersUpdatesAreVisible(int type) throws SQLException {
// We do not have updates.
return false;
}
@Override
public boolean othersDeletesAreVisible(int type) throws SQLException {
// We do not have deletes.
return false;
}
@Override
public boolean othersInsertsAreVisible(int type) throws SQLException {
// We do not have inserts.
return false;
}
@Override
public boolean updatesAreDetected(int type) throws SQLException {
// We do not have updates.
return false;
}
@Override
public boolean deletesAreDetected(int type) throws SQLException {
// We do not have deletes.
return false;
}
@Override
public boolean insertsAreDetected(int type) throws SQLException {
// We do not have inserts.
return false;
}
@Override
public boolean supportsBatchUpdates() throws SQLException {
// We do not have updates.
return false;
}
@Override
public ResultSet getUDTs(
String catalog, String schemaPattern, String typeNamePattern, int[] types)
throws SQLException {
// We do not have UDTs.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "TYPE_CAT", "TYPE_CAT", "string", n));
doc.values.add(new Column("", "", "", "TYPE_SCHEM", "TYPE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "TYPE_NAME", "TYPE_NAME", "string", n));
doc.values.add(new Column("", "", "", "CLASS_NAME", "CLASS_NAME", "string", n));
doc.values.add(new Column("", "", "", "DATA_TYPE", "DATA_TYPE", "int", n));
doc.values.add(new Column("", "", "", "REMARKS", "REMARKS", "string", n));
doc.values.add(new Column("", "", "", "BASE_TYPE", "BASE_TYPE", "int", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public Connection getConnection() throws SQLException {
return conn;
}
// ------------------- JDBC 3.0 -------------------------
@Override
public boolean supportsSavepoints() throws SQLException {
return false;
}
@Override
public boolean supportsNamedParameters() throws SQLException {
return false;
}
@Override
public boolean supportsMultipleOpenResults() throws SQLException {
return false;
}
@Override
public boolean supportsGetGeneratedKeys() throws SQLException {
// This is related to keys generated automatically on inserts,
// and we do not support inserts.
return false;
}
@Override
public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern)
throws SQLException {
// We do not have UDTs.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "TYPE_CAT", "TYPE_CAT", "string", n));
doc.values.add(new Column("", "", "", "TYPE_SCHEM", "TYPE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "TYPE_NAME", "TYPE_NAME", "string", n));
doc.values.add(new Column("", "", "", "SUPERTYPE_CAT", "SUPERTYPE_CAT", "string", n));
doc.values.add(new Column("", "", "", "SUPERTYPE_SCHEM", "SUPERTYPE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "SUPERTYPE_NAME", "SUPERTYPE_NAME", "string", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern)
throws SQLException {
// We do not have SuperTables.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "TABLE_CAT", "TABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "TABLE_SCHEM", "TABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "TABLE_NAME", "TABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "SUPERTABLE_NAME", "SUPERTABLE_NAME", "string", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public ResultSet getAttributes(
String catalog,
String schemaPattern,
String typeNamePattern,
String attributeNamePattern)
throws SQLException {
// We do not have UDTs.
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "TYPE_CAT", "TYPE_CAT", "string", n));
doc.values.add(new Column("", "", "", "TYPE_SCHEM", "TYPE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "TYPE_NAME", "TYPE_NAME", "string", n));
doc.values.add(new Column("", "", "", "ATTR_NAME", "ATTR_NAME", "string", n));
doc.values.add(new Column("", "", "", "DATA_TYPE", "DATA_TYPE", "int", n));
doc.values.add(new Column("", "", "", "ATTR_TYPE_NAME", "ATTR_TYPE_NAME", "string", n));
doc.values.add(new Column("", "", "", "ATTR_SIZE", "ATTR_SIZE", "int", n));
doc.values.add(new Column("", "", "", "DECIMAL_DIGITS", "DECIMAL_DIGITS", "int", n));
doc.values.add(new Column("", "", "", "NUM_PREC_RADIX", "NUM_PREC_RADIX", "int", n));
doc.values.add(new Column("", "", "", "NULLABLE", "NULLABLE", "int", n));
doc.values.add(new Column("", "", "", "REMARKS", "REMARKS", "string", n));
doc.values.add(new Column("", "", "", "ATTR_DEF", "ATTR_DEF", "string", n));
doc.values.add(new Column("", "", "", "SQL_DATA_TYPE", "SQL_DATA_TYPE", "int", n));
doc.values.add(new Column("", "", "", "SQL_DATETIME_SUB", "SQL_DATETIME_SUB", "int", n));
doc.values.add(new Column("", "", "", "CHAR_OCTET_LENGTH", "CHAR_OCTET_LENGTH", "int", n));
doc.values.add(new Column("", "", "", "ORDINAL_POSITION", "ORDINAL_POSITION", "int", n));
doc.values.add(new Column("", "", "", "IS_NULLABLE", "IS_NULLABLE", "string", n));
doc.values.add(new Column("", "", "", "SCOPE_CATALOG", "SCOPE_CATALOG", "string", n));
doc.values.add(new Column("", "", "", "SCOPE_SCHEMA", "SCOPE_SCHEMA", "string", n));
doc.values.add(new Column("", "", "", "SCOPE_TABLE", "SCOPE_TABLE", "string", n));
doc.values.add(new Column("", "", "", "SOURCE_DATA_TYPE", "SOURCE_DATA_TYPE", "int", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public boolean supportsResultSetHoldability(int holdability) throws SQLException {
return false;
}
@Override
public int getResultSetHoldability() throws SQLException {
return ResultSet.HOLD_CURSORS_OVER_COMMIT;
}
@Override
public int getDatabaseMajorVersion() throws SQLException {
return MongoDriver.MAJOR_VERSION;
}
@Override
public int getDatabaseMinorVersion() throws SQLException {
return MongoDriver.MINOR_VERSION;
}
@Override
public int getJDBCMajorVersion() throws SQLException {
return 4;
}
@Override
public int getJDBCMinorVersion() throws SQLException {
return 2;
}
@Override
public int getSQLStateType() throws SQLException {
// This is what postgres returns.
return sqlStateSQL;
}
@Override
public boolean locatorsUpdateCopy() throws SQLException {
// It does not matter what return here. But we don't have locators
// or allow them to be updated.
return false;
}
@Override
public boolean supportsStatementPooling() throws SQLException {
return false;
}
//------------------------- JDBC 4.0 -----------------------------------
@Override
public RowIdLifetime getRowIdLifetime() throws SQLException {
return RowIdLifetime.ROWID_UNSUPPORTED;
}
@Override
public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
return getSchemas();
}
@Override
public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException {
// This is related to using stored procedure escape syntax, which we do not support.
return false;
}
@Override
public boolean autoCommitFailureClosesAllResultSets() throws SQLException {
// No writes.
return false;
}
@Override
public ResultSet getClientInfoProperties() throws SQLException {
ArrayList rows = new ArrayList<>(4);
MongoResultDoc row = new MongoResultDoc();
row.values = new ArrayList<>();
row.values.add(new Column("", "", "", "NAME", "NAME", "string", new BsonString("user")));
row.values.add(new Column("", "", "", "MAX_LEN", "MAX_LEN", "int", new BsonInt32(0)));
row.values.add(
new Column(
"",
"",
"",
"DEFAULT_VALUE",
"DEFAULT_VALUE",
"string",
new BsonString("")));
row.values.add(
new Column(
"",
"",
"",
"DESCRIPTION",
"DESCRIPTION",
"string",
new BsonString("database user for the connection")));
rows.add(row);
row = new MongoResultDoc();
row.values = new ArrayList<>();
row.values.add(
new Column("", "", "", "NAME", "NAME", "string", new BsonString("password")));
row.values.add(new Column("", "", "", "MAX_LEN", "MAX_LEN", "int", new BsonInt32(0)));
row.values.add(
new Column(
"",
"",
"",
"DEFAULT_VALUE",
"DEFAULT_VALUE",
"string",
new BsonString("")));
row.values.add(
new Column(
"",
"",
"",
"DESCRIPTION",
"DESCRIPTION",
"string",
new BsonString("user password for the connection")));
rows.add(row);
row = new MongoResultDoc();
row.values = new ArrayList<>();
row.values.add(
new Column("", "", "", "NAME", "NAME", "string", new BsonString("conversionMode")));
row.values.add(new Column("", "", "", "MAX_LEN", "MAX_LEN", "int", new BsonInt32(0)));
row.values.add(
new Column(
"",
"",
"",
"DEFAULT_VALUE",
"DEFAULT_VALUE",
"string",
new BsonString("")));
row.values.add(
new Column(
"",
"",
"",
"DESCRIPTION",
"DESCRIPTION",
"string",
new BsonString(
"conversionMode can be strict or relaxed. When strict, "
+ "failing conversions result in Exceptions. When relaxed, "
+ "failing conversions result in NULL values.")));
rows.add(row);
row = new MongoResultDoc();
row.values = new ArrayList<>();
row.values.add(
new Column("", "", "", "NAME", "NAME", "string", new BsonString("database")));
row.values.add(new Column("", "", "", "MAX_LEN", "MAX_LEN", "int", new BsonInt32(0)));
row.values.add(
new Column(
"",
"",
"",
"DEFAULT_VALUE",
"DEFAULT_VALUE",
"string",
new BsonString("")));
row.values.add(
new Column(
"",
"",
"",
"DESCRIPTION",
"DESCRIPTION",
"string",
new BsonString("database to connect to")));
rows.add(row);
return new MongoResultSet(null, new MongoExplicitCursor(rows), true);
}
private MongoResultDoc getFunctionDoc(String functionName, String remarks) {
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>(5);
doc.values.add(
new Column(
"",
"",
"",
"FUNCTION_CAT",
"FUNCTION_CAT",
"string",
new BsonString("def")));
doc.values.add(
new Column(
"", "", "", "FUNCTION_SCHEM", "FUNCTION_SCHEM", "string", new BsonNull()));
doc.values.add(
new Column(
"",
"",
"",
"FUNCTION_NAME",
"FUNCTION_NAME",
"string",
new BsonString(functionName)));
doc.values.add(
new Column(
"",
"",
"",
"REMARKS",
"REMARKS",
"string",
// perhaps at some point add comments explaining the function.
new BsonString(remarks)));
doc.values.add(
new Column(
"",
"",
"",
"FUNCTION_TYPE",
"FUNCTION_TYPE",
"int",
new BsonInt32(functionNoTable)));
doc.values.add(
new Column(
"",
"",
"",
"SPECIFIC_NAME",
"SPECIFIC_NAME",
"string",
new BsonString(functionName)));
return doc;
}
@Override
public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern)
throws SQLException {
ArrayList docs = new ArrayList<>(MongoFunction.functionNames.length);
Pattern functionPatternRE = null;
if (functionNamePattern != null) {
functionPatternRE = Pattern.compile(functionNamePattern.replaceAll("%", ".*"));
}
for (MongoFunction func : MongoFunction.functions) {
String functionName = func.name;
String remarks = func.comment;
if (functionPatternRE != null && !functionPatternRE.matcher(functionName).matches()) {
continue;
}
MongoResultDoc doc = getFunctionDoc(func.name, func.comment);
docs.add(doc);
}
// If there are no docs, we need to construct the emptyResultSet.
if (docs.size() == 0) {
MongoResultDoc doc = getFunctionDoc("", "");
doc.emptyResultSet = true;
docs.add(doc);
}
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
private BsonValue bsonInt32(Integer i) {
if (i == null) {
return new BsonNull();
}
return new BsonInt32(i);
}
private MongoResultDoc getFunctionColumnDoc(
MongoFunction func, int i, String argName, String argType, boolean isReturnColumn) {
BsonValue n = new BsonNull();
String functionName = func.name;
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>(17);
doc.values.add(
new Column(
"",
"",
"",
"FUNCTION_CAT",
"FUNCTION_CAT",
"string",
new BsonString("def")));
doc.values.add(new Column("", "", "", "FUNCTION_SCHEM", "FUNCTION_SCHEM", "string", n));
doc.values.add(
new Column(
"",
"",
"",
"FUNCTION_NAME",
"FUNCTION_NAME",
"string",
new BsonString(functionName)));
doc.values.add(
new Column(
"",
"",
"",
"COLUMN_NAME",
"COLUMN_NAME",
"string",
new BsonString(argName)));
doc.values.add(
new Column(
"",
"",
"",
"COLUMN_TYPE",
"COLUMN_TYPE",
"int",
new BsonInt32(isReturnColumn ? functionReturn : functionColumnIn)));
doc.values.add(
new Column(
"",
"",
"",
"DATA_TYPE",
"DATA_TYPE",
"int",
new BsonInt32(typeNum(argType))));
doc.values.add(
new Column(
"",
"",
"",
"TYPE_NAME",
"TYPE_NAME",
"string",
argType == null ? n : new BsonString(argType)));
doc.values.add(
new Column(
"", "", "", "PRECISION", "PRECISION", "int", bsonInt32(typePrec(argType))));
doc.values.add(
new Column("", "", "", "LENGTH", "LENGTH", "int", bsonInt32(typeBytes(argType))));
doc.values.add(
new Column("", "", "", "SCALE", "SCALE", "int", bsonInt32(typeScale(argType))));
doc.values.add(
new Column("", "", "", "RADIX", "RADIX", "int", bsonInt32(typeRadix(argType))));
doc.values.add(
new Column(
"",
"",
"",
"NULLABLE",
"NULLABLE",
"int",
new BsonInt32(functionNullable)));
doc.values.add(
new Column(
"", "", "", "REMARKS", "REMARKS", "string", new BsonString(func.comment)));
doc.values.add(
new Column(
"",
"",
"",
"CHAR_OCTET_LENGTH",
"CHAR_OCTET_LENGTH",
"int",
bsonInt32(typeBytes(argType))));
doc.values.add(
new Column(
"",
"",
"",
"ORDINAL_POSITION",
"ORDINAL_POSITION",
"int",
new BsonInt32(i)));
doc.values.add(
new Column(
"", "", "", "IS_NULLABLE", "IS_NULLABLE", "string", new BsonString("YES")));
doc.values.add(
new Column(
"",
"",
"",
"SPECIFIC_NAME",
"SPECIFIC_NAME",
"string",
new BsonString(functionName)));
return doc;
}
@Override
public ResultSet getFunctionColumns(
String catalog,
String schemaPattern,
String functionNamePattern,
String columnNamePattern)
throws SQLException {
ArrayList docs = new ArrayList<>(MongoFunction.functionNames.length);
Pattern functionNamePatternRE = null;
Pattern columnNamePatternRE = null;
if (functionNamePattern != null) {
functionNamePatternRE = Pattern.compile(functionNamePattern.replaceAll("%", ".*"));
}
if (columnNamePattern != null) {
columnNamePatternRE = Pattern.compile(columnNamePattern.replaceAll("%", ".*"));
}
for (MongoFunction func : MongoFunction.functions) {
String functionName = func.name;
if (functionNamePatternRE != null
&& !functionNamePatternRE.matcher(functionName).matches()) {
continue;
}
int i = 0;
for (String argType : func.argTypes) {
// We don't have better names for our arguments, for the most part.
++i;
String columnName = "arg" + i;
if (columnNamePatternRE != null
&& !columnNamePatternRE.matcher(columnName).matches()) {
continue;
}
MongoResultDoc doc = getFunctionColumnDoc(func, i, columnName, argType, false);
docs.add(doc);
}
String columnName = "argReturn";
if (columnNamePatternRE == null || columnNamePatternRE.matcher(columnName).matches()) {
MongoResultDoc doc =
getFunctionColumnDoc(func, i, "argReturn", func.returnType, true);
docs.add(doc);
}
}
// If there are no docs, we need to construct the emptyResultSet.
if (docs.size() == 0) {
// The values we pass here don't matter, since this is the emptyResultSet.
MongoResultDoc doc = getFunctionColumnDoc(MongoFunction.functions[0], 0, "", "", false);
doc.emptyResultSet = true;
docs.add(doc);
}
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
//--------------------------JDBC 4.1 -----------------------------
@Override
public ResultSet getPseudoColumns(
String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern)
throws SQLException {
// We do not support pseudoColumns (hidden columns).
BsonValue n = new BsonNull();
ArrayList docs = new ArrayList<>();
MongoResultDoc doc = new MongoResultDoc();
doc.values = new ArrayList<>();
doc.values.add(new Column("", "", "", "TABLE_CAT", "TABLE_CAT", "string", n));
doc.values.add(new Column("", "", "", "TABLE_SCHEM", "TABLE_SCHEM", "string", n));
doc.values.add(new Column("", "", "", "TABLE_NAME", "TABLE_NAME", "string", n));
doc.values.add(new Column("", "", "", "COLUMN_NAME", "COLUMN_NAME", "string", n));
doc.values.add(new Column("", "", "", "DATA_TYPE", "DATA_TYPE", "int", n));
doc.values.add(new Column("", "", "", "COLUMN_SIZE", "COLUMN_SIZE", "int", n));
doc.values.add(new Column("", "", "", "DECIMAL_DIGITS", "DECIMAL_DIGITS", "int", n));
doc.values.add(new Column("", "", "", "NUM_PREC_RADIX", "NUM_PREC_RADIX", "string", n));
doc.values.add(new Column("", "", "", "COLUMN_USAGE", "COLUMN_USAGE", "string", n));
doc.values.add(new Column("", "", "", "REMARKS", "REMARKS", "string", n));
doc.values.add(new Column("", "", "", "CHAR_OCTET_LENGTH", "CHAR_OCTET_LENGTH", "int", n));
doc.values.add(new Column("", "", "", "IS_NULLABLE", "IS_NULLABLE", "string", n));
doc.emptyResultSet = true;
docs.add(doc);
return new MongoResultSet(null, new MongoExplicitCursor(docs), true);
}
@Override
public boolean generatedKeyAlwaysReturned() throws SQLException {
// We do not have generated keys.
return false;
}
// java.sql.Wrapper impl
@Override
public boolean isWrapperFor(Class> iface) throws SQLException {
return iface.isInstance(this);
}
@SuppressWarnings("unchecked")
@Override
public T unwrap(Class iface) throws SQLException {
return (T) this;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy