Package com.foundationdb.server.service.is

Source Code of com.foundationdb.server.service.is.BasicInfoSchemaTablesServiceImpl$RootPathTable

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.foundationdb.server.service.is;

import com.foundationdb.ais.model.AkibanInformationSchema;
import com.foundationdb.ais.model.Column;
import com.foundationdb.ais.model.ForeignKey;
import com.foundationdb.ais.model.FullTextIndex;
import com.foundationdb.ais.model.GroupIndex;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.IndexColumn;
import com.foundationdb.ais.model.Join;
import com.foundationdb.ais.model.JoinColumn;
import com.foundationdb.ais.model.Parameter;
import com.foundationdb.ais.model.Routine;
import com.foundationdb.ais.model.Schema;
import com.foundationdb.ais.model.Sequence;
import com.foundationdb.ais.model.SQLJJar;
import com.foundationdb.ais.model.Table;
import com.foundationdb.ais.model.TableIndex;
import com.foundationdb.ais.model.TableName;
import com.foundationdb.ais.model.View;
import com.foundationdb.ais.model.aisb2.AISBBasedBuilder;
import com.foundationdb.ais.model.aisb2.NewAISBuilder;
import com.foundationdb.qp.memoryadapter.BasicFactoryBase;
import com.foundationdb.qp.memoryadapter.MemoryAdapter;
import com.foundationdb.qp.memoryadapter.MemoryGroupCursor;
import com.foundationdb.qp.memoryadapter.MemoryGroupCursor.GroupScan;
import com.foundationdb.qp.row.ValuesRow;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.server.service.Service;
import com.foundationdb.server.service.routines.ScriptEngineManagerProvider;
import com.foundationdb.server.service.security.SecurityService;
import com.foundationdb.server.service.session.Session;
import com.foundationdb.server.store.SchemaManager;
import com.foundationdb.server.types.Attribute;
import com.foundationdb.server.types.TBundleID;
import com.foundationdb.server.types.TClass;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.common.types.StringAttribute;
import com.foundationdb.server.types.common.types.DecimalAttribute;
import com.foundationdb.server.types.common.types.TBinary;
import com.foundationdb.server.types.common.types.TypeValidator;
import com.foundationdb.server.types.common.types.TypesTranslator;
import com.foundationdb.server.types.service.TypesRegistry;
import com.foundationdb.sql.pg.PostgresType;
import com.google.common.collect.Iterators;
import com.google.inject.Inject;

import javax.script.ScriptEngineFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;

