/** * Copyright (c) 2014, the Railo Company Ltd. * Copyright (c) 2015, Lucee Assosication Switzerland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * */ package lucee.runtime.tag; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.TimeZone; import lucee.commons.io.log.Log; import lucee.commons.lang.ClassException; import lucee.commons.lang.StringUtil; import lucee.loader.engine.CFMLEngine; import lucee.runtime.PageContext; import lucee.runtime.PageContextImpl; import lucee.runtime.PageSource; import lucee.runtime.cache.tag.CacheHandler; import lucee.runtime.cache.tag.CacheHandlerCollectionImpl; import lucee.runtime.cache.tag.CacheItem; import lucee.runtime.cache.tag.query.QueryResultCacheItem; import lucee.runtime.config.Config; import lucee.runtime.config.ConfigImpl; import lucee.runtime.config.ConfigWeb; import lucee.runtime.config.ConfigWebImpl; import lucee.runtime.config.Constants; import lucee.runtime.db.DataSource; import lucee.runtime.db.DataSourceImpl; import lucee.runtime.db.DatasourceConnection; import lucee.runtime.db.DatasourceManagerImpl; import lucee.runtime.db.HSQLDBHandler; import lucee.runtime.db.SQL; import lucee.runtime.db.SQLImpl; import lucee.runtime.db.SQLItem; import lucee.runtime.debug.DebuggerImpl; import lucee.runtime.exp.ApplicationException; import lucee.runtime.exp.DatabaseException; import lucee.runtime.exp.PageException; import lucee.runtime.ext.tag.BodyTagTryCatchFinallyImpl; import lucee.runtime.functions.displayFormatting.DecimalFormat; import lucee.runtime.listener.AppListenerUtil; import lucee.runtime.op.Caster; import lucee.runtime.op.Decision; import lucee.runtime.orm.ORMSession; import lucee.runtime.orm.ORMUtil; import lucee.runtime.tag.util.QueryParamConverter; import lucee.runtime.type.Array; import lucee.runtime.type.ArrayImpl; import lucee.runtime.type.Collection; import lucee.runtime.type.Collection.Key; import lucee.runtime.type.KeyImpl; import lucee.runtime.type.QueryColumn; import lucee.runtime.type.QueryImpl; import lucee.runtime.type.Struct; import lucee.runtime.type.StructImpl; import lucee.runtime.type.dt.DateTime; import lucee.runtime.type.dt.TimeSpan; import lucee.runtime.type.dt.TimeSpanImpl; import lucee.runtime.type.query.QueryArray; import lucee.runtime.type.query.QueryResult; import lucee.runtime.type.query.QueryStruct; import lucee.runtime.type.query.SimpleQuery; import lucee.runtime.type.scope.Argument; import lucee.runtime.type.util.CollectionUtil; import lucee.runtime.type.util.KeyConstants; import lucee.runtime.type.util.ListUtil; import lucee.runtime.util.PageContextUtil; import org.osgi.framework.BundleException; /** * Passes SQL statements to a data source. Not limited to queries. **/ public final class Query extends BodyTagTryCatchFinallyImpl { private static final Collection.Key SQL_PARAMETERS = KeyImpl.intern("sqlparameters"); private static final Collection.Key CFQUERY = KeyImpl.intern("cfquery"); private static final Collection.Key GENERATEDKEY = KeyImpl.intern("generatedKey"); private static final Collection.Key MAX_RESULTS = KeyImpl.intern("maxResults"); private static final Collection.Key TIMEOUT = KeyConstants._timeout; private static final int RETURN_TYPE_UNDEFINED = 0; private static final int RETURN_TYPE_QUERY = 1; private static final int RETURN_TYPE_ARRAY = 2; private static final int RETURN_TYPE_STRUCT = 3; public static final int RETURN_TYPE_STORED_PROC = 4; /** If specified, password overrides the password value specified in the data source setup. */ private String password; /** The name of the data source from which this query should retrieve data. */ private DataSource datasource=null; /** The maximum number of milliseconds for the query to execute before returning an error ** indicating that the query has timed-out. This attribute is not supported by most ODBC drivers. ** timeout is supported by the SQL Server 6.x or above driver. The minimum and maximum allowable values ** vary, depending on the driver. */ private TimeSpan timeout=null; /** This is the age of which the query data can be */ private Object cachedWithin; /** Specifies the maximum number of rows to fetch at a time from the server. The range is 1, ** default to 100. This parameter applies to ORACLE native database drivers and to ODBC drivers. ** Certain ODBC drivers may dynamically reduce the block factor at runtime. */ private int blockfactor=-1; /** The database driver type. */ private String dbtype; /** Used for debugging queries. Specifying this attribute causes the SQL statement submitted to the ** data source and the number of records returned from the query to be returned. */ private boolean debug=true; /* This is specific to JTags, and allows you to give the cache a specific name */ //private String cachename; /** Specifies the maximum number of rows to return in the record set. */ private int maxrows=-1; /** If specified, username overrides the username value specified in the data source setup. */ private String username; /** */ private DateTime cachedAfter; /** The name query. Must begin with a letter and may consist of letters, numbers, and the underscore ** character, spaces are not allowed. The query name is used later in the page to reference the query's ** record set. */ private String name; private String result=null; //private static HSQLDBHandler hsql=new HSQLDBHandler(); private boolean orgPSQ; private boolean hasChangedPSQ; ArrayList<SQLItem> items=new ArrayList<SQLItem>(); private boolean unique; private Struct ormoptions; private int returntype=RETURN_TYPE_UNDEFINED; private TimeZone timezone; private TimeZone tmpTZ; private boolean lazy; private Object params; private int nestingLevel=0; private boolean setReturnVariable=false; private Object rtn; private Key columnName; private boolean literalTimestampWithTSOffset; private boolean previousLiteralTimestampWithTSOffset; private String[] tags=null; @Override public void release() { super.release(); items.clear(); password=null; datasource=null; timeout=null; cachedWithin=null; cachedAfter=null; //cachename=""; blockfactor=-1; dbtype=null; debug=true; maxrows=-1; username=null; name=""; result=null; orgPSQ=false; hasChangedPSQ=false; unique=false; ormoptions=null; returntype=RETURN_TYPE_UNDEFINED; timezone=null; tmpTZ=null; lazy=false; params=null; nestingLevel=0; rtn=null; setReturnVariable=false; columnName=null; literalTimestampWithTSOffset=false; previousLiteralTimestampWithTSOffset=false; tags=null; } public void setTags(Object oTags) throws PageException { if(StringUtil.isEmpty(oTags)) return; // to Array Array arr; if(Decision.isArray(oTags)) arr = Caster.toArray(oTags); else arr = ListUtil.listToArrayRemoveEmpty(Caster.toString(oTags), ','); // to String[] Iterator<Object> it = arr.valueIterator(); List<String> list=new ArrayList<String>(); String str; while(it.hasNext()) { str = Caster.toString(it.next()); if(!StringUtil.isEmpty(str)) list.add(str); } tags=list.toArray(new String[list.size()]); } public void setOrmoptions(Struct ormoptions) { this.ormoptions = ormoptions; } public void setReturntype(String strReturntype) throws ApplicationException { if(StringUtil.isEmpty(strReturntype)) return; strReturntype=strReturntype.toLowerCase().trim(); if(strReturntype.equals("query")) returntype=RETURN_TYPE_QUERY; //mail.setType(lucee.runtime.mail.Mail.TYPE_TEXT); else if(strReturntype.equals("struct")) returntype=RETURN_TYPE_STRUCT; else if(strReturntype.equals("array") || strReturntype.equals("array_of_struct") || strReturntype.equals("array-of-struct") || strReturntype.equals("arrayofstruct") || strReturntype.equals("array_of_entity") || strReturntype.equals("array-of-entity") || strReturntype.equals("arrayofentities") || strReturntype.equals("array_of_entities") || strReturntype.equals("array-of-entities") || strReturntype.equals("arrayofentities")) returntype=RETURN_TYPE_ARRAY; else throw new ApplicationException("attribute returntype of tag query has an invalid value","valid values are [query,array] but value is now ["+strReturntype+"]"); } public void setUnique(boolean unique) { this.unique = unique; } /** * @param result the result to set */ public void setResult(String result) { this.result = result; } /** * @param psq set preserver single quote */ public void setPsq(boolean psq) { orgPSQ=pageContext.getPsq(); if(orgPSQ!=psq){ pageContext.setPsq(psq); hasChangedPSQ=true; } } /** set the value password * If specified, password overrides the password value specified in the data source setup. * @param password value to set **/ public void setPassword(String password) { this.password=password; } /** set the value datasource * The name of the data source from which this query should retrieve data. * @param datasource value to set * @throws ClassException * @throws BundleException **/ public void setDatasource(Object datasource) throws PageException, ClassException, BundleException { if (Decision.isStruct(datasource)) { this.datasource=AppListenerUtil.toDataSource(pageContext.getConfig(),"__temp__", Caster.toStruct(datasource),pageContext.getConfig().getLog("application")); } else if (Decision.isString(datasource)) { this.datasource=pageContext.getDataSource(Caster.toString(datasource)); } else { throw new ApplicationException("attribute [datasource] must be datasource name or a datasource definition(struct)"); } } /** set the value timeout * The maximum number of milliseconds for the query to execute before returning an error * indicating that the query has timed-out. This attribute is not supported by most ODBC drivers. * timeout is supported by the SQL Server 6.x or above driver. The minimum and maximum allowable values * vary, depending on the driver. * @param timeout value to set * @throws PageException **/ public void setTimeout(Object timeout) throws PageException { if(timeout instanceof TimeSpan) this.timeout=(TimeSpan) timeout; // seconds else { int i = Caster.toIntValue(timeout); if(i<0) throw new ApplicationException("invalid value ["+i+"] for attribute timeout, value must be a positive integer greater or equal than 0"); this.timeout=new TimeSpanImpl(0, 0, 0, i); } } /** set the value cachedafter * This is the age of which the query data can be * @param cachedafter value to set **/ public void setCachedafter(DateTime cachedafter) { //lucee.print.ln("cachedafter:"+cachedafter); this.cachedAfter=cachedafter; } /** set the value cachename * This is specific to JTags, and allows you to give the cache a specific name * @param cachename value to set **/ public void setCachename(String cachename) { //DeprecatedUtil.tagAttribute(pageContext,"query", "cachename"); //this.cachename=cachename; } public void setColumnkey(String columnKey) { if(StringUtil.isEmpty(columnKey,true)) return; this.columnName=KeyImpl.init(columnKey); } public void setCachedwithin(Object cachedwithin) { if(StringUtil.isEmpty(cachedwithin)) return; this.cachedWithin=cachedwithin; } public void setLazy(boolean lazy) { this.lazy=lazy; } /** set the value providerdsn * Data source name for the COM provider, OLE-DB only. * @param providerdsn value to set * @throws ApplicationException **/ public void setProviderdsn(String providerdsn) throws ApplicationException { //DeprecatedUtil.tagAttribute(pageContext,"Query", "providerdsn"); } /** set the value connectstring * @param connectstring value to set * @throws ApplicationException **/ public void setConnectstring(String connectstring) throws ApplicationException { //DeprecatedUtil.tagAttribute(pageContext,"Query", "connectstring"); } public void setTimezone(TimeZone tz) { if(tz==null) return; this.timezone=tz; } /** set the value blockfactor * Specifies the maximum number of rows to fetch at a time from the server. The range is 1, * default to 100. This parameter applies to ORACLE native database drivers and to ODBC drivers. * Certain ODBC drivers may dynamically reduce the block factor at runtime. * @param blockfactor value to set **/ public void setBlockfactor(double blockfactor) { this.blockfactor=(int) blockfactor; } /** set the value dbtype * The database driver type. * @param dbtype value to set **/ public void setDbtype(String dbtype) { this.dbtype=dbtype.toLowerCase(); } /** set the value debug * Used for debugging queries. Specifying this attribute causes the SQL statement submitted to the * data source and the number of records returned from the query to be returned. * @param debug value to set **/ public void setDebug(boolean debug) { this.debug=debug; } /** set the value dbname * The database name, Sybase System 11 driver and SQLOLEDB provider only. If specified, dbName * overrides the default database specified in the data source. * @param dbname value to set * @throws ApplicationException **/ public void setDbname(String dbname) { //DeprecatedUtil.tagAttribute(pageContext,"Query", "dbname"); } /** set the value maxrows * Specifies the maximum number of rows to return in the record set. * @param maxrows value to set **/ public void setMaxrows(double maxrows) { this.maxrows=(int) maxrows; } /** set the value username * If specified, username overrides the username value specified in the data source setup. * @param username value to set **/ public void setUsername(String username) { if(!StringUtil.isEmpty(username)) this.username=username; } /** set the value provider * COM provider, OLE-DB only. * @param provider value to set * @throws ApplicationException **/ public void setProvider(String provider) { //DeprecatedUtil.tagAttribute(pageContext,"Query", "provider"); } /** set the value dbserver * For native database drivers and the SQLOLEDB provider, specifies the name of the database server * computer. If specified, dbServer overrides the server specified in the data source. * @param dbserver value to set * @throws ApplicationException **/ public void setDbserver(String dbserver) { //DeprecatedUtil.tagAttribute(pageContext,"Query", "dbserver"); } /** set the value name * The name query. Must begin with a letter and may consist of letters, numbers, and the underscore * character, spaces are not allowed. The query name is used later in the page to reference the query's * record set. * @param name value to set **/ public void setName(String name) { this.name=name; } public String getName() { return name==null? "query":name; } /** * @param item */ public void setParam(SQLItem item) { items.add(item); } public void setParams(Object params) { this.params=params; } public void setNestinglevel(double nestingLevel) { this.nestingLevel=(int)nestingLevel; } @Override public int doStartTag() throws PageException { // default datasource if(datasource==null && (dbtype==null || !dbtype.equals("query"))){ Object obj = pageContext.getApplicationContext().getDefDataSource(); if(StringUtil.isEmpty(obj)) { boolean isCFML=pageContext.getRequestDialect()==CFMLEngine.DIALECT_CFML; throw new ApplicationException( "attribute [datasource] is required, when attribute [dbtype] has not value [query] and no default datasource is defined", "you can define a default datasource as attribute [defaultdatasource] of the tag " +(isCFML?Constants.CFML_APPLICATION_TAG_NAME:Constants.LUCEE_APPLICATION_TAG_NAME) +" or as data member of the " +(isCFML?Constants.CFML_APPLICATION_EVENT_HANDLER:Constants.LUCEE_APPLICATION_EVENT_HANDLER) +" (this.defaultdatasource=\"mydatasource\";)"); } datasource=obj instanceof DataSource?(DataSource)obj:pageContext.getDataSource(Caster.toString(obj)); } //timeout if(datasource instanceof DataSourceImpl && ((DataSourceImpl) datasource).getAlwaysSetTimeout()) { TimeSpan remaining = PageContextUtil.remainingTime(pageContext,true); if(this.timeout==null || ((int)this.timeout.getSeconds())<=0 || timeout.getSeconds()>remaining.getSeconds()) { // not set this.timeout=remaining; } } // timezone if(timezone!=null || (datasource!=null && (timezone=datasource.getTimeZone())!=null)) { tmpTZ=pageContext.getTimeZone(); pageContext.setTimeZone(timezone); } PageContextImpl pci = ((PageContextImpl)pageContext); // cache within if(StringUtil.isEmpty(cachedWithin)){ Object tmp = ((PageContextImpl)pageContext).getCachedWithin(ConfigWeb.CACHEDWITHIN_QUERY); if(tmp!=null)setCachedwithin(tmp); } // literal timestamp with TSOffset if(datasource instanceof DataSourceImpl) literalTimestampWithTSOffset=((DataSourceImpl)datasource).getLiteralTimestampWithTSOffset(); else literalTimestampWithTSOffset=false; previousLiteralTimestampWithTSOffset=pci.getTimestampWithTSOffset(); pci.setTimestampWithTSOffset(literalTimestampWithTSOffset); return EVAL_BODY_BUFFERED; } @Override public int doEndTag() throws PageException { if(hasChangedPSQ)pageContext.setPsq(orgPSQ); String strSQL=bodyContent.getString(); // no SQL String defined if(strSQL.length()==0) throw new DatabaseException("no sql string defined, inside query tag",null,null,null); try { strSQL=strSQL.trim(); // cannot use attribute params and queryparam tag if(items.size()>0 && params!=null) throw new DatabaseException("you cannot use the attribute params and sub tags queryparam at the same time",null,null,null); // create SQL SQL sql; if(params!=null) { if(params instanceof Argument) sql=QueryParamConverter.convert(strSQL, (Argument) params); else if(Decision.isArray(params)) sql=QueryParamConverter.convert(strSQL, Caster.toArray(params)); else if(Decision.isStruct(params)) sql=QueryParamConverter.convert(strSQL, Caster.toStruct(params)); else throw new DatabaseException("value of the attribute [params] has to be a struct or a array",null,null,null); } else sql=items.size()>0?new SQLImpl(strSQL,items.toArray(new SQLItem[items.size()])):new SQLImpl(strSQL); QueryResult qr=null; //lucee.runtime.type.Query query=null; long exe=0; boolean hasCached=cachedWithin!=null || cachedAfter!=null; String cacheId=null; if(hasCached) { String id = CacheHandlerCollectionImpl.createId(sql,datasource!=null?datasource.getName():null,username,password,returntype); CacheHandler ch = pageContext.getConfig().getCacheHandlerCollection(Config.CACHE_TYPE_QUERY,null).getInstanceMatchingObject(cachedWithin,null); if(ch!=null) { cacheId=ch.id(); CacheItem ci = ch.get(pageContext, id); if(ci instanceof QueryResultCacheItem) { QueryResultCacheItem ce = (QueryResultCacheItem) ci; if(ce.isCachedAfter(cachedAfter)) qr= ce.getQueryResult(); } } else { List<String> patterns = pageContext.getConfig().getCacheHandlerCollection(Config.CACHE_TYPE_QUERY,null).getPatterns(); throw new ApplicationException("cachedwithin value ["+cachedWithin+"] is invalid, valid values are for example ["+ListUtil.listToList(patterns, ", ")+"]"); } //query=pageContext.getQueryCache().getQuery(pageContext,sql,datasource!=null?datasource.getName():null,username,password,cachedafter); } if(qr==null) { // QoQ if("query".equals(dbtype)) { lucee.runtime.type.Query q=executeQoQ(sql); if(returntype==RETURN_TYPE_ARRAY) qr=QueryArray.toQueryArray(q); // TODO this should be done in queryExecute itself so we not have to convert afterwards else if(returntype==RETURN_TYPE_STRUCT){ if(columnName==null) throw new ApplicationException("attribute columnKey is required when return type is set to struct"); qr=QueryStruct.toQueryStruct(q,columnName); // TODO this should be done in queryExecute itself so we not have to convert afterwards } else qr=(QueryResult)q; } // ORM and Datasource else { long start=System.nanoTime(); Object obj = ("orm".equals(dbtype) || "hql".equals(dbtype))? executeORM(sql,returntype,ormoptions): executeDatasoure(sql,result!=null,pageContext.getTimeZone()); if(obj instanceof QueryResult) qr=(QueryResult)obj; else { if(setReturnVariable){ rtn=obj; } else if(!StringUtil.isEmpty(name)) { pageContext.setVariable(name,obj); } if(result!=null){ Struct sct=new StructImpl(); sct.setEL(KeyConstants._cached, Boolean.FALSE); long time=System.nanoTime()-start; sct.setEL(KeyConstants._executionTime, Caster.toDouble(time/1000000)); sct.setEL(KeyConstants._executionTimeNano, Caster.toDouble(time)); sct.setEL(KeyConstants._SQL, sql.getSQLString()); if(Decision.isArray(obj)){ } else sct.setEL(KeyConstants._RECORDCOUNT, Caster.toDouble(1)); pageContext.setVariable(result, sct); } else setExecutionTime((System.nanoTime()-start)/1000000); return EVAL_PAGE; } } //else query=executeDatasoure(sql,result!=null,pageContext.getTimeZone()); if(cachedWithin!=null) { String id = CacheHandlerCollectionImpl.createId(sql,datasource!=null?datasource.getName():null,username,password,returntype); CacheHandler ch = pageContext.getConfig().getCacheHandlerCollection(Config.CACHE_TYPE_QUERY,null).getInstanceMatchingObject(cachedWithin,null); if(ch!=null) { CacheItem ci = QueryResultCacheItem.newInstance(qr,tags,null); if(ci!=null)ch.set(pageContext, id,cachedWithin,ci); } } exe=qr.getExecutionTime(); } else qr.setCacheType(cacheId); if(pageContext.getConfig().debug() && debug) { boolean logdb=((ConfigImpl)pageContext.getConfig()).hasDebugOptions(ConfigImpl.DEBUG_DATABASE); if(logdb){ boolean debugUsage=DebuggerImpl.debugQueryUsage(pageContext,qr); DebuggerImpl di=(DebuggerImpl) pageContext.getDebugger(); di.addQuery(debugUsage?qr:null,datasource!=null?datasource.getName():null,name,sql,qr.getRecordcount(),getPageSource(),exe); } } if(setReturnVariable){ rtn=qr; } else if((qr.getColumncount()+qr.getRecordcount())>0 && !StringUtil.isEmpty(name)) { pageContext.setVariable(name,qr); } // Result if(result!=null) { Struct sct=new StructImpl(); sct.setEL(KeyConstants._cached, Caster.toBoolean(qr.isCached())); if((qr.getColumncount()+qr.getRecordcount())>0){ String list = ListUtil.arrayToList( qr instanceof lucee.runtime.type.Query? ((lucee.runtime.type.Query)qr).getColumnNamesAsString(): CollectionUtil.toString(qr.getColumnNames(), false) ,","); sct.setEL(KeyConstants._COLUMNLIST, list); } int rc=qr.getRecordcount(); if(rc==0)rc=qr.getUpdateCount(); sct.setEL(KeyConstants._RECORDCOUNT, Caster.toDouble(rc)); sct.setEL(KeyConstants._executionTime, Caster.toDouble(qr.getExecutionTime()/1000000)); sct.setEL(KeyConstants._executionTimeNano, Caster.toDouble(qr.getExecutionTime())); sct.setEL(KeyConstants._SQL, sql.getSQLString()); // GENERATED KEYS lucee.runtime.type.Query qi = Caster.toQuery(qr,null); if(qi !=null){ lucee.runtime.type.Query qryKeys = qi.getGeneratedKeys(); if(qryKeys!=null){ StringBuilder generatedKey=new StringBuilder(),sb; Collection.Key[] columnNames = qryKeys.getColumnNames(); QueryColumn column; for(int c=0;c<columnNames.length;c++){ column = qryKeys.getColumn(columnNames[c]); sb=new StringBuilder(); int size=column.size(); for(int row=1;row<=size;row++) { if(row>1)sb.append(','); sb.append(Caster.toString(column.get(row,null))); } if(sb.length()>0){ sct.setEL(columnNames[c], sb.toString()); if(generatedKey.length()>0)generatedKey.append(','); generatedKey.append(sb); } } if(generatedKey.length()>0) sct.setEL(GENERATEDKEY, generatedKey.toString()); } } // sqlparameters SQLItem[] params = sql.getItems(); if(params!=null && params.length>0) { Array arr=new ArrayImpl(); sct.setEL(SQL_PARAMETERS, arr); for(int i=0;i<params.length;i++) { arr.append(params[i].getValue()); } } pageContext.setVariable(result, sct); } // cfquery.executiontime else { setExecutionTime(exe/1000000); } // listener ((ConfigWebImpl)pageContext.getConfig()).getActionMonitorCollector() .log(pageContext, "query", "Query", exe, qr); // log Log log = pageContext.getConfig().getLog("datasource"); if(log.getLogLevel()>=Log.LEVEL_INFO) { log.info("query tag", "executed ["+sql.toString().trim()+"] in "+DecimalFormat.call(pageContext, exe/1000000D)+" ms"); } } catch (PageException pe) { // log pageContext.getConfig().getLog("datasource").error("query tag", pe); throw pe; } finally { ((PageContextImpl)pageContext).setTimestampWithTSOffset(previousLiteralTimestampWithTSOffset); if(tmpTZ!=null) { pageContext.setTimeZone(tmpTZ); } } return EVAL_PAGE; } private PageSource getPageSource() { if(nestingLevel>0) { PageContextImpl pci=(PageContextImpl) pageContext; List<PageSource> list = pci.getPageSourceList(); int index=list.size()-1-nestingLevel; if(index>=0) return list.get(index); } return pageContext.getCurrentPageSource(); } private void setExecutionTime(long exe) { Struct sct=new StructImpl(); sct.setEL(KeyConstants._executionTime,new Double(exe)); pageContext.undefinedScope().setEL(CFQUERY,sct); } private Object executeORM(SQL sql, int returnType, Struct ormoptions) throws PageException { ORMSession session=ORMUtil.getSession(pageContext); if(ormoptions==null) ormoptions=new StructImpl(); String dsn = null; if (ormoptions!=null) dsn = Caster.toString(ormoptions.get(KeyConstants._datasource,null),null); if(StringUtil.isEmpty(dsn,true)) dsn=ORMUtil.getDefaultDataSource(pageContext).getName(); // params SQLItem[] _items = sql.getItems(); Array params=new ArrayImpl(); for(int i=0;i<_items.length;i++){ params.appendEL(_items[i]); } // query options if(maxrows!=-1 && !ormoptions.containsKey(MAX_RESULTS)) ormoptions.setEL(MAX_RESULTS, new Double(maxrows)); if(timeout!=null && ((int)timeout.getSeconds())>0 && !ormoptions.containsKey(TIMEOUT)) ormoptions.setEL(TIMEOUT, new Double(timeout.getSeconds())); /* MUST offset: Specifies the start index of the resultset from where it has to start the retrieval. cacheable: Whether the result of this query is to be cached in the secondary cache. Default is false. cachename: Name of the cache in secondary cache. */ Object res = session.executeQuery(pageContext,dsn,sql.getSQLString(),params,unique,ormoptions); if(returnType==RETURN_TYPE_ARRAY || returnType==RETURN_TYPE_UNDEFINED) return res; return session.toQuery(pageContext, res, null); } public static Object _call(PageContext pc,String hql, Object params, boolean unique, Struct queryOptions) throws PageException { ORMSession session=ORMUtil.getSession(pc); String dsn = Caster.toString(queryOptions.get(KeyConstants._datasource,null),null); if(StringUtil.isEmpty(dsn,true)) dsn=ORMUtil.getDefaultDataSource(pc).getName(); if(Decision.isCastableToArray(params)) return session.executeQuery(pc,dsn,hql,Caster.toArray(params),unique,queryOptions); else if(Decision.isCastableToStruct(params)) return session.executeQuery(pc,dsn,hql,Caster.toStruct(params),unique,queryOptions); else return session.executeQuery(pc,dsn,hql,(Array)params,unique,queryOptions); } private lucee.runtime.type.Query executeQoQ(SQL sql) throws PageException { try { return new HSQLDBHandler().execute(pageContext,sql,maxrows,blockfactor,timeout); } catch (Exception e) { throw Caster.toPageException(e); } } private QueryResult executeDatasoure(SQL sql,boolean createUpdateData,TimeZone tz) throws PageException { DatasourceManagerImpl manager = (DatasourceManagerImpl) pageContext.getDataSourceManager(); DatasourceConnection dc=manager.getConnection(pageContext,datasource, username, password); try { if(lazy && !createUpdateData && cachedWithin==null && cachedAfter==null && result==null) { if(returntype!=RETURN_TYPE_QUERY) throw new DatabaseException("only return type query is allowed when lazy is set to true", null, sql, dc); return new SimpleQuery(pageContext,dc,sql,maxrows,blockfactor,timeout,getName(),getPageSource().getDisplayPath(),tz); } if(returntype==RETURN_TYPE_ARRAY) return QueryImpl.toArray(pageContext,dc,sql,maxrows,blockfactor,timeout,getName(),getPageSource().getDisplayPath(),createUpdateData,true); if(returntype==RETURN_TYPE_STRUCT){ if(columnName==null) throw new ApplicationException("attribute columnKey is required when return type is set to struct"); return QueryImpl.toStruct(pageContext,dc,sql,columnName,maxrows,blockfactor,timeout,getName(),getPageSource().getDisplayPath(),createUpdateData,true); } return new QueryImpl(pageContext,dc,sql,maxrows,blockfactor,timeout,getName(),getPageSource().getDisplayPath(),createUpdateData,true); } finally { manager.releaseConnection(pageContext,dc); } } @Override public void doInitBody() { } @Override public int doAfterBody() { return SKIP_BODY; } public void setReturnVariable(boolean setReturnVariable) { this.setReturnVariable=setReturnVariable; } public Object getReturnVariable() { return rtn; } }