All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.h2.table.MetaTable Maven / Gradle / Ivy

There is a newer version: 2.3.232
Show newest version
/*
 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
 * Version 1.0, and under the Eclipse Public License, Version 1.0
 * (http://h2database.com/html/license.html).
 * Initial Developer: H2 Group
 */
package org.h2.table;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import org.h2.command.Command;
import org.h2.constraint.Constraint;
import org.h2.constraint.ConstraintCheck;
import org.h2.constraint.ConstraintReferential;
import org.h2.constraint.ConstraintUnique;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
import org.h2.engine.FunctionAlias;
import org.h2.engine.Right;
import org.h2.engine.Role;
import org.h2.engine.Session;
import org.h2.engine.Setting;
import org.h2.engine.User;
import org.h2.engine.UserAggregate;
import org.h2.engine.UserDataType;
import org.h2.expression.ValueExpression;
import org.h2.index.Index;
import org.h2.index.IndexType;
import org.h2.index.MetaIndex;
import org.h2.index.MultiVersionIndex;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.schema.Constant;
import org.h2.schema.Schema;
import org.h2.schema.SchemaObject;
import org.h2.schema.Sequence;
import org.h2.schema.TriggerObject;
import org.h2.store.InDoubtTransaction;
import org.h2.store.PageStore;
import org.h2.tools.Csv;
import org.h2.util.MathUtils;
import org.h2.util.New;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
import org.h2.value.CompareMode;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueNull;
import org.h2.value.ValueString;
import org.h2.value.ValueStringIgnoreCase;

/**
 * This class is responsible to build the database meta data pseudo tables.
 */
public class MetaTable extends Table {

    /**
     * The approximate number of rows of a meta table.
     */
    public static final long ROW_COUNT_APPROXIMATION = 1000;

    private static final String CHARACTER_SET_NAME = "Unicode";

    private static final int TABLES = 0;
    private static final int COLUMNS = 1;
    private static final int INDEXES = 2;
    private static final int TABLE_TYPES = 3;
    private static final int TYPE_INFO = 4;
    private static final int CATALOGS = 5;
    private static final int SETTINGS = 6;
    private static final int HELP = 7;
    private static final int SEQUENCES = 8;
    private static final int USERS = 9;
    private static final int ROLES = 10;
    private static final int RIGHTS = 11;
    private static final int FUNCTION_ALIASES = 12;
    private static final int SCHEMATA = 13;
    private static final int TABLE_PRIVILEGES = 14;
    private static final int COLUMN_PRIVILEGES = 15;
    private static final int COLLATIONS = 16;
    private static final int VIEWS = 17;
    private static final int IN_DOUBT = 18;
    private static final int CROSS_REFERENCES = 19;
    private static final int CONSTRAINTS = 20;
    private static final int FUNCTION_COLUMNS = 21;
    private static final int CONSTANTS = 22;
    private static final int DOMAINS = 23;
    private static final int TRIGGERS = 24;
    private static final int SESSIONS = 25;
    private static final int LOCKS = 26;
    private static final int SESSION_STATE = 27;
    private static final int META_TABLE_TYPE_COUNT = SESSION_STATE + 1;

    private final int type;
    private final int indexColumn;
    private MetaIndex metaIndex;

