001    /*
002    // $Id: //open/mondrian/src/main/mondrian/xmla/RowsetDefinition.java#62 $
003    // This software is subject to the terms of the Common Public License
004    // Agreement, available at the following URL:
005    // http://www.opensource.org/licenses/cpl.html.
006    // Copyright (C) 2003-2008 Julian Hyde
007    // All Rights Reserved.
008    // You must accept the terms of that agreement to use this software.
009    */
010    package mondrian.xmla;
011    
012    import mondrian.olap.*;
013    import mondrian.olap.fun.FunInfo;
014    import mondrian.rolap.RolapCube;
015    import mondrian.rolap.RolapLevel;
016    import mondrian.rolap.RolapSchema;
017    import mondrian.rolap.RolapAggregator;
018    import mondrian.rolap.RolapMember;
019    
020    import java.lang.reflect.Field;
021    import java.lang.reflect.Method;
022    import java.lang.reflect.InvocationTargetException;
023    import java.util.*;
024    import java.text.SimpleDateFormat;
025    import java.text.Format;
026    
027    /**
028     * <code>RowsetDefinition</code> defines a rowset, including the columns it
029     * should contain.
030     *
031     * <p>See "XML for Analysis Rowsets", page 38 of the XML for Analysis
032     * Specification, version 1.1.
033     *
034     * @author jhyde
035     * @version $Id: //open/mondrian/src/main/mondrian/xmla/RowsetDefinition.java#62 $
036     */
037    enum RowsetDefinition {
038        /**
039         * Returns a list of XML for Analysis data sources
040         * available on the server or Web Service. (For an
041         * example of how these may be published, see
042         * "XML for Analysis Implementation Walkthrough"
043         * in the XML for Analysis specification.)
044         *
045         *  http://msdn2.microsoft.com/en-us/library/ms126129(SQL.90).aspx
046         *
047         *
048         * restrictions
049         *
050         * Not supported
051         */
052        DISCOVER_DATASOURCES(
053            0,
054            "Returns a list of XML for Analysis data sources available on the server or Web Service.",
055            new Column[] {
056                DiscoverDatasourcesRowset.DataSourceName,
057                DiscoverDatasourcesRowset.DataSourceDescription,
058                DiscoverDatasourcesRowset.URL,
059                DiscoverDatasourcesRowset.DataSourceInfo,
060                DiscoverDatasourcesRowset.ProviderName,
061                DiscoverDatasourcesRowset.ProviderType,
062                DiscoverDatasourcesRowset.AuthenticationMode,
063            },
064            null /* not sorted */) {
065            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
066                return new DiscoverDatasourcesRowset(request, handler);
067            }
068        },
069    
070        /**
071         * Note that SQL Server also returns the data-mining columns.
072         *
073         *
074         * restrictions
075         *
076         * Not supported
077         */
078        DISCOVER_SCHEMA_ROWSETS(
079            2,
080            "Returns the names, values, and other information of all supported RequestType enumeration values.",
081            new Column[] {
082                DiscoverSchemaRowsetsRowset.SchemaName,
083                DiscoverSchemaRowsetsRowset.SchemaGuid,
084                DiscoverSchemaRowsetsRowset.Restrictions,
085                DiscoverSchemaRowsetsRowset.Description,
086            },
087            null /* not sorted */) {
088            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
089                return new DiscoverSchemaRowsetsRowset(request, handler);
090            }
091            protected void writeRowsetXmlSchemaRowDef(SaxWriter writer) {
092                writer.startElement("xsd:complexType", new String[] {
093                    "name", "row"
094                });
095                writer.startElement("xsd:sequence");
096                for (Column column : columnDefinitions) {
097                    final String name = XmlaUtil.encodeElementName(column.name);
098    
099                    if (column == DiscoverSchemaRowsetsRowset.Restrictions) {
100                        writer.startElement("xsd:element", new String[]{
101                            "sql:field", column.name,
102                            "name", name,
103                            "minOccurs", "0",
104                            "maxOccurs", "unbounded"
105                        });
106                        writer.startElement("xsd:complexType");
107                        writer.startElement("xsd:sequence");
108                        writer.element("xsd:element", new String[]{
109                            "name", "Name",
110                            "type", "xsd:string",
111                            "sql:field", "Name"
112                        });
113                        writer.element("xsd:element", new String[]{
114                            "name", "Type",
115                            "type", "xsd:string",
116                            "sql:field", "Type"
117                        });
118    
119                        writer.endElement(); // xsd:sequence
120                        writer.endElement(); // xsd:complexType
121                        writer.endElement(); // xsd:element
122    
123                    } else {
124                        final String xsdType = column.type.columnType;
125    
126                        String[] attrs;
127                        if (column.nullable) {
128                            if (column.unbounded) {
129                                attrs = new String[]{
130                                    "sql:field", column.name,
131                                    "name", name,
132                                    "type", xsdType,
133                                    "minOccurs", "0",
134                                    "maxOccurs", "unbounded"
135                                };
136                            } else {
137                                attrs = new String[]{
138                                    "sql:field", column.name,
139                                    "name", name,
140                                    "type", xsdType,
141                                    "minOccurs", "0"
142                                };
143                            }
144                        } else {
145                            if (column.unbounded) {
146                                attrs = new String[]{
147                                    "sql:field", column.name,
148                                    "name", name,
149                                    "type", xsdType,
150                                    "maxOccurs", "unbounded"
151                                };
152                            } else {
153                                attrs = new String[]{
154                                    "sql:field", column.name,
155                                    "name", name,
156                                    "type", xsdType
157                                };
158                            }
159                        }
160                        writer.element("xsd:element", attrs);
161                    }
162                }
163                writer.endElement(); // xsd:sequence
164                writer.endElement(); // xsd:complexType
165            }
166        },
167    
168        /**
169         *
170         *
171         *
172         * restrictions
173         *
174         * Not supported
175         */
176        DISCOVER_ENUMERATORS(
177            3,
178            "Returns a list of names, data types, and enumeration values for enumerators supported by the provider of a specific data source.",
179            new Column[] {
180                DiscoverEnumeratorsRowset.EnumName,
181                DiscoverEnumeratorsRowset.EnumDescription,
182                DiscoverEnumeratorsRowset.EnumType,
183                DiscoverEnumeratorsRowset.ElementName,
184                DiscoverEnumeratorsRowset.ElementDescription,
185                DiscoverEnumeratorsRowset.ElementValue,
186            },
187            null /* not sorted */) {
188            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
189                return new DiscoverEnumeratorsRowset(request, handler);
190            }
191        },
192    
193        /**
194         *
195         *
196         *
197         * restrictions
198         *
199         * Not supported
200         */
201        DISCOVER_PROPERTIES(
202            1,
203            "Returns a list of information and values about the requested properties that are supported by the specified data source provider.",
204            new Column[] {
205                DiscoverPropertiesRowset.PropertyName,
206                DiscoverPropertiesRowset.PropertyDescription,
207                DiscoverPropertiesRowset.PropertyType,
208                DiscoverPropertiesRowset.PropertyAccessType,
209                DiscoverPropertiesRowset.IsRequired,
210                DiscoverPropertiesRowset.Value,
211            },
212            null /* not sorted */) {
213            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
214                return new DiscoverPropertiesRowset(request, handler);
215            }
216        },
217    
218        /**
219         *
220         *
221         *
222         * restrictions
223         *
224         * Not supported
225         */
226        DISCOVER_KEYWORDS(
227            4,
228            "Returns an XML list of keywords reserved by the provider.",
229            new Column[] {
230                DiscoverKeywordsRowset.Keyword,
231            },
232            null /* not sorted */) {
233            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
234                return new DiscoverKeywordsRowset(request, handler);
235            }
236        },
237    
238        /**
239         *
240         *
241         *
242         * restrictions
243         *
244         * Not supported
245         */
246        DISCOVER_LITERALS(
247            5,
248            "Returns information about literals supported by the provider.",
249            new Column[] {
250                DiscoverLiteralsRowset.LiteralName,
251                DiscoverLiteralsRowset.LiteralValue,
252                DiscoverLiteralsRowset.LiteralInvalidChars,
253                DiscoverLiteralsRowset.LiteralInvalidStartingChars,
254                DiscoverLiteralsRowset.LiteralMaxLength,
255            },
256            null /* not sorted */) {
257            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
258                return new DiscoverLiteralsRowset(request, handler);
259            }
260        },
261    
262        /**
263         *
264         *
265         *
266         * restrictions
267         *
268         * Not supported
269         */
270        DBSCHEMA_CATALOGS(
271            6,
272            "Returns information about literals supported by the provider.",
273            new Column[] {
274                DbschemaCatalogsRowset.CatalogName,
275                DbschemaCatalogsRowset.Description,
276                DbschemaCatalogsRowset.Roles,
277                DbschemaCatalogsRowset.DateModified,
278            },
279            new Column[] {
280                DbschemaCatalogsRowset.CatalogName,
281            }) {
282            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
283                return new DbschemaCatalogsRowset(request, handler);
284            }
285        },
286    
287        /**
288         *
289         *
290         *
291         * restrictions
292         *
293         * Not supported
294         *    COLUMN_OLAP_TYPE
295         */
296        DBSCHEMA_COLUMNS(
297            7, null,
298            new Column[] {
299                DbschemaColumnsRowset.TableCatalog,
300                DbschemaColumnsRowset.TableSchema,
301                DbschemaColumnsRowset.TableName,
302                DbschemaColumnsRowset.ColumnName,
303                DbschemaColumnsRowset.OrdinalPosition,
304                DbschemaColumnsRowset.ColumnHasDefault,
305                DbschemaColumnsRowset.ColumnFlags,
306                DbschemaColumnsRowset.IsNullable,
307                DbschemaColumnsRowset.DataType,
308                DbschemaColumnsRowset.CharacterMaximumLength,
309                DbschemaColumnsRowset.CharacterOctetLength,
310                DbschemaColumnsRowset.NumericPrecision,
311                DbschemaColumnsRowset.NumericScale,
312            },
313            new Column[] {
314                DbschemaColumnsRowset.TableCatalog,
315                DbschemaColumnsRowset.TableSchema,
316                DbschemaColumnsRowset.TableName,
317            }) {
318            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
319                return new DbschemaColumnsRowset(request, handler);
320            }
321        },
322    
323        /**
324         *
325         *
326         *
327         * restrictions
328         *
329         * Not supported
330         */
331        DBSCHEMA_PROVIDER_TYPES(
332            8, null,
333            new Column[] {
334                DbschemaProviderTypesRowset.TypeName,
335                DbschemaProviderTypesRowset.DataType,
336                DbschemaProviderTypesRowset.ColumnSize,
337                DbschemaProviderTypesRowset.LiteralPrefix,
338                DbschemaProviderTypesRowset.LiteralSuffix,
339                DbschemaProviderTypesRowset.IsNullable,
340                DbschemaProviderTypesRowset.CaseSensitive,
341                DbschemaProviderTypesRowset.Searchable,
342                DbschemaProviderTypesRowset.UnsignedAttribute,
343                DbschemaProviderTypesRowset.FixedPrecScale,
344                DbschemaProviderTypesRowset.AutoUniqueValue,
345                DbschemaProviderTypesRowset.IsLong,
346                DbschemaProviderTypesRowset.BestMatch,
347            },
348            new Column[] {
349                DbschemaProviderTypesRowset.DataType,
350            }) {
351            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
352                return new DbschemaProviderTypesRowset(request, handler);
353            }
354        },
355    
356        DBSCHEMA_SCHEMATA(
357            8, null,
358            new Column[] {
359                DbschemaSchemataRowset.CatalogName,
360                DbschemaSchemataRowset.SchemaName,
361                DbschemaSchemataRowset.SchemaOwner,
362            },
363            new Column[] {
364                DbschemaSchemataRowset.CatalogName,
365                DbschemaSchemataRowset.SchemaName,
366                DbschemaSchemataRowset.SchemaOwner,
367            }) {
368            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
369                return new DbschemaSchemataRowset(request, handler);
370            }
371        },
372    
373        /**
374         * http://msdn2.microsoft.com/en-us/library/ms126299(SQL.90).aspx
375         *
376         * restrictions:
377         *   TABLE_CATALOG Optional
378         *   TABLE_SCHEMA Optional
379         *   TABLE_NAME Optional
380         *   TABLE_TYPE Optional
381         *   TABLE_OLAP_TYPE Optional
382         *
383         * Not supported
384         */
385        DBSCHEMA_TABLES(
386            9, null,
387            new Column[] {
388                DbschemaTablesRowset.TableCatalog,
389                DbschemaTablesRowset.TableSchema,
390                DbschemaTablesRowset.TableName,
391                DbschemaTablesRowset.TableType,
392                DbschemaTablesRowset.TableGuid,
393                DbschemaTablesRowset.Description,
394                DbschemaTablesRowset.TablePropId,
395                DbschemaTablesRowset.DateCreated,
396                DbschemaTablesRowset.DateModified,
397                //TableOlapType,
398            },
399            new Column[] {
400                DbschemaTablesRowset.TableType,
401                DbschemaTablesRowset.TableCatalog,
402                DbschemaTablesRowset.TableSchema,
403                DbschemaTablesRowset.TableName,
404            }) {
405            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
406                return new DbschemaTablesRowset(request, handler);
407            }
408        },
409    
410        /**
411         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbtables_info_rowset.asp
412         *
413         *
414         * restrictions
415         *
416         * Not supported
417         */
418        DBSCHEMA_TABLES_INFO(
419            10, null,
420            new Column[] {
421                DbschemaTablesInfoRowset.TableCatalog,
422                DbschemaTablesInfoRowset.TableSchema,
423                DbschemaTablesInfoRowset.TableName,
424                DbschemaTablesInfoRowset.TableType,
425                DbschemaTablesInfoRowset.TableGuid,
426                DbschemaTablesInfoRowset.Bookmarks,
427                DbschemaTablesInfoRowset.BookmarkType,
428                DbschemaTablesInfoRowset.BookmarkDataType,
429                DbschemaTablesInfoRowset.BookmarkMaximumLength,
430                DbschemaTablesInfoRowset.BookmarkInformation,
431                DbschemaTablesInfoRowset.TableVersion,
432                DbschemaTablesInfoRowset.Cardinality,
433                DbschemaTablesInfoRowset.Description,
434                DbschemaTablesInfoRowset.TablePropId,
435            },
436            null /* cannot find doc -- presume unsorted */) {
437            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
438                return new DbschemaTablesInfoRowset(request, handler);
439            }
440        },
441    
442        /**
443         * http://msdn2.microsoft.com/en-us/library/ms126032(SQL.90).aspx
444         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/olapactions_rowset.asp
445         *
446         * restrictions
447         *   CATALOG_NAME Optional
448         *   SCHEMA_NAME Optional
449         *   CUBE_NAME Mandatory
450         *   ACTION_NAME Optional
451         *   ACTION_TYPE Optional
452         *   COORDINATE Mandatory
453         *   COORDINATE_TYPE Mandatory
454         *   INVOCATION
455         *      (Optional) The INVOCATION restriction column defaults to the
456         *      value of MDACTION_INVOCATION_INTERACTIVE. To retrieve all
457         *      actions, use the MDACTION_INVOCATION_ALL value in the
458         *      INVOCATION restriction column.
459         *   CUBE_SOURCE
460         *      (Optional) A bitmap with one of the following valid values:
461         *
462         *      1 CUBE
463         *      2 DIMENSION
464         *
465         *      Default restriction is a value of 1.
466         *
467         * Not supported
468         */
469        MDSCHEMA_ACTIONS(
470            11, null, new Column[] {
471            MdschemaActionsRowset.SchemaName,
472            MdschemaActionsRowset.CubeName,
473            MdschemaActionsRowset.ActionName,
474            MdschemaActionsRowset.Coordinate,
475            MdschemaActionsRowset.CoordinateType,
476        }, new Column[] {
477            // Spec says sort on CATALOG_NAME, SCHEMA_NAME, CUBE_NAME,
478            // ACTION_NAME.
479            MdschemaActionsRowset.SchemaName,
480            MdschemaActionsRowset.CubeName,
481            MdschemaActionsRowset.ActionName,
482        }) {
483            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
484                return new MdschemaActionsRowset(request, handler);
485            }
486        },
487    
488        /**
489         * http://msdn2.microsoft.com/en-us/library/ms126271(SQL.90).aspx
490         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/olapproperties_rowset.asp
491         *
492         * restrictions
493         *   CATALOG_NAME Optional.
494         *   SCHEMA_NAME Optional.
495         *   CUBE_NAME Optional.
496         *   CUBE_TYPE
497         *      (Optional) A bitmap with one of these valid values:
498         *      1 CUBE
499         *      2 DIMENSION
500         *     Default restriction is a value of 1.
501         *   BASE_CUBE_NAME Optional.
502         *
503         * Not supported
504         *   CREATED_ON
505         *   LAST_SCHEMA_UPDATE
506         *   SCHEMA_UPDATED_BY
507         *   LAST_DATA_UPDATE
508         *   DATA_UPDATED_BY
509         *   ANNOTATIONS
510         */
511        MDSCHEMA_CUBES(
512            12, null,
513            new Column[] {
514                MdschemaCubesRowset.CatalogName,
515                MdschemaCubesRowset.SchemaName,
516                MdschemaCubesRowset.CubeName,
517                MdschemaCubesRowset.CubeType,
518                MdschemaCubesRowset.CubeGuid,
519                MdschemaCubesRowset.CreatedOn,
520                MdschemaCubesRowset.LastSchemaUpdate,
521                MdschemaCubesRowset.SchemaUpdatedBy,
522                MdschemaCubesRowset.LastDataUpdate,
523                MdschemaCubesRowset.DataUpdatedBy,
524                MdschemaCubesRowset.IsDrillthroughEnabled,
525                MdschemaCubesRowset.IsWriteEnabled,
526                MdschemaCubesRowset.IsLinkable,
527                MdschemaCubesRowset.IsSqlEnabled,
528                MdschemaCubesRowset.Description
529            },
530            new Column[] {
531                MdschemaCubesRowset.CatalogName,
532                MdschemaCubesRowset.SchemaName,
533                MdschemaCubesRowset.CubeName,
534            }) {
535            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
536                return new MdschemaCubesRowset(request, handler);
537            }
538        },
539    
540        /**
541         * http://msdn2.microsoft.com/en-us/library/ms126180(SQL.90).aspx
542         * http://msdn2.microsoft.com/en-us/library/ms126180.aspx
543         *
544         * restrictions
545         *    CATALOG_NAME Optional.
546         *    SCHEMA_NAME Optional.
547         *    CUBE_NAME Optional.
548         *    DIMENSION_NAME Optional.
549         *    DIMENSION_UNIQUE_NAME Optional.
550         *    CUBE_SOURCE (Optional) A bitmap with one of the following valid values:
551         *      1 CUBE
552         *      2 DIMENSION
553         *    Default restriction is a value of 1.
554         *
555         *    DIMENSION_VISIBILITY (Optional) A bitmap with one of the following valid values:
556         *      1 Visible
557         *      2 Not visible
558         *    Default restriction is a value of 1.
559         */
560        MDSCHEMA_DIMENSIONS(
561            13, null,
562            new Column[] {
563                MdschemaDimensionsRowset.CatalogName,
564                MdschemaDimensionsRowset.SchemaName,
565                MdschemaDimensionsRowset.CubeName,
566                MdschemaDimensionsRowset.DimensionName,
567                MdschemaDimensionsRowset.DimensionUniqueName,
568                MdschemaDimensionsRowset.DimensionGuid,
569                MdschemaDimensionsRowset.DimensionCaption,
570                MdschemaDimensionsRowset.DimensionOrdinal,
571                MdschemaDimensionsRowset.DimensionType,
572                MdschemaDimensionsRowset.DimensionCardinality,
573                MdschemaDimensionsRowset.DefaultHierarchy,
574                MdschemaDimensionsRowset.Description,
575                MdschemaDimensionsRowset.IsVirtual,
576                MdschemaDimensionsRowset.IsReadWrite,
577                MdschemaDimensionsRowset.DimensionUniqueSettings,
578                MdschemaDimensionsRowset.DimensionMasterUniqueName,
579                MdschemaDimensionsRowset.DimensionIsVisible,
580            },
581            new Column[] {
582                MdschemaDimensionsRowset.CatalogName,
583                MdschemaDimensionsRowset.SchemaName,
584                MdschemaDimensionsRowset.CubeName,
585                MdschemaDimensionsRowset.DimensionName,
586            }) {
587            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
588                return new MdschemaDimensionsRowset(request, handler);
589            }
590        },
591    
592        /**
593         * http://msdn2.microsoft.com/en-us/library/ms126257(SQL.90).aspx
594         * http://msdn.microsoft.com/library/en-us/oledb/htm/olapfunctions_rowset.asp
595         *
596         * restrictions
597         *   LIBRARY_NAME Optional.
598         *   INTERFACE_NAME Optional.
599         *   FUNCTION_NAME Optional.
600         *   ORIGIN Optional.
601         *
602         * Not supported
603         *  DLL_NAME
604         *    Optional
605         *  HELP_FILE
606         *    Optional
607         *  HELP_CONTEXT
608         *    Optional
609         *    - SQL Server xml schema says that this must be present
610         *  OBJECT
611         *    Optional
612         *  CAPTION The display caption for the function.
613         */
614        MDSCHEMA_FUNCTIONS(
615            14, null,
616            new Column[] {
617                MdschemaFunctionsRowset.FunctionName,
618                MdschemaFunctionsRowset.Description,
619                MdschemaFunctionsRowset.ParameterList,
620                MdschemaFunctionsRowset.ReturnType,
621                MdschemaFunctionsRowset.Origin,
622                MdschemaFunctionsRowset.InterfaceName,
623                MdschemaFunctionsRowset.LibraryName,
624                MdschemaFunctionsRowset.Caption,
625            },
626            new Column[] {
627                MdschemaFunctionsRowset.LibraryName,
628                MdschemaFunctionsRowset.InterfaceName,
629                MdschemaFunctionsRowset.FunctionName,
630                MdschemaFunctionsRowset.Origin,
631            }) {
632            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
633                return new MdschemaFunctionsRowset(request, handler);
634            }
635        },
636    
637        /**
638         * http://msdn2.microsoft.com/en-us/library/ms126062(SQL.90).aspx
639         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/olapproperties_rowset.asp
640         *
641         * restrictions
642         *    CATALOG_NAME Optional.
643         *    SCHEMA_NAME Optional.
644         *    CUBE_NAME Optional.
645         *    DIMENSION_UNIQUE_NAME Optional.
646         *    HIERARCHY_NAME Optional.
647         *    HIERARCHY_UNIQUE_NAME Optional.
648         *    HIERARCHY_ORIGIN
649         *       (Optional) A default restriction is in effect
650         *       on MD_USER_DEFINED and MD_SYSTEM_ENABLED.
651         *    CUBE_SOURCE
652         *      (Optional) A bitmap with one of the following valid values:
653         *      1 CUBE
654         *      2 DIMENSION
655         *      Default restriction is a value of 1.
656         *    HIERARCHY_VISIBILITY
657         *      (Optional) A bitmap with one of the following valid values:
658         *      1 Visible
659         *      2 Not visible
660         *      Default restriction is a value of 1.
661         *
662         * Not supported
663         *  HIERARCHY_IS_VISIBLE
664         *  HIERARCHY_ORIGIN
665         *  HIERARCHY_DISPLAY_FOLDER
666         *  INSTANCE_SELECTION
667         */
668        MDSCHEMA_HIERARCHIES(
669            15, null,
670            new Column[] {
671                MdschemaHierarchiesRowset.CatalogName,
672                MdschemaHierarchiesRowset.SchemaName,
673                MdschemaHierarchiesRowset.CubeName,
674                MdschemaHierarchiesRowset.DimensionUniqueName,
675                MdschemaHierarchiesRowset.HierarchyName,
676                MdschemaHierarchiesRowset.HierarchyUniqueName,
677                MdschemaHierarchiesRowset.HierarchyGuid,
678                MdschemaHierarchiesRowset.HierarchyCaption,
679                MdschemaHierarchiesRowset.DimensionType,
680                MdschemaHierarchiesRowset.HierarchyCardinality,
681                MdschemaHierarchiesRowset.DefaultMember,
682                MdschemaHierarchiesRowset.AllMember,
683                MdschemaHierarchiesRowset.Description,
684                MdschemaHierarchiesRowset.Structure,
685                MdschemaHierarchiesRowset.IsVirtual,
686                MdschemaHierarchiesRowset.IsReadWrite,
687                MdschemaHierarchiesRowset.DimensionUniqueSettings,
688                MdschemaHierarchiesRowset.DimensionIsVisible,
689                MdschemaHierarchiesRowset.HierarchyOrdinal,
690                MdschemaHierarchiesRowset.DimensionIsShared,
691                MdschemaHierarchiesRowset.ParentChild,
692            },
693            new Column[] {
694                MdschemaHierarchiesRowset.CatalogName,
695                MdschemaHierarchiesRowset.SchemaName,
696                MdschemaHierarchiesRowset.CubeName,
697                MdschemaHierarchiesRowset.DimensionUniqueName,
698                MdschemaHierarchiesRowset.HierarchyName,
699            }) {
700            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
701                return new MdschemaHierarchiesRowset(request, handler);
702            }
703        },
704    
705        /**
706         * http://msdn2.microsoft.com/en-us/library/ms126038(SQL.90).aspx
707         * http://msdn.microsoft.com/library/en-us/oledb/htm/olaplevels_rowset.asp
708         *
709         * restriction
710         *   CATALOG_NAME Optional.
711         *   SCHEMA_NAME Optional.
712         *   CUBE_NAME Optional.
713         *   DIMENSION_UNIQUE_NAME Optional.
714         *   HIERARCHY_UNIQUE_NAME Optional.
715         *   LEVEL_NAME Optional.
716         *   LEVEL_UNIQUE_NAME Optional.
717         *   LEVEL_ORIGIN
718         *       (Optional) A default restriction is in effect
719         *       on MD_USER_DEFINED and MD_SYSTEM_ENABLED
720         *   CUBE_SOURCE
721         *       (Optional) A bitmap with one of the following valid values:
722         *       1 CUBE
723         *       2 DIMENSION
724         *       Default restriction is a value of 1.
725         *   LEVEL_VISIBILITY
726         *       (Optional) A bitmap with one of the following values:
727         *       1 Visible
728         *       2 Not visible
729         *       Default restriction is a value of 1.
730         *
731         * Not supported
732         *  CUSTOM_ROLLUP_SETTINGS
733         *  LEVEL_UNIQUE_SETTINGS
734         *  LEVEL_ORDERING_PROPERTY
735         *  LEVEL_DBTYPE
736         *  LEVEL_MASTER_UNIQUE_NAME
737         *  LEVEL_NAME_SQL_COLUMN_NAME Customers:(All)!NAME
738         *  LEVEL_KEY_SQL_COLUMN_NAME Customers:(All)!KEY
739         *  LEVEL_UNIQUE_NAME_SQL_COLUMN_NAME Customers:(All)!UNIQUE_NAME
740         *  LEVEL_ATTRIBUTE_HIERARCHY_NAME
741         *  LEVEL_KEY_CARDINALITY
742         *  LEVEL_ORIGIN
743         *
744         */
745        MDSCHEMA_LEVELS(
746            16, null,
747            new Column[] {
748                MdschemaLevelsRowset.CatalogName,
749                MdschemaLevelsRowset.SchemaName,
750                MdschemaLevelsRowset.CubeName,
751                MdschemaLevelsRowset.DimensionUniqueName,
752                MdschemaLevelsRowset.HierarchyUniqueName,
753                MdschemaLevelsRowset.LevelName,
754                MdschemaLevelsRowset.LevelUniqueName,
755                MdschemaLevelsRowset.LevelGuid,
756                MdschemaLevelsRowset.LevelCaption,
757                MdschemaLevelsRowset.LevelNumber,
758                MdschemaLevelsRowset.LevelCardinality,
759                MdschemaLevelsRowset.LevelType,
760                MdschemaLevelsRowset.CustomRollupSettings,
761                MdschemaLevelsRowset.LevelUniqueSettings,
762                MdschemaLevelsRowset.LevelIsVisible,
763                MdschemaLevelsRowset.Description,
764            },
765            new Column[] {
766                MdschemaLevelsRowset.CatalogName,
767                MdschemaLevelsRowset.SchemaName,
768                MdschemaLevelsRowset.CubeName,
769                MdschemaLevelsRowset.DimensionUniqueName,
770                MdschemaLevelsRowset.HierarchyUniqueName,
771                MdschemaLevelsRowset.LevelNumber,
772            }) {
773            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
774                return new MdschemaLevelsRowset(request, handler);
775            }
776        },
777    
778        /**
779         * http://msdn2.microsoft.com/en-us/library/ms126250(SQL.90).aspx
780         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/olapmeasures_rowset.asp
781         *
782         * restrictions
783         *   CATALOG_NAME Optional.
784         *   SCHEMA_NAME Optional.
785         *   CUBE_NAME Optional.
786         *   MEASURE_NAME Optional.
787         *   MEASURE_UNIQUE_NAME Optional.
788         *   CUBE_SOURCE
789         *     (Optional) A bitmap with one of the following valid values:
790         *     1 CUBE
791         *     2 DIMENSION
792         *     Default restriction is a value of 1.
793         *   MEASURE_VISIBILITY
794         *     (Optional) A bitmap with one of the following valid values:
795         *     1 Visible
796         *     2 Not Visible
797         *     Default restriction is a value of 1.
798         *
799         * Not supported
800         *  MEASURE_GUID
801         *  NUMERIC_PRECISION
802         *  NUMERIC_SCALE
803         *  MEASURE_UNITS
804         *  EXPRESSION
805         *  MEASURE_NAME_SQL_COLUMN_NAME
806         *  MEASURE_UNQUALIFIED_CAPTION
807         *  MEASUREGROUP_NAME
808         *  MEASURE_DISPLAY_FOLDER
809         *  DEFAULT_FORMAT_STRING
810         */
811        MDSCHEMA_MEASURES(
812            17, null,
813            new Column[] {
814                MdschemaMeasuresRowset.CatalogName,
815                MdschemaMeasuresRowset.SchemaName,
816                MdschemaMeasuresRowset.CubeName,
817                MdschemaMeasuresRowset.MeasureName,
818                MdschemaMeasuresRowset.MeasureUniqueName,
819                MdschemaMeasuresRowset.MeasureCaption,
820                MdschemaMeasuresRowset.MeasureGuid,
821                MdschemaMeasuresRowset.MeasureAggregator,
822                MdschemaMeasuresRowset.DataType,
823                MdschemaMeasuresRowset.MeasureIsVisible,
824                MdschemaMeasuresRowset.LevelsList,
825                MdschemaMeasuresRowset.Description,
826            },
827            new Column[] {
828                MdschemaMeasuresRowset.CatalogName,
829                MdschemaMeasuresRowset.SchemaName,
830                MdschemaMeasuresRowset.CubeName,
831                MdschemaMeasuresRowset.MeasureName,
832            }) {
833            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
834                return new MdschemaMeasuresRowset(request, handler);
835            }
836        },
837    
838        /**
839         *
840         * http://msdn2.microsoft.com/es-es/library/ms126046.aspx
841         *
842         *
843         * restrictions
844         *   CATALOG_NAME Optional.
845         *   SCHEMA_NAME Optional.
846         *   CUBE_NAME Optional.
847         *   DIMENSION_UNIQUE_NAME Optional.
848         *   HIERARCHY_UNIQUE_NAME Optional.
849         *   LEVEL_UNIQUE_NAME Optional.
850         *   LEVEL_NUMBER Optional.
851         *   MEMBER_NAME Optional.
852         *   MEMBER_UNIQUE_NAME Optional.
853         *   MEMBER_CAPTION Optional.
854         *   MEMBER_TYPE Optional.
855         *   TREE_OP (Optional) Only applies to a single member:
856         *      MDTREEOP_ANCESTORS (0x20) returns all of the ancestors.
857         *      MDTREEOP_CHILDREN (0x01) returns only the immediate children.
858         *      MDTREEOP_SIBLINGS (0x02) returns members on the same level.
859         *      MDTREEOP_PARENT (0x04) returns only the immediate parent.
860         *      MDTREEOP_SELF (0x08) returns itself in the list of
861         *                 returned rows.
862         *      MDTREEOP_DESCENDANTS (0x10) returns all of the descendants.
863         *   CUBE_SOURCE (Optional) A bitmap with one of the
864         *      following valid values:
865         *        1 CUBE
866         *        2 DIMENSION
867         *      Default restriction is a value of 1.
868         *
869         * Not supported
870         */
871        MDSCHEMA_MEMBERS(
872            18, null,
873            new Column[] {
874                MdschemaMembersRowset.CatalogName,
875                MdschemaMembersRowset.SchemaName,
876                MdschemaMembersRowset.CubeName,
877                MdschemaMembersRowset.DimensionUniqueName,
878                MdschemaMembersRowset.HierarchyUniqueName,
879                MdschemaMembersRowset.LevelUniqueName,
880                MdschemaMembersRowset.LevelNumber,
881                MdschemaMembersRowset.MemberOrdinal,
882                MdschemaMembersRowset.MemberName,
883                MdschemaMembersRowset.MemberUniqueName,
884                MdschemaMembersRowset.MemberType,
885                MdschemaMembersRowset.MemberGuid,
886                MdschemaMembersRowset.MemberCaption,
887                MdschemaMembersRowset.ChildrenCardinality,
888                MdschemaMembersRowset.ParentLevel,
889                MdschemaMembersRowset.ParentUniqueName,
890                MdschemaMembersRowset.ParentCount,
891                MdschemaMembersRowset.TreeOp,
892                MdschemaMembersRowset.Depth,
893            },
894            new Column[] {
895                MdschemaMembersRowset.CatalogName,
896                MdschemaMembersRowset.SchemaName,
897                MdschemaMembersRowset.CubeName,
898                MdschemaMembersRowset.DimensionUniqueName,
899                MdschemaMembersRowset.HierarchyUniqueName,
900                MdschemaMembersRowset.LevelUniqueName,
901                MdschemaMembersRowset.LevelNumber,
902                MdschemaMembersRowset.MemberOrdinal,
903            }) {
904            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
905                return new MdschemaMembersRowset(request, handler);
906            }
907        },
908    
909        /**
910         * http://msdn2.microsoft.com/en-us/library/ms126309(SQL.90).aspx
911         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/olapproperties_rowset.asp
912         *
913         * restrictions
914         *    CATALOG_NAME Mandatory
915         *    SCHEMA_NAME Optional
916         *    CUBE_NAME Optional
917         *    DIMENSION_UNIQUE_NAME Optional
918         *    HIERARCHY_UNIQUE_NAME Optional
919         *    LEVEL_UNIQUE_NAME Optional
920         *
921         *    MEMBER_UNIQUE_NAME Optional
922         *    PROPERTY_NAME Optional
923         *    PROPERTY_TYPE Optional
924         *    PROPERTY_CONTENT_TYPE
925         *       (Optional) A default restriction is in place on MDPROP_MEMBER
926         *       OR MDPROP_CELL.
927         *    PROPERTY_ORIGIN
928         *       (Optional) A default restriction is in place on MD_USER_DEFINED
929         *       OR MD_SYSTEM_ENABLED
930         *    CUBE_SOURCE
931         *       (Optional) A bitmap with one of the following valid values:
932         *       1 CUBE
933         *       2 DIMENSION
934         *       Default restriction is a value of 1.
935         *    PROPERTY_VISIBILITY
936         *       (Optional) A bitmap with one of the following valid values:
937         *       1 Visible
938         *       2 Not visible
939         *       Default restriction is a value of 1.
940         *
941         * Not supported
942         *    PROPERTY_ORIGIN
943         *    CUBE_SOURCE
944         *    PROPERTY_VISIBILITY
945         *    CHARACTER_MAXIMUM_LENGTH
946         *    CHARACTER_OCTET_LENGTH
947         *    NUMERIC_PRECISION
948         *    NUMERIC_SCALE
949         *    DESCRIPTION
950         *    SQL_COLUMN_NAME
951         *    LANGUAGE
952         *    PROPERTY_ATTRIBUTE_HIERARCHY_NAME
953         *    PROPERTY_CARDINALITY
954         *    MIME_TYPE
955         *    PROPERTY_IS_VISIBLE
956         */
957        MDSCHEMA_PROPERTIES(
958            19, null,
959            new Column[] {
960                MdschemaPropertiesRowset.CatalogName,
961                MdschemaPropertiesRowset.SchemaName,
962                MdschemaPropertiesRowset.CubeName,
963                MdschemaPropertiesRowset.DimensionUniqueName,
964                MdschemaPropertiesRowset.HierarchyUniqueName,
965                MdschemaPropertiesRowset.LevelUniqueName,
966                MdschemaPropertiesRowset.MemberUniqueName,
967                MdschemaPropertiesRowset.PropertyName,
968                MdschemaPropertiesRowset.PropertyCaption,
969                MdschemaPropertiesRowset.PropertyType,
970                MdschemaPropertiesRowset.DataType,
971                MdschemaPropertiesRowset.PropertyContentType,
972                MdschemaPropertiesRowset.Description
973            },
974            null /* not sorted */) {
975            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
976                return new MdschemaPropertiesRowset(request, handler);
977            }
978        },
979    
980        /**
981         * http://msdn2.microsoft.com/en-us/library/ms126290(SQL.90).aspx
982         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/olapproperties_rowset.asp
983         *
984         * restrictions
985         *    CATALOG_NAME Optional.
986         *    SCHEMA_NAME Optional.
987         *    CUBE_NAME Optional.
988         *    SET_NAME Optional.
989         *    SCOPE Optional.
990         *    HIERARCHY_UNIQUE_NAME Optional.
991         *    CUBE_SOURCE Optional.
992         *        Note: Only one hierarchy can be included, and only those named
993         *        sets whose hierarchies exactly match the restriction are
994         *        returned.
995         *
996         * Not supported
997         *    EXPRESSION
998         *    DIMENSIONS
999         *    SET_DISPLAY_FOLDER
1000         */
1001        MDSCHEMA_SETS(
1002            20, null,
1003            new Column[] {
1004                MdschemaSetsRowset.CatalogName,
1005                MdschemaSetsRowset.SchemaName,
1006                MdschemaSetsRowset.CubeName,
1007                MdschemaSetsRowset.SetName,
1008                MdschemaSetsRowset.Scope,
1009            },
1010            new Column[] {
1011                MdschemaSetsRowset.CatalogName,
1012                MdschemaSetsRowset.SchemaName,
1013                MdschemaSetsRowset.CubeName,
1014            }) {
1015            public Rowset getRowset(XmlaRequest request, XmlaHandler handler) {
1016                return new MdschemaSetsRowset(request, handler);
1017            }
1018        };
1019    
1020        private static final boolean EMIT_INVISIBLE_MEMBERS = true;
1021    
1022        transient final Column[] columnDefinitions;
1023        transient final Column[] sortColumnDefinitions;
1024    
1025        /**
1026         * Date the schema was last modified.
1027         *
1028         * <p>TODO: currently schema grammar does not support modify date
1029         * so we return just some date for now.
1030         */
1031        private static final String dateModified = "2005-01-25T17:35:32";
1032        private final String description;
1033    
1034        /**
1035         * Creates a rowset definition.
1036         *
1037         * @param ordinal Rowset ordinal, per OLE DB for OLAP
1038         * @param description Description
1039         * @param columnDefinitions List of column definitions
1040         * @param sortColumnDefinitions List of column definitions to sort on,
1041         */
1042        RowsetDefinition(
1043            int ordinal,
1044            String description,
1045            Column[] columnDefinitions,
1046            Column[] sortColumnDefinitions)
1047        {
1048            Util.discard(ordinal);
1049            this.description = description;
1050            this.columnDefinitions = columnDefinitions;
1051            this.sortColumnDefinitions = sortColumnDefinitions;
1052        }
1053    
1054        public abstract Rowset getRowset(XmlaRequest request, XmlaHandler handler);
1055    
1056        public Column lookupColumn(String name) {
1057            for (Column columnDefinition : columnDefinitions) {
1058                if (columnDefinition.name.equals(name)) {
1059                    return columnDefinition;
1060                }
1061            }
1062            return null;
1063        }
1064    
1065        /**
1066         * Returns a comparator with which to sort rows of this rowset definition.
1067         * The sort order is defined by the {@link #sortColumnDefinitions} field.
1068         * If the rowset is not sorted, returns null.
1069         */
1070        Comparator<Rowset.Row> getComparator() {
1071            if (sortColumnDefinitions == null) {
1072                return null;
1073            }
1074            return new Comparator<Rowset.Row>() {
1075                public int compare(Rowset.Row row1, Rowset.Row row2) {
1076                    // A faster implementation is welcome.
1077                    for (Column sortColumn : sortColumnDefinitions) {
1078                        Comparable val1 = (Comparable) row1.get(sortColumn.name);
1079                        Comparable val2 = (Comparable) row2.get(sortColumn.name);
1080                        if ((val1 == null) && (val2 == null)) {
1081                            // columns can be optional, compare next column
1082                            continue;
1083                        } else if (val1 == null) {
1084                            return -1;
1085                        } else if (val2 == null) {
1086                            return 1;
1087                        } else if (val1 instanceof String &&
1088                            val2 instanceof String) {
1089                            int v =
1090                                ((String) val1).compareToIgnoreCase((String) val2);
1091                            // if equal (= 0), compare next column
1092                            if (v != 0) {
1093                                return v;
1094                            }
1095                        } else {
1096                            int v = val1.compareTo(val2);
1097                            // if equal (= 0), compare next column
1098                            if (v != 0) {
1099                                return v;
1100                            }
1101                        }
1102                    }
1103                    return 0;
1104                }
1105            };
1106        }
1107    
1108        /**
1109         * Generates an XML schema description to the writer.
1110         * This is broken into top, Row definition and bottom so that on a
1111         * case by case basis a RowsetDefinition can redefine the Row
1112         * definition output. The default assumes a flat set of elements, but
1113         * for example, SchemaRowsets has a element with child elements.
1114         *
1115         * @param writer SAX writer
1116         * @see XmlaHandler#writeDatasetXmlSchema(SaxWriter, mondrian.xmla.XmlaHandler.SetType)
1117         */
1118        void writeRowsetXmlSchema(SaxWriter writer) {
1119            writeRowsetXmlSchemaTop(writer);
1120            writeRowsetXmlSchemaRowDef(writer);
1121            writeRowsetXmlSchemaBottom(writer);
1122        }
1123    
1124        protected void writeRowsetXmlSchemaTop(SaxWriter writer) {
1125            writer.startElement("xsd:schema", new String[] {
1126                "xmlns:xsd", XmlaConstants.NS_XSD,
1127                "xmlns", XmlaConstants.NS_XMLA_ROWSET,
1128                "xmlns:xsi", XmlaConstants.NS_XSI,
1129                "xmlns:sql", "urn:schemas-microsoft-com:xml-sql",
1130                "targetNamespace", XmlaConstants.NS_XMLA_ROWSET,
1131                "elementFormDefault", "qualified"
1132            });
1133    
1134            writer.startElement("xsd:element", new String[] {
1135                "name", "root"
1136            });
1137            writer.startElement("xsd:complexType");
1138            writer.startElement("xsd:sequence");
1139            writer.element("xsd:element", new String[] {
1140                "name", "row",
1141                "type", "row",
1142                "minOccurs", "0",
1143                "maxOccurs", "unbounded"
1144            });
1145            writer.endElement(); // xsd:sequence
1146            writer.endElement(); // xsd:complexType
1147            writer.endElement(); // xsd:element
1148    
1149            // MS SQL includes this in its schema section even thought
1150            // its not need for most queries.
1151            writer.startElement("xsd:simpleType", new String[] {
1152                "name", "uuid"
1153            });
1154            writer.startElement("xsd:restriction", new String[] {
1155                "base", "xsd:string"
1156            });
1157            writer.element("xsd:pattern", new String[] {
1158                "value", "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"
1159            });
1160    
1161            writer.endElement(); // xsd:restriction
1162            writer.endElement(); // xsd:simpleType
1163    
1164        }
1165    
1166        protected void writeRowsetXmlSchemaRowDef(SaxWriter writer) {
1167            writer.startElement("xsd:complexType", new String[] {
1168                "name", "row"
1169            });
1170            writer.startElement("xsd:sequence");
1171            for (Column column : columnDefinitions) {
1172                final String name = XmlaUtil.encodeElementName(column.name);
1173                final String xsdType = column.type.columnType;
1174    
1175                String[] attrs;
1176                if (column.nullable) {
1177                    if (column.unbounded) {
1178                        attrs = new String[]{
1179                            "sql:field", column.name,
1180                            "name", name,
1181                            "type", xsdType,
1182                            "minOccurs", "0",
1183                            "maxOccurs", "unbounded"
1184                        };
1185                    } else {
1186                        attrs = new String[]{
1187                            "sql:field", column.name,
1188                            "name", name,
1189                            "type", xsdType,
1190                            "minOccurs", "0"
1191                        };
1192                    }
1193                } else {
1194                    if (column.unbounded) {
1195                        attrs = new String[]{
1196                            "sql:field", column.name,
1197                            "name", name,
1198                            "type", xsdType,
1199                            "maxOccurs", "unbounded"
1200                        };
1201                    } else {
1202                        attrs = new String[]{
1203                            "sql:field", column.name,
1204                            "name", name,
1205                            "type", xsdType
1206                        };
1207                    }
1208                }
1209                writer.element("xsd:element", attrs);
1210            }
1211            writer.endElement(); // xsd:sequence
1212            writer.endElement(); // xsd:complexType
1213        }
1214    
1215        protected void writeRowsetXmlSchemaBottom(SaxWriter writer) {
1216            writer.endElement(); // xsd:schema
1217        }
1218    
1219        enum Type {
1220            String("xsd:string"),
1221            StringArray("xsd:string"),
1222            Array("xsd:string"),
1223            Enumeration("xsd:string"),
1224            EnumerationArray("xsd:string"),
1225            EnumString("xsd:string"),
1226            Boolean("xsd:boolean"),
1227            StringSometimesArray("xsd:string"),
1228            Integer("xsd:int"),
1229            UnsignedInteger("xsd:unsignedInt"),
1230            DateTime("xsd:dateTime"),
1231            Short("xsd:short"),
1232            UUID("uuid"),
1233            UnsignedShort("xsd:unsignedShort"),
1234            Long("xsd:long"),
1235            UnsignedLong("xsd:unsignedLong");
1236    
1237            public final String columnType;
1238    
1239            Type(String columnType) {
1240                this.columnType = columnType;
1241            }
1242    
1243            boolean isEnum() {
1244                return this == Enumeration ||
1245                    this == EnumerationArray ||
1246                    this == EnumString;
1247            }
1248    
1249            String getName() {
1250                return this == String ? "string" : name();
1251            }
1252        }
1253    
1254        private static DBType getDBTypeFromProperty(Property prop) {
1255            switch (prop.getType()) {
1256            case TYPE_STRING:
1257                return DBType.WSTR;
1258            case TYPE_NUMERIC:
1259                return DBType.R8;
1260            case TYPE_BOOLEAN:
1261                return DBType.BOOL;
1262            case TYPE_OTHER:
1263            default:
1264                // TODO: what type is it really, its not a string
1265                return DBType.WSTR;
1266            }
1267        }
1268    
1269        /**
1270         * The only OLE DB Types Indicators returned by SQL Server are thoses coded
1271         * below.
1272         * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbtype_indicators.asp
1273         */
1274    
1275        enum DBType {
1276            /*
1277            * The following values exactly match VARENUM
1278            * in Automation and may be used in VARIANT.
1279            */
1280            I4("INTEGER", 3, "DBTYPE_I4", "A four-byte, signed integer: INTEGER"),
1281    
1282            R8("DOUBLE", 5,
1283                "DBTYPE_R8", "A double-precision floating-point value: Double"),
1284    
1285            CY("CURRENCY", 6, "DBTYPE_CY", "A currency value: LARGE_INTEGER, Currency is a fixed-point number with four digits to the right of the decimal point. It is stored in an eight-byte signed integer, scaled by 10,000."),
1286    
1287            BOOL("BOOLEAN", 11, "DBTYPE_BOOL", "A Boolean value stored in the same way as in Automation: VARIANT_BOOL; 0 means false and ~0 (bitwise, the value is not 0; that is, all bits are set to 1) means true."),
1288    
1289            /**
1290             * Used by SQL Server for value.
1291             */
1292            VARIANT("VARIANT", 12, "DBTYPE_VARIANT", "An Automation VARIANT"),
1293    
1294            /**
1295             * Used by SQL Server for font size.
1296             */
1297            UI2("UNSIGNED_SHORT", 18, "DBTYPE_UI2", "A two-byte, unsigned integer"),
1298    
1299            /**
1300             * Used by SQL Server for colors, font flags and cell ordinal.
1301             */
1302            UI4("UNSIGNED_INTEGER", 19, "DBTYPE_UI4", "A four-byte, unsigned integer"),
1303    
1304            /*
1305            * The following values exactly match VARENUM
1306            * in Automation but cannot be used in VARIANT.
1307            */
1308            I8("LARGE_INTEGER", 20, "DBTYPE_I8", "An eight-byte, signed integer: LARGE_INTEGER"),
1309    
1310            /*
1311            * The following values are not in VARENUM in OLE.
1312            */
1313            WSTR("STRING", 130, "DBTYPE_WSTR", "A null-terminated Unicode character string: wchar_t[length]; If DBTYPE_WSTR is used by itself, the number of bytes allocated for the string, including the null-termination character, is specified by cbMaxLen in the DBBINDING structure. If DBTYPE_WSTR is combined with DBTYPE_BYREF, the number of bytes allocated for the string, including the null-termination character, is at least the length of the string plus two. In either case, the actual length of the string is determined from the bound length value. The maximum length of the string is the number of allocated bytes divided by sizeof(wchar_t) and truncated to the nearest integer.");
1314    
1315    
1316            /**
1317             * The length of a non-numeric column or parameter that refers to either
1318             * the maximum or the length defined for this type by the provider. For
1319             * character data, this is the maximum or defined length in characters.
1320             * For DateTime data types, this is the length of the string
1321             * representation (assuming the maximum allowed precision of the
1322             * fractional seconds component).
1323             *
1324             * If the data type is numeric, this is the upper bound on the maximum
1325             * precision of the data type.
1326             int columnSize;
1327             */
1328    
1329            private final String userName;
1330            private final int userOrdinal;
1331            /**
1332             *  A Boolean that indicates whether the data type is nullable.
1333             *  VARIANT_TRUE indicates that the data type is nullable.
1334             *  VARIANT_FALSE indicates that the data type is not nullable.
1335             *  NULL-- indicates that it is not known whether the data type is
1336             *  nullable.
1337             boolean isNullable;
1338             */
1339    
1340            String dbTypeIndicator;
1341    
1342            DBType(
1343                String userName,
1344                int userOrdinal,
1345                String dbTypeIndicator,
1346                String description)
1347            {
1348                this.userName = userName;
1349                this.userOrdinal = userOrdinal;
1350                Util.discard(description);
1351                this.dbTypeIndicator = dbTypeIndicator;
1352            }
1353    
1354        }
1355    
1356        static class Column {
1357    
1358            /**
1359             * This is used as the true value for the restriction parameter.
1360             */
1361            static final boolean RESTRICTION = true;
1362            /**
1363             * This is used as the false value for the restriction parameter.
1364             */
1365            static final boolean NOT_RESTRICTION = false;
1366    
1367            /**
1368             * This is used as the false value for the nullable parameter.
1369             */
1370            static final boolean REQUIRED = false;
1371            /**
1372             * This is used as the true value for the nullable parameter.
1373             */
1374            static final boolean OPTIONAL = true;
1375    
1376            /**
1377             * This is used as the false value for the unbounded parameter.
1378             */
1379            static final boolean ONE_MAX = false;
1380            /**
1381             * This is used as the true value for the unbounded parameter.
1382             */
1383            static final boolean UNBOUNDED = true;
1384    
1385            final String name;
1386            final Type type;
1387            final Enumeration enumeration;
1388            final String description;
1389            final boolean restriction;
1390            final boolean nullable;
1391            final boolean unbounded;
1392    
1393            /**
1394             * Creates a column.
1395             *
1396             * @param name Name of column
1397             * @param type           A {@link mondrian.xmla.RowsetDefinition.Type} value
1398             * @param enumeratedType Must be specified for enumeration or array
1399             *                       of enumerations
1400             * @param description Description of column
1401             * @param restriction Whether column can be used as a filter on its
1402             *     rowset
1403             * @param nullable Whether column can contain null values
1404             * @pre type != null
1405             * @pre (type == Type.Enumeration || type == Type.EnumerationArray || type == Type.EnumString) == (enumeratedType != null)
1406             * @pre description == null || description.indexOf('\r') == -1
1407             */
1408            Column(
1409                String name,
1410                Type type,
1411                Enumeration enumeratedType,
1412                boolean restriction,
1413                boolean nullable,
1414                String description)
1415            {
1416                this(
1417                    name, type, enumeratedType,
1418                    restriction, nullable, ONE_MAX, description);
1419            }
1420    
1421            Column(
1422                String name,
1423                Type type,
1424                Enumeration enumeratedType,
1425                boolean restriction,
1426                boolean nullable,
1427                boolean unbounded,
1428                String description)
1429            {
1430                assert type != null;
1431                assert (type == Type.Enumeration ||
1432                    type == Type.EnumerationArray ||
1433                    type == Type.EnumString) ==
1434                    (enumeratedType != null);
1435                // Line endings must be UNIX style (LF) not Windows style (LF+CR).
1436                // Thus the client will receive the same XML, regardless
1437                // of the server O/S.
1438                assert description == null || description.indexOf('\r') == -1;
1439                this.name = name;
1440                this.type = type;
1441                this.enumeration = enumeratedType;
1442                this.description = description;
1443                this.restriction = restriction;
1444                this.nullable = nullable;
1445                this.unbounded = unbounded;
1446            }
1447    
1448            /**
1449             * Retrieves a value of this column from a row. The base implementation
1450             * uses reflection to call an accessor method; a derived class may
1451             * provide a different implementation.
1452             *
1453             * @param row Row
1454             */
1455            protected Object get(Object row) {
1456                return getFromAccessor(row);
1457            }
1458    
1459            /**
1460             * Retrieves the value of this column "MyColumn" from a field called
1461             * "myColumn".
1462             *
1463             * @param row Current row
1464             * @return Value of given this property of the given row
1465             */
1466            protected final Object getFromField(Object row) {
1467                try {
1468                    String javaFieldName = name.substring(0, 1).toLowerCase() +
1469                        name.substring(1);
1470                    Field field = row.getClass().getField(javaFieldName);
1471                    return field.get(row);
1472                } catch (NoSuchFieldException e) {
1473                    throw Util.newInternal(e, "Error while accessing rowset column " + name);
1474                } catch (SecurityException e) {
1475                    throw Util.newInternal(e, "Error while accessing rowset column " + name);
1476                } catch (IllegalAccessException e) {
1477                    throw Util.newInternal(e, "Error while accessing rowset column " + name);
1478                }
1479            }
1480    
1481            /**
1482             * Retrieves the value of this column "MyColumn" by calling a method
1483             * called "getMyColumn()".
1484             *
1485             * @param row Current row
1486             * @return Value of given this property of the given row
1487             */
1488            protected final Object getFromAccessor(Object row) {
1489                try {
1490                    String javaMethodName = "get" + name;
1491                    Method method = row.getClass().getMethod(javaMethodName);
1492                    return method.invoke(row);
1493                } catch (SecurityException e) {
1494                    throw Util.newInternal(e, "Error while accessing rowset column " + name);
1495                } catch (IllegalAccessException e) {
1496                    throw Util.newInternal(e, "Error while accessing rowset column " + name);
1497                } catch (NoSuchMethodException e) {
1498                    throw Util.newInternal(e, "Error while accessing rowset column " + name);
1499                } catch (InvocationTargetException e) {
1500                    throw Util.newInternal(e, "Error while accessing rowset column " + name);
1501                }
1502            }
1503    
1504            public String getColumnType() {
1505                if (type.isEnum()) {
1506                    return enumeration.type.columnType;
1507                }
1508                return type.columnType;
1509            }
1510        }
1511    
1512        // -------------------------------------------------------------------------
1513        // From this point on, just rowset classess.
1514    
1515        static class DiscoverDatasourcesRowset extends Rowset {
1516            private static final Column DataSourceName =
1517                new Column(
1518                    "DataSourceName",
1519                    Type.String,
1520                    null,
1521                    Column.RESTRICTION,
1522                    Column.REQUIRED,
1523                    "The name of the data source, such as FoodMart 2000.");
1524            private static final Column DataSourceDescription =
1525                new Column(
1526                    "DataSourceDescription",
1527                    Type.String,
1528                    null,
1529                    Column.NOT_RESTRICTION,
1530                    Column.OPTIONAL,
1531                    "A description of the data source, as entered by the publisher.");
1532            private static final Column URL =
1533                new Column(
1534                    "URL",
1535                    Type.String,
1536                    null,
1537                    Column.RESTRICTION,
1538                    Column.OPTIONAL,
1539                    "The unique path that shows where to invoke the XML for Analysis methods for that data source.");
1540            private static final Column DataSourceInfo =
1541                new Column(
1542                    "DataSourceInfo",
1543                    Type.String,
1544                    null,
1545                    Column.NOT_RESTRICTION,
1546                    Column.OPTIONAL,
1547                    "A string containing any additional information required to connect to the data source. This can include the Initial Catalog property or other information for the provider.\n" +
1548                        "Example: \"Provider=MSOLAP;Data Source=Local;\"");
1549            private static final Column ProviderName =
1550                new Column(
1551                    "ProviderName",
1552                    Type.String,
1553                    null,
1554                    Column.RESTRICTION,
1555                    Column.OPTIONAL,
1556                    "The name of the provider behind the data source. \n" +
1557                        "Example: \"MSDASQL\"");
1558            private static final Column ProviderType =
1559                new Column(
1560                    "ProviderType",
1561                    Type.EnumerationArray,
1562                    Enumeration.ProviderType.enumeration,
1563                    Column.RESTRICTION,
1564                    Column.REQUIRED,
1565                    Column.UNBOUNDED,
1566                    "The types of data supported by the provider. May include one or more of the following types. Example follows this table.\n" +
1567                        "TDP: tabular data provider.\n" +
1568                        "MDP: multidimensional data provider.\n" +
1569                        "DMP: data mining provider. A DMP provider implements the OLE DB for Data Mining specification.");
1570            private static final Column AuthenticationMode =
1571                new Column(
1572                    "AuthenticationMode",
1573                    Type.EnumString,
1574                    Enumeration.AuthenticationMode.enumeration,
1575                    Column.RESTRICTION,
1576                    Column.REQUIRED,
1577                    "Specification of what type of security mode the data source uses. Values can be one of the following:\n" +
1578                        "Unauthenticated: no user ID or password needs to be sent.\n" +
1579                        "Authenticated: User ID and Password must be included in the information required for the connection.\n" +
1580                        "Integrated: the data source uses the underlying security to determine authorization, such as Integrated Security provided by Microsoft Internet Information Services (IIS).");
1581    
1582            public DiscoverDatasourcesRowset(XmlaRequest request, XmlaHandler handler) {
1583                super(DISCOVER_DATASOURCES, request, handler);
1584            }
1585    
1586            public void populate(
1587                XmlaResponse response,
1588                List<Row> rows)
1589                throws XmlaException
1590            {
1591                for (DataSourcesConfig.DataSource ds : handler
1592                    .getDataSourceEntries().values()) {
1593                    Row row = new Row();
1594                    row.set(DataSourceName.name, ds.getDataSourceName());
1595                    row.set(DataSourceDescription.name,
1596                        ds.getDataSourceDescription());
1597                    row.set(URL.name, ds.getURL());
1598                    row.set(DataSourceInfo.name, ds.getDataSourceName());
1599                    row.set(ProviderName.name, ds.getProviderName());
1600                    row.set(ProviderType.name, ds.getProviderType());
1601                    row.set(AuthenticationMode.name, ds.getAuthenticationMode());
1602                    addRow(row, rows);
1603                }
1604            }
1605    
1606            protected void setProperty(
1607                PropertyDefinition propertyDef,
1608                String value)
1609            {
1610                switch (propertyDef) {
1611                case Content:
1612                    break;
1613                default:
1614                    super.setProperty(propertyDef, value);
1615                }
1616            }
1617        }
1618    
1619        static class DiscoverSchemaRowsetsRowset extends Rowset {
1620            private static final Column SchemaName =
1621                new Column(
1622                    "SchemaName",
1623                    Type.StringArray,
1624                    null,
1625                    Column.RESTRICTION,
1626                    Column.REQUIRED,
1627                    "The name of the schema/request. This returns the values in the RequestTypes enumeration, plus any additional types supported by the provider. The provider defines rowset structures for the additional types");
1628            private static final Column SchemaGuid =
1629                new Column(
1630                    "SchemaGuid",
1631                    Type.UUID,
1632                    null,
1633                    Column.NOT_RESTRICTION,
1634                    Column.OPTIONAL,
1635                    "The GUID of the schema.");
1636            private static final Column Restrictions =
1637                new Column(
1638                    "Restrictions",
1639                    Type.Array,
1640                    null,
1641                    Column.NOT_RESTRICTION,
1642                    Column.REQUIRED,
1643                    "An array of the restrictions suppoted by provider. An example follows this table.");
1644            private static final Column Description =
1645                new Column(
1646                    "Description",
1647                    Type.String,
1648                    null,
1649                    Column.NOT_RESTRICTION,
1650                    Column.REQUIRED,
1651                    "A localizable description of the schema");
1652    
1653            public DiscoverSchemaRowsetsRowset(XmlaRequest request, XmlaHandler handler) {
1654                super(DISCOVER_SCHEMA_ROWSETS, request, handler);
1655            }
1656    
1657            public void populate(
1658                XmlaResponse response,
1659                List<Row> rows)
1660                throws XmlaException
1661            {
1662                RowsetDefinition[] rowsetDefinitions =
1663                    RowsetDefinition.class.getEnumConstants().clone();
1664                Arrays.sort(
1665                    rowsetDefinitions,
1666                    new Comparator<Enum>() {
1667                        public int compare(Enum o1, Enum o2) {
1668                            return o1.name().compareTo(o2.name());
1669                        }
1670                    });
1671                for (RowsetDefinition rowsetDefinition : rowsetDefinitions) {
1672                    Row row = new Row();
1673                    row.set(SchemaName.name, rowsetDefinition.name());
1674    
1675                    // TODO: If we have a SchemaGuid output here
1676                    //row.set(SchemaGuid.name, "");
1677    
1678                    row.set(Restrictions.name, getRestrictions(rowsetDefinition));
1679    
1680                    String desc = rowsetDefinition.getDescription();
1681                    row.set(Description.name, (desc == null) ? "" : desc);
1682                    addRow(row, rows);
1683                }
1684            }
1685    
1686            private List<XmlElement> getRestrictions(RowsetDefinition rowsetDefinition) {
1687                List<XmlElement> restrictionList = new ArrayList<XmlElement>();
1688                final Column[] columns = rowsetDefinition.columnDefinitions;
1689                for (Column column : columns) {
1690                    if (column.restriction) {
1691                        restrictionList.add(
1692                            new XmlElement(Restrictions.name,
1693                                null,
1694                                new XmlElement[]{
1695                                    new XmlElement("Name", null, column.name),
1696                                    new XmlElement("Type",
1697                                        null,
1698                                        column.getColumnType())
1699                                }));
1700                    }
1701                }
1702                return restrictionList;
1703            }
1704    
1705            protected void setProperty(PropertyDefinition propertyDef, String value) {
1706                switch (propertyDef) {
1707                case Content:
1708                    break;
1709                default:
1710                    super.setProperty(propertyDef, value);
1711                }
1712            }
1713        }
1714    
1715        public String getDescription() {
1716            return description;
1717        }
1718    
1719        static class DiscoverPropertiesRowset extends Rowset {
1720            private final RestrictionTest propertyNameRT;
1721    
1722            DiscoverPropertiesRowset(XmlaRequest request, XmlaHandler handler) {
1723                super(DISCOVER_PROPERTIES, request, handler);
1724                propertyNameRT = getRestrictionTest(PropertyName);
1725            }
1726    
1727            private static final Column PropertyName =
1728                new Column(
1729                    "PropertyName",
1730                    Type.StringSometimesArray,
1731                    null,
1732                    Column.RESTRICTION,
1733                    Column.REQUIRED,
1734                    "The name of the property.");
1735            private static final Column PropertyDescription =
1736                new Column(
1737                    "PropertyDescription",
1738                    Type.String,
1739                    null,
1740                    Column.NOT_RESTRICTION,
1741                    Column.REQUIRED,
1742                    "A localizable text description of the property.");
1743            private static final Column PropertyType =
1744                new Column(
1745                    "PropertyType",
1746                    Type.String,
1747                    null,
1748                    Column.NOT_RESTRICTION,
1749                    Column.REQUIRED,
1750                    "The XML data type of the property.");
1751            private static final Column PropertyAccessType =
1752                new Column(
1753                    "PropertyAccessType",
1754                    Type.EnumString,
1755                    Enumeration.Access.enumeration,
1756                    Column.NOT_RESTRICTION,
1757                    Column.REQUIRED,
1758                    "Access for the property. The value can be Read, Write, or ReadWrite.");
1759            private static final Column IsRequired =
1760                new Column(
1761                    "IsRequired",
1762                    Type.Boolean,
1763                    null,
1764                    Column.NOT_RESTRICTION,
1765                    Column.REQUIRED,
1766                    "True if a property is required, false if it is not required.");
1767            private static final Column Value =
1768                new Column(
1769                    "Value",
1770                    Type.String,
1771                    null,
1772                    Column.NOT_RESTRICTION,
1773                    Column.REQUIRED,
1774                    "The current value of the property.");
1775    
1776            public void populate(
1777                XmlaResponse response,
1778                List<Row> rows)
1779                throws XmlaException
1780            {
1781                for (PropertyDefinition propertyDefinition :
1782                    PropertyDefinition.class.getEnumConstants()) {
1783                    if (!propertyNameRT.passes(propertyDefinition.name())) {
1784                        continue;
1785                    }
1786                    Row row = new Row();
1787                    row.set(PropertyName.name, propertyDefinition.name());
1788                    row.set(PropertyDescription.name, propertyDefinition.description);
1789                    row.set(PropertyType.name, propertyDefinition.type.getName());
1790                    row.set(PropertyAccessType.name, propertyDefinition.access);
1791                    row.set(IsRequired.name, false);
1792                    row.set(Value.name, propertyDefinition.value);
1793                    addRow(row, rows);
1794                }
1795            }
1796    
1797            protected void setProperty(PropertyDefinition propertyDef, String value) {
1798                switch (propertyDef) {
1799                case Content:
1800                    break;
1801                default:
1802                    super.setProperty(propertyDef, value);
1803                }
1804            }
1805        }
1806    
1807        static class DiscoverEnumeratorsRowset extends Rowset {
1808            DiscoverEnumeratorsRowset(XmlaRequest request, XmlaHandler handler) {
1809                super(DISCOVER_ENUMERATORS, request, handler);
1810            }
1811    
1812            private static final Column EnumName =
1813                new Column(
1814                    "EnumName",
1815                    Type.StringArray,
1816                    null,
1817                    Column.RESTRICTION,
1818                    Column.REQUIRED,
1819                    "The name of the enumerator that contains a set of values.");
1820            private static final Column EnumDescription =
1821                new Column(
1822                    "EnumDescription",
1823                    Type.String,
1824                    null,
1825                    Column.NOT_RESTRICTION,
1826                    Column.OPTIONAL,
1827                    "A localizable description of the enumerator.");
1828            private static final Column EnumType =
1829                new Column(
1830                    "EnumType",
1831                    Type.String,
1832                    null,
1833                    Column.NOT_RESTRICTION,
1834                    Column.REQUIRED,
1835                    "The data type of the Enum values.");
1836            private static final Column ElementName =
1837                new Column(
1838                    "ElementName",
1839                    Type.String,
1840                    null,
1841                    Column.NOT_RESTRICTION,
1842                    Column.REQUIRED,
1843                    "The name of one of the value elements in the enumerator set.\n" + "Example: TDP");
1844            private static final Column ElementDescription =
1845                new Column(
1846                    "ElementDescription",
1847                    Type.String,
1848                    null,
1849                    Column.NOT_RESTRICTION,
1850                    Column.OPTIONAL,
1851                    "A localizable description of the element (optional).");
1852            private static final Column ElementValue =
1853                new Column(
1854                    "ElementValue",
1855                    Type.String,
1856                    null,
1857                    Column.NOT_RESTRICTION,
1858                    Column.OPTIONAL,
1859                    "The value of the element.\n" + "Example: 01");
1860    
1861            public void populate(
1862                XmlaResponse response,
1863                List<Row> rows)
1864                throws XmlaException
1865            {
1866                List<Enumeration> enumerators = getEnumerators();
1867                for (Enumeration enumerator : enumerators) {
1868                    final String[] valueNames = enumerator.getNames();
1869                    for (String valueName : valueNames) {
1870                        final Enum<?> value = enumerator.getValue(valueName, true);
1871                        Row row = new Row();
1872                        row.set(EnumName.name, enumerator.name);
1873                        row.set(EnumDescription.name, enumerator.description);
1874    
1875                        // Note: SQL Server always has EnumType string
1876                        // Need type of element of array, not the array
1877                        // it self.
1878                        row.set(EnumType.name, "string");
1879    
1880                        String name =
1881                            value instanceof Enumeration.EnumWithName ?
1882                                ((Enumeration.EnumWithName) value).userName() :
1883                                value.name();
1884                        row.set(ElementName.name, name);
1885    
1886                        if (value instanceof Enumeration.EnumWithDesc) {
1887                            String description =
1888                                ((Enumeration.EnumWithDesc) value).getDescription();
1889                            row.set(ElementDescription.name, description);
1890                        }
1891    
1892                        switch (enumerator.type) {
1893                        case String:
1894                        case StringArray:
1895                            // these don't have ordinals
1896                            break;
1897                        default:
1898                            int ordinal =
1899                                value instanceof Enumeration.EnumWithOrdinal ?
1900                                    ((Enumeration.EnumWithOrdinal) value).userOrdinal() :
1901                                    value.ordinal();
1902                            row.set(ElementValue.name, ordinal);
1903                            break;
1904                        }
1905                        addRow(row, rows);
1906                    }
1907                }
1908            }
1909    
1910            private static List<Enumeration> getEnumerators() {
1911                SortedSet<Enumeration> enumeratorSet = new TreeSet<Enumeration>(
1912                    new Comparator<Enumeration>() {
1913                        public int compare(Enumeration o1, Enumeration o2) {
1914                            return o1.name.compareTo(o2.name);
1915                        }
1916                    }
1917                );
1918                for (RowsetDefinition rowsetDefinition : RowsetDefinition.class.getEnumConstants()) {
1919                    for (Column column : rowsetDefinition.columnDefinitions) {
1920                        if (column.enumeration != null) {
1921                            enumeratorSet.add(column.enumeration);
1922                        }
1923                    }
1924                }
1925                return new ArrayList<Enumeration>(enumeratorSet);
1926            }
1927    
1928            protected void setProperty(PropertyDefinition propertyDef, String value) {
1929                switch (propertyDef) {
1930                case Content:
1931                    break;
1932                default:
1933                    super.setProperty(propertyDef, value);
1934                }
1935            }
1936        }
1937    
1938        static class DiscoverKeywordsRowset extends Rowset {
1939            DiscoverKeywordsRowset(XmlaRequest request, XmlaHandler handler) {
1940                super(DISCOVER_KEYWORDS, request, handler);
1941            }
1942    
1943            private static final Column Keyword =
1944                new Column(
1945                    "Keyword",
1946                    Type.StringSometimesArray,
1947                    null,
1948                    Column.RESTRICTION,
1949                    Column.REQUIRED,
1950                    "A list of all the keywords reserved by a provider.\n" +
1951                        "Example: AND");
1952    
1953            public void populate(
1954                XmlaResponse response,
1955                List<Row> rows)
1956                throws XmlaException
1957            {
1958                MondrianServer mondrianServer = MondrianServer.forConnection(null);
1959                for (String keyword : mondrianServer.getKeywords()) {
1960                    Row row = new Row();
1961                    row.set(Keyword.name, keyword);
1962                    addRow(row, rows);
1963                }
1964            }
1965    
1966            protected void setProperty(
1967                PropertyDefinition propertyDef,
1968                String value)
1969            {
1970                switch (propertyDef) {
1971                case Content:
1972                    break;
1973                default:
1974                    super.setProperty(propertyDef, value);
1975                }
1976            }
1977        }
1978    
1979        static class DiscoverLiteralsRowset extends Rowset {
1980            DiscoverLiteralsRowset(XmlaRequest request, XmlaHandler handler) {
1981                super(DISCOVER_LITERALS, request, handler);
1982            }
1983    
1984            private static final Column LiteralName = new Column(
1985                "LiteralName",
1986                Type.StringSometimesArray,
1987                null,
1988                Column.RESTRICTION,
1989                Column.REQUIRED,
1990                "The name of the literal described in the row.\n" + "Example: DBLITERAL_LIKE_PERCENT");
1991    
1992            private static final Column LiteralValue = new Column(
1993                "LiteralValue",
1994                Type.String,
1995                null,
1996                Column.NOT_RESTRICTION,
1997                Column.OPTIONAL,
1998                "Contains the actual literal value.\n" + "Example, if LiteralName is DBLITERAL_LIKE_PERCENT and the percent character (%) is used to match zero or more characters in a LIKE clause, this column's value would be \"%\".");
1999    
2000            private static final Column LiteralInvalidChars = new Column(
2001                "LiteralInvalidChars",
2002                Type.String,
2003                null,
2004                Column.NOT_RESTRICTION,
2005                Column.OPTIONAL,
2006                "The characters, in the literal, that are not valid.\n" + "For example, if table names can contain anything other than a numeric character, this string would be \"0123456789\".");
2007    
2008            private static final Column LiteralInvalidStartingChars = new Column(
2009                "LiteralInvalidStartingChars",
2010                Type.String,
2011                null,
2012                Column.NOT_RESTRICTION,
2013                Column.OPTIONAL,
2014                "The characters that are not valid as the first character of the literal. If the literal can start with any valid character, this is null.");
2015    
2016            private static final Column LiteralMaxLength = new Column(
2017                "LiteralMaxLength",
2018                Type.Integer,
2019                null,
2020                Column.NOT_RESTRICTION,
2021                Column.OPTIONAL,
2022                "The maximum number of characters in the literal. If there is no maximum or the maximum is unknown, the value is ?1.");
2023    
2024            public void populate(
2025                XmlaResponse response,
2026                List<Row> rows)
2027                throws XmlaException
2028            {
2029                populate(Enumeration.Literal.class, rows);
2030            }
2031    
2032            protected void setProperty(
2033                PropertyDefinition propertyDef,
2034                String value)
2035            {
2036                switch (propertyDef) {
2037                case Content:
2038                    break;
2039                default:
2040                    super.setProperty(propertyDef, value);
2041                }
2042            }
2043        }
2044    
2045        static class DbschemaCatalogsRowset extends Rowset {
2046            private final RestrictionTest catalogNameRT;
2047    
2048            DbschemaCatalogsRowset(XmlaRequest request, XmlaHandler handler) {
2049                super(DBSCHEMA_CATALOGS, request, handler);
2050                catalogNameRT = getRestrictionTest(CatalogName);
2051            }
2052    
2053            private static final Column CatalogName =
2054                new Column(
2055                    "CATALOG_NAME",
2056                    Type.String,
2057                    null,
2058                    Column.RESTRICTION,
2059                    Column.REQUIRED,
2060                    "Catalog name. Cannot be NULL.");
2061            private static final Column Description =
2062                new Column(
2063                    "DESCRIPTION",
2064                    Type.String,
2065                    null,
2066                    Column.NOT_RESTRICTION,
2067                    Column.REQUIRED,
2068                    "Human-readable description of the catalog.");
2069            private static final Column Roles =
2070                new Column(
2071                    "ROLES",
2072                    Type.String,
2073                    null,
2074                    Column.NOT_RESTRICTION,
2075                    Column.REQUIRED,
2076                    "A comma delimited list of roles to which the current user belongs. An asterisk (*) is included as a role if the current user is a server or database administrator. Username is appended to ROLES if one of the roles uses dynamic security.");
2077            private static final Column DateModified =
2078                new Column(
2079                    "DATE_MODIFIED",
2080                    Type.DateTime,
2081                    null,
2082                    Column.NOT_RESTRICTION,
2083                    Column.OPTIONAL,
2084                    "The date that the catalog was last modified.");
2085    
2086            public void populate(
2087                XmlaResponse response,
2088                List<Row> rows)
2089                throws XmlaException
2090            {
2091                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
2092                DataSourcesConfig.Catalog[] catalogs = ds.catalogs.catalogs;
2093                String roleName = request.getRoleName();
2094                Role role = request.getRole();
2095    
2096                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
2097                    if (dsCatalog == null || dsCatalog.definition == null) {
2098                        continue;
2099                    }
2100                    Connection connection =
2101                        handler.getConnection(dsCatalog, role, roleName);
2102                    if (connection == null) {
2103                        continue;
2104                    }
2105                    if (!catalogNameRT.passes(dsCatalog.name)) {
2106                        continue;
2107                    }
2108                    final Schema schema = connection.getSchema();
2109    
2110                    Row row = new Row();
2111                    row.set(CatalogName.name, dsCatalog.name);
2112    
2113                    // TODO: currently schema grammar does not support a description
2114                    row.set(Description.name, "No description available");
2115    
2116                    // get Role names
2117                    // TODO: this returns ALL roles, no the current user's roles
2118                    StringBuilder buf = new StringBuilder(100);
2119                    serialize(buf, ((RolapSchema) schema).roleNames());
2120                    row.set(Roles.name, buf.toString());
2121    
2122                    // TODO: currently schema grammar does not support modify date
2123                    // so we return just some date for now.
2124                    if (false) {
2125                        row.set(DateModified.name, dateModified);
2126                    }
2127                    addRow(row, rows);
2128                }
2129            }
2130    
2131            protected void setProperty(PropertyDefinition propertyDef, String value) {
2132                switch (propertyDef) {
2133                case Content:
2134                    break;
2135                default:
2136                    super.setProperty(propertyDef, value);
2137                }
2138            }
2139        }
2140    
2141        static class DbschemaColumnsRowset extends Rowset {
2142            private final RestrictionTest tableCatalogRT;
2143            private final RestrictionTest tableNameRT;
2144            private final RestrictionTest columnNameRT;
2145    
2146            DbschemaColumnsRowset(XmlaRequest request, XmlaHandler handler) {
2147                super(DBSCHEMA_COLUMNS, request, handler);
2148                tableCatalogRT = getRestrictionTest(TableCatalog);
2149                tableNameRT = getRestrictionTest(TableName);
2150                columnNameRT = getRestrictionTest(ColumnName);
2151            }
2152    
2153            private static final Column TableCatalog =
2154                new Column(
2155                    "TABLE_CATALOG",
2156                    Type.String,
2157                    null,
2158                    Column.RESTRICTION,
2159                    Column.REQUIRED,
2160                    "The name of the Database.");
2161            private static final Column TableSchema =
2162                new Column(
2163                    "TABLE_SCHEMA",
2164                    Type.String,
2165                    null,
2166                    Column.RESTRICTION,
2167                    Column.OPTIONAL,
2168                    null);
2169            private static final Column TableName =
2170                new Column(
2171                    "TABLE_NAME",
2172                    Type.String,
2173                    null,
2174                    Column.RESTRICTION,
2175                    Column.REQUIRED,
2176                    "The name of the cube.");
2177            private static final Column ColumnName =
2178                new Column(
2179                    "COLUMN_NAME",
2180                    Type.String,
2181                    null,
2182                    Column.RESTRICTION,
2183                    Column.REQUIRED,
2184                    "The name of the attribute hierarchy or measure.");
2185            private static final Column OrdinalPosition =
2186                new Column(
2187                    "ORDINAL_POSITION",
2188                    Type.UnsignedInteger,
2189                    null,
2190                    Column.NOT_RESTRICTION,
2191                    Column.REQUIRED,
2192                    "The position of the column, beginning with 1.");
2193            private static final Column ColumnHasDefault =
2194                new Column(
2195                    "COLUMN_HAS_DEFAULT",
2196                    Type.Boolean,
2197                    null,
2198                    Column.NOT_RESTRICTION,
2199                    Column.OPTIONAL,
2200                    "Not supported.");
2201            /*
2202             *  A bitmask indicating the information stored in
2203             *      DBCOLUMNFLAGS in OLE DB.
2204             *  1 = Bookmark
2205             *  2 = Fixed length
2206             *  4 = Nullable
2207             *  8 = Row versioning
2208             *  16 = Updateable column
2209             *
2210             * And, of course, MS SQL Server sometimes has the value of 80!!
2211            */
2212            private static final Column ColumnFlags =
2213                new Column(
2214                    "COLUMN_FLAGS",
2215                    Type.UnsignedInteger,
2216                    null,
2217                    Column.NOT_RESTRICTION,
2218                    Column.REQUIRED,
2219                    "A DBCOLUMNFLAGS bitmask indicating column properties.");
2220            private static final Column IsNullable =
2221                new Column(
2222                    "IS_NULLABLE",
2223                    Type.Boolean,
2224                    null,
2225                    Column.NOT_RESTRICTION,
2226                    Column.REQUIRED,
2227                    "Always returns false.");
2228            private static final Column DataType =
2229                new Column(
2230                    "DATA_TYPE",
2231                    Type.UnsignedShort,
2232                    null,
2233                    Column.NOT_RESTRICTION,
2234                    Column.REQUIRED,
2235                    "The data type of the column. Returns a string for dimension columns and a variant for measures.");
2236            private static final Column CharacterMaximumLength =
2237                new Column(
2238                    "CHARACTER_MAXIMUM_LENGTH",
2239                    Type.UnsignedInteger,
2240                    null,
2241                    Column.NOT_RESTRICTION,
2242                    Column.OPTIONAL,
2243                    "The maximum possible length of a value within the column.");
2244            private static final Column CharacterOctetLength =
2245                new Column(
2246                    "CHARACTER_OCTET_LENGTH",
2247                    Type.UnsignedInteger,
2248                    null,
2249                    Column.NOT_RESTRICTION,
2250                    Column.OPTIONAL,
2251                    "The maximum possible length of a value within the column, in bytes, for character or binary columns.");
2252            private static final Column NumericPrecision =
2253                new Column(
2254                    "NUMERIC_PRECISION",
2255                    Type.UnsignedShort,
2256                    null,
2257                    Column.NOT_RESTRICTION,
2258                    Column.OPTIONAL,
2259                    "The maximum precision of the column for numeric data types other than DBTYPE_VARNUMERIC.");
2260            private static final Column NumericScale =
2261                new Column(
2262                    "NUMERIC_SCALE",
2263                    Type.Short,
2264                    null,
2265                    Column.NOT_RESTRICTION,
2266                    Column.OPTIONAL,
2267                    "The number of digits to the right of the decimal point for DBTYPE_DECIMAL, DBTYPE_NUMERIC, DBTYPE_VARNUMERIC. Otherwise, this is NULL.");
2268    
2269            public void populate(
2270                XmlaResponse response,
2271                List<Row> rows)
2272                throws XmlaException
2273            {
2274                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
2275                DataSourcesConfig.Catalog[] catalogs = ds.catalogs.catalogs;
2276                String roleName = request.getRoleName();
2277                Role role = request.getRole();
2278    
2279                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
2280                    if (dsCatalog == null || dsCatalog.definition == null) {
2281                        continue;
2282                    }
2283                    Connection connection =
2284                        handler.getConnection(dsCatalog, role, roleName);
2285                    if (connection == null) {
2286                        continue;
2287                    }
2288                    final Schema schema = connection.getSchema();
2289                    String catalogName = dsCatalog.name;
2290                    if (!tableCatalogRT.passes(catalogName)) {
2291                        continue;
2292                    }
2293    
2294                    int ordinalPosition = 1;
2295                    Row row;
2296    
2297                    for (Cube cube1 : sortedCubes(schema)) {
2298                        RolapCube cube = (RolapCube) cube1;
2299                        SchemaReader schemaReader =
2300                            cube.getSchemaReader(
2301                                connection.getRole());
2302                        String cubeName = cube.getName();
2303                        if (!tableNameRT.passes(cubeName)) {
2304                            continue;
2305                        }
2306                        for (Dimension dimension : cube.getDimensions()) {
2307                            Hierarchy[] hierarchies = dimension.getHierarchies();
2308                            for (Hierarchy hierarchy : hierarchies) {
2309                                ordinalPosition = populateHierarchy(
2310                                    schemaReader, cube, (HierarchyBase) hierarchy,
2311                                    ordinalPosition, rows);
2312                            }
2313                        }
2314    
2315                        List<RolapMember> rms = cube.getMeasuresMembers();
2316                        for (int k = 1; k < rms.size(); k++) {
2317                            RolapMember member = rms.get(k);
2318    
2319                            // null == true for regular cubes
2320                            // virtual cubes do not set the visible property
2321                            // on its measures so it might be null.
2322                            Boolean visible = (Boolean)
2323                                member.getPropertyValue(Property.VISIBLE.name);
2324                            if (visible == null) {
2325                                visible = true;
2326                            }
2327                            if (!EMIT_INVISIBLE_MEMBERS && !visible) {
2328                                continue;
2329                            }
2330    
2331                            String memberName = member.getName();
2332                            if (!columnNameRT.passes("Measures:" + memberName)) {
2333                                continue;
2334                            }
2335    
2336                            row = new Row();
2337                            row.set(TableCatalog.name, catalogName);
2338                            row.set(TableName.name, cubeName);
2339                            row.set(ColumnName.name, "Measures:" + memberName);
2340                            row.set(OrdinalPosition.name, ordinalPosition++);
2341                            row.set(ColumnHasDefault.name, false);
2342                            row.set(ColumnFlags.name, 0);
2343                            row.set(IsNullable.name, false);
2344                            // TODO: here is where one tries to determine the
2345                            // type of the column - since these are all
2346                            // Measures, aggregate Measures??, maybe they
2347                            // are all numeric? (or currency)
2348                            row.set(DataType.name, DBType.R8.userOrdinal);
2349                            // TODO: 16/255 seems to be what MS SQL Server
2350                            // always returns.
2351                            row.set(NumericPrecision.name, 16);
2352                            row.set(NumericScale.name, 255);
2353                            addRow(row, rows);
2354                        }
2355                    }
2356                }
2357            }
2358    
2359            private int populateHierarchy(
2360                SchemaReader schemaReader,
2361                RolapCube cube,
2362                HierarchyBase hierarchy,
2363                int ordinalPosition,
2364                List<Row> rows)
2365            {
2366                // Access control
2367                if (!canAccess(schemaReader, hierarchy)) {
2368                    return ordinalPosition;
2369                }
2370                String schemaName = cube.getSchema().getName();
2371                String cubeName = cube.getName();
2372                String hierarchyName = hierarchy.getName();
2373    
2374                if (hierarchy.hasAll()) {
2375                    Row row = new Row();
2376                    row.set(TableCatalog.name, schemaName);
2377                    row.set(TableName.name, cubeName);
2378                    row.set(ColumnName.name, hierarchyName + ":(All)!NAME");
2379                    row.set(OrdinalPosition.name, ordinalPosition++);
2380                    row.set(ColumnHasDefault.name, false);
2381                    row.set(ColumnFlags.name, 0);
2382                    row.set(IsNullable.name, false);
2383                    // names are always WSTR
2384                    row.set(DataType.name, DBType.WSTR.userOrdinal);
2385                    row.set(CharacterMaximumLength.name, 0);
2386                    row.set(CharacterOctetLength.name, 0);
2387                    addRow(row, rows);
2388    
2389                    row = new Row();
2390                    row.set(TableCatalog.name, schemaName);
2391                    row.set(TableName.name, cubeName);
2392                    row.set(ColumnName.name, hierarchyName + ":(All)!UNIQUE_NAME");
2393                    row.set(OrdinalPosition.name, ordinalPosition++);
2394                    row.set(ColumnHasDefault.name, false);
2395                    row.set(ColumnFlags.name, 0);
2396                    row.set(IsNullable.name, false);
2397                    // names are always WSTR
2398                    row.set(DataType.name, DBType.WSTR.userOrdinal);
2399                    row.set(CharacterMaximumLength.name, 0);
2400                    row.set(CharacterOctetLength.name, 0);
2401                    addRow(row, rows);
2402    
2403                    if (false) {
2404                        // TODO: SQLServer outputs this hasall KEY column name -
2405                        // don't know what it's for
2406                        row = new Row();
2407                        row.set(TableCatalog.name, schemaName);
2408                        row.set(TableName.name, cubeName);
2409                        row.set(ColumnName.name, hierarchyName + ":(All)!KEY");
2410                        row.set(OrdinalPosition.name, ordinalPosition++);
2411                        row.set(ColumnHasDefault.name, false);
2412                        row.set(ColumnFlags.name, 0);
2413                        row.set(IsNullable.name, false);
2414                        // names are always BOOL
2415                        row.set(DataType.name, DBType.BOOL.userOrdinal);
2416                        row.set(NumericPrecision.name, 255);
2417                        row.set(NumericScale.name, 255);
2418                        addRow(row, rows);
2419                    }
2420                }
2421    
2422                for (Level level : schemaReader.getHierarchyLevels(hierarchy)) {
2423                    ordinalPosition = populateLevel(
2424                        cube, hierarchy, level, ordinalPosition, rows);
2425                }
2426                return ordinalPosition;
2427            }
2428    
2429            private int populateLevel(
2430                Cube cube,
2431                HierarchyBase hierarchy,
2432                Level level,
2433                int ordinalPosition,
2434                List<Row> rows)
2435            {
2436                String schemaName = cube.getSchema().getName();
2437                String cubeName = cube.getName();
2438                String hierarchyName = hierarchy.getName();
2439                String levelName = level.getName();
2440    
2441                Row row = new Row();
2442                row.set(TableCatalog.name, schemaName);
2443                row.set(TableName.name, cubeName);
2444                row.set(ColumnName.name,
2445                    hierarchyName + ':' + levelName + "!NAME");
2446                row.set(OrdinalPosition.name, ordinalPosition++);
2447                row.set(ColumnHasDefault.name, false);
2448                row.set(ColumnFlags.name, 0);
2449                row.set(IsNullable.name, false);
2450                // names are always WSTR
2451                row.set(DataType.name, DBType.WSTR.userOrdinal);
2452                row.set(CharacterMaximumLength.name, 0);
2453                row.set(CharacterOctetLength.name, 0);
2454                addRow(row, rows);
2455    
2456                row = new Row();
2457                row.set(TableCatalog.name, schemaName);
2458                row.set(TableName.name, cubeName);
2459                row.set(ColumnName.name,
2460                    hierarchyName + ':' + levelName + "!UNIQUE_NAME");
2461                row.set(OrdinalPosition.name, ordinalPosition++);
2462                row.set(ColumnHasDefault.name, false);
2463                row.set(ColumnFlags.name, 0);
2464                row.set(IsNullable.name, false);
2465                // names are always WSTR
2466                row.set(DataType.name, DBType.WSTR.userOrdinal);
2467                row.set(CharacterMaximumLength.name, 0);
2468                row.set(CharacterOctetLength.name, 0);
2469                addRow(row, rows);
2470    
2471    /*
2472    TODO: see above
2473                row = new Row();
2474                row.set(TableCatalog.name, schemaName);
2475                row.set(TableName.name, cubeName);
2476                row.set(ColumnName.name,
2477                    hierarchyName + ":" + levelName + "!KEY");
2478                row.set(OrdinalPosition.name, ordinalPosition++);
2479                row.set(ColumnHasDefault.name, false);
2480                row.set(ColumnFlags.name, 0);
2481                row.set(IsNullable.name, false);
2482                // names are always BOOL
2483                row.set(DataType.name, DBType.BOOL.ordinal());
2484                row.set(NumericPrecision.name, 255);
2485                row.set(NumericScale.name, 255);
2486                addRow(row, rows);
2487    */
2488                Property[] props = level.getProperties();
2489                for (Property prop : props) {
2490                    String propName = prop.getName();
2491    
2492                    row = new Row();
2493                    row.set(TableCatalog.name, schemaName);
2494                    row.set(TableName.name, cubeName);
2495                    row.set(ColumnName.name,
2496                        hierarchyName + ':' + levelName + '!' + propName);
2497                    row.set(OrdinalPosition.name, ordinalPosition++);
2498                    row.set(ColumnHasDefault.name, false);
2499                    row.set(ColumnFlags.name, 0);
2500                    row.set(IsNullable.name, false);
2501    
2502                    DBType dbType = getDBTypeFromProperty(prop);
2503                    row.set(DataType.name, dbType.userOrdinal);
2504    
2505                    switch (prop.getType()) {
2506                    case TYPE_STRING:
2507                        row.set(CharacterMaximumLength.name, 0);
2508                        row.set(CharacterOctetLength.name, 0);
2509                        break;
2510                    case TYPE_NUMERIC:
2511                        // TODO: 16/255 seems to be what MS SQL Server
2512                        // always returns.
2513                        row.set(NumericPrecision.name, 16);
2514                        row.set(NumericScale.name, 255);
2515                        break;
2516                    case TYPE_BOOLEAN:
2517                        row.set(NumericPrecision.name, 255);
2518                        row.set(NumericScale.name, 255);
2519                        break;
2520                    case TYPE_OTHER:
2521                        // TODO: what type is it really, its
2522                        // not a string
2523                        row.set(CharacterMaximumLength.name, 0);
2524                        row.set(CharacterOctetLength.name, 0);
2525                        break;
2526                    }
2527                    addRow(row, rows);
2528                }
2529                return ordinalPosition;
2530            }
2531    
2532            protected void setProperty(PropertyDefinition propertyDef, String value) {
2533                switch (propertyDef) {
2534                case Content:
2535                    break;
2536                default:
2537                    super.setProperty(propertyDef, value);
2538                }
2539            }
2540        }
2541    
2542        static class DbschemaProviderTypesRowset extends Rowset {
2543            private final RestrictionTest dataTypeRT;
2544    
2545            DbschemaProviderTypesRowset(XmlaRequest request, XmlaHandler handler) {
2546                super(DBSCHEMA_PROVIDER_TYPES, request, handler);
2547                dataTypeRT = getRestrictionTest(DataType);
2548            }
2549    
2550            /*
2551            DATA_TYPE DBTYPE_UI2
2552            BEST_MATCH DBTYPE_BOOL
2553            Column(String name, Type type, Enumeration enumeratedType,
2554            boolean restriction, boolean nullable, String description)
2555            */
2556            /*
2557             * These are the columns returned by SQL Server.
2558             */
2559            private static final Column TypeName =
2560                new Column(
2561                    "TYPE_NAME",
2562                    Type.String,
2563                    null,
2564                    Column.NOT_RESTRICTION,
2565                    Column.REQUIRED,
2566                    "The provider-specific data type name.");
2567            private static final Column DataType =
2568                new Column(
2569                    "DATA_TYPE",
2570                    Type.UnsignedShort,
2571                    null,
2572                    Column.RESTRICTION,
2573                    Column.REQUIRED,
2574                    "The indicator of the data type.");
2575            private static final Column ColumnSize =
2576                new Column(
2577                    "COLUMN_SIZE",
2578                    Type.UnsignedInteger,
2579                    null,
2580                    Column.NOT_RESTRICTION,
2581                    Column.REQUIRED,
2582                    "The length of a non-numeric column. If the data type is numeric, this is the upper bound on the maximum precision of the data type.");
2583            private static final Column LiteralPrefix =
2584                new Column(
2585                    "LITERAL_PREFIX",
2586                    Type.String,
2587                    null,
2588                    Column.NOT_RESTRICTION,
2589                    Column.OPTIONAL,
2590                    "The character or characters used to prefix a literal of this type in a text command.");
2591            private static final Column LiteralSuffix =
2592                new Column(
2593                    "LITERAL_SUFFIX",
2594                    Type.String,
2595                    null,
2596                    Column.NOT_RESTRICTION,
2597                    Column.OPTIONAL,
2598                    "The character or characters used to suffix a literal of this type in a text command.");
2599            private static final Column IsNullable =
2600                new Column(
2601                    "IS_NULLABLE",
2602                    Type.Boolean,
2603                    null,
2604                    Column.NOT_RESTRICTION,
2605                    Column.OPTIONAL,
2606                    "A Boolean that indicates whether the data type is nullable. NULL-- indicates that it is not known whether the data type is nullable.");
2607            private static final Column CaseSensitive =
2608                new Column(
2609                    "CASE_SENSITIVE",
2610                    Type.Boolean,
2611                    null,
2612                    Column.NOT_RESTRICTION,
2613                    Column.OPTIONAL,
2614                    "A Boolean that indicates whether the data type is a characters type and case-sensitive.");
2615            private static final Column Searchable =
2616                new Column(
2617                    "SEARCHABLE",
2618                    Type.UnsignedInteger,
2619                    null,
2620                    Column.NOT_RESTRICTION,
2621                    Column.OPTIONAL,
2622                    "An integer indicating how the data type can be used in searches if the provider supports ICommandText; otherwise, NULL.");
2623            private static final Column UnsignedAttribute =
2624                new Column(
2625                    "UNSIGNED_ATTRIBUTE",
2626                    Type.Boolean,
2627                    null,
2628                    Column.NOT_RESTRICTION,
2629                    Column.OPTIONAL,
2630                    "A Boolean that indicates whether the data type is unsigned.");
2631            private static final Column FixedPrecScale =
2632                new Column(
2633                    "FIXED_PREC_SCALE",
2634                    Type.Boolean,
2635                    null,
2636                    Column.NOT_RESTRICTION,
2637                    Column.OPTIONAL,
2638                    "A Boolean that indicates whether the data type has a fixed precision and scale.");
2639            private static final Column AutoUniqueValue =
2640                new Column(
2641                    "AUTO_UNIQUE_VALUE",
2642                    Type.Boolean,
2643                    null,
2644                    Column.NOT_RESTRICTION,
2645                    Column.OPTIONAL,
2646                    "A Boolean that indicates whether the data type is autoincrementing.");
2647            private static final Column IsLong =
2648                new Column(
2649                    "IS_LONG",
2650                    Type.Boolean,
2651                    null,
2652                    Column.NOT_RESTRICTION,
2653                    Column.OPTIONAL,
2654                    "A Boolean that indicates whether the data type is a binary large object (BLOB) and has very long data.");
2655            private static final Column BestMatch =
2656                new Column(
2657                    "BEST_MATCH",
2658                    Type.Boolean,
2659                    null,
2660                    Column.RESTRICTION,
2661                    Column.OPTIONAL,
2662                    "A Boolean that indicates whether the data type is a best match.");
2663    
2664            public void populate(
2665                XmlaResponse response,
2666                List<Row> rows)
2667                throws XmlaException
2668            {
2669                // Identifies the (base) data types supported by the data provider.
2670                Row row;
2671    
2672                // i4
2673                Integer dt = DBType.I4.userOrdinal;
2674                if (dataTypeRT.passes(dt)) {
2675                    row = new Row();
2676                    row.set(TypeName.name, DBType.I4.userName);
2677                    row.set(DataType.name, dt);
2678                    row.set(ColumnSize.name, 8);
2679                    row.set(IsNullable.name, true);
2680                    row.set(Searchable.name, null);
2681                    row.set(UnsignedAttribute.name, false);
2682                    row.set(FixedPrecScale.name, false);
2683                    row.set(AutoUniqueValue.name, false);
2684                    row.set(IsLong.name, false);
2685                    row.set(BestMatch.name, true);
2686                    addRow(row, rows);
2687                }
2688    
2689                // R8
2690                dt = DBType.R8.userOrdinal;
2691                if (dataTypeRT.passes(dt)) {
2692                    row = new Row();
2693                    row.set(TypeName.name, DBType.R8.userName);
2694                    row.set(DataType.name, dt);
2695                    row.set(ColumnSize.name, 16);
2696                    row.set(IsNullable.name, true);
2697                    row.set(Searchable.name, null);
2698                    row.set(UnsignedAttribute.name, false);
2699                    row.set(FixedPrecScale.name, false);
2700                    row.set(AutoUniqueValue.name, false);
2701                    row.set(IsLong.name, false);
2702                    row.set(BestMatch.name, true);
2703                    addRow(row, rows);
2704                }
2705    
2706                // CY
2707                dt = DBType.CY.userOrdinal;
2708                if (dataTypeRT.passes(dt)) {
2709                    row = new Row();
2710                    row.set(TypeName.name, DBType.CY.userName);
2711                    row.set(DataType.name, dt);
2712                    row.set(ColumnSize.name, 8);
2713                    row.set(IsNullable.name, true);
2714                    row.set(Searchable.name, null);
2715                    row.set(UnsignedAttribute.name, false);
2716                    row.set(FixedPrecScale.name, false);
2717                    row.set(AutoUniqueValue.name, false);
2718                    row.set(IsLong.name, false);
2719                    row.set(BestMatch.name, true);
2720                    addRow(row, rows);
2721                }
2722    
2723                // BOOL
2724                dt = DBType.BOOL.userOrdinal;
2725                if (dataTypeRT.passes(dt)) {
2726                    row = new Row();
2727                    row.set(TypeName.name, DBType.BOOL.userName);
2728                    row.set(DataType.name, dt);
2729                    row.set(ColumnSize.name, 1);
2730                    row.set(IsNullable.name, true);
2731                    row.set(Searchable.name, null);
2732                    row.set(UnsignedAttribute.name, false);
2733                    row.set(FixedPrecScale.name, false);
2734                    row.set(AutoUniqueValue.name, false);
2735                    row.set(IsLong.name, false);
2736                    row.set(BestMatch.name, true);
2737                    addRow(row, rows);
2738                }
2739    
2740                // I8
2741                dt = DBType.I8.userOrdinal;
2742                if (dataTypeRT.passes(dt)) {
2743                    row = new Row();
2744                    row.set(TypeName.name, DBType.I8.userName);
2745                    row.set(DataType.name, dt);
2746                    row.set(ColumnSize.name, 16);
2747                    row.set(IsNullable.name, true);
2748                    row.set(Searchable.name, null);
2749                    row.set(UnsignedAttribute.name, false);
2750                    row.set(FixedPrecScale.name, false);
2751                    row.set(AutoUniqueValue.name, false);
2752                    row.set(IsLong.name, false);
2753                    row.set(BestMatch.name, true);
2754                    addRow(row, rows);
2755                }
2756    
2757                // WSTR
2758                dt = DBType.WSTR.userOrdinal;
2759                if (dataTypeRT.passes(dt)) {
2760                    row = new Row();
2761                    row.set(TypeName.name, DBType.WSTR.userName);
2762                    row.set(DataType.name, dt);
2763                    // how big are the string columns in the db
2764                    row.set(ColumnSize.name, 255);
2765                    row.set(LiteralPrefix.name, "\"");
2766                    row.set(LiteralSuffix.name, "\"");
2767                    row.set(IsNullable.name, true);
2768                    row.set(CaseSensitive.name, false);
2769                    row.set(Searchable.name, null);
2770                    row.set(FixedPrecScale.name, false);
2771                    row.set(AutoUniqueValue.name, false);
2772                    row.set(IsLong.name, false);
2773                    row.set(BestMatch.name, true);
2774                    addRow(row, rows);
2775                }
2776            }
2777    
2778            protected void setProperty(PropertyDefinition propertyDef, String value) {
2779                switch (propertyDef) {
2780                case Content:
2781                    break;
2782                default:
2783                    super.setProperty(propertyDef, value);
2784                }
2785            }
2786        }
2787    
2788        static class DbschemaSchemataRowset extends Rowset {
2789            private final RestrictionTest catalogNameRT;
2790    
2791            DbschemaSchemataRowset(XmlaRequest request, XmlaHandler handler) {
2792                super(DBSCHEMA_SCHEMATA, request, handler);
2793                catalogNameRT = getRestrictionTest(CatalogName);
2794            }
2795    
2796            /*
2797             * These are the columns returned by SQL Server.
2798             */
2799            private static final Column CatalogName =
2800                new Column(
2801                    "CATALOG_NAME",
2802                    Type.String,
2803                    null,
2804                    Column.RESTRICTION,
2805                    Column.REQUIRED,
2806                    "The provider-specific data type name.");
2807            private static final Column SchemaName =
2808                new Column(
2809                    "SCHEMA_NAME",
2810                    Type.String,
2811                    null,
2812                    Column.RESTRICTION,
2813                    Column.REQUIRED,
2814                    "The indicator of the data type.");
2815            private static final Column SchemaOwner =
2816                new Column(
2817                    "SCHEMA_OWNER",
2818                    Type.String,
2819                    null,
2820                    Column.RESTRICTION,
2821                    Column.REQUIRED,
2822                    "The length of a non-numeric column. If the data type is numeric, this is the upper bound on the maximum precision of the data type.");
2823    
2824            public void populate(
2825                XmlaResponse response,
2826                List<Row> rows)
2827                throws XmlaException
2828            {
2829                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
2830                DataSourcesConfig.Catalog[] catalogs = ds.catalogs.catalogs;
2831                String roleName = request.getRoleName();
2832                Role role = request.getRole();
2833    
2834                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
2835                    if (dsCatalog == null || dsCatalog.definition == null) {
2836                        continue;
2837                    }
2838                    Connection connection =
2839                        handler.getConnection(dsCatalog, role, roleName);
2840                    if (connection == null) {
2841                        continue;
2842                    }
2843                    if (!catalogNameRT.passes(dsCatalog.name)) {
2844                        continue;
2845                    }
2846                    final Schema schema = connection.getSchema();
2847                    Row row = new Row();
2848                    row.set(CatalogName.name, dsCatalog.name);
2849                    row.set(SchemaName.name, schema.getName());
2850                    row.set(SchemaOwner.name, "");
2851                    addRow(row, rows);
2852                }
2853            }
2854    
2855            protected void setProperty(PropertyDefinition propertyDef, String value) {
2856                switch (propertyDef) {
2857                case Content:
2858                    break;
2859                default:
2860                    super.setProperty(propertyDef, value);
2861                }
2862            }
2863        }
2864    
2865        static class DbschemaTablesRowset extends Rowset {
2866            private final RestrictionTest tableCatalogRT;
2867            private final RestrictionTest tableNameRT;
2868            private final RestrictionTest tableTypeRT;
2869    
2870            DbschemaTablesRowset(XmlaRequest request, XmlaHandler handler) {
2871                super(DBSCHEMA_TABLES, request, handler);
2872                tableCatalogRT = getRestrictionTest(TableCatalog);
2873                tableNameRT = getRestrictionTest(TableName);
2874                tableTypeRT = getRestrictionTest(TableType);
2875            }
2876    
2877            private static final Column TableCatalog =
2878                new Column(
2879                    "TABLE_CATALOG",
2880                    Type.String,
2881                    null,
2882                    Column.RESTRICTION,
2883                    Column.REQUIRED,
2884                    "The name of the catalog to which this object belongs.");
2885            private static final Column TableSchema =
2886                new Column(
2887                    "TABLE_SCHEMA",
2888                    Type.String,
2889                    null,
2890                    Column.RESTRICTION,
2891                    Column.OPTIONAL,
2892                    "The name of the cube to which this object belongs.");
2893            private static final Column TableName =
2894                new Column(
2895                    "TABLE_NAME",
2896                    Type.String,
2897                    null,
2898                    Column.RESTRICTION,
2899                    Column.REQUIRED,
2900                    "The name of the object, if TABLE_TYPE is TABLE.");
2901            private static final Column TableType =
2902                new Column(
2903                    "TABLE_TYPE",
2904                    Type.String,
2905                    null,
2906                    Column.RESTRICTION,
2907                    Column.REQUIRED,
2908                    "The type of the table. TABLE indicates the object is a measure group. SYSTEM TABLE indicates the object is a dimension.");
2909    
2910            private static final Column TableGuid =
2911                new Column(
2912                    "TABLE_GUID",
2913                    Type.UUID,
2914                    null,
2915                    Column.NOT_RESTRICTION,
2916                    Column.OPTIONAL,
2917                    "Not supported.");
2918            private static final Column Description =
2919                new Column(
2920                    "DESCRIPTION",
2921                    Type.String,
2922                    null,
2923                    Column.NOT_RESTRICTION,
2924                    Column.OPTIONAL,
2925                    "A human-readable description of the object.");
2926            private static final Column TablePropId =
2927                new Column(
2928                    "TABLE_PROPID",
2929                    Type.UnsignedInteger,
2930                    null,
2931                    Column.NOT_RESTRICTION,
2932                    Column.OPTIONAL,
2933                    "Not supported.");
2934            private static final Column DateCreated =
2935                new Column(
2936                    "DATE_CREATED",
2937                    Type.DateTime,
2938                    null,
2939                    Column.NOT_RESTRICTION,
2940                    Column.OPTIONAL,
2941                    "Not supported.");
2942            private static final Column DateModified =
2943                new Column(
2944                    "DATE_MODIFIED",
2945                    Type.DateTime,
2946                    null,
2947                    Column.NOT_RESTRICTION,
2948                    Column.OPTIONAL,
2949                    "The date the object was last modified.");
2950    
2951            /*
2952            private static final Column TableOlapType =
2953                new Column(
2954                    "TABLE_OLAP_TYPE",
2955                    Type.String,
2956                    null,
2957                    Column.RESTRICTION,
2958                    Column.OPTIONAL,
2959                    "The OLAP type of the object.  MEASURE_GROUP indicates the object is a measure group.  CUBE_DIMENSION indicated the object is a dimension.");
2960            */
2961    
2962            public void populate(
2963                XmlaResponse response,
2964                List<Row> rows)
2965                throws XmlaException
2966            {
2967                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
2968                DataSourcesConfig.Catalog[] catalogs =
2969                    handler.getCatalogs(request, ds);
2970                String roleName = request.getRoleName();
2971                Role role = request.getRole();
2972    
2973                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
2974                    if (dsCatalog == null || dsCatalog.definition == null) {
2975                        continue;
2976                    }
2977                    Connection connection =
2978                        handler.getConnection(dsCatalog, role, roleName);
2979                    if (connection == null) {
2980                        continue;
2981                    }
2982                    final Schema schema = connection.getSchema();
2983                    String catalogName = dsCatalog.name;
2984                    if (!tableCatalogRT.passes(catalogName)) {
2985                        continue;
2986                    }
2987    
2988                    //final String schemaName = schema.getName();
2989    
2990                    Row row;
2991                    for (Cube cube1 : sortedCubes(schema)) {
2992                        RolapCube cube = (RolapCube) cube1;
2993                        String cubeName = cube.getName();
2994    
2995                        if (!tableNameRT.passes(cubeName)) {
2996                            continue;
2997                        }
2998                        SchemaReader schemaReader =
2999                            cube.getSchemaReader(
3000                                connection.getRole());
3001    
3002                        String desc = cube.getDescription();
3003                        if (desc == null) {
3004                            //TODO: currently this is always null
3005                            desc = catalogName + " - " + cubeName + " Cube";
3006                        }
3007    
3008    
3009                        if (tableTypeRT.passes("TABLE")) {
3010                            row = new Row();
3011                            row.set(TableCatalog.name, catalogName);
3012                            row.set(TableName.name, cubeName);
3013                            row.set(TableType.name, "TABLE");
3014                            row.set(Description.name, desc);
3015                            if (false) {
3016                                row.set(DateModified.name, dateModified);
3017                            }
3018                            addRow(row, rows);
3019                        }
3020    
3021    
3022                        if (tableTypeRT.passes("SYSTEM TABLE")) {
3023                            for (Dimension dimension : cube.getDimensions()) {
3024                                if (dimension.isMeasures()) {
3025                                    continue;
3026                                }
3027                                Hierarchy[] hierarchies =
3028                                    dimension.getHierarchies();
3029                                for (Hierarchy hierarchy1 : hierarchies) {
3030                                    HierarchyBase hierarchy =
3031                                        (HierarchyBase) hierarchy1;
3032                                    populateHierarchy(schemaReader, cube,
3033                                        hierarchy, rows);
3034                                }
3035                            }
3036                        }
3037                    }
3038                }
3039            }
3040    
3041            private void populateHierarchy(
3042                SchemaReader schemaReader,
3043                RolapCube cube,
3044                HierarchyBase hierarchy,
3045                List<Row> rows)
3046            {
3047                // Access control
3048                if (!canAccess(schemaReader, hierarchy)) {
3049                    return;
3050                }
3051    /*
3052                String schemaName = cube.getSchema().getName();
3053                String cubeName = cube.getName();
3054                String hierarchyName = hierarchy.getName();
3055    
3056                String desc = hierarchy.getDescription();
3057                if (desc == null) {
3058                    //TODO: currently this is always null
3059                    desc = schemaName +
3060                        " - " +
3061                        cubeName +
3062                        " Cube - " +
3063                        hierarchyName +
3064                        " Hierarchy";
3065                }
3066    
3067                if (hierarchy.hasAll()) {
3068                    String tableName = cubeName +
3069                        ':' + hierarchyName + ':' + "(All)";
3070    
3071                    Row row = new Row();
3072                    row.set(TableCatalog.name, schemaName);
3073                    row.set(TableName.name, tableName);
3074                    row.set(TableType.name, "SYSTEM TABLE");
3075                    row.set(Description.name, desc);
3076                    row.set(DateModified.name, dateModified);
3077                    addRow(row, rows);
3078                }
3079    */
3080                Level[] levels = hierarchy.getLevels();
3081                for (Level level : levels) {
3082                    populateLevel(cube, hierarchy, level, rows);
3083                }
3084            }
3085    
3086            private void populateLevel(
3087                RolapCube cube,
3088                HierarchyBase hierarchy,
3089                Level level,
3090                List<Row> rows)
3091            {
3092                String schemaName = cube.getSchema().getName();
3093                String cubeName = cube.getName();
3094                String hierarchyName = hierarchy.getName();
3095                String levelName = level.getName();
3096    
3097                String tableName = cubeName +
3098                    ':' + hierarchyName + ':' + levelName;
3099    
3100                String desc = level.getDescription();
3101                if (desc == null) {
3102                    //TODO: currently this is always null
3103                    desc = schemaName +
3104                        " - " +
3105                        cubeName +
3106                        " Cube - " +
3107                        hierarchyName +
3108                        " Hierarchy - " +
3109                        levelName +
3110                        " Level";
3111                }
3112    
3113                Row row = new Row();
3114                row.set(TableCatalog.name, schemaName);
3115                row.set(TableName.name, tableName);
3116                row.set(TableType.name, "SYSTEM TABLE");
3117                row.set(Description.name, desc);
3118                if (false) {
3119                    row.set(DateModified.name, dateModified);
3120                }
3121                addRow(row, rows);
3122            }
3123    
3124            protected void setProperty(PropertyDefinition propertyDef, String value) {
3125                switch (propertyDef) {
3126                case Content:
3127                    break;
3128                default:
3129                    super.setProperty(propertyDef, value);
3130                }
3131            }
3132        }
3133    
3134        // TODO: Is this needed????
3135        static class DbschemaTablesInfoRowset extends Rowset {
3136            DbschemaTablesInfoRowset(XmlaRequest request, XmlaHandler handler) {
3137                super(DBSCHEMA_TABLES_INFO, request, handler);
3138            }
3139    
3140            private static final Column TableCatalog =
3141                new Column(
3142                    "TABLE_CATALOG",
3143                    Type.String,
3144                    null,
3145                    Column.RESTRICTION,
3146                    Column.OPTIONAL,
3147                    "Catalog name. NULL if the provider does not support catalogs.");
3148            private static final Column TableSchema =
3149                new Column(
3150                    "TABLE_SCHEMA",
3151                    Type.String,
3152                    null,
3153                    Column.RESTRICTION,
3154                    Column.OPTIONAL,
3155                    "Unqualified schema name. NULL if the provider does not support schemas.");
3156            private static final Column TableName =
3157                new Column(
3158                    "TABLE_NAME",
3159                    Type.String,
3160                    null,
3161                    Column.RESTRICTION,
3162                    Column.REQUIRED,
3163                    "Table name.");
3164            private static final Column TableType =
3165                new Column(
3166                    "TABLE_TYPE",
3167                    Type.String,
3168                    null,
3169                    Column.RESTRICTION,
3170                    Column.REQUIRED,
3171                    "Table type. One of the following or a provider-specific value: ALIAS, TABLE, SYNONYM, SYSTEM TABLE, VIEW, GLOBAL TEMPORARY, LOCAL TEMPORARY, EXTERNAL TABLE, SYSTEM VIEW");
3172            private static final Column TableGuid =
3173                new Column(
3174                    "TABLE_GUID",
3175                    Type.UUID,
3176                    null,
3177                    Column.NOT_RESTRICTION,
3178                    Column.OPTIONAL,
3179                    "GUID that uniquely identifies the table. Providers that do not use GUIDs to identify tables should return NULL in this column.");
3180    
3181            private static final Column Bookmarks =
3182                new Column(
3183                    "BOOKMARKS",
3184                    Type.Boolean,
3185                    null,
3186                    Column.NOT_RESTRICTION,
3187                    Column.REQUIRED,
3188                    "Whether this table supports bookmarks. Allways is false.");
3189            private static final Column BookmarkType =
3190                new Column(
3191                    "BOOKMARK_TYPE",
3192                    Type.Integer,
3193                    null,
3194                    Column.NOT_RESTRICTION,
3195                    Column.OPTIONAL,
3196                    "Default bookmark type supported on this table.");
3197            private static final Column BookmarkDataType =
3198                new Column(
3199                    "BOOKMARK_DATATYPE",
3200                    Type.UnsignedShort,
3201                    null,
3202                    Column.NOT_RESTRICTION,
3203                    Column.OPTIONAL,
3204                    "The indicator of the bookmark's native data type.");
3205            private static final Column BookmarkMaximumLength =
3206                new Column(
3207                    "BOOKMARK_MAXIMUM_LENGTH",
3208                    Type.UnsignedInteger,
3209                    null,
3210                    Column.NOT_RESTRICTION,
3211                    Column.OPTIONAL,
3212                    "Maximum length of the bookmark in bytes.");
3213            private static final Column BookmarkInformation =
3214                new Column(
3215                    "BOOKMARK_INFORMATION",
3216                    Type.UnsignedInteger,
3217                    null,
3218                    Column.NOT_RESTRICTION,
3219                    Column.OPTIONAL,
3220                    "A bitmask specifying additional information about bookmarks over the rowset. ");
3221            private static final Column TableVersion =
3222                new Column(
3223                    "TABLE_VERSION",
3224                    Type.Long,
3225                    null,
3226                    Column.NOT_RESTRICTION,
3227                    Column.OPTIONAL,
3228                    "Version number for this table or NULL if the provider does not support returning table version information.");
3229            private static final Column Cardinality =
3230                new Column(
3231                    "CARDINALITY",
3232                    Type.UnsignedLong,
3233                    null,
3234                    Column.NOT_RESTRICTION,
3235                    Column.REQUIRED,
3236                    "Cardinality (number of rows) of the table.");
3237            private static final Column Description =
3238                new Column(
3239                    "DESCRIPTION",
3240                    Type.String,
3241                    null,
3242                    Column.NOT_RESTRICTION,
3243                    Column.OPTIONAL,
3244                    "Human-readable description of the table.");
3245            private static final Column TablePropId =
3246                new Column(
3247                    "TABLE_PROPID",
3248                    Type.UnsignedInteger,
3249                    null,
3250                    Column.NOT_RESTRICTION,
3251                    Column.OPTIONAL,
3252                    "Property ID of the table. Return null.");
3253    
3254            public void populate(
3255                XmlaResponse response,
3256                List<Row> rows)
3257                throws XmlaException
3258            {
3259                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
3260                DataSourcesConfig.Catalog[] catalogs =
3261                    handler.getCatalogs(request, ds);
3262                String roleName = request.getRoleName();
3263                Role role = request.getRole();
3264    
3265                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
3266                    if (dsCatalog == null || dsCatalog.definition == null) {
3267                        continue;
3268                    }
3269                    Connection connection =
3270                        handler.getConnection(dsCatalog, role, roleName);
3271                    if (connection == null) {
3272                        continue;
3273                    }
3274                    final Schema schema = connection.getSchema();
3275                    String catalogName = dsCatalog.name;
3276                    //final String catalogName = schema.getName();
3277    
3278                    //TODO: Is this cubes or tables? SQL Server returns what
3279                    // in foodmart are cube names for TABLE_NAME
3280                    // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/oledb/htm/oledbtables_info_rowset.asp
3281                    for (Cube cube1 : sortedCubes(schema)) {
3282                        RolapCube cube = (RolapCube) cube1;
3283                        String cubeName = cube.getName();
3284                        String desc = cube.getDescription();
3285                        if (desc == null) {
3286                            //TODO: currently this is always null
3287                            desc = catalogName + " - " + cubeName + " Cube";
3288                        }
3289                        //TODO: SQL Server returns 1000000 for all tables
3290                        int cardinality = 1000000;
3291                        String version = "null";
3292    
3293                        Row row = new Row();
3294                        row.set(TableCatalog.name, catalogName);
3295                        row.set(TableName.name, cubeName);
3296                        row.set(TableType.name, "TABLE");
3297                        row.set(Bookmarks.name, false);
3298                        row.set(TableVersion.name, version);
3299                        row.set(Cardinality.name, cardinality);
3300                        row.set(Description.name, desc);
3301                        addRow(row, rows);
3302                    }
3303                }
3304            }
3305    
3306            protected void setProperty(PropertyDefinition propertyDef, String value) {
3307                switch (propertyDef) {
3308                case Content:
3309                    break;
3310                default:
3311                    super.setProperty(propertyDef, value);
3312                }
3313            }
3314        }
3315    
3316        static class MdschemaActionsRowset extends Rowset {
3317            MdschemaActionsRowset(XmlaRequest request, XmlaHandler handler) {
3318                super(MDSCHEMA_ACTIONS, request, handler);
3319            }
3320    
3321            private static final Column SchemaName =
3322                new Column(
3323                    "SCHEMA_NAME",
3324                    Type.String,
3325                    null,
3326                    Column.RESTRICTION,
3327                    Column.OPTIONAL,
3328                    "The name of the schema to which this action belongs.");
3329            private static final Column CubeName =
3330                new Column(
3331                    "CUBE_NAME",
3332                    Type.String,
3333                    null,
3334                    Column.RESTRICTION,
3335                    Column.REQUIRED,
3336                    "The name of the cube to which this action belongs.");
3337            private static final Column ActionName =
3338                new Column(
3339                    "ACTION_NAME",
3340                    Type.String,
3341                    null,
3342                    Column.RESTRICTION,
3343                    Column.REQUIRED,
3344                    "The name of the action.");
3345            private static final Column Coordinate =
3346                new Column(
3347                    "COORDINATE",
3348                    Type.String,
3349                    null,
3350                    Column.RESTRICTION,
3351                    Column.REQUIRED,
3352                    null);
3353            private static final Column CoordinateType =
3354                new Column(
3355                    "COORDINATE_TYPE",
3356                    Type.Integer,
3357                    null,
3358                    Column.RESTRICTION,
3359                    Column.REQUIRED,
3360                    null);
3361            /*
3362                TODO: optional columns
3363            ACTION_TYPE
3364            INVOCATION
3365            CUBE_SOURCE
3366        */
3367    
3368            public void populate(
3369                XmlaResponse response,
3370                List<Row> rows)
3371                throws XmlaException
3372            {
3373                // mondrian doesn't support actions. It's not an error to ask for
3374                // them, there just aren't any
3375            }
3376        }
3377    
3378        // REF http://msdn.microsoft.com/library/en-us/oledb/htm/olapcubes_rowset.asp
3379        static class MdschemaCubesRowset extends Rowset {
3380            private final RestrictionTest catalogNameRT;
3381            private final RestrictionTest schemaNameRT;
3382            private final RestrictionTest cubeNameRT;
3383    
3384            MdschemaCubesRowset(XmlaRequest request, XmlaHandler handler) {
3385                super(MDSCHEMA_CUBES, request, handler);
3386                catalogNameRT = getRestrictionTest(CatalogName);
3387                schemaNameRT = getRestrictionTest(SchemaName);
3388                cubeNameRT = getRestrictionTest(CubeName);
3389            }
3390    
3391            private static final String MD_CUBTYPE_CUBE = "CUBE";
3392            private static final String MD_CUBTYPE_VIRTUAL_CUBE = "VIRTUAL CUBE";
3393    
3394            private static final Column CatalogName =
3395                new Column(
3396                    "CATALOG_NAME",
3397                    Type.String,
3398                    null,
3399                    Column.RESTRICTION,
3400                    Column.OPTIONAL,
3401                    "The name of the catalog to which this cube belongs.");
3402            private static final Column SchemaName =
3403                new Column(
3404                    "SCHEMA_NAME",
3405                    Type.String,
3406                    null,
3407                    Column.RESTRICTION,
3408                    Column.OPTIONAL,
3409                    "The name of the schema to which this cube belongs.");
3410            private static final Column CubeName =
3411                new Column(
3412                    "CUBE_NAME",
3413                    Type.String,
3414                    null,
3415                    Column.RESTRICTION,
3416                    Column.REQUIRED,
3417                    "Name of the cube.");
3418            private static final Column CubeType =
3419                new Column(
3420                    "CUBE_TYPE",
3421                    Type.String,
3422                    null,
3423                    Column.RESTRICTION,
3424                    Column.REQUIRED,
3425                    "Cube type.");
3426            private static final Column CubeGuid =
3427                new Column(
3428                    "CUBE_GUID",
3429                    Type.UUID,
3430                    null,
3431                    Column.NOT_RESTRICTION,
3432                    Column.OPTIONAL,
3433                    "Cube type.");
3434            private static final Column CreatedOn =
3435                new Column(
3436                    "CREATED_ON",
3437                    Type.DateTime,
3438                    null,
3439                    Column.NOT_RESTRICTION,
3440                    Column.OPTIONAL,
3441                    "Date and time of cube creation.");
3442            private static final Column LastSchemaUpdate =
3443                new Column(
3444                    "LAST_SCHEMA_UPDATE",
3445                    Type.DateTime,
3446                    null,
3447                    Column.NOT_RESTRICTION,
3448                    Column.OPTIONAL,
3449                    "Date and time of last schema update.");
3450            private static final Column SchemaUpdatedBy =
3451                new Column(
3452                    "SCHEMA_UPDATED_BY",
3453                    Type.String,
3454                    null,
3455                    Column.NOT_RESTRICTION,
3456                    Column.OPTIONAL,
3457                    "User ID of the person who last updated the schema.");
3458            private static final Column LastDataUpdate =
3459                new Column(
3460                    "LAST_DATA_UPDATE",
3461                    Type.DateTime,
3462                    null,
3463                    Column.NOT_RESTRICTION,
3464                    Column.OPTIONAL,
3465                    "Date and time of last data update.");
3466            private static final Column DataUpdatedBy =
3467                new Column(
3468                    "DATA_UPDATED_BY",
3469                    Type.String,
3470                    null,
3471                    Column.NOT_RESTRICTION,
3472                    Column.OPTIONAL,
3473                    "User ID of the person who last updated the data. ");
3474            private static final Column IsDrillthroughEnabled =
3475                new Column(
3476                    "IS_DRILLTHROUGH_ENABLED",
3477                    Type.Boolean,
3478                    null,
3479                    Column.NOT_RESTRICTION,
3480                    Column.REQUIRED,
3481                    "Describes whether DRILLTHROUGH can be performed on the members of a cube");
3482            private static final Column IsWriteEnabled =
3483                new Column(
3484                    "IS_WRITE_ENABLED",
3485                    Type.Boolean,
3486                    null,
3487                    Column.NOT_RESTRICTION,
3488                    Column.REQUIRED,
3489                    "Describes whether a cube is write-enabled");
3490            private static final Column IsLinkable =
3491                new Column(
3492                    "IS_LINKABLE",
3493                    Type.Boolean,
3494                    null,
3495                    Column.NOT_RESTRICTION,
3496                    Column.REQUIRED,
3497                    "Describes whether a cube can be used in a linked cube");
3498            private static final Column IsSqlEnabled =
3499                new Column(
3500                    "IS_SQL_ENABLED",
3501                    Type.Boolean,
3502                    null,
3503                    Column.NOT_RESTRICTION,
3504                    Column.REQUIRED,
3505                    "Describes whether or not SQL can be used on the cube");
3506            private static final Column Description =
3507                new Column(
3508                    "DESCRIPTION",
3509                    Type.String,
3510                    null,
3511                    Column.NOT_RESTRICTION,
3512                    Column.OPTIONAL,
3513                    "A user-friendly description of the dimension.");
3514    
3515            public void populate(
3516                XmlaResponse response,
3517                List<Row> rows)
3518                throws XmlaException
3519            {
3520                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
3521                DataSourcesConfig.Catalog[] catalogs =
3522                    handler.getCatalogs(request, ds);
3523                String roleName = request.getRoleName();
3524                Role role = request.getRole();
3525    
3526                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
3527                    if (dsCatalog == null || dsCatalog.definition == null) {
3528                        continue;
3529                    }
3530                    Connection connection =
3531                        handler.getConnection(dsCatalog, role, roleName);
3532                    if (connection == null) {
3533                        continue;
3534                    }
3535                    String catalogName = dsCatalog.name;
3536                    if (!catalogNameRT.passes(catalogName)) {
3537                        continue;
3538                    }
3539    
3540                    final Schema schema = connection.getSchema();
3541                    if (!schemaNameRT.passes(schema.getName())) {
3542                        continue;
3543                    }
3544                    for (Cube cube : sortedCubes(schema)) {
3545                        // Access control
3546                        if (!canAccess(schema.getSchemaReader(), cube)) {
3547                            continue;
3548                        }
3549                        if (!cubeNameRT.passes(cube.getName())) {
3550                            continue;
3551                        }
3552    
3553                        String desc = cube.getDescription();
3554                        if (desc == null) {
3555                            desc = catalogName +
3556                                " Schema - " +
3557                                cube.getName() +
3558                                " Cube";
3559                        }
3560    
3561                        Row row = new Row();
3562                        row.set(CatalogName.name, catalogName);
3563                        row.set(SchemaName.name, schema.getName());
3564                        row.set(CubeName.name, cube.getName());
3565                        row.set(CubeType.name,
3566                            ((RolapCube) cube).isVirtual()
3567                                ? MD_CUBTYPE_VIRTUAL_CUBE : MD_CUBTYPE_CUBE);
3568                        //row.set(CubeGuid.name, "");
3569                        //row.set(CreatedOn.name, "");
3570                        //row.set(LastSchemaUpdate.name, "");
3571                        //row.set(SchemaUpdatedBy.name, "");
3572                        //row.set(LastDataUpdate.name, "");
3573                        //row.set(DataUpdatedBy.name, "");
3574                        row.set(IsDrillthroughEnabled.name, true);
3575                        row.set(IsWriteEnabled.name, false);
3576                        row.set(IsLinkable.name, false);
3577                        row.set(IsSqlEnabled.name, false);
3578                        row.set(Description.name, desc);
3579                        Format formatter =
3580                            new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
3581                        String formattedDate =
3582                            formatter.format(schema.getSchemaLoadDate());
3583                        row.set(LastSchemaUpdate.name, formattedDate);
3584                        addRow(row, rows);
3585                    }
3586                }
3587            }
3588    
3589            protected void setProperty(PropertyDefinition propertyDef, String value) {
3590                switch (propertyDef) {
3591                case Content:
3592                    break;
3593                default:
3594                    super.setProperty(propertyDef, value);
3595                }
3596            }
3597        }
3598    
3599        // REF http://msdn.microsoft.com/library/en-us/oledb/htm/olapdimensions_rowset.asp
3600        static class MdschemaDimensionsRowset extends Rowset {
3601            private final RestrictionTest catalogRT;
3602            private final RestrictionTest schemaNameRT;
3603            private final RestrictionTest cubeNameRT;
3604            private final RestrictionTest dimensionUniqueNameRT;
3605            private final RestrictionTest dimensionNameRT;
3606    
3607            MdschemaDimensionsRowset(XmlaRequest request, XmlaHandler handler) {
3608                super(MDSCHEMA_DIMENSIONS, request, handler);
3609                catalogRT = getRestrictionTest(CatalogName);
3610                schemaNameRT = getRestrictionTest(SchemaName);
3611                cubeNameRT = getRestrictionTest(CubeName);
3612                dimensionUniqueNameRT = getRestrictionTest(DimensionUniqueName);
3613                dimensionNameRT = getRestrictionTest(DimensionName);
3614            }
3615    
3616            public static final int MD_DIMTYPE_OTHER = 3;
3617            public static final int MD_DIMTYPE_MEASURE = 2;
3618            public static final int MD_DIMTYPE_TIME = 1;
3619    
3620            private static final Column CatalogName =
3621                new Column(
3622                    "CATALOG_NAME",
3623                    Type.String,
3624                    null,
3625                    Column.RESTRICTION,
3626                    Column.OPTIONAL,
3627                    "The name of the database.");
3628            private static final Column SchemaName =
3629                new Column(
3630                    "SCHEMA_NAME",
3631                    Type.String,
3632                    null,
3633                    Column.RESTRICTION,
3634                    Column.OPTIONAL,
3635                    "Not supported.");
3636            private static final Column CubeName =
3637                new Column(
3638                    "CUBE_NAME",
3639                    Type.String,
3640                    null,
3641                    Column.RESTRICTION,
3642                    Column.REQUIRED,
3643                    "The name of the cube.");
3644            private static final Column DimensionName =
3645                new Column(
3646                    "DIMENSION_NAME",
3647                    Type.String,
3648                    null,
3649                    Column.RESTRICTION,
3650                    Column.REQUIRED,
3651                    "The name of the dimension. ");
3652            private static final Column DimensionUniqueName =
3653                new Column(
3654                    "DIMENSION_UNIQUE_NAME",
3655                    Type.String,
3656                    null,
3657                    Column.RESTRICTION,
3658                    Column.REQUIRED,
3659                    "The unique name of the dimension.");
3660            private static final Column DimensionGuid =
3661                new Column(
3662                    "DIMENSION_GUID",
3663                    Type.UUID,
3664                    null,
3665                    Column.NOT_RESTRICTION,
3666                    Column.OPTIONAL,
3667                    "Not supported.");
3668            private static final Column DimensionCaption =
3669                new Column(
3670                    "DIMENSION_CAPTION",
3671                    Type.String,
3672                    null,
3673                    Column.NOT_RESTRICTION,
3674                    Column.REQUIRED,
3675                    "The caption of the dimension.");
3676            private static final Column DimensionOrdinal =
3677                new Column(
3678                    "DIMENSION_ORDINAL",
3679                    Type.UnsignedInteger,
3680                    null,
3681                    Column.NOT_RESTRICTION,
3682                    Column.REQUIRED,
3683                    "The position of the dimension within the cube.");
3684            /*
3685             * SQL Server returns values:
3686             *   MD_DIMTYPE_TIME (1)
3687             *   MD_DIMTYPE_MEASURE (2)
3688             *   MD_DIMTYPE_OTHER (3)
3689             */
3690            private static final Column DimensionType =
3691                new Column(
3692                    "DIMENSION_TYPE",
3693                    Type.Short,
3694                    null,
3695                    Column.NOT_RESTRICTION,
3696                    Column.REQUIRED,
3697                    "The type of the dimension.");
3698            private static final Column DimensionCardinality =
3699                new Column(
3700                    "DIMENSION_CARDINALITY",
3701                    Type.UnsignedInteger,
3702                    null,
3703                    Column.NOT_RESTRICTION,
3704                    Column.REQUIRED,
3705                    "The number of members in the key attribute.");
3706            private static final Column DefaultHierarchy =
3707                new Column(
3708                    "DEFAULT_HIERARCHY",
3709                    Type.String,
3710                    null,
3711                    Column.NOT_RESTRICTION,
3712                    Column.REQUIRED,
3713                    "A hierarchy from the dimension. Preserved for backwards compatibility.");
3714            private static final Column Description =
3715                new Column(
3716                    "DESCRIPTION",
3717                    Type.String,
3718                    null,
3719                    Column.NOT_RESTRICTION,
3720                    Column.OPTIONAL,
3721                    "A user-friendly description of the dimension.");
3722            private static final Column IsVirtual =
3723                new Column(
3724                    "IS_VIRTUAL",
3725                    Type.Boolean,
3726                    null,
3727                    Column.NOT_RESTRICTION,
3728                    Column.OPTIONAL,
3729                    "Always FALSE.");
3730            private static final Column IsReadWrite =
3731                new Column("IS_READWRITE",
3732                    Type.Boolean,
3733                    null,
3734                    Column.NOT_RESTRICTION,
3735                    Column.OPTIONAL,
3736                    "A Boolean that indicates whether the dimension is write-enabled.");
3737            /*
3738             * SQL Server returns values: 0 or 1
3739             */
3740            private static final Column DimensionUniqueSettings =
3741                new Column(
3742                    "DIMENSION_UNIQUE_SETTINGS",
3743                    Type.Integer,
3744                    null,
3745                    Column.NOT_RESTRICTION,
3746                    Column.OPTIONAL,
3747                    "A bitmap that specifies which columns contain unique values if the dimension contains only members with unique names.");
3748            private static final Column DimensionMasterUniqueName =
3749                new Column(
3750                    "DIMENSION_MASTER_UNIQUE_NAME",
3751                    Type.String,
3752                    null,
3753                    Column.NOT_RESTRICTION,
3754                    Column.OPTIONAL,
3755                    "Always NULL.");
3756            private static final Column DimensionIsVisible =
3757                new Column(
3758                    "DIMENSION_IS_VISIBLE",
3759                    Type.Boolean,
3760                    null,
3761                    Column.NOT_RESTRICTION,
3762                    Column.OPTIONAL,
3763                    "Always TRUE.");
3764    
3765            public void populate(
3766                XmlaResponse response,
3767                List<Row> rows)
3768                throws XmlaException
3769            {
3770                DataSourcesConfig.DataSource ds =
3771                    handler.getDataSource(request);
3772                DataSourcesConfig.Catalog[] catalogs =
3773                    handler.getCatalogs(request, ds);
3774                String roleName = request.getRoleName();
3775                Role role = request.getRole();
3776    
3777                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
3778                    if (dsCatalog == null || dsCatalog.definition == null) {
3779                        continue;
3780                    }
3781                    String catalogName = dsCatalog.name;
3782                    if (!catalogRT.passes(catalogName)) {
3783                        continue;
3784                    }
3785    
3786                    Connection connection =
3787                        handler.getConnection(dsCatalog, role, roleName);
3788                    if (connection == null) {
3789                        continue;
3790                    }
3791                    populateCatalog(connection, catalogName, rows);
3792                }
3793            }
3794    
3795            protected void populateCatalog(
3796                Connection connection,
3797                String catalogName,
3798                List<Row> rows)
3799                throws XmlaException
3800            {
3801                Schema schema = connection.getSchema();
3802                if (!schemaNameRT.passes(schema.getName())) {
3803                    return;
3804                }
3805                for (Cube cube : sortedCubes(schema)) {
3806                    if (!cubeNameRT.passes(cube.getName())) {
3807                        continue;
3808                    }
3809                    SchemaReader schemaReader =
3810                        cube.getSchemaReader(
3811                            connection.getRole());
3812                    populateCube(schemaReader, catalogName, cube, rows);
3813                }
3814            }
3815    
3816            protected void populateCube(
3817                SchemaReader schemaReader,
3818                String catalogName,
3819                Cube cube,
3820                List<Row> rows)
3821                throws XmlaException
3822            {
3823                for (Dimension dimension : cube.getDimensions()) {
3824                    String name = dimension.getName();
3825                    String unique = dimension.getUniqueName();
3826                    if (dimensionNameRT.passes(name) &&
3827                        dimensionUniqueNameRT.passes(unique)) {
3828                        populateDimension(schemaReader, catalogName,
3829                            cube, dimension, rows);
3830                    }
3831                }
3832            }
3833    
3834            protected void populateDimension(
3835                SchemaReader schemaReader,
3836                String catalogName,
3837                Cube cube,
3838                Dimension dimension,
3839                List<Row> rows)
3840                throws XmlaException
3841            {
3842                // Access control
3843                if (!canAccess(schemaReader, dimension)) {
3844                    return;
3845                }
3846    
3847                String desc = dimension.getDescription();
3848                if (desc == null) {
3849                    desc = cube.getName() +
3850                        " Cube - " +
3851                        dimension.getName() +
3852                        " Dimension";
3853                }
3854    
3855                Row row = new Row();
3856                row.set(CatalogName.name, catalogName);
3857                row.set(SchemaName.name, cube.getSchema().getName());
3858                row.set(CubeName.name, cube.getName());
3859                row.set(DimensionName.name, dimension.getName());
3860                row.set(DimensionUniqueName.name, dimension.getUniqueName());
3861                row.set(DimensionCaption.name, dimension.getCaption());
3862                row.set(DimensionOrdinal.name, dimension.getOrdinal(cube));
3863                row.set(DimensionType.name, getDimensionType(dimension));
3864    
3865                //Is this the number of primaryKey members there are??
3866                // According to microsoft this is:
3867                //    "The number of members in the key attribute."
3868                // There may be a better way of doing this but
3869                // this is what I came up with. Note that I need to
3870                // add '1' to the number inorder for it to match
3871                // match what microsoft SQL Server is producing.
3872                // The '1' might have to do with whether or not the
3873                // hierarchy has a 'all' member or not - don't know yet.
3874                // large data set total for Orders cube 0m42.923s
3875                Hierarchy firstHierarchy = dimension.getHierarchies()[0];
3876                Level[] levels = firstHierarchy.getLevels();
3877                Level lastLevel = levels[levels.length - 1];
3878    
3879    
3880    
3881                /*
3882                if override config setting is set
3883                    if approxRowCount has a value
3884                        use it
3885                else
3886                                        do default
3887                */
3888    
3889                // Added by TWI to returned cached row numbers
3890                int n = schemaReader.getLevelCardinality(lastLevel, true, true);
3891                row.set(DimensionCardinality.name, n + 1);
3892    
3893                // TODO: I think that this is just the dimension name
3894                row.set(DefaultHierarchy.name, dimension.getUniqueName());
3895                row.set(Description.name, desc);
3896                row.set(IsVirtual.name, false);
3897                // SQL Server always returns false
3898                row.set(IsReadWrite.name, false);
3899                // TODO: don't know what to do here
3900                // Are these the levels with uniqueMembers == true?
3901                // How are they mapped to specific column numbers?
3902                row.set(DimensionUniqueSettings.name, 0);
3903                row.set(DimensionIsVisible.name, true);
3904    
3905                addRow(row, rows);
3906            }
3907    
3908            protected void setProperty(PropertyDefinition propertyDef, String value) {
3909                switch (propertyDef) {
3910                case Content:
3911                    break;
3912                default:
3913                    super.setProperty(propertyDef, value);
3914                }
3915            }
3916        }
3917    
3918        static int getDimensionType(Dimension dim) {
3919            if (dim.isMeasures()) {
3920                return MdschemaDimensionsRowset.MD_DIMTYPE_MEASURE;
3921            } else if (DimensionType.TimeDimension.equals(dim.getDimensionType())) {
3922                return MdschemaDimensionsRowset.MD_DIMTYPE_TIME;
3923            } else {
3924                return MdschemaDimensionsRowset.MD_DIMTYPE_OTHER;
3925            }
3926        }
3927    
3928        static class MdschemaFunctionsRowset extends Rowset {
3929            /**
3930             * http://www.csidata.com/custserv/onlinehelp/VBSdocs/vbs57.htm
3931             */
3932            enum VarType {
3933                Empty("Uninitialized (default)"),
3934                Null("Contains no valid data"),
3935                Integer("Integer subtype"),
3936                Long("Long subtype"),
3937                Single("Single subtype"),
3938                Double("Double subtype"),
3939                Currency("Currency subtype"),
3940                Date("Date subtype"),
3941                String("String subtype"),
3942                Object("Object subtype"),
3943                Error("Error subtype"),
3944                Boolean("Boolean subtype"),
3945                Variant("Variant subtype"),
3946                DataObject("DataObject subtype"),
3947                Decimal("Decimal subtype"),
3948                Byte("Byte subtype"),
3949                Array("Array subtype");
3950    
3951                static VarType forCategory(int category) {
3952                    switch (category) {
3953                    case Category.Unknown:
3954                        // expression == unknown ???
3955                        // case Category.Expression:
3956                        return Empty;
3957                    case Category.Array:
3958                        return Array;
3959                    case Category.Dimension:
3960                    case Category.Hierarchy:
3961                    case Category.Level:
3962                    case Category.Member:
3963                    case Category.Set:
3964                    case Category.Tuple:
3965                    case Category.Cube:
3966                    case Category.Value:
3967                        return Variant;
3968                    case Category.Logical:
3969                        return Boolean;
3970                    case Category.Numeric:
3971                        return Double;
3972                    case Category.String:
3973                    case Category.Symbol:
3974                    case Category.Constant:
3975                        return String;
3976                    case Category.DateTime:
3977                        return Date;
3978                    case Category.Integer:
3979                    case Category.Mask:
3980                        return Integer;
3981                    }
3982                    // NOTE: this should never happen
3983                    return Empty;
3984                }
3985    
3986                VarType(String description) {
3987                    Util.discard(description);
3988                }
3989            }
3990    
3991            private final RestrictionTest functionNameRT;
3992    
3993            MdschemaFunctionsRowset(XmlaRequest request, XmlaHandler handler) {
3994                super(MDSCHEMA_FUNCTIONS, request, handler);
3995                functionNameRT = getRestrictionTest(FunctionName);
3996            }
3997    
3998            private static final Column FunctionName =
3999                new Column(
4000                    "FUNCTION_NAME",
4001                    Type.String,
4002                    null,
4003                    Column.RESTRICTION,
4004                    Column.REQUIRED,
4005                    "The name of the function.");
4006            private static final Column Description =
4007                new Column(
4008                    "DESCRIPTION",
4009                    Type.String,
4010                    null,
4011                    Column.NOT_RESTRICTION,
4012                    Column.OPTIONAL,
4013                    "A description of the function.");
4014            private static final Column ParameterList =
4015                new Column(
4016                    "PARAMETER_LIST",
4017                    Type.String,
4018                    null,
4019                    Column.NOT_RESTRICTION,
4020                    Column.OPTIONAL,
4021                    "A comma delimited list of parameters.");
4022            private static final Column ReturnType =
4023                new Column(
4024                    "RETURN_TYPE",
4025                    Type.Integer,
4026                    null,
4027                    Column.NOT_RESTRICTION,
4028                    Column.REQUIRED,
4029                    "The VARTYPE of the return data type of the function.");
4030            private static final Column Origin =
4031                new Column(
4032                    "ORIGIN",
4033                    Type.Integer,
4034                    null,
4035                    Column.RESTRICTION,
4036                    Column.REQUIRED,
4037                    "The origin of the function:  1 for MDX functions.  2 for user-defined functions.");
4038            private static final Column InterfaceName =
4039                new Column(
4040                    "INTERFACE_NAME",
4041                    Type.String,
4042                    null,
4043                    Column.RESTRICTION,
4044                    Column.REQUIRED,
4045                    "The name of the interface for user-defined functions");
4046            private static final Column LibraryName =
4047                new Column(
4048                    "LIBRARY_NAME",
4049                    Type.String,
4050                    null,
4051                    Column.RESTRICTION,
4052                    Column.OPTIONAL,
4053                    "The name of the type library for user-defined functions. NULL for MDX functions.");
4054            private static final Column Caption =
4055                new Column(
4056                    "CAPTION",
4057                    Type.String,
4058                    null,
4059                    Column.NOT_RESTRICTION,
4060                    Column.OPTIONAL,
4061                    "The display caption for the function.");
4062    
4063            public void populate(
4064                XmlaResponse response,
4065                List<Row> rows)
4066                throws XmlaException
4067            {
4068                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
4069                DataSourcesConfig.Catalog[] catalogs =
4070                    handler.getCatalogs(request, ds);
4071                String roleName = request.getRoleName();
4072                Role role = request.getRole();
4073    
4074                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
4075                    if (dsCatalog == null || dsCatalog.definition == null) {
4076                        continue;
4077                    }
4078                    Connection connection =
4079                        handler.getConnection(dsCatalog, role, roleName);
4080                    if (connection == null) {
4081                        continue;
4082                    }
4083                    final Schema schema = connection.getSchema();
4084                    FunTable funTable = schema.getFunTable();
4085    
4086                    StringBuilder buf = new StringBuilder(50);
4087                    List<FunInfo> functions = funTable.getFunInfoList();
4088                    for (FunInfo fi : functions) {
4089                        switch (fi.getSyntax()) {
4090                        case Empty:
4091                        case Internal:
4092                        case Parentheses:
4093                            continue;
4094                        }
4095                        if (!functionNameRT.passes(fi.getName())) {
4096                            continue;
4097                        }
4098    
4099                        int[][] paramCategories = fi.getParameterCategories();
4100                        int[] returnCategories = fi.getReturnCategories();
4101    
4102                        // Convert Windows newlines in 'description' to UNIX format.
4103                        String description = fi.getDescription();
4104                        if (description != null) {
4105                            description = Util.replace(fi.getDescription(),
4106                                "\r",
4107                                "");
4108                        }
4109                        if ((paramCategories == null) ||
4110                            (paramCategories.length == 0)) {
4111                            Row row = new Row();
4112                            row.set(FunctionName.name, fi.getName());
4113                            row.set(Description.name, description);
4114                            row.set(ParameterList.name, "(none)");
4115                            row.set(ReturnType.name, 1);
4116                            row.set(Origin.name, 1);
4117                            //row.set(LibraryName.name, "");
4118                            // TODO WHAT VALUE should this have
4119                            row.set(InterfaceName.name, "");
4120                            row.set(Caption.name, fi.getName());
4121                            addRow(row, rows);
4122    
4123                        } else {
4124                            for (int i = 0; i < paramCategories.length; i++) {
4125                                int[] pc = paramCategories[i];
4126                                int returnCategory = returnCategories[i];
4127    
4128                                Row row = new Row();
4129                                row.set(FunctionName.name, fi.getName());
4130                                row.set(Description.name, description);
4131    
4132                                buf.setLength(0);
4133                                for (int j = 0; j < pc.length; j++) {
4134                                    int v = pc[j];
4135                                    if (j > 0) {
4136                                        buf.append(", ");
4137                                    }
4138                                    buf.append(Category.instance.getDescription(
4139                                        v & Category.Mask));
4140                                }
4141                                row.set(ParameterList.name, buf.toString());
4142    
4143                                VarType varType = VarType.forCategory(returnCategory);
4144                                row.set(ReturnType.name, varType.ordinal());
4145    
4146                                //TODO: currently FunInfo can not tell us which
4147                                // functions are MDX and which are UDFs.
4148                                row.set(Origin.name, 1);
4149    
4150                                // TODO: Name of the type library for UDFs. NULL for MDX
4151                                // functions.
4152                                //row.set(LibraryName.name, "");
4153    
4154                                // TODO: Name of the interface for UDF and Group name
4155                                // for the MDX functions.
4156                                // TODO WHAT VALUE should this have
4157                                row.set(InterfaceName.name, "");
4158    
4159                                row.set(Caption.name, fi.getName());
4160                                addRow(row, rows);
4161                            }
4162                        }
4163                    }
4164                }
4165            }
4166    
4167            protected void setProperty(PropertyDefinition propertyDef, String value) {
4168                switch (propertyDef) {
4169                case Content:
4170                    break;
4171                default:
4172                    super.setProperty(propertyDef, value);
4173                }
4174            }
4175        }
4176    
4177        static class MdschemaHierarchiesRowset extends Rowset {
4178            private final RestrictionTest catalogRT;
4179            private final RestrictionTest schemaNameRT;
4180            private final RestrictionTest cubeNameRT;
4181            private final RestrictionTest dimensionUniqueNameRT;
4182            private final RestrictionTest hierarchyUniqueNameRT;
4183            private final RestrictionTest hierarchyNameRT;
4184    
4185            MdschemaHierarchiesRowset(XmlaRequest request, XmlaHandler handler) {
4186                super(MDSCHEMA_HIERARCHIES, request, handler);
4187                catalogRT = getRestrictionTest(CatalogName);
4188                schemaNameRT = getRestrictionTest(SchemaName);
4189                cubeNameRT = getRestrictionTest(CubeName);
4190                dimensionUniqueNameRT = getRestrictionTest(DimensionUniqueName);
4191                hierarchyUniqueNameRT = getRestrictionTest(HierarchyUniqueName);
4192                hierarchyNameRT = getRestrictionTest(HierarchyName);
4193            }
4194    
4195            private static final Column CatalogName =
4196                new Column(
4197                    "CATALOG_NAME",
4198                    Type.String,
4199                    null,
4200                    Column.RESTRICTION,
4201                    Column.OPTIONAL,
4202                    "The name of the catalog to which this hierarchy belongs.");
4203            private static final Column SchemaName =
4204                new Column(
4205                    "SCHEMA_NAME",
4206                    Type.String,
4207                    null,
4208                    Column.RESTRICTION,
4209                    Column.OPTIONAL,
4210                    "Not supported");
4211            private static final Column CubeName =
4212                new Column(
4213                    "CUBE_NAME",
4214                    Type.String,
4215                    null,
4216                    Column.RESTRICTION,
4217                    Column.REQUIRED,
4218                    "The name of the cube to which this hierarchy belongs.");
4219            private static final Column DimensionUniqueName =
4220                new Column(
4221                    "DIMENSION_UNIQUE_NAME",
4222                    Type.String,
4223                    null,
4224                    Column.RESTRICTION,
4225                    Column.REQUIRED,
4226                    "The unique name of the dimension to which this hierarchy belongs. ");
4227            private static final Column HierarchyName =
4228                new Column(
4229                    "HIERARCHY_NAME",
4230                    Type.String,
4231                    null,
4232                    Column.RESTRICTION,
4233                    Column.REQUIRED,
4234                    "The name of the hierarchy. Blank if there is only a single hierarchy in the dimension.");
4235            private static final Column HierarchyUniqueName =
4236                new Column(
4237                    "HIERARCHY_UNIQUE_NAME",
4238                    Type.String,
4239                    null,
4240                    Column.RESTRICTION,
4241                    Column.REQUIRED,
4242                    "The unique name of the hierarchy.");
4243    
4244            private static final Column HierarchyGuid =
4245                new Column(
4246                    "HIERARCHY_GUID",
4247                    Type.UUID,
4248                    null,
4249                    Column.NOT_RESTRICTION,
4250                    Column.OPTIONAL,
4251                    "Hierarchy GUID.");
4252    
4253            private static final Column HierarchyCaption =
4254                new Column(
4255                    "HIERARCHY_CAPTION",
4256                    Type.String,
4257                    null,
4258                    Column.NOT_RESTRICTION,
4259                    Column.REQUIRED,
4260                    "A label or a caption associated with the hierarchy.");
4261            private static final Column DimensionType =
4262                new Column(
4263                    "DIMENSION_TYPE",
4264                    Type.Short,
4265                    null,
4266                    Column.NOT_RESTRICTION,
4267                    Column.REQUIRED,
4268                    "The type of the dimension. ");
4269            private static final Column HierarchyCardinality =
4270                new Column(
4271                    "HIERARCHY_CARDINALITY",
4272                    Type.UnsignedInteger,
4273                    null,
4274                    Column.NOT_RESTRICTION,
4275                    Column.REQUIRED,
4276                    "The number of members in the hierarchy.");
4277            private static final Column DefaultMember =
4278                new Column(
4279                    "DEFAULT_MEMBER",
4280                    Type.String,
4281                    null,
4282                    Column.NOT_RESTRICTION,
4283                    Column.OPTIONAL,
4284                    "The default member for this hierarchy. ");
4285            private static final Column AllMember =
4286                new Column(
4287                    "ALL_MEMBER",
4288                    Type.String,
4289                    null,
4290                    Column.NOT_RESTRICTION,
4291                    Column.OPTIONAL,
4292                    "The member at the highest level of rollup in the hierarchy.");
4293            private static final Column Description =
4294                new Column(
4295                    "DESCRIPTION",
4296                    Type.String,
4297                    null,
4298                    Column.NOT_RESTRICTION,
4299                    Column.OPTIONAL,
4300                    "A human-readable description of the hierarchy. NULL if no description exists.");
4301            private static final Column Structure =
4302                new Column(
4303                    "STRUCTURE",
4304                    Type.Short,
4305                    null,
4306                    Column.NOT_RESTRICTION,
4307                    Column.REQUIRED,
4308                    "The structure of the hierarchy.");
4309            private static final Column IsVirtual =
4310                new Column(
4311                    "IS_VIRTUAL",
4312                    Type.Boolean,
4313                    null,
4314                    Column.NOT_RESTRICTION,
4315                    Column.REQUIRED,
4316                    "Always returns False.");
4317            private static final Column IsReadWrite =
4318                new Column(
4319                    "IS_READWRITE",
4320                    Type.Boolean,
4321                    null,
4322                    Column.NOT_RESTRICTION,
4323                    Column.REQUIRED,
4324                    "A Boolean that indicates whether the Write Back to dimension column is enabled.");
4325            private static final Column DimensionUniqueSettings =
4326                new Column(
4327                    "DIMENSION_UNIQUE_SETTINGS",
4328                    Type.Integer,
4329                    null,
4330                    Column.NOT_RESTRICTION,
4331                    Column.REQUIRED,
4332                    "Always returns MDDIMENSIONS_MEMBER_KEY_UNIQUE (1).");
4333            private static final Column DimensionIsVisible =
4334                new Column(
4335                    "DIMENSION_IS_VISIBLE",
4336                    Type.Boolean,
4337                    null,
4338                    Column.NOT_RESTRICTION,
4339                    Column.REQUIRED,
4340                    "Always returns true.");
4341            private static final Column HierarchyOrdinal =
4342                new Column(
4343                    "HIERARCHY_ORDINAL",
4344                    Type.UnsignedInteger,
4345                    null,
4346                    Column.NOT_RESTRICTION,
4347                    Column.REQUIRED,
4348                    "The ordinal number of the hierarchy across all hierarchies of the cube.");
4349            private static final Column DimensionIsShared =
4350                new Column(
4351                    "DIMENSION_IS_SHARED",
4352                    Type.Boolean,
4353                    null,
4354                    Column.NOT_RESTRICTION,
4355                    Column.REQUIRED,
4356                    "Always returns true.");
4357    
4358    
4359            /*
4360             * NOTE: This is non-standard, where did it come from?
4361             */
4362            private static final Column ParentChild =
4363                new Column(
4364                    "PARENT_CHILD",
4365                    Type.Boolean,
4366                    null,
4367                    Column.NOT_RESTRICTION,
4368                    Column.OPTIONAL,
4369                    "Is hierarchy a parent.");
4370    
4371            public void populate(
4372                XmlaResponse response,
4373                List<Row> rows)
4374                throws XmlaException
4375            {
4376                DataSourcesConfig.DataSource ds =
4377                    handler.getDataSource(request);
4378                DataSourcesConfig.Catalog[] catalogs =
4379                    handler.getCatalogs(request, ds);
4380                String roleName = request.getRoleName();
4381                Role role = request.getRole();
4382    
4383                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
4384                    if (dsCatalog == null || dsCatalog.definition == null) {
4385                        continue;
4386                    }
4387                    String catalogName = dsCatalog.name;
4388                    if (!catalogRT.passes(catalogName)) {
4389                        continue;
4390                    }
4391    
4392                    Connection connection =
4393                        handler.getConnection(dsCatalog, role, roleName);
4394                    if (connection == null) {
4395                        continue;
4396                    }
4397                    populateCatalog(connection, catalogName, rows);
4398                }
4399            }
4400    
4401            protected void populateCatalog(
4402                Connection connection,
4403                String catalogName,
4404                List<Row> rows)
4405                throws XmlaException
4406            {
4407                Schema schema = connection.getSchema();
4408                if (!schemaNameRT.passes(schema.getName())) {
4409                    return;
4410                }
4411                for (Cube cube : sortedCubes(schema)) {
4412                    if (!cubeNameRT.passes(cube.getName())) {
4413                        continue;
4414                    }
4415                    // RME
4416                    //SchemaReader schemaReader = connection.getSchemaReader();
4417                    // want to pick up cube's
4418                    SchemaReader schemaReader = cube.getSchemaReader(connection.getRole());
4419                    populateCube(schemaReader, catalogName, cube, rows);
4420                }
4421            }
4422    
4423            protected void populateCube(
4424                SchemaReader schemaReader,
4425                String catalogName,
4426                Cube cube,
4427                List<Row> rows)
4428                throws XmlaException
4429            {
4430                int ordinal = 0;
4431                for (Dimension dimension : cube.getDimensions()) {
4432                    String unique = dimension.getUniqueName();
4433                    // Must increment ordinal for all dimensions but
4434                    // only output some of them.
4435                    boolean genOutput = dimensionUniqueNameRT.passes(unique);
4436                    ordinal = populateDimension(
4437                        genOutput,
4438                        schemaReader, catalogName,
4439                        cube, dimension, ordinal, rows);
4440                }
4441            }
4442    
4443            protected int populateDimension(
4444                boolean genOutput,
4445                SchemaReader schemaReader,
4446                String catalogName,
4447                Cube cube,
4448                Dimension dimension,
4449                int ordinal,
4450                List<Row> rows)
4451                throws XmlaException
4452            {
4453                Hierarchy[] hierarchies = dimension.getHierarchies();
4454                for (Hierarchy hierarchy : hierarchies) {
4455                    if (genOutput) {
4456                        String unique = hierarchy.getUniqueName();
4457                        if (hierarchyNameRT.passes(hierarchy.getName()) &&
4458                            hierarchyUniqueNameRT.passes(unique)) {
4459                            populateHierarchy(schemaReader, catalogName,
4460                                cube, dimension, (HierarchyBase) hierarchy,
4461                                ordinal++, rows);
4462                        } else {
4463                            ordinal++;
4464                        }
4465                    } else {
4466                        ordinal++;
4467                    }
4468                }
4469                return ordinal;
4470            }
4471    
4472            protected void populateHierarchy(
4473                SchemaReader schemaReader,
4474                String catalogName,
4475                Cube cube,
4476                Dimension dimension,
4477                HierarchyBase hierarchy,
4478                int ordinal,
4479                List<Row> rows)
4480                throws XmlaException
4481            {
4482                // Access control
4483                if (!canAccess(schemaReader, hierarchy)) {
4484                    return;
4485                }
4486    
4487                String desc = hierarchy.getDescription();
4488                if (desc == null) {
4489                    desc = cube.getName() +
4490                        " Cube - " +
4491                        hierarchy.getName() +
4492                        " Hierarchy";
4493                }
4494    
4495                Row row = new Row();
4496                row.set(CatalogName.name, catalogName);
4497                row.set(SchemaName.name, cube.getSchema().getName());
4498                row.set(CubeName.name, cube.getName());
4499                row.set(DimensionUniqueName.name, dimension.getUniqueName());
4500                row.set(HierarchyName.name, hierarchy.getName());
4501                row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
4502                //row.set(HierarchyGuid.name, "");
4503    
4504                row.set(HierarchyCaption.name, hierarchy.getCaption());
4505                row.set(DimensionType.name, getDimensionType(dimension));
4506                // The number of members in the hierarchy. Because
4507                // of the presence of multiple hierarchies, this number
4508                // might not be the same as DIMENSION_CARDINALITY. This
4509                // value can be an approximation of the real
4510                // cardinality. Consumers should not assume that this
4511                // value is accurate.
4512                int cardinality =
4513                    RolapMember.getHierarchyCardinality(schemaReader, hierarchy);
4514                row.set(HierarchyCardinality.name, cardinality);
4515    
4516                row.set(DefaultMember.name, hierarchy.getDefaultMember());
4517                if (hierarchy.hasAll()) {
4518                    row.set(AllMember.name,
4519                        Util.makeFqName(hierarchy, hierarchy.getAllMemberName()));
4520                }
4521                row.set(Description.name, desc);
4522    
4523                //TODO: only support:
4524                // MD_STRUCTURE_FULLYBALANCED (0)
4525                // MD_STRUCTURE_RAGGEDBALANCED (1)
4526                row.set(Structure.name, hierarchy.isRagged() ? 1 : 0);
4527    
4528                row.set(IsVirtual.name, false);
4529                row.set(IsReadWrite.name, false);
4530    
4531                // NOTE that SQL Server returns '0' not '1'.
4532                row.set(DimensionUniqueSettings.name, 0);
4533    
4534                // always true
4535                row.set(DimensionIsVisible.name, true);
4536    
4537                row.set(HierarchyOrdinal.name, ordinal);
4538    
4539                // always true
4540                row.set(DimensionIsShared.name, true);
4541    
4542                RolapLevel nonAllFirstLevel =
4543                    (RolapLevel) hierarchy.getLevels()[
4544                        (hierarchy.hasAll() ? 1 : 0)];
4545                row.set(ParentChild.name, nonAllFirstLevel.isParentChild());
4546                addRow(row, rows);
4547            }
4548    
4549            protected void setProperty(
4550                PropertyDefinition propertyDef,
4551                String value)
4552            {
4553                switch (propertyDef) {
4554                case Content:
4555                    break;
4556                default:
4557                    super.setProperty(propertyDef, value);
4558                }
4559            }
4560        }
4561    
4562        static class MdschemaLevelsRowset extends Rowset {
4563            private final RestrictionTest catalogRT;
4564            private final RestrictionTest schemaNameRT;
4565            private final RestrictionTest cubeNameRT;
4566            private final RestrictionTest dimensionUniqueNameRT;
4567            private final RestrictionTest hierarchyUniqueNameRT;
4568            private final RestrictionTest levelUniqueNameRT;
4569            private final RestrictionTest levelNameRT;
4570    
4571            MdschemaLevelsRowset(XmlaRequest request, XmlaHandler handler) {
4572                super(MDSCHEMA_LEVELS, request, handler);
4573                catalogRT = getRestrictionTest(CatalogName);
4574                schemaNameRT = getRestrictionTest(SchemaName);
4575                cubeNameRT = getRestrictionTest(CubeName);
4576                dimensionUniqueNameRT = getRestrictionTest(DimensionUniqueName);
4577                hierarchyUniqueNameRT = getRestrictionTest(HierarchyUniqueName);
4578                levelUniqueNameRT = getRestrictionTest(LevelUniqueName);
4579                levelNameRT = getRestrictionTest(LevelName);
4580            }
4581    
4582            public static final int MDLEVEL_TYPE_UNKNOWN = 0x0000;
4583            public static final int MDLEVEL_TYPE_REGULAR = 0x0000;
4584            public static final int MDLEVEL_TYPE_ALL = 0x0001;
4585            public static final int MDLEVEL_TYPE_CALCULATED = 0x0002;
4586            public static final int MDLEVEL_TYPE_TIME = 0x0004;
4587            public static final int MDLEVEL_TYPE_RESERVED1 = 0x0008;
4588            public static final int MDLEVEL_TYPE_TIME_YEARS = 0x0014;
4589            public static final int MDLEVEL_TYPE_TIME_HALF_YEAR = 0x0024;
4590            public static final int MDLEVEL_TYPE_TIME_QUARTERS = 0x0044;
4591            public static final int MDLEVEL_TYPE_TIME_MONTHS = 0x0084;
4592            public static final int MDLEVEL_TYPE_TIME_WEEKS = 0x0104;
4593            public static final int MDLEVEL_TYPE_TIME_DAYS = 0x0204;
4594            public static final int MDLEVEL_TYPE_TIME_HOURS = 0x0304;
4595            public static final int MDLEVEL_TYPE_TIME_MINUTES = 0x0404;
4596            public static final int MDLEVEL_TYPE_TIME_SECONDS = 0x0804;
4597            public static final int MDLEVEL_TYPE_TIME_UNDEFINED = 0x1004;
4598    
4599            private static final Column CatalogName =
4600                new Column(
4601                    "CATALOG_NAME",
4602                    Type.String,
4603                    null,
4604                    Column.RESTRICTION,
4605                    Column.OPTIONAL,
4606                    "The name of the catalog to which this level belongs.");
4607            private static final Column SchemaName =
4608                new Column(
4609                    "SCHEMA_NAME",
4610                    Type.String,
4611                    null,
4612                    Column.RESTRICTION,
4613                    Column.OPTIONAL,
4614                    "The name of the schema to which this level belongs.");
4615            private static final Column CubeName =
4616                new Column(
4617                    "CUBE_NAME",
4618                    Type.String,
4619                    null,
4620                    Column.RESTRICTION,
4621                    Column.REQUIRED,
4622                    "The name of the cube to which this level belongs.");
4623            private static final Column DimensionUniqueName =
4624                new Column(
4625                    "DIMENSION_UNIQUE_NAME",
4626                    Type.String,
4627                    null,
4628                    Column.RESTRICTION,
4629                    Column.REQUIRED,
4630                    "The unique name of the dimension to which this level belongs.");
4631            private static final Column HierarchyUniqueName =
4632                new Column(
4633                    "HIERARCHY_UNIQUE_NAME",
4634                    Type.String,
4635                    null,
4636                    Column.RESTRICTION,
4637                    Column.REQUIRED,
4638                    "The unique name of the hierarchy.");
4639            private static final Column LevelName =
4640                new Column(
4641                    "LEVEL_NAME",
4642                    Type.String,
4643                    null,
4644                    Column.RESTRICTION,
4645                    Column.REQUIRED,
4646                    "The name of the level.");
4647            private static final Column LevelUniqueName =
4648                new Column(
4649                    "LEVEL_UNIQUE_NAME",
4650                    Type.String,
4651                    null,
4652                    Column.RESTRICTION,
4653                    Column.REQUIRED,
4654                    "The properly escaped unique name of the level.");
4655            private static final Column LevelGuid =
4656                new Column(
4657                    "LEVEL_GUID",
4658                    Type.UUID,
4659                    null,
4660                    Column.NOT_RESTRICTION,
4661                    Column.OPTIONAL,
4662                    "Level GUID.");
4663            private static final Column LevelCaption =
4664                new Column(
4665                    "LEVEL_CAPTION",
4666                    Type.String,
4667                    null,
4668                    Column.NOT_RESTRICTION,
4669                    Column.REQUIRED,
4670                    "A label or caption associated with the hierarchy.");
4671            private static final Column LevelNumber =
4672                new Column(
4673                    "LEVEL_NUMBER",
4674                    Type.UnsignedInteger,
4675                    null,
4676                    Column.NOT_RESTRICTION,
4677                    Column.REQUIRED,
4678                    "The distance of the level from the root of the hierarchy. Root level is zero (0).");
4679            private static final Column LevelCardinality =
4680                new Column(
4681                    "LEVEL_CARDINALITY",
4682                    Type.UnsignedInteger,
4683                    null,
4684                    Column.NOT_RESTRICTION,
4685                    Column.REQUIRED,
4686                    "The number of members in the level. This value can be an approximation of the real cardinality.");
4687            private static final Column LevelType =
4688                new Column(
4689                    "LEVEL_TYPE",
4690                    Type.Integer,
4691                    null,
4692                    Column.NOT_RESTRICTION,
4693                    Column.REQUIRED,
4694                    "Type of the level");
4695            private static final Column CustomRollupSettings =
4696                new Column(
4697                    "CUSTOM_ROLLUP_SETTINGS",
4698                    Type.Integer,
4699                    null,
4700                    Column.NOT_RESTRICTION,
4701                    Column.REQUIRED,
4702                    "A bitmap that specifies the custom rollup options.");
4703            private static final Column LevelUniqueSettings =
4704                new Column(
4705                    "LEVEL_UNIQUE_SETTINGS",
4706                    Type.Integer,
4707                    null,
4708                    Column.NOT_RESTRICTION,
4709                    Column.REQUIRED,
4710                    "A bitmap that specifies which columns contain unique values, if the level only has members with unique names or keys.");
4711            private static final Column LevelIsVisible =
4712                new Column(
4713                    "LEVEL_IS_VISIBLE",
4714                    Type.Boolean,
4715                    null,
4716                    Column.NOT_RESTRICTION,
4717                    Column.REQUIRED,
4718                    "A Boolean that indicates whether the level is visible.");
4719            private static final Column Description =
4720                new Column(
4721                    "DESCRIPTION",
4722                    Type.String,
4723                    null,
4724                    Column.NOT_RESTRICTION,
4725                    Column.OPTIONAL,
4726                    "A human-readable description of the level. NULL if no description exists.");
4727    
4728            public void populate(
4729                XmlaResponse response,
4730                List<Row> rows)
4731                throws XmlaException
4732            {
4733                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
4734                DataSourcesConfig.Catalog[] catalogs =
4735                    handler.getCatalogs(request, ds);
4736                String roleName = request.getRoleName();
4737                Role role = request.getRole();
4738    
4739                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
4740                    if (dsCatalog == null || dsCatalog.definition == null) {
4741                        continue;
4742                    }
4743                    String catalogName = dsCatalog.name;
4744                    if (!catalogRT.passes(catalogName)) {
4745                        continue;
4746                    }
4747    
4748                    Connection connection =
4749                        handler.getConnection(dsCatalog, role, roleName);
4750                    if (connection == null) {
4751                        continue;
4752                    }
4753                    populateCatalog(connection, catalogName, rows);
4754                }
4755            }
4756    
4757            protected void populateCatalog(
4758                Connection connection,
4759                String catalogName,
4760                List<Row> rows)
4761                throws XmlaException
4762            {
4763                Schema schema = connection.getSchema();
4764                if (!schemaNameRT.passes(schema.getName())) {
4765                    return;
4766                }
4767                for (Cube cube : sortedCubes(schema)) {
4768                    if (!cubeNameRT.passes(cube.getName())) {
4769                        continue;
4770                    }
4771                    SchemaReader schemaReader =
4772                        cube.getSchemaReader(
4773                            connection.getRole());
4774                    populateCube(schemaReader, catalogName, cube, rows);
4775                }
4776            }
4777    
4778            protected void populateCube(
4779                SchemaReader schemaReader,
4780                String catalogName,
4781                Cube cube,
4782                List<Row> rows)
4783                throws XmlaException
4784            {
4785                for (Dimension dimension : cube.getDimensions()) {
4786                    String uniqueName = dimension.getUniqueName();
4787                    if (dimensionUniqueNameRT.passes(uniqueName)) {
4788                        populateDimension(schemaReader, catalogName,
4789                            cube, dimension, rows);
4790                    }
4791                }
4792            }
4793    
4794            protected void populateDimension(
4795                SchemaReader schemaReader,
4796                String catalogName,
4797                Cube cube,
4798                Dimension dimension,
4799                List<Row> rows)
4800                throws XmlaException
4801            {
4802                Hierarchy[] hierarchies = dimension.getHierarchies();
4803                for (Hierarchy hierarchy : hierarchies) {
4804                    String uniqueName = hierarchy.getUniqueName();
4805                    if (hierarchyUniqueNameRT.passes(uniqueName)) {
4806                        populateHierarchy(schemaReader, catalogName,
4807                            cube, hierarchy, rows);
4808                    }
4809                }
4810            }
4811    
4812            protected void populateHierarchy(
4813                SchemaReader schemaReader,
4814                String catalogName,
4815                Cube cube,
4816                Hierarchy hierarchy,
4817                List<Row> rows)
4818                throws XmlaException
4819            {
4820                for (Level level : schemaReader.getHierarchyLevels(hierarchy)) {
4821                    String uniqueName = level.getUniqueName();
4822                    String name = level.getName();
4823                    if (levelUniqueNameRT.passes(uniqueName) &&
4824                        levelNameRT.passes(name)) {
4825                        outputLevel(schemaReader,
4826                            catalogName, cube, hierarchy, level, rows);
4827                    }
4828                }
4829            }
4830    
4831            /**
4832             * Outputs a level.
4833             *
4834             * @param schemaReader Schema reader
4835             * @param catalogName Catalog name
4836             * @param cube Cube definition
4837             * @param hierarchy Hierarchy
4838             * @param level Level
4839             * @param rows List of rows to output to
4840             * @return whether the level is visible
4841             * @throws XmlaException If error occurs
4842             */
4843            protected boolean outputLevel(
4844                SchemaReader schemaReader,
4845                String catalogName,
4846                Cube cube,
4847                Hierarchy hierarchy,
4848                Level level,
4849                List<Row> rows)
4850                throws XmlaException
4851            {
4852                // Access control
4853                if (!canAccess(schemaReader, level)) {
4854                    return false;
4855                }
4856                String desc = level.getDescription();
4857                if (desc == null) {
4858                    desc = cube.getName() +
4859                        " Cube - " +
4860                        hierarchy.getName() +
4861                        " Hierarchy" +
4862                        level.getName() +
4863                        " Level";
4864                }
4865    
4866                int adjustedLevelDepth = level.getDepth();
4867                Role.HierarchyAccess hierarchyAccess =
4868                    schemaReader.getRole().getAccessDetails(hierarchy);
4869                if (hierarchyAccess != null) {
4870                    adjustedLevelDepth -= hierarchyAccess.getTopLevelDepth();
4871                }
4872    
4873                Row row = new Row();
4874                row.set(CatalogName.name, catalogName);
4875                row.set(SchemaName.name, cube.getSchema().getName());
4876                row.set(CubeName.name, cube.getName());
4877                row.set(DimensionUniqueName.name,
4878                    hierarchy.getDimension().getUniqueName());
4879                row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
4880                row.set(LevelName.name, level.getName());
4881                row.set(LevelUniqueName.name, level.getUniqueName());
4882                //row.set(LevelGuid.name, "");
4883                row.set(LevelCaption.name, level.getCaption());
4884                // see notes on this #getDepth()
4885                row.set(LevelNumber.name, adjustedLevelDepth);
4886    
4887                // Get level cardinality
4888                // According to microsoft this is:
4889                //   "The number of members in the level."
4890                int n = schemaReader.getLevelCardinality(level, true, true);
4891                row.set(LevelCardinality.name, n);
4892    
4893                row.set(LevelType.name, getLevelType(level));
4894    
4895                // TODO: most of the time this is correct
4896                row.set(CustomRollupSettings.name, 0);
4897    
4898                if (level instanceof RolapLevel) {
4899                    RolapLevel rl = (RolapLevel) level;
4900                    row.set(
4901                        LevelUniqueSettings.name,
4902                        (rl.isUnique() ? 1 : 0) +
4903                            (rl.isAll() ? 2 : 0));
4904                } else {
4905                    // can not access unique member attribute
4906                    // this is the best we can do.
4907                    row.set(
4908                        LevelUniqueSettings.name,
4909                        (level.isAll() ? 2 : 0));
4910                }
4911                row.set(LevelIsVisible.name, true);
4912                row.set(Description.name, desc);
4913                addRow(row, rows);
4914                return true;
4915            }
4916    
4917            private int getLevelType(Level lev) {
4918                int ret = 0;
4919    
4920                if (lev.isAll()) {
4921                    ret |= MDLEVEL_TYPE_ALL;
4922                }
4923    
4924                mondrian.olap.LevelType type = lev.getLevelType();
4925                switch (type) {
4926                case Regular:
4927                    ret |= MDLEVEL_TYPE_REGULAR;
4928                    break;
4929                case TimeDays:
4930                    ret |= MDLEVEL_TYPE_TIME_DAYS;
4931                    break;
4932                case TimeMonths:
4933                    ret |= MDLEVEL_TYPE_TIME_MONTHS;
4934                    break;
4935                case TimeQuarters:
4936                    ret |= MDLEVEL_TYPE_TIME_QUARTERS;
4937                    break;
4938                case TimeWeeks:
4939                    ret |= MDLEVEL_TYPE_TIME_WEEKS;
4940                    break;
4941                case TimeYears:
4942                    ret |= MDLEVEL_TYPE_TIME_YEARS;
4943                    break;
4944                default:
4945                    ret |= MDLEVEL_TYPE_UNKNOWN;
4946                }
4947    
4948                return ret;
4949            }
4950    
4951            protected void setProperty(PropertyDefinition propertyDef, String value) {
4952                switch (propertyDef) {
4953                case Content:
4954                    break;
4955                default:
4956                    super.setProperty(propertyDef, value);
4957                }
4958            }
4959        }
4960    
4961    
4962        // REF http://msdn.microsoft.com/library/en-us/oledb/htm/olapmeasures_rowset.asp
4963        static class MdschemaMeasuresRowset extends Rowset {
4964            public static final int MDMEASURE_AGGR_UNKNOWN = 0;
4965            public static final int MDMEASURE_AGGR_SUM = 1;
4966            public static final int MDMEASURE_AGGR_COUNT = 2;
4967            public static final int MDMEASURE_AGGR_MIN = 3;
4968            public static final int MDMEASURE_AGGR_MAX = 4;
4969            public static final int MDMEASURE_AGGR_AVG = 5;
4970            public static final int MDMEASURE_AGGR_VAR = 6;
4971            public static final int MDMEASURE_AGGR_STD = 7;
4972            public static final int MDMEASURE_AGGR_CALCULATED = 127;
4973    
4974            private final RestrictionTest catalogRT;
4975            private final RestrictionTest schemaNameRT;
4976            private final RestrictionTest cubeNameRT;
4977            private final RestrictionTest measureUniqueNameRT;
4978            private final RestrictionTest measureNameRT;
4979    
4980            MdschemaMeasuresRowset(XmlaRequest request, XmlaHandler handler) {
4981                super(MDSCHEMA_MEASURES, request, handler);
4982                catalogRT = getRestrictionTest(CatalogName);
4983                schemaNameRT = getRestrictionTest(SchemaName);
4984                cubeNameRT = getRestrictionTest(CubeName);
4985                measureNameRT = getRestrictionTest(MeasureName);
4986                measureUniqueNameRT = getRestrictionTest(MeasureUniqueName);
4987            }
4988    
4989            private static final Column CatalogName =
4990                new Column(
4991                    "CATALOG_NAME",
4992                    Type.String,
4993                    null,
4994                    Column.RESTRICTION,
4995                    Column.OPTIONAL,
4996                    "The name of the catalog to which this measure belongs. ");
4997            private static final Column SchemaName =
4998                new Column(
4999                    "SCHEMA_NAME",
5000                    Type.String,
5001                    null,
5002                    Column.RESTRICTION,
5003                    Column.OPTIONAL,
5004                    "The name of the schema to which this measure belongs.");
5005            private static final Column CubeName =
5006                new Column(
5007                    "CUBE_NAME",
5008                    Type.String,
5009                    null,
5010                    Column.RESTRICTION,
5011                    Column.REQUIRED,
5012                    "The name of the cube to which this measure belongs.");
5013            private static final Column MeasureName =
5014                new Column(
5015                    "MEASURE_NAME",
5016                    Type.String,
5017                    null,
5018                    Column.RESTRICTION,
5019                    Column.REQUIRED,
5020                    "The name of the measure.");
5021            private static final Column MeasureUniqueName =
5022                new Column(
5023                    "MEASURE_UNIQUE_NAME",
5024                    Type.String,
5025                    null,
5026                    Column.RESTRICTION,
5027                    Column.REQUIRED,
5028                    "The Unique name of the measure.");
5029            private static final Column MeasureCaption =
5030                new Column(
5031                    "MEASURE_CAPTION",
5032                    Type.String,
5033                    null,
5034                    Column.NOT_RESTRICTION,
5035                    Column.REQUIRED,
5036                    "A label or caption associated with the measure. ");
5037            private static final Column MeasureGuid =
5038                new Column(
5039                    "MEASURE_GUID",
5040                    Type.UUID,
5041                    null,
5042                    Column.NOT_RESTRICTION,
5043                    Column.OPTIONAL,
5044                    "Measure GUID.");
5045            private static final Column MeasureAggregator =
5046                new Column(
5047                    "MEASURE_AGGREGATOR",
5048                    Type.Integer,
5049                    null,
5050                    Column.NOT_RESTRICTION,
5051                    Column.REQUIRED,
5052                    "How a measure was derived. ");
5053            private static final Column DataType =
5054                new Column(
5055                    "DATA_TYPE",
5056                    Type.UnsignedShort,
5057                    null,
5058                    Column.NOT_RESTRICTION,
5059                    Column.REQUIRED,
5060                    "Data type of the measure.");
5061            private static final Column MeasureIsVisible =
5062                new Column(
5063                    "MEASURE_IS_VISIBLE",
5064                    Type.Boolean,
5065                    null,
5066                    Column.NOT_RESTRICTION,
5067                    Column.REQUIRED,
5068                    "A Boolean that always returns True. If the measure is not visible, it will not be included in the schema rowset.");
5069            private static final Column LevelsList =
5070                new Column(
5071                    "LEVELS_LIST",
5072                    Type.String,
5073                    null,
5074                    Column.NOT_RESTRICTION,
5075                    Column.OPTIONAL,
5076                    "A string that always returns NULL. EXCEPT that SQL Server returns non-null values!!!");
5077            private static final Column Description =
5078                new Column(
5079                    "DESCRIPTION",
5080                    Type.String,
5081                    null,
5082                    Column.NOT_RESTRICTION,
5083                    Column.OPTIONAL,
5084                    "A human-readable description of the measure. ");
5085    
5086            public void populate(
5087                XmlaResponse response,
5088                List<Row> rows)
5089                throws XmlaException
5090            {
5091                DataSourcesConfig.DataSource ds = handler.getDataSource(request);
5092                DataSourcesConfig.Catalog[] catalogs =
5093                    handler.getCatalogs(request, ds);
5094                String roleName = request.getRoleName();
5095                Role role = request.getRole();
5096    
5097                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
5098                    if (dsCatalog == null || dsCatalog.definition == null) {
5099                        continue;
5100                    }
5101                    Connection connection =
5102                        handler.getConnection(dsCatalog, role, roleName);
5103                    if (connection == null) {
5104                        continue;
5105                    }
5106    
5107                    String catalogName = dsCatalog.name;
5108                    if (catalogRT.passes(catalogName)) {
5109                        populateCatalog(connection, catalogName, rows);
5110                    }
5111                }
5112            }
5113    
5114            protected void populateCatalog(
5115                Connection connection,
5116                String catalogName,
5117                List<Row> rows)
5118                throws XmlaException
5119            {
5120                // SQL Server actually includes the LEVELS_LIST row
5121                StringBuilder buf = new StringBuilder(100);
5122    
5123                Schema schema = connection.getSchema();
5124                if (!schemaNameRT.passes(schema.getName())) {
5125                    return;
5126                }
5127                for (Cube cube : sortedCubes(schema)) {
5128                    if (cubeNameRT.passes(cube.getName())) {
5129                        SchemaReader schemaReader =
5130                            cube.getSchemaReader(
5131                                connection.getRole());
5132                        Dimension measuresDimension = cube.getDimensions()[0];
5133                        Hierarchy measuresHierarchy =
5134                            measuresDimension.getHierarchies()[0];
5135                        Level measuresLevel =
5136                            measuresHierarchy.getLevels()[0];
5137    
5138                        buf.setLength(0);
5139    
5140                        int j = 0;
5141                        for (Dimension dimension : cube.getDimensions()) {
5142                            if (dimension.isMeasures()) {
5143                                continue;
5144                            }
5145                            for (Hierarchy hierarchy : dimension.getHierarchies()) {
5146                                Level[] levels = hierarchy.getLevels();
5147                                Level lastLevel = levels[levels.length - 1];
5148                                if (j++ > 0) {
5149                                    buf.append(',');
5150                                }
5151                                buf.append(lastLevel.getUniqueName());
5152                            }
5153                        }
5154                        String levelListStr = buf.toString();
5155    
5156                        List<Member> storedMembers =
5157                            schemaReader.getLevelMembers(measuresLevel, false);
5158                        for (Member member : storedMembers) {
5159                            String name = member.getName();
5160                            String unique = member.getUniqueName();
5161                            if (measureNameRT.passes(name) &&
5162                                measureUniqueNameRT.passes(unique)) {
5163                                populateMember(schemaReader, catalogName,
5164                                    member, cube, levelListStr, rows);
5165                            }
5166                        }
5167    
5168                        for (Member member :
5169                            schemaReader.getCalculatedMembers(measuresHierarchy)) {
5170                            String name = member.getName();
5171                            String unique = member.getUniqueName();
5172                            if (measureNameRT.passes(name) &&
5173                                measureUniqueNameRT.passes(unique)) {
5174                                populateMember(schemaReader, catalogName,
5175                                    member, cube, null, rows);
5176                            }
5177                        }
5178                    }
5179                }
5180            }
5181    
5182            private void populateMember(
5183                SchemaReader schemaReader,
5184                String catalogName,
5185                Member member,
5186                Cube cube,
5187                String levelListStr,
5188                List<Row> rows)
5189            {
5190                // Access control
5191                if (!canAccess(schemaReader, member)) {
5192                    return;
5193                }
5194    
5195                Boolean visible =
5196                    (Boolean) member.getPropertyValue(Property.VISIBLE.name);
5197                if (visible == null) {
5198                    visible = true;
5199                }
5200                if (!EMIT_INVISIBLE_MEMBERS && !visible) {
5201                    return;
5202                }
5203    
5204                //TODO: currently this is always null
5205                String desc = member.getDescription();
5206                if (desc == null) {
5207                    desc = cube.getName() +
5208                        " Cube - " +
5209                        member.getName() +
5210                        " Member";
5211                }
5212    
5213                Row row = new Row();
5214                row.set(CatalogName.name, catalogName);
5215                row.set(SchemaName.name, cube.getSchema().getName());
5216                row.set(CubeName.name, cube.getName());
5217                row.set(MeasureName.name, member.getName());
5218                row.set(MeasureUniqueName.name, member.getUniqueName());
5219                row.set(MeasureCaption.name, member.getCaption());
5220                //row.set(MeasureGuid.name, "");
5221    
5222                Object aggProp =
5223                    member.getPropertyValue(Property.AGGREGATION_TYPE.getName());
5224                int aggNumber = MDMEASURE_AGGR_UNKNOWN;
5225                if (aggProp != null) {
5226                    RolapAggregator agg = (RolapAggregator) aggProp;
5227                    if (agg == RolapAggregator.Sum) {
5228                        aggNumber = MDMEASURE_AGGR_SUM;
5229                    } else if (agg == RolapAggregator.Count) {
5230                        aggNumber = MDMEASURE_AGGR_COUNT;
5231                    } else if (agg == RolapAggregator.Min) {
5232                        aggNumber = MDMEASURE_AGGR_MIN;
5233                    } else if (agg == RolapAggregator.Max) {
5234                        aggNumber = MDMEASURE_AGGR_MAX;
5235                    } else if (agg == RolapAggregator.Avg) {
5236                        aggNumber = MDMEASURE_AGGR_AVG;
5237                    }
5238                    //TODO: what are VAR and STD
5239                } else {
5240                    aggNumber = MDMEASURE_AGGR_CALCULATED;
5241                }
5242                row.set(MeasureAggregator.name, aggNumber);
5243    
5244                // DATA_TYPE DBType best guess is string
5245                int dbType = DBType.WSTR.userOrdinal;
5246                String datatype = (String)
5247                    member.getPropertyValue(Property.DATATYPE.getName());
5248                if (datatype != null) {
5249                    if (datatype.equals("Integer")) {
5250                        dbType = DBType.I4.userOrdinal;
5251                    } else if (datatype.equals("Numeric")) {
5252                        dbType = DBType.R8.userOrdinal;
5253                    } else {
5254                        dbType = DBType.WSTR.userOrdinal;
5255                    }
5256                }
5257                row.set(DataType.name, dbType);
5258                row.set(MeasureIsVisible.name, visible);
5259    
5260                if (levelListStr != null) {
5261                    row.set(LevelsList.name, levelListStr);
5262                }
5263    
5264                row.set(Description.name, desc);
5265                addRow(row, rows);
5266            }
5267    
5268            protected void setProperty(PropertyDefinition propertyDef, String value) {
5269                switch (propertyDef) {
5270                case Content:
5271                    break;
5272                default:
5273                    super.setProperty(propertyDef, value);
5274                }
5275            }
5276        }
5277    
5278        static class MdschemaMembersRowset extends Rowset {
5279            private final RestrictionTest catalogRT;
5280            private final RestrictionTest schemaNameRT;
5281            private final RestrictionTest cubeNameRT;
5282            private final RestrictionTest dimensionUniqueNameRT;
5283            private final RestrictionTest hierarchyUniqueNameRT;
5284            private final RestrictionTest memberNameRT;
5285            private final RestrictionTest memberUniqueNameRT;
5286            private final RestrictionTest memberTypeRT;
5287    
5288            MdschemaMembersRowset(XmlaRequest request, XmlaHandler handler) {
5289                super(MDSCHEMA_MEMBERS, request, handler);
5290                catalogRT = getRestrictionTest(CatalogName);
5291                schemaNameRT = getRestrictionTest(SchemaName);
5292                cubeNameRT = getRestrictionTest(CubeName);
5293                dimensionUniqueNameRT = getRestrictionTest(DimensionUniqueName);
5294                hierarchyUniqueNameRT = getRestrictionTest(HierarchyUniqueName);
5295                memberNameRT = getRestrictionTest(MemberName);
5296                memberUniqueNameRT = getRestrictionTest(MemberUniqueName);
5297                memberTypeRT = getRestrictionTest(MemberType);
5298            }
5299    
5300            private static final Column CatalogName =
5301                new Column(
5302                    "CATALOG_NAME",
5303                    Type.String,
5304                    null,
5305                    Column.RESTRICTION,
5306                    Column.OPTIONAL,
5307                    "The name of the catalog to which this member belongs. ");
5308            private static final Column SchemaName =
5309                new Column(
5310                    "SCHEMA_NAME",
5311                    Type.String,
5312                    null,
5313                    Column.RESTRICTION,
5314                    Column.OPTIONAL,
5315                    "The name of the schema to which this member belongs. ");
5316            private static final Column CubeName =
5317                new Column(
5318                    "CUBE_NAME",
5319                    Type.String,
5320                    null,
5321                    Column.RESTRICTION,
5322                    Column.REQUIRED,
5323                    "Name of the cube to which this member belongs.");
5324            private static final Column DimensionUniqueName =
5325                new Column(
5326                    "DIMENSION_UNIQUE_NAME",
5327                    Type.String,
5328                    null,
5329                    Column.RESTRICTION,
5330                    Column.REQUIRED,
5331                    "Unique name of the dimension to which this member belongs. ");
5332            private static final Column HierarchyUniqueName =
5333                new Column(
5334                    "HIERARCHY_UNIQUE_NAME",
5335                    Type.String,
5336                    null,
5337                    Column.RESTRICTION,
5338                    Column.REQUIRED,
5339                    "Unique name of the hierarchy. If the member belongs to more than one hierarchy, there is one row for each hierarchy to which it belongs.");
5340            private static final Column LevelUniqueName =
5341                new Column(
5342                    "LEVEL_UNIQUE_NAME",
5343                    Type.String,
5344                    null,
5345                    Column.RESTRICTION,
5346                    Column.REQUIRED,
5347                    " Unique name of the level to which the member belongs.");
5348            private static final Column LevelNumber =
5349                new Column(
5350                    "LEVEL_NUMBER",
5351                    Type.UnsignedInteger,
5352                    null,
5353                    Column.RESTRICTION,
5354                    Column.REQUIRED,
5355                    "The distance of the member from the root of the hierarchy.");
5356            private static final Column MemberOrdinal =
5357                new Column(
5358                    "MEMBER_ORDINAL",
5359                    Type.UnsignedInteger,
5360                    null,
5361                    Column.NOT_RESTRICTION,
5362                    Column.REQUIRED,
5363                    "Ordinal number of the member. Sort rank of the member when members of this dimension are sorted in their natural sort order. If providers do not have the concept of natural ordering, this should be the rank when sorted by MEMBER_NAME.");
5364            private static final Column MemberName =
5365                new Column(
5366                    "MEMBER_NAME",
5367                    Type.String,
5368                    null,
5369                    Column.RESTRICTION,
5370                    Column.REQUIRED,
5371                    "Name of the member.");
5372            private static final Column MemberUniqueName =
5373                new Column(
5374                    "MEMBER_UNIQUE_NAME",
5375                    Type.StringSometimesArray,
5376                    null,
5377                    Column.RESTRICTION,
5378                    Column.REQUIRED,
5379                    " Unique name of the member.");
5380            private static final Column MemberType =
5381                new Column(
5382                    "MEMBER_TYPE",
5383                    Type.Integer,
5384                    null,
5385                    Column.RESTRICTION,
5386                    Column.REQUIRED,
5387                    "Type of the member.");
5388            private static final Column MemberGuid =
5389                new Column(
5390                    "MEMBER_GUID",
5391                    Type.UUID,
5392                    null,
5393                    Column.NOT_RESTRICTION,
5394                    Column.OPTIONAL,
5395                    "Memeber GUID.");
5396            private static final Column MemberCaption =
5397                new Column(
5398                    "MEMBER_CAPTION",
5399                    Type.String,
5400                    null,
5401                    Column.RESTRICTION,
5402                    Column.REQUIRED,
5403                    "A label or caption associated with the member.");
5404            private static final Column ChildrenCardinality =
5405                new Column(
5406                    "CHILDREN_CARDINALITY",
5407                    Type.UnsignedInteger,
5408                    null,
5409                    Column.NOT_RESTRICTION,
5410                    Column.REQUIRED,
5411                    "Number of children that the member has.");
5412            private static final Column ParentLevel =
5413                new Column(
5414                    "PARENT_LEVEL",
5415                    Type.UnsignedInteger,
5416                    null,
5417                    Column.NOT_RESTRICTION,
5418                    Column.REQUIRED,
5419                    "The distance of the member's parent from the root level of the hierarchy. ");
5420            private static final Column ParentUniqueName =
5421                new Column(
5422                    "PARENT_UNIQUE_NAME",
5423                    Type.String,
5424                    null,
5425                    Column.NOT_RESTRICTION,
5426                    Column.OPTIONAL,
5427                    "Unique name of the member's parent.");
5428            private static final Column ParentCount =
5429                new Column(
5430                    "PARENT_COUNT",
5431                    Type.UnsignedInteger,
5432                    null,
5433                    Column.NOT_RESTRICTION,
5434                    Column.REQUIRED,
5435                    "Number of parents that this member has.");
5436            private static final Column TreeOp =
5437                new Column(
5438                    "TREE_OP",
5439                    Type.Enumeration,
5440                    Enumeration.TreeOp.enumeration,
5441                    Column.RESTRICTION,
5442                    Column.OPTIONAL,
5443                    "Tree Operation");
5444            /* Mondrian specified member properties. */
5445            private static final Column Depth =
5446                new Column(
5447                    "DEPTH",
5448                    Type.Integer,
5449                    null,
5450                    Column.NOT_RESTRICTION,
5451                    Column.OPTIONAL,
5452                    "depth");
5453    
5454            public void populate(
5455                XmlaResponse response,
5456                List<Row> rows)
5457                throws XmlaException
5458            {
5459                DataSourcesConfig.DataSource ds =
5460                    handler.getDataSource(request);
5461                DataSourcesConfig.Catalog[] catalogs =
5462                    handler.getCatalogs(request, ds);
5463                String roleName = request.getRoleName();
5464                Role role = request.getRole();
5465    
5466                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
5467                    if (dsCatalog == null || dsCatalog.definition == null) {
5468                        continue;
5469                    }
5470                    Connection connection =
5471                        handler.getConnection(dsCatalog, role, roleName);
5472                    if (connection == null) {
5473                        continue;
5474                    }
5475    
5476                    String catalogName = dsCatalog.name;
5477                    if (catalogRT.passes(catalogName)) {
5478                        populateCatalog(connection, catalogName, rows);
5479                    }
5480                }
5481            }
5482    
5483            protected void populateCatalog(
5484                Connection connection,
5485                String catalogName,
5486                List<Row> rows)
5487                throws XmlaException
5488            {
5489                Schema schema = connection.getSchema();
5490                if (!schemaNameRT.passes(schema.getName())) {
5491                    return;
5492                }
5493                for (Cube cube : sortedCubes(schema)) {
5494                    if (cubeNameRT.passes(cube.getName())) {
5495                        if (isRestricted(MemberUniqueName)) {
5496                            // NOTE: it is believed that if MEMBER_UNIQUE_NAME is
5497                            // a restriction, then none of the remaining possible
5498                            // restrictions other than TREE_OP are relevant
5499                            // (or allowed??).
5500                            SchemaReader schemaReader = cube.getSchemaReader(null);
5501                            outputUniqueMemberName(
5502                                schemaReader, catalogName, cube, rows);
5503                        } else {
5504                            SchemaReader schemaReader =
5505                                cube.getSchemaReader(
5506                                    connection.getRole());
5507                            populateCube(schemaReader, catalogName, cube, rows);
5508                        }
5509    
5510                    }
5511                }
5512            }
5513    
5514            protected void populateCube(
5515                SchemaReader schemaReader,
5516                String catalogName,
5517                Cube cube,
5518                List<Row> rows)
5519                throws XmlaException
5520            {
5521                if (isRestricted(LevelUniqueName)) {
5522                    // Note: If the LEVEL_UNIQUE_NAME has been specified, then
5523                    // the dimension and hierarchy are specified implicitly.
5524                    String levelUniqueName =
5525                        getRestrictionValueAsString(LevelUniqueName);
5526                    if (levelUniqueName == null) {
5527                        // The query specified two or more unique names
5528                        // which means that nothing will match.
5529                        return;
5530                    }
5531    
5532                    final List<Id.Segment> nameParts =
5533                        Util.parseIdentifier(levelUniqueName);
5534                    Hierarchy hier = cube.lookupHierarchy(nameParts.get(0), false);
5535                    if (hier == null) {
5536                        return;
5537                    }
5538                    Level[] levels = hier.getLevels();
5539                    for (Level level : levels) {
5540                        if (!level.getUniqueName().equals(levelUniqueName)) {
5541                            continue;
5542                        }
5543                        // Get members of this level, without access control, but
5544                        // including calculated members.
5545                        List<Member> members =
5546                            cube.getSchemaReader(null).getLevelMembers(level, true);
5547                        outputMembers(
5548                            schemaReader, members, catalogName, cube, rows);
5549                    }
5550                } else {
5551                    for (Dimension dimension : cube.getDimensions()) {
5552                        String uniqueName = dimension.getUniqueName();
5553                        if (dimensionUniqueNameRT.passes(uniqueName)) {
5554                            populateDimension(
5555                                schemaReader, catalogName,
5556                                cube, dimension, rows);
5557                        }
5558                    }
5559                }
5560            }
5561    
5562            protected void populateDimension(
5563                SchemaReader schemaReader,
5564                String catalogName,
5565                Cube cube,
5566                Dimension dimension,
5567                List<Row> rows)
5568                throws XmlaException
5569            {
5570                Hierarchy[] hierarchies = dimension.getHierarchies();
5571                for (Hierarchy hierarchy : hierarchies) {
5572                    String uniqueName = hierarchy.getUniqueName();
5573                    if (hierarchyUniqueNameRT.passes(uniqueName)) {
5574                        populateHierarchy(
5575                            schemaReader, catalogName,
5576                            cube, hierarchy, rows);
5577                    }
5578                }
5579            }
5580    
5581            protected void populateHierarchy(
5582                SchemaReader schemaReader,
5583                String catalogName,
5584                Cube cube,
5585                Hierarchy hierarchy,
5586                List<Row> rows)
5587                throws XmlaException
5588            {
5589                if (isRestricted(LevelNumber)) {
5590                    int levelNumber = getRestrictionValueAsInt(LevelNumber);
5591                    if (levelNumber == -1) {
5592                        LOGGER.warn("RowsetDefinition.populateHierarchy: " +
5593                            "LevelNumber invalid");
5594                        return;
5595                    }
5596                    Level[] levels = hierarchy.getLevels();
5597                    if (levelNumber >= levels.length) {
5598                        LOGGER.warn("RowsetDefinition.populateHierarchy: " +
5599                            "LevelNumber (" +
5600                            levelNumber +
5601                            ") is greater than number of levels (" +
5602                            levels.length +
5603                            ") for hierarchy \"" +
5604                            hierarchy.getUniqueName() +
5605                            "\"");
5606                        return;
5607                    }
5608    
5609                    Level level = levels[levelNumber];
5610                    List<Member> members =
5611                        schemaReader.getLevelMembers(level, false);
5612                    outputMembers(schemaReader, members, catalogName, cube, rows);
5613                } else {
5614                    // At this point we get ALL of the members associated with
5615                    // the Hierarchy (rather than getting them one at a time).
5616                    // The value returned is not used at this point but they are
5617                    // now cached in the SchemaReader.
5618                    List<List<Member>> membersArray =
5619                        RolapMember.getAllMembers(schemaReader, hierarchy);
5620                    for (List<Member> members : membersArray) {
5621                        outputMembers(
5622                            schemaReader, members,
5623                            catalogName, cube, rows);
5624                    }
5625                }
5626            }
5627    
5628            /**
5629             * Returns whether a value contains all of the bits in a mask.
5630             */
5631            private static boolean mask(int value, int mask) {
5632                return (value & mask) == mask;
5633            }
5634    
5635            /**
5636             * Adds a member to a result list and, depending upon the
5637             * <code>treeOp</code> parameter, other relatives of the member. This
5638             * method recursively invokes itself to walk up, down, or across the
5639             * hierarchy.
5640             */
5641            private void populateMember(
5642                final SchemaReader schemaReader,
5643                String catalogName,
5644                Cube cube,
5645                Member member,
5646                int treeOp,
5647                List<Row> rows)
5648            {
5649                // Visit node itself.
5650                if (mask(treeOp, Enumeration.TreeOp.Self.userOrdinal())) {
5651                    outputMember(schemaReader, member, catalogName, cube, rows);
5652                }
5653                // Visit node's siblings (not including itself).
5654                if (mask(treeOp, Enumeration.TreeOp.Siblings.userOrdinal())) {
5655                    final Member parent =
5656                        schemaReader.getMemberParent(member);
5657                    final List<Member> siblings = (parent == null)
5658                        ?  schemaReader.getHierarchyRootMembers(member.getHierarchy())
5659                        : schemaReader.getMemberChildren(parent);
5660    
5661                    for (Member sibling : siblings) {
5662                        if (sibling.equals(member)) {
5663                            continue;
5664                        }
5665                        populateMember(
5666                            schemaReader, catalogName,
5667                            cube, sibling,
5668                            Enumeration.TreeOp.Self.userOrdinal(), rows);
5669                    }
5670                }
5671                // Visit node's descendants or its immediate children, but not both.
5672                if (mask(treeOp, Enumeration.TreeOp.Descendants.userOrdinal())) {
5673                    final List<Member> children = schemaReader.getMemberChildren(member);
5674                    for (Member child : children) {
5675                        populateMember(
5676                            schemaReader, catalogName,
5677                            cube, child,
5678                            Enumeration.TreeOp.Self.userOrdinal() |
5679                                Enumeration.TreeOp.Descendants.userOrdinal(),
5680                            rows);
5681                    }
5682                } else if (mask(treeOp, Enumeration.TreeOp.Children.userOrdinal())) {
5683                    final List<Member> children =
5684                        schemaReader.getMemberChildren(member);
5685                    for (Member child : children) {
5686                        populateMember(
5687                            schemaReader, catalogName,
5688                            cube, child,
5689                            Enumeration.TreeOp.Self.userOrdinal(), rows);
5690                    }
5691                }
5692                // Visit node's ancestors or its immediate parent, but not both.
5693                if (mask(treeOp, Enumeration.TreeOp.Ancestors.userOrdinal())) {
5694                    final Member parent = schemaReader.getMemberParent(member);
5695                    if (parent != null) {
5696                        populateMember(
5697                            schemaReader, catalogName,
5698                            cube, parent,
5699                            Enumeration.TreeOp.Self.userOrdinal() |
5700                                Enumeration.TreeOp.Ancestors.userOrdinal(), rows);
5701                    }
5702                } else if (mask(treeOp, Enumeration.TreeOp.Parent.userOrdinal())) {
5703                    final Member parent = schemaReader.getMemberParent(member);
5704                    if (parent != null) {
5705                        populateMember(
5706                            schemaReader, catalogName,
5707                            cube, parent,
5708                            Enumeration.TreeOp.Self.userOrdinal(), rows);
5709                    }
5710                }
5711            }
5712    
5713            protected ArrayList<Column> pruneRestrictions(ArrayList<Column> list) {
5714                // If they've restricted TreeOp, we don't want to literally filter
5715                // the result on TreeOp (because it's not an output column) or
5716                // on MemberUniqueName (because TreeOp will have caused us to
5717                // generate other members than the one asked for).
5718                if (list.contains(TreeOp)) {
5719                    list.remove(TreeOp);
5720                    list.remove(MemberUniqueName);
5721                }
5722                return list;
5723            }
5724    
5725            private void outputMembers(
5726                final SchemaReader schemaReader,
5727                List<Member> members,
5728                final String catalogName,
5729                Cube cube, List<Row> rows) {
5730    
5731                for (Member member : members) {
5732                    outputMember(schemaReader, member, catalogName, cube, rows);
5733                }
5734            }
5735    
5736            private void outputUniqueMemberName(
5737                final SchemaReader schemaReader,
5738                final String catalogName,
5739                Cube cube, List<Row> rows)
5740            {
5741                final Object unameRestrictions =
5742                    restrictions.get(MemberUniqueName.name);
5743                List<String> list;
5744                if (unameRestrictions instanceof String) {
5745                    list = Collections.singletonList((String) unameRestrictions);
5746                } else {
5747                    list = (List<String>) unameRestrictions;
5748                }
5749                for (String memberUniqueName : list) {
5750                    final List<Id.Segment> nameParts =
5751                        Util.parseIdentifier(memberUniqueName);
5752    
5753                    Member member = schemaReader.getMemberByUniqueName(
5754                        nameParts, false);
5755    
5756                    if (member == null) {
5757                        return;
5758                    }
5759                    if (isRestricted(TreeOp)) {
5760                        int treeOp = getRestrictionValueAsInt(TreeOp);
5761                        if (treeOp == -1) {
5762                            return;
5763                        }
5764                        populateMember(
5765                            schemaReader, catalogName,
5766                            cube, member, treeOp, rows);
5767                    } else {
5768                        outputMember(
5769                            schemaReader, member, catalogName, cube, rows);
5770                    }
5771                }
5772            }
5773    
5774            private void outputMember(
5775                final SchemaReader schemaReader,
5776                Member member,
5777                final String catalogName,
5778                Cube cube,
5779                List<Row> rows)
5780            {
5781                // Access control
5782                if (!canAccess(schemaReader, member)) {
5783                    return;
5784                }
5785                if (! memberNameRT.passes(member.getName())) {
5786                    return;
5787                }
5788                if (! memberTypeRT.passes(member.getMemberType())) {
5789                    return;
5790                }
5791    
5792                if (member.getOrdinal() == -1) {
5793                    RolapMember.setOrdinals(schemaReader, member);
5794                }
5795    
5796                // Check whether the member is visible, otherwise do not dump.
5797                Boolean visible =
5798                    (Boolean) member.getPropertyValue(Property.VISIBLE.name);
5799                if (visible == null) {
5800                    visible = true;
5801                }
5802                if (!EMIT_INVISIBLE_MEMBERS && !visible) {
5803                    return;
5804                }
5805    
5806                final Level level = member.getLevel();
5807                final Hierarchy hierarchy = level.getHierarchy();
5808                final Dimension dimension = hierarchy.getDimension();
5809    
5810                int adjustedLevelDepth = level.getDepth();
5811                Role.HierarchyAccess hierarchyAccess =
5812                    schemaReader.getRole().getAccessDetails(hierarchy);
5813                if (hierarchyAccess != null) {
5814                    adjustedLevelDepth -= hierarchyAccess.getTopLevelDepth();
5815                }
5816    
5817                Row row = new Row();
5818                row.set(CatalogName.name, catalogName);
5819                row.set(SchemaName.name, cube.getSchema().getName());
5820                row.set(CubeName.name, cube.getName());
5821                row.set(DimensionUniqueName.name, dimension.getUniqueName());
5822                row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
5823                row.set(LevelUniqueName.name, level.getUniqueName());
5824                row.set(LevelNumber.name, adjustedLevelDepth);
5825                row.set(MemberOrdinal.name, false ? 0 : member.getOrdinal());
5826                row.set(MemberName.name, member.getName());
5827                row.set(MemberUniqueName.name, member.getUniqueName());
5828                row.set(MemberType.name, member.getMemberType().ordinal());
5829                //row.set(MemberGuid.name, "");
5830                row.set(MemberCaption.name, member.getCaption());
5831                row.set(ChildrenCardinality.name,
5832                    member.getPropertyValue(Property.CHILDREN_CARDINALITY.name));
5833                row.set(ChildrenCardinality.name, 100);
5834    
5835                if (adjustedLevelDepth == 0) {
5836                    row.set(ParentLevel.name, 0);
5837                } else {
5838                    row.set(ParentLevel.name, adjustedLevelDepth - 1);
5839                    String parentUniqueName = member.getParentUniqueName();
5840                    if (parentUniqueName != null) {
5841                        row.set(ParentUniqueName.name, parentUniqueName);
5842                    }
5843                }
5844    
5845                row.set(ParentCount.name, member.getParentMember() == null ? 0 : 1);
5846    
5847                row.set(Depth.name, member.getDepth());
5848                addRow(row, rows);
5849            }
5850    
5851            protected void setProperty(
5852                PropertyDefinition propertyDef,
5853                String value)
5854            {
5855                switch (propertyDef) {
5856                case Content:
5857                    break;
5858                default:
5859                    super.setProperty(propertyDef, value);
5860                }
5861            }
5862    
5863    /*
5864    RME: this code was used to test various algorithms to set member ordinals.
5865            void setOrdinals(SchemaReader schemaReader, Member startMember) {
5866                //int v = 0;
5867                //int v = 1;
5868                //int v = 2;
5869                //int v = 3;
5870                int v = 4;
5871    LOGGER.debug("RowsetDefinition.setOrdinals: v==" +v);
5872                if (v == 0) {
5873                    // foodmart time for Sales: time=63
5874                    // For very big data set, it takes time= 651865ms
5875                    RolapMember.setOrdinals(schemaReader, startMember);
5876                } else if (v == 1) {
5877                    // foodmart time for Sales: time=32
5878                    // result is same as v == 0
5879                    // For very big data set, it takes time= 73880ms
5880                    Hierarchy hierarchy = startMember.getHierarchy();
5881                    Member[][] membersArray =
5882                        RolapMember.getAllMembers(schemaReader, hierarchy);
5883                    RolapMember.setOrdinals(schemaReader, startMember);
5884                } else if (v == 2) {
5885                    // foodmart time for Sales: time=18
5886                    // result is NOT same as v == 0
5887                    int ordinal = 0;
5888                    Hierarchy hierarchy = startMember.getHierarchy();
5889                    Member[][] membersArray =
5890                        RolapMember.getAllMembers(schemaReader, hierarchy);
5891    
5892                    // RME: this does a breath first setting of ordinals
5893                    // for very big data set, it takes time= 4197ms
5894                    for (int i = 0; i < membersArray.length; i++) {
5895                        Member[] members = membersArray[i];
5896                        for (int j = 0; j < members.length; j++) {
5897                            Member member = members[j];
5898                             ((RolapMember) member).setOrdinal(ordinal++);
5899                        }
5900                    }
5901                } else if (v == 3) {
5902                    // foodmart time for Sales: time=19
5903                    // result is same as v == 0
5904                    int ordinal = 1;
5905                    int depth = 1;
5906                    Hierarchy hierarchy = startMember.getHierarchy();
5907                    Member[][] membersArray =
5908                        RolapMember.getAllMembers(schemaReader, hierarchy);
5909                    Member[] rootMembers = membersArray[0];
5910    LOGGER.debug("RowsetDefinition.setOrdinals: rootMembers.length=" +rootMembers.length);
5911                    // RME: this does a depth first setting of ordinals
5912                    // for very big data set, it takes time= 97310ms
5913                    for (int i = 0; i < rootMembers.length; i++) {
5914                        Member member = rootMembers[i];
5915                        if (member.getOrdinal() == -1) {
5916    LOGGER.debug("RowsetDefinition.setOrdinals: member=" +member.getName());
5917                            ((RolapMember) member).setOrdinal(ordinal++);
5918                        } else {
5919    LOGGER.debug("RowsetDefinition.setOrdinals: NO member=" +member.getName());
5920                        }
5921                        ordinal = setOrdinals(ordinal, member, membersArray, depth);
5922                    }
5923                } else {
5924                    // foodmart time for Sales: time=17
5925                    // result is same as v == 0
5926                    // bottom up depth first
5927                    // For very big data set, it takes time= 4241ms
5928                    int ordinal = 1;
5929                    Hierarchy hierarchy = startMember.getHierarchy();
5930                    Member[][] membersArray =
5931                        RolapMember.getAllMembers(schemaReader, hierarchy);
5932                    Member[] leafMembers = membersArray[membersArray.length-1];
5933    
5934                    for (int i = 0; i < leafMembers.length; i++) {
5935                        Member child = leafMembers[i];
5936                        ordinal = bottomUpSetOrdinals(ordinal, child);
5937                        ((RolapMember) child).setOrdinal(ordinal++);
5938                    }
5939                    boolean needsFullTopDown = false;
5940                    for (int i = 0; i < membersArray.length-1; i++) {
5941                        Member[] members = membersArray[i];
5942                        for (int j = 0; j < members.length; j++) {
5943                            Member member = members[j];
5944                            if (member.getOrdinal() == -1) {
5945                                needsFullTopDown = true;
5946                                break;
5947                            }
5948                        }
5949                    }
5950    LOGGER.debug("RowsetDefinition.setOrdinals: needsFullTopDown=" +needsFullTopDown);
5951                }
5952            }
5953            int bottomUpSetOrdinals(int ordinal, Member child) {
5954                Member parent = child.getParentMember();
5955                if (parent.getOrdinal() == -1) {
5956                    ordinal = bottomUpSetOrdinals(ordinal, parent);
5957                    ((RolapMember) parent).setOrdinal(ordinal++);
5958                }
5959                return ordinal;
5960            }
5961    
5962    
5963            // RME: this is used as part of the depth first setting of ordinals
5964            int setOrdinals(int ordinal, Member parent, Member[][] membersArray, int depth) {
5965                boolean nextLevelExists = (depth + 1 < membersArray.length);
5966                Member[] members = membersArray[depth];
5967                for (int i = 0; i < members.length; i++) {
5968                    Member member = members[i];
5969                    if (member.getParentMember() == parent) {
5970                        ((RolapMember) member).setOrdinal(ordinal++);
5971                        if (nextLevelExists) {
5972                            ordinal =
5973                                setOrdinals(ordinal, member, membersArray, depth + 1);
5974                        }
5975                    }
5976                }
5977                return ordinal;
5978            }
5979    */
5980    
5981        }
5982    
5983        static class MdschemaSetsRowset extends Rowset {
5984            private final RestrictionTest catalogRT;
5985            private final RestrictionTest schemaNameRT;
5986            private final RestrictionTest cubeNameRT;
5987            private final RestrictionTest setNameRT;
5988            private static final String GLOBAL_SCOPE = "1";
5989    
5990            MdschemaSetsRowset(XmlaRequest request, XmlaHandler handler) {
5991                super(MDSCHEMA_SETS, request, handler);
5992                catalogRT = getRestrictionTest(CatalogName);
5993                schemaNameRT = getRestrictionTest(SchemaName);
5994                cubeNameRT = getRestrictionTest(CubeName);
5995                setNameRT = getRestrictionTest(SetName);
5996            }
5997    
5998            private static final Column CatalogName =
5999                new Column(
6000                    "CATALOG_NAME",
6001                    Type.String,
6002                    null,
6003                    true,
6004                    true,
6005                    null);
6006            private static final Column SchemaName =
6007                new Column(
6008                    "SCHEMA_NAME",
6009                    Type.String,
6010                    null,
6011                    true,
6012                    true,
6013                    null);
6014            private static final Column CubeName =
6015                new Column(
6016                    "CUBE_NAME",
6017                    Type.String,
6018                    null,
6019                    true,
6020                    false,
6021                    null);
6022            private static final Column SetName =
6023                new Column(
6024                    "SET_NAME",
6025                    Type.String,
6026                    null,
6027                    true,
6028                    false,
6029                    null);
6030            private static final Column SetCaption =
6031                new Column(
6032                    "SET_CAPTION",
6033                    Type.String,
6034                    null,
6035                    true,
6036                    true,
6037                    null);
6038            private static final Column Scope =
6039                new Column(
6040                    "SCOPE",
6041                    Type.Integer,
6042                    null,
6043                    true,
6044                    false,
6045                    null);
6046            private static final Column Description =
6047                new Column("DESCRIPTION",
6048                    Type.String,
6049                    null,
6050                    false,
6051                    true,
6052                    "A human-readable description of the measure.");
6053    
6054            public void populate(
6055                XmlaResponse response,
6056                List<Row> rows)
6057                throws XmlaException
6058            {
6059                DataSourcesConfig.DataSource ds =
6060                    handler.getDataSource(request);
6061                DataSourcesConfig.Catalog[] catalogs =
6062                    handler.getCatalogs(request, ds);
6063                String roleName = request.getRoleName();
6064                Role role = request.getRole();
6065    
6066                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
6067                    if (dsCatalog == null || dsCatalog.definition == null) {
6068                        continue;
6069                    }
6070                    Connection connection =
6071                        handler.getConnection(dsCatalog, role, roleName);
6072                    if (connection == null) {
6073                        continue;
6074                    }
6075    
6076                    String catalogName = dsCatalog.name;
6077                    if (!catalogRT.passes(catalogName)) {
6078                        continue;
6079                    }
6080                    processCatalog(connection, catalogName, rows);
6081                }
6082            }
6083    
6084            private void processCatalog(
6085                Connection connection,
6086                String catalogName,
6087                List<Row> rows)
6088            {
6089                Schema schema = connection.getSchema();
6090                if (!schemaNameRT.passes(schema.getName())) {
6091                    return;
6092                }
6093                Cube[] cubes = connection.getSchemaReader().getCubes();
6094                for (Cube cube : cubes) {
6095                    if (!cubeNameRT.passes(cube.getName())) {
6096                         continue;
6097                    }
6098                    populateNamedSets(cube, catalogName, rows);
6099                }
6100            }
6101    
6102            private void populateNamedSets(
6103                Cube cube,
6104                String catalogName,
6105                List<Row> rows)
6106            {
6107                for (NamedSet namedSet : cube.getNamedSets()) {
6108                    if (!setNameRT.passes(namedSet.getUniqueName())) {
6109                        continue;
6110                    }
6111                    Row row = new Row();
6112                    row.set(CatalogName.name, catalogName);
6113                    row.set(SchemaName.name, cube.getSchema().getName());
6114                    row.set(CubeName.name, cube.getName());
6115                    row.set(SetName.name, namedSet.getUniqueName());
6116                    row.set(Scope.name, GLOBAL_SCOPE);
6117                    row.set(Description.name, namedSet.getDescription());
6118                    addRow(row, rows);
6119                }
6120            }
6121        }
6122    
6123        static class MdschemaPropertiesRowset extends Rowset {
6124            private final RestrictionTest catalogRT;
6125            private final RestrictionTest schemaNameRT;
6126            private final RestrictionTest cubeNameRT;
6127            private final RestrictionTest dimensionUniqueNameRT;
6128            private final RestrictionTest hierarchyUniqueNameRT;
6129            private final RestrictionTest propertyNameRT;
6130    
6131            MdschemaPropertiesRowset(XmlaRequest request, XmlaHandler handler) {
6132                super(MDSCHEMA_PROPERTIES, request, handler);
6133                catalogRT = getRestrictionTest(CatalogName);
6134                schemaNameRT = getRestrictionTest(SchemaName);
6135                cubeNameRT = getRestrictionTest(CubeName);
6136                dimensionUniqueNameRT = getRestrictionTest(DimensionUniqueName);
6137                hierarchyUniqueNameRT = getRestrictionTest(HierarchyUniqueName);
6138                propertyNameRT = getRestrictionTest(PropertyName);
6139            }
6140    
6141            private static final int MDPROP_MEMBER = 0x01;
6142            private static final int MDPROP_CELL = 0x02;
6143            private static final int MDPROP_SYSTEM = 0x04;
6144            private static final int MDPROP_BLOB = 0x08;
6145    
6146            private static final int MD_PROPTYPE_REGULAR = 0x00;
6147    
6148            private static final Column CatalogName =
6149                new Column(
6150                    "CATALOG_NAME",
6151                    Type.String,
6152                    null,
6153                    Column.RESTRICTION,
6154                    Column.OPTIONAL,
6155                    "The name of the database.");
6156            private static final Column SchemaName =
6157                new Column(
6158                    "SCHEMA_NAME",
6159                    Type.String,
6160                    null,
6161                    Column.RESTRICTION,
6162                    Column.OPTIONAL,
6163                    "The name of the schema to which this property belongs.");
6164            private static final Column CubeName =
6165                new Column(
6166                    "CUBE_NAME",
6167                    Type.String,
6168                    null,
6169                    Column.RESTRICTION,
6170                    Column.REQUIRED,
6171                    "The name of the cube.");
6172            private static final Column DimensionUniqueName =
6173                new Column(
6174                    "DIMENSION_UNIQUE_NAME",
6175                    Type.String,
6176                    null,
6177                    Column.RESTRICTION,
6178                    Column.REQUIRED,
6179                    "The unique name of the dimension.");
6180            private static final Column HierarchyUniqueName =
6181                new Column(
6182                    "HIERARCHY_UNIQUE_NAME",
6183                    Type.String,
6184                    null,
6185                    Column.RESTRICTION,
6186                    Column.REQUIRED,
6187                    "The unique name of the hierarchy.");
6188            private static final Column LevelUniqueName =
6189                new Column(
6190                    "LEVEL_UNIQUE_NAME",
6191                    Type.String,
6192                    null,
6193                    Column.RESTRICTION,
6194                    Column.REQUIRED,
6195                    "The unique name of the level to which this property belongs.");
6196            // According to MS this should not be nullable
6197            private static final Column MemberUniqueName =
6198                new Column(
6199                    "MEMBER_UNIQUE_NAME",
6200                    Type.String,
6201                    null,
6202                    Column.RESTRICTION,
6203                    Column.OPTIONAL,
6204                    "The unique name of the member to which the property belongs.");
6205            private static final Column PropertyName =
6206                new Column(
6207                    "PROPERTY_NAME",
6208                    Type.String,
6209                    null,
6210                    Column.RESTRICTION,
6211                    Column.REQUIRED,
6212                    "Name of the property.");
6213            private static final Column PropertyType =
6214                new Column(
6215                    "PROPERTY_TYPE",
6216                    Type.Short,
6217                    null,
6218                    Column.RESTRICTION,
6219                    Column.REQUIRED,
6220                    "A bitmap that specifies the type of the property");
6221            private static final Column PropertyCaption =
6222                new Column(
6223                    "PROPERTY_CAPTION",
6224                    Type.String,
6225                    null,
6226                    Column.NOT_RESTRICTION,
6227                    Column.REQUIRED,
6228                    "A label or caption associated with the property, used primarily for display purposes.");
6229            private static final Column DataType =
6230                new Column(
6231                    "DATA_TYPE",
6232                    Type.UnsignedShort,
6233                    null,
6234                    Column.NOT_RESTRICTION,
6235                    Column.REQUIRED,
6236                    "Data type of the property.");
6237            private static final Column PropertyContentType =
6238                new Column(
6239                    "PROPERTY_CONTENT_TYPE",
6240                    Type.Short,
6241                    null,
6242                    Column.RESTRICTION,
6243                    Column.OPTIONAL,
6244                    "The type of the property. ");
6245            private static final Column Description =
6246                new Column(
6247                    "DESCRIPTION",
6248                    Type.String,
6249                    null,
6250                    Column.NOT_RESTRICTION,
6251                    Column.OPTIONAL,
6252                    "A human-readable description of the measure. ");
6253    
6254            public void populate(
6255                XmlaResponse response,
6256                List<Row> rows)
6257                throws XmlaException
6258            {
6259                DataSourcesConfig.DataSource ds =
6260                    handler.getDataSource(request);
6261                DataSourcesConfig.Catalog[] catalogs =
6262                    handler.getCatalogs(request, ds);
6263                String roleName = request.getRoleName();
6264                Role role = request.getRole();
6265    
6266                for (DataSourcesConfig.Catalog dsCatalog : catalogs) {
6267                    if (dsCatalog == null || dsCatalog.definition == null) {
6268                        continue;
6269                    }
6270                    Connection connection =
6271                        handler.getConnection(dsCatalog, role, roleName);
6272                    if (connection == null) {
6273                        continue;
6274                    }
6275    
6276                    String catalogName = dsCatalog.name;
6277                    if (catalogRT.passes(catalogName)) {
6278                        populateCatalog(connection, catalogName, rows);
6279                    }
6280                }
6281            }
6282    
6283            protected void populateCatalog(
6284                Connection connection,
6285                String catalogName,
6286                List<Row> rows)
6287                throws XmlaException
6288            {
6289                Schema schema = connection.getSchema();
6290                if (!schemaNameRT.passes(schema.getName())) {
6291                    return;
6292                }
6293                for (Cube cube : sortedCubes(schema)) {
6294                    if (cubeNameRT.passes(cube.getName())) {
6295                        SchemaReader schemaReader =
6296                            cube.getSchemaReader(
6297                                connection.getRole());
6298                        populateCube(schemaReader, catalogName, cube, rows);
6299                    }
6300                }
6301            }
6302    
6303            protected void populateCube(
6304                SchemaReader schemaReader,
6305                String catalogName,
6306                Cube cube,
6307                List<Row> rows)
6308                throws XmlaException
6309            {
6310                if (isRestricted(LevelUniqueName)) {
6311                    // Note: If the LEVEL_UNIQUE_NAME has been specified, then
6312                    // the dimension and hierarchy are specified implicitly.
6313                    String levelUniqueName =
6314                        getRestrictionValueAsString(LevelUniqueName);
6315                    if (levelUniqueName == null) {
6316                        // The query specified two or more unique names
6317                        // which means that nothing will match.
6318                        return;
6319                    }
6320                    final List<Id.Segment> nameParts =
6321                        Util.parseIdentifier(levelUniqueName);
6322                    Hierarchy hier = cube.lookupHierarchy(nameParts.get(0), false);
6323                    if (hier == null) {
6324                        return;
6325                    }
6326                    for (Level level : schemaReader.getHierarchyLevels(hier)) {
6327                        if (level.getUniqueName().equals(levelUniqueName)) {
6328                            populateLevel(schemaReader, catalogName,
6329                                cube, level, rows);
6330                            break;
6331                        }
6332                    }
6333    
6334                } else {
6335                    for (Dimension dimension : cube.getDimensions()) {
6336                        String uniqueName = dimension.getUniqueName();
6337                        if (dimensionUniqueNameRT.passes(uniqueName)) {
6338                            populateDimension(schemaReader, catalogName,
6339                                cube, dimension, rows);
6340                        }
6341                    }
6342                }
6343            }
6344    
6345            private void populateDimension(
6346                final SchemaReader schemaReader,
6347                final String catalogName,
6348                Cube cube,
6349                Dimension dimension,
6350                List<Row> rows)
6351            {
6352                Hierarchy[] hierarchies = dimension.getHierarchies();
6353                for (Hierarchy hierarchy : hierarchies) {
6354                    String unique = hierarchy.getUniqueName();
6355                    if (hierarchyUniqueNameRT.passes(unique)) {
6356                        populateHierarchy(schemaReader, catalogName,
6357                            cube, hierarchy, rows);
6358                    }
6359                }
6360            }
6361    
6362            private void populateHierarchy(
6363                final SchemaReader schemaReader,
6364                final String catalogName,
6365                Cube cube,
6366                Hierarchy hierarchy,
6367                List<Row> rows)
6368            {
6369                for (Level level : schemaReader.getHierarchyLevels(hierarchy)) {
6370                    populateLevel(schemaReader, catalogName,
6371                        cube, level, rows);
6372                }
6373            }
6374    
6375            private void populateLevel(
6376                final SchemaReader schemaReader,
6377                final String catalogName,
6378                Cube cube,
6379                Level level,
6380                List<Row> rows)
6381            {
6382                Property[] properties = level.getProperties();
6383                for (Property property : properties) {
6384                    if (propertyNameRT.passes(property.getName())) {
6385                        outputProperty(schemaReader, property,
6386                            catalogName, cube, level, rows);
6387                    }
6388                }
6389            }
6390    
6391            private void outputProperty(
6392                final SchemaReader schemaReader,
6393                Property property,
6394                final String catalogName,
6395                Cube cube,
6396                Level level,
6397                List<Row> rows)
6398            {
6399                Hierarchy hierarchy = level.getHierarchy();
6400                Dimension dimension = hierarchy.getDimension();
6401    
6402                String propertyName = property.getName();
6403    
6404                Row row = new Row();
6405                row.set(CatalogName.name, catalogName);
6406                row.set(SchemaName.name, cube.getSchema().getName());
6407                row.set(CubeName.name, cube.getName());
6408                row.set(DimensionUniqueName.name, dimension.getUniqueName());
6409                row.set(HierarchyUniqueName.name, hierarchy.getUniqueName());
6410                row.set(LevelUniqueName.name, level.getUniqueName());
6411                //TODO: what is the correct value here
6412                //row.set(MemberUniqueName.name, "");
6413    
6414                row.set(PropertyName.name, propertyName);
6415                // Only member properties now
6416                row.set(PropertyType.name, MDPROP_MEMBER);
6417                row.set(PropertyContentType.name, MD_PROPTYPE_REGULAR);
6418                row.set(PropertyCaption.name, property.getCaption());
6419                DBType dbType = getDBTypeFromProperty(property);
6420                row.set(DataType.name, dbType.userOrdinal);
6421    
6422                String desc = cube.getName() +
6423                    " Cube - " +
6424                    hierarchy.getName() +
6425                    " Hierarchy - " +
6426                    level.getName() +
6427                    " Level - " +
6428                    property.getName() +
6429                    " Property";
6430                row.set(Description.name, desc);
6431    
6432                addRow(row, rows);
6433            }
6434    
6435            protected void setProperty(
6436                PropertyDefinition propertyDef,
6437                String value)
6438            {
6439                switch (propertyDef) {
6440                case Content:
6441                    break;
6442                default:
6443                    super.setProperty(propertyDef, value);
6444                }
6445            }
6446        }
6447    
6448        private static boolean canAccess(
6449            SchemaReader schemaReader,
6450            OlapElement elem)
6451        {
6452            Role role = schemaReader.getRole();
6453            return role.canAccess(elem);
6454        }
6455    
6456        private static <T extends Comparable> List<T> sort(
6457            Collection<T> collection)
6458        {
6459            Object[] a = collection.toArray(new Object[collection.size()]);
6460            Arrays.sort(a);
6461            return Util.cast(Arrays.asList(a));
6462        }
6463    
6464        private static <T> List<T> sortArray(
6465            T[] a,
6466            Comparator<T> comparator)
6467        {
6468            T[] a2 = a.clone();
6469            Arrays.sort(a2, comparator);
6470            return Arrays.asList(a2);
6471        }
6472    
6473        static void serialize(StringBuilder buf, Collection<String> strings) {
6474            int k = 0;
6475            for (String name : sort(strings)) {
6476                if (k++ > 0) {
6477                    buf.append(',');
6478                }
6479                buf.append(name);
6480            }
6481        }
6482    
6483        static List<Cube> sortedCubes(Schema schema) {
6484            final Cube[] cubes = schema.getCubes();
6485            return sortArray(
6486                cubes,
6487                new Comparator<Cube>() {
6488                    public int compare(Cube o1, Cube o2) {
6489                        return o1.getName().compareTo(o2.getName());
6490                    }
6491                }
6492            );
6493        }
6494    }
6495    
6496    // End RowsetDefinition.java