001/**
002 *
003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
004 *
005 * This library is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU Lesser General Public
007 * License as published by the Free Software Foundation; either 
008 * version 2.1 of the License, or (at your option) any later version.
009 * 
010 * This library is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013 * Lesser General Public License for more details.
014 * 
015 * You should have received a copy of the GNU Lesser General Public 
016 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
017 * 
018 **/
019package lucee.runtime.tag;
020
021import java.sql.DatabaseMetaData;
022import java.sql.ResultSet;
023import java.sql.SQLException;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.Map;
027import java.util.Set;
028import java.util.regex.Pattern;
029
030import lucee.commons.io.IOUtil;
031import lucee.commons.lang.StringUtil;
032import lucee.commons.sql.SQLUtil;
033import lucee.runtime.PageContext;
034import lucee.runtime.config.Constants;
035import lucee.runtime.db.DataSource;
036import lucee.runtime.db.DataSourceManager;
037import lucee.runtime.db.DatasourceConnection;
038import lucee.runtime.exp.ApplicationException;
039import lucee.runtime.exp.DatabaseException;
040import lucee.runtime.exp.PageException;
041import lucee.runtime.ext.tag.TagImpl;
042import lucee.runtime.listener.ApplicationContextPro;
043import lucee.runtime.op.Caster;
044import lucee.runtime.timer.Stopwatch;
045import lucee.runtime.type.Array;
046import lucee.runtime.type.ArrayImpl;
047import lucee.runtime.type.Collection;
048import lucee.runtime.type.Collection.Key;
049import lucee.runtime.type.KeyImpl;
050import lucee.runtime.type.Query;
051import lucee.runtime.type.QueryColumn;
052import lucee.runtime.type.QueryImpl;
053import lucee.runtime.type.SVArray;
054import lucee.runtime.type.Struct;
055import lucee.runtime.type.StructImpl;
056import lucee.runtime.type.util.KeyConstants;
057
058/**
059* Handles all interactions with files. The attributes you use with cffile depend on the value of the action attribute. 
060*  For example, if the action = "write", use the attributes associated with writing a text file.
061*
062*
063*
064**/
065public final class DBInfo extends TagImpl {
066
067        private static final Key TABLE_NAME = KeyImpl.intern("TABLE_NAME");
068        private static final Key COLUMN_NAME = KeyImpl.intern("COLUMN_NAME");
069        private static final Key IS_PRIMARYKEY = KeyImpl.intern("IS_PRIMARYKEY");
070        private static final Key IS_FOREIGNKEY = KeyImpl.intern("IS_FOREIGNKEY");
071        private static final Key COLUMN_DEF = KeyImpl.intern("COLUMN_DEF");
072        private static final Key COLUMN_DEFAULT_VALUE = KeyImpl.intern("COLUMN_DEFAULT_VALUE");
073        private static final Key COLUMN_DEFAULT = KeyImpl.intern("COLUMN_DEFAULT");
074        private static final Key REFERENCED_PRIMARYKEY = KeyImpl.intern("REFERENCED_PRIMARYKEY");
075        private static final Key REFERENCED_PRIMARYKEY_TABLE = KeyImpl.intern("REFERENCED_PRIMARYKEY_TABLE");
076        private static final Key USER = KeyImpl.intern("USER");
077        private static final Key TABLE_SCHEM = KeyImpl.intern("TABLE_SCHEM");
078        private static final Key DECIMAL_DIGITS = KeyImpl.intern("DECIMAL_DIGITS");
079
080        private static final Key DATABASE_NAME = KeyImpl.intern("database_name");
081        private static final Key TABLE_CAT = KeyImpl.intern("TABLE_CAT");
082        private static final Key PROCEDURE = KeyImpl.intern("procedure");
083        private static final Key CATALOG = KeyImpl.intern("catalog");
084        private static final Key SCHEMA = KeyImpl.intern("schema");
085        private static final Key DATABASE_PRODUCTNAME = KeyImpl.intern("DATABASE_PRODUCTNAME");
086        private static final Key DATABASE_VERSION = KeyImpl.intern("DATABASE_VERSION");
087        private static final Key DRIVER_NAME = KeyImpl.intern("DRIVER_NAME");
088        private static final Key DRIVER_VERSION = KeyImpl.intern("DRIVER_VERSION");
089        private static final Key JDBC_MAJOR_VERSION = KeyImpl.intern("JDBC_MAJOR_VERSION");
090        private static final Key JDBC_MINOR_VERSION = KeyImpl.intern("JDBC_MINOR_VERSION");     
091        
092        private static final int TYPE_NONE=0;
093        private static final int TYPE_DBNAMES=1;
094        private static final int TYPE_TABLES=2;
095        private static final int TYPE_TABLE_COLUMNS = 3;
096        private static final int TYPE_VERSION = 4;
097        private static final int TYPE_PROCEDURES = 5;
098        private static final int TYPE_PROCEDURE_COLUMNS = 6;
099        private static final int TYPE_FOREIGNKEYS = 7;
100        private static final int TYPE_INDEX = 8;
101        private static final int TYPE_USERS = 9;
102        private static final int TYPE_TERMS = 10;
103        private static final Collection.Key CARDINALITY = KeyImpl.init("CARDINALITY");
104
105        
106        //private static final String[] ALL_TABLE_TYPES = {"TABLE", "VIEW", "SYSTEM TABLE", "SYNONYM"};
107        
108        private String datasource;
109        private String name;
110        private int type;
111        
112        
113        private String dbname;
114        private String password;
115        private String pattern;
116        private String table;
117        private String procedure;
118        private String username;
119        private String strType;
120        
121        
122        @Override
123        public void release()   {
124                super.release();
125                datasource=null;
126                name=null;
127                type=TYPE_NONE;
128                dbname=null;
129                password=null;
130                pattern=null;
131                table=null;
132                procedure=null;
133                username=null;
134                
135                
136                
137        }
138
139        /**
140         * @param procedure the procedure to set
141         */
142        public void setProcedure(String procedure) {
143                this.procedure = procedure;
144        }
145
146        /**
147         * @param datasource the datasource to set
148         */
149        public void setDatasource(String datasource) {
150                this.datasource = datasource;
151        }
152
153        /**
154         * @param name the name to set
155         */
156        public void setName(String name) {
157                this.name = name;
158        }
159
160        /**
161         * @param type the type to set
162         * @throws ApplicationException 
163         */
164        public void setType(String strType) throws ApplicationException {
165                this.strType=strType;
166                strType=strType.toLowerCase().trim();
167                
168                if("dbnames".equals(strType))                   this.type=TYPE_DBNAMES;
169                else if("dbname".equals(strType))               this.type=TYPE_DBNAMES;
170                else if("tables".equals(strType))               this.type=TYPE_TABLES;
171                else if("table".equals(strType))                this.type=TYPE_TABLES;
172                else if("columns".equals(strType))              this.type=TYPE_TABLE_COLUMNS;
173                else if("column".equals(strType))               this.type=TYPE_TABLE_COLUMNS;
174                else if("version".equals(strType))              this.type=TYPE_VERSION;
175                else if("procedures".equals(strType))   this.type=TYPE_PROCEDURES;
176                else if("procedure".equals(strType))    this.type=TYPE_PROCEDURES;
177                
178
179                else if("table_columns".equals(strType))        this.type=TYPE_TABLE_COLUMNS;
180                else if("table_column".equals(strType))         this.type=TYPE_TABLE_COLUMNS;
181                else if("column_table".equals(strType))         this.type=TYPE_TABLE_COLUMNS;
182                else if("column_tables".equals(strType))        this.type=TYPE_TABLE_COLUMNS;
183                
184                else if("tablecolumns".equals(strType))         this.type=TYPE_TABLE_COLUMNS;
185                else if("tablecolumn".equals(strType))          this.type=TYPE_TABLE_COLUMNS;
186                else if("columntable".equals(strType))          this.type=TYPE_TABLE_COLUMNS;
187                else if("columntables".equals(strType))         this.type=TYPE_TABLE_COLUMNS;
188                
189                else if("procedure_columns".equals(strType))    this.type=TYPE_PROCEDURE_COLUMNS;
190                else if("procedure_column".equals(strType))             this.type=TYPE_PROCEDURE_COLUMNS;
191                else if("column_procedure".equals(strType))             this.type=TYPE_PROCEDURE_COLUMNS;
192                else if("column_procedures".equals(strType))    this.type=TYPE_PROCEDURE_COLUMNS;
193                
194                else if("procedurecolumns".equals(strType))     this.type=TYPE_PROCEDURE_COLUMNS;
195                else if("procedurecolumn".equals(strType))              this.type=TYPE_PROCEDURE_COLUMNS;
196                else if("columnprocedure".equals(strType))              this.type=TYPE_PROCEDURE_COLUMNS;
197                else if("columnprocedures".equals(strType))     this.type=TYPE_PROCEDURE_COLUMNS;
198                
199                else if("foreignkeys".equals(strType))  this.type=TYPE_FOREIGNKEYS;
200                else if("foreignkey".equals(strType))   this.type=TYPE_FOREIGNKEYS;
201                else if("index".equals(strType))                this.type=TYPE_INDEX;
202                else if("users".equals(strType))                this.type=TYPE_USERS;
203                else if("user".equals(strType))                 this.type=TYPE_USERS;
204                
205                else if("term".equals(strType))         this.type=TYPE_TERMS;
206                else if("terms".equals(strType))        this.type=TYPE_TERMS;
207                
208                else throw new ApplicationException("invalid value for attribute type ["+strType+"]",
209                                "valid values are [dbname,tables,columns,version,procedures,foreignkeys,index,users]");
210                
211        }
212
213        /**
214         * @param dbname the dbname to set
215         */
216        public void setDbname(String dbname) {
217                this.dbname = dbname;
218        }
219        public void setDbnames(String dbname) {
220                this.dbname = dbname;
221        }
222
223        /**
224         * @param password the password to set
225         */
226        public void setPassword(String password) {
227                this.password = password;
228        }
229
230        /**
231         * @param pattern the pattern to set
232         */
233        public void setPattern(String pattern) {
234                this.pattern = pattern;
235        }
236
237        /**
238         * @param table the table to set
239         */
240        public void setTable(String table) {
241                this.table = table;
242        }
243
244        /**
245         * @param username the username to set
246         */
247        public void setUsername(String username) {
248                this.username = username;
249        }
250
251
252        @Override
253        public int doStartTag() throws PageException    {
254                Object ds=getDatasource(pageContext, datasource);
255                DataSourceManager manager = pageContext.getDataSourceManager();
256                DatasourceConnection dc=ds instanceof DataSource?
257                        manager.getConnection(pageContext,(DataSource)ds,username,password):
258                        manager.getConnection(pageContext,Caster.toString(ds),username,password);
259                try {
260                        
261                        if(type==TYPE_TABLE_COLUMNS)    typeColumns(dc.getConnection().getMetaData());
262                        else if(type==TYPE_DBNAMES)             typeDBNames(dc.getConnection().getMetaData());
263                        else if(type==TYPE_FOREIGNKEYS) typeForeignKeys(dc.getConnection().getMetaData());
264                        else if(type==TYPE_INDEX)               typeIndex(dc.getConnection().getMetaData());
265                        else if(type==TYPE_PROCEDURES)  typeProcedures(dc.getConnection().getMetaData());
266                        else if(type==TYPE_PROCEDURE_COLUMNS)typeProcedureColumns(dc.getConnection().getMetaData());
267                        else if(type==TYPE_TERMS)               typeTerms(dc.getConnection().getMetaData());
268                        else if(type==TYPE_TABLES)              typeTables(dc.getConnection().getMetaData());
269                        else if(type==TYPE_VERSION)             typeVersion(dc.getConnection().getMetaData());
270                        else if(type==TYPE_USERS)               typeUsers(dc.getConnection().getMetaData());
271                        
272                }
273                catch(SQLException sqle) {
274                        throw new DatabaseException(sqle,dc);
275                }
276                finally {
277                        manager.releaseConnection(pageContext,dc);
278                }
279                
280                
281                                                                
282                         
283                return SKIP_BODY;
284        }
285
286        
287        
288        private void typeColumns(DatabaseMetaData metaData) throws PageException, SQLException {
289                required("table",table);
290                
291                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
292                stopwatch.start();
293
294                table=setCase(metaData, table);
295                pattern=setCase(metaData, pattern);
296                if(StringUtil.isEmpty(pattern,true)) pattern=null;
297                String schema=null;
298                int index=table.indexOf('.');
299                if(index>0) {
300                        schema=table.substring(0,index);
301                        table=table.substring(index+1);
302                }
303                checkTable(metaData);
304                
305        Query qry = new QueryImpl(
306                        metaData.getColumns(dbname, schema, table, pattern),
307                        "query",
308                        pageContext.getTimeZone());
309        
310                int len=qry.getRecordcount();
311
312                if(qry.getColumn(COLUMN_DEF,null) != null)
313                        qry.rename(COLUMN_DEF,COLUMN_DEFAULT_VALUE);
314                else if(qry.getColumn(COLUMN_DEFAULT,null) != null)
315                        qry.rename(COLUMN_DEFAULT,COLUMN_DEFAULT_VALUE);
316                
317                // make sure decimal digits exists
318                QueryColumn col = qry.getColumn(DECIMAL_DIGITS,null);
319                if(col==null){
320                        Array arr=new ArrayImpl();
321                        for(int i=1;i<=len;i++) {
322                                arr.append(lucee.runtime.op.Constants.DOUBLE_ZERO);
323                        }
324                        qry.addColumn(DECIMAL_DIGITS, arr);
325                }
326                
327                
328                // add is primary
329                Map primaries = new HashMap();
330                String tblName;
331                Array isPrimary=new ArrayImpl();
332                Set set;
333                Object o;
334                for(int i=1;i<=len;i++) {
335                        
336                        // decimal digits
337                        o=qry.getAt(DECIMAL_DIGITS, i,null);
338                        if(o==null)qry.setAtEL(DECIMAL_DIGITS, i,lucee.runtime.op.Constants.DOUBLE_ZERO);
339                        
340                        set=(Set) primaries.get(tblName=(String) qry.getAt(TABLE_NAME, i));
341                        if(set==null) {
342                                set=toSet(metaData.getPrimaryKeys(dbname, null, tblName),true,"COLUMN_NAME");
343                                primaries.put(tblName,set);
344                        }
345                        isPrimary.append(set.contains(qry.getAt(COLUMN_NAME, i))?"YES":"NO"); 
346                }
347                qry.addColumn(IS_PRIMARYKEY, isPrimary);
348                
349                // add is foreignkey
350                Map foreigns = new HashMap();
351                Array isForeign=new ArrayImpl();
352                Array refPrim=new ArrayImpl();
353                Array refPrimTbl=new ArrayImpl();
354                //Map map,inner;
355                Map<String, Map<String, SVArray>> map;
356                Map<String, SVArray> inner;
357                for(int i=1;i<=len;i++) {
358                        map=(Map) foreigns.get(tblName=(String) qry.getAt(TABLE_NAME, i));
359                        if(map==null) {
360                                map=toMap(
361                                                metaData.getImportedKeys(dbname, schema, table),
362                                                true,
363                                                "FKCOLUMN_NAME",
364                                                new String[]{"PKCOLUMN_NAME","PKTABLE_NAME"});
365                                foreigns.put(tblName, map);
366                        }
367                        inner = map.get(qry.getAt(COLUMN_NAME, i));
368                        if(inner!=null) {
369                                isForeign.append("YES");
370                                refPrim.append(inner.get("PKCOLUMN_NAME")); 
371                                refPrimTbl.append(inner.get("PKTABLE_NAME"));
372                        }
373                        else {
374                                isForeign.append("NO"); 
375                                refPrim.append("N/A"); 
376                                refPrimTbl.append("N/A");        
377                        }
378                }
379                qry.addColumn(IS_FOREIGNKEY, isForeign);
380                qry.addColumn(REFERENCED_PRIMARYKEY, refPrim);
381                qry.addColumn(REFERENCED_PRIMARYKEY_TABLE, refPrimTbl);
382                
383                
384                qry.setExecutionTime(stopwatch.time());
385        
386        
387                pageContext.setVariable(name, qry);
388        }
389
390        private Map<String,Map<String, SVArray>> toMap(ResultSet result,boolean closeResult, String columnName,String[] additional) throws SQLException {
391                Map<String,Map<String, SVArray>> map=new HashMap<String,Map<String, SVArray>>();
392                Map<String, SVArray> inner;
393                String col;
394                SVArray item;
395                if(result==null) return map;
396                try {
397                        while(result.next()){
398                                col=result.getString(columnName);
399                                inner=map.get(col);
400                                if(inner!=null) {
401                                        for(int i=0;i<additional.length;i++) {
402                                                item=inner.get(additional[i]);
403                                                item.add(result.getString(additional[i]));
404                                                item.setPosition(item.size());
405                                        }
406                                }
407                                else {
408                                        inner=new HashMap<String, SVArray>();
409                                        map.put(col, inner);
410                                        for(int i=0;i<additional.length;i++) {
411                                                item=new SVArray();
412                                                item.add(result.getString(additional[i]));
413                                                inner.put(additional[i], item);
414                                        }
415                                }
416                        }
417                }
418                finally {
419                        if(closeResult)IOUtil.closeEL(result);
420                }
421                return map;
422        }
423        
424        private Set<String> toSet(ResultSet result, boolean closeResult, String columnName) throws SQLException {
425                Set<String> set = new HashSet<String>();
426                if(result==null) return set;
427                
428                try{
429                        while(result.next()){
430                                set.add(result.getString(columnName)); 
431                        }
432                        return set;
433                }
434                finally {
435                        if(closeResult)IOUtil.closeEL(result);
436                }
437                
438        }
439
440        private void typeDBNames(DatabaseMetaData metaData) throws PageException, SQLException {
441
442                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
443                stopwatch.start();
444        
445        lucee.runtime.type.Query catalogs = new QueryImpl(metaData.getCatalogs(),"query",pageContext.getTimeZone());
446        lucee.runtime.type.Query scheme = new QueryImpl(metaData.getSchemas(),"query",pageContext.getTimeZone());
447        
448        Pattern p=null;
449        if(pattern!=null && !"%".equals(pattern)) 
450                p=SQLUtil.pattern(pattern, true);
451                
452        
453        
454        String[] columns=new String[]{"database_name","type"};
455                String[] types=new String[]{"VARCHAR","VARCHAR"};
456                lucee.runtime.type.Query qry=new QueryImpl(columns,types,0,"query");
457                int row=1,len=catalogs.getRecordcount();
458                String value;
459                // catalog
460                for(int i=1;i<=len;i++) {
461                        value=(String) catalogs.getAt(TABLE_CAT, i);
462                        if(!matchPattern(value,p)) continue;
463                        qry.addRow();
464                        qry.setAt(DATABASE_NAME, row, value);
465                        qry.setAt(KeyConstants._type, row, "CATALOG");
466                        row++;
467                }
468                // scheme
469                len=scheme.getRecordcount();
470                for(int i=1;i<=len;i++) {
471                        value=(String) scheme.getAt(TABLE_SCHEM, i);
472                        if(!matchPattern(value,p)) continue;
473                        qry.addRow();
474                        qry.setAt(DATABASE_NAME, row, value);
475                        qry.setAt(KeyConstants._type, row, "SCHEMA");
476                        row++;
477                }
478                
479        qry.setExecutionTime(stopwatch.time());
480        
481        
482                pageContext.setVariable(name, qry);
483        }
484
485        private void typeForeignKeys(DatabaseMetaData metaData) throws PageException, SQLException {
486                required("table",table);
487                
488                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
489                stopwatch.start();
490        table=setCase(metaData, table);
491        int index=table.indexOf('.');
492                String schema=null;
493        if(index>0) {
494                        schema=table.substring(0,index);
495                        table=table.substring(index+1);
496                }
497                
498                checkTable(metaData);
499                
500                lucee.runtime.type.Query qry = new QueryImpl(
501                                metaData.getExportedKeys(dbname, schema, table),
502                                "query",
503                                pageContext.getTimeZone());             
504                qry.setExecutionTime(stopwatch.time());        
505        
506                pageContext.setVariable(name, qry);
507        }
508
509        private void checkTable(DatabaseMetaData metaData) throws SQLException, ApplicationException {
510                ResultSet tables =null;
511                
512                try {
513                        tables = metaData.getTables(null, null, setCase(metaData,table), null);
514                        if(!tables.next()) throw new ApplicationException("there is no table that match the following pattern ["+table+"]");
515                }
516                finally {
517                        if(tables!=null) tables.close();
518                }
519        }
520
521        private String setCase(DatabaseMetaData metaData, String id) throws SQLException {
522                if(id==null) return null;
523                
524                if(metaData.storesLowerCaseIdentifiers()) return id.toLowerCase();
525                if(metaData.storesUpperCaseIdentifiers()) return id.toUpperCase();
526                return id;
527        }
528
529        private void typeIndex(DatabaseMetaData metaData) throws PageException, SQLException {
530                required("table",table);
531                
532                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
533                stopwatch.start();
534        
535                table=setCase(metaData, table);
536        int index=table.indexOf('.');
537                String schema=null;
538        if(index>0) {
539                        schema=table.substring(0,index);
540                        table=table.substring(index+1);
541                }
542                
543                checkTable(metaData);
544                
545        ResultSet tables = metaData.getIndexInfo(dbname, schema, table, false, true);
546        lucee.runtime.type.Query qry = new QueryImpl(tables,"query",pageContext.getTimeZone());
547        
548        // type int 2 string
549        int rows = qry.getRecordcount();
550        String strType;
551        int type,card;
552        for(int row=1;row<=rows;row++){
553                
554                // type
555                switch(type=Caster.toIntValue(qry.getAt(KeyConstants._type,row))){
556                case 0:
557                        strType="Table Statistic";
558                break;
559                case 1:
560                        strType="Clustered Index";
561                break;
562                case 2:
563                        strType="Hashed Index";
564                break;
565                case 3:
566                        strType="Other Index";
567                break;
568                default:
569                        strType=Caster.toString(type);
570                }
571                qry.setAt(KeyConstants._type, row, strType);
572                
573                // CARDINALITY
574                card=Caster.toIntValue(qry.getAt(CARDINALITY,row),0);
575                qry.setAt(CARDINALITY, row, Caster.toDouble(card));
576                
577        }
578        qry.setExecutionTime(stopwatch.time());
579        
580        
581                pageContext.setVariable(name, qry);
582        }
583
584        private void typeProcedures(DatabaseMetaData metaData) throws SQLException, PageException {
585                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
586                stopwatch.start();
587        
588                String schema=null;
589                pattern=setCase(metaData, pattern);
590                if(StringUtil.isEmpty(pattern,true)) {
591                        pattern=null;
592                }
593                
594        lucee.runtime.type.Query qry = new QueryImpl(
595                        metaData.getProcedures(dbname, schema, pattern),
596                        "query",
597                        pageContext.getTimeZone());
598        qry.setExecutionTime(stopwatch.time());
599        
600        
601                pageContext.setVariable(name, qry);
602        }
603        
604        private void typeProcedureColumns(DatabaseMetaData metaData) throws SQLException, PageException {
605                required("procedure",procedure);
606                
607                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
608                stopwatch.start();
609                
610                procedure=setCase(metaData, procedure);         
611                pattern=setCase(metaData, pattern);
612                if(StringUtil.isEmpty(pattern,true)) pattern=null;
613                String schema=null;
614                int index=procedure.indexOf('.');
615                if(index>0) {
616                        schema=procedure.substring(0,index);
617                        procedure=procedure.substring(index+1);
618                }
619                
620                
621                lucee.runtime.type.Query qry = new QueryImpl(
622                                metaData.getProcedureColumns(dbname, schema, procedure, pattern),
623                                "query",
624                                pageContext.getTimeZone());
625                qry.setExecutionTime(stopwatch.time());
626        
627        
628                pageContext.setVariable(name, qry);
629        }
630
631        private void typeTerms(DatabaseMetaData metaData) throws SQLException, PageException {
632                Struct sct=new StructImpl();
633                sct.setEL(PROCEDURE, metaData.getProcedureTerm());
634                sct.setEL(CATALOG, metaData.getCatalogTerm());
635                sct.setEL(SCHEMA, metaData.getSchemaTerm());
636                
637                pageContext.setVariable(name, sct);
638        }
639
640        private void typeTables(DatabaseMetaData metaData) throws PageException, SQLException {
641
642                
643                
644                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
645                stopwatch.start();
646        
647                pattern=setCase(metaData, pattern);
648                
649        lucee.runtime.type.Query qry = new QueryImpl(
650                        metaData.getTables(dbname, null, pattern, null),
651                        "query",
652                        pageContext.getTimeZone());
653        qry.setExecutionTime(stopwatch.time());
654        
655        
656                pageContext.setVariable(name, qry);
657        }
658
659        private void typeVersion(DatabaseMetaData metaData) throws PageException, SQLException {
660
661                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
662                stopwatch.start();
663                
664                Key[] columns=new Key[]{DATABASE_PRODUCTNAME,DATABASE_VERSION,DRIVER_NAME,DRIVER_VERSION,JDBC_MAJOR_VERSION,JDBC_MINOR_VERSION};
665                String[] types=new String[]{"VARCHAR","VARCHAR","VARCHAR","VARCHAR","DOUBLE","DOUBLE"};
666                
667                lucee.runtime.type.Query qry=new QueryImpl(columns,types,1,"query");
668
669                qry.setAt(DATABASE_PRODUCTNAME,1,metaData.getDatabaseProductName());
670                qry.setAt(DATABASE_VERSION,1,metaData.getDatabaseProductVersion());
671                qry.setAt(DRIVER_NAME,1,metaData.getDriverName());
672                qry.setAt(DRIVER_VERSION,1,metaData.getDriverVersion());
673                qry.setAt(JDBC_MAJOR_VERSION,1,new Double(metaData.getJDBCMajorVersion()));
674                qry.setAt(JDBC_MINOR_VERSION,1,new Double(metaData.getJDBCMinorVersion()));
675                
676                
677        qry.setExecutionTime(stopwatch.time());
678        
679        
680                pageContext.setVariable(name, qry);
681        }
682        
683        private void typeUsers(DatabaseMetaData metaData) throws PageException, SQLException {
684                
685                Stopwatch stopwatch=new Stopwatch(Stopwatch.UNIT_NANO);
686                stopwatch.start();
687        
688                checkTable(metaData);
689                ResultSet result = metaData.getSchemas();
690                Query qry = new QueryImpl(result,"query",pageContext.getTimeZone());
691                
692                
693                qry.rename(TABLE_SCHEM,USER);
694        
695        qry.setExecutionTime(stopwatch.time());
696        
697        
698                pageContext.setVariable(name, qry);
699        }
700
701        private void required(String name, String value) throws ApplicationException {
702                if(value==null)
703                        throw new ApplicationException("Missing attribute ["+name+"]. The type ["+strType+"] requires the attribute [" + name + "].");
704        }
705
706        private static  boolean matchPattern(String value, Pattern pattern) {
707                if(pattern==null) return true;
708                return SQLUtil.match(pattern, value);
709        }
710
711        @Override
712        public int doEndTag()   {
713                return EVAL_PAGE;
714        }
715        
716
717        public static Object getDatasource(PageContext pageContext, String datasource) throws ApplicationException {
718                if(StringUtil.isEmpty(datasource)){
719                        Object ds=((ApplicationContextPro)pageContext.getApplicationContext()).getDefDataSource();
720
721                        if(StringUtil.isEmpty(ds))
722                                throw new ApplicationException(
723                                                "attribute [datasource] is required, when no default datasource is defined",
724                                                "you can define a default datasource as attribute [defaultdatasource] of the tag "+Constants.CFAPP_NAME+" or as data member of the "+Constants.APP_CFC+" (this.defaultdatasource=\"mydatasource\";)");
725                        return ds;
726                }
727                return datasource;
728        }
729}