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.type;
020
021import java.io.ByteArrayInputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.io.ObjectInput;
025import java.io.ObjectOutput;
026import java.io.Reader;
027import java.io.StringReader;
028import java.io.UnsupportedEncodingException;
029import java.math.BigDecimal;
030import java.net.URL;
031import java.nio.charset.Charset;
032import java.sql.Blob;
033import java.sql.Clob;
034import java.sql.NClob;
035import java.sql.PreparedStatement;
036import java.sql.Ref;
037import java.sql.ResultSet;
038import java.sql.ResultSetMetaData;
039import java.sql.RowId;
040import java.sql.SQLException;
041import java.sql.SQLWarning;
042import java.sql.SQLXML;
043import java.sql.Statement;
044import java.sql.Time;
045import java.sql.Timestamp;
046import java.sql.Types;
047import java.util.Arrays;
048import java.util.Calendar;
049import java.util.Comparator;
050import java.util.HashMap;
051import java.util.HashSet;
052import java.util.Iterator;
053import java.util.Map;
054import java.util.Map.Entry;
055import java.util.Set;
056import java.util.TimeZone;
057
058import lucee.commons.db.DBUtil;
059import lucee.commons.io.IOUtil;
060import lucee.commons.lang.ExceptionUtil;
061import lucee.commons.lang.StringUtil;
062import lucee.commons.sql.SQLUtil;
063import lucee.loader.engine.CFMLEngineFactory;
064import lucee.runtime.PageContext;
065import lucee.runtime.config.NullSupportHelper;
066import lucee.runtime.converter.ScriptConverter;
067import lucee.runtime.db.CFTypes;
068import lucee.runtime.db.DataSourceUtil;
069import lucee.runtime.db.DatasourceConnection;
070import lucee.runtime.db.DatasourceConnectionImpl;
071import lucee.runtime.db.SQL;
072import lucee.runtime.db.SQLCaster;
073import lucee.runtime.db.SQLItem;
074import lucee.runtime.dump.DumpData;
075import lucee.runtime.dump.DumpProperties;
076import lucee.runtime.engine.ThreadLocalPageContext;
077import lucee.runtime.exp.ApplicationException;
078import lucee.runtime.exp.DatabaseException;
079import lucee.runtime.exp.ExpressionException;
080import lucee.runtime.exp.PageException;
081import lucee.runtime.exp.PageRuntimeException;
082import lucee.runtime.interpreter.CFMLExpressionInterpreter;
083import lucee.runtime.op.Caster;
084import lucee.runtime.op.Decision;
085import lucee.runtime.op.Duplicator;
086import lucee.runtime.op.ThreadLocalDuplication;
087import lucee.runtime.op.date.DateCaster;
088import lucee.runtime.query.caster.Cast;
089import lucee.runtime.type.comparator.NumberSortRegisterComparator;
090import lucee.runtime.type.comparator.SortRegister;
091import lucee.runtime.type.comparator.SortRegisterComparator;
092import lucee.runtime.type.dt.DateTime;
093import lucee.runtime.type.dt.DateTimeImpl;
094import lucee.runtime.type.dt.TimeSpan;
095import lucee.runtime.type.it.CollectionIterator;
096import lucee.runtime.type.it.EntryIterator;
097import lucee.runtime.type.it.ForEachQueryIterator;
098import lucee.runtime.type.it.KeyIterator;
099import lucee.runtime.type.it.StringIterator;
100import lucee.runtime.type.sql.BlobImpl;
101import lucee.runtime.type.sql.ClobImpl;
102import lucee.runtime.type.util.ArrayUtil;
103import lucee.runtime.type.util.CollectionUtil;
104import lucee.runtime.type.util.KeyConstants;
105import lucee.runtime.type.util.ListUtil;
106import lucee.runtime.type.util.MemberUtil;
107import lucee.runtime.type.util.QueryUtil;
108
109/**
110 * implementation of the query interface
111 */
112/**
113 * 
114 */
115public class QueryImpl implements Query,Objects {
116
117        private static final long serialVersionUID = 1035795427320192551L; // do not chnage
118
119
120        /**
121         * @return the template
122         */
123        public String getTemplate() {
124                return template;
125        }
126
127        public static final Collection.Key GENERATED_KEYS = KeyImpl.intern("GENERATED_KEYS");
128        public static final Collection.Key GENERATEDKEYS = KeyImpl.intern("GENERATEDKEYS");
129        
130        
131        
132        //private static int count=0;
133        private QueryColumnImpl[] columns;
134        private Collection.Key[] columnNames;
135        private SQL sql;
136        private ArrayInt arrCurrentRow=new ArrayInt();
137        private int recordcount=0;
138        private int columncount;
139        private long exeTime=0;
140    private String cacheType=null;
141    private String name;
142        private int updateCount;
143    private QueryImpl generatedKeys;
144        private String template;
145        
146        
147        @Override
148        public int executionTime() {
149                return (int) exeTime;
150        }
151
152        /**
153         * create a QueryImpl from a SQL Resultset
154     * @param result SQL Resultset
155         * @param maxrow
156         * @param name 
157         * @throws PageException
158     */
159    public QueryImpl(ResultSet result, int maxrow, String name, TimeZone tz) throws PageException {
160        this.name=name;
161        //Stopwatch stopwatch=new Stopwatch();
162                //stopwatch.start();
163                long start=System.nanoTime();
164        try {
165            fillResult(null,result,maxrow,false,false,tz);
166        } catch (SQLException e) {
167            throw new DatabaseException(e,null);
168        } catch (IOException e) {
169            throw Caster.toPageException(e);
170        }
171                exeTime=System.nanoTime()-start;
172    }
173    
174    /**
175     * Constructor of the class
176     * only for internal usage (cloning/deserialize)
177     */
178    public QueryImpl() {
179    }
180    
181    
182    public QueryImpl(ResultSet result, String name,TimeZone tz) throws PageException {
183                this.name=name;
184        
185                try {   
186                    fillResult(null,result,-1,true,false,tz);
187                } 
188                catch (SQLException e) {
189                        throw new DatabaseException(e,null);
190                } 
191                catch (Exception e) {
192                    throw Caster.toPageException(e);
193                } 
194        }
195        
196        /**
197         * constructor of the class, to generate a resultset from a sql query
198         * @param dc Connection to a database
199         * @param name 
200         * @param sql sql to execute
201         * @param maxrow maxrow for the resultset
202         * @throws PageException
203         */     
204    public QueryImpl(PageContext pc, DatasourceConnection dc,SQL sql,int maxrow, int fetchsize,TimeSpan timeout, String name) throws PageException {
205        this(pc,dc, sql, maxrow, fetchsize, timeout, name,null,false,true);
206    }
207    
208
209    public QueryImpl(PageContext pc, DatasourceConnection dc,SQL sql,int maxrow, int fetchsize,TimeSpan timeout, String name,String template) throws PageException {
210        this(pc,dc, sql, maxrow, fetchsize, timeout, name,template,false,true);
211    }
212    
213        public QueryImpl(PageContext pc, DatasourceConnection dc,SQL sql,int maxrow, int fetchsize,TimeSpan timeout, String name,String template,boolean createUpdateData, boolean allowToCachePreperadeStatement) throws PageException {
214                this.name=name;
215                this.template=template;
216        this.sql=sql;
217                TimeZone tz = ThreadLocalPageContext.getTimeZone(pc);
218        //ResultSet result=null;
219                Statement stat=null;
220                // check SQL Restrictions
221                if(dc.getDatasource().hasSQLRestriction()) {
222                        QueryUtil.checkSQLRestriction(dc,sql);
223        }
224                // check if datasource support Generated Keys
225                boolean createGeneratedKeys=createUpdateData;
226        if(createUpdateData){
227                DatasourceConnectionImpl dci=(DatasourceConnectionImpl) dc;
228                if(!dci.supportsGetGeneratedKeys())createGeneratedKeys=false;
229        }
230
231        
232                //Stopwatch stopwatch=new Stopwatch();
233        long start=System.nanoTime();
234                //stopwatch.start();
235                boolean hasResult=false;
236                //boolean closeStatement=true;
237                try {   
238                        SQLItem[] items=sql.getItems();
239                        if(items.length==0) {
240                        stat=dc.getConnection().createStatement();
241                        setAttributes(stat,maxrow,fetchsize,timeout);
242                     // some driver do not support second argument
243                        //hasResult=createGeneratedKeys?stat.execute(sql.getSQLString(),Statement.RETURN_GENERATED_KEYS):stat.execute(sql.getSQLString());
244                        hasResult=QueryUtil.execute(pc,stat,createGeneratedKeys,sql);
245                }
246                else {
247                        // some driver do not support second argument
248                        PreparedStatement preStat = dc.getPreparedStatement(sql, createGeneratedKeys,allowToCachePreperadeStatement);
249                        //closeStatement=false;
250                        stat=preStat;
251                    setAttributes(preStat,maxrow,fetchsize,timeout);
252                    setItems(ThreadLocalPageContext.getTimeZone(pc),preStat,items);
253                        hasResult=QueryUtil.execute(pc,preStat);    
254                }
255                        int uc;
256                        ResultSet res;
257                        do {
258                                if(hasResult) {
259                                        res=stat.getResultSet();
260                                        if(fillResult(dc,res, maxrow, true,createGeneratedKeys,tz))break;
261                                }
262                                else if((uc=setUpdateCount(stat))!=-1){
263                                        if(uc>0 && createGeneratedKeys)setGeneratedKeys(dc, stat,tz);
264                                }
265                                else break;
266                                try{
267                                        hasResult=stat.getMoreResults(Statement.CLOSE_CURRENT_RESULT);
268                                }
269                                catch(Throwable t){
270                                        ExceptionUtil.rethrowIfNecessary(t);
271                                        break;
272                                }
273                        }
274                        while(true);
275                } 
276                catch (SQLException e) {
277                        throw new DatabaseException(e,sql,dc);
278                } 
279                catch (Throwable e) {
280                        throw Caster.toPageException(e);
281                }
282        finally {
283                //if(closeStatement)
284                        DBUtil.closeEL(stat);
285        }  
286                exeTime=System.nanoTime()-start;
287
288                if(columncount==0) {
289                        if(columnNames==null) columnNames=new Collection.Key[0];
290                        if(columns==null) columns=new QueryColumnImpl[0];
291                }
292        }
293        
294        private int setUpdateCount(Statement stat)  {
295                try{
296                        int uc=stat.getUpdateCount();
297                        if(uc>-1){
298                                updateCount+=uc;
299                                return uc;
300                        }
301                }
302                catch(Throwable t){
303                        ExceptionUtil.rethrowIfNecessary(t);
304                }
305                return -1;
306        }
307        
308        private boolean setGeneratedKeys(DatasourceConnection dc,Statement stat, TimeZone tz)  {
309                try{
310                        ResultSet rs = stat.getGeneratedKeys();
311                        setGeneratedKeys(dc, rs,tz);
312                        return true;
313                }
314                catch(Throwable t) {
315                        ExceptionUtil.rethrowIfNecessary(t);
316                        t.printStackTrace();
317                        return false;
318                }
319        }
320        
321        private void setGeneratedKeys(DatasourceConnection dc,ResultSet rs, TimeZone tz) throws PageException  {
322                generatedKeys=new QueryImpl(rs,"",tz);
323                
324                // ACF compatibility action 
325                if(generatedKeys.getColumnCount()==1 && DataSourceUtil.isMSSQL(dc)) {
326                        generatedKeys.renameEL(GENERATED_KEYS,KeyConstants._IDENTITYCOL);
327                        generatedKeys.renameEL(GENERATEDKEYS,KeyConstants._IDENTITYCOL);
328                        generatedKeys.renameEL(KeyConstants._ID,KeyConstants._IDENTITYCOL);
329                }
330        }
331        
332        /*private void setUpdateData(Statement stat, boolean createGeneratedKeys, boolean createUpdateCount)  {
333                
334                // update Count
335                if(createUpdateCount){
336                        try{
337                                updateCount=stat.getUpdateCount();
338                        }
339                        catch(Throwable t){
340                                t.printStackTrace();
341                        }
342                }
343                // generated keys
344                if(createGeneratedKeys){
345                        try{
346                                ResultSet rs = stat.getGeneratedKeys();
347                                generatedKeys=new QueryImpl(rs,"");
348                        }
349                        catch(Throwable t){
350                                t.printStackTrace();
351                        }
352                }
353        }*/
354
355
356        private void setItems(TimeZone tz,PreparedStatement preStat, SQLItem[] items) throws DatabaseException, PageException, SQLException {
357                for(int i=0;i<items.length;i++) {
358            SQLCaster.setValue(tz,preStat,i+1,items[i]);
359        }
360        }
361
362        public int getUpdateCount() {
363                return updateCount;
364        }
365        public Query getGeneratedKeys() {
366                return generatedKeys;
367        }
368
369        private void setAttributes(Statement stat,int maxrow, int fetchsize,TimeSpan timeout) throws SQLException {
370                if(maxrow>-1) stat.setMaxRows(maxrow);
371        if(fetchsize>0)stat.setFetchSize(fetchsize);
372        if(timeout!=null && ((int)timeout.getSeconds())>0) 
373                DataSourceUtil.setQueryTimeoutSilent(stat,(int)timeout.getSeconds());
374        }
375
376    private boolean fillResult(DatasourceConnection dc, ResultSet result, int maxrow, boolean closeResult,boolean createGeneratedKeys, TimeZone tz) throws SQLException, IOException, PageException {
377        if(result==null) return false;
378        try {
379                recordcount=0;
380                        ResultSetMetaData meta = result.getMetaData();
381                        columncount=meta.getColumnCount();
382                        
383                // set header arrays
384                        Collection.Key[] tmpColumnNames = new Collection.Key[columncount];
385                        int count=0;
386                        Collection.Key key;
387                        String columnName;
388                        for(int i=0;i<columncount;i++) {
389                                columnName=QueryUtil.getColumnName(meta,i+1);
390                                if(StringUtil.isEmpty(columnName))columnName="column_"+i;
391                                key=KeyImpl.init(columnName);
392                                int index=getIndexFrom(tmpColumnNames,key,0,i);
393                                if(index==-1) {
394                                        tmpColumnNames[i]=key;
395                                        count++;
396                                }
397                        }
398                        
399        
400                        columncount=count;
401                        columnNames=new Collection.Key[columncount];
402                        columns=new QueryColumnImpl[columncount];
403                        Cast[] casts = new Cast[columncount];
404                        
405                // get all used ints
406                        int[] usedColumns=new int[columncount];
407                        count=0;
408                        for(int i=0;i<tmpColumnNames.length;i++) {
409                                if(tmpColumnNames[i]!=null) {
410                                        usedColumns[count++]=i;
411                                }
412                        }       
413                                                
414                // set used column names
415                        int[] types=new int[columns.length];
416                        for(int i=0;i<usedColumns.length;i++) {
417                    columnNames[i]=tmpColumnNames[usedColumns[i]];
418                    columns[i]=new QueryColumnImpl(this,columnNames[i],types[i]=meta.getColumnType(usedColumns[i]+1));
419                    
420                    if(types[i]==Types.TIMESTAMP)       casts[i]=Cast.TIMESTAMP;
421                    else if(types[i]==Types.TIME)       casts[i]=Cast.TIME;
422                    else if(types[i]==Types.DATE)       casts[i]=Cast.DATE;
423                    else if(types[i]==Types.CLOB)       casts[i]=Cast.CLOB;
424                    else if(types[i]==Types.BLOB)       casts[i]=Cast.BLOB;
425                    else if(types[i]==Types.BIT)        casts[i]=Cast.BIT;
426                    else if(types[i]==Types.ARRAY)      casts[i]=Cast.ARRAY;
427                    else if(types[i]==Types.BIGINT)     casts[i]=Cast.BIGINT;
428                    //else if(types[i]==Types.TINYINT)  casts[i]=Cast.ARRAY;
429                    
430                    else if(types[i]==CFTypes.OPAQUE){
431                        if(SQLUtil.isOracle(result.getStatement().getConnection()))
432                                casts[i]=Cast.ORACLE_OPAQUE;
433                        else 
434                                casts[i]=Cast.OTHER;
435                                        
436                    }
437                    else casts[i]=Cast.OTHER;
438                        }
439                        
440                        if(createGeneratedKeys && columncount==1 && columnNames[0].equals(GENERATED_KEYS) && dc!=null && DataSourceUtil.isMSSQLDriver(dc)) {
441                                columncount=0;
442                                columnNames=null;
443                                columns=null;
444                                setGeneratedKeys(dc, result,tz);
445                                return false;
446                        }
447                        
448        
449                // fill data
450                        //Object o;
451                        while(result.next()) {
452                                if(maxrow>-1 && recordcount>=maxrow) {
453                                        break;
454                                }
455                                for(int i=0;i<usedColumns.length;i++) {
456                                    columns[i].add(casts[i].toCFType(tz,types[i], result, usedColumns[i]+1));
457                                }
458                                ++recordcount;
459                        }
460        }
461        finally {
462                if(closeResult)IOUtil.closeEL(result);
463        }
464                
465                return true;
466        }
467
468    private Object toBytes(Blob blob) throws IOException, SQLException {
469                return IOUtil.toBytes((blob).getBinaryStream());
470        }
471
472        private static Object toString(Clob clob) throws IOException, SQLException {
473                return IOUtil.toString(clob.getCharacterStream());
474        }
475
476    private static int getIndexFrom(Collection.Key[] tmpColumnNames, Collection.Key key, int from, int to) {
477                for(int i=from;i<to;i++) {
478                        if(tmpColumnNames[i]!=null && tmpColumnNames[i].equalsIgnoreCase(key))return i;
479                }
480                return -1;
481        }
482
483        /**
484         * constructor of the class, to generate a empty resultset (no database execution)
485         * @param strColumns columns for the resultset
486         * @param rowNumber count of rows to generate (empty fields)
487         * @param name 
488         * @deprecated use instead <code>QueryImpl(Collection.Key[] columnKeys, int rowNumber,String name)</code>
489         */
490        public QueryImpl(String[] strColumns, int rowNumber,String name) {
491        this.name=name;
492        columncount=strColumns.length;
493                recordcount=rowNumber;
494                columnNames=new Collection.Key[columncount];
495                columns=new QueryColumnImpl[columncount];
496                for(int i=0;i<strColumns.length;i++) {
497                        columnNames[i]=KeyImpl.init(strColumns[i].trim());
498                        columns[i]=new QueryColumnImpl(this,columnNames[i],Types.OTHER,recordcount);
499                }
500        }
501
502        /**
503         * constructor of the class, to generate a empty resultset (no database execution)
504         * @param strColumns columns for the resultset
505         * @param rowNumber count of rows to generate (empty fields)
506         * @param name 
507         */
508        public QueryImpl(Collection.Key[] columnKeys, int rowNumber,String name) throws DatabaseException {
509                this.name=name;
510        columncount=columnKeys.length;
511                recordcount=rowNumber;
512                columnNames=new Collection.Key[columncount];
513                columns=new QueryColumnImpl[columncount];
514                for(int i=0;i<columnKeys.length;i++) {
515                        columnNames[i]=columnKeys[i];
516                        columns[i]=new QueryColumnImpl(this,columnNames[i],Types.OTHER,recordcount);
517                }
518                validateColumnNames(columnNames);
519        }
520        
521        /**
522         * constructor of the class, to generate a empty resultset (no database execution)
523         * @param strColumns columns for the resultset
524         * @param strTypes array of the types
525         * @param rowNumber count of rows to generate (empty fields)
526         * @param name 
527         * @throws DatabaseException 
528         */
529        public QueryImpl(String[] strColumns, String[] strTypes, int rowNumber, String name) throws DatabaseException {
530        this.name=name;
531        columncount=strColumns.length;
532                if(strTypes.length!=columncount) throw new DatabaseException("columns and types has not the same count",null,null,null);
533                recordcount=rowNumber;
534                columnNames=new Collection.Key[columncount];
535                columns=new QueryColumnImpl[columncount];
536                for(int i=0;i<strColumns.length;i++) {
537                        columnNames[i]=KeyImpl.init(strColumns[i].trim());
538                        columns[i]=new QueryColumnImpl(this,columnNames[i],SQLCaster.toSQLType(strTypes[i]),recordcount);
539                }
540        }
541        
542        /**
543         * constructor of the class, to generate a empty resultset (no database execution)
544         * @param strColumns columns for the resultset
545         * @param strTypes array of the types
546         * @param rowNumber count of rows to generate (empty fields)
547         * @param name 
548         * @throws DatabaseException 
549         */
550        public QueryImpl(Collection.Key[] columnNames, String[] strTypes, int rowNumber, String name) throws DatabaseException {
551        this.name=name;
552        this.columnNames=columnNames;
553        columncount=columnNames.length;
554                if(strTypes.length!=columncount) throw new DatabaseException("columns and types has not the same count",null,null,null);
555                recordcount=rowNumber;
556                columns=new QueryColumnImpl[columncount];
557                for(int i=0;i<columnNames.length;i++) {
558                        columns[i]=new QueryColumnImpl(this,columnNames[i],SQLCaster.toSQLType(strTypes[i]),recordcount);
559                }
560                validateColumnNames(columnNames);
561        }
562        
563        /**
564         * constructor of the class, to generate a empty resultset (no database execution)
565         * @param arrColumns columns for the resultset
566         * @param rowNumber count of rows to generate (empty fields)
567         * @param name 
568         * @throws DatabaseException 
569         */
570        public QueryImpl(Array arrColumns, int rowNumber, String name) throws DatabaseException {
571        this.name=name;
572        columncount=arrColumns.size();
573                recordcount=rowNumber;
574                columnNames=new Collection.Key[columncount];
575                columns=new QueryColumnImpl[columncount];
576                for(int i=0;i<columncount;i++) {
577                        columnNames[i]=KeyImpl.init(arrColumns.get(i+1,"").toString().trim());
578                        columns[i]=new QueryColumnImpl(this,columnNames[i],Types.OTHER,recordcount);
579                }
580                validateColumnNames(columnNames);
581        }
582
583        /**
584         * constructor of the class, to generate a empty resultset (no database execution)
585         * @param arrColumns columns for the resultset
586         * @param arrTypes type of the columns
587         * @param rowNumber count of rows to generate (empty fields)
588         * @param name 
589         * @throws PageException
590         */
591        public QueryImpl(Array arrColumns, Array arrTypes, int rowNumber, String name) throws PageException {
592        this.name=name;
593        columncount=arrColumns.size();
594                if(arrTypes.size()!=columncount) throw new DatabaseException("columns and types has not the same count",null,null,null);
595                recordcount=rowNumber;
596                columnNames=new Collection.Key[columncount];
597                columns=new QueryColumnImpl[columncount];
598                for(int i=0;i<columncount;i++) {
599                        columnNames[i]=KeyImpl.init(arrColumns.get(i+1,"").toString().trim());
600                        columns[i]=new QueryColumnImpl(this,columnNames[i],SQLCaster.toSQLType(Caster.toString(arrTypes.get(i+1,""))),recordcount);
601                }
602                validateColumnNames(columnNames);
603        }
604
605        /**
606         * constructor of the class
607         * @param columnNames columns definition as String Array
608         * @param arrColumns values
609         * @param name 
610         * @throws DatabaseException
611         */
612
613        public QueryImpl(String[] strColumnNames, Array[] arrColumns, String name) throws DatabaseException {
614                this(CollectionUtil.toKeys(strColumnNames,true),arrColumns,name);               
615        }       
616        
617        private static void validateColumnNames(Key[] columnNames) throws DatabaseException {
618                Set<String> testMap=new HashSet<String>();
619                for(int i=0     ;i<columnNames.length;i++) {
620                        
621                        // Only allow column names that are valid variable name
622                        //if(!Decision.isSimpleVariableName(columnNames[i]))
623                        //      throw new DatabaseException("invalid column name ["+columnNames[i]+"] for query", "column names must start with a letter and can be followed by letters numbers and underscores [_]. RegExp:[a-zA-Z][a-zA-Z0-9_]*",null,null,null);
624                        
625                        if(testMap.contains(columnNames[i].getLowerString()))
626                                throw new DatabaseException("invalid parameter for query, ambiguous column name "+columnNames[i],"columnNames: "+ListUtil.arrayToListTrim( _toStringKeys(columnNames),","),null,null);
627                        testMap.add(columnNames[i].getLowerString());
628                }
629        }
630        
631
632        
633        private static String[] _toStringKeys(Collection.Key[] columnNames) {
634                String[] strColumnNames=new String[columnNames.length];
635                for(int i=0     ;i<strColumnNames.length;i++) {
636                        strColumnNames[i]=columnNames[i].getString();
637                }
638                return strColumnNames;
639        }
640        
641        /*public QueryImpl(Collection.Key[] columnNames, QueryColumn[] columns, String name,long exeTime, boolean isCached,SQL sql) throws DatabaseException {
642                this.columnNames=columnNames;
643                this.columns=columns;
644                this.exeTime=exeTime;
645                this.isCached=isCached;
646                this.name=name;
647                this.columncount=columnNames.length;
648                this.recordcount=columns.length==0?0:columns[0].size();
649                this.sql=sql;
650                
651        }*/
652
653        public QueryImpl(Collection.Key[] columnNames, Array[] arrColumns, String name) throws DatabaseException {
654        this.name=name;
655        
656        if(columnNames.length!=arrColumns.length)
657                        throw new DatabaseException("invalid parameter for query, not the same count from names and columns","names:"+columnNames.length+";columns:"+arrColumns.length,null,null);
658                int len=0;
659                columns=new QueryColumnImpl[arrColumns.length];
660                if(arrColumns.length>0) {
661                // test columns
662                        len=arrColumns[0].size();
663                        for(int i=0;i<arrColumns.length;i++) {
664                                if(arrColumns[i].size()!=len)
665                                        throw new DatabaseException("invalid parameter for query, all columns must have the same size","column[1]:"+len+"<>column["+(i+1)+"]:"+arrColumns[i].size(),null,null);
666                                //columns[i]=new QueryColumnTypeFlex(arrColumns[i]);
667                                columns[i]=new QueryColumnImpl(this,columnNames[i],arrColumns[i],Types.OTHER);
668                        }
669                // test keys
670                        validateColumnNames(columnNames);
671                }
672                
673                columncount=columns.length;
674                recordcount=len;
675                this.columnNames=columnNames;
676        }
677        
678    /**
679     * constructor of the class
680     * @param columnList
681     * @param data
682     * @param name 
683     * @throws DatabaseException 
684     */
685    public QueryImpl(String[] strColumnList, Object[][] data,String name) throws DatabaseException {
686        
687        this(toCollKeyArr(strColumnList),data.length,name);
688        
689        for(int iRow=0;iRow<data.length;iRow++) {
690            Object[] row=data[iRow];
691            for(int iCol=0;iCol<row.length;iCol++) {
692                //print.ln(columnList[iCol]+":"+iRow+"="+row[iCol]);
693                setAtEL(columnNames[iCol],iRow+1,row[iCol]);
694            }
695        }
696    }
697
698    private static Collection.Key[] toCollKeyArr(String[] strColumnList) {
699        Collection.Key[] columnList=new Collection.Key[strColumnList.length];
700                for(int i=0     ;i<columnList.length;i++) {
701                        columnList[i]=KeyImpl.init(strColumnList[i].trim());
702                }
703                return columnList;
704        }
705
706        @Override
707        public int size() {
708                return columncount;
709        }
710
711        @Override
712        public Collection.Key[] keys() {
713                return columnNames;
714        }
715
716        @Override
717        public Object removeEL(Collection.Key key) {
718                return setEL(key,null);
719        }
720
721        @Override
722        public Object remove(Collection.Key key) throws PageException {
723                return set(key,null);
724        }
725
726        @Override
727        public void clear() {
728                for(int i=0;i<columns.length;i++) {
729                        columns[i].clear();
730        }
731        recordcount=0;
732        }
733
734        @Override
735        public Object get(String key, Object defaultValue) {
736                return getAt(key,
737                                arrCurrentRow.get(getPid(), 1),
738                                defaultValue);
739        }
740
741        //private static int pidc=0;
742        private int getPid() {
743                
744                PageContext pc = ThreadLocalPageContext.get();
745                if(pc==null) {
746                        pc=CFMLEngineFactory.getInstance().getThreadPageContext();
747                        if(pc==null)throw new RuntimeException("cannot get pid for current thread");
748                }
749                return pc.getId();
750        }
751
752        @Override
753        public Object get(Collection.Key key, Object defaultValue) {
754                return getAt(key,
755                                arrCurrentRow.get(getPid(), 1),
756                                defaultValue);
757        }
758
759        @Override
760        public Object get(String key) throws PageException {
761                return getAt(key,
762                                arrCurrentRow.get(getPid(), 1)
763                                );
764        }
765
766        @Override
767        public Object get(Collection.Key key) throws PageException {
768                return getAt(key,
769                                arrCurrentRow.get(getPid(), 1)
770                                );
771        }
772
773        @Override
774        public Object getAt(String key, int row, Object defaultValue) {
775                return getAt(KeyImpl.init(key), row, defaultValue);
776        }
777
778        public Object getAt(Collection.Key key, int row, Object defaultValue) {
779                int index=getIndexFromKey(key);
780                if(index!=-1) {
781                        return columns[index].get(row,defaultValue);
782                }
783                if(key.length()>=10) {
784                if(key.equals(KeyConstants._RECORDCOUNT)) return new Double(getRecordcount());
785                if(key.equals(KeyConstants._CURRENTROW)) return new Double(row);
786                if(key.equals(KeyConstants._COLUMNLIST)) return getColumnlist(true);
787                }
788        return defaultValue;
789        }
790        
791        @Override
792        public Object getAt(String key, int row) throws PageException {
793                return getAt(KeyImpl.init(key), row);
794        }
795
796        @Override
797        public Object getAt(Collection.Key key, int row) throws PageException {
798                int index=getIndexFromKey(key);
799                if(index!=-1) {
800                        return columns[index].get(row, NullSupportHelper.empty());
801                }
802                if(key.length()>=10) {
803                if(key.equals(KeyConstants._RECORDCOUNT)) return new Double(getRecordcount());
804                if(key.equals(KeyConstants._CURRENTROW)) return new Double(row);
805                        if(key.equals(KeyConstants._COLUMNLIST)) return getColumnlist(true);
806        }
807                throw new DatabaseException("column ["+key+"] not found in query, columns are ["+getColumnlist(false)+"]",null,sql,null);
808        }
809        
810
811    @Override
812    public synchronized int removeRow(int row) throws PageException {
813        //disconnectCache();
814        
815        for(int i=0;i<columns.length;i++) {
816            columns[i].removeRow(row);
817        }
818        return --recordcount;
819    }
820
821    @Override
822    public int removeRowEL(int row) {
823        //disconnectCache();
824        
825        try {
826            return removeRow(row);
827        } catch (PageException e) {
828            return recordcount;
829        }
830    }
831    
832    @Override
833    public QueryColumn removeColumn(String key) throws DatabaseException {
834        return removeColumn(KeyImpl.init(key));
835    }
836
837        @Override
838        public QueryColumn removeColumn(Collection.Key key) throws DatabaseException {
839                //disconnectCache();
840        
841        QueryColumn removed = removeColumnEL(key);
842        if(removed==null) {
843            if(key.equals(KeyConstants._RECORDCOUNT) || 
844                        key.equals(KeyConstants._CURRENTROW) || 
845                        key.equals(KeyConstants._COLUMNLIST))
846                throw new DatabaseException("can't remove "+key+" this is not a row","existing rows are ["+getColumnlist(false)+"]",null,null);
847            throw new DatabaseException("can't remove row ["+key+"], this row doesn't exist",
848                    "existing rows are ["+getColumnlist(false)+"]",null,null);
849        }
850        return removed;
851        }
852
853    @Override
854    public synchronized QueryColumn removeColumnEL(String key) {
855        return removeColumnEL(KeyImpl.init(key));
856    }
857
858        public QueryColumn removeColumnEL(Collection.Key key) {
859                //disconnectCache();
860        
861        int index=getIndexFromKey(key);
862        if(index!=-1) {
863            int current=0;
864            QueryColumn removed=null;
865            Collection.Key[] newColumnNames=new Collection.Key[columnNames.length-1];
866            QueryColumnImpl[] newColumns=new QueryColumnImpl[columns.length-1];
867            for(int i=0;i<columns.length;i++) {
868                if(i==index) {
869                    removed=columns[i];
870                }
871                else {
872                    newColumnNames[current]=columnNames[i];
873                    newColumns[current++]=columns[i];
874                }
875            }
876            columnNames=newColumnNames;
877            columns=newColumns;
878            columncount--;
879            return removed;
880        }
881        return null;
882        }
883
884        @Override
885        public Object setEL(String key, Object value) { 
886                return setEL(KeyImpl.init(key),value);  
887        }
888
889        @Override
890        public Object setEL(Collection.Key key, Object value) {
891                return setAtEL(key,
892                                arrCurrentRow.get(getPid(), 1),
893                                value);
894        }
895        
896        @Override
897        public Object set(String key, Object value) throws PageException {
898                return set(KeyImpl.init(key),value);
899        }
900
901        @Override
902        public Object set(Collection.Key key, Object value) throws PageException {
903                return setAt(key,
904                                arrCurrentRow.get(getPid(), 1),
905                                value);
906        }
907    
908    @Override
909    public Object setAt(String key,int row, Object value) throws PageException {
910        return setAt(KeyImpl.init(key), row, value);
911    }
912
913    public Object setAt(Collection.Key key, int row, Object value) throws PageException {
914                int index=getIndexFromKey(key);
915        if(index!=-1) {
916            return columns[index].set(row,value);
917        }
918        throw new DatabaseException("column ["+key+"] does not exist","columns are ["+getColumnlist(false)+"]",sql,null);
919        }
920    
921    @Override
922    public Object setAtEL(String key,int row, Object value) {
923        return setAtEL(KeyImpl.init(key), row, value);
924    }
925
926        public Object setAtEL(Collection.Key key, int row, Object value) {
927                int index=getIndexFromKey(key);
928        if(index!=-1) {
929            return columns[index].setEL(row,value);
930        }
931        return null;
932        }
933
934        @Override
935        public boolean next() {
936                return next(getPid());
937        }
938
939        @Override
940        public boolean next(int pid) {
941                if(recordcount>=(arrCurrentRow.set(pid,arrCurrentRow.get(pid,0)+1))) {
942                        return true;
943                }
944                arrCurrentRow.set(pid,0);
945                return false;
946        }
947
948        @Override
949        public void reset() {
950                reset(getPid());
951        }
952        
953        public void reset(int pid) {
954                arrCurrentRow.set(pid,0);
955        }
956
957        @Override
958        public int getRecordcount() {
959                return recordcount;
960        }
961        
962        @Override
963        public int getCurrentrow(int pid) {
964                return arrCurrentRow.get(pid,1);
965        }
966
967
968        /**
969         * return a string list of all columns
970         * @return string list
971         */
972        public String getColumnlist(boolean upperCase) {
973                //if(upperCase)upperCase=((ConfigImpl)ThreadLocalPageContext.getConfig()).getDotNotationUpperCase();
974                StringBuffer sb=new StringBuffer();
975                for(int i=0;i<columnNames.length;i++) {
976                        if(i>0)sb.append(',');
977                        sb.append(upperCase?columnNames[i].getUpperString():columnNames[i].getString());
978                }
979                return sb.toString();
980        }
981        public String getColumnlist() {
982                return getColumnlist(true);
983        }
984
985        public boolean go(int index) {
986                return go(index,getPid());
987        }
988        
989        public boolean go(int index, int pid) {
990                if(index>0 && index<=recordcount) {
991                        arrCurrentRow.set(pid, index);
992                        return true;
993                }
994                arrCurrentRow.set(pid, 0);
995                return false;
996        }
997
998        @Override
999        public boolean isEmpty() {
1000                return recordcount+columncount==0;
1001        }
1002
1003        @Override
1004        public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) {
1005                return QueryUtil.toDumpData(this, pageContext, maxlevel, dp);
1006        }
1007        
1008        /**
1009         * sorts a query by a column
1010         * @param column colun to sort
1011         * @throws PageException
1012         */
1013        public void sort(String column) throws PageException {
1014                sort(column,Query.ORDER_ASC);
1015        }
1016
1017        @Override
1018        public void sort(Collection.Key column) throws PageException {
1019                sort(column,Query.ORDER_ASC);
1020        }
1021
1022        /**
1023         * sorts a query by a column 
1024         * @param strColumn column to sort
1025         * @param order sort type (Query.ORDER_ASC or Query.ORDER_DESC)
1026         * @throws PageException
1027         */
1028        public synchronized void sort(String strColumn, int order) throws PageException {
1029                //disconnectCache();
1030        sort(getColumn(strColumn),order);
1031        }
1032        
1033        @Override
1034        public synchronized void sort(Collection.Key keyColumn, int order) throws PageException {
1035                //disconnectCache();
1036        sort(getColumn(keyColumn),order);
1037        }
1038        
1039        private void sort(QueryColumn column, int order) throws PageException {
1040        int type = column.getType();
1041                
1042                SortRegister[] arr= ArrayUtil.toSortRegisterArray(column);
1043                
1044                Arrays.sort(arr,
1045                (
1046                                type==Types.BIGINT || 
1047                                type==Types.BIT || 
1048                                type==Types.INTEGER || 
1049                                type==Types.SMALLINT || 
1050                                type==Types.TINYINT     ||
1051                        type==Types.DECIMAL || 
1052                        type==Types.DOUBLE || 
1053                        type==Types.NUMERIC || 
1054                        type==Types.REAL)?
1055                
1056                (Comparator)new NumberSortRegisterComparator(order==ORDER_ASC):(Comparator)new SortRegisterComparator(null,order==ORDER_ASC,true,true)
1057                );
1058                
1059                for(int i=0;i<columns.length;i++) {
1060                        column=columns[i];
1061                        int len=column.size();
1062                        QueryColumnImpl newCol=new QueryColumnImpl(this,columnNames[i],columns[i].getType(),len);
1063                        for(int y=1;y<=len;y++) {
1064                                newCol.set(y,column.get(arr[y-1].getOldPosition()+1,null));
1065                        }
1066                        columns[i]=newCol;
1067                }
1068        }
1069
1070        @Override
1071         public synchronized boolean addRow(int count) {                
1072         //disconnectCache();
1073            
1074         for(int i=0;i<columns.length;i++) {
1075                 QueryColumnPro column = columns[i];
1076                        column.addRow(count);
1077                 }
1078                 recordcount+=count;
1079                 return true;
1080         }
1081         
1082         @Override
1083        public boolean addColumn(String columnName, Array content) throws DatabaseException {
1084                return addColumn(columnName,content,Types.OTHER);
1085         }
1086
1087        public boolean addColumn(Collection.Key columnName, Array content) throws PageException {
1088                return addColumn(columnName,content,Types.OTHER);
1089        }
1090        
1091    @Override
1092    public synchronized boolean addColumn(String columnName, Array content, int type) throws DatabaseException {
1093                return addColumn(KeyImpl.init(columnName.trim()), content, type);
1094    }
1095
1096        @Override
1097        public boolean addColumn(Collection.Key columnName, Array content, int type) throws DatabaseException {
1098                //disconnectCache();
1099        // TODO Meta type
1100                content=(Array) Duplicator.duplicate(content,false);
1101                
1102                if(getIndexFromKey(columnName)!=-1)
1103                        throw new DatabaseException("column name ["+columnName.getString()+"] already exist",null,sql,null);
1104                if(content.size()!=getRecordcount()) {
1105                        //throw new DatabaseException("array for the new column has not the same size like the query (arrayLen!=query.recordcount)");
1106                        if(content.size()>getRecordcount()) addRow(content.size()-getRecordcount());
1107                        else content.setEL(getRecordcount(),"");
1108                }
1109                QueryColumnImpl[] newColumns=new QueryColumnImpl[columns.length+1];
1110                Collection.Key[] newColumnNames=new Collection.Key[columns.length+1];
1111                boolean logUsage=false;
1112                for(int i=0;i<columns.length;i++) {
1113                        newColumns[i]=columns[i];
1114                        newColumnNames[i]=columnNames[i];
1115                        if(!logUsage && columns[i] instanceof DebugQueryColumn) logUsage=true;
1116                }
1117                newColumns[columns.length]=new QueryColumnImpl(this,columnName,content,type);
1118                newColumnNames[columns.length]=columnName;
1119                columns=newColumns;
1120                columnNames=newColumnNames;
1121                
1122                columncount++;
1123                
1124                if(logUsage)enableShowQueryUsage();
1125                
1126                return true;
1127        }
1128        
1129
1130        /* *
1131         * if this query is still connected with cache (same query also in cache)
1132     * it will disconnetd from cache (clone object and add clone to cache)
1133         */
1134        //protected void disconnectCache() {}
1135
1136        
1137        @Override
1138    public Object clone() {
1139        return cloneQuery(true);
1140    }
1141    
1142    @Override
1143    public Collection duplicate(boolean deepCopy) {
1144        return cloneQuery(true);
1145    }
1146    
1147
1148        
1149    
1150    /**
1151     * @return clones the query object
1152     */
1153    public QueryImpl cloneQuery(boolean deepCopy) {
1154        QueryImpl newResult=new QueryImpl();
1155        boolean inside=ThreadLocalDuplication.set(this, newResult);
1156        try{
1157                if(columnNames!=null){
1158                        newResult.columnNames=new Collection.Key[columnNames.length];
1159                        newResult.columns=new QueryColumnImpl[columnNames.length];
1160                        for(int i=0;i<columnNames.length;i++) {
1161                                newResult.columnNames[i]=columnNames[i];
1162                                newResult.columns[i]=columns[i].cloneColumnImpl(deepCopy);
1163                        }
1164                }
1165                newResult.sql=sql;
1166                newResult.template=template;
1167                newResult.recordcount=recordcount;
1168                newResult.columncount=columncount;
1169                newResult.cacheType=cacheType;
1170                newResult.name=name;
1171                newResult.exeTime=exeTime;
1172                newResult.updateCount=updateCount;
1173                if(generatedKeys!=null)newResult.generatedKeys=generatedKeys.cloneQuery(false);
1174                return newResult;
1175        }
1176        finally {
1177                if(!inside)ThreadLocalDuplication.reset();
1178        }
1179    }
1180
1181        @Override
1182        public synchronized int[] getTypes() {
1183                int[] types=new int[columns.length];
1184                for(int i=0;i<columns.length;i++) {
1185                    types[i]=columns[i].getType();
1186                }
1187                return types;
1188        }
1189        
1190        @Override
1191        public synchronized Map<Collection.Key,String> getTypesAsMap() {
1192                
1193                Map<Collection.Key,String> map=new HashMap<Collection.Key,String>();
1194                for(int i=0;i<columns.length;i++) {
1195                        map.put(columnNames[i],columns[i].getTypeAsString());
1196                }
1197                return map;
1198        }
1199
1200        @Override
1201        public QueryColumn getColumn(String key) throws DatabaseException {
1202                return getColumn(KeyImpl.init(key.trim()));
1203        }
1204
1205        @Override
1206        public QueryColumn getColumn(Collection.Key key) throws DatabaseException {
1207                int index=getIndexFromKey(key);
1208                if(index!=-1) return columns[index];
1209        
1210                if(key.length()>=10) {
1211                if(key.equals(KeyConstants._RECORDCOUNT)) return new QueryColumnRef(this,key,Types.INTEGER);
1212                if(key.equals(KeyConstants._CURRENTROW)) return new QueryColumnRef(this,key,Types.INTEGER);
1213                if(key.equals(KeyConstants._COLUMNLIST)) return new QueryColumnRef(this,key,Types.INTEGER);
1214                }
1215        throw new DatabaseException("key ["+key.getString()+"] not found in query, columns are ["+getColumnlist(false)+"]",null,sql,null);
1216        }
1217        
1218
1219        private void renameEL(Collection.Key src, Collection.Key trg) {
1220                int index=getIndexFromKey(src);
1221                if(index!=-1){
1222                        columnNames[index]=trg;
1223                        columns[index].setKey(trg);
1224                }
1225        }
1226        
1227        public synchronized void rename(Collection.Key columnName,Collection.Key newColumnName) throws ExpressionException {
1228                int index=getIndexFromKey(columnName);
1229                if(index==-1) throw new ExpressionException("invalid column name definitions");
1230                columnNames[index]=newColumnName;
1231                columns[index].setKey(newColumnName);
1232        }
1233        
1234
1235        @Override
1236        public QueryColumn getColumn(String key, QueryColumn defaultValue) {
1237                return getColumn(KeyImpl.init(key.trim()),defaultValue);
1238        }
1239
1240        @Override
1241        public QueryColumn getColumn(Collection.Key key, QueryColumn defaultValue) {
1242        int index=getIndexFromKey(key);
1243                if(index!=-1) return columns[index];
1244        if(key.length()>=10) {
1245                if(key.equals(KeyConstants._RECORDCOUNT)) return new QueryColumnRef(this,key,Types.INTEGER);
1246                if(key.equals(KeyConstants._CURRENTROW)) return new QueryColumnRef(this,key,Types.INTEGER);
1247                if(key.equals(KeyConstants._COLUMNLIST)) return new QueryColumnRef(this,key,Types.INTEGER);
1248        }
1249        return defaultValue;
1250        }
1251        
1252        @Override
1253        public String toString() {
1254                Collection.Key[] keys=keys();
1255                
1256                StringBuffer sb=new StringBuffer();
1257
1258                sb.append("Query\n");
1259                sb.append("---------------------------------------------------\n");
1260                
1261                if(sql!=null) {
1262                        sb.append(sql+"\n");
1263                        sb.append("---------------------------------------------------\n");
1264                }
1265
1266                if(exeTime>0)        {
1267                        sb.append("Execution Time (ns): "+exeTime+"\n");
1268                        sb.append("---------------------------------------------------\n");
1269                }
1270                
1271                sb.append("Recordcount: "+getRecordcount()+"\n");
1272                sb.append("---------------------------------------------------\n");
1273                String trenner="";
1274                for(int i=0;i<keys.length;i++) {
1275                        trenner+="+---------------------";
1276                }
1277                trenner+="+\n";
1278                sb.append(trenner);
1279        
1280        // Header
1281                for(int i=0;i<keys.length;i++) {
1282                        sb.append(getToStringField(keys[i].getString()));
1283                }
1284                sb.append("|\n");
1285                sb.append(trenner);
1286                sb.append(trenner);
1287                
1288        // body
1289                for(int i=0;i<recordcount;i++) {
1290                        for(int y=0;y<keys.length;y++) {
1291                                try {
1292                                        Object o=getAt(keys[y],i+1);
1293                                        if(o instanceof String)sb.append(getToStringField(o.toString()));
1294                                        else if(o instanceof Number) sb.append(getToStringField(Caster.toString(((Number)o))));
1295                                        else if(o instanceof Clob) sb.append(getToStringField(Caster.toString(o)));                                                     
1296                                        else sb.append(getToStringField(o.toString()));
1297                                } catch (PageException e) {
1298                                        sb.append(getToStringField("[empty]"));
1299                                }
1300                        }
1301                        sb.append("|\n");
1302                        sb.append(trenner);
1303                }
1304                return sb.toString();
1305        }
1306
1307        private String getToStringField(String str) {
1308                if(str==null) return "|                    ";
1309                else if(str.length()<21) {
1310                        String s="|"+str;
1311                        for(int i=str.length();i<21;i++)s+=" ";
1312                        return s;
1313                }
1314                else if(str.length()==21) return "|"+str;
1315                else  return "|"+str.substring(0,18)+"...";
1316        }
1317        
1318        /**
1319         * 
1320         * @param type
1321         * @return return String represetation of a Type from int type
1322         */
1323        public static String getColumTypeName(int type) {
1324                switch(type) {
1325                        case Types.ARRAY: return "OBJECT";
1326                        case Types.BIGINT: return "BIGINT";
1327                        case Types.BINARY: return "BINARY";
1328                        case Types.BIT: return "BIT";
1329                        case Types.BLOB: return "OBJECT";
1330                        case Types.BOOLEAN: return "BOOLEAN";
1331                        case Types.CHAR: return "CHAR";
1332                        case Types.NCHAR: return "NCHAR";
1333                        case Types.CLOB: return "OBJECT";
1334                        case Types.NCLOB: return "OBJECT";
1335                        case Types.DATALINK: return "OBJECT";
1336                        case Types.DATE: return "DATE";
1337                        case Types.DECIMAL: return "DECIMAL";
1338                        case Types.DISTINCT: return "OBJECT";
1339                        case Types.DOUBLE: return "DOUBLE";
1340                        case Types.FLOAT: return "DOUBLE";
1341                        case Types.INTEGER: return "INTEGER";
1342                        case Types.JAVA_OBJECT: return "OBJECT";
1343                        case Types.LONGVARBINARY: return "LONGVARBINARY";
1344                        case Types.LONGVARCHAR: return "LONGVARCHAR";
1345                        case Types.NULL: return "OBJECT";
1346                        case Types.NUMERIC: return "NUMERIC";
1347                        case Types.OTHER: return "OBJECT";
1348                        case Types.REAL: return "REAL";
1349                        case Types.REF: return "OBJECT";
1350                        case Types.SMALLINT: return "SMALLINT";
1351                        case Types.STRUCT: return "OBJECT";
1352                        case Types.TIME: return "TIME";
1353                        case Types.TIMESTAMP: return "TIMESTAMP";
1354                        case Types.TINYINT: return "TINYINT";
1355                        case Types.VARBINARY: return "VARBINARY";
1356                        case Types.NVARCHAR: return "NVARCHAR";
1357                        case Types.SQLXML: return "SQLXML";
1358                        case Types.VARCHAR: return "VARCHAR";
1359                        default : return "VARCHAR";
1360                }
1361        }
1362
1363        private int getIndexFromKey(String key) {
1364                String lc = StringUtil.toLowerCase(key);
1365                for(int i=0;i<columnNames.length;i++) {
1366                        if(columnNames[i].getLowerString().equals(lc)) return i;
1367                }
1368                return -1;
1369        }
1370        
1371        private int getIndexFromKey(Collection.Key key) {
1372                
1373                for(int i=0;i<columnNames.length;i++) {
1374                        if(columnNames[i].equalsIgnoreCase(key)) return i;
1375                }
1376                return -1;
1377        }
1378        
1379        @Override
1380        public void setExecutionTime(long exeTime) {
1381                this.exeTime=exeTime;
1382        }
1383
1384    /**
1385     * @param maxrows
1386     * @return has cutted or not
1387     */
1388    public synchronized boolean cutRowsTo(int maxrows) {
1389        //disconnectCache();
1390        
1391        if(maxrows>-1 && maxrows<getRecordcount()) {
1392                         for(int i=0;i<columns.length;i++) {
1393                                QueryColumn column = columns[i];
1394                                column.cutRowsTo(maxrows);
1395                         }
1396                         recordcount=maxrows;
1397                         return true;
1398        }
1399        return false;
1400    }
1401
1402    public void setCacheType(String cacheType) {
1403        this.cacheType=cacheType; 
1404    }
1405
1406    public String getCacheType() {
1407        return cacheType; 
1408    }
1409
1410    @Override
1411    public void setCached(boolean isCached) {
1412        throw new RuntimeException("method no longer supported"); 
1413    }
1414
1415    @Override
1416    public boolean isCached() {
1417        return cacheType!=null;
1418    }
1419
1420
1421
1422    @Override
1423    public int addRow() {
1424                addRow(1);
1425                return getRecordcount();
1426    }
1427
1428
1429    public Key getColumnName(int columnIndex) {
1430        Key[] keys = keys();
1431                if(columnIndex<1 || columnIndex>keys.length) return null;
1432                return keys[columnIndex-1];
1433    }
1434
1435    @Override
1436    public int getColumnIndex(String coulmnName) {
1437        Collection.Key[] keys = keys();
1438                for(int i=0;i<keys.length;i++) {
1439                        if(keys[i].getString().equalsIgnoreCase(coulmnName)) return i+1;
1440                }
1441                return -1;
1442    }
1443
1444
1445
1446    @Override
1447    public String[] getColumns() {
1448        return getColumnNamesAsString();
1449    }
1450    
1451    @Override
1452    public Collection.Key[] getColumnNames() {
1453        Collection.Key[] keys = keys();
1454        Collection.Key[] rtn=new Collection.Key[keys.length];
1455                System.arraycopy(keys,0,rtn,0,keys.length);
1456                return rtn;
1457    }
1458    
1459    public void setColumnNames(Collection.Key[] trg) throws PageException {
1460        columncount=trg.length;
1461        Collection.Key[] src = keys();
1462        
1463        // target < source
1464        if(trg.length<src.length){
1465                this.columnNames=new Collection.Key[trg.length];
1466                QueryColumnImpl[] tmp=new QueryColumnImpl[trg.length];
1467                for(int i=0;i<trg.length;i++){
1468                        this.columnNames[i]=trg[i];
1469                        tmp[i]=this.columns[i];
1470                        tmp[i].setKey(trg[i]);
1471                }
1472                this.columns=tmp;
1473                return;
1474        }
1475        
1476        if(trg.length>src.length){
1477                int recordcount=getRecordcount();
1478                for(int i=src.length;i<trg.length;i++){
1479                        
1480                        Array arr=new ArrayImpl();
1481                        for(int r=1;r<=recordcount;r++){
1482                                arr.setE(i,"");
1483                        }
1484                        addColumn(trg[i], arr);
1485                }
1486                src = keys();
1487        }
1488        
1489                for(int i=0;i<trg.length;i++){
1490                        this.columnNames[i]=trg[i];
1491                        this.columns[i].setKey(trg[i]);
1492        }
1493    }
1494    
1495
1496
1497        @Override
1498        public String[] getColumnNamesAsString() {
1499                return CollectionUtil.keysAsString(this);
1500        }
1501        
1502        public int getColumnCount() {
1503                return columncount;
1504        }
1505
1506    @Override
1507    public String getData(int row, int col) throws IndexOutOfBoundsException {
1508        Collection.Key[] keys = keys();
1509                if(col<1 || col>keys.length) {
1510                        new IndexOutOfBoundsException("invalid column index to retrieve Data from query, valid index goes from 1 to "+keys.length);
1511                }
1512                
1513                Object o=getAt(keys[col-1],row,NullSupportHelper.NULL());
1514                if(o==NullSupportHelper.NULL())
1515                        throw new IndexOutOfBoundsException("invalid row index to retrieve Data from query, valid index goes from 1 to "+getRecordcount());
1516                return Caster.toString( o,NullSupportHelper.full()?null:"");
1517    }
1518
1519
1520
1521    @Override
1522    public String getName() {
1523        return this.name;
1524    }
1525
1526
1527
1528    @Override
1529    public int getRowCount() {
1530        return getRecordcount();
1531    }
1532
1533
1534
1535    @Override
1536    public void setData(int row, int col, String value) throws IndexOutOfBoundsException {
1537        Collection.Key[] keys = keys();
1538                if(col<1 || col>keys.length) {
1539                        new IndexOutOfBoundsException("invalid column index to retrieve Data from query, valid index goes from 1 to "+keys.length);
1540                }
1541                try {
1542                        setAt(keys[col-1],row,value);
1543                } 
1544                catch (PageException e) {
1545                        throw new IndexOutOfBoundsException("invalid row index to retrieve Data from query, valid index goes from 1 to "+getRecordcount());
1546                }
1547    }
1548
1549    @Override
1550    public boolean containsKey(String key) {
1551        return getColumn(key,null)!=null;
1552    }   
1553
1554        @Override
1555        public boolean containsKey(Collection.Key key) {
1556        return getColumn(key,null)!=null;
1557        }
1558        
1559    @Override
1560    public String castToString() throws ExpressionException {
1561        throw new ExpressionException("Can't cast Complex Object Type Query to String",
1562          "Use Built-In-Function \"serialize(Query):String\" to create a String from Query");
1563    }
1564
1565        @Override
1566        public String castToString(String defaultValue) {
1567                return defaultValue;
1568        }
1569
1570
1571    @Override
1572    public boolean castToBooleanValue() throws ExpressionException {
1573        throw new ExpressionException("Can't cast Complex Object Type Query to a boolean value");
1574    }
1575    
1576    @Override
1577    public Boolean castToBoolean(Boolean defaultValue) {
1578        return defaultValue;
1579    }
1580
1581
1582    @Override
1583    public double castToDoubleValue() throws ExpressionException {
1584        throw new ExpressionException("Can't cast Complex Object Type Query to a number value");
1585    }
1586    
1587    @Override
1588    public double castToDoubleValue(double defaultValue) {
1589        return defaultValue;
1590    }
1591
1592
1593    @Override
1594    public DateTime castToDateTime() throws ExpressionException {
1595        throw new ExpressionException("Can't cast Complex Object Type Query to a Date");
1596    }
1597    
1598    @Override
1599    public DateTime castToDateTime(DateTime defaultValue) {
1600        return defaultValue;
1601    }
1602
1603        @Override
1604        public int compareTo(boolean b) throws ExpressionException {
1605                throw new ExpressionException("can't compare Complex Object Type Query with a boolean value");
1606        }
1607
1608        @Override
1609        public int compareTo(DateTime dt) throws PageException {
1610                throw new ExpressionException("can't compare Complex Object Type Query with a DateTime Object");
1611        }
1612
1613        @Override
1614        public int compareTo(double d) throws PageException {
1615                throw new ExpressionException("can't compare Complex Object Type Query with a numeric value");
1616        }
1617
1618        @Override
1619        public int compareTo(String str) throws PageException {
1620                throw new ExpressionException("can't compare Complex Object Type Query with a String");
1621        }
1622
1623    public synchronized Array getMetaDataSimple() {
1624        Array cols=new ArrayImpl();
1625        Struct column;
1626        for(int i=0;i<columns.length;i++) {
1627                column=new StructImpl();
1628                column.setEL(KeyConstants._name,columnNames[i].getString());
1629                column.setEL("isCaseSensitive",Boolean.FALSE);
1630                column.setEL("typeName",columns[i].getTypeAsString());
1631                cols.appendEL(column);
1632        }
1633        return cols;
1634    }
1635
1636        /**
1637         * @return the sql
1638         */
1639        public SQL getSql() {
1640                return sql;
1641        }
1642
1643        /**
1644         * @param sql the sql to set
1645         */
1646        public void setSql(SQL sql) {
1647                this.sql = sql;
1648        }
1649
1650
1651        @Override
1652        public Object getObject(String columnName) throws SQLException {
1653                int currentrow;
1654                if((currentrow=arrCurrentRow.get(getPid(),0))==0) return null;
1655                return getAt(columnName,currentrow,null);
1656        }
1657        
1658        @Override
1659        public Object getObject(int columnIndex) throws SQLException {
1660                if(columnIndex>0 && columnIndex<=columncount) return  getObject(this.columnNames[columnIndex-1].getString());
1661                return null;
1662        }
1663        
1664        @Override
1665        public String getString(int columnIndex) throws SQLException {
1666                Object rtn = getObject(columnIndex);
1667                if(rtn==null)return null;
1668                if(Decision.isCastableToString(rtn)) return Caster.toString(rtn,null);
1669                throw new SQLException("can't cast value to string");
1670        }
1671        
1672        @Override
1673        public String getString(String columnName) throws SQLException {
1674                Object rtn = getObject(columnName);
1675                if(rtn==null)return null;
1676                if(Decision.isCastableToString(rtn)) return Caster.toString(rtn,null);
1677                throw new SQLException("can't cast value to string");
1678        }
1679        
1680        @Override
1681        public boolean getBoolean(int columnIndex) throws SQLException {
1682                Object rtn = getObject(columnIndex);
1683                if(rtn==null)return false;
1684                if(Decision.isCastableToBoolean(rtn)) return Caster.toBooleanValue(rtn,false);
1685                throw new SQLException("can't cast value to boolean");
1686        }
1687        
1688        @Override
1689        public boolean getBoolean(String columnName) throws SQLException {
1690                Object rtn = getObject(columnName);
1691                if(rtn==null)return false;
1692                if(Decision.isCastableToBoolean(rtn)) return Caster.toBooleanValue(rtn,false);
1693                throw new SQLException("can't cast value to boolean");
1694        }
1695        
1696        
1697        // ---------------------------------------
1698
1699        @Override
1700        public Object call(PageContext pc, Key methodName, Object[] arguments) throws PageException {
1701                return MemberUtil.call(pc, this, methodName, arguments, lucee.commons.lang.CFTypes.TYPE_QUERY, "query");
1702                //return Reflector.callMethod(this,methodName,arguments);
1703        }
1704
1705        @Override
1706        public Object callWithNamedValues(PageContext pc, Key methodName, Struct args) throws PageException {   
1707                return MemberUtil.callWithNamedValues(pc, this, methodName, args,lucee.commons.lang.CFTypes.TYPE_QUERY, "query");
1708        }
1709
1710        @Override
1711        public Object get(PageContext pc, Key key, Object defaultValue) {
1712                return getAt(key,arrCurrentRow.get(
1713                                pc.getId(),1),defaultValue);
1714        }
1715
1716        @Override
1717        public Object get(PageContext pc, Key key) throws PageException {
1718                return getAt(key,arrCurrentRow.get(pc.getId(),1));
1719        }
1720
1721        public boolean isInitalized() {
1722                return true;
1723        }
1724
1725        @Override
1726        public Object set(PageContext pc, Key propertyName, Object value) throws PageException {
1727                return setAt(propertyName,arrCurrentRow.get(pc.getId(),1),value);
1728        }
1729
1730        @Override
1731        public Object setEL(PageContext pc, Key propertyName, Object value) {
1732                return setAtEL(propertyName,arrCurrentRow.get(pc.getId(),1),value);
1733        }
1734        
1735        @Override
1736        public boolean wasNull() {
1737                throw new PageRuntimeException(new ApplicationException("method [wasNull] is not supported"));
1738        }
1739
1740        @Override
1741        public boolean absolute(int row) throws SQLException {
1742                if(recordcount==0) {
1743                        if(row!=0) throw new SQLException("invalid row ["+row+"], query is Empty");
1744                        return false;
1745                }
1746                //row=row%recordcount;
1747                
1748                if(row>0) arrCurrentRow.set(getPid(),row);
1749                else arrCurrentRow.set(getPid(),(recordcount+1)+row);
1750                return true;
1751        }
1752
1753        @Override
1754        public void afterLast() throws SQLException {
1755                arrCurrentRow.set(getPid(),recordcount+1);
1756        }
1757
1758        @Override
1759        public void beforeFirst() throws SQLException {
1760                arrCurrentRow.set(getPid(),0);
1761        }
1762
1763        @Override
1764        public void cancelRowUpdates() throws SQLException {
1765                // ignored
1766        }
1767
1768        @Override
1769        public void clearWarnings() throws SQLException {
1770                // ignored
1771        }
1772
1773        @Override
1774        public void close() throws SQLException {
1775                // ignored
1776        }
1777
1778        @Override
1779        public void deleteRow() throws SQLException {
1780                try {
1781                        removeRow(arrCurrentRow.get(getPid()));
1782                } catch (Exception e) {
1783                        throw new SQLException(e.getMessage());
1784                }
1785        }
1786
1787        @Override
1788        public int findColumn(String columnName) throws SQLException {
1789                int index= getColumnIndex(columnName);
1790                if(index==-1) throw new SQLException("invald column definitions ["+columnName+"]");
1791                return index;
1792        }
1793
1794        @Override
1795        public boolean first() throws SQLException {
1796                return absolute(1);
1797        }
1798
1799        public java.sql.Array getArray(int i) throws SQLException {
1800                throw new SQLException("method is not implemented");
1801        }
1802
1803        public java.sql.Array getArray(String colName) throws SQLException {
1804                throw new SQLException("method is not implemented");
1805        }
1806
1807        @Override
1808        public InputStream getAsciiStream(int columnIndex) throws SQLException {
1809                String res = getString(columnIndex);
1810                if(res==null)return null;
1811                return new ByteArrayInputStream(res.getBytes());
1812        }
1813
1814        @Override
1815        public InputStream getAsciiStream(String columnName) throws SQLException {
1816                String res = getString(columnName);
1817                if(res==null)return null;
1818                return new ByteArrayInputStream(res.getBytes());
1819        }
1820
1821        @Override
1822        public BigDecimal getBigDecimal(int columnIndex) throws SQLException {
1823                return new BigDecimal(getDouble(columnIndex));
1824        }
1825
1826        @Override
1827        public BigDecimal getBigDecimal(String columnName) throws SQLException {
1828                return new BigDecimal(getDouble(columnName));
1829        }
1830
1831        @Override
1832        public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
1833                return new BigDecimal(getDouble(columnIndex));
1834        }
1835
1836        @Override
1837        public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
1838                return new BigDecimal(getDouble(columnName));
1839        }
1840
1841        @Override
1842        public InputStream getBinaryStream(int columnIndex) throws SQLException {
1843                Object obj = getObject(columnIndex);
1844                if(obj==null)return null;
1845                try {
1846                        return Caster.toInputStream(obj,(Charset)null);
1847                } catch (Exception e) {
1848                        throw new SQLException(e.getMessage());
1849                }
1850        }
1851
1852        @Override
1853        public InputStream getBinaryStream(String columnName) throws SQLException {
1854                Object obj = getObject(columnName);
1855                if(obj==null)return null;
1856                try {
1857                        return Caster.toInputStream(obj,(Charset)null);
1858                } catch (Exception e) {
1859                        throw new SQLException(e.getMessage());
1860                }
1861        }
1862
1863        @Override
1864        public Blob getBlob(int i) throws SQLException {
1865                byte[] bytes = getBytes(i);
1866                if(bytes==null) return null;
1867                try {
1868                        return BlobImpl.toBlob(bytes);
1869                } 
1870                catch (PageException e) {
1871                        throw new SQLException(e.getMessage());
1872                }
1873        }
1874
1875        @Override
1876        public Blob getBlob(String colName) throws SQLException {
1877                byte[] bytes = getBytes(colName);
1878                if(bytes==null) return null;
1879                try {
1880                        return BlobImpl.toBlob(bytes);
1881                } catch (PageException e) {
1882                        throw new SQLException(e.getMessage());
1883                }
1884        }
1885
1886        @Override
1887        public byte getByte(int columnIndex) throws SQLException {
1888                Object obj = getObject(columnIndex);
1889                if(obj==null) return (byte)0;
1890                try {
1891                        return Caster.toByteValue(obj);
1892                } catch (PageException e) {
1893                        throw new SQLException(e.getMessage());
1894                }
1895        }
1896
1897        @Override
1898        public byte getByte(String columnName) throws SQLException {
1899                Object obj = getObject(columnName);
1900                if(obj==null) return (byte)0;
1901                try {
1902                        return Caster.toByteValue(obj);
1903                } catch (PageException e) {
1904                        throw new SQLException(e.getMessage());
1905                }
1906        }
1907
1908        @Override
1909        public byte[] getBytes(int columnIndex) throws SQLException {
1910                Object obj = getObject(columnIndex);
1911                if(obj==null) return null;
1912                try {
1913                        return Caster.toBytes(obj,(Charset)null);
1914                } catch (Exception e) {
1915                        throw new SQLException(e.getMessage());
1916                }
1917        }
1918
1919        @Override
1920        public byte[] getBytes(String columnName) throws SQLException {
1921                Object obj = getObject(columnName);
1922                if(obj==null) return null;
1923                try {
1924                        return Caster.toBytes(obj,(Charset)null);
1925                } catch (Exception e) {
1926                        throw new SQLException(e.getMessage());
1927                }
1928        }
1929
1930        @Override
1931        public Reader getCharacterStream(int columnIndex) throws SQLException {
1932                String str=getString(columnIndex);
1933                if(str==null) return null;
1934                return new StringReader(str);
1935        }
1936
1937        @Override
1938        public Reader getCharacterStream(String columnName) throws SQLException {
1939                String str=getString(columnName);
1940                if(str==null) return null;
1941                return new StringReader(str);
1942        }
1943
1944        @Override
1945        public Clob getClob(int i) throws SQLException {
1946                String str=getString(i);
1947                if(str==null) return null;
1948                return ClobImpl.toClob(str);
1949        }
1950
1951        @Override
1952        public Clob getClob(String colName) throws SQLException {
1953                String str=getString(colName);
1954                if(str==null) return null;
1955                return ClobImpl.toClob(str);
1956        }
1957
1958        @Override
1959        public int getConcurrency() throws SQLException {
1960                return 0;
1961        }
1962
1963        @Override
1964        public String getCursorName() throws SQLException {
1965                return null;
1966        }
1967
1968        @Override
1969        public java.sql.Date getDate(int columnIndex) throws SQLException {
1970                Object obj=getObject(columnIndex);
1971                if(obj==null) return null;
1972                try {
1973                        return new java.sql.Date(Caster.toDate(obj, false, null).getTime());
1974                } catch (PageException e) {
1975                        throw new SQLException(e.getMessage());
1976                }
1977        }
1978
1979        @Override
1980        public java.sql.Date getDate(String columnName) throws SQLException {
1981                Object obj=getObject(columnName);
1982                if(obj==null) return null;
1983                try {
1984                        return new java.sql.Date(Caster.toDate(obj, false, null).getTime());
1985                } catch (PageException e) {
1986                        throw new SQLException(e.getMessage());
1987                }
1988        }
1989
1990        @Override
1991        public java.sql.Date getDate(int columnIndex, Calendar cal)throws SQLException {
1992                return getDate(columnIndex); // TODO impl
1993        }
1994
1995        @Override
1996        public java.sql.Date getDate(String columnName, Calendar cal) throws SQLException {
1997                return getDate(columnName);// TODO impl
1998        }
1999
2000        @Override
2001        public double getDouble(int columnIndex) throws SQLException {
2002                Object obj=getObject(columnIndex);
2003                if(obj==null) return 0;
2004                try {
2005                        return Caster.toDoubleValue(obj);
2006                } catch (PageException e) {
2007                        throw new SQLException(e.getMessage());
2008                }
2009        }
2010
2011        @Override
2012        public double getDouble(String columnName) throws SQLException {
2013                Object obj=getObject(columnName);
2014                if(obj==null) return 0;
2015                try {
2016                        return Caster.toDoubleValue(obj);
2017                } catch (PageException e) {
2018                        throw new SQLException(e.getMessage());
2019                }
2020        }
2021
2022        @Override
2023        public int getFetchDirection() throws SQLException {
2024                return 1000;
2025        }
2026
2027        @Override
2028        public int getFetchSize() throws SQLException {
2029                return 0;
2030        }
2031
2032        @Override
2033        public float getFloat(int columnIndex) throws SQLException {
2034                Object obj=getObject(columnIndex);
2035                if(obj==null) return 0;
2036                try {
2037                        return Caster.toFloatValue(obj);
2038                } catch (PageException e) {
2039                        throw new SQLException(e.getMessage());
2040                }
2041        }
2042
2043        @Override
2044        public float getFloat(String columnName) throws SQLException {
2045                Object obj=getObject(columnName);
2046                if(obj==null) return 0;
2047                try {
2048                        return Caster.toFloatValue(obj);
2049                } catch (PageException e) {
2050                        throw new SQLException(e.getMessage());
2051                }
2052        }
2053
2054        @Override
2055        public int getInt(int columnIndex) throws SQLException {
2056                Object obj=getObject(columnIndex);
2057                if(obj==null) return 0;
2058                try {
2059                        return Caster.toIntValue(obj);
2060                } catch (PageException e) {
2061                        throw new SQLException(e.getMessage());
2062                }
2063        }
2064
2065        @Override
2066        public int getInt(String columnName) throws SQLException {
2067                Object obj=getObject(columnName);
2068                if(obj==null) return 0;
2069                try {
2070                        return Caster.toIntValue(obj);
2071                } catch (PageException e) {
2072                        throw new SQLException(e.getMessage());
2073                }
2074        }
2075
2076        @Override
2077        public long getLong(int columnIndex) throws SQLException {
2078                Object obj=getObject(columnIndex);
2079                if(obj==null) return 0;
2080                try {
2081                        return Caster.toLongValue(obj);
2082                } catch (PageException e) {
2083                        throw new SQLException(e.getMessage());
2084                }
2085        }
2086
2087        @Override
2088        public long getLong(String columnName) throws SQLException {
2089                Object obj=getObject(columnName);
2090                if(obj==null) return 0;
2091                try {
2092                        return Caster.toLongValue(obj);
2093                } catch (PageException e) {
2094                        throw new SQLException(e.getMessage());
2095                }
2096        }
2097
2098        @Override
2099        public Object getObject(int i, Map map) throws SQLException {
2100                throw new SQLException("method is not implemented");
2101        }
2102
2103        @Override
2104        public Object getObject(String colName, Map map) throws SQLException {
2105                throw new SQLException("method is not implemented");
2106        }
2107
2108        // used only with java 7, do not set @Override
2109        public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
2110                return (T) QueryUtil.getObject(this,columnIndex, type);
2111        }
2112
2113        // used only with java 7, do not set @Override
2114        public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
2115                return (T) QueryUtil.getObject(this,columnLabel, type);
2116        }
2117
2118        @Override
2119        public Ref getRef(int i) throws SQLException {
2120                throw new SQLException("method is not implemented");
2121        }
2122
2123        @Override
2124        public Ref getRef(String colName) throws SQLException {
2125                throw new SQLException("method is not implemented");
2126        }
2127
2128        @Override
2129        public int getRow() throws SQLException {
2130                return arrCurrentRow.get(getPid(),0);
2131        }
2132
2133        @Override
2134        public short getShort(int columnIndex) throws SQLException {
2135                Object obj=getObject(columnIndex);
2136                if(obj==null) return 0;
2137                try {
2138                        return Caster.toShortValue(obj);
2139                } catch (PageException e) {
2140                        throw new SQLException(e.getMessage());
2141                }
2142        }
2143
2144        @Override
2145        public short getShort(String columnName) throws SQLException {
2146                Object obj=getObject(columnName);
2147                if(obj==null) return 0;
2148                try {
2149                        return Caster.toShortValue(obj);
2150                } catch (PageException e) {
2151                        throw new SQLException(e.getMessage());
2152                }
2153        }
2154
2155        public Statement getStatement() throws SQLException {
2156                throw new SQLException("method is not implemented");
2157        }
2158
2159        @Override
2160        public Time getTime(int columnIndex) throws SQLException {
2161                Object obj=getObject(columnIndex);
2162                if(obj==null) return null;
2163                try {
2164                        return new Time(DateCaster.toTime(null, obj).getTime());
2165                } catch (PageException e) {
2166                        throw new SQLException(e.getMessage());
2167                }
2168        }
2169
2170        @Override
2171        public Time getTime(String columnName) throws SQLException {
2172                Object obj=getObject(columnName);
2173                if(obj==null) return null;
2174                try {
2175                        return new Time(DateCaster.toTime(null, obj).getTime());
2176                } catch (PageException e) {
2177                        throw new SQLException(e.getMessage());
2178                }
2179        }
2180
2181        @Override
2182        public Time getTime(int columnIndex, Calendar cal) throws SQLException {
2183                return getTime(columnIndex);// TODO impl
2184        }
2185
2186        @Override
2187        public Time getTime(String columnName, Calendar cal) throws SQLException {
2188                return getTime(columnName);// TODO impl
2189        }
2190
2191        @Override
2192        public Timestamp getTimestamp(int columnIndex) throws SQLException {
2193                Object obj=getObject(columnIndex);
2194                if(obj==null) return null;
2195                try {
2196                        return new Timestamp(DateCaster.toTime(null, obj).getTime());
2197                } catch (PageException e) {
2198                        throw new SQLException(e.getMessage());
2199                }
2200        }
2201
2202        @Override
2203        public Timestamp getTimestamp(String columnName) throws SQLException {
2204                Object obj=getObject(columnName);
2205                if(obj==null) return null;
2206                try {
2207                        return new Timestamp(DateCaster.toTime(null, obj).getTime());
2208                } catch (PageException e) {
2209                        throw new SQLException(e.getMessage());
2210                }
2211        }
2212
2213        @Override
2214        public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException {
2215                return getTimestamp(columnIndex);// TODO impl
2216        }
2217
2218        @Override
2219        public Timestamp getTimestamp(String columnName, Calendar cal) throws SQLException {
2220                return getTimestamp(columnName);// TODO impl
2221        }
2222
2223        @Override
2224        public int getType() throws SQLException {
2225                return 0;
2226        }
2227
2228        @Override
2229        public URL getURL(int columnIndex) throws SQLException {
2230                throw new SQLException("method is not implemented");
2231        }
2232
2233        @Override
2234        public URL getURL(String columnName) throws SQLException {
2235                throw new SQLException("method is not implemented");
2236        }
2237
2238        @Override
2239        public InputStream getUnicodeStream(int columnIndex) throws SQLException {
2240                String str=getString(columnIndex);
2241                if(str==null) return null;
2242                try {
2243                        return new ByteArrayInputStream(str.getBytes("UTF-8"));
2244                } 
2245                catch (UnsupportedEncodingException e) {
2246                        throw new SQLException(e.getMessage());
2247                }
2248        }
2249
2250        @Override
2251        public InputStream getUnicodeStream(String columnName) throws SQLException {
2252                String str=getString(columnName);
2253                if(str==null) return null;
2254                try {
2255                        return new ByteArrayInputStream(str.getBytes("UTF-8"));
2256                } 
2257                catch (UnsupportedEncodingException e) {
2258                        throw new SQLException(e.getMessage());
2259                }
2260        }
2261
2262        @Override
2263        public SQLWarning getWarnings() throws SQLException {
2264                throw new SQLException("method is not implemented");
2265        }
2266
2267        @Override
2268        public void insertRow() throws SQLException {
2269                throw new SQLException("method is not implemented");
2270        }
2271
2272        @Override
2273        public boolean isAfterLast() throws SQLException {
2274                return getCurrentrow(ThreadLocalPageContext.get().getId())>recordcount;
2275        }
2276
2277        @Override
2278        public boolean isBeforeFirst() throws SQLException {
2279                return arrCurrentRow.get(getPid(),0)==0;
2280        }
2281
2282        @Override
2283        public boolean isFirst() throws SQLException {
2284                return arrCurrentRow.get(getPid(),0)==1;
2285        }
2286
2287        public boolean isLast() throws SQLException {
2288                return arrCurrentRow.get(getPid(),0)==recordcount;
2289        }
2290
2291        public boolean last() throws SQLException {
2292                return absolute(recordcount);
2293        }
2294
2295        public void moveToCurrentRow() throws SQLException {
2296                // ignore
2297        }
2298
2299        public void moveToInsertRow() throws SQLException {
2300                // ignore
2301        }
2302
2303
2304        public boolean previous() {
2305                return previous(getPid());
2306        }
2307        
2308        public boolean previous(int pid) {
2309                if(0<(arrCurrentRow.set(pid,arrCurrentRow.get(pid,0)-1))) {
2310                        return true;
2311                }
2312                arrCurrentRow.set(pid,0);
2313                return false;
2314        }
2315
2316        public void refreshRow() throws SQLException {
2317                // ignore
2318                
2319        }
2320
2321        @Override
2322        public boolean relative(int rows) throws SQLException {
2323                return absolute(getRow()+rows);
2324        }
2325
2326        @Override
2327        public boolean rowDeleted() throws SQLException {
2328                return false;
2329        }
2330
2331        @Override
2332        public boolean rowInserted() throws SQLException {
2333                return false;
2334        }
2335
2336        @Override
2337        public boolean rowUpdated() throws SQLException {
2338                return false;
2339        }
2340
2341        public void setFetchDirection(int direction) throws SQLException {
2342                // ignore
2343        }
2344
2345        public void setFetchSize(int rows) throws SQLException {
2346                // ignore
2347        }
2348
2349        @Override
2350        public void updateArray(int columnIndex, java.sql.Array x)throws SQLException {
2351                updateObject(columnIndex, x.getArray());
2352        }
2353
2354        @Override
2355        public void updateArray(String columnName, java.sql.Array x)throws SQLException {
2356                updateObject(columnName, x.getArray());
2357        }
2358
2359        @Override
2360        public void updateAsciiStream(int columnIndex, InputStream x, int length)throws SQLException {
2361                updateBinaryStream(columnIndex, x, length);
2362        }
2363
2364        @Override
2365        public void updateAsciiStream(String columnName, InputStream x, int length)throws SQLException {
2366                updateBinaryStream(columnName, x, length);
2367        }
2368
2369        @Override
2370        public void updateBigDecimal(int columnIndex, BigDecimal x)throws SQLException {
2371                updateObject(columnIndex, x.toString());
2372        }
2373
2374        @Override
2375        public void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
2376                updateObject(columnName, x.toString());
2377        }
2378
2379        @Override
2380        public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException {
2381                try {
2382                        updateObject(columnIndex, IOUtil.toBytesMax(x, length));
2383                } catch (IOException e) {
2384                        throw new SQLException(e.getMessage());
2385                }
2386        }
2387
2388        @Override
2389        public void updateBinaryStream(String columnName, InputStream x, int length) throws SQLException {
2390                try {
2391                        updateObject(columnName, IOUtil.toBytesMax(x, length));
2392                } catch (IOException e) {
2393                        throw new SQLException(e.getMessage());
2394                }
2395        }
2396
2397        @Override
2398        public void updateBlob(int columnIndex, Blob x) throws SQLException {
2399                try {
2400                        updateObject(columnIndex, toBytes(x));
2401                } catch (IOException e) {
2402                        throw new SQLException(e.getMessage());
2403                }
2404        }
2405
2406        @Override
2407        public void updateBlob(String columnName, Blob x) throws SQLException {
2408                try {
2409                        updateObject(columnName, toBytes(x));
2410                } catch (IOException e) {
2411                        throw new SQLException(e.getMessage());
2412                }
2413        }
2414
2415        @Override
2416        public void updateBoolean(int columnIndex, boolean x) throws SQLException {
2417                updateObject(columnIndex, Caster.toBoolean(x));
2418        }
2419
2420        @Override
2421        public void updateBoolean(String columnName, boolean x) throws SQLException {
2422                updateObject(columnName, Caster.toBoolean(x));
2423        }
2424
2425        @Override
2426        public void updateByte(int columnIndex, byte x) throws SQLException {
2427                updateObject(columnIndex, new Byte(x));
2428        }
2429
2430        @Override
2431        public void updateByte(String columnName, byte x) throws SQLException {
2432                updateObject(columnName, new Byte(x));
2433        }
2434
2435        @Override
2436        public void updateBytes(int columnIndex, byte[] x) throws SQLException {
2437                updateObject(columnIndex, x);
2438        }
2439
2440        @Override
2441        public void updateBytes(String columnName, byte[] x) throws SQLException {
2442                updateObject(columnName, x);
2443        }
2444
2445        @Override
2446        public void updateCharacterStream(int columnIndex, Reader reader, int length)throws SQLException {
2447                try {
2448                        updateObject(columnIndex, IOUtil.toString(reader));
2449                } catch (Exception e) {
2450                        throw new SQLException(e.getMessage());
2451                }
2452        }
2453
2454        @Override
2455        public void updateCharacterStream(String columnName, Reader reader,int length) throws SQLException {
2456                try {
2457                        updateObject(columnName, IOUtil.toString(reader));
2458                } catch (Exception e) {
2459                        throw new SQLException(e.getMessage());
2460                }
2461        }
2462
2463        @Override
2464        public void updateClob(int columnIndex, Clob x) throws SQLException {
2465                try {
2466                        updateObject(columnIndex, toString(x));
2467                } catch (IOException e) {
2468                        throw new SQLException(e.getMessage());
2469                }
2470        }
2471
2472        @Override
2473        public void updateClob(String columnName, Clob x) throws SQLException {
2474                try {
2475                        updateObject(columnName, toString(x));
2476                } catch (IOException e) {
2477                        throw new SQLException(e.getMessage());
2478                }
2479        }
2480
2481        @Override
2482        public void updateDate(int columnIndex, java.sql.Date x)throws SQLException {
2483                updateObject(columnIndex, Caster.toDate(x, false, null, null));
2484        }
2485
2486        @Override
2487        public void updateDate(String columnName, java.sql.Date x)throws SQLException {
2488                updateObject(columnName, Caster.toDate(x, false, null, null));
2489        }
2490
2491        @Override
2492        public void updateDouble(int columnIndex, double x) throws SQLException {
2493                updateObject(columnIndex, Caster.toDouble(x));
2494        }
2495
2496        @Override
2497        public void updateDouble(String columnName, double x) throws SQLException {
2498                updateObject(columnName, Caster.toDouble(x));
2499        }
2500
2501        @Override
2502        public void updateFloat(int columnIndex, float x) throws SQLException {
2503                updateObject(columnIndex, Caster.toDouble(x));
2504        }
2505
2506        @Override
2507        public void updateFloat(String columnName, float x) throws SQLException {
2508                updateObject(columnName, Caster.toDouble(x));
2509        }
2510
2511        @Override
2512        public void updateInt(int columnIndex, int x) throws SQLException {
2513                updateObject(columnIndex, Caster.toDouble(x));
2514        }
2515
2516        @Override
2517        public void updateInt(String columnName, int x) throws SQLException {
2518                updateObject(columnName, Caster.toDouble(x));
2519        }
2520
2521        @Override
2522        public void updateLong(int columnIndex, long x) throws SQLException {
2523                updateObject(columnIndex, Caster.toDouble(x));
2524        }
2525
2526        @Override
2527        public void updateLong(String columnName, long x) throws SQLException {
2528                updateObject(columnName, Caster.toDouble(x));
2529        }
2530
2531        @Override
2532        public void updateNull(int columnIndex) throws SQLException {
2533                updateObject(columnIndex, null);
2534        }
2535
2536        @Override
2537        public void updateNull(String columnName) throws SQLException {
2538                updateObject(columnName, null);
2539        }
2540
2541        @Override
2542        public void updateObject(int columnIndex, Object x) throws SQLException {
2543                try {
2544                        set(getColumnName(columnIndex), x);
2545                } catch (PageException e) {
2546                        throw new SQLException(e.getMessage());
2547                }
2548        }
2549
2550        @Override
2551        public void updateObject(String columnName, Object x) throws SQLException {
2552                try {
2553                        set(KeyImpl.init(columnName), x);
2554                } catch (PageException e) {
2555                        throw new SQLException(e.getMessage());
2556                }
2557        }
2558
2559        @Override
2560        public void updateObject(int columnIndex, Object x, int scale)throws SQLException {
2561                updateObject(columnIndex, x);
2562        }
2563
2564        @Override
2565        public void updateObject(String columnName, Object x, int scale)throws SQLException {
2566                updateObject(columnName, x);
2567        }
2568
2569        @Override
2570        public void updateRef(int columnIndex, Ref x) throws SQLException {
2571                updateObject(columnIndex, x.getObject());
2572        }
2573
2574        @Override
2575        public void updateRef(String columnName, Ref x) throws SQLException {
2576                updateObject(columnName, x.getObject());
2577        }
2578
2579        public void updateRow() throws SQLException {
2580                throw new SQLException("method is not implemented");
2581        }
2582
2583        @Override
2584        public void updateShort(int columnIndex, short x) throws SQLException {
2585                updateObject(columnIndex, Caster.toDouble(x));
2586        }
2587
2588        @Override
2589        public void updateShort(String columnName, short x) throws SQLException {
2590                updateObject(columnName, Caster.toDouble(x));
2591        }
2592
2593        @Override
2594        public void updateString(int columnIndex, String x) throws SQLException {
2595                updateObject(columnIndex, x);
2596        }
2597
2598        @Override
2599        public void updateString(String columnName, String x) throws SQLException {
2600                updateObject(columnName, x);
2601        }
2602
2603        @Override
2604        public void updateTime(int columnIndex, Time x) throws SQLException {
2605                updateObject(columnIndex, new DateTimeImpl(x.getTime(),false));
2606        }
2607
2608        @Override
2609        public void updateTime(String columnName, Time x) throws SQLException {
2610                updateObject(columnName, new DateTimeImpl(x.getTime(),false));
2611        }
2612
2613        @Override
2614        public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
2615                updateObject(columnIndex, new DateTimeImpl(x.getTime(),false));
2616        }
2617
2618        @Override
2619        public void updateTimestamp(String columnName, Timestamp x) throws SQLException {
2620                updateObject(columnName, new DateTimeImpl(x.getTime(),false));
2621        }
2622
2623        @Override
2624        public ResultSetMetaData getMetaData() throws SQLException {
2625                throw new SQLException("method is not implemented");
2626        }
2627
2628        @Override
2629        public Iterator<Collection.Key> keyIterator() {
2630                return new KeyIterator(keys());
2631        }
2632    
2633        @Override
2634        public Iterator<String> keysAsStringIterator() {
2635        return new StringIterator(keys());
2636    }
2637        
2638        @Override
2639        public Iterator<Entry<Key, Object>> entryIterator() {
2640                return new EntryIterator(this, keys());
2641        }
2642        
2643        public Iterator<Object> valueIterator() {
2644                return new CollectionIterator(keys(),this);
2645        }
2646
2647        public void readExternal(ObjectInput in) throws IOException {
2648                try {
2649                        QueryImpl other=(QueryImpl) new CFMLExpressionInterpreter(false).interpret(ThreadLocalPageContext.get(),in.readUTF());
2650                        this.arrCurrentRow=other.arrCurrentRow;
2651                        this.columncount=other.columncount;
2652                        this.columnNames=other.columnNames;
2653                        this.columns=other.columns;
2654                        this.exeTime=other.exeTime;
2655                        this.generatedKeys=other.generatedKeys;
2656                        this.cacheType=other.cacheType;
2657                        this.name=other.name;
2658                        this.recordcount=other.recordcount;
2659                        this.sql=other.sql;
2660                        this.updateCount=other.updateCount;
2661                        
2662                } catch (PageException e) {
2663                        throw new IOException(e.getMessage());
2664                }
2665        }
2666
2667        public void writeExternal(ObjectOutput out) {
2668                try {
2669                        out.writeUTF(new ScriptConverter().serialize(this));
2670                } 
2671                catch (Throwable t) {
2672                        ExceptionUtil.rethrowIfNecessary(t);
2673                }
2674        }
2675
2676        public int getHoldability() throws SQLException {
2677                throw notSupported();
2678        }
2679
2680        public boolean isClosed() throws SQLException {
2681                return false;
2682        }
2683
2684        public void updateNString(int columnIndex, String nString)throws SQLException {
2685                updateString(columnIndex, nString);
2686        }
2687
2688        public void updateNString(String columnLabel, String nString)throws SQLException {
2689                updateString(columnLabel, nString);
2690        }
2691
2692        
2693
2694        public String getNString(int columnIndex) throws SQLException {
2695                return getString(columnIndex);
2696        }
2697
2698        public String getNString(String columnLabel) throws SQLException {
2699                return getString(columnLabel);
2700        }
2701
2702        public Reader getNCharacterStream(int columnIndex) throws SQLException {
2703                return getCharacterStream(columnIndex);
2704        }
2705
2706        public Reader getNCharacterStream(String columnLabel) throws SQLException {
2707                return getCharacterStream(columnLabel);
2708        }
2709
2710        public void updateNCharacterStream(int columnIndex, Reader x, long length)throws SQLException {
2711                updateCharacterStream(columnIndex, x, length);
2712        }
2713
2714        public void updateNCharacterStream(String columnLabel, Reader reader,long length) throws SQLException {
2715                throw notSupported();
2716        }
2717
2718        public void updateAsciiStream(int columnIndex, InputStream x, long length)throws SQLException {
2719                throw notSupported();
2720        }
2721
2722        public void updateBinaryStream(int columnIndex, InputStream x, long length)throws SQLException {
2723                throw notSupported();
2724        }
2725
2726        public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
2727                throw notSupported();
2728        }
2729
2730        public void updateAsciiStream(String columnLabel, InputStream x, long length)throws SQLException {
2731                throw notSupported();
2732        }
2733
2734        public void updateBinaryStream(String columnLabel, InputStream x,long length) throws SQLException {
2735                throw notSupported();
2736        }
2737
2738        public void updateCharacterStream(String columnLabel, Reader reader,long length) throws SQLException {
2739                throw notSupported();
2740        }
2741
2742        public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException {
2743                throw notSupported();
2744        }
2745
2746        public void updateBlob(String columnLabel, InputStream inputStream,long length) throws SQLException {
2747                throw notSupported();
2748        }
2749
2750        public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
2751                throw notSupported();
2752        }
2753
2754        public void updateClob(String columnLabel, Reader reader, long length) throws SQLException {
2755                throw notSupported();
2756        }
2757
2758        public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
2759                updateClob(columnIndex, reader, length);
2760        }
2761
2762        public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException {
2763                updateClob(columnLabel, reader,length);
2764        }
2765
2766        public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
2767                updateCharacterStream(columnIndex, x);
2768        }
2769
2770        public void updateNCharacterStream(String columnLabel, Reader reader)throws SQLException {
2771                throw notSupported();
2772        }
2773
2774        public void updateAsciiStream(int columnIndex, InputStream x)throws SQLException {
2775                throw notSupported();
2776        }
2777
2778        public void updateBinaryStream(int columnIndex, InputStream x)throws SQLException {
2779                throw notSupported();
2780        }
2781
2782        public void updateCharacterStream(int columnIndex, Reader x) throws SQLException {
2783                throw notSupported();
2784        }
2785
2786        public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException {
2787                throw notSupported();
2788        }
2789
2790        public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException {
2791                throw notSupported();
2792        }
2793
2794        public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException {
2795                throw notSupported();
2796        }
2797
2798        public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
2799                throw notSupported();
2800        }
2801
2802        public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException {
2803                throw notSupported();
2804        }
2805
2806        public void updateClob(int columnIndex, Reader reader) throws SQLException {
2807                throw notSupported();
2808        }
2809
2810        public void updateClob(String columnLabel, Reader reader) throws SQLException {
2811                throw notSupported();
2812        }
2813
2814        public void updateNClob(int columnIndex, Reader reader) throws SQLException {
2815                updateClob(columnIndex, reader);
2816        }
2817
2818        public void updateNClob(String columnLabel, Reader reader) throws SQLException {
2819                updateClob(columnLabel, reader);
2820        }
2821
2822        public <T> T unwrap(Class<T> iface) throws SQLException {
2823                throw notSupported();
2824        }
2825
2826        public boolean isWrapperFor(Class<?> iface) throws SQLException {
2827                throw notSupported();
2828        }
2829        
2830
2831        
2832        //JDK6: uncomment this for compiling with JDK6 
2833         
2834        public void updateNClob(int columnIndex, NClob nClob) throws SQLException {
2835                throw notSupported();
2836        }
2837
2838        public void updateNClob(String columnLabel, NClob nClob) throws SQLException {
2839                throw notSupported();
2840        }
2841
2842        public NClob getNClob(int columnIndex) throws SQLException {
2843                throw notSupported();
2844        }
2845
2846        public NClob getNClob(String columnLabel) throws SQLException {
2847                throw notSupported();
2848        }
2849
2850        public SQLXML getSQLXML(int columnIndex) throws SQLException {
2851                throw notSupported();
2852        }
2853
2854        public SQLXML getSQLXML(String columnLabel) throws SQLException {
2855                throw notSupported();
2856        }
2857
2858        public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
2859                throw notSupported();
2860        }
2861
2862        public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException {
2863                throw notSupported();
2864        }
2865        
2866        public RowId getRowId(int columnIndex) throws SQLException {
2867                throw notSupported();
2868        }
2869
2870        public RowId getRowId(String columnLabel) throws SQLException {
2871                throw notSupported();
2872        }
2873
2874        public void updateRowId(int columnIndex, RowId x) throws SQLException {
2875                throw notSupported();
2876        }
2877
2878        public void updateRowId(String columnLabel, RowId x) throws SQLException {
2879                throw notSupported();
2880        }
2881        
2882        public void removeRows(int index, int count) throws PageException {
2883                QueryUtil.removeRows(this,index,count);
2884        }
2885        
2886
2887        private SQLException notSupported() {
2888                return new SQLException("this feature is not supported");
2889        }
2890
2891        public synchronized void enableShowQueryUsage() {
2892                if(columns!=null)for(int i=0;i<columns.length;i++){
2893                        columns[i]=columns[i]._toDebugColumn();
2894                }
2895        }
2896
2897        @Override
2898        public long getExecutionTime() {
2899                return exeTime;
2900        }
2901        
2902        public static QueryImpl cloneQuery(Query qry,boolean deepCopy) {
2903        QueryImpl newResult=new QueryImpl();
2904        boolean inside=ThreadLocalDuplication.set(qry, newResult);
2905        try{
2906                newResult.columnNames=qry.getColumnNames();
2907                newResult.columns=new QueryColumnImpl[newResult.columnNames.length];
2908                QueryColumn col;
2909                for(int i=0;i<newResult.columnNames.length;i++) {
2910                        col =  qry.getColumn(newResult.columnNames[i],null);
2911                        newResult.columns[i]=QueryUtil.duplicate2QueryColumnImpl(newResult,col,deepCopy);
2912                }
2913                
2914                        
2915                    newResult.sql=qry.getSql();
2916                newResult.template=qry.getTemplate();
2917                newResult.recordcount=qry.getRecordcount();
2918                newResult.columncount=newResult.columnNames.length;
2919                newResult.cacheType=qry instanceof QueryImpl?((QueryImpl)qry).getCacheType():null;
2920                newResult.name=qry.getName();
2921                newResult.exeTime=qry.getExecutionTime();
2922                newResult.updateCount=qry.getUpdateCount();
2923                if(qry.getGeneratedKeys()!=null)newResult.generatedKeys=((QueryImpl)qry.getGeneratedKeys()).cloneQuery(false);
2924                return newResult;
2925        }
2926        finally {
2927                if(!inside)ThreadLocalDuplication.reset();
2928        }
2929    }
2930        
2931        @Override
2932        public java.util.Iterator getIterator() {
2933                return new ForEachQueryIterator(this, ThreadLocalPageContext.get().getId());
2934    }
2935        
2936        @Override
2937        public boolean equals(Object obj){
2938                if(!(obj instanceof Collection)) return false;
2939                return CollectionUtil.equals(this,(Collection)obj);
2940        }
2941        
2942        /*@Override
2943        public int hashCode() {
2944                return CollectionUtil.hashCode(this);
2945        }*/
2946}