/* ** Name: exec ** ** Description: ** Send SQL statement to server and process results. ** A cursor is opened when executing a SELECT and a ** result-set is generated. Non-SELECT statements ** are simply executed and the row count is updated. ** ** Input: ** text Statement text. ** mode Execute mode: QUERY, UPDATE, UNKNOWN. ** ** Output: ** None. ** ** Returns: ** void. ** ** History: ** 26-Oct-99 (gordy) ** Extracted from interface methods. ** 15-Nov-99 (gordy) ** Pass max row count and column length to result set. ** 16-Nov-99 (gordy) ** Added query timeouts. ** 13-Dec-99 (gordy) ** Added fetch limit. ** 16-May-00 (gordy) ** Move SQL parsing back into individual query processing ** sections so that it occurs after parser.getCursonName(). ** 19-May-00 (gordy) ** If select loops have been enabled, and a cursor name has ** not been assigned, execute queries directly rather than ** opening a cursor. Locking had to be reorganized since ** select loops are not unlocked until the result set is ** closed. Also needed additional handling when executing ** (supposedly) non-queries and get a result set by mistake. ** 28-Mar-01 (gordy) ** Call result-set shut() to avoid top-level tracing. ** 20-Jun-01 (gordy) ** Pass cursor name to result-set according to spec: ** NULL if READONLY and not provided by application. ** 21-Jun-01 (gordy) ** Re-worked parsing to eliminate redundant code. Parser ** parameter replaced with simple query text string. Query ** parameter replaced with mode to convey caller intent. ** 20-Aug-01 (gordy) ** Use cursor type constants. Send READONLY flag to server. ** Warn if cursor mode changed to READONLY. ** 25-Oct-01 (gordy) ** Select loops are only supported at PROTO_2 and above. ** 31-Oct-01 (gordy) ** Timezone now passed to AdvanSQL. ** 31-Oct-02 (gordy) ** Adapted for generic GCF driver. ** 19-Feb-03 (gordy) ** Added method to calculate fetch size. ** 15-Apr-03 (gordy) ** Added connection timezones separate from date formatters. ** 7-Jul-03 (gordy) ** Added Ingres timezone config which affects date/time literals. ** 4-Aug-03 (gordy) ** Created neew result-set class for updatable cursors. ** 20-Aug-03 (gordy) ** Send AUTO_CLOSE flag for query statements. ** 12-Sep-03 (gordy) ** Timezone replaced by GMT indicator. ** 6-Oct-03 (gordy) ** Fetch first block of rows for select-loops and read-only cursors. ** 19-Jun-06 (gordy) ** I give up... GMT indicator replaced with connection. */ private void exec( String text, int mode ) { SqlParse parser = new SqlParse( text, conn ); int type = parser.getQueryType(); AdvanRSMD rsmd; String cursor = null; clearResults(); if ( mode == UNKNOWN ) mode = (type == SqlParse.QT_SELECT) ? QUERY : UPDATE; if ( type == SqlParse.QT_DELETE || type == SqlParse.QT_UPDATE ) cursor = parser.getCursorName(); msg.LockConnection(); try { String sql = parser.parseSQL( parse_escapes ); if ( mode == QUERY ) { int concurrency = getConcurrency( parser.getConcurrency() ); if ( conn.select_loops && crsr_name == null && concurrency != DrvConst.DRV_CRSR_UPDATE && conn.msg_protocol_level >= MSG_PROTO_2 ) { /* ** Select statement using a select loop. */ bool needEOG = true; msg.begin( MSG_QUERY ); msg.write( MSG_QRY_EXQ ); if ( conn.msg_protocol_level >= MSG_PROTO_3 ) { msg.write( MSG_QP_FLAGS ); msg.write( (short)4 ); msg.write( MSG_QF_FETCH_FIRST | MSG_QF_AUTO_CLOSE ); needEOG = false; } msg.write( MSG_QP_QTXT ); msg.write( sql ); msg.done( true ); if ( (rsmd = readResults( timeout, needEOG )) == null ) throw SqlEx.get( ERR_GC4017_NO_RESULT_SET ); resultSet = new RsltSlct( conn, this, rsmd, rslt_val_stmt, getPreFetchSize(), msg.moreMessages()); } else { /* ** Select statement using a cursor. Generate a ** cursor ID if not provided by the application. */ short flags = 0; bool needEOG = true; cursor = (crsr_name != null) ? crsr_name : conn.getUniqueID( crsr_prefix ); /* ** Parser removes FOR READONLY clause because it isn't ** a part of Ingres SELECT syntax. Tell server that ** cursor should be READONLY (kludge older protocol ** by restoring clause to query). */ if ( concurrency == DrvConst.DRV_CRSR_READONLY ) if ( conn.msg_protocol_level < MSG_PROTO_2 ) sql += " for readonly"; else if ( conn.msg_protocol_level < MSG_PROTO_3 ) flags |= MSG_QF_READONLY; else { flags |= MSG_QF_READONLY | MSG_QF_FETCH_FIRST; needEOG = false; } if ( conn.msg_protocol_level >= MSG_PROTO_3 ) flags |= MSG_QF_AUTO_CLOSE; msg.begin( MSG_QUERY ); msg.write( MSG_QRY_OCQ ); if ( flags != 0 ) { msg.write( MSG_QP_FLAGS ); msg.write( (short)2 ); msg.write( flags ); } msg.write( MSG_QP_CRSR_NAME ); msg.write( cursor ); msg.write( MSG_QP_QTXT ); msg.write( sql ); msg.done( true ); if ( (rsmd = readResults( timeout, needEOG )) == null ) throw SqlEx.get( ERR_GC4017_NO_RESULT_SET ); /* ** The cursor name is passed to the result-set ** for updatable cursors or if provided by the ** application (2.1 API spec). */ if ( (rslt_flags & MSG_RF_READ_ONLY) == 0 ) { resultSet = new RsltUpd( conn, this, rsmd, rslt_val_stmt, cursor ); if ( msg.moreMessages() ) readResults( timeout, needEOG ); } else { if ( rs_concur == DrvConst.DRV_CRSR_UPDATE ) setWarning( SqlEx.get( ERR_GC4016_RS_CHANGED ) ); resultSet = new RsltCurs( conn, this, rsmd, rslt_val_stmt, crsr_name, getPreFetchSize(), msg.moreMessages() ); } msg.UnlockConnection(); } } else if ( cursor != null ) { /* ** Positioned Delete/Update. */ msg.begin( MSG_QUERY ); msg.write( (type == SqlParse.QT_DELETE) ? MSG_QRY_CDEL : MSG_QRY_CUPD ); msg.write( MSG_QP_CRSR_NAME ); msg.write( cursor ); msg.write( MSG_QP_QTXT ); msg.write( sql ); msg.done( true ); if ( readResults( timeout, true ) != null ) // shouldn't happen throw SqlEx.get( ERR_GC4018_RESULT_SET_NOT_PERMITTED ); msg.UnlockConnection(); } else { /* ** Non-query statement. */ msg.begin( MSG_QUERY ); msg.write( MSG_QRY_EXQ ); msg.write( MSG_QP_QTXT ); msg.write( sql ); msg.done( true ); if ( (rsmd = readResults( timeout, true )) == null ) msg.UnlockConnection(); // We be done. else { /* ** Query unexpectedly returned a result-set. ** We need to cleanup the tuple stream and ** statement in the server. The easiest ** way to do this is go ahead and create a ** result-set and close it. */ resultSet = new RsltSlct( conn, this, rsmd, rslt_val_stmt, 1, false ); try { resultSet.shut(); } catch( SqlEx ) {} resultSet = null; throw SqlEx.get( ERR_GC4018_RESULT_SET_NOT_PERMITTED ); } } } catch( SqlEx ex ) { if ( trace.enabled() ) trace.log( title + ".execute(): error executing query" ); if ( trace.enabled( 1 ) ) ex.trace( trace ); msg.UnlockConnection(); throw ex; } return; }
AdvanCall( DrvConn conn, String sql ) : // Initialize base class and override defaults. base( conn, DrvConst.TYPE_FORWARD_ONLY, DrvConst.DRV_CRSR_READONLY, DrvConst.CLOSE_CURSORS_AT_COMMIT ) { title = trace.getTraceName() + "-CallableStatement[" + inst_id + "]"; tr_id = "Call[" + inst_id + "]"; /* ** Flag all parameters as procedure parameters. */ paramSet.setDefaultFlags( ParamSet.PS_FLG_PROC_IN ); bool native_proc = false; /* ** Process the call procedure syntax. */ try { SqlParse parser = new SqlParse( sql, conn ); procInfo = new ProcInfo( conn ); parser.parseCall( procInfo ); native_proc = (parser.getQueryType() == SqlParse.QT_NATIVE_PROC); } catch( SqlEx ex ) { if ( trace.enabled() ) trace.log( title + ": error parsing query text" ); if ( trace.enabled( 1 ) ) ex.trace( trace ); throw ex; } /* ** Count the number of dynamic parameters and allocate ** the dynamic parameter mapping arrays. Clear the param ** information if only default parameters. */ int param_cnt = procInfo.getParamCount(); int dflt_cnt = 0; int dynamic = 0; for( int param = 0; param < param_cnt; param++ ) switch( procInfo.getParamType( param ) ) { case ProcInfo.PARAM_DYNAMIC : dynamic++; break; case ProcInfo.PARAM_DEFAULT : dflt_cnt++; break; } if ( param_cnt > 0 ) if ( param_cnt == dflt_cnt ) procInfo.clearParams(); // No real parameters. else if ( param_cnt != dynamic ) { /* ** Load meta-data parameter names to ensure ** correct relationship between dynamic and ** non-dynamic parameters. See discussion ** in class description for further details. */ if ( ! procInfo.paramNamesLoaded() ) procInfo.loadParamNames(); } if ( procInfo.getReturn() ) { /* ** The mapping arrays include an entry for the procedure ** result parameter to provide the correct offest for other ** parameters, but the entry can't be used for mapping. */ dynamic++; /* ** Native procedure syntax places the procedure result ** parameter last. Escape syntax places it first. */ procRsltIndex = (native_proc ? dynamic-1 : 0); } /* ** The dynamic parameter map is used to map dynamic parameter ** ordinal indexes into internal parameter indexes. Mapping ** is required because the internal parameters include non- ** dynamic parameters and the dynamic parameters may include ** the procedure result. */ dpMap = (dynamic > 0) ? new int[ dynamic ] : noMap; /* ** The output parameter map is used to map internal parameter ** indexes (see above) into output result map indexes. The ** registered type/scale is saved in associated arrays (which ** also provide space, in the last entry, for the procedure ** result registration info). */ opMap = (param_cnt > 0) ? new int[ param_cnt ] : noMap; opType = new ProviderType[ param_cnt + 1 ]; // Include procedure result opScale = new int[ param_cnt + 1 ]; // Include procedure result /* ** Initialize the dynamic parameter mapping array ** and any static literal parameters. */ initParameters(); return; } // AdvanCall