public class BasicInfoSchemaTablesServiceImpl
    extends SchemaTablesService
    implements Service, BasicInfoSchemaTablesService {
   
    static final TableName SCHEMATA = new TableName(SCHEMA_NAME, "schemata");
    static final TableName TABLES = new TableName(SCHEMA_NAME, "tables");
    static final TableName COLUMNS = new TableName(SCHEMA_NAME, "columns");
    static final TableName TABLE_CONSTRAINTS = new TableName(SCHEMA_NAME, "table_constraints");
    static final TableName REFERENTIAL_CONSTRAINTS = new TableName(SCHEMA_NAME, "referential_constraints");
    static final TableName GROUPING_CONSTRAINTS = new TableName(SCHEMA_NAME, "grouping_constraints");
    static final TableName KEY_COLUMN_USAGE = new TableName(SCHEMA_NAME, "key_column_usage");
    static final TableName INDEXES = new TableName(SCHEMA_NAME, "indexes");
    static final TableName INDEX_COLUMNS = new TableName(SCHEMA_NAME, "index_columns");
    static final TableName SEQUENCES = new TableName(SCHEMA_NAME, "sequences");
    static final TableName VIEWS = new TableName(SCHEMA_NAME, "views");
    static final TableName VIEW_TABLE_USAGE = new TableName(SCHEMA_NAME, "view_table_usage");
    static final TableName VIEW_COLUMN_USAGE = new TableName(SCHEMA_NAME, "view_column_usage");
    static final TableName ROUTINES = new TableName(SCHEMA_NAME, "routines");
    static final TableName PARAMETERS = new TableName(SCHEMA_NAME, "parameters");
    static final TableName JARS = new TableName(SCHEMA_NAME, "jars");
    static final TableName ROUTINE_JAR_USAGE = new TableName(SCHEMA_NAME, "routine_jar_usage");
    static final TableName SCRIPT_ENGINES = new TableName(SCHEMA_NAME, "script_engines");
    static final TableName SCRIPT_ENGINE_NAMES = new TableName(SCHEMA_NAME, "script_engine_names");
    static final TableName TYPES = new TableName (SCHEMA_NAME, "types");
    static final TableName TYPE_BUNDLES = new TableName (SCHEMA_NAME, "type_bundles");
    static final TableName TYPE_ATTRIBUTES = new TableName (SCHEMA_NAME, "type_attributes");

    private final SecurityService securityService;
    private final ScriptEngineManagerProvider scriptEngineManagerProvider;

    @Inject
    public BasicInfoSchemaTablesServiceImpl(SchemaManager schemaManager,
                                            SecurityService securityService,
                                            ScriptEngineManagerProvider scriptEngineManagerProvider) {
        super(schemaManager);
        this.securityService = securityService;
        this.scriptEngineManagerProvider = scriptEngineManagerProvider;
    }

    @Override
    public void start() {
        AkibanInformationSchema ais = createTablesToRegister(getTypesTranslator());
        attachFactories(ais);
    }

    @Override
    public void stop() {
        // Nothing
    }

    @Override
    public void crash() {
        // Nothing
    }

    protected boolean isAccessible(Session session, String schemaName) {
        if ((session == null) || (securityService == null))
            return true;
        return securityService.isAccessible(session, schemaName);
    }

    protected boolean isAccessible(Session session, TableName name) {
        return isAccessible(session, name.getSchemaName());
    }

    protected TypesRegistry getTypesRegistry() {
        return schemaManager.getTypesRegistry();
    }

    protected TypesTranslator getTypesTranslator() {
        return schemaManager.getTypesTranslator();
    }

    private List<ScriptEngineFactory> getScriptEngineFactories() {
        return scriptEngineManagerProvider.getManager().getEngineFactories();
    }

    private class SchemataFactory extends BasicFactoryBase {
        public SchemataFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            return getAIS(session).getSchemas().size();
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<Schema> it;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                it = getAIS(session).getSchemas().values().iterator();
            }

            @Override
            public Row next() {
                while(it.hasNext()) {
                    Schema schema = it.next();
                    if(isAccessible(session, schema.getName())) {
                        return new ValuesRow(rowType,
                                            null,        // catalog
                                             schema.getName(),
                                             null,              // owner
                                             null,null, null,   // default charset catalog/schema/name
                                             null,              // sql path
                                             null,null, null,   // default charset catalog/schema/name
                                             ++rowCounter /*hidden pk*/);
                    }
                }
                return null;
           }
        }
    }

    private AkibanInformationSchema getAIS(Session session) {
        return schemaManager.getAis(session);
    }

    private class TablesFactory extends BasicFactoryBase {
        public TablesFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            AkibanInformationSchema ais = getAIS(session);
            return ais.getTables().size() + ais.getViews().size() ;
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<Table> tableIt;
            Iterator<View> viewIt = null;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.tableIt = getAIS(session).getTables().values().iterator();
            }

            @Override
            public Row next() {
                if(viewIt == null) {
                    while(tableIt.hasNext()) {
                        Table table = tableIt.next();
                        String tableType = (table.getName().inSystemSchema() ? "SYSTEM " : "") +
                                           (table.hasMemoryTableFactory() ? "VIEW" : "TABLE");
                        final Integer ordinal = table.hasMemoryTableFactory() ? null : table.getOrdinal();
                        final boolean isInsertable = !table.hasMemoryTableFactory();
                        if(isAccessible(session, table.getName())) {
                            return new ValuesRow(rowType,
                                     null,       //catalog
                                     table.getName().getSchemaName(),
                                     table.getName().getTableName(),
                                     tableType,
                                     null,                  // self reference column
                                     null,                  // reference generation
                                     boolResult(isInsertable),
                                     boolResult(false),     // is types
                                     null,                  //commit action
                                     null,                  // charset catalog
                                     null,
                                     table.getDefaultedCharsetName().toLowerCase(),
                                     null,                  // collation catalog
                                     null,
                                     table.getDefaultedCollationName().toLowerCase(),
                                     table.getTableId(),
                                     ordinal,
                                     table.getGroup().getStorageNameString(),
                                     table.getGroup().getStorageDescription().getStorageFormat(),
                                     ++rowCounter /*hidden pk*/);
                        }
                    }
                    viewIt = getAIS(session).getViews().values().iterator();
                }
                while(viewIt.hasNext()) {
                    View view = viewIt.next();
                    if(isAccessible(session, view.getName())) {
                        return new ValuesRow(rowType,
                                null,
                                 view.getName().getSchemaName(),
                                 view.getName().getTableName(),
                                 "VIEW",
                                 null,      //self reference
                                 null,      //reference generation
                                 boolResult(false), // insertable
                                 boolResult(false), // is typed
                                 null,              // commit action
                                 null,null,null,    // charset catalog/schema/name
                                 null,null,null,    // collation catalog/schema/name
                                 null,              // tableId
                                 null,              // ordinal
                                 null,              // storage_name
                                 null,              // storage_format
                                 ++rowCounter /*hidden pk*/);
                    }
                }
                return null;
            }
        }
    }

    private class ColumnsFactory extends BasicFactoryBase {
        public ColumnsFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            AkibanInformationSchema ais = getAIS(session);
            long count = 0;
            for(Table table : ais.getTables().values()) {
                count += table.getColumns().size();
            }
            for(View view : ais.getViews().values()) {
                count += view.getColumns().size();
            }
            return count;
        }
       
        private class Scan extends BaseScan {
            final Session session;
            final Iterator<Table> tableIt;
            Iterator<View> viewIt = null;
            Iterator<Column> columnIt;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.tableIt = getAIS(session).getTables().values().iterator();
            }

            @Override
            public Row next() {
                getCols:
                while(columnIt == null || !columnIt.hasNext()) {
                    if(viewIt == null) {
                        while(tableIt.hasNext()) {
                            Table table = tableIt.next();
                            if(isAccessible(session, table.getName())) {
                                columnIt = table.getColumns().iterator();
                                continue getCols;
                            }
                        }
                        viewIt = getAIS(session).getViews().values().iterator();
                    }
                    while(viewIt.hasNext()) {
                        View view = viewIt.next();
                        if(isAccessible(session, view.getName())) {
                            columnIt = view.getColumns().iterator();
                            continue getCols;
                        }
                    }
                    return null;
                }

                Column column = columnIt.next();

                Long precision = null;
                Long scale = null;
                Long radix = null;
                String charset = null;
                String collation = null;
                if (column.getType().hasAttributes(DecimalAttribute.class)) {
                    precision = (long) column.getType().attribute(DecimalAttribute.PRECISION);
                    scale = (long) column.getType().attribute(DecimalAttribute.SCALE);
                    radix = 10L;
                }
                Long charMaxLength = null;
                Long charOctetLength = null;
                if (column.hasCharsetAndCollation()) {
                    charset = column.getCharsetName().toLowerCase();
                    collation = column.getCollationName().toLowerCase();
                }
                if (column.getType().hasAttributes(StringAttribute.class)) {
                    charMaxLength = (long) column.getType().attribute(StringAttribute.MAX_LENGTH);
                    charOctetLength = column.getMaxStorageSize() - column.getPrefixSize();
                }
                else if (column.getType().hasAttributes(TBinary.Attrs.class)) {
                    charMaxLength = charOctetLength = (long) column.getType().attribute(TBinary.Attrs.LENGTH);
                }
                String sequenceSchema = null;
                String sequenceName = null;
                String identityGeneration = null;
                long identityStart = 0,
                        identityIncrement = 0,
                        identityMin = 0,
                        identityMax = 0;
                String identityCycle = null;
                if (column.getIdentityGenerator() != null) {
                    sequenceSchema = column.getIdentityGenerator().getSequenceName().getSchemaName();
                    sequenceName   = column.getIdentityGenerator().getSequenceName().getTableName();
                    identityGeneration = column.getDefaultIdentity() ? "BY DEFAULT" : "ALWAYS";
                    identityStart = column.getIdentityGenerator().getStartsWith();
                    identityIncrement = column.getIdentityGenerator().getIncrement();
                    identityMin = column.getIdentityGenerator().getMinValue();
                    identityMax = column.getIdentityGenerator().getMaxValue();
                    identityCycle = boolResult(column.getIdentityGenerator().isCycle());
                }
                String defaultString = null;
                if (column.getDefaultValue() != null) {
                    defaultString = column.getDefaultValue();
                }
                else if (column.getDefaultFunction() != null) {
                    defaultString = column.getDefaultFunction() + "()";
                }
               
                return new ValuesRow(rowType,
                                    null,
                                     column.getColumnar().getName().getSchemaName(),
                                     column.getColumnar().getName().getTableName(),
                                     column.getName(),
                                     column.getPosition().longValue(),
                                     defaultString,
                                     boolResult(column.getNullable()),
                                     column.getTypeName(),
                                     charMaxLength,
                                     charOctetLength,
                                     precision,
                                     radix,
                                     scale,
                                     null,          // charset catalog
                                     null,          // charset schema
                                     charset,
                                     null,              // collation catalog
                                     null,              //collation schema
                                     collation,
                                     null,null,null,    // domain catalog/schema/name
                                     null,null,null,    // udt catalog/schema/name
                                     null,null,null,    // scope catalog/schema/name
                                     null,              //maximum cardinality
                                     boolResult(false), // is self referencing
                                     boolResult(identityGeneration != null),
                                     identityGeneration,
                                     identityGeneration != null ? identityStart : null,
                                     identityGeneration != null ? identityIncrement : null,
                                     identityGeneration != null ? identityMin : null,
                                     identityGeneration != null ? identityMax : null,
                                     identityCycle,
                                     boolResult(false), // is generated
                                     null,              // generation expression
                                     boolResult(true)// is Updatable
                                     null,              // sequence catalog
                                     sequenceSchema,
                                     sequenceName,
                                     ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class TableConstraintsFactory extends BasicFactoryBase {
        public TableConstraintsFactory(TableName sourceTable) {
            super(sourceTable);
        }

        private TableConstraintsIteration newIteration(Session session,
                                                       AkibanInformationSchema ais) {
            return new TableConstraintsIteration(session, ais.getTables().values().iterator());
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            AkibanInformationSchema ais = getAIS(session);
            long count = 0;
            TableConstraintsIteration it = newIteration(null, ais);
            while(it.next()) {
                ++count;
            }
            return count;
        }

        private class Scan extends BaseScan {
            final TableConstraintsIteration it;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.it = newIteration(session, getAIS(session));
            }

            @Override
            public Row next() {
                if(!it.next()) {
                    return null;
                }
                return new ValuesRow(rowType,
                        null,   //constraint catalog
                         it.getTable().getName().getSchemaName(),
                         it.getName(),
                         null,          // table_catalog
                         it.getTable().getName().getSchemaName(),
                         it.getTable().getName().getTableName(),
                         it.getType(),
                         boolResult(it.isDeferrable()),
                         boolResult(it.isInitiallyDeferred()),
                         boolResult(true)// enforced
                         ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class ReferentialConstraintsFactory extends BasicFactoryBase {
        public ReferentialConstraintsFactory(TableName sourceTable) {
            super(sourceTable);
        }

        private TableConstraintsIteration newIteration(Session session,
                                                       AkibanInformationSchema ais) {
            return new TableConstraintsIteration(session, ais.getTables().values().iterator());
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            AkibanInformationSchema ais = getAIS(session);
            long count = 0;
            TableConstraintsIteration it = newIteration(null, ais);
            while(it.next()) {
                if (it.isForeignKey()) {
                    ++count;
                }
            }
            return count;
        }

        private class Scan extends BaseScan {
            final TableConstraintsIteration it;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.it = newIteration(session, getAIS(session));
            }

            @Override
            public Row next() {
                do {
                    if(!it.next()) {
                        return null;
                    }
                } while (!it.isForeignKey());
                ForeignKey fk = it.getTable().getReferencingForeignKey(it.getIndex().getIndexName().getName());
                return new ValuesRow(rowType,
                        null,   //constraint catalog
                         fk.getConstraintName().getSchemaName(),
                         fk.getConstraintName().getTableName(),
                         null,          //unique_constraint catalog
                         fk.getReferencedIndex().getConstraintName().getSchemaName(),
                         fk.getReferencedIndex().getConstraintName().getTableName(),
                         "NONE",
                         fk.getUpdateAction().toSQL(),
                         fk.getDeleteAction().toSQL(),
                         ++rowCounter /*hidden pk*/);
            }
        }
    }

    private static class RootPathTable {
        final Table root;
        final String path;
        final Table table;

        public RootPathTable(Table root, String path, Table table) {
            this.root = root;
            this.path = path;
            this.table = table;
        }

        @Override
        public String toString() {
            return root.getName() + ", " + table.getName() + ", " + path;
        }
    }

    private static class TableIDComparator implements Comparator<Table> {
        @Override
        public int compare(Table o1, Table o2) {
            return o1.getTableId().compareTo(o2.getTableId());
        }
    }
    private static final TableIDComparator TABLE_ID_COMPARATOR = new TableIDComparator();

    private static void addBranchToList(List<RootPathTable> list, StringBuilder builder, Table root, Table branch) {
        if(builder.length() != 0) {
            builder.append('/');
        }
        builder.append(branch.getName().getSchemaName());
        builder.append('.');
        builder.append(branch.getName().getTableName());
        list.add(new RootPathTable(root, builder.toString(), branch));

        // For tables at the same depth, comparing table IDs is currently synonymous with ordinals
        List<Table> children = new ArrayList<>();
        for(Join join : branch.getChildJoins()) {
            children.add(join.getChild());
        }
        Collections.sort(children, TABLE_ID_COMPARATOR);

        for(Table child : children) {
            int saveLen = builder.length();
            addBranchToList(list, builder, root, child);
            builder.setLength(saveLen);
        }
    }

    private class GroupingConstraintsFactory extends BasicFactoryBase {
        public GroupingConstraintsFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            return getAIS(session).getTables().size();
        }

        private class Scan extends BaseScan {
            final List<RootPathTable> rootPathTables;
            final Iterator<RootPathTable> it;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                AkibanInformationSchema ais = getAIS(session);

                // Desired output: groups together, ordered by branch (ordinal), then ordered by depth
                // Highest level sorting will be by schema.root, which seems as good as any
                rootPathTables = new ArrayList<>();
                Collection<Table> allTables = new ArrayList<>();
                for(Table table : ais.getTables().values()) {
                    if(isAccessible(session, table.getName())) {
                        allTables.add(table);
                    }
                }
                StringBuilder builder = new StringBuilder();
                for(Table table : allTables) {
                    if(table.isRoot()) {
                        addBranchToList(rootPathTables, builder, table, table);
                        builder.setLength(0);
                    }
                }
                assert rootPathTables.size() == allTables.size() : "Didn't collect all tables";
                it = rootPathTables.iterator();
            }

            @Override
            public Row next() {
                if (!it.hasNext()) {
                    return null;
                }
               
                RootPathTable rpt = it.next();
                Table table = rpt.table;
                String constraintName = null;
                String uniqueSchema = null;
                String uniqueConstraint = null;

                Join join = table.getParentJoin();
                if (table.getParentJoin() != null) {
                    constraintName = join.getConstraintName().getTableName();
                    uniqueSchema = join.getParent().getName().getSchemaName();
                    uniqueConstraint = join.getParent().getPrimaryKey().getIndex().getConstraintName().getTableName();
                }
               
                return new ValuesRow(rowType,
                                    null,                               //root table catalog
                                     rpt.root.getName().getSchemaName(),// root_table_schema
                                     rpt.root.getName().getTableName(), // root_table_name
                                     null,                              //constraint catalog
                                     table.getName().getSchemaName(),   // constraint_schema
                                     table.getName().getTableName(),    // constraint_table_name
                                     rpt.path,                          // path
                                     table.getDepth().longValue(),      // depth
                                     constraintName,                    // constraint_name
                                     null,                              // unique_catalog
                                     uniqueSchema,                      // unique_schema
                                     uniqueConstraint,                  // unique_constraint_name
                                     ++rowCounter);
            }
        }
    }

    private class KeyColumnUsageFactory extends BasicFactoryBase {
        public KeyColumnUsageFactory(TableName sourceTable) {
            super(sourceTable);
        }

        private TableConstraintsIteration newIteration(Session session,
                                                       AkibanInformationSchema ais) {
            return new TableConstraintsIteration(session, ais.getTables().values().iterator());
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            int count = 0;
            TableConstraintsIteration it = newIteration(null, getAIS(session));
            while(it.next()) {
                ++count;
            }
            return count;
        }

        private class Scan extends BaseScan {
            final TableConstraintsIteration it;
            Iterator<IndexColumn> indexColIt;
            Iterator<JoinColumn> joinColIt;
            String colName;
            String constraintName;
            int colPos;
            Long posInUnique;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.it = newIteration(session, getAIS(session));
            }

            // Find position in parents PK
            private Integer findPosInIndex(Column column, Index index) {
                // Find position in the parents pk
                for(IndexColumn indexCol : index.getKeyColumns()) {
                    if(column == indexCol.getColumn()) {
                        return indexCol.getPosition();
                    }
                }
                return null;
            }

            public boolean advance() {
                posInUnique = null;
                if(joinColIt != null && joinColIt.hasNext()) {
                    JoinColumn joinColumn = joinColIt.next();
                    colName = joinColumn.getChild().getName();
                    posInUnique = findPosInIndex(joinColumn.getParent(), joinColumn.getParent().getTable().getPrimaryKey().getIndex()).longValue();
                    constraintName = it.getName();
                } else if(indexColIt != null && indexColIt.hasNext()) {
                    IndexColumn indexColumn = indexColIt.next();
                    colName = indexColumn.getColumn().getName();
                    constraintName = indexColumn.getIndex().getConstraintName() == null ? null : indexColumn.getIndex().getConstraintName().getTableName();
                    if (it.isForeignKey()) {
                        ForeignKey fk = it.getTable().getReferencingForeignKey(it.getIndex().getIndexName().getName());
                        posInUnique = findPosInIndex(fk.getReferencedColumns().get(indexColumn.getPosition()), fk.getReferencedIndex()).longValue();
                        //this is the constructed referencing index, its IndexName is the foreign key constraint name
                        constraintName = indexColumn.getIndex().getIndexName().getName();
                    }
                } else if(it.next()) {
                    joinColIt = null;
                    indexColIt = null;
                    if(it.isGrouping()) {
                        joinColIt = it.getTable().getParentJoin().getJoinColumns().iterator();
                    } else {
                        indexColIt = it.getIndex().getKeyColumns().iterator();
                    }
                    colPos = -1;
                    return advance();
                } else {
                    return false;
                }
                ++colPos;
                return true;
            }

            @Override
            public Row next() {
                if(!advance()) {
                    return null;
                }
                return new ValuesRow(rowType,
                        null,       // constraint catalog,
                        it.getTable().getName().getSchemaName(),
                        constraintName,
                        null,       // table catalog
                        it.getTable().getName().getSchemaName(),
                        it.getTable().getName().getTableName(),
                        colName,
                        new Long(colPos),
                        posInUnique,
                        ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class IndexesFactory extends BasicFactoryBase {
        public IndexesFactory(TableName sourceTable) {
            super(sourceTable);
        }

        private IndexIteration newIteration(Session session,
                                            AkibanInformationSchema ais) {
            return new IndexIteration(session, ais.getTables().values().iterator());
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            long count = 0;
            IndexIteration it = newIteration(null, getAIS(session));
            while(it.next() != null) {
                ++count;
            }
            return count;
        }

        private class Scan extends BaseScan {
            final IndexIteration indexIt;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.indexIt = newIteration(session, getAIS(session));
            }

            @Override
            public Row next() {
                Index index = indexIt.next();
                if(index == null) {
                    return null;
                }
                final String indexType;
                if(index.isPrimaryKey()) {
                    indexType = "PRIMARY";
                } else if(index.isUnique()) {
                    indexType = "UNIQUE";
                } else {
                    indexType = "INDEX";
                }
                return new ValuesRow(rowType,
                        null,
                        indexIt.getTable().getName().getSchemaName(),
                        indexIt.getTable().getName().getTableName(),
                        index.getIndexName().getName(),
                        null,
                        index.getConstraintName() == null ? null : index.getConstraintName().getSchemaName(),
                        index.getConstraintName() == null ? null : index.getConstraintName().getTableName(),
                        index.getIndexId(),
                        index.getStorageNameString(),
                        index.getStorageDescription().getStorageFormat(),
                        indexType,
                        boolResult(index.isUnique()),
                        index.isGroupIndex() ? index.getJoinType().name() : null,
                        (index.getIndexMethod() == Index.IndexMethod.NORMAL) ? null : index.getIndexMethod().name(),
                        ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class IndexColumnsFactory extends BasicFactoryBase {
        public IndexColumnsFactory(TableName sourceTable) {
            super(sourceTable);
        }

        private IndexIteration newIteration(Session session,
                                            AkibanInformationSchema ais) {
            return new IndexIteration(session, ais.getTables().values().iterator());
        }

        @Override
        public MemoryGroupCursor.GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            IndexIteration indexIt = newIteration(null, getAIS(session));
            long count = 0;
            Index index;
            while((index = indexIt.next()) != null) {
                count += index.getKeyColumns().size();
            }
            return count;
        }

        private class Scan extends BaseScan {
            final IndexIteration indexIt;
            Iterator<IndexColumn> indexColumnIt;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.indexIt = newIteration(session, getAIS(session));
            }

            private IndexColumn advance() {
                while(indexColumnIt == null || !indexColumnIt.hasNext()) {
                    Index index = indexIt.next();
                    if(index == null) {
                        return null;
                    }
                    indexColumnIt = index.getKeyColumns().iterator(); // Always at least 1
                }
                return indexColumnIt.next();
            }

            @Override
            public Row next() {
                IndexColumn indexColumn = advance();
                if(indexColumn == null) {
                    return null;
                }
                return new ValuesRow(rowType,
                        null,
                         indexIt.getTable().getName().getSchemaName(),
                         indexIt.getTable().getName().getTableName(),
                         indexColumn.getIndex().getIndexName().getName(),
                         null,
                         indexColumn.getColumn().getTable().getName().getSchemaName(),
                         indexColumn.getColumn().getTable().getName().getTableName(),
                         indexColumn.getColumn().getName(),
                         indexColumn.getPosition().longValue(),
                         boolResult(indexColumn.isAscending()),
                         ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class SequencesFactory extends BasicFactoryBase {
        public SequencesFactory (TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            return getAIS(session).getSequences().size();
        }
       
        private class Scan extends BaseScan {
            final Session session;
            final Iterator<Sequence> it;
            final static String datatype = "bigint";
           
            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.it =  getAIS(session).getSequences().values().iterator();
            }

            @Override
            public Row next() {
                while(it.hasNext()) {
                    Sequence sequence = it.next();
                    if(isAccessible(session, sequence.getSequenceName()) &&
                            !sequence.isInternalSequence()) {
                        return new ValuesRow(rowType,
                                             null,      //sequence catalog
                                             sequence.getSequenceName().getSchemaName(),
                                             sequence.getSequenceName().getTableName(),
                                             datatype,
                                             sequence.getStartsWith(),
                                             sequence.getMinValue(),
                                             sequence.getMaxValue(),
                                             sequence.getIncrement(),
                                             boolResult(sequence.isCycle()),
                                             sequence.getStorageNameString(),
                                             ++rowCounter);
                    }
                }
                return null;
            }
        }
    }
    private class ViewsFactory extends BasicFactoryBase {
        public ViewsFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            return getAIS(session).getViews().size();
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<View> it;
            final static String checkOption = "NONE";

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                it = getAIS(session).getViews().values().iterator();
            }

            @Override
            public Row next() {
                while(it.hasNext()) {
                    View view = it.next();
                    if(isAccessible(session, view.getName())) {
                        return new ValuesRow(rowType,
                                            null,       // view catalog
                                             view.getName().getSchemaName(),
                                             view.getName().getTableName(),
                                             view.getDefinition(),
                                             checkOption,
                                             boolResult(false),
                                             boolResult(false),
                                             boolResult(false),
                                             boolResult(false),
                                             boolResult(false),
                                             ++rowCounter /*hidden pk*/);
                    }
                }
                return null;
            }
        }
    }

    private class ViewTableUsageFactory extends BasicFactoryBase {
        public ViewTableUsageFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            long count = 0;
            for (View view : getAIS(session).getViews().values()) {
                count += view.getTableReferences().size();
            }
            return count;
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<View> viewIt;
            View view;
            Iterator<TableName> tableIt = null;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.viewIt =  getAIS(session).getViews().values().iterator();
            }

            @Override
            public Row next() {
                getTables:
                while((tableIt == null) || !tableIt.hasNext()) {
                    while(viewIt.hasNext()) {
                        view = viewIt.next();
                        if(isAccessible(session, view.getName())) {
                            tableIt = view.getTableReferences().iterator();
                            continue getTables;
                        }
                    }
                    return null;
                }
                TableName table = tableIt.next();
                return new ValuesRow(rowType,
                                    null,
                                     view.getName().getSchemaName(),
                                     view.getName().getTableName(),
                                    null,
                                     table.getSchemaName(),
                                     table.getTableName(),
                                     ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class ViewColumnUsageFactory extends BasicFactoryBase {
        public ViewColumnUsageFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            long count = 0;
            for (View view : getAIS(session).getViews().values()) {
                for (Collection<String> columns : view.getTableColumnReferences().values()) {
                    count += columns.size();
                }
            }
            return count;
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<View> viewIt;
            View view;
            Iterator<Map.Entry<TableName,Collection<String>>> tableIt = null;
            TableName table;
            Iterator<String> columnIt = null;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.viewIt = getAIS(session).getViews().values().iterator();
            }

            @Override
            public Row next() {
                while((columnIt == null) || !columnIt.hasNext()) {
                    getTables:
                    while((tableIt == null) || !tableIt.hasNext()) {
                        while(viewIt.hasNext()) {
                            view = viewIt.next();
                            if(isAccessible(session, view.getName())) {
                                tableIt = view.getTableColumnReferences().entrySet().iterator();                           
                                continue getTables;
                            }
                        }
                        return null;
                    }
                    Map.Entry<TableName,Collection<String>> entry = tableIt.next();
                    table = entry.getKey();
                    columnIt = entry.getValue().iterator();
                }
                String column = columnIt.next();
                return new ValuesRow(rowType,
                                    null,
                                     view.getName().getSchemaName(),
                                     view.getName().getTableName(),
                                    null,
                                     table.getSchemaName(),
                                     table.getTableName(),
                                     column,
                                     ++rowCounter /*hidden pk*/);
            }
        }
    }
   
    private class TableConstraintsIteration {
        private final Session session;
        private final Iterator<Table> tableIt;
        private Iterator<? extends Index> indexIt;
        private Table curTable;
        private Index curIndex;
        private String name;
        private String type;
        private boolean isDeferrable = false;
        private boolean isInitiallyDeferred = false;

        public TableConstraintsIteration(Session session, Iterator<Table> tableIt) {
            this.session = session;
            this.tableIt = tableIt;
        }

        public boolean next() {
            while(curTable != null || tableIt.hasNext()) {
                if(curTable == null) {
                    curTable = tableIt.next();
                    if (!isAccessible(session, curTable.getName())) {
                        curTable = null;
                        continue;
                    }
                    if(curTable.getParentJoin() != null) {
                        name = curTable.getParentJoin().getConstraintName().getTableName();
                        type = "GROUPING";
                        return true;
                    }
                }
                if(indexIt == null) {
                    indexIt = curTable.getIndexes().iterator();
                }
                while(indexIt.hasNext()) {
                    curIndex = indexIt.next();
                    if(curIndex.isUnique()) {
                        name = curIndex.getConstraintName().getTableName();
                        type = curIndex.isPrimaryKey() ? "PRIMARY KEY" : "UNIQUE";
                        isDeferrable = false;
                        isInitiallyDeferred = false;
                        return true;
                    } else if(curIndex.isConnectedToFK()) {
                        // this is the constructed referencing index, its IndexName is the foreign key constraint name
                        name = curIndex.getIndexName().getName();
                        type = "FOREIGN KEY";
                       
                        ForeignKey fk = curTable.getReferencingForeignKey(curIndex.getIndexName().getName());
                        isDeferrable = fk.isDeferrable();
                        isInitiallyDeferred = fk.isInitiallyDeferred();
                        return true;
                    }
                }
                indexIt = null;
                curIndex = null;
                curTable = null;
            }
            return false;
        }

        public String getName() {
            return name;
        }

        public String getType() {
            return type;
        }

        public Table getTable() {
            return curTable;
        }

        public Index getIndex() {
            return curIndex;
        }

        public boolean isGrouping() {
            return indexIt == null;
        }

        public boolean isDeferrable() {
            return isDeferrable;
        }
       
        public boolean isInitiallyDeferred() {
            return isInitiallyDeferred;
        }
       
        public boolean isForeignKey() {
            return !isGrouping() && curIndex.isConnectedToFK();
        }
    }

    private class IndexIteration {
        private final Session session;
        private final Iterator<Table> tableIt;
        Iterator<TableIndex> tableIndexIt;
        Iterator<GroupIndex> groupIndexIt;
        Iterator<FullTextIndex> textIndexIt;
        Table curTable;

        public IndexIteration(Session session,
                              Iterator<Table> tableIt) {
            this.session = session;
            this.tableIt = tableIt;
        }

        public Index next() {
            getIndexes:
            while(tableIndexIt == null || !tableIndexIt.hasNext()) {
                while(groupIndexIt != null && groupIndexIt.hasNext()) {
                    GroupIndex index = groupIndexIt.next();
                    if(index.leafMostTable() == curTable) {
                        return index;
                    }
                }
                while(textIndexIt != null && textIndexIt.hasNext()) {
                    return textIndexIt.next();
                }
                while(tableIt.hasNext()) {
                    curTable = tableIt.next();
                    if(isAccessible(session, curTable.getName())) {
                        tableIndexIt = curTable.getIndexes().iterator();
                        groupIndexIt = curTable.getGroup().getIndexes().iterator();
                        textIndexIt = curTable.getOwnFullTextIndexes().iterator();
                        continue getIndexes;
                    }
                }
                return null;
            }
            return tableIndexIt.next();
        }

        public Table getTable() {
            return curTable;
        }
    }

    private class RoutinesFactory extends BasicFactoryBase {
        public RoutinesFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            return getAIS(session).getRoutines().size() ;
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<Routine> it;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.it = getAIS(session).getRoutines().values().iterator();
            }

            @Override
            public Row next() {
                while(it.hasNext()) {
                    Routine routine = it.next();
                    if(isAccessible(session, routine.getName())) {
                        String routineType = (routine.getName().inSystemSchema() ? "SYSTEM " : "") +
                                             (routine.isProcedure() ? "PROCEDURE" : "FUNCTION");
                        return new ValuesRow(rowType,
                                            null,
                                             routine.getName().getSchemaName(),
                                             routine.getName().getTableName(),
                                            null,
                                             routine.getName().getSchemaName(),
                                             routine.getName().getTableName(),
                                             routineType,
                                             null, null, null,                  // module catalog/schema/name
                                             null, null, null,                  // udt catalog/schema/name
                                             routine.getLanguage().equals("SQL") ? "SQL" : "EXTERNAL", // body
                                             routine.getDefinition(),                           // definition
                                             routine.getExternalName(),                         //external name
                                             routine.getLanguage(),                             //language
                                             routine.getCallingConvention().name(), //parameter_style
                                             boolResult(false),                     //is deterministic
                                             (routine.getSQLAllowed() == null) ? null : routine.getSQLAllowed().name().replace('_', ' '),
                                             routine.isProcedure() ? null : boolResult(!routine.isCalledOnNullInput()),
                                             null, //sql path
                                             boolResult(true),      // schema level routine
                                             (long)(routine.getDynamicResultSets()),
                                             null, null, // defined cast, implicit invoke
                                             null, //security type
                                             null, //as locator
                                             boolResult(false), //is udt dependent
                                             null, //created timestamp
                                             null, //last updated timestamp
                                             null, //new savepoint level
                                             ++rowCounter /*hidden pk*/);
                    }
                }
                return null;
            }
        }
    }

    private class ParametersFactory extends BasicFactoryBase {
        public ParametersFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            long count = 0;
            for (Routine routine : getAIS(session).getRoutines().values()) {
                if (routine.getReturnValue() != null) {
                    count++;
                }
                count += routine.getParameters().size();
            }
            return count;
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<Routine> routinesIt;
            Iterator<Parameter> paramsIt;
            long ordinal;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.routinesIt = getAIS(session).getRoutines().values().iterator();
            }

            @Override
            public Row next() {
                Parameter param;
                while (true) {
                    if (paramsIt != null) {
                        if (paramsIt.hasNext()) {
                            param = paramsIt.next();
                            ordinal++;
                            break;
                        }
                    }
                    if (!routinesIt.hasNext())
                        return null;
                    Routine routine = routinesIt.next();
                    if (!isAccessible(session, routine.getName()))
                        continue;
                    paramsIt = routine.getParameters().iterator();
                    ordinal = 0;
                    param = routine.getReturnValue();
                    if (param != null) {
                        ordinal++;
                        break;
                    }
                }
                Long length = null;
                Long precision = null;
                Long scale = null;
                Long radix = null;

                if (param.getType().hasAttributes(StringAttribute.class))
                {
                    length = (long)param.getType().attribute(StringAttribute.MAX_LENGTH);
                } else if (param.getType().hasAttributes(DecimalAttribute.class)) {
                    precision = (long)param.getType().attribute(DecimalAttribute.PRECISION);
                    scale = (long)param.getType().attribute(DecimalAttribute.SCALE);
                    radix = 10L;
                }
                return new ValuesRow(rowType,
                                    null, //Routine catalog
                                     param.getRoutine().getName().getSchemaName(),
                                     param.getRoutine().getName().getTableName(),
                                     param.getName(),
                                     ordinal,
                                     param.getTypeName(),
                                     length,
                                     precision,
                                     radix,
                                     scale,
                                     (param.getDirection() == Parameter.Direction.RETURN) ? "OUT" : param.getDirection().name(),
                                     boolResult(param.getDirection() == Parameter.Direction.RETURN),
                                     null, //parameter default
                                     ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class JarsFactory extends BasicFactoryBase {
        public JarsFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            return getAIS(session).getSQLJJars().size() ;
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<SQLJJar> it;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.it = getAIS(session).getSQLJJars().values().iterator();
            }

            @Override
            public Row next() {
                while(it.hasNext()) {
                    SQLJJar jar = it.next();
                    if(isAccessible(session, jar.getName())) {
                        return new ValuesRow(rowType,
                                            null, //jar catalog
                                             jar.getName().getSchemaName(),
                                             jar.getName().getTableName(),
                                             jar.getURL().toExternalForm(),
                                             ++rowCounter /*hidden pk*/);
                    }
                }
                return null;
            }
        }
    }

    private class RoutineJarUsageFactory extends BasicFactoryBase {
        public RoutineJarUsageFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(adapter.getSession(), getRowType(adapter));
        }

        @Override
        public long rowCount(Session session) {
            long count = 0;
            for (Routine routine : getAIS(session).getRoutines().values()) {
                if (routine.getSQLJJar() != null) {
                    count++;
                }
            }
            return count;
        }

        private class Scan extends BaseScan {
            final Session session;
            final Iterator<Routine> it;

            public Scan(Session session, RowType rowType) {
                super(rowType);
                this.session = session;
                this.it = getAIS(session).getRoutines().values().iterator();
            }

            @Override
            public Row next() {
                while (it.hasNext()) {
                    Routine routine = it.next();
                    if (!isAccessible(session, routine.getName()))
                        continue;
                    SQLJJar jar = routine.getSQLJJar();
                    if (jar != null) {
                        return new ValuesRow(rowType,
                                            null,       // routine catalog
                                             routine.getName().getSchemaName(),
                                             routine.getName().getTableName(),
                                            null,       // jar catalog
                                             jar.getName().getSchemaName(),
                                             jar.getName().getTableName(),
                                             ++rowCounter /*hidden pk*/);
                    }
                }
                return null;
            }
        }
    }

    private class ScriptEnginesISFactory extends BasicFactoryBase {
        public ScriptEnginesISFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public long rowCount(Session session) {
            return getScriptEngineFactories().size();
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(getScriptEngineFactories().listIterator(), getRowType(adapter));
        }

        private class Scan extends BaseScan {
            final ListIterator<ScriptEngineFactory> it;

            public Scan(ListIterator<ScriptEngineFactory> it, RowType rowType) {
                super(rowType);
                this.it = it;
            }

            @Override
            public Row next() {
                if (!it.hasNext())
                    return null;
                ScriptEngineFactory factory = it.next();
                return new ValuesRow(
                        rowType,
                        it.nextIndex(), // use nextIndex so that the IDs are 1-based
                        factory.getEngineName(),
                        factory.getEngineVersion(),
                        factory.getLanguageName(),
                        factory.getLanguageVersion(),
                        ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class ScriptEngineNamesISFactory extends BasicFactoryBase {
        public ScriptEngineNamesISFactory(TableName sourceTable) {
            super(sourceTable);
        }

        @Override
        public long rowCount(Session session) {
            int c = 0;
            for (ScriptEngineFactory factory : getScriptEngineFactories())
                c += factory.getNames().size();
            return c;
        }

        @Override
        public GroupScan getGroupScan(MemoryAdapter adapter) {
            return new Scan(getScriptEngineFactories().listIterator(), getRowType(adapter));
        }

        private class Scan extends BaseScan {
            final ListIterator<ScriptEngineFactory> factories;
            Iterator<String> names;

            public Scan(ListIterator<ScriptEngineFactory> factories, RowType rowType) {
                super(rowType);
                this.factories = factories;
                this.names = Iterators.emptyIterator();
            }

            @Override
            public Row next() {
                while (!names.hasNext()) {
                    if (!factories.hasNext())
                        return null;
                    names = factories.next().getNames().iterator();
                }
                String nextName = names.next();
                return new ValuesRow(
                        rowType,
                        nextName,
                        factories.nextIndex(),      // use nextIndex so that the IDs are 1-based
                        ++rowCounter /*hidden pk*/);
            }
        }
    }

    private class TypeAttributesFactory extends BasicFactoryBase {
        public TypeAttributesFactory (TableName sourceTable) {
            super (sourceTable);
        }
        @Override
        public long rowCount(Session session) {
            return getTypesRegistry().getTypeClasses().size();
        }
        @Override
        public GroupScan getGroupScan (MemoryAdapter adapter) {
            return new Scan (getRowType(adapter));
        }
       
        private class Scan extends BaseScan {
            final Iterator<? extends TClass> typesList;
            Iterator<? extends Attribute> attrs = null;
            TClass currType = null;
          
            public Scan (RowType rowType) {
                super (rowType);
                typesList = getTypesRegistry().getTypeClasses().iterator();
            }
           
            private TClass nextType() {
                TClass type = null;
                do {
                    if (!typesList.hasNext())
                        return null;
                    type = typesList.next();
                } while (!TypeValidator.isSupportedForColumn(type));
                return type;
            }
          
            @Override
            public Row next() {
                if (attrs == null || !attrs.hasNext()) {
                    do {
                        if ((currType = nextType()) == null)
                            return null;
                        attrs = currType.attributes().iterator();
                    } while (!attrs.hasNext());
                }
                Attribute attr = attrs.next();
                return new ValuesRow (rowType,
                        currType.name().unqualifiedName(),
                        attr.name(),
                        ++rowCounter);
            }
        }
    }
   
    private class TypeBundlesFactory extends BasicFactoryBase {
        public TypeBundlesFactory (TableName sourceTable) {
            super (sourceTable);
        }
       
        @Override
        public long rowCount(Session session) {
            return getTypesRegistry().getTypeBundleIDs().size();
        }
       
        @Override
        public GroupScan getGroupScan (MemoryAdapter adapter) {
            return new Scan (getRowType(adapter));
        }
       
        private class Scan extends BaseScan {
            final Iterator<? extends TBundleID> bundlesList;
            public Scan (RowType rowType) {
                super(rowType);
                bundlesList = getTypesRegistry().getTypeBundleIDs().iterator();
            }
           
            @Override
            public Row next() {
                if (!bundlesList.hasNext())
                    return null;

                TBundleID bundle = bundlesList.next();
                return new ValuesRow (rowType,
                                      bundle.name(),
                                      bundle.uuid().toString(),
                                      ++rowCounter);
            }
        }
    }

    private class TypeFactory extends BasicFactoryBase {
        public TypeFactory (TableName sourceTable) {
            super(sourceTable);
        }
       
        @Override
        public long rowCount(Session session) {
            return getTypesRegistry().getTypeClasses().size();
        }
       
        @Override
        public GroupScan getGroupScan (MemoryAdapter adapter) {
            return new Scan(getRowType(adapter));
        }
       
        private class Scan extends BaseScan {
            final Iterator<? extends TClass> typesList;
            public Scan (RowType rowType) {
                super(rowType);
                typesList = getTypesRegistry().getTypeClasses().iterator();
            }
           
            @Override
            public Row next() {
                // Skip the unsupported types
                TClass tClass = null;
                do {
                    if (!typesList.hasNext())
                        return null;
                    tClass = typesList.next();
                } while (!TypeValidator.isSupportedForColumn(tClass));
               
                boolean indexable = TypeValidator.isSupportedForIndex(tClass);
                TInstance type = tClass.instance(true);
                PostgresType pgType = PostgresType.fromTInstance(type);
               
                String bundle = tClass.name().bundleId().name();
                String category = tClass.name().categoryName();
                String name = tClass.name().unqualifiedName();
               
                String attribute1 = null;
                String attribute2 = null;
                String attribute3 = null;
                Iterator<? extends Attribute> attrs = tClass.attributes().iterator();
                if (attrs.hasNext()) {
                    attribute1 = attrs.next().name();
                }
                if (attrs.hasNext()) {
                    attribute2 = attrs.next().name();
                }
                if (attrs.hasNext()) {
                    attribute3 = attrs.next().name();
                }
                Long size = tClass.hasFixedSerializationSize() ? (long)tClass.fixedSerializationSize() : null;
               
                Integer jdbcTypeID = tClass.jdbcType();
               
                return new ValuesRow (rowType,
                        name,
                        category,
                        bundle,
                        attribute1,
                        attribute2,
                        attribute3,
                        size,
                        (long)pgType.getOid(),
                        (long)jdbcTypeID,
                        boolResult(indexable),
                        ++rowCounter);
            }
        }
    }
   
    //
    // Package, for testing
    //

    static AkibanInformationSchema createTablesToRegister(TypesTranslator typesTranslator) {
        // bug1127376: Grouping constraint names are auto-generated and very long. Use a big value until that changes.
        final int GROUPING_CONSTRAINT_MAX = PATH_MAX;

        NewAISBuilder builder = AISBBasedBuilder.create(typesTranslator);
        builder.table(SCHEMATA)
                .colString("catalog_name", IDENT_MAX, true)
                .colString("schema_name", IDENT_MAX, false)
                .colString("schema_owner", IDENT_MAX, true)
                .colString("default_character_set_catalog", IDENT_MAX, true)
                .colString("default_character_set_schema", IDENT_MAX, true)
                .colString("default_character_set_name", IDENT_MAX, true)
                .colString("sql_path", PATH_MAX, true)
                .colString("default_collation_catalog", IDENT_MAX, true)
                .colString("default_collation_schema", IDENT_MAX, true)
                .colString("default_collation_name", IDENT_MAX, true);
        //primary key (schema_name)
        builder.table(TABLES)
                .colString("table_catalog", IDENT_MAX, true)
                .colString("table_schema", IDENT_MAX, false)
                .colString("table_name", IDENT_MAX, false)
                .colString("table_type", IDENT_MAX, false)
                .colString("self_referencing_column", IDENT_MAX, true)
                .colText("reference_generation", true)
                .colString("is_insertable_into", YES_NO_MAX, false)
                .colString("is_typed", YES_NO_MAX, false)
                .colString("commit_action", DESCRIPTOR_MAX, true)
                .colString("default_character_set_catalog", IDENT_MAX, true)
                .colString("default_character_set_schema", IDENT_MAX, true)
                .colString("default_character_set_name", IDENT_MAX, true)
                .colString("default_collation_catalog", IDENT_MAX, true)
                .colString("default_collation_schema", IDENT_MAX, true)
                .colString("default_collation_name", IDENT_MAX, true)
                .colBigInt("table_id", true)
                .colBigInt("group_ordinal", true)
                .colString("storage_name", PATH_MAX, true)
                .colString("storage_format", IDENT_MAX, true);
        //primary key (table_schema, table_name)
        //foreign_key (table_schema) references SCHEMATA (schema_name)
        //foreign key (character_set_schema, character_set_name) references CHARACTER_SETS
        //foreign key (collations_schema, collation_name) references COLLATIONS
        //foreign key (storage_name) references STORAGE_TREES
        builder.table(COLUMNS)
                .colString("table_catalog", IDENT_MAX, true)
                .colString("table_schema", IDENT_MAX, false)
                .colString("table_name", IDENT_MAX, false)
                .colString("column_name", IDENT_MAX, false)
                .colBigInt("ordinal_position", false)
                .colString("column_default", PATH_MAX, true)
                .colString("is_nullable", YES_NO_MAX, false)
                .colString("data_type", DESCRIPTOR_MAX, false)
                .colBigInt("character_maximum_length", true)
                .colBigInt("character_octet_length", true)
                .colBigInt("numeric_precision", true)
                .colBigInt("numeric_precision_radix", true)
                .colBigInt("numeric_scale", true)
                .colString("character_set_catalog", IDENT_MAX, true)
                .colString("character_set_schema", IDENT_MAX, true)
                .colString("character_set_name", IDENT_MAX, true)
                .colString("collation_catalog", IDENT_MAX, true)
                .colString("collation_schema", IDENT_MAX, true)
                .colString("collation_name", IDENT_MAX, true)
                .colString("domain_catalog", IDENT_MAX, true)
                .colString("domain_schema", IDENT_MAX, true)
                .colString("domain_name", IDENT_MAX, true)
                .colString("udt_catalog",IDENT_MAX, true)
                .colString("udt_schema", IDENT_MAX, true)
                .colString("udt_name", IDENT_MAX, true)
                .colString("scope_catalog", IDENT_MAX, true)
                .colString("scope_schema", IDENT_MAX, true)
                .colString("scope_name", IDENT_MAX, true)
                .colBigInt("maximum_cardinality", true)
                .colString("is_self_referencing", YES_NO_MAX, false)
                .colString("is_identity", YES_NO_MAX, false)
                .colString("identity_generation", IDENT_MAX, true)
                .colBigInt("identity_start", true)
                .colBigInt("identity_increment", true)
                .colBigInt("identity_maximum", true)
                .colBigInt("identity_minimum", true)
                .colString("identity_cycle", YES_NO_MAX, true)
                .colString("is_generated", YES_NO_MAX, false)
                .colString("generation_expression", PATH_MAX, true)
                .colString("is_updatable", YES_NO_MAX, true)
                .colString("sequence_catalog", IDENT_MAX, true)
                .colString("sequence_schema", IDENT_MAX, true)
                .colString("sequence_name", IDENT_MAX, true);
        //primary key(table_schema, table_name, column_name)
        //foreign key(table_schema, table_name) references TABLES (table_schema, table_name)
        //foreign key (type) references TYPES (type_name)
        //foreign key (character_set_schema, character_set_name) references CHARACTER_SETS
        //foreign key (collation_schema, collation_name) references COLLATIONS
        builder.table(TABLE_CONSTRAINTS)
                .colString("constraint_catalog", IDENT_MAX, true)
                .colString("constraint_schema", IDENT_MAX, false)
                .colString("constraint_name", GROUPING_CONSTRAINT_MAX, false)
                .colString("table_catalog", IDENT_MAX, true)
                .colString("table_schema", IDENT_MAX, false)
                .colString("table_name", IDENT_MAX, false)
                .colString("constraint_type", DESCRIPTOR_MAX, false)
                .colString("is_deferrable", YES_NO_MAX, false)
                .colString("initially_deferred", YES_NO_MAX, false)
                .colString("enforced", YES_NO_MAX, false);
        //primary key (constraint_schema, constraint_table, constraint_name)
        //foreign key (table_schema, table_name) references TABLES
        builder.table(REFERENTIAL_CONSTRAINTS)
            .colString("constraint_catalog", IDENT_MAX, true)
            .colString("constraint_schema", IDENT_MAX, false)
            .colString("constraint_name", IDENT_MAX, false)
            .colString("unique_constraint_catalog", IDENT_MAX, true)
            .colString("unique_constraint_schema", IDENT_MAX, false)
            .colString("unique_constraint_name", IDENT_MAX, false)
            .colString("match_option", DESCRIPTOR_MAX, false)
            .colString("update_rule", DESCRIPTOR_MAX, false)
            .colString("delete_rule", DESCRIPTOR_MAX, false);
        //foreign key (constraint_schema, constraint_name)
        //    references TABLE_CONSTRAINTS (constraint_schema, constraint_name)
        builder.table(GROUPING_CONSTRAINTS)
                .colString("root_table_catalog", IDENT_MAX, true)
                .colString("root_table_schema", IDENT_MAX, false)
                .colString("root_table_name", IDENT_MAX, false)
                .colString("constraint_catalog",IDENT_MAX, true)
                .colString("constraint_schema", IDENT_MAX, false)
                .colString("constraint_table_name", IDENT_MAX, false)
                .colString("path", IDENT_MAX, false)
                .colBigInt("depth", false)
                .colString("constraint_name", GROUPING_CONSTRAINT_MAX, true)
                .colString("unique_catalog", IDENT_MAX, true)
                .colString("unique_schema", IDENT_MAX, true)
                .colString("unique_constraint_name", GROUPING_CONSTRAINT_MAX, true);
        //foreign key (constraint_schema, constraint_name)
        //    references TABLE_CONSTRAINTS (constraint_schema, constraint_name)
        builder.table(KEY_COLUMN_USAGE)
            .colString("constraint_catalog", IDENT_MAX, true)
            .colString("constraint_schema", IDENT_MAX, false)
            .colString("constraint_name", GROUPING_CONSTRAINT_MAX, false)
            .colString("table_catalog", IDENT_MAX, true)
            .colString("table_schema", IDENT_MAX, false)
            .colString("table_name", IDENT_MAX, false)
            .colString("column_name", IDENT_MAX, false)
            .colBigInt("ordinal_position", false)
            .colBigInt("position_in_unique_constraint", true);
        //primary key  (constraint_schema, constraint_name, column_name),
        //foreign key (constraint_schema, constraint_name) references TABLE_CONSTRAINTS
        builder.table(INDEXES)
                .colString("table_catalog", IDENT_MAX, true)
                .colString("table_schema", IDENT_MAX, false)
                .colString("table_name", IDENT_MAX, false)
                .colString("index_name", IDENT_MAX, false)
                .colString("constraint_catalog", IDENT_MAX, true)
                .colString("constraint_schema", IDENT_MAX, true)
                .colString("constraint_name", IDENT_MAX, true)
                .colBigInt("index_id", false)
                .colString("storage_name", PATH_MAX, true)
                .colString("storage_format", IDENT_MAX, true)
                .colString("index_type", DESCRIPTOR_MAX, false)
                .colString("is_unique", YES_NO_MAX, false)
                .colString("join_type", DESCRIPTOR_MAX, true)
                .colString("index_method", IDENT_MAX, true);
        //primary key(table_schema, table_name, index_name)
        //foreign key (constraint_schema, constraint_name)
        //    references TABLE_CONSTRAINTS (constraint_schema, constraint_name)
        //foreign key (table_schema, table_name) references TABLES (table_schema, table_name)
        builder.table(INDEX_COLUMNS)
                .colString("index_table_catalog", IDENT_MAX, true)
                .colString("index_table_schema", IDENT_MAX, false)
                .colString("index_table_name", IDENT_MAX, false)
                .colString("index_name", IDENT_MAX, false)
                .colString("column_catalog", IDENT_MAX, true)
                .colString("column_schema", IDENT_MAX, false)
                .colString("column_table", IDENT_MAX, false)
                .colString("column_name", IDENT_MAX, false)
                .colBigInt("ordinal_position", false)
                .colString("is_ascending", IDENT_MAX, false);
        //primary key(index_table_schema, index_table, index_name, column_schema, column_table, column_name)
        //foreign key(index_table_schema, index_table_name, index_name)
        //    references INDEXES (table_schema, table_name, index_name)
        //foreign key (column_schema, column_table, column_name)
        //    references COLUMNS (table_schema, table_name, column_name)
        builder.table(SEQUENCES)
                .colString("sequence_catalog", IDENT_MAX, true)
                .colString("sequence_schema", IDENT_MAX, false)
                .colString("sequence_name", IDENT_MAX, false)
                .colString("data_type", DESCRIPTOR_MAX, false)
                .colBigInt("start_value", false)
                .colBigInt("minimum_value", false)
                .colBigInt("maximum_value", false)
                .colBigInt("increment", false)
                .colString("cycle_option", YES_NO_MAX, false)
                .colString("storage_name", IDENT_MAX, false);
        //primary key (sequence_schema, sequence_name)
        //foreign key (data_type) references type (type_name)
               
        builder.table(VIEWS)
                .colString("table_catalog", IDENT_MAX,true)
                .colString("table_schema", IDENT_MAX, false)
                .colString("table_name", IDENT_MAX, false)
                .colText("view_definition", false)
                .colString("check_option", DESCRIPTOR_MAX, false)
                .colString("is_updatable", YES_NO_MAX, false)
                .colString("is_insertable_into", YES_NO_MAX, false)
                .colString("is_trigger_updatable", YES_NO_MAX, false)
                .colString("is_trigger_deletable", YES_NO_MAX, false)
                .colString("is_trigger_insertable_into", YES_NO_MAX, false);
        //primary key(table_schema, table_name)
        //foreign key(table_schema, table_name) references TABLES (table_schema, table_name)

        builder.table(VIEW_TABLE_USAGE)
                .colString("view_catalog", IDENT_MAX, true)
                .colString("view_schema", IDENT_MAX, false)
                .colString("view_name", IDENT_MAX, false)
                .colString("table_catalog", IDENT_MAX, true)
                .colString("table_schema", IDENT_MAX, false)
                .colString("table_name", IDENT_MAX, false);
        //foreign key(view_schema, view_name) references VIEWS (schema_name, table_name)
        //foreign key(table_schema, table_name) references TABLES (schema_name, table_name)

        builder.table(VIEW_COLUMN_USAGE)
                .colString("view_catalog", IDENT_MAX, true)
                .colString("view_schema", IDENT_MAX, false)
                .colString("view_name", IDENT_MAX, false)
                .colString("table_catalog", IDENT_MAX, true)
                .colString("table_schema", IDENT_MAX, false)
                .colString("table_name", IDENT_MAX, false)
                .colString("column_name", IDENT_MAX, false);
        //foreign key(view_schema, view_name) references VIEWS (schema_name, table_name)
        //foreign key(table_schema, table_name, column_name) references COLUMNS
        builder.table(ROUTINES)
                .colString("specific_catalog", IDENT_MAX,true)
                .colString("specific_schema", IDENT_MAX, false)
                .colString("specific_name", IDENT_MAX, false)
                .colString("routine_catalog", IDENT_MAX, true)
                .colString("routine_schema", IDENT_MAX, false)
                .colString("routine_name", IDENT_MAX, false)
                .colString("routine_type", DESCRIPTOR_MAX, false)
                .colString("module_catalog", IDENT_MAX, true)
                .colString("module_schema", IDENT_MAX, true)
                .colString("module_name", IDENT_MAX, true)
                .colString("udt_catalog", IDENT_MAX, true)
                .colString("udt_schema", IDENT_MAX, true)
                .colString("udt_name", IDENT_MAX, true)
                .colString("routine_body", DESCRIPTOR_MAX, true)
                .colText("routine_definition", true)
                .colString("external_name", PATH_MAX, true)
                .colString("language", IDENT_MAX, false)
                .colString("parameter_style", IDENT_MAX, false)
                .colString("is_deterministic", YES_NO_MAX, false)
                .colString("sql_data_access", IDENT_MAX, true)
                .colString("is_null_call", YES_NO_MAX, true)
                .colString("sql_path", PATH_MAX, true)
                .colString("schema_level_routine", YES_NO_MAX, false)
                .colBigInt("max_dynamic_result_sets", false)
                .colString("is_user_defined_cast", YES_NO_MAX, true)
                .colString("is_implicitly_invokable", YES_NO_MAX, true)
                .colString("security_type", DESCRIPTOR_MAX, true)
                .colString("as_locator", YES_NO_MAX, true)
                .colString("is_udt_dependent", YES_NO_MAX, true)
                .colSystemTimestamp("created", true)
                .colSystemTimestamp("last_updated", true)
                .colString("new_savepoint_level", YES_NO_MAX,true);
        //primary key(routine_schema, routine_name)

        builder.table(PARAMETERS)
                .colString("specific_catalog", IDENT_MAX,true)
                .colString("specific_schema", IDENT_MAX, false)
                .colString("specific_name", IDENT_MAX, false)
                .colString("parameter_name", IDENT_MAX, true)
                .colBigInt("ordinal_position", false)
                .colString("data_type", DESCRIPTOR_MAX, false)
                .colBigInt("character_maximum_length", true)
                .colBigInt("numeric_precision", true)
                .colBigInt("numeric_precision_radix", true)
                .colBigInt("numeric_scale",true)
                .colString("parameter_mode", DESCRIPTOR_MAX, false)
                .colString("is_result", YES_NO_MAX, false)
                .colString("parameter_default", PATH_MAX, true);
        //primary key(specific_schema, specific_name, parameter_name, ordinal_position)
        //foreign key(routine_schema, routine_name) references ROUTINES (routine_schema, routine_name)
        //foreign key (type) references TYPES (type_name)

        builder.table(JARS)
                .colString("jar_catalog", IDENT_MAX, true)
                .colString("jar_schema", IDENT_MAX, false)
                .colString("jar_name", IDENT_MAX, false)
                .colString("java_path", PATH_MAX, true);
        //primary key(jar_schema, jar_name)

        builder.table(ROUTINE_JAR_USAGE)
                .colString("specific_catalog", IDENT_MAX, true)
                .colString("specific_schema", IDENT_MAX, false)
                .colString("specific_name", IDENT_MAX, false)
                .colString("jar_catalog", IDENT_MAX, true)
                .colString("jar_schema", IDENT_MAX, false)
                .colString("jar_name", IDENT_MAX, false);
        //foreign key(specific_schema, specific_name) references ROUTINES (specific_schema, specific_name)
        //foreign key(jar_schema, jar_name) references JARS (jar_schema, jar_name)

        builder.table(SCRIPT_ENGINES)
                .colInt("engine_id", false)
                .colString("engine_name", IDENT_MAX, false)
                .colString("engine_version", IDENT_MAX, false)
                .colString("language_name", IDENT_MAX, false)
                .colString("language_version", IDENT_MAX, false);
        //primary key(engine_id)

        builder.table(SCRIPT_ENGINE_NAMES)
                .colString("name", IDENT_MAX, false)
                .colInt("engine_id", false);
        //foreign key (engine_id) references SCRIPT_ENGINES (engine_id)

        builder.table(TYPES)
            .colString("type_name", IDENT_MAX, false)
            .colString("type_category", IDENT_MAX, false)
            .colString("type_bundle_name", IDENT_MAX)
            .colString("attribute_1", IDENT_MAX)
            .colString("attribute_2", IDENT_MAX)
            .colString("attribute_3", IDENT_MAX)
            .colInt("fixed_length")
            .colInt("postgres_oid")
            .colInt("jdbc_type_id")
            .colString("indexable", YES_NO_MAX);

        builder.table(TYPE_BUNDLES)
            .colString("type_bundle_name", IDENT_MAX, false)
            .colString("bundle_guid", IDENT_MAX, false);
       
        builder.table(TYPE_ATTRIBUTES)
            .colString("type_name", IDENT_MAX, false)
            .colString("attribute_name", IDENT_MAX, false);
       
        return builder.ais(false);
    }

    void attachFactories(AkibanInformationSchema ais) {
        attach(ais, SCHEMATA, SchemataFactory.class);
        attach(ais, TABLES, TablesFactory.class);
        attach(ais, COLUMNS, ColumnsFactory.class);
        attach(ais, TABLE_CONSTRAINTS, TableConstraintsFactory.class);
        attach(ais, REFERENTIAL_CONSTRAINTS, ReferentialConstraintsFactory.class);
        attach(ais, GROUPING_CONSTRAINTS, GroupingConstraintsFactory.class);
        attach(ais, KEY_COLUMN_USAGE, KeyColumnUsageFactory.class);
        attach(ais, INDEXES, IndexesFactory.class);
        attach(ais, INDEX_COLUMNS, IndexColumnsFactory.class);
        attach(ais, SEQUENCES, SequencesFactory.class);
        attach(ais, VIEWS, ViewsFactory.class);
        attach(ais, VIEW_TABLE_USAGE, ViewTableUsageFactory.class);
        attach(ais, VIEW_COLUMN_USAGE, ViewColumnUsageFactory.class);
        attach(ais, ROUTINES, RoutinesFactory.class);
        attach(ais, PARAMETERS, ParametersFactory.class);
        attach(ais, JARS, JarsFactory.class);
        attach(ais, ROUTINE_JAR_USAGE, RoutineJarUsageFactory.class);
        attach(ais, SCRIPT_ENGINES, ScriptEnginesISFactory.class);
        attach(ais, SCRIPT_ENGINE_NAMES, ScriptEngineNamesISFactory.class);
        attach(ais, TYPES, TypeFactory.class);
        attach(ais, TYPE_BUNDLES, TypeBundlesFactory.class);
        attach(ais, TYPE_ATTRIBUTES, TypeAttributesFactory.class);
    }
}
TOP

Related Classes of com.foundationdb.server.service.is.BasicInfoSchemaTablesServiceImpl$RootPathTable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.