    /**
     * Create a new metadata table.
     *
     * @param schema the schema
     * @param id the object id
     * @param type the meta table type
     */
    public MetaTable(Schema schema, int id, int type) {
        // tableName will be set later
        super(schema, id, null, true, true);
        this.type = type;
        Column[] cols;
        String indexColumnName = null;
        switch(type) {
        case TABLES:
            setObjectName("TABLES");
            cols = createColumns(
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "TABLE_TYPE",
                    // extensions
                    "STORAGE_TYPE",
                    "SQL",
                    "REMARKS",
                    "LAST_MODIFICATION BIGINT",
                    "ID INT",
                    "TYPE_NAME",
                    "TABLE_CLASS"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case COLUMNS:
            setObjectName("COLUMNS");
            cols = createColumns(
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "COLUMN_NAME",
                    "ORDINAL_POSITION INT",
                    "COLUMN_DEFAULT",
                    "IS_NULLABLE",
                    "DATA_TYPE INT",
                    "CHARACTER_MAXIMUM_LENGTH INT",
                    "CHARACTER_OCTET_LENGTH INT",
                    "NUMERIC_PRECISION INT",
                    "NUMERIC_PRECISION_RADIX INT",
                    "NUMERIC_SCALE INT",
                    "CHARACTER_SET_NAME",
                    "COLLATION_NAME",
                    // extensions
                    "TYPE_NAME",
                    "NULLABLE INT",
                    "IS_COMPUTED BIT",
                    "SELECTIVITY INT",
                    "CHECK_CONSTRAINT",
                    "SEQUENCE_NAME",
                    "REMARKS",
                    "SOURCE_DATA_TYPE SMALLINT"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case INDEXES:
            setObjectName("INDEXES");
            cols = createColumns(
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "NON_UNIQUE BIT",
                    "INDEX_NAME",
                    "ORDINAL_POSITION SMALLINT",
                    "COLUMN_NAME",
                    "CARDINALITY INT",
                    "PRIMARY_KEY BIT",
                    "INDEX_TYPE_NAME",
                    "IS_GENERATED BIT",
                    "INDEX_TYPE SMALLINT",
                    "ASC_OR_DESC",
                    "PAGES INT",
                    "FILTER_CONDITION",
                    "REMARKS",
                    "SQL",
                    "ID INT",
                    "SORT_TYPE INT",
                    "CONSTRAINT_NAME",
                    "INDEX_CLASS"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case TABLE_TYPES:
            setObjectName("TABLE_TYPES");
            cols = createColumns("TYPE");
            break;
        case TYPE_INFO:
            setObjectName("TYPE_INFO");
            cols = createColumns(
                "TYPE_NAME",
                "DATA_TYPE INT",
                "PRECISION INT",
                "PREFIX",
                "SUFFIX",
                "PARAMS",
                "AUTO_INCREMENT BIT",
                "MINIMUM_SCALE SMALLINT",
                "MAXIMUM_SCALE SMALLINT",
                "RADIX INT",
                "POS INT",
                "CASE_SENSITIVE BIT",
                "NULLABLE SMALLINT",
                "SEARCHABLE SMALLINT"
            );
            break;
        case CATALOGS:
            setObjectName("CATALOGS");
            cols = createColumns("CATALOG_NAME");
            break;
        case SETTINGS:
            setObjectName("SETTINGS");
            cols = createColumns("NAME", "VALUE");
            break;
        case HELP:
            setObjectName("HELP");
            cols = createColumns(
                    "ID INT",
                    "SECTION",
                    "TOPIC",
                    "SYNTAX",
                    "TEXT"
            );
            break;
        case SEQUENCES:
            setObjectName("SEQUENCES");
            cols = createColumns(
                    "SEQUENCE_CATALOG",
                    "SEQUENCE_SCHEMA",
                    "SEQUENCE_NAME",
                    "CURRENT_VALUE BIGINT",
                    "INCREMENT BIGINT",
                    "IS_GENERATED BIT",
                    "REMARKS",
                    "CACHE BIGINT",
                    "ID INT"
            );
            break;
        case USERS:
            setObjectName("USERS");
            cols = createColumns(
                    "NAME",
                    "ADMIN",
                    "REMARKS",
                    "ID INT"
            );
            break;
        case ROLES:
            setObjectName("ROLES");
            cols = createColumns(
                    "NAME",
                    "REMARKS",
                    "ID INT"
            );
            break;
        case RIGHTS:
            setObjectName("RIGHTS");
            cols = createColumns(
                    "GRANTEE",
                    "GRANTEETYPE",
                    "GRANTEDROLE",
                    "RIGHTS",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "ID INT"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case FUNCTION_ALIASES:
            setObjectName("FUNCTION_ALIASES");
            cols = createColumns(
                    "ALIAS_CATALOG",
                    "ALIAS_SCHEMA",
                    "ALIAS_NAME",
                    "JAVA_CLASS",
                    "JAVA_METHOD",
                    "DATA_TYPE INT",
                    "TYPE_NAME",
                    "COLUMN_COUNT INT",
                    "RETURNS_RESULT SMALLINT",
                    "REMARKS",
                    "ID INT",
                    "SOURCE"
            );
            break;
        case FUNCTION_COLUMNS:
            setObjectName("FUNCTION_COLUMNS");
            cols = createColumns(
                    "ALIAS_CATALOG",
                    "ALIAS_SCHEMA",
                    "ALIAS_NAME",
                    "JAVA_CLASS",
                    "JAVA_METHOD",
                    "COLUMN_COUNT INT",
                    "POS INT",
                    "COLUMN_NAME",
                    "DATA_TYPE INT",
                    "TYPE_NAME",
                    "PRECISION INT",
                    "SCALE SMALLINT",
                    "RADIX SMALLINT",
                    "NULLABLE SMALLINT",
                    "COLUMN_TYPE SMALLINT",
                    "REMARKS",
                    "COLUMN_DEFAULT"
            );
            break;
        case SCHEMATA:
            setObjectName("SCHEMATA");
            cols = createColumns(
                    "CATALOG_NAME",
                    "SCHEMA_NAME",
                    "SCHEMA_OWNER",
                    "DEFAULT_CHARACTER_SET_NAME",
                    "DEFAULT_COLLATION_NAME",
                    "IS_DEFAULT BIT",
                    "REMARKS",
                    "ID INT"
            );
            break;
        case TABLE_PRIVILEGES:
            setObjectName("TABLE_PRIVILEGES");
            cols = createColumns(
                    "GRANTOR",
                    "GRANTEE",
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "PRIVILEGE_TYPE",
                    "IS_GRANTABLE"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case COLUMN_PRIVILEGES:
            setObjectName("COLUMN_PRIVILEGES");
            cols = createColumns(
                    "GRANTOR",
                    "GRANTEE",
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "COLUMN_NAME",
                    "PRIVILEGE_TYPE",
                    "IS_GRANTABLE"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case COLLATIONS:
            setObjectName("COLLATIONS");
            cols = createColumns(
                    "NAME",
                    "KEY"
            );
            break;
        case VIEWS:
            setObjectName("VIEWS");
            cols = createColumns(
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "VIEW_DEFINITION",
                    "CHECK_OPTION",
                    "IS_UPDATABLE",
                    "STATUS",
                    "REMARKS",
                    "ID INT"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case IN_DOUBT:
            setObjectName("IN_DOUBT");
            cols = createColumns(
                    "TRANSACTION",
                    "STATE"
            );
            break;
        case CROSS_REFERENCES:
            setObjectName("CROSS_REFERENCES");
            cols = createColumns(
                    "PKTABLE_CATALOG",
                    "PKTABLE_SCHEMA",
                    "PKTABLE_NAME",
                    "PKCOLUMN_NAME",
                    "FKTABLE_CATALOG",
                    "FKTABLE_SCHEMA",
                    "FKTABLE_NAME",
                    "FKCOLUMN_NAME",
                    "ORDINAL_POSITION SMALLINT",
                    "UPDATE_RULE SMALLINT",
                    "DELETE_RULE SMALLINT",
                    "FK_NAME",
                    "PK_NAME",
                    "DEFERRABILITY SMALLINT"
            );
            indexColumnName = "PKTABLE_NAME";
            break;
        case CONSTRAINTS:
            setObjectName("CONSTRAINTS");
            cols = createColumns(
                    "CONSTRAINT_CATALOG",
                    "CONSTRAINT_SCHEMA",
                    "CONSTRAINT_NAME",
                    "CONSTRAINT_TYPE",
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "UNIQUE_INDEX_NAME",
                    "CHECK_EXPRESSION",
                    "COLUMN_LIST",
                    "REMARKS",
                    "SQL",
                    "ID INT"
            );
            indexColumnName = "TABLE_NAME";
            break;
        case CONSTANTS:
            setObjectName("CONSTANTS");
            cols = createColumns(
                    "CONSTANT_CATALOG",
                    "CONSTANT_SCHEMA",
                    "CONSTANT_NAME",
                    "DATA_TYPE INT",
                    "REMARKS",
                    "SQL",
                    "ID INT"
            );
            break;
        case DOMAINS:
            setObjectName("DOMAINS");
            cols = createColumns(
                    "DOMAIN_CATALOG",
                    "DOMAIN_SCHEMA",
                    "DOMAIN_NAME",
                    "COLUMN_DEFAULT",
                    "IS_NULLABLE",
                    "DATA_TYPE INT",
                    "PRECISION INT",
                    "SCALE INT",
                    "TYPE_NAME",
                    "SELECTIVITY INT",
                    "CHECK_CONSTRAINT",
                    "REMARKS",
                    "SQL",
                    "ID INT"
            );
            break;
        case TRIGGERS:
            setObjectName("TRIGGERS");
            cols = createColumns(
                    "TRIGGER_CATALOG",
                    "TRIGGER_SCHEMA",
                    "TRIGGER_NAME",
                    "TRIGGER_TYPE",
                    "TABLE_CATALOG",
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "BEFORE BIT",
                    "JAVA_CLASS",
                    "QUEUE_SIZE INT",
                    "NO_WAIT BIT",
                    "REMARKS",
                    "SQL",
                    "ID INT"
            );
            break;
        case SESSIONS: {
            setObjectName("SESSIONS");
            cols = createColumns(
                    "ID INT",
                    "USER_NAME",
                    "SESSION_START",
                    "STATEMENT",
                    "STATEMENT_START"
            );
            break;
        }
        case LOCKS: {
            setObjectName("LOCKS");
            cols = createColumns(
                    "TABLE_SCHEMA",
                    "TABLE_NAME",
                    "SESSION_ID INT",
                    "LOCK_TYPE"
            );
            break;
        }
        case SESSION_STATE: {
            setObjectName("SESSION_STATE");
            cols = createColumns(
                    "KEY",
                    "SQL"
            );
            break;
        }
        default:
            throw DbException.throwInternalError("type="+type);
        }
        setColumns(cols);

        if (indexColumnName == null) {
            indexColumn = -1;
        } else {
            indexColumn = getColumn(indexColumnName).getColumnId();
            IndexColumn[] indexCols = IndexColumn.wrap(new Column[] { cols[indexColumn] });
            metaIndex = new MetaIndex(this, indexCols, false);
        }
    }

    private Column[] createColumns(String... names) {
        Column[] cols = new Column[names.length];
        for (int i = 0; i < names.length; i++) {
            String nameType = names[i];
            int idx = nameType.indexOf(' ');
            int dataType;
            String name;
            if (idx < 0) {
                dataType = database.getMode().lowerCaseIdentifiers ? Value.STRING_IGNORECASE : Value.STRING;
                name = nameType;
            } else {
                dataType = DataType.getTypeByName(nameType.substring(idx + 1)).type;
                name = nameType.substring(0, idx);
            }
            cols[i] = new Column(name, dataType);
        }
        return cols;
    }

    public String getDropSQL() {
        return null;
    }

    public String getCreateSQL() {
        return null;
    }

    public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols, IndexType indexType,
            boolean create, String indexComment) {
        throw DbException.getUnsupportedException("META");
    }

    public void lock(Session session, boolean exclusive, boolean force) {
        // nothing to do
    }

    public boolean isLockedExclusively() {
        return false;
    }

    private String identifier(String s) {
        if (database.getMode().lowerCaseIdentifiers) {
            s = s == null ? null : StringUtils.toLowerEnglish(s);
        }
        return s;
    }

    private ArrayList getAllTables(Session session) {
        ArrayList
tables = database.getAllTablesAndViews(true); ArrayList
tempTables = session.getLocalTempTables(); tables.addAll(tempTables); return tables; } private boolean checkIndex(Session session, String value, Value indexFrom, Value indexTo) { if (value == null || (indexFrom == null && indexTo == null)) { return true; } Database db = session.getDatabase(); if (database.getMode().lowerCaseIdentifiers) { Value v = ValueStringIgnoreCase.get(value); if (indexFrom.equals(indexTo) && db.compare(v, indexFrom) != 0) { return false; } } else { Value v = ValueString.get(value); if (indexFrom != null && db.compare(v, indexFrom) < 0) { return false; } if (indexTo != null && db.compare(v, indexTo) > 0) { return false; } } return true; } private static String replaceNullWithEmpty(String s) { return s == null ? "" : s; } private boolean hideTable(Table table, Session session) { return table.isHidden() && session != database.getSystemSession(); } /** * Generate the data for the given metadata table using the given first and * last row filters. * * @param session the session * @param first the first row to return * @param last the last row to return * @return the generated rows */ public ArrayList generateRows(Session session, SearchRow first, SearchRow last) { Value indexFrom = null, indexTo = null; if (indexColumn >= 0) { if (first != null) { indexFrom = first.getValue(indexColumn); } if (last != null) { indexTo = last.getValue(indexColumn); } } ArrayList rows = New.arrayList(); String catalog = identifier(database.getShortName()); boolean admin = session.getUser().isAdmin(); switch (type) { case TABLES: { for (Table table : getAllTables(session)) { String tableName = identifier(table.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } if (hideTable(table, session)) { continue; } String storageType; if (table.isTemporary()) { if (table.isGlobalTemporary()) { storageType = "GLOBAL TEMPORARY"; } else { storageType = "LOCAL TEMPORARY"; } } else { storageType = table.isPersistIndexes() ? "CACHED" : "MEMORY"; } add(rows, // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME tableName, // TABLE_TYPE table.getTableType(), // STORAGE_TYPE storageType, // SQL table.getCreateSQL(), // REMARKS replaceNullWithEmpty(table.getComment()), // LAST_MODIFICATION "" + table.getMaxDataModificationId(), // ID "" + table.getId(), // TYPE_NAME null, // TABLE_CLASS table.getClass().getName() ); } break; } case COLUMNS: { for (Table table : getAllTables(session)) { String tableName = identifier(table.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } if (hideTable(table, session)) { continue; } Column[] cols = table.getColumns(); String collation = database.getCompareMode().getName(); for (int j = 0; j < cols.length; j++) { Column c = cols[j]; Sequence sequence = c.getSequence(); add(rows, // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME tableName, // COLUMN_NAME identifier(c.getName()), // ORDINAL_POSITION String.valueOf(j + 1), // COLUMN_DEFAULT c.getDefaultSQL(), // IS_NULLABLE c.isNullable() ? "YES" : "NO", // DATA_TYPE "" + DataType.convertTypeToSQLType(c.getType()), // CHARACTER_MAXIMUM_LENGTH "" + c.getPrecisionAsInt(), // CHARACTER_OCTET_LENGTH "" + c.getPrecisionAsInt(), // NUMERIC_PRECISION "" + c.getPrecisionAsInt(), // NUMERIC_PRECISION_RADIX "10", // NUMERIC_SCALE "" + c.getScale(), // CHARACTER_SET_NAME CHARACTER_SET_NAME, // COLLATION_NAME collation, // TYPE_NAME identifier(DataType.getDataType(c.getType()).name), // NULLABLE "" + (c.isNullable() ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls) , // IS_COMPUTED "" + (c.getComputed() ? "TRUE" : "FALSE"), // SELECTIVITY "" + (c.getSelectivity()), // CHECK_CONSTRAINT c.getCheckConstraintSQL(session, c.getName()), // SEQUENCE_NAME sequence == null ? null : sequence.getName(), // REMARKS replaceNullWithEmpty(c.getComment()), // SOURCE_DATA_TYPE null ); } } break; } case INDEXES: { for (Table table : getAllTables(session)) { String tableName = identifier(table.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } if (hideTable(table, session)) { continue; } ArrayList indexes = table.getIndexes(); ArrayList constraints = table.getConstraints(); for (int j = 0; indexes != null && j < indexes.size(); j++) { Index index = indexes.get(j); if (index.getCreateSQL() == null) { continue; } String constraintName = null; for (int k = 0; constraints != null && k < constraints.size(); k++) { Constraint constraint = constraints.get(k); if (constraint.usesIndex(index)) { if (index.getIndexType().isPrimaryKey()) { if (constraint.getConstraintType().equals(Constraint.PRIMARY_KEY)) { constraintName = constraint.getName(); } } else { constraintName = constraint.getName(); } } } IndexColumn[] cols = index.getIndexColumns(); String indexClass; if (index instanceof MultiVersionIndex) { indexClass = ((MultiVersionIndex) index).getBaseIndex().getClass().getName(); } else { indexClass = index.getClass().getName(); } for (int k = 0; k < cols.length; k++) { IndexColumn idxCol = cols[k]; Column column = idxCol.column; add(rows, // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME tableName, // NON_UNIQUE index.getIndexType().isUnique() ? "FALSE" : "TRUE", // INDEX_NAME identifier(index.getName()), // ORDINAL_POSITION "" + (k+1), // COLUMN_NAME identifier(column.getName()), // CARDINALITY "0", // PRIMARY_KEY index.getIndexType().isPrimaryKey() ? "TRUE" : "FALSE", // INDEX_TYPE_NAME index.getIndexType().getSQL(), // IS_GENERATED index.getIndexType().getBelongsToConstraint() ? "TRUE" : "FALSE", // INDEX_TYPE "" + DatabaseMetaData.tableIndexOther, // ASC_OR_DESC (idxCol.sortType & SortOrder.DESCENDING) != 0 ? "D" : "A", // PAGES "0", // FILTER_CONDITION "", // REMARKS replaceNullWithEmpty(index.getComment()), // SQL index.getCreateSQL(), // ID "" + index.getId(), // SORT_TYPE "" + idxCol.sortType, // CONSTRAINT_NAME constraintName, // INDEX_CLASS indexClass ); } } } break; } case TABLE_TYPES: { add(rows, Table.TABLE); add(rows, Table.TABLE_LINK); add(rows, Table.SYSTEM_TABLE); add(rows, Table.VIEW); break; } case CATALOGS: { add(rows, catalog); break; } case SETTINGS: { for (Setting s : database.getAllSettings()) { String value = s.getStringValue(); if (value == null) { value = "" + s.getIntValue(); } add(rows, identifier(s.getName()), value ); } add(rows, "info.BUILD_ID", "" + Constants.BUILD_ID); add(rows, "info.VERSION_MAJOR", "" + Constants.VERSION_MAJOR); add(rows, "info.VERSION_MINOR", "" + Constants.VERSION_MINOR); add(rows, "info.VERSION", "" + Constants.getFullVersion()); if (admin) { String[] settings = { "java.runtime.version", "java.vm.name", "java.vendor", "os.name", "os.arch", "os.version", "sun.os.patch.level", "file.separator", "path.separator", "line.separator", "user.country", "user.language", "user.variant", "file.encoding" }; for (String s : settings) { add(rows, "property." + s, Utils.getProperty(s, "")); } } add(rows, "EXCLUSIVE", database.getExclusiveSession() == null ? "FALSE" : "TRUE"); add(rows, "MODE", database.getMode().getName()); add(rows, "MULTI_THREADED", database.isMultiThreaded() ? "1" : "0"); add(rows, "MVCC", database.isMultiVersion() ? "TRUE" : "FALSE"); add(rows, "QUERY_TIMEOUT", "" + session.getQueryTimeout()); add(rows, "LOG", "" + database.getLogMode()); // database settings ArrayList settingNames = New.arrayList(); HashMap s = database.getSettings().getSettings(); for (String k : s.keySet()) { settingNames.add(k); } Collections.sort(settingNames); for (String k : settingNames) { add(rows, k, s.get(k)); } if (database.isPersistent()) { PageStore store = database.getPageStore(); add(rows, "info.FILE_WRITE_TOTAL", "" + store.getWriteCountTotal()); add(rows, "info.FILE_WRITE", "" + store.getWriteCount()); add(rows, "info.FILE_READ", "" + store.getReadCount()); add(rows, "info.PAGE_COUNT", "" + store.getPageCount()); add(rows, "info.PAGE_SIZE", "" + store.getPageSize()); add(rows, "info.CACHE_MAX_SIZE", "" + store.getCache().getMaxMemory()); add(rows, "info.CACHE_SIZE", "" + store.getCache().getMemory()); } break; } case TYPE_INFO: { for (DataType t : DataType.getTypes()) { if (t.hidden || t.sqlType == Value.NULL) { continue; } add(rows, // TYPE_NAME t.name, // DATA_TYPE String.valueOf(t.sqlType), // PRECISION String.valueOf(MathUtils.convertLongToInt(t.maxPrecision)), // PREFIX t.prefix, // SUFFIX t.suffix, // PARAMS t.params, // AUTO_INCREMENT String.valueOf(t.autoIncrement), // MINIMUM_SCALE String.valueOf(t.minScale), // MAXIMUM_SCALE String.valueOf(t.maxScale), // RADIX t.decimal ? "10" : null, // POS String.valueOf(t.sqlTypePos), // CASE_SENSITIVE String.valueOf(t.caseSensitive), // NULLABLE "" + DatabaseMetaData.typeNullable, // SEARCHABLE "" + DatabaseMetaData.typeSearchable ); } break; } case HELP: { String resource = "/org/h2/res/help.csv"; try { byte[] data = Utils.getResource(resource); Reader reader = new InputStreamReader(new ByteArrayInputStream(data)); Csv csv = new Csv(); csv.setLineCommentCharacter('#'); ResultSet rs = csv.read(reader, null); for (int i = 0; rs.next(); i++) { add(rows, // ID String.valueOf(i), // SECTION rs.getString(1).trim(), // TOPIC rs.getString(2).trim(), // SYNTAX rs.getString(3).trim(), // TEXT rs.getString(4).trim() ); } } catch (Exception e) { throw DbException.convert(e); } break; } case SEQUENCES: { for (SchemaObject obj : database.getAllSchemaObjects(DbObject.SEQUENCE)) { Sequence s = (Sequence) obj; add(rows, // SEQUENCE_CATALOG catalog, // SEQUENCE_SCHEMA identifier(s.getSchema().getName()), // SEQUENCE_NAME identifier(s.getName()), // CURRENT_VALUE String.valueOf(s.getCurrentValue()), // INCREMENT String.valueOf(s.getIncrement()), // IS_GENERATED s.getBelongsToTable() ? "TRUE" : "FALSE", // REMARKS replaceNullWithEmpty(s.getComment()), // CACHE String.valueOf(s.getCacheSize()), // ID "" + s.getId() ); } break; } case USERS: { for (User u : database.getAllUsers()) { if (admin || session.getUser() == u) { add(rows, // NAME identifier(u.getName()), // ADMIN String.valueOf(u.isAdmin()), // REMARKS replaceNullWithEmpty(u.getComment()), // ID "" + u.getId() ); } } break; } case ROLES: { for (Role r : database.getAllRoles()) { if (admin || session.getUser().isRoleGranted(r)) { add(rows, // NAME identifier(r.getName()), // REMARKS replaceNullWithEmpty(r.getComment()), // ID "" + r.getId() ); } } break; } case RIGHTS: { if (admin) { for (Right r : database.getAllRights()) { Role role = r.getGrantedRole(); DbObject grantee = r.getGrantee(); String rightType = grantee.getType() == DbObject.USER ? "USER" : "ROLE"; if (role == null) { Table granted = r.getGrantedTable(); String tableName = identifier(granted.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } add(rows, // GRANTEE identifier(grantee.getName()), // GRANTEETYPE rightType, // GRANTEDROLE "", // RIGHTS r.getRights(), // TABLE_SCHEMA identifier(granted.getSchema().getName()), // TABLE_NAME identifier(granted.getName()), // ID "" + r.getId() ); } else { add(rows, // GRANTEE identifier(grantee.getName()), // GRANTEETYPE rightType, // GRANTEDROLE identifier(role.getName()), // RIGHTS "", // TABLE_SCHEMA "", // TABLE_NAME "", // ID "" + r.getId() ); } } } break; } case FUNCTION_ALIASES: { for (SchemaObject aliasAsSchemaObject : database.getAllSchemaObjects(DbObject.FUNCTION_ALIAS)) { FunctionAlias alias = (FunctionAlias) aliasAsSchemaObject; for (FunctionAlias.JavaMethod method : alias.getJavaMethods()) { int returnsResult = method.getDataType() == Value.NULL ? DatabaseMetaData.procedureNoResult : DatabaseMetaData.procedureReturnsResult; add(rows, // ALIAS_CATALOG catalog, // ALIAS_SCHEMA alias.getSchema().getName(), // ALIAS_NAME identifier(alias.getName()), // JAVA_CLASS alias.getJavaClassName(), // JAVA_METHOD alias.getJavaMethodName(), // DATA_TYPE "" + DataType.convertTypeToSQLType(method.getDataType()), // TYPE_NAME DataType.getDataType(method.getDataType()).name, // COLUMN_COUNT INT "" + method.getParameterCount(), // RETURNS_RESULT SMALLINT "" + returnsResult, // REMARKS replaceNullWithEmpty(alias.getComment()), // ID "" + alias.getId(), // SOURCE alias.getSource() // when adding more columns, see also below ); } } for (UserAggregate agg : database.getAllAggregates()) { int returnsResult = DatabaseMetaData.procedureReturnsResult; add(rows, // ALIAS_CATALOG catalog, // ALIAS_SCHEMA Constants.SCHEMA_MAIN, // ALIAS_NAME identifier(agg.getName()), // JAVA_CLASS agg.getJavaClassName(), // JAVA_METHOD "", // DATA_TYPE "" + DataType.convertTypeToSQLType(Value.NULL), // TYPE_NAME DataType.getDataType(Value.NULL).name, // COLUMN_COUNT INT "1", // RETURNS_RESULT SMALLINT "" + returnsResult, // REMARKS replaceNullWithEmpty(agg.getComment()), // ID "" + agg.getId(), // SOURCE "" // when adding more columns, see also below ); } break; } case FUNCTION_COLUMNS: { for (SchemaObject aliasAsSchemaObject : database.getAllSchemaObjects(DbObject.FUNCTION_ALIAS)) { FunctionAlias alias = (FunctionAlias) aliasAsSchemaObject; for (FunctionAlias.JavaMethod method : alias.getJavaMethods()) { Class[] columnList = method.getColumnClasses(); for (int k = 0; k < columnList.length; k++) { if (method.hasConnectionParam() && k == 0) { continue; } Class clazz = columnList[k]; int dataType = DataType.getTypeFromClass(clazz); DataType dt = DataType.getDataType(dataType); int nullable = clazz.isPrimitive() ? DatabaseMetaData.columnNoNulls : DatabaseMetaData.columnNullable; add(rows, // ALIAS_CATALOG catalog, // ALIAS_SCHEMA alias.getSchema().getName(), // ALIAS_NAME identifier(alias.getName()), // JAVA_CLASS alias.getJavaClassName(), // JAVA_METHOD alias.getJavaMethodName(), // COLUMN_COUNT "" + method.getParameterCount(), // POS INT "" + (k + (method.hasConnectionParam() ? 0 : 1)), // COLUMN_NAME "P" + (k + 1), // DATA_TYPE "" + DataType.convertTypeToSQLType(dt.type), // TYPE_NAME dt.name, // PRECISION INT "" + MathUtils.convertLongToInt(dt.defaultPrecision), // SCALE "" + dt.defaultScale, // RADIX "10", // NULLABLE SMALLINT "" + nullable, // COLUMN_TYPE "" + DatabaseMetaData.procedureColumnIn, // REMARKS "", // COLUMN_DEFAULT null ); } } } break; } case SCHEMATA: { String collation = database.getCompareMode().getName(); for (Schema schema : database.getAllSchemas()) { add(rows, // CATALOG_NAME catalog, // SCHEMA_NAME identifier(schema.getName()), // SCHEMA_OWNER identifier(schema.getOwner().getName()), // DEFAULT_CHARACTER_SET_NAME CHARACTER_SET_NAME, // DEFAULT_COLLATION_NAME collation, // IS_DEFAULT Constants.SCHEMA_MAIN.equals(schema.getName()) ? "TRUE" : "FALSE", // REMARKS replaceNullWithEmpty(schema.getComment()), // ID "" + schema.getId() ); } break; } case TABLE_PRIVILEGES: { for (Right r : database.getAllRights()) { Table table = r.getGrantedTable(); if (table == null || hideTable(table, session)) { continue; } String tableName = identifier(table.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } addPrivileges(rows, r.getGrantee(), catalog, table, null, r.getRightMask()); } break; } case COLUMN_PRIVILEGES: { for (Right r : database.getAllRights()) { Table table = r.getGrantedTable(); if (table == null || hideTable(table, session)) { continue; } String tableName = identifier(table.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } DbObject grantee = r.getGrantee(); int mask = r.getRightMask(); for (Column column : table.getColumns()) { addPrivileges(rows, grantee, catalog, table, column.getName(), mask); } } break; } case COLLATIONS: { for (Locale l : Collator.getAvailableLocales()) { add(rows, // NAME CompareMode.getName(l), // KEY l.toString() ); } break; } case VIEWS: { for (Table table : getAllTables(session)) { if (!table.getTableType().equals(Table.VIEW)) { continue; } String tableName = identifier(table.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } TableView view = (TableView) table; add(rows, // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME tableName, // VIEW_DEFINITION table.getCreateSQL(), // CHECK_OPTION "NONE", // IS_UPDATABLE "NO", // STATUS view.isInvalid() ? "INVALID" : "VALID", // REMARKS replaceNullWithEmpty(view.getComment()), // ID "" + view.getId() ); } break; } case IN_DOUBT: { ArrayList prepared = database.getInDoubtTransactions(); if (prepared != null && admin) { for (InDoubtTransaction prep : prepared) { add(rows, // TRANSACTION prep.getTransaction(), // STATE prep.getState() ); } } break; } case CROSS_REFERENCES: { for (SchemaObject obj : database.getAllSchemaObjects(DbObject.CONSTRAINT)) { Constraint constraint = (Constraint) obj; if (!(constraint.getConstraintType().equals(Constraint.REFERENTIAL))) { continue; } ConstraintReferential ref = (ConstraintReferential) constraint; IndexColumn[] cols = ref.getColumns(); IndexColumn[] refCols = ref.getRefColumns(); Table tab = ref.getTable(); Table refTab = ref.getRefTable(); String tableName = identifier(refTab.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } int update = getRefAction(ref.getUpdateAction()); int delete = getRefAction(ref.getDeleteAction()); for (int j = 0; j < cols.length; j++) { add(rows, // PKTABLE_CATALOG catalog, // PKTABLE_SCHEMA identifier(refTab.getSchema().getName()), // PKTABLE_NAME identifier(refTab.getName()), // PKCOLUMN_NAME identifier(refCols[j].column.getName()), // FKTABLE_CATALOG catalog, // FKTABLE_SCHEMA identifier(tab.getSchema().getName()), // FKTABLE_NAME identifier(tab.getName()), // FKCOLUMN_NAME identifier(cols[j].column.getName()), // ORDINAL_POSITION String.valueOf(j + 1), // UPDATE_RULE SMALLINT String.valueOf(update), // DELETE_RULE SMALLINT String.valueOf(delete), // FK_NAME identifier(ref.getName()), // PK_NAME identifier(ref.getUniqueIndex().getName()), // DEFERRABILITY "" + DatabaseMetaData.importedKeyNotDeferrable ); } } break; } case CONSTRAINTS: { for (SchemaObject obj : database.getAllSchemaObjects(DbObject.CONSTRAINT)) { Constraint constraint = (Constraint) obj; String constraintType = constraint.getConstraintType(); String checkExpression = null; IndexColumn[] indexColumns = null; Table table = constraint.getTable(); if (hideTable(table, session)) { continue; } Index index = constraint.getUniqueIndex(); String uniqueIndexName = null; if (index != null) { uniqueIndexName = index.getName(); } String tableName = identifier(table.getName()); if (!checkIndex(session, tableName, indexFrom, indexTo)) { continue; } if (constraintType.equals(Constraint.CHECK)) { checkExpression = ((ConstraintCheck) constraint).getExpression().getSQL(); } else if (constraintType.equals(Constraint.UNIQUE) || constraintType.equals(Constraint.PRIMARY_KEY)) { indexColumns = ((ConstraintUnique) constraint).getColumns(); } else if (constraintType.equals(Constraint.REFERENTIAL)) { indexColumns = ((ConstraintReferential) constraint).getColumns(); } String columnList = null; if (indexColumns != null) { StatementBuilder buff = new StatementBuilder(); for (IndexColumn col : indexColumns) { buff.appendExceptFirst(","); buff.append(col.column.getName()); } columnList = buff.toString(); } add(rows, // CONSTRAINT_CATALOG catalog, // CONSTRAINT_SCHEMA identifier(constraint.getSchema().getName()), // CONSTRAINT_NAME identifier(constraint.getName()), // CONSTRAINT_TYPE constraintType, // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME tableName, // UNIQUE_INDEX_NAME uniqueIndexName, // CHECK_EXPRESSION checkExpression, // COLUMN_LIST columnList, // REMARKS replaceNullWithEmpty(constraint.getComment()), // SQL constraint.getCreateSQL(), // ID "" + constraint.getId() ); } break; } case CONSTANTS: { for (SchemaObject obj : database.getAllSchemaObjects(DbObject.CONSTANT)) { Constant constant = (Constant) obj; ValueExpression expr = constant.getValue(); add(rows, // CONSTANT_CATALOG catalog, // CONSTANT_SCHEMA identifier(constant.getSchema().getName()), // CONSTANT_NAME identifier(constant.getName()), // CONSTANT_TYPE "" + DataType.convertTypeToSQLType(expr.getType()), // REMARKS replaceNullWithEmpty(constant.getComment()), // SQL expr.getSQL(), // ID "" + constant.getId() ); } break; } case DOMAINS: { for (UserDataType dt : database.getAllUserDataTypes()) { Column col = dt.getColumn(); add(rows, // DOMAIN_CATALOG catalog, // DOMAIN_SCHEMA Constants.SCHEMA_MAIN, // DOMAIN_NAME identifier(dt.getName()), // COLUMN_DEFAULT col.getDefaultSQL(), // IS_NULLABLE col.isNullable() ? "YES" : "NO", // DATA_TYPE "" + col.getDataType().sqlType, // PRECISION INT "" + col.getPrecisionAsInt(), // SCALE INT "" + col.getScale(), // TYPE_NAME col.getDataType().name, // SELECTIVITY INT "" + col.getSelectivity(), // CHECK_CONSTRAINT "" + col.getCheckConstraintSQL(session, "VALUE"), // REMARKS replaceNullWithEmpty(dt.getComment()), // SQL "" + dt.getCreateSQL(), // ID "" + dt.getId() ); } break; } case TRIGGERS: { for (SchemaObject obj : database.getAllSchemaObjects(DbObject.TRIGGER)) { TriggerObject trigger = (TriggerObject) obj; Table table = trigger.getTable(); add(rows, // TRIGGER_CATALOG catalog, // TRIGGER_SCHEMA identifier(trigger.getSchema().getName()), // TRIGGER_NAME identifier(trigger.getName()), // TRIGGER_TYPE trigger.getTypeNameList(), // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME identifier(table.getName()), // BEFORE BIT "" + trigger.isBefore(), // JAVA_CLASS trigger.getTriggerClassName(), // QUEUE_SIZE INT "" + trigger.getQueueSize(), // NO_WAIT BIT "" + trigger.isNoWait(), // REMARKS replaceNullWithEmpty(trigger.getComment()), // SQL trigger.getCreateSQL(), // ID "" + trigger.getId() ); } break; } case SESSIONS: { long now = System.currentTimeMillis(); for (Session s : database.getSessions(false)) { if (admin || s == session) { Command command = s.getCurrentCommand(); long start = s.getCurrentCommandStart(); if (start == 0) { start = now; } add(rows, // ID "" + s.getId(), // USER_NAME s.getUser().getName(), // SESSION_START new Timestamp(s.getSessionStart()).toString(), // STATEMENT command == null ? null : command.toString(), // STATEMENT_START new Timestamp(start).toString() ); } } break; } case LOCKS: { for (Session s : database.getSessions(false)) { if (admin || s == session) { for (Table table : s.getLocks()) { add(rows, // TABLE_SCHEMA table.getSchema().getName(), // TABLE_NAME table.getName(), // SESSION_ID "" + s.getId(), // LOCK_TYPE table.isLockedExclusivelyBy(s) ? "WRITE" : "READ" ); } } } break; } case SESSION_STATE: { for (String name : session.getVariableNames()) { Value v = session.getVariable(name); add(rows, // KEY "@" + name, // SQL "SET @" + name + " " + v.getSQL() ); } for (Table table : session.getLocalTempTables()) { add(rows, // KEY "TABLE " + table.getName(), // SQL table.getCreateSQL() ); } String[] path = session.getSchemaSearchPath(); if (path != null && path.length > 0) { StatementBuilder buff = new StatementBuilder("SET SCHEMA_SEARCH_PATH "); for (String p : path) { buff.appendExceptFirst(", "); buff.append(StringUtils.quoteIdentifier(p)); } add(rows, // KEY "SCHEMA_SEARCH_PATH", // SQL buff.toString() ); } String schema = session.getCurrentSchemaName(); if (schema != null) { add(rows, // KEY "SCHEMA", // SQL "SET SCHEMA " + StringUtils.quoteIdentifier(schema) ); } break; } default: DbException.throwInternalError("type="+type); } return rows; } private static int getRefAction(int action) { switch(action) { case ConstraintReferential.CASCADE: return DatabaseMetaData.importedKeyCascade; case ConstraintReferential.RESTRICT: return DatabaseMetaData.importedKeyRestrict; case ConstraintReferential.SET_DEFAULT: return DatabaseMetaData.importedKeySetDefault; case ConstraintReferential.SET_NULL: return DatabaseMetaData.importedKeySetNull; default: throw DbException.throwInternalError("action="+action); } } public void removeRow(Session session, Row row) { throw DbException.getUnsupportedException("META"); } public void addRow(Session session, Row row) { throw DbException.getUnsupportedException("META"); } public void removeChildrenAndResources(Session session) { throw DbException.getUnsupportedException("META"); } public void close(Session session) { // nothing to do } public void unlock(Session s) { // nothing to do } private void addPrivileges(ArrayList rows, DbObject grantee, String catalog, Table table, String column, int rightMask) { if ((rightMask & Right.SELECT) != 0) { addPrivilege(rows, grantee, catalog, table, column, "SELECT"); } if ((rightMask & Right.INSERT) != 0) { addPrivilege(rows, grantee, catalog, table, column, "INSERT"); } if ((rightMask & Right.UPDATE) != 0) { addPrivilege(rows, grantee, catalog, table, column, "UPDATE"); } if ((rightMask & Right.DELETE) != 0) { addPrivilege(rows, grantee, catalog, table, column, "DELETE"); } } private void addPrivilege(ArrayList rows, DbObject grantee, String catalog, Table table, String column, String right) { String isGrantable = "NO"; if (grantee.getType() == DbObject.USER) { User user = (User) grantee; if (user.isAdmin()) { // the right is grantable if the grantee is an admin isGrantable = "YES"; } } if (column == null) { add(rows, // GRANTOR null, // GRANTEE identifier(grantee.getName()), // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME identifier(table.getName()), // PRIVILEGE_TYPE right, // IS_GRANTABLE isGrantable ); } else { add(rows, // GRANTOR null, // GRANTEE identifier(grantee.getName()), // TABLE_CATALOG catalog, // TABLE_SCHEMA identifier(table.getSchema().getName()), // TABLE_NAME identifier(table.getName()), // COLUMN_NAME identifier(column), // PRIVILEGE_TYPE right, // IS_GRANTABLE isGrantable ); } } private void add(ArrayList rows, String... strings) { Value[] values = new Value[strings.length]; for (int i = 0; i < strings.length; i++) { String s = strings[i]; Value v = (s == null) ? (Value) ValueNull.INSTANCE : ValueString.get(s); Column col = columns[i]; v = col.convert(v); values[i] = v; } Row row = new Row(values, 1); row.setKey(rows.size()); rows.add(row); } public void checkRename() { throw DbException.getUnsupportedException("META"); } public void checkSupportAlter() { throw DbException.getUnsupportedException("META"); } public void truncate(Session session) { throw DbException.getUnsupportedException("META"); } public long getRowCount(Session session) { throw DbException.throwInternalError(); } public boolean canGetRowCount() { return false; } public boolean canDrop() { return false; } public String getTableType() { return Table.SYSTEM_TABLE; } public Index getScanIndex(Session session) { return new MetaIndex(this, IndexColumn.wrap(columns), true); } public ArrayList getIndexes() { ArrayList list = New.arrayList(); if (metaIndex == null) { return list; } list.add(new MetaIndex(this, IndexColumn.wrap(columns), true)); // TODO re-use the index list.add(metaIndex); return list; } public long getMaxDataModificationId() { switch (type) { case SETTINGS: case IN_DOUBT: case SESSIONS: case LOCKS: case SESSION_STATE: return Long.MAX_VALUE; } return database.getModificationDataId(); } public Index getUniqueIndex() { return null; } /** * Get the number of meta table types. Supported meta table * types are 0 .. this value - 1. * * @return the number of meta table types */ public static int getMetaTableTypeCount() { return META_TABLE_TYPE_COUNT; } public long getRowCountApproximation() { return ROW_COUNT_APPROXIMATION; } public boolean isDeterministic() { return true; } public boolean canReference() { return false; } }