private RoutineScope GetRoutineScopeFromRoutineInfo(RoutineInfo ri) { RoutineScope rs = new RoutineScope() { OwningRoutine = ri, Variables = new Dictionary<string,StoreType>() }; foreach (StoreType st in ri.Locals.Values) { rs.Variables.Add(st.Name, new StoreType(st)); } return rs; }
/// <summary> /// Replaces in the current statement, any references to local variables/arguments /// with their value. /// </summary> /// <param name="t"></param> /// <returns>A string with the expression after replacing the values.</returns> internal string ReplaceVarsWithValues(CommonTree t, CommonTokenStream cts, RoutineScope rs ) { Dictionary<string, StoreType> vars = rs.Variables; StringBuilder sb = new StringBuilder(); //foreach (IToken tok in cts.GetTokens(t.TokenStartIndex, t.TokenStopIndex)) for (int i = t.TokenStartIndex; i <= t.TokenStopIndex; i++) { IToken tok = cts.Get(i); IToken tok2 = null; StoreType st = null; if ((tok.Type == MySQL51Parser.AT1) && ((i + 1) <= t.TokenStopIndex) && ((tok2 = cts.Get(i + 1)).Type == MySQL51Parser.ID)) { string id = string.Format("@{0}", tok2.Text ); // TODO: What about qualified names line a.b? There's no way to add variables like that (until we support triggers and new.col). if (vars.TryGetValue(id, out st)) { i++; sb.Append(StoreType.WrapValue(st.Value)); } } else if (((tok.Type == MySQL51Parser.NEW) || ( Cmp( tok.Text, "old" ) == 0 )) && ((i + 1) <= t.TokenStopIndex) && ( cts.Get( i + 1 ).Type == MySQL51Parser.DOT ) && ( ( i + 2 ) <= t.TokenStopIndex ) && ( ( tok2 = cts.Get( i + 2 ) ).Type == MySQL51Parser.ID )) { string id = string.Format("{0}.{1}", tok.Text, tok2.Text ); if (vars.TryGetValue(id, out st)) { i += 2; sb.Append(StoreType.WrapValue(st.Value)); } else { sb.Append(tok.Text); } } else if (vars.TryGetValue(tok.Text, out st)) { sb.Append(StoreType.WrapValue(st.Value)); } else { sb.Append(tok.Text); } } return sb.ToString(); }
public object Eval(string expression, RoutineScope rs) { CommonTokenStream cts; StringBuilder sb = new StringBuilder(); MySQL51Parser.program_return pr = this.ParseSql( string.Format("select {0}", expression), false, out sb, out cts); return Eval((CommonTree)((CommonTree)pr.Tree).GetChild(0), cts, rs); }
private object Eval(CommonTree t, CommonTokenStream cts, RoutineScope rs) { string expr = ReplaceVarsWithValues(t, cts, rs); return ExecuteScalar(expr); }
/// <summary> /// Instruments a routine. /// </summary> /// <param name="routine"></param> private void InstrumentRoutine( RoutineInfo routine ) { /* * - Parse formal arguments. * - Parse begin block. * - For each begin block parse declare's, then instructions. * - When finish parsing tree, make a backup. * - Then instrument it. * */ // Parse args StringBuilder sb = new StringBuilder(); CommonTokenStream tokenStream; MySQL51Parser.program_return tree = ParseSql(routine.SourceCode, false, out sb, out tokenStream); routine.ParsedTree = ( CommonTree )( tree.Tree ); if (routine.ParsedTree.IsNil) { routine.ParsedTree = ( CommonTree )routine.ParsedTree.GetChild(0); } routine.TokenStream = tokenStream; Dictionary<string, StoreType> args = ParseArgs( routine.ParsedTree ); // Parse Begin block CommonTree beginEnd = GetBeginEnd(routine.ParsedTree); // There won't be always a begin/end block. if (beginEnd == null) { // begin/end block StringBuilder sbNewRoutineSql = new StringBuilder(); ITree lastChild = routine.ParsedTree.GetChild(routine.ParsedTree.ChildCount - 1); ConcatTokens(sbNewRoutineSql, routine.TokenStream, 0, lastChild.TokenStartIndex - 1); sbNewRoutineSql.AppendLine(); sbNewRoutineSql.AppendLine("begin"); ConcatTokens(sbNewRoutineSql, routine.TokenStream, lastChild.TokenStartIndex, lastChild.TokenStopIndex); if ( Cmp( routine.TokenStream.Get( lastChild.TokenStopIndex ).Text, ";" ) != 0 ) { sbNewRoutineSql.Append(';'); } sbNewRoutineSql.AppendLine(); sbNewRoutineSql.AppendLine( "end" ); StringBuilder sbErrors; tokenStream = null; string sSql = sbNewRoutineSql.ToString(); tree = ParseSql(sSql, false, out sbErrors, out tokenStream); routine.TokenStream = tokenStream; routine.SourceCode = sSql; routine.ParsedTree = (CommonTree)(tree.Tree); if (routine.ParsedTree.IsNil) { routine.ParsedTree = (CommonTree)routine.ParsedTree.GetChild(0); } // No need to reparse args, just begin/end beginEnd = GetBeginEnd(routine.ParsedTree); } // Get declare variables, sessions, new & old ParseDeclares(beginEnd, args); ParseSessions(tokenStream, args); RegisterInternalVars(args); if (routine.Type == RoutineInfoType.Trigger) { RegisterNewOldVars(routine, args); } // forces to backup the routine if (Scope.Count == 0) { RoutineScope tempScope = new RoutineScope(); tempScope.OwningRoutine = routine; tempScope.GetFileName(); } else CurrentScope.GetFileName(); // generate instrumentation code StringBuilder preInscode = new StringBuilder(); // track internal variables... // Workaround: row_count() is affected by any precious SET statement, so must be first preInscode.AppendFormat("set {0} = row_count();", VAR_DBG_ROW_COUNT); preInscode.AppendLine(); preInscode.AppendFormat("set {0} = last_insert_id();", VAR_DBG_LAST_INSERT_ID); preInscode.AppendLine(); preInscode.AppendFormat("set {0} = found_rows();", VAR_DBG_FOUND_ROWS); preInscode.AppendLine(); preInscode.Append(" call `serversidedebugger`.`SetDebugScopeVar`( {3}, {0}, '@@@lineno', {1} );" ).AppendLine(); preInscode.Append(" call `serversidedebugger`.`SetDebugScopeVar`( {3}, {0}, '@@@colno', {4} );").AppendLine(); // ...and user variables foreach (StoreType st in args.Values) { if (st.VarKind == VarKindEnum.Internal) continue; preInscode.AppendLine("call `serversidedebugger`.`SetDebugScopeVar`( {3}, {0}, "). AppendFormat("'{0}', cast( {0} as binary ) );", st.Name).AppendLine(); } StringBuilder postInscode = new StringBuilder(); foreach (StoreType st in args.Values) { if (st.VarKind == VarKindEnum.Internal) continue; postInscode.AppendFormat( @"set {0} = ( select cast( `serversidedebugger`.`DebugScope`.`VarValue` as {1} ) from `serversidedebugger`.`DebugScope` where `serversidedebugger`.`DebugScope`.`DebugSessionId` = {2} ", st.Name, st.GetCastExpressionFromBinary(), DebugSessionId). AppendFormat(" and `serversidedebugger`.`DebugScope`.`DebugScopeLevel` = {0} and `serversidedebugger`.`DebugScope`.`VarName` = '{1}' and `serversidedebugger`.`DebugScope`.`Id` = ( select max( `serversidedebugger`.`DebugScope`.`Id` ) from `serversidedebugger`.`DebugScope` where `serversidedebugger`.`DebugScope`.`DebugSessionId` = {2} and `serversidedebugger`.`DebugScope`.`DebugScopeLevel` = {0} and `serversidedebugger`.`DebugScope`.`VarName` = '{1}' ));", "{0}", st.Name, DebugSessionId ); postInscode.AppendLine(); } routine.PreInstrumentationCode = preInscode.ToString(); routine.PostInstrumentationCode = postInscode.ToString(); routine.Locals = args; // finally instrument. StringBuilder sql = new StringBuilder(); // Instrumeting code... GenerateInstrumentedCode(routine, sql); routine.InstrumentedSourceCode = sql.ToString(); string sqlDrop = string.Format("drop {0} {1}", routine.Type.ToString(), routine.Name); string db = _utilCon.Database; if (!string.IsNullOrEmpty(routine.Schema)) { ExecuteRaw(string.Format("use `{0}`", routine.Schema )); } ExecuteRaw(sqlDrop); try { ExecuteRaw(string.Format("delimiter //\n{0}\n//", routine.InstrumentedSourceCode)); } catch (Exception) { // In case of exception restore previous non-instrumented version. ExecuteRaw(string.Format("delimiter //\n{0}\n//", routine.SourceCode)); throw; } finally { ExecuteRaw(string.Format("use `{0}`", db)); } }