/* ** Name: parseCall ** ** Description: ** Populates a procedure information object with the info ** parsed from the request text. ** ** The following Database Procedure call syntax is accepted: ** ** {[? =] CALL [schema.]name[( params )]} ** EXECUTE PROCEDURE [schema.]name[( params )] [INTO ?] ** CALLPROC [schema.]name[( params )] [INTO ?] ** ** params: param_spec | param_spec, params ** param_spec: [name =] [value] ** value: ? | numeric_literal | char_literal | hex_string | ** SESSION.table_name ** ** Note that parameter values are optional and will be flagged ** as default parameters. An empty parenthesis set implies a ** single default parameter. ** ** Input: ** None. ** ** Output: ** procInfo Procedure information. ** ** Returns: ** void. ** ** History: ** 13-Jun-00 (gordy) ** Created. ** 28-Mar-01 (gordy) ** ProcInfo now parameter rather than return value as ** info needed for constructing ProcInfo is not available. ** 11-Jul-01 (loera01) SIR 105309 ** Added support for Ingres syntax. ** 27-Nov-01 (loera01) ** Added support for standard syntax for global temp table ** parameters. ** 31-Oct-02 (gordy) ** Moved procedure return value and global temp table parameter ** parsing into this method. ** 24-Feb-03 (gordy) ** ProcInfo parameter index is now 0 based. Permit named ** parameters in escape syntax. */ public void parseCall( ProcInfo procInfo ) { int token, index; String name; /* ** By calling getQueryType() we verify that this is ** a procedure call request, determine if procedure ** return value is expected, and position the token ** scanner to read the procedure name (need to force ** type to UNKNOWN otherwise getQueryType() may not ** do the proper scanning). */ query_type = UNKNOWN; getQueryType(); if ( (query_type != QT_PROCEDURE && query_type != QT_NATIVE_PROC) || nextToken( false ) != IDENT ) throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); /* ** Save the procedure name, but check for schema qualification. */ name = new String( text, token_beg, token_end - token_beg ); token = nextToken( false ); if ( token == P_PERIOD ) { if ( nextToken( false ) != IDENT ) throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); procInfo.setSchema( name ); name = new String( text, token_beg, token_end - token_beg ); token = nextToken( false ); } procInfo.setName( name ); /* ** Process the optional parameter list. */ if ( token == P_LPAREN ) { for( index = 0, token = nextToken( false ); token != EOF; index++, token = nextToken( false ) ) { /* ** Process next parameter value. */ switch( token ) { case P_RPAREN : // End of parameter list. procInfo.setParam( index, ProcInfo.PARAM_DEFAULT, null ); goto break_for_loop; // We be done. case P_COMMA : // Missing parameter. procInfo.setParam( index, ProcInfo.PARAM_DEFAULT, null ); goto continue_for_loop; // Ready for next parameter. case P_QMARK : // Dynamic parameter marker procInfo.setParam( index, ProcInfo.PARAM_DYNAMIC, null ); break; case STRING : // String parameter value. procInfo.setParam( index, ProcInfo.PARAM_CHAR, new String( text, token_beg + 1, token_end - token_beg - 2 ) ); // skip quotes break; case P_PLUS : // Numeric parameter value. case P_MINUS : case P_PERIOD : case NUMBER : try { int type; Object value; String str; type = parseNumeric( token_beg ); str = new String( text, token_beg, token_end - token_beg ); switch( type ) { case NUM_INT : type = ProcInfo.PARAM_INT; value = Int32.Parse( str ); break; case NUM_DEC : type = ProcInfo.PARAM_DEC; value = Decimal.Parse( str ); break; case NUM_FLT : type = ProcInfo.PARAM_FLOAT; value = Double.Parse( str ); break; default : throw new FormatException(); } procInfo.setParam( index, type, value ); } catch( Exception ) { throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); } break; case IDENT: /* ** This may be a Global Temp Table parameter, a hex ** literal or parameter name. The following token ** differentiates between the three possibilities. */ int prev_end = token_end; int prev_beg = token_beg; switch( nextToken( false ) ) { case P_PERIOD : // Global Temp Table? if ( keyword( text, prev_beg, prev_end, keywords ) == KW_SESSION && nextToken( false ) == IDENT ) procInfo.setParam( index, ProcInfo.PARAM_SESSION, new String(text,token_beg,token_end - token_beg) ); else throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); break; case STRING : // Hex literal? if ( (prev_end - prev_beg) == 1 && (text[ prev_beg ] == 'X' || text[ prev_beg ] == 'x') ) procInfo.setParam( index, ProcInfo.PARAM_BYTE, hex2bin( text, token_beg + 1, token_end - token_beg - 2 ) ); else throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); break; case P_EQUAL : // Parameter name procInfo.setParamName( index, new String( text, prev_beg, prev_end - prev_beg ) ); /* ** The parameter value can be processed by ** restarting at the top of the loop (but ** need to avoid incrementing the index). */ index--; goto continue_for_loop; default : throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); } break; default: throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); } /* ** A parameter has just been processed. Either another ** parameter follows, indicated by a separating comma, ** or the end of the parameter list is expected (the ** closing parenthesis). */ if ( (token = nextToken( false )) == P_RPAREN ) break; else if ( token != P_COMMA ) throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); continue_for_loop: continue; } // end for loop break_for_loop: /* ** We should be at the end of the parameter list. */ if ( token == P_RPAREN ) token = nextToken( false ); else throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); } /* ** Check syntax termination. For the escape sequence this ** is just the closing brace: '}'. For native Ingres, only the ** optional return value is expected: 'INTO ?'. */ if ( query_type == QT_PROCEDURE ) { if ( token != P_RBRACE || nextToken( false ) != EOF ) throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); } else if ( token != EOF ) { if ( token == IDENT && keyword( text, token_beg, token_end, keywords ) == KW_INTO && nextToken( false ) == P_QMARK && nextToken( false ) == EOF ) procedure_return = true; else throw SqlEx.get( ERR_GC4014_CALL_SYNTAX ); } procInfo.setReturn( procedure_return ); 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