001/*
002 * ModeShape (http://www.modeshape.org)
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *       http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.modeshape.jdbc;
017
018import java.sql.Connection;
019import java.sql.DatabaseMetaData;
020import java.sql.ResultSet;
021import java.sql.RowIdLifetime;
022import java.sql.SQLException;
023import java.sql.SQLFeatureNotSupportedException;
024import java.sql.Types;
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.Collections;
028import java.util.Comparator;
029import java.util.HashSet;
030import java.util.List;
031import java.util.Map;
032import java.util.Set;
033import javax.jcr.PropertyType;
034import javax.jcr.Repository;
035import javax.jcr.RepositoryException;
036import javax.jcr.Value;
037import javax.jcr.nodetype.NodeType;
038import javax.jcr.nodetype.PropertyDefinition;
039import javax.jcr.query.QueryResult;
040import javax.jcr.version.OnParentVersionAction;
041import org.modeshape.jdbc.metadata.JDBCColumnNames;
042import org.modeshape.jdbc.metadata.JDBCColumnPositions;
043import org.modeshape.jdbc.metadata.MetaDataQueryResult;
044import org.modeshape.jdbc.metadata.MetadataProvider;
045import org.modeshape.jdbc.metadata.ResultSetMetaDataImpl;
046import org.modeshape.jdbc.metadata.ResultsMetadataConstants;
047import org.modeshape.jdbc.metadata.ResultsMetadataConstants.NULL_TYPES;
048
049/**
050 * This driver's implementation of JDBC {@link DatabaseMetaData}.
051 */
052public class JcrMetaData implements DatabaseMetaData {
053
054    protected static final List<PropertyDefinition> PSEUDO_COLUMN_DEFNS;
055    protected static final List<String> PSEUDO_COLUMN_NAMES;
056
057    static {
058        List<PropertyDefinition> defns = new ArrayList<PropertyDefinition>();
059        List<String> defnNames = new ArrayList<String>();
060        boolean auto = true;
061        boolean mand = false;
062        boolean prot = true;
063        boolean mult = false;
064        boolean search = true;
065        boolean order = true;
066        defns.add(new PseudoPropertyDefinition(null, "jcr:path", PropertyType.PATH, auto, mand, prot, mult, search, order));
067        defnNames.add("jcr:path");
068        defns.add(new PseudoPropertyDefinition(null, "jcr:name", PropertyType.NAME, auto, mand, prot, mult, search, order));
069        defnNames.add("jcr:name");
070        defns.add(new PseudoPropertyDefinition(null, "jcr:score", PropertyType.DOUBLE, auto, mand, prot, mult, search, order));
071        defnNames.add("jcr:score");
072        defns.add(new PseudoPropertyDefinition(null, "mode:localName", PropertyType.STRING, auto, mand, prot, mult, search, order));
073        defnNames.add("mode:localName");
074        defns.add(new PseudoPropertyDefinition(null, "mode:depth", PropertyType.LONG, auto, mand, prot, mult, search, order));
075        defnNames.add("mode:depth");
076        defns.add(new PseudoPropertyDefinition(null, "mode:id", PropertyType.STRING, auto, mand, prot, mult, search, order));
077        defnNames.add("mode:id");
078
079        PSEUDO_COLUMN_DEFNS = Collections.unmodifiableList(defns);
080        PSEUDO_COLUMN_NAMES = Collections.unmodifiableList(defnNames);
081
082    }
083
084    /** CONSTANTS */
085    protected static final String WILDCARD = "%"; //$NON-NLS-1$
086    protected static final Integer DEFAULT_ZERO = 0;
087    protected static final int NO_LIMIT = 0;
088
089    private JcrConnection connection;
090    private String catalogName;
091
092    public JcrMetaData( JcrConnection connection ) {
093        this.connection = connection;
094        assert this.connection != null;
095        catalogName = connection.getCatalog();
096        assert catalogName != null;
097    }
098
099    /**
100     * This method always returns an emtpy result set. *
101     * <p>
102     * <em>Note:</em> This method is part of the JDBC API in JDK 1.7.
103     * </p>
104     * 
105     * @param catalog
106     * @param schemaPattern
107     * @param tableNamePattern
108     * @param columnNamePattern
109     * @return the pseudo columns
110     * @throws SQLException
111     */
112    @Override
113    public ResultSet getPseudoColumns( String catalog,
114                                       String schemaPattern,
115                                       String tableNamePattern,
116                                       String columnNamePattern ) throws SQLException {
117        return new JcrResultSet();
118    }
119
120    /**
121     * This method always returns true. *
122     * <p>
123     * <em>Note:</em> This method is part of the JDBC API in JDK 1.7.
124     * </p>
125     * 
126     * @return true
127     * @throws SQLException
128     */
129    @Override
130    public boolean generatedKeyAlwaysReturned() throws SQLException {
131        return true;
132    }
133
134    @Override
135    public int getDriverMajorVersion() {
136        return connection.driverInfo().getMajorVersion();
137    }
138
139    @Override
140    public int getDriverMinorVersion() {
141        return connection.driverInfo().getMinorVersion();
142    }
143
144    @Override
145    public String getDriverName() {
146        return connection.driverInfo().getName();
147    }
148
149    @Override
150    public String getDriverVersion() {
151        return connection.driverInfo().getVersion();
152    }
153
154    @Override
155    public int getDatabaseMajorVersion() {
156        String[] parts = getDatabaseProductVersion().split("[.-]");
157        return parts.length > 0 && parts[0] != null ? Integer.parseInt(parts[0]) : 0;
158    }
159
160    @Override
161    public int getDatabaseMinorVersion() {
162        String[] parts = getDatabaseProductVersion().split("[.-]");
163        return parts.length > 1 && parts[1] != null ? Integer.parseInt(parts[1]) : 0;
164    }
165
166    @Override
167    public String getDatabaseProductName() {
168        return this.connection.getRepositoryDelegate().getDescriptor(Repository.REP_NAME_DESC);
169    }
170
171    public String getDatabaseProductUrl() {
172        return this.connection.getRepositoryDelegate().getDescriptor(Repository.REP_VENDOR_URL_DESC);
173    }
174
175    @Override
176    public String getDatabaseProductVersion() {
177        return this.connection.getRepositoryDelegate().getDescriptor(Repository.REP_VERSION_DESC);
178    }
179
180    @Override
181    public int getJDBCMajorVersion() {
182        return 2;
183    }
184
185    @Override
186    public int getJDBCMinorVersion() {
187        return 0;
188    }
189
190    @Override
191    public Connection getConnection() {
192        return connection;
193    }
194
195    @Override
196    public boolean isReadOnly() {
197        return true;
198    }
199
200    @Override
201    public boolean allProceduresAreCallable() {
202        return false;
203    }
204
205    @Override
206    public boolean allTablesAreSelectable() {
207        return false;
208    }
209
210    @Override
211    public boolean autoCommitFailureClosesAllResultSets() {
212        return false;
213    }
214
215    @Override
216    public boolean dataDefinitionCausesTransactionCommit() {
217        return false;
218    }
219
220    @Override
221    public boolean dataDefinitionIgnoredInTransactions() {
222        return false;
223    }
224
225    @Override
226    public boolean deletesAreDetected( int type ) {
227        return false;
228    }
229
230    @Override
231    public boolean doesMaxRowSizeIncludeBlobs() {
232        return false;
233    }
234
235    @Override
236    public ResultSet getAttributes( String catalog,
237                                    String schemaPattern,
238                                    String typeNamePattern,
239                                    String attributeNamePattern ) throws SQLException {
240        throw new SQLFeatureNotSupportedException();
241    }
242
243    @Override
244    public ResultSet getBestRowIdentifier( String catalog,
245                                           String schema,
246                                           String table,
247                                           int scope,
248                                           boolean nullable ) throws SQLException {
249        throw new SQLFeatureNotSupportedException();
250    }
251
252    @Override
253    public String getCatalogSeparator() {
254        return null;
255    }
256
257    /**
258     * {@inheritDoc}
259     * <p>
260     * This driver maps the repository name as the JDBC catalog name. Therefore, this method returns 'Repository' for the catalog
261     * term.
262     * </p>
263     * 
264     * @see java.sql.DatabaseMetaData#getCatalogTerm()
265     */
266    @Override
267    public String getCatalogTerm() {
268        return "Repository";
269    }
270
271    /**
272     * {@inheritDoc}
273     * <p>
274     * This driver maps the repository name as the JDBC catalog name. Therefore, this method returns a result set containing only
275     * the repository's name.
276     * </p>
277     * 
278     * @see java.sql.DatabaseMetaData#getCatalogs()
279     */
280    @SuppressWarnings( "unchecked" )
281    @Override
282    public ResultSet getCatalogs() throws SQLException {
283        List<List<?>> records = new ArrayList<List<?>>(1);
284
285        List<String> row = Arrays.asList(catalogName);
286        records.add(row);
287
288        /***********************************************************************
289         * Hardcoding JDBC column names for the columns returned in results object
290         ***********************************************************************/
291
292        Map<?, Object>[] metadataList = new Map[1];
293
294        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.CATALOGS.TABLE_CAT,
295                                                             JcrType.DefaultDataTypes.STRING,
296                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
297
298        MetadataProvider provider = new MetadataProvider(metadataList);
299
300        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
301
302        JcrStatement stmt = new JcrStatement(this.connection);
303        QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
304
305        return new JcrResultSet(stmt, queryresult, resultSetMetaData);
306    }
307
308    @Override
309    public ResultSet getClientInfoProperties() throws SQLException {
310        throw new SQLFeatureNotSupportedException();
311    }
312
313    @Override
314    public ResultSet getColumnPrivileges( String catalog,
315                                          String schema,
316                                          String table,
317                                          String columnNamePattern ) throws SQLException {
318        throw new SQLFeatureNotSupportedException();
319    }
320
321    @SuppressWarnings( "unchecked" )
322    @Override
323    public ResultSet getColumns( String catalog,
324                                 String schemaPattern,
325                                 String tableNamePattern,
326                                 String columnNamePattern ) throws SQLException {
327        LocalJcrDriver.logger.debug("getcolumns: " + catalog + ":" + schemaPattern + ":" + tableNamePattern + ":"
328                                    + columnNamePattern);
329
330        // Get all tables if tableNamePattern is null
331        if (tableNamePattern == null) tableNamePattern = WILDCARD;
332
333        Map<?, Object>[] metadataList = new Map[JDBCColumnPositions.COLUMNS.MAX_COLUMNS];
334
335        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.TABLE_CAT,
336                                                             JcrType.DefaultDataTypes.STRING,
337                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
338        metadataList[1] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.TABLE_SCHEM,
339                                                             JcrType.DefaultDataTypes.STRING,
340                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
341        metadataList[2] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.TABLE_NAME,
342                                                             JcrType.DefaultDataTypes.STRING,
343                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
344        metadataList[3] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.COLUMN_NAME,
345                                                             JcrType.DefaultDataTypes.STRING,
346                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
347        metadataList[4] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.DATA_TYPE,
348                                                             JcrType.DefaultDataTypes.LONG,
349                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
350        metadataList[5] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.TYPE_NAME,
351                                                             JcrType.DefaultDataTypes.STRING,
352                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
353        metadataList[6] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.COLUMN_SIZE,
354                                                             JcrType.DefaultDataTypes.LONG,
355                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
356        metadataList[7] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.BUFFER_LENGTH,
357                                                             JcrType.DefaultDataTypes.LONG,
358                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
359        metadataList[8] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.DECIMAL_DIGITS,
360                                                             JcrType.DefaultDataTypes.LONG,
361                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
362        metadataList[9] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.NUM_PREC_RADIX,
363                                                             JcrType.DefaultDataTypes.LONG,
364                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
365
366        metadataList[10] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.NULLABLE,
367                                                              JcrType.DefaultDataTypes.LONG,
368                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
369        metadataList[11] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.REMARKS,
370                                                              JcrType.DefaultDataTypes.STRING,
371                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
372        metadataList[12] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.COLUMN_DEF,
373                                                              JcrType.DefaultDataTypes.STRING,
374                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
375        metadataList[13] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.SQL_DATA_TYPE,
376                                                              JcrType.DefaultDataTypes.LONG,
377                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
378        metadataList[14] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.SQL_DATETIME_SUB,
379                                                              JcrType.DefaultDataTypes.LONG,
380                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
381        metadataList[15] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.CHAR_OCTET_LENGTH,
382                                                              JcrType.DefaultDataTypes.LONG,
383                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
384        metadataList[16] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.ORDINAL_POSITION,
385                                                              JcrType.DefaultDataTypes.LONG,
386                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
387        metadataList[17] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.IS_NULLABLE,
388                                                              JcrType.DefaultDataTypes.STRING,
389                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
390        metadataList[18] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.SCOPE_CATLOG,
391                                                              JcrType.DefaultDataTypes.STRING,
392                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
393        metadataList[19] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.SCOPE_SCHEMA,
394                                                              JcrType.DefaultDataTypes.STRING,
395                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
396
397        metadataList[20] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.SCOPE_TABLE,
398                                                              JcrType.DefaultDataTypes.STRING,
399                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
400        metadataList[21] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.SOURCE_DATA_TYPE,
401                                                              JcrType.DefaultDataTypes.LONG,
402                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
403
404        MetadataProvider provider = new MetadataProvider(metadataList);
405
406        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
407
408        List<List<?>> records = new ArrayList<List<?>>();
409
410        try {
411
412            List<NodeType> nodetypes = filterNodeTypes(tableNamePattern);
413
414            // process each node
415            for (NodeType type : nodetypes) {
416
417                if (type.getPropertyDefinitions() == null) {
418                    throw new SQLException("Program Error:  missing propertydefintions for " + type.getName());
419                }
420
421                List<PropertyDefinition> defns = filterPropertyDefnitions(columnNamePattern, type);
422
423                int ordinal = 0;
424                // build the list of records.
425                for (PropertyDefinition propDefn : defns) {
426
427                    JcrType jcrtype = JcrType.typeInfo(propDefn.getRequiredType());
428
429                    Integer nullable = propDefn.isMandatory() ? ResultsMetadataConstants.NULL_TYPES.NOT_NULL : ResultsMetadataConstants.NULL_TYPES.NULLABLE;
430
431                    List<Object> currentRow = loadCurrentRow(type.getName(), propDefn.getName(), jcrtype, nullable,
432                                                             propDefn.isMandatory(), ordinal);
433
434                    // add the current row to the list of records.
435                    records.add(currentRow);
436
437                    ++ordinal;
438                }
439                // if columns where added and if Teiid Support is requested, then add the mode:properties to the list of columns
440                if (ordinal > 0 && this.connection.getRepositoryDelegate().getConnectionInfo().isTeiidSupport()) {
441                    if (this.connection.getRepositoryDelegate().getConnectionInfo().isTeiidSupport()) {
442                        List<Object> currentRow = loadCurrentRow(type.getName(), "mode:properties",
443                                                                 JcrType.typeInfo(PropertyType.STRING),
444                                                                 ResultsMetadataConstants.NULL_TYPES.NULLABLE, false, ordinal);
445
446                        records.add(currentRow);
447                    }
448                }
449
450            }
451
452            JcrStatement jcrstmt = new JcrStatement(this.connection);
453            QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
454            return new JcrResultSet(jcrstmt, queryresult, resultSetMetaData);
455
456        } catch (RepositoryException e) {
457            throw new SQLException(e.getLocalizedMessage());
458        }
459    }
460
461    private List<Object> loadCurrentRow( String tableName,
462                                         String columnName,
463                                         JcrType jcrtype,
464                                         Integer nullable,
465                                         boolean isMandatory,
466                                         int ordinal ) {
467        // list represents a record on the Results object.
468        List<Object> currentRow = new ArrayList<Object>(JDBCColumnPositions.COLUMNS.MAX_COLUMNS);
469
470        currentRow.add(catalogName); // TABLE_CAT
471        currentRow.add("NULL"); // TABLE_SCHEM
472        currentRow.add(tableName); // TABLE_NAME
473        currentRow.add(columnName); // COLUMN_NAME
474        currentRow.add(jcrtype.getJdbcType()); // DATA_TYPE
475        currentRow.add(jcrtype.getJdbcTypeName()); // TYPE_NAME
476        currentRow.add(jcrtype.getNominalDisplaySize()); // COLUMN_SIZE
477        currentRow.add("NULL"); // BUFFER_LENGTH
478        currentRow.add(JcrMetaData.DEFAULT_ZERO); // DECIMAL_DIGITS
479        currentRow.add(JcrMetaData.DEFAULT_ZERO); // NUM_PREC_RADIX
480
481        currentRow.add(nullable); // NULLABLE
482        currentRow.add(""); // REMARKS
483        currentRow.add("NULL"); // COLUMN_DEF
484        currentRow.add(JcrMetaData.DEFAULT_ZERO); // COLUMN_DEF
485        currentRow.add(JcrMetaData.DEFAULT_ZERO); // SQL_DATETIME_SUB
486
487        currentRow.add(JcrMetaData.DEFAULT_ZERO); // CHAR_OCTET_LENGTH
488        currentRow.add(ordinal + 1); // ORDINAL_POSITION
489        currentRow.add(isMandatory ? "NO" : "YES"); // IS_NULLABLE
490        currentRow.add("NULL"); // SCOPE_CATLOG
491        currentRow.add("NULL"); // SCOPE_SCHEMA
492
493        currentRow.add("NULL"); // SCOPE_TABLE
494        currentRow.add(JcrMetaData.DEFAULT_ZERO); // SOURCE_DATA_TYPE
495
496        return currentRow;
497    }
498
499    @Override
500    public ResultSet getCrossReference( String parentCatalog,
501                                        String parentSchema,
502                                        String parentTable,
503                                        String foreignCatalog,
504                                        String foreignSchema,
505                                        String foreignTable ) throws SQLException {
506        throw new SQLFeatureNotSupportedException();
507    }
508
509    @Override
510    public int getDefaultTransactionIsolation() {
511        return Connection.TRANSACTION_NONE;
512    }
513
514    /**
515     * {@inheritDoc}
516     * <p>
517     * This driver maps REFERENCE properties as keys, and therefore it represents as imported keys those REFERENCE properties on
518     * the type identified by the table name.
519     * </p>
520     * 
521     * @see java.sql.DatabaseMetaData#getExportedKeys(java.lang.String, java.lang.String, java.lang.String)
522     */
523    @Override
524    public ResultSet getExportedKeys( String catalog,
525                                      String schema,
526                                      String table ) throws SQLException {
527        return getImportedKeys(catalog, schema, table); // empty, but same resultsetmetadata
528    }
529
530    @Override
531    public String getExtraNameCharacters() {
532        return null;
533    }
534
535    @Override
536    public ResultSet getFunctionColumns( String catalog,
537                                         String schemaPattern,
538                                         String functionNamePattern,
539                                         String columnNamePattern ) throws SQLException {
540        throw new SQLFeatureNotSupportedException();
541    }
542
543    @Override
544    public ResultSet getFunctions( String catalog,
545                                   String schemaPattern,
546                                   String functionNamePattern ) throws SQLException {
547        throw new SQLFeatureNotSupportedException();
548    }
549
550    /**
551     * {@inheritDoc}
552     * <p>
553     * JCR-SQL2 allows identifiers to be surrounded by matching single quotes, double quotes, or opening and closing square
554     * brackets. Therefore, this method returns a single-quote character as the quote string.
555     * </p>
556     * 
557     * @see java.sql.DatabaseMetaData#getIdentifierQuoteString()
558     */
559    @Override
560    public String getIdentifierQuoteString() {
561        return "\"";
562    }
563
564    /**
565     * {@inheritDoc}
566     * <p>
567     * This driver maps REFERENCE properties as keys, and therefore it represents as imported keys those properties on other types
568     * referencing the type identified by the table name.
569     * </p>
570     * 
571     * @see java.sql.DatabaseMetaData#getImportedKeys(java.lang.String, java.lang.String, java.lang.String)
572     */
573    @Override
574    public ResultSet getImportedKeys( String catalog,
575                                      String schema,
576                                      String table ) throws SQLException {
577        @SuppressWarnings( "unchecked" )
578        Map<?, Object>[] metadataList = new Map[JDBCColumnPositions.REFERENCE_KEYS.MAX_COLUMNS];
579        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.REFERENCE_KEYS_INFO.PK_TABLE_CAT,
580                                                             JcrType.DefaultDataTypes.STRING,
581                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
582        metadataList[1] = MetadataProvider.getColumnMetadata(catalogName, null,
583                                                             JDBCColumnNames.REFERENCE_KEYS_INFO.PK_TABLE_SCHEM,
584                                                             JcrType.DefaultDataTypes.STRING,
585                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
586        metadataList[2] = MetadataProvider.getColumnMetadata(catalogName, null,
587                                                             JDBCColumnNames.REFERENCE_KEYS_INFO.PK_TABLE_NAME,
588                                                             JcrType.DefaultDataTypes.STRING,
589                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
590        metadataList[3] = MetadataProvider.getColumnMetadata(catalogName, null,
591                                                             JDBCColumnNames.REFERENCE_KEYS_INFO.PK_COLUMN_NAME,
592                                                             JcrType.DefaultDataTypes.STRING,
593                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
594        metadataList[4] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.REFERENCE_KEYS_INFO.FK_TABLE_CAT,
595                                                             JcrType.DefaultDataTypes.STRING,
596                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
597        metadataList[5] = MetadataProvider.getColumnMetadata(catalogName, null,
598                                                             JDBCColumnNames.REFERENCE_KEYS_INFO.FK_TABLE_SCHEM,
599                                                             JcrType.DefaultDataTypes.STRING,
600                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
601        metadataList[6] = MetadataProvider.getColumnMetadata(catalogName, null,
602                                                             JDBCColumnNames.REFERENCE_KEYS_INFO.FK_TABLE_NAME,
603                                                             JcrType.DefaultDataTypes.STRING,
604                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
605        metadataList[7] = MetadataProvider.getColumnMetadata(catalogName, null,
606                                                             JDBCColumnNames.REFERENCE_KEYS_INFO.FK_COLUMN_NAME,
607                                                             JcrType.DefaultDataTypes.STRING,
608                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
609        metadataList[8] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.REFERENCE_KEYS_INFO.KEY_SEQ,
610                                                             JcrType.DefaultDataTypes.LONG,
611                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
612        metadataList[9] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.REFERENCE_KEYS_INFO.UPDATE_RULE,
613                                                             JcrType.DefaultDataTypes.LONG,
614                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
615        metadataList[10] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.REFERENCE_KEYS_INFO.DELETE_RULE,
616                                                              JcrType.DefaultDataTypes.LONG,
617                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
618        metadataList[11] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.REFERENCE_KEYS_INFO.FK_NAME,
619                                                              JcrType.DefaultDataTypes.STRING,
620                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
621        metadataList[12] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.REFERENCE_KEYS_INFO.PK_NAME,
622                                                              JcrType.DefaultDataTypes.STRING,
623                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
624        metadataList[13] = MetadataProvider.getColumnMetadata(catalogName, null,
625                                                              JDBCColumnNames.REFERENCE_KEYS_INFO.DEFERRABILITY,
626                                                              JcrType.DefaultDataTypes.LONG,
627                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
628        JcrStatement jcrstmt = new JcrStatement(this.connection);
629        MetadataProvider provider = new MetadataProvider(metadataList);
630        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
631        List<List<?>> records = Collections.emptyList();
632        QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
633        return new JcrResultSet(jcrstmt, queryresult, resultSetMetaData);
634    }
635
636    @SuppressWarnings( "unchecked" )
637    @Override
638    public ResultSet getIndexInfo( String catalog,
639                                   String schema,
640                                   String tableNamePattern,
641                                   boolean unique,
642                                   boolean approximate ) throws SQLException {
643
644        // Get index information for all tables if tableNamePattern is null
645        if (tableNamePattern == null) tableNamePattern = WILDCARD;
646
647        Map<?, Object>[] metadataList = new Map[JDBCColumnPositions.INDEX_INFO.MAX_COLUMNS];
648        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.TABLE_CAT,
649                                                             JcrType.DefaultDataTypes.STRING,
650                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
651        metadataList[1] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.TABLE_SCHEM,
652                                                             JcrType.DefaultDataTypes.STRING,
653                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
654        metadataList[2] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.TABLE_NAME,
655                                                             JcrType.DefaultDataTypes.STRING,
656                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
657        metadataList[3] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.NON_UNIQUE,
658                                                             JcrType.DefaultDataTypes.BOOLEAN,
659                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
660        metadataList[4] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.INDEX_QUALIFIER,
661                                                             JcrType.DefaultDataTypes.STRING,
662                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
663        metadataList[5] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.INDEX_NAME,
664                                                             JcrType.DefaultDataTypes.STRING,
665                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
666        metadataList[6] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.TYPE,
667                                                             JcrType.DefaultDataTypes.LONG,
668                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
669        metadataList[7] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.ORDINAL_POSITION,
670                                                             JcrType.DefaultDataTypes.LONG,
671                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
672        metadataList[8] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.COLUMN_NAME,
673                                                             JcrType.DefaultDataTypes.LONG,
674                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
675        metadataList[9] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.ASC_OR_DESC,
676                                                             JcrType.DefaultDataTypes.STRING,
677                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
678
679        metadataList[10] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.CARDINALITY,
680                                                              JcrType.DefaultDataTypes.LONG,
681                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
682        metadataList[11] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.PAGES,
683                                                              JcrType.DefaultDataTypes.LONG,
684                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
685        metadataList[12] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.INDEX_INFO.FILTER_CONDITION,
686                                                              JcrType.DefaultDataTypes.STRING,
687                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
688
689        try {
690            Boolean nonUnique = Boolean.FALSE;
691            List<List<?>> records = new ArrayList<List<?>>();
692            for (NodeType type : filterNodeTypes(tableNamePattern)) {
693                // Create a unique key for each "jcr:uuid" property, so do this only for those node types
694                // that somehow extend "mix:referenceable" ...
695                if (!type.isNodeType("mix:referenceable")) continue;
696
697                // Every table has a "jcr:path" pseudo-column that is the primary key ...
698                List<Object> currentRow = new ArrayList<Object>(JDBCColumnPositions.INDEX_INFO.MAX_COLUMNS);
699                currentRow.add(catalogName); // TABLE_CAT
700                currentRow.add("NULL"); // TABLE_SCHEM
701                currentRow.add(type.getName()); // TABLE_NAME
702                currentRow.add(nonUnique); // NON_UNIQUE
703                currentRow.add(catalogName); // INDEX_QUALIFIER
704                currentRow.add(type.getName() + "_UK"); // INDEX_NAME
705                currentRow.add(DatabaseMetaData.tableIndexHashed); // TYPE
706                currentRow.add((short)1); // ORDINAL_POSITION
707                currentRow.add(type.getName()); // COLUMN_NAME
708                currentRow.add("A"); // ASC_OR_DESC
709                currentRow.add(0); // CARDINALITY
710                currentRow.add(1); // PAGES
711                currentRow.add(null); // FILTER_CONDITION
712
713                // add the current row to the list of records.
714                records.add(currentRow);
715            }
716
717            JcrStatement jcrstmt = new JcrStatement(this.connection);
718            MetadataProvider provider = new MetadataProvider(metadataList);
719            ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
720            QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
721            return new JcrResultSet(jcrstmt, queryresult, resultSetMetaData);
722        } catch (RepositoryException e) {
723            throw new SQLException(e.getLocalizedMessage());
724        }
725    }
726
727    /**
728     * {@inheritDoc}
729     * <p>
730     * There is no maximum length of binary literals (or if there is a limit it is not known), so this method returns 0.
731     * </p>
732     * 
733     * @see java.sql.DatabaseMetaData#getMaxBinaryLiteralLength()
734     */
735    @Override
736    public int getMaxBinaryLiteralLength() {
737        return JcrMetaData.NO_LIMIT; // no limit
738    }
739
740    /**
741     * {@inheritDoc}
742     * <p>
743     * There is no maximum length of the catalog (repository) names - or the limit is not known.
744     * </p>
745     * 
746     * @see java.sql.DatabaseMetaData#getMaxCatalogNameLength()
747     */
748    @Override
749    public int getMaxCatalogNameLength() {
750        return JcrMetaData.NO_LIMIT; // none
751    }
752
753    /**
754     * {@inheritDoc}
755     * <p>
756     * There is no maximum length of character literals (or if there is a limit it is not known), so this method returns 0.
757     * </p>
758     * 
759     * @see java.sql.DatabaseMetaData#getMaxCharLiteralLength()
760     */
761    @Override
762    public int getMaxCharLiteralLength() {
763        return JcrMetaData.NO_LIMIT;
764    }
765
766    /**
767     * {@inheritDoc}
768     * <p>
769     * There is no maximum length of column names (or if there is a limit it is not known), so this method returns 0.
770     * </p>
771     * 
772     * @see java.sql.DatabaseMetaData#getMaxColumnNameLength()
773     */
774    @Override
775    public int getMaxColumnNameLength() {
776        return JcrMetaData.NO_LIMIT; // no limit
777    }
778
779    /**
780     * {@inheritDoc}
781     * <p>
782     * JCR-SQL2 does not support GROUP BY, so this method returns 0.
783     * </p>
784     * 
785     * @see java.sql.DatabaseMetaData#getMaxColumnsInGroupBy()
786     */
787    @Override
788    public int getMaxColumnsInGroupBy() {
789        return 0;
790    }
791
792    /**
793     * {@inheritDoc}
794     * <p>
795     * There is no limit to the number of columns in an index (or if there is a limit it is not known), so this method returns 0.
796     * </p>
797     * 
798     * @see java.sql.DatabaseMetaData#getMaxColumnsInIndex()
799     */
800    @Override
801    public int getMaxColumnsInIndex() {
802        return JcrMetaData.NO_LIMIT; // no limit
803    }
804
805    /**
806     * {@inheritDoc}
807     * <p>
808     * There is no limit to the number of columns in an ORDER BY statement (or if there is a limit it is not known), so this
809     * method returns 0.
810     * </p>
811     * 
812     * @see java.sql.DatabaseMetaData#getMaxColumnsInOrderBy()
813     */
814    @Override
815    public int getMaxColumnsInOrderBy() {
816        return JcrMetaData.NO_LIMIT; // not known (technically there is no limit, but there would be a practical limit)
817    }
818
819    /**
820     * {@inheritDoc}
821     * <p>
822     * There is no limit to the number of columns in a select statement (or if there is a limit it is not known), so this method
823     * returns 0.
824     * </p>
825     * 
826     * @see java.sql.DatabaseMetaData#getMaxColumnsInSelect()
827     */
828    @Override
829    public int getMaxColumnsInSelect() {
830        return JcrMetaData.NO_LIMIT; // no limit
831    }
832
833    /**
834     * {@inheritDoc}
835     * <p>
836     * There is no limit to the number of columns in a table (or if there is a limit it is not known), so this method returns 0.
837     * </p>
838     * 
839     * @see java.sql.DatabaseMetaData#getMaxColumnsInTable()
840     */
841    @Override
842    public int getMaxColumnsInTable() {
843        return JcrMetaData.NO_LIMIT; // no limit
844    }
845
846    /**
847     * {@inheritDoc}
848     * <p>
849     * There is no limit to the number of connections (or if there is a limit it is not known), so this method returns 0.
850     * </p>
851     * 
852     * @see java.sql.DatabaseMetaData#getMaxConnections()
853     */
854    @Override
855    public int getMaxConnections() {
856        return JcrMetaData.NO_LIMIT; // no limit
857    }
858
859    /**
860     * {@inheritDoc}
861     * <p>
862     * There are no cursors (or there is no limit), so this method returns 0.
863     * </p>
864     * 
865     * @see java.sql.DatabaseMetaData#getMaxCursorNameLength()
866     */
867    @Override
868    public int getMaxCursorNameLength() {
869        return 0;
870    }
871
872    /**
873     * {@inheritDoc}
874     * <p>
875     * There are no indexes (or there is no limit), so this method returns 0.
876     * </p>
877     * 
878     * @see java.sql.DatabaseMetaData#getMaxIndexLength()
879     */
880    @Override
881    public int getMaxIndexLength() {
882        return 0; // no limit
883    }
884
885    /**
886     * {@inheritDoc}
887     * <p>
888     * There are no procedures, so this method returns 0.
889     * </p>
890     * 
891     * @see java.sql.DatabaseMetaData#getMaxProcedureNameLength()
892     */
893    @Override
894    public int getMaxProcedureNameLength() {
895        return 0; // no limit
896    }
897
898    /**
899     * {@inheritDoc}
900     * <p>
901     * There is no maximum row size.
902     * </p>
903     * 
904     * @see java.sql.DatabaseMetaData#getMaxRowSize()
905     */
906    @Override
907    public int getMaxRowSize() {
908        return JcrMetaData.NO_LIMIT; // no limit
909    }
910
911    /**
912     * {@inheritDoc}
913     * <p>
914     * There is no maximum length of the schema (workspace) names - or the limit is not known.
915     * </p>
916     * 
917     * @see java.sql.DatabaseMetaData#getMaxSchemaNameLength()
918     */
919    @Override
920    public int getMaxSchemaNameLength() {
921        return JcrMetaData.NO_LIMIT; // none
922    }
923
924    @Override
925    public int getMaxStatementLength() {
926        return 0; // no limit
927    }
928
929    @Override
930    public int getMaxStatements() {
931        return 0; // no limit
932    }
933
934    @Override
935    public int getMaxTableNameLength() {
936        return 0; // no limit
937    }
938
939    @Override
940    public int getMaxTablesInSelect() {
941        return 0; // not known (technically there is no limit, but there would be a practical limit)
942    }
943
944    @Override
945    public int getMaxUserNameLength() {
946        return 0; // no limit
947    }
948
949    @Override
950    public String getNumericFunctions() {
951        return null;
952    }
953
954    @SuppressWarnings( "unchecked" )
955    @Override
956    public ResultSet getPrimaryKeys( String catalog,
957                                     String schema,
958                                     String tableNamePattern ) throws SQLException {
959        // Get primary keys for all tables if tableNamePattern is null
960        if (tableNamePattern == null) tableNamePattern = WILDCARD;
961
962        Map<?, Object>[] metadataList = new Map[JDBCColumnPositions.PRIMARY_KEYS.MAX_COLUMNS];
963        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PRIMARY_KEYS.TABLE_CAT,
964                                                             JcrType.DefaultDataTypes.STRING,
965                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
966        metadataList[1] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PRIMARY_KEYS.TABLE_SCHEM,
967                                                             JcrType.DefaultDataTypes.STRING,
968                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
969        metadataList[2] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PRIMARY_KEYS.TABLE_NAME,
970                                                             JcrType.DefaultDataTypes.STRING,
971                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
972        metadataList[3] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PRIMARY_KEYS.COLUMN_NAME,
973                                                             JcrType.DefaultDataTypes.STRING,
974                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
975        metadataList[4] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PRIMARY_KEYS.KEY_SEQ,
976                                                             JcrType.DefaultDataTypes.LONG,
977                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
978        metadataList[5] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PRIMARY_KEYS.PK_NAME,
979                                                             JcrType.DefaultDataTypes.STRING,
980                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
981
982        try {
983            List<List<?>> records = new ArrayList<List<?>>();
984            for (NodeType type : filterNodeTypes(tableNamePattern)) {
985                // Every table has a "jcr:path" pseudo-column that is the primary key ...
986                List<Object> currentRow = new ArrayList<Object>(JDBCColumnPositions.PRIMARY_KEYS.MAX_COLUMNS);
987                currentRow.add(catalogName); // TABLE_CAT
988                currentRow.add("NULL"); // TABLE_SCHEM
989                currentRow.add(type.getName()); // TABLE_NAME
990                currentRow.add("jcr:path"); // COLUMN_NAME
991                currentRow.add(1); // KEY_SEQ
992                currentRow.add(type.getName() + "_PK"); // PK_NAME
993
994                // add the current row to the list of records.
995                records.add(currentRow);
996            }
997
998            JcrStatement jcrstmt = new JcrStatement(this.connection);
999            MetadataProvider provider = new MetadataProvider(metadataList);
1000            ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
1001            QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
1002            return new JcrResultSet(jcrstmt, queryresult, resultSetMetaData);
1003        } catch (RepositoryException e) {
1004            throw new SQLException(e.getLocalizedMessage());
1005        }
1006    }
1007
1008    @Override
1009    public ResultSet getProcedureColumns( String catalog,
1010                                          String schemaPattern,
1011                                          String procedureNamePattern,
1012                                          String columnNamePattern ) throws SQLException {
1013        throw new SQLFeatureNotSupportedException();
1014    }
1015
1016    @Override
1017    public String getProcedureTerm() {
1018        return null;
1019    }
1020
1021    @Override
1022    public ResultSet getProcedures( String catalog,
1023                                    String schemaPattern,
1024                                    String procedureNamePattern ) throws SQLException {
1025        @SuppressWarnings( "unchecked" )
1026        Map<?, Object>[] metadataList = new Map[JDBCColumnPositions.PROCEDURES.MAX_COLUMNS];
1027        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.PROCEDURE_CAT,
1028                                                             JcrType.DefaultDataTypes.STRING,
1029                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1030        metadataList[1] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.PROCEDURE_SCHEM,
1031                                                             JcrType.DefaultDataTypes.STRING,
1032                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1033        metadataList[2] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.PROCEDURE_NAME,
1034                                                             JcrType.DefaultDataTypes.STRING,
1035                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1036        metadataList[3] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.RESERVED1,
1037                                                             JcrType.DefaultDataTypes.STRING,
1038                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1039        metadataList[4] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.RESERVED2,
1040                                                             JcrType.DefaultDataTypes.STRING,
1041                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1042        metadataList[5] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.RESERVED3,
1043                                                             JcrType.DefaultDataTypes.STRING,
1044                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1045        metadataList[6] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.REMARKS,
1046                                                             JcrType.DefaultDataTypes.STRING,
1047                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1048        metadataList[7] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.PROCEDURE_TYPE,
1049                                                             JcrType.DefaultDataTypes.LONG,
1050                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1051        metadataList[8] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.PROCEDURES.SPECIFIC_NAME,
1052                                                             JcrType.DefaultDataTypes.STRING,
1053                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1054
1055        JcrStatement jcrstmt = new JcrStatement(this.connection);
1056        MetadataProvider provider = new MetadataProvider(metadataList);
1057        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
1058        List<List<?>> records = Collections.emptyList();
1059        QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
1060        return new JcrResultSet(jcrstmt, queryresult, resultSetMetaData);
1061    }
1062
1063    @Override
1064    public int getResultSetHoldability() {
1065        return 0;
1066    }
1067
1068    @Override
1069    public RowIdLifetime getRowIdLifetime() {
1070        return RowIdLifetime.ROWID_UNSUPPORTED;
1071    }
1072
1073    @Override
1074    public String getSQLKeywords() {
1075        return null;
1076    }
1077
1078    @Override
1079    public int getSQLStateType() {
1080        return 0;
1081    }
1082
1083    @Override
1084    public String getSchemaTerm() {
1085        return " ";
1086    }
1087
1088    @SuppressWarnings( "unchecked" )
1089    @Override
1090    public ResultSet getSchemas() throws SQLException {
1091        List<List<?>> records = new ArrayList<List<?>>(1);
1092
1093        /***********************************************************************
1094         * Hardcoding JDBC column names for the columns returned in results object
1095         ***********************************************************************/
1096
1097        Map<?, Object>[] metadataList = new Map[1];
1098
1099        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.COLUMNS.TABLE_SCHEM,
1100                                                             JcrType.DefaultDataTypes.STRING,
1101                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1102
1103        MetadataProvider provider = new MetadataProvider(metadataList);
1104
1105        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
1106
1107        JcrStatement stmt = new JcrStatement(this.connection);
1108        QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
1109        ResultSet rs = new JcrResultSet(stmt, queryresult, resultSetMetaData);
1110
1111        return rs;
1112    }
1113
1114    @Override
1115    public ResultSet getSchemas( String catalog,
1116                                 String schemaPattern ) throws SQLException {
1117        return getSchemas();
1118    }
1119
1120    @Override
1121    public String getSearchStringEscape() {
1122        return null;
1123    }
1124
1125    @Override
1126    public String getStringFunctions() {
1127        return null;
1128    }
1129
1130    @Override
1131    public ResultSet getSuperTables( String catalog,
1132                                     String schemaPattern,
1133                                     String tableNamePattern ) throws SQLException {
1134        throw new SQLFeatureNotSupportedException();
1135    }
1136
1137    @Override
1138    public ResultSet getSuperTypes( String catalog,
1139                                    String schemaPattern,
1140                                    String typeNamePattern ) throws SQLException {
1141        throw new SQLFeatureNotSupportedException();
1142    }
1143
1144    @Override
1145    public String getSystemFunctions() {
1146        return null;
1147    }
1148
1149    @Override
1150    public ResultSet getTablePrivileges( String catalog,
1151                                         String schemaPattern,
1152                                         String tableNamePattern ) throws SQLException {
1153        throw new SQLFeatureNotSupportedException();
1154    }
1155
1156    @SuppressWarnings( "unchecked" )
1157    @Override
1158    public ResultSet getTableTypes() throws SQLException {
1159
1160        List<List<?>> records = new ArrayList<List<?>>(1);
1161        List<String> row = Arrays.asList(ResultsMetadataConstants.TABLE_TYPES.VIEW);
1162        records.add(row);
1163
1164        /***********************************************************************
1165         * Hardcoding JDBC column names for the columns returned in results object
1166         ***********************************************************************/
1167
1168        Map<?, Object>[] metadataList = new Map[1];
1169
1170        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLE_TYPES.TABLE_TYPE,
1171                                                             JcrType.DefaultDataTypes.STRING,
1172                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1173
1174        MetadataProvider provider = new MetadataProvider(metadataList);
1175
1176        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
1177
1178        JcrStatement stmt = new JcrStatement(this.connection);
1179        QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
1180        return new JcrResultSet(stmt, queryresult, resultSetMetaData);
1181    }
1182
1183    @SuppressWarnings( "unchecked" )
1184    @Override
1185    public ResultSet getTables( String catalog,
1186                                String schemaPattern,
1187                                String tableNamePattern,
1188                                String[] types ) throws SQLException {
1189
1190        LocalJcrDriver.logger.debug("getTables: " + catalog + ":" + schemaPattern + ":" + tableNamePattern + ":" + types);
1191
1192        // Get all tables if tableNamePattern is null
1193        if (tableNamePattern == null) {
1194            tableNamePattern = WILDCARD;
1195        }
1196
1197        Map<?, Object>[] metadataList = new Map[JDBCColumnPositions.TABLES.MAX_COLUMNS];
1198
1199        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.TABLE_CAT,
1200                                                             JcrType.DefaultDataTypes.STRING,
1201                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1202        metadataList[1] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.TABLE_SCHEM,
1203                                                             JcrType.DefaultDataTypes.STRING,
1204                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1205        metadataList[2] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.TABLE_NAME,
1206                                                             JcrType.DefaultDataTypes.STRING,
1207                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1208        metadataList[3] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.TABLE_TYPE,
1209                                                             JcrType.DefaultDataTypes.STRING,
1210                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1211        metadataList[4] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.REMARKS,
1212                                                             JcrType.DefaultDataTypes.STRING,
1213                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1214        metadataList[5] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.TYPE_CAT,
1215                                                             JcrType.DefaultDataTypes.STRING,
1216                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1217        metadataList[6] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.TYPE_SCHEM,
1218                                                             JcrType.DefaultDataTypes.STRING,
1219                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1220        metadataList[7] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.TYPE_NAME,
1221                                                             JcrType.DefaultDataTypes.STRING,
1222                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1223        metadataList[8] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.SELF_REFERENCING_COL_NAME,
1224                                                             JcrType.DefaultDataTypes.STRING,
1225                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1226        metadataList[9] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TABLES.REF_GENERATION,
1227                                                             JcrType.DefaultDataTypes.STRING,
1228                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1229
1230        MetadataProvider provider = new MetadataProvider(metadataList);
1231
1232        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
1233
1234        List<List<?>> records = new ArrayList<List<?>>();
1235
1236        try {
1237            List<NodeType> nodetypes = filterNodeTypes(tableNamePattern);
1238
1239            // build the list of records from the nodetypes.
1240            for (NodeType type : nodetypes) {
1241
1242                if (!type.isQueryable()) {
1243                    continue;
1244                }
1245
1246                // list represents a record on the Results object.
1247                List<Object> currentRow = new ArrayList<Object>(JDBCColumnPositions.TABLES.MAX_COLUMNS);
1248                // add values in the current record on the Results object to the list
1249                // number of values to be fetched from each row is MAX_COLUMNS.
1250
1251                currentRow.add(catalogName); // TABLE_CAT
1252                currentRow.add("NULL"); // TABLE_SCHEM
1253                currentRow.add(type.getName()); // TABLE_NAME
1254                currentRow.add(ResultsMetadataConstants.TABLE_TYPES.VIEW); // TABLE_TYPE
1255                currentRow.add("Is Mixin: " + type.isMixin()); // REMARKS
1256                currentRow.add("NULL"); // TYPE_CAT
1257                currentRow.add("NULL"); // TYPE_SCHEM
1258                currentRow.add("NULL"); // TYPE_NAME
1259                currentRow.add(type.getPrimaryItemName()); // SELF_REF
1260                currentRow.add("DERIVED"); // REF_GEN
1261
1262                // add the current row to the list of records.
1263                records.add(currentRow);
1264            }// end of while
1265
1266            JcrStatement jcrstmt = new JcrStatement(this.connection);
1267            QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
1268
1269            return new JcrResultSet(jcrstmt, queryresult, resultSetMetaData);
1270
1271        } catch (RepositoryException e) {
1272            throw new SQLException(e.getLocalizedMessage());
1273        }
1274    }
1275
1276    @Override
1277    public String getTimeDateFunctions() {
1278        return null;
1279    }
1280
1281    private static List<List<?>> typeInfoRows() {
1282        List<List<?>> rows = new ArrayList<List<?>>();
1283        rows.add(typeInfoRow(JcrType.DefaultDataTypes.STRING, Types.VARCHAR, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1284                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1285        rows.add(typeInfoRow(JcrType.DefaultDataTypes.BINARY, Types.BLOB, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1286                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1287        rows.add(typeInfoRow(JcrType.DefaultDataTypes.BOOLEAN, Types.BOOLEAN, 1, NULL_TYPES.NULLABLE, true,
1288                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1289        rows.add(typeInfoRow(JcrType.DefaultDataTypes.DATE, Types.TIMESTAMP, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1290                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1291        rows.add(typeInfoRow(JcrType.DefaultDataTypes.DECIMAL, Types.DECIMAL, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1292                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1293        rows.add(typeInfoRow(JcrType.DefaultDataTypes.DOUBLE, Types.DOUBLE, 18, NULL_TYPES.NULLABLE, true,
1294                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1295        rows.add(typeInfoRow(JcrType.DefaultDataTypes.LONG, Types.BIGINT, 18, NULL_TYPES.NULLABLE, true,
1296                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1297        rows.add(typeInfoRow(JcrType.DefaultDataTypes.NAME, Types.VARCHAR, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1298                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1299        rows.add(typeInfoRow(JcrType.DefaultDataTypes.PATH, Types.VARCHAR, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1300                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1301        rows.add(typeInfoRow(JcrType.DefaultDataTypes.REFERENCE, Types.VARCHAR, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1302                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1303        rows.add(typeInfoRow(JcrType.DefaultDataTypes.WEAK_REF, Types.VARCHAR, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1304                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1305        rows.add(typeInfoRow(JcrType.DefaultDataTypes.URI, Types.VARCHAR, Integer.MAX_VALUE, NULL_TYPES.NULLABLE, true,
1306                             ResultsMetadataConstants.SEARCH_TYPES.SEARCHABLE, false, false, 0, 0));
1307        return rows;
1308    }
1309
1310    private static List<?> typeInfoRow( String typeName,
1311                                        int sqlType,
1312                                        int precision,
1313                                        Integer nullability,
1314                                        boolean caseSensitive,
1315                                        Integer searchable,
1316                                        boolean isUnsigned,
1317                                        boolean canBeAutoIncremented,
1318                                        int minimumScale,
1319                                        int maximumScale ) {
1320        List<Object> row = new ArrayList<Object>(JDBCColumnPositions.TYPE_INFO.MAX_COLUMNS);
1321        row.add(typeName);
1322        row.add(sqlType);
1323        row.add(precision);
1324        row.add(null); // literal prefix
1325        row.add(null); // literal suffix
1326        row.add(null); // create params
1327        row.add(nullability);
1328        row.add(caseSensitive);
1329        row.add(searchable);
1330        row.add(isUnsigned);
1331        row.add(false); // is money
1332        row.add(canBeAutoIncremented);
1333        row.add(null); // localized type name
1334        row.add(minimumScale);
1335        row.add(maximumScale);
1336        row.add(0); // unused
1337        row.add(0); // unused
1338        row.add(10); // radix
1339        return row;
1340    }
1341
1342    @Override
1343    public ResultSet getTypeInfo() throws SQLException {
1344        @SuppressWarnings( "unchecked" )
1345        Map<?, Object>[] metadataList = new Map[JDBCColumnPositions.TYPE_INFO.MAX_COLUMNS];
1346
1347        metadataList[0] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.TYPE_NAME,
1348                                                             JcrType.DefaultDataTypes.STRING,
1349                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1350        metadataList[1] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.DATA_TYPE,
1351                                                             JcrType.DefaultDataTypes.LONG,
1352                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1353        metadataList[2] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.PRECISION,
1354                                                             JcrType.DefaultDataTypes.LONG,
1355                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1356        metadataList[3] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.LITERAL_PREFIX,
1357                                                             JcrType.DefaultDataTypes.STRING,
1358                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1359        metadataList[4] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.LITERAL_SUFFIX,
1360                                                             JcrType.DefaultDataTypes.STRING,
1361                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1362        metadataList[5] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.CREATE_PARAMS,
1363                                                             JcrType.DefaultDataTypes.STRING,
1364                                                             ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1365        metadataList[6] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.NULLABLE,
1366                                                             JcrType.DefaultDataTypes.LONG,
1367                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1368        metadataList[7] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.CASE_SENSITIVE,
1369                                                             JcrType.DefaultDataTypes.BOOLEAN,
1370                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1371        metadataList[8] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.SEARCHABLE,
1372                                                             JcrType.DefaultDataTypes.LONG,
1373                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1374        metadataList[9] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.UNSIGNED_ATTRIBUTE,
1375                                                             JcrType.DefaultDataTypes.BOOLEAN,
1376                                                             ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1377        metadataList[10] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.FIXED_PREC_SCALE,
1378                                                              JcrType.DefaultDataTypes.BOOLEAN,
1379                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1380        metadataList[11] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.AUTOINCREMENT,
1381                                                              JcrType.DefaultDataTypes.BOOLEAN,
1382                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1383        metadataList[12] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.LOCAL_TYPE_NAME,
1384                                                              JcrType.DefaultDataTypes.STRING,
1385                                                              ResultsMetadataConstants.NULL_TYPES.NULLABLE, this.connection);
1386        metadataList[13] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.MINIMUM_SCALE,
1387                                                              JcrType.DefaultDataTypes.LONG,
1388                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1389        metadataList[14] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.MAXIMUM_SCALE,
1390                                                              JcrType.DefaultDataTypes.LONG,
1391                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1392        metadataList[15] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.SQL_DATA_TYPE,
1393                                                              JcrType.DefaultDataTypes.LONG,
1394                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1395        metadataList[16] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.SQL_DATETIME_SUB,
1396                                                              JcrType.DefaultDataTypes.LONG,
1397                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1398        metadataList[17] = MetadataProvider.getColumnMetadata(catalogName, null, JDBCColumnNames.TYPE_INFO.NUM_PREC_RADIX,
1399                                                              JcrType.DefaultDataTypes.LONG,
1400                                                              ResultsMetadataConstants.NULL_TYPES.NOT_NULL, this.connection);
1401
1402        // Build the result set metadata ...
1403        MetadataProvider provider = new MetadataProvider(metadataList);
1404        ResultSetMetaDataImpl resultSetMetaData = new ResultSetMetaDataImpl(provider);
1405        // The rows ...
1406        List<List<?>> records = typeInfoRows();
1407        // And the result set ...
1408        JcrStatement jcrstmt = new JcrStatement(this.connection);
1409        QueryResult queryresult = MetaDataQueryResult.createResultSet(records, resultSetMetaData);
1410        return new JcrResultSet(jcrstmt, queryresult, resultSetMetaData);
1411    }
1412
1413    @Override
1414    public ResultSet getUDTs( String catalog,
1415                              String schemaPattern,
1416                              String typeNamePattern,
1417                              int[] types ) throws SQLException {
1418        throw new SQLFeatureNotSupportedException();
1419    }
1420
1421    /**
1422     * {@inheritDoc}
1423     * <p>
1424     * This method returns the effective URL of the connection, which includes all connection properties (even if those properties
1425     * were passed in via the Properties argument). Note that each character in the password is replaced with a '*' character.
1426     * </p>
1427     * 
1428     * @see java.sql.DatabaseMetaData#getURL()
1429     */
1430    @Override
1431    public String getURL() {
1432        return connection.info().getEffectiveUrl();
1433    }
1434
1435    @Override
1436    public String getUserName() {
1437        return connection.info().getUsername();
1438    }
1439
1440    @Override
1441    public ResultSet getVersionColumns( String catalog,
1442                                        String schema,
1443                                        String table ) throws SQLException {
1444        throw new SQLFeatureNotSupportedException();
1445    }
1446
1447    @Override
1448    public boolean insertsAreDetected( int type ) {
1449        return false; // read-only
1450    }
1451
1452    @Override
1453    public boolean isCatalogAtStart() {
1454        return true;
1455    }
1456
1457    @Override
1458    public boolean locatorsUpdateCopy() {
1459        return false; // read-only
1460    }
1461
1462    @Override
1463    public boolean nullPlusNonNullIsNull() {
1464        return false;
1465    }
1466
1467    /**
1468     * {@inheritDoc}
1469     * <p>
1470     * Assumed to be false for JCR implementations (meaning that sort order IS used), though section 6.7.37 of JCR 2.0
1471     * specification says ordering of null values is implementation-determined.
1472     * </p>
1473     * 
1474     * @see java.sql.DatabaseMetaData#nullsAreSortedAtEnd()
1475     */
1476    @Override
1477    public boolean nullsAreSortedAtEnd() {
1478        return false;
1479    }
1480
1481    /**
1482     * {@inheritDoc}
1483     * <p>
1484     * Assumed to be false for JCR implementations (meaning that sort order IS used), though section 6.7.37 of JCR 2.0
1485     * specification says ordering of null values is implementation-determined.
1486     * </p>
1487     * 
1488     * @see java.sql.DatabaseMetaData#nullsAreSortedAtStart()
1489     */
1490    @Override
1491    public boolean nullsAreSortedAtStart() {
1492        return false;
1493    }
1494
1495    /**
1496     * {@inheritDoc}
1497     * <p>
1498     * Assumed to be false for JCR implementations, though section 6.7.37 of JCR 2.0 specification says ordering of null values is
1499     * implementation-determined.
1500     * </p>
1501     * 
1502     * @see java.sql.DatabaseMetaData#nullsAreSortedHigh()
1503     */
1504    @Override
1505    public boolean nullsAreSortedHigh() {
1506        return false;
1507    }
1508
1509    /**
1510     * {@inheritDoc}
1511     * <p>
1512     * Assumed to be true for JCR implementations, though section 6.7.37 of JCR 2.0 specification says ordering of null values is
1513     * implementation-determined.
1514     * </p>
1515     * 
1516     * @see java.sql.DatabaseMetaData#nullsAreSortedLow()
1517     */
1518    @Override
1519    public boolean nullsAreSortedLow() {
1520        return true;
1521    }
1522
1523    @Override
1524    public boolean othersDeletesAreVisible( int type ) {
1525        return false; // read-only
1526    }
1527
1528    @Override
1529    public boolean othersInsertsAreVisible( int type ) {
1530        return false; // read-only
1531    }
1532
1533    @Override
1534    public boolean othersUpdatesAreVisible( int type ) {
1535        return false; // read-only
1536    }
1537
1538    @Override
1539    public boolean ownDeletesAreVisible( int type ) {
1540        return false; // read-only
1541    }
1542
1543    @Override
1544    public boolean ownInsertsAreVisible( int type ) {
1545        return false; // read-only
1546    }
1547
1548    @Override
1549    public boolean ownUpdatesAreVisible( int type ) {
1550        return false; // read-only
1551    }
1552
1553    @Override
1554    public boolean storesLowerCaseIdentifiers() {
1555        return false; // JCR node types are case-sensitive
1556    }
1557
1558    @Override
1559    public boolean storesLowerCaseQuotedIdentifiers() {
1560        return false; // JCR node types are case-sensitive
1561    }
1562
1563    @Override
1564    public boolean storesMixedCaseIdentifiers() {
1565        return false; // JCR node types are case-sensitive
1566    }
1567
1568    @Override
1569    public boolean storesMixedCaseQuotedIdentifiers() {
1570        return false; // JCR node types are case-sensitive
1571    }
1572
1573    @Override
1574    public boolean storesUpperCaseIdentifiers() {
1575        return false; // JCR node types are case-sensitive
1576    }
1577
1578    @Override
1579    public boolean storesUpperCaseQuotedIdentifiers() {
1580        return false; // JCR node types are case-sensitive
1581    }
1582
1583    @Override
1584    public boolean supportsANSI92EntryLevelSQL() {
1585        return false; // JCR-SQL2 is not entry-level ANSI92 SQL
1586    }
1587
1588    @Override
1589    public boolean supportsANSI92FullSQL() {
1590        return false; // JCR-SQL2 is not full ANSI92 SQL
1591    }
1592
1593    @Override
1594    public boolean supportsANSI92IntermediateSQL() {
1595        return false; // JCR-SQL2 is not intermediate ANSI92 SQL
1596    }
1597
1598    @Override
1599    public boolean supportsAlterTableWithAddColumn() {
1600        // Not in JCR-SQL2
1601        return false;
1602    }
1603
1604    @Override
1605    public boolean supportsAlterTableWithDropColumn() {
1606        // Not in JCR-SQL2
1607        return false;
1608    }
1609
1610    @Override
1611    public boolean supportsBatchUpdates() {
1612        // Not in JCR-SQL2
1613        return false;
1614    }
1615
1616    @Override
1617    public boolean supportsCatalogsInDataManipulation() {
1618        // Not in JCR-SQL2
1619        return false;
1620    }
1621
1622    @Override
1623    public boolean supportsCatalogsInIndexDefinitions() {
1624        // No such thing in JCR-SQL2
1625        return false;
1626    }
1627
1628    @Override
1629    public boolean supportsCatalogsInPrivilegeDefinitions() {
1630        // No defining privileges in JCR 1.0 or 2.0 or JCR-SQL2
1631        return false;
1632    }
1633
1634    @Override
1635    public boolean supportsCatalogsInProcedureCalls() {
1636        // No such thing in JCR-SQL2
1637        return false;
1638    }
1639
1640    @Override
1641    public boolean supportsCatalogsInTableDefinitions() {
1642        // No defining tables in JCR 1.0 or 2.0 or JCR-SQL2
1643        return false;
1644    }
1645
1646    @Override
1647    public boolean supportsColumnAliasing() {
1648        // JCR-SQL2 does support aliases on column names (section 6.7.39)
1649        return false;
1650    }
1651
1652    @Override
1653    public boolean supportsConvert() {
1654        return false;
1655    }
1656
1657    @Override
1658    public boolean supportsConvert( int fromType,
1659                                    int toType ) {
1660        return false;
1661    }
1662
1663    @Override
1664    public boolean supportsCoreSQLGrammar() {
1665        return false;
1666    }
1667
1668    @Override
1669    public boolean supportsCorrelatedSubqueries() {
1670        return false;
1671    }
1672
1673    @Override
1674    public boolean supportsDataDefinitionAndDataManipulationTransactions() {
1675        return false;
1676    }
1677
1678    @Override
1679    public boolean supportsDataManipulationTransactionsOnly() {
1680        return false;
1681    }
1682
1683    @Override
1684    public boolean supportsDifferentTableCorrelationNames() {
1685        return false;
1686    }
1687
1688    @Override
1689    public boolean supportsExpressionsInOrderBy() {
1690        return false;
1691    }
1692
1693    @Override
1694    public boolean supportsExtendedSQLGrammar() {
1695        return false;
1696    }
1697
1698    @Override
1699    public boolean supportsFullOuterJoins() {
1700        // JCR-SQL2 does not support FULL OUTER JOIN ...
1701        return false;
1702    }
1703
1704    @Override
1705    public boolean supportsGetGeneratedKeys() {
1706        return false;
1707    }
1708
1709    @Override
1710    public boolean supportsGroupBy() {
1711        return false; // not in JCR-SQL2;
1712    }
1713
1714    @Override
1715    public boolean supportsGroupByBeyondSelect() {
1716        return false; // not in JCR-SQL2;
1717    }
1718
1719    @Override
1720    public boolean supportsGroupByUnrelated() {
1721        return false; // not in JCR-SQL2;
1722    }
1723
1724    @Override
1725    public boolean supportsIntegrityEnhancementFacility() {
1726        return false;
1727    }
1728
1729    @Override
1730    public boolean supportsLikeEscapeClause() {
1731        return false;
1732    }
1733
1734    @Override
1735    public boolean supportsLimitedOuterJoins() {
1736        return false;
1737    }
1738
1739    @Override
1740    public boolean supportsMinimumSQLGrammar() {
1741        return false;
1742    }
1743
1744    @Override
1745    public boolean supportsMixedCaseIdentifiers() {
1746        return false;
1747    }
1748
1749    @Override
1750    public boolean supportsMixedCaseQuotedIdentifiers() {
1751        return false;
1752    }
1753
1754    @Override
1755    public boolean supportsMultipleOpenResults() {
1756        return false;
1757    }
1758
1759    @Override
1760    public boolean supportsMultipleResultSets() {
1761        return false;
1762    }
1763
1764    @Override
1765    public boolean supportsMultipleTransactions() {
1766        return false;
1767    }
1768
1769    @Override
1770    public boolean supportsNamedParameters() {
1771        return false;
1772    }
1773
1774    @Override
1775    public boolean supportsNonNullableColumns() {
1776        return false;
1777    }
1778
1779    @Override
1780    public boolean supportsOpenCursorsAcrossCommit() {
1781        return false;
1782    }
1783
1784    @Override
1785    public boolean supportsOpenCursorsAcrossRollback() {
1786        return false;
1787    }
1788
1789    @Override
1790    public boolean supportsOpenStatementsAcrossCommit() {
1791        return false;
1792    }
1793
1794    @Override
1795    public boolean supportsOpenStatementsAcrossRollback() {
1796        return false;
1797    }
1798
1799    @Override
1800    public boolean supportsOrderByUnrelated() {
1801        return false;
1802    }
1803
1804    @Override
1805    public boolean supportsOuterJoins() {
1806        return true; // JCR-SQL2
1807    }
1808
1809    @Override
1810    public boolean supportsPositionedDelete() {
1811        return false; // read-only
1812    }
1813
1814    @Override
1815    public boolean supportsPositionedUpdate() {
1816        return false; // read-only
1817    }
1818
1819    @Override
1820    public boolean supportsResultSetConcurrency( int type,
1821                                                 int concurrency ) {
1822        return false;
1823    }
1824
1825    @Override
1826    public boolean supportsResultSetHoldability( int holdability ) {
1827        return false;
1828    }
1829
1830    @Override
1831    public boolean supportsResultSetType( int type ) {
1832        return false;
1833    }
1834
1835    @Override
1836    public boolean supportsSavepoints() {
1837        return false; // nope
1838    }
1839
1840    @Override
1841    public boolean supportsSchemasInDataManipulation() {
1842        return false; // nope
1843    }
1844
1845    @Override
1846    public boolean supportsSchemasInIndexDefinitions() {
1847        return false; // nope
1848    }
1849
1850    @Override
1851    public boolean supportsSchemasInPrivilegeDefinitions() {
1852        return false; // nope
1853    }
1854
1855    @Override
1856    public boolean supportsSchemasInProcedureCalls() {
1857        return false; // nope
1858    }
1859
1860    @Override
1861    public boolean supportsSchemasInTableDefinitions() {
1862        return false; // nope
1863    }
1864
1865    @Override
1866    public boolean supportsSelectForUpdate() {
1867        return false; // read-only
1868    }
1869
1870    @Override
1871    public boolean supportsStatementPooling() {
1872        return false; // nope
1873    }
1874
1875    @Override
1876    public boolean supportsStoredFunctionsUsingCallSyntax() {
1877        return false; // nope
1878    }
1879
1880    @Override
1881    public boolean supportsStoredProcedures() {
1882        return false; // nope
1883    }
1884
1885    @Override
1886    public boolean supportsSubqueriesInComparisons() {
1887        return false; // no subqueries in JCR-SQL2
1888    }
1889
1890    @Override
1891    public boolean supportsSubqueriesInExists() {
1892        return false; // no subqueries in JCR-SQL2
1893    }
1894
1895    @Override
1896    public boolean supportsSubqueriesInIns() {
1897        return false; // no subqueries in JCR-SQL2
1898    }
1899
1900    @Override
1901    public boolean supportsSubqueriesInQuantifieds() {
1902        return false; // no subqueries in JCR-SQL2
1903    }
1904
1905    @Override
1906    public boolean supportsTableCorrelationNames() {
1907        // JCR-SQL2 does support table aliases that can be used as prefixes for column names
1908        return true;
1909    }
1910
1911    @Override
1912    public boolean supportsTransactionIsolationLevel( int level ) {
1913        return level == Connection.TRANSACTION_READ_COMMITTED;
1914    }
1915
1916    @Override
1917    public boolean supportsTransactions() {
1918        // Generally, JCR does support transactions ...
1919        return false;
1920    }
1921
1922    @Override
1923    public boolean supportsUnion() {
1924        // JCR-SQL2 does not support UNION ...
1925        return false;
1926    }
1927
1928    @Override
1929    public boolean supportsUnionAll() {
1930        // JCR-SQL2 does not support UNION ALL ...
1931        return false;
1932    }
1933
1934    @Override
1935    public boolean updatesAreDetected( int type ) {
1936        return false;
1937    }
1938
1939    @Override
1940    public boolean usesLocalFilePerTable() {
1941        return false;
1942    }
1943
1944    @Override
1945    public boolean usesLocalFiles() {
1946        return false;
1947    }
1948
1949    @Override
1950    public boolean isWrapperFor( Class<?> iface ) {
1951        return iface.isInstance(this);
1952    }
1953
1954    @Override
1955    public <T> T unwrap( Class<T> iface ) throws SQLException {
1956        if (!isWrapperFor(iface)) {
1957            throw new SQLException(JdbcLocalI18n.classDoesNotImplementInterface.text(DatabaseMetaData.class.getSimpleName(),
1958                                                                                     iface.getName()));
1959        }
1960
1961        return iface.cast(this);
1962    }
1963
1964    private List<NodeType> filterNodeTypes( String tableNamePattern ) throws RepositoryException {
1965        List<NodeType> nodetypes = null;
1966
1967        if (tableNamePattern.trim().equals(WILDCARD)) {
1968
1969            nodetypes = new ArrayList<>(this.connection.getRepositoryDelegate().nodeTypes());
1970
1971        } else if (tableNamePattern.contains(WILDCARD)) {
1972            nodetypes = new ArrayList<NodeType>();
1973            String partName = null;
1974            boolean isLeading = false;
1975            boolean isTrailing = false;
1976            partName = tableNamePattern;
1977
1978            if (partName.startsWith(WILDCARD)) {
1979                partName = partName.substring(1);
1980                isLeading = true;
1981            }
1982            if (partName.endsWith(WILDCARD) && partName.length() > 1) {
1983                partName = partName.substring(0, partName.length() - 1);
1984                isTrailing = true;
1985            }
1986
1987            List<NodeType> nts = new ArrayList<>(this.connection.getRepositoryDelegate().nodeTypes());
1988            // build the list of records from server's Results object.
1989            for (NodeType type : nts) {
1990                if (isLeading) {
1991                    if (isTrailing) {
1992                        if (type.getName().indexOf(partName, 1) > -1) {
1993                            nodetypes.add(type);
1994                        }
1995                    } else if (type.getName().endsWith(partName)) {
1996                        nodetypes.add(type);
1997                    }
1998
1999                } else if (isTrailing) {
2000                    if (type.getName().startsWith(partName)) {
2001                        nodetypes.add(type);
2002                    }
2003                }
2004            }
2005
2006        } else {
2007            NodeType nt = this.connection.getRepositoryDelegate().nodeType(tableNamePattern);
2008            nodetypes = new ArrayList<NodeType>(1);
2009            if (nt != null) {
2010                nodetypes.add(nt);
2011            }
2012        }
2013
2014        if (nodetypes.size() > 1) {
2015            final Comparator<NodeType> name_order = new Comparator<NodeType>() {
2016                @Override
2017                public int compare( NodeType e1,
2018                                    NodeType e2 ) {
2019                    return e1.getName().compareTo(e2.getName());
2020                }
2021            };
2022            Collections.sort(nodetypes, name_order);
2023        }
2024
2025        return nodetypes;
2026    }
2027
2028    private List<PropertyDefinition> filterPropertyDefnitions( String columnNamePattern,
2029                                                               NodeType nodeType ) {
2030
2031        List<PropertyDefinition> allDefns = new ArrayList<PropertyDefinition>();
2032        addPropertyDefinitions(allDefns, nodeType);
2033
2034        List<PropertyDefinition> resultDefns = null;
2035
2036        if (columnNamePattern == null || columnNamePattern.trim().equals(WILDCARD)) {
2037            resultDefns = allDefns;
2038        } else if (columnNamePattern.contains(WILDCARD)) {
2039            resultDefns = new ArrayList<PropertyDefinition>();
2040            String partName = null;
2041            boolean isLeading = false;
2042            boolean isTrailing = false;
2043            partName = columnNamePattern;
2044
2045            if (partName.startsWith(WILDCARD)) {
2046                partName = partName.substring(1);
2047                isLeading = true;
2048            }
2049            if (partName.endsWith(WILDCARD) && partName.length() > 1) {
2050                partName = partName.substring(0, partName.length() - 1);
2051                isTrailing = true;
2052            }
2053
2054            for (PropertyDefinition defn : allDefns) {
2055                if (isLeading) {
2056                    if (isTrailing) {
2057                        if (defn.getName().indexOf(partName, 1) > -1) {
2058                            resultDefns.add(defn);
2059                        }
2060                    } else if (defn.getName().endsWith(partName)) {
2061                        resultDefns.add(defn);
2062                    }
2063
2064                } else if (isTrailing) {
2065                    if (defn.getName().startsWith(partName)) {
2066                        resultDefns.add(defn);
2067                    }
2068                }
2069            }
2070
2071        } else {
2072            resultDefns = new ArrayList<PropertyDefinition>();
2073
2074            for (PropertyDefinition defn : allDefns) {
2075                if (defn.getName().equals(columnNamePattern)) {
2076                    resultDefns.add(defn);
2077                }
2078            }
2079
2080        }
2081
2082        if (resultDefns.size() > 1) {
2083            final Comparator<PropertyDefinition> name_order = new Comparator<PropertyDefinition>() {
2084                @Override
2085                public int compare( PropertyDefinition e1,
2086                                    PropertyDefinition e2 ) {
2087                    return e1.getName().compareTo(e2.getName());
2088                }
2089            };
2090            Collections.sort(resultDefns, name_order);
2091        }
2092
2093        return resultDefns;
2094    }
2095
2096    private void addPropertyDefinitions( List<PropertyDefinition> mapDefns,
2097                                         NodeType nodetype ) {
2098
2099        Set<String> colNames = new HashSet<String>(mapDefns.size());
2100        // All tables have these pseudo-columns ...
2101        mapDefns.addAll(PSEUDO_COLUMN_DEFNS);
2102        colNames.addAll(PSEUDO_COLUMN_NAMES);
2103
2104        for (PropertyDefinition defn : nodetype.getPropertyDefinitions()) {
2105            // Don't include residual (e.g., '*') properties as columns ...
2106            if (defn.getName().equalsIgnoreCase("*")) continue;
2107            // Don't include multi-valued properties as columns ...
2108            if (defn.isMultiple()) continue;
2109            // Don't include any properties defined in the "modeint" internal namespace ...
2110            if (defn.getName().startsWith("modeint:")) continue;
2111
2112            if (!colNames.contains(defn.getName())) {
2113                mapDefns.add(defn);
2114                colNames.add(defn.getName());
2115            }
2116        }
2117    }
2118
2119    protected static class PseudoPropertyDefinition implements PropertyDefinition {
2120
2121        private static final String[] NO_STRINGS = new String[] {};
2122        private static final Value[] NO_VALUES = new Value[] {};
2123
2124        private final String[] queryOps;
2125        private final Value[] defaultValues;
2126        private final int requiredType;
2127        private final String[] constraints;
2128        private final boolean isFullTextSearchable;
2129        private final boolean isMultiple;
2130        private final boolean isQueryOrderable;
2131        private final boolean isAutoCreated;
2132        private final boolean isMandatory;
2133        private final boolean isProtected;
2134        private final String name;
2135        private final NodeType declaringNodeType;
2136        private final int onParentVersioning;
2137
2138        protected PseudoPropertyDefinition( NodeType declaringNodeType,
2139                                            String name,
2140                                            int requiredType,
2141                                            boolean autoCreated,
2142                                            boolean mandatory,
2143                                            boolean isProtected,
2144                                            boolean multiple,
2145                                            boolean fullTextSearchable,
2146                                            boolean queryOrderable,
2147                                            Value[] defaultValues,
2148                                            String[] constraints,
2149                                            int onParentVersioning,
2150                                            String[] queryOps ) {
2151            this.declaringNodeType = declaringNodeType;
2152            this.name = name;
2153            this.queryOps = queryOps != null ? queryOps : NO_STRINGS;
2154            this.defaultValues = defaultValues != null ? defaultValues : NO_VALUES;
2155            this.requiredType = requiredType;
2156            this.constraints = constraints != null ? constraints : NO_STRINGS;
2157            this.isFullTextSearchable = fullTextSearchable;
2158            this.isAutoCreated = autoCreated;
2159            this.isMultiple = multiple;
2160            this.isQueryOrderable = queryOrderable;
2161            this.isMandatory = mandatory;
2162            this.isProtected = isProtected;
2163            this.onParentVersioning = onParentVersioning;
2164        }
2165
2166        protected PseudoPropertyDefinition( NodeType declaringNodeType,
2167                                            String name,
2168                                            int requiredType,
2169                                            boolean autoCreated,
2170                                            boolean mandatory,
2171                                            boolean isProtected,
2172                                            boolean multiple,
2173                                            boolean fullTextSearchable,
2174                                            boolean queryOrderable ) {
2175            this(declaringNodeType, name, requiredType, autoCreated, mandatory, isProtected, multiple, fullTextSearchable,
2176                 queryOrderable, null, null, OnParentVersionAction.COPY, null);
2177        }
2178
2179        @Override
2180        public String[] getAvailableQueryOperators() {
2181            return queryOps;
2182        }
2183
2184        @Override
2185        public Value[] getDefaultValues() {
2186            return defaultValues;
2187        }
2188
2189        @Override
2190        public int getRequiredType() {
2191            return requiredType;
2192        }
2193
2194        @Override
2195        public String[] getValueConstraints() {
2196            return constraints;
2197        }
2198
2199        @Override
2200        public boolean isFullTextSearchable() {
2201            return isFullTextSearchable;
2202        }
2203
2204        @Override
2205        public boolean isMultiple() {
2206            return isMultiple;
2207        }
2208
2209        @Override
2210        public boolean isQueryOrderable() {
2211            return isQueryOrderable;
2212        }
2213
2214        @Override
2215        public NodeType getDeclaringNodeType() {
2216            return declaringNodeType;
2217        }
2218
2219        @Override
2220        public String getName() {
2221            return name;
2222        }
2223
2224        @Override
2225        public int getOnParentVersion() {
2226            return onParentVersioning;
2227        }
2228
2229        @Override
2230        public boolean isAutoCreated() {
2231            return isAutoCreated;
2232        }
2233
2234        @Override
2235        public boolean isMandatory() {
2236            return isMandatory;
2237        }
2238
2239        @Override
2240        public boolean isProtected() {
2241            return isProtected;
2242        }
2243
2244    }
2245
2246}