Ejemplo n.º 1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="SqlBatchItem"/> class.
 /// </summary>
 /// <param name="text">The text.</param>
 /// <param name="source">The source.</param>
 public SqlBatchItem(string text, IBatchSource source)
 {
     // Remove any line terminators
     this.Text       = text.TrimEnd(Environment.NewLine.ToCharArray());
     this.Source     = source;
     this.LineNumber = source.CurrentLineNumber;
 }
        /// <summary>
        /// Creates the invalid tokenizer state exception.
        /// </summary>
        /// <param name="tokenizerState">State of the tokenizer.</param>
        /// <param name="batchSource">The batch source.</param>
        /// <returns>A derived <see cref="ParserException"/> based on the state</returns>
        internal static ParserException CreateInvalidTokenizerStateException(
            TokenizerState tokenizerState,
            IBatchSource batchSource)
        {
            switch (tokenizerState)
            {
            case TokenizerState.SingleQuoteString:
            case TokenizerState.DoubleQuoteString:

                return(new UnclosedStringLiteralException(tokenizerState, batchSource));

            case TokenizerState.BlockComment:

                return(new UnclosedBlockCommentException(tokenizerState, batchSource));

            default:

                return(new ParserException($"Unexpected state {tokenizerState}", batchSource));
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="T:Firefly.SqlCmdParser.InputSourceChangedEventArgs" /> class.
 /// </summary>
 /// <param name="nodeNumber">The execution node number.</param>
 /// <param name="newSource">The new source.</param>
 /// <param name="outputDestination">The output destination.</param>
 /// <inheritdoc />
 internal InputSourceChangedEventArgs(int nodeNumber, IBatchSource newSource, OutputDestination outputDestination)
     : base(nodeNumber, outputDestination)
 {
     this.Source = newSource;
 }
Ejemplo n.º 4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="UnclosedStringLiteralException"/> class.
 /// </summary>
 /// <param name="tokenizerState">State of the tokenizer.</param>
 /// <param name="batchSource">The batch source.</param>
 internal UnclosedStringLiteralException(TokenizerState tokenizerState, IBatchSource batchSource)
     : base(tokenizerState, batchSource)
 {
 }
Ejemplo n.º 5
0
 /// <summary>
 /// Initializes a new instance of the <see cref="UnclosedBlockCommentException"/> class.
 /// </summary>
 /// <param name="tokenizerState">State of the tokenizer.</param>
 /// <param name="batchSource">The batch source.</param>
 internal UnclosedBlockCommentException(TokenizerState tokenizerState, IBatchSource batchSource)
     : base(tokenizerState, batchSource)
 {
 }
Ejemplo n.º 6
0
 /// <summary>
 /// Sets the input source.
 /// </summary>
 /// <param name="newSource">The new source.</param>
 private void SetInputSource(IBatchSource newSource)
 {
     this.sourceStack.Push(newSource);
     this.InputSourceChanged?.Invoke(this, new InputSourceChangedEventArgs(this.nodeNumber, newSource, this.CommandExecuter.StdoutDestination));
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Parses this instance.
        /// </summary>
        /// <exception cref="ParserException">Syntax error or unrecognized command directive
        /// or
        /// or</exception>
        public void Parse()
        {
            var sw = new Stopwatch();

            sw.Start();

            try
            {
                if (this.runConfiguration.OutputFile != null)
                {
                    this.CommandExecuter.Out(OutputDestination.File, this.runConfiguration.OutputFile);
                }

                this.InputSourceChanged += this.CommandExecuter.OnInputSourceChanged;

                this.CommandExecuter.ConnectWithConnectionString(this.runConfiguration.ConnectionString);

                this.SetInputSource(this.runConfiguration.InitialBatchSource);
                var tokenizer = new Tokenizer();

                while (this.sourceStack.Count > 0)
                {
                    this.Source = this.sourceStack.Peek();

                    if (this.currentBatch == null)
                    {
                        this.currentBatch = new SqlBatch();
                    }

                    var line = this.Source.GetNextLine();

                    if (line == null)
                    {
                        // End of input source
                        if (tokenizer.State != TokenizerState.None)
                        {
                            // Incomplete parse
                            throw new ParserException(tokenizer.State, this.Source);
                        }

                        this.sourceStack.Pop();
                        continue;
                    }

                    tokenizer.AddLine(line);
                    Token token;

                    var isStartOfLine = true;

                    while ((token = tokenizer.GetNextToken()) != null)
                    {
                        if (tokenizer.State == TokenizerState.None)
                        {
                            // Not in a comment or a multi-line string literal
                            if (isStartOfLine)
                            {
                                // Command matching only when we are at the beginning of a line
                                var commandMatched = false;

                                foreach (var commandMatcher in this.commandMatchers)
                                {
                                    if (!commandMatcher.IsMatch(token.TokenValue))
                                    {
                                        continue;
                                    }

                                    switch (commandMatcher)
                                    {
                                    case GoCommand go:

                                        if (string.IsNullOrWhiteSpace(this.currentBatch.Sql))
                                        {
                                            break;
                                        }

                                        try
                                        {
                                            // Increment first, so a failed batch is counted
                                            ++this.BatchCount;
                                            this.CommandExecuter.ProcessBatch(this.currentBatch, go.ExecutionCount);
                                        }
                                        finally
                                        {
                                            this.previousBatch = this.currentBatch;
                                            this.currentBatch  = new SqlBatch();
                                        }

                                        break;

                                    case SetvarCommand setvar:

                                        if (setvar.VarValue == null)
                                        {
                                            this.VariableResolver.DeleteVariable(setvar.VarValue);
                                        }
                                        else
                                        {
                                            this.VariableResolver.SetVariable(setvar.VarName, setvar.VarValue);
                                        }

                                        break;

                                    case IncludeCommand include:

                                        string resolvedPath;

                                        if (Path.IsPathRooted(include.Filename))
                                        {
                                            resolvedPath = include.Filename;
                                        }
                                        else if (!include.Filename.Contains(Path.DirectorySeparatorChar))
                                        {
                                            // Assume the included file is in the same location as the current batch source, or current directory if no source.
                                            resolvedPath = Path.Combine(
                                                File.Exists(this.Source.Filename)
                                                        ? Path.GetDirectoryName(this.Source.Filename)
                                                        : this.currentDirectoryResolver.GetCurrentDirectory(),
                                                include.Filename);
                                        }
                                        else
                                        {
                                            resolvedPath = include.Filename;
                                        }

                                        this.SetInputSource(this.CommandExecuter.IncludeFileName(resolvedPath));

                                        break;

                                    case ConnectCommand connect:

                                        connect.ResolveConnectionParameters(this.VariableResolver);

                                        if (connect.Server != null)
                                        {
                                            // Execute whatever we have so far on the current connection before reconnecting
                                            try
                                            {
                                                this.CommandExecuter.ProcessBatch(this.currentBatch, 1);
                                            }
                                            finally
                                            {
                                                this.previousBatch = this.currentBatch;
                                                this.currentBatch  = new SqlBatch();
                                                ++this.BatchCount;
                                            }

                                            this.CommandExecuter.Connect(
                                                connect.Timeout,
                                                connect.Server,
                                                connect.Username,
                                                connect.Password);
                                        }

                                        break;

                                    case EdCommand _:

                                        if (!this.disableInteractiveCommands && Environment.UserInteractive)
                                        {
                                            var currentBatchStr = this.currentBatch.Sql;
                                            var batchToEdit     =
                                                string.IsNullOrEmpty(currentBatchStr)
                                                        ? this.previousBatch.Sql
                                                        : currentBatchStr;

                                            if (!string.IsNullOrWhiteSpace(batchToEdit))
                                            {
                                                var editedBatch = this.CommandExecuter.Ed(batchToEdit);

                                                if (editedBatch != null)
                                                {
                                                    this.SetInputSource(editedBatch);
                                                    this.currentBatch = new SqlBatch();
                                                }
                                            }
                                        }

                                        break;

                                    case ExitCommand exit:

                                        if (exit.ExitImmediately)
                                        {
                                            return;
                                        }

                                        this.CommandExecuter.Exit(this.currentBatch, exit.ExitBatch);
                                        return;

                                    case ErrorCommand error:

                                        this.CommandExecuter.Error(
                                            error.OutputDestination,
                                            FileParameterCommand.GetNodeFilepath(this.nodeNumber, error.Filename));
                                        break;

                                    case OutCommand @out:

                                        this.CommandExecuter.Out(
                                            @out.OutputDestination,
                                            FileParameterCommand.GetNodeFilepath(this.nodeNumber, @out.Filename));
                                        break;

                                    // ReSharper disable once UnusedVariable
                                    case ServerListCommand serverList:

                                        if (!this.disableInteractiveCommands)
                                        {
                                            this.CommandExecuter.ServerList();
                                        }

                                        break;

                                    // ReSharper disable once UnusedVariable
                                    case HelpCommand help:

                                        if (!this.disableInteractiveCommands)
                                        {
                                            this.CommandExecuter.Help();
                                        }

                                        break;

                                    case ResetCommand _:

                                        if (!this.disableInteractiveCommands)
                                        {
                                            this.CommandExecuter.Reset();
                                            this.currentBatch.Clear();
                                        }

                                        break;

                                    case ListCommand _:

                                        if (!this.disableInteractiveCommands)
                                        {
                                            this.CommandExecuter.List(this.currentBatch.Sql);
                                        }

                                        break;

                                    case ListVarCommand _:

                                        if (!this.disableInteractiveCommands)
                                        {
                                            this.CommandExecuter.ListVar(this.VariableResolver.Variables);
                                        }

                                        break;

                                    case ShellCommand shell:

                                        this.CommandExecuter.ExecuteShellCommand(shell.Command);
                                        break;

                                    case QuitCommand _:

                                        this.CommandExecuter.Quit();

                                        // Stop parsing
                                        return;

                                    case OnErrorCommand onError:

                                        this.CommandExecuter.OnError(onError.ErrorAction);
                                        break;

                                    case InvalidCommand _:

                                        throw new ParserException(
                                                  "Syntax error or unrecognized command directive",
                                                  this.Source);
                                    }

                                    commandMatched = true;
                                    break;
                                }

                                if (commandMatched)
                                {
                                    // Jump to next line, appending a blank line where we removed the command
                                    break;
                                }
                            }

                            this.AppendToken(token);
                        }
                        else
                        {
                            // In a comment, just append to batch buffer
                            this.AppendToken(token);
                        }

                        isStartOfLine = false;
                    }
                }

                if (tokenizer.State != TokenizerState.None)
                {
                    // Incomplete parse
                    throw ParserException.CreateInvalidTokenizerStateException(tokenizer.State, this.Source);
                }

                // If we have anything left, it's the last batch
                if (!string.IsNullOrWhiteSpace(this.currentBatch.Sql))
                {
                    // Increment first, so a failed batch is counted
                    ++this.BatchCount;
                    this.CommandExecuter.ProcessBatch(this.currentBatch, 1);
                }
            }
            catch (ParserException)
            {
                throw;
            }
            catch (SqlException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new ParserException(ex.Message, ex, this.Source);
            }
            finally
            {
                // Output statistics
                sw.Stop();

                var batches = this.BatchCount == 1 ? "batch" : "batches";
                var errors  = this.CommandExecuter.ErrorCount == 1 ? "error" : "errors";

                this.CommandExecuter.WriteStdoutMessage(
                    string.Join(
                        Environment.NewLine,
                        Environment.NewLine,
                        $"{this.BatchCount} {batches} processed in {sw.Elapsed.Minutes} min, {sw.Elapsed.Seconds}.{sw.Elapsed.Milliseconds:D3} sec.",
                        $"{this.CommandExecuter.ErrorCount} SQL {errors} in execution."));
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="ParserException"/> class.
 /// </summary>
 /// <param name="message">The message.</param>
 /// <param name="inner">The inner exception.</param>
 /// <param name="batchSource">The batch source.</param>
 /// <inheritdoc />
 internal ParserException(string message, Exception inner, IBatchSource batchSource)
     : base(message, inner)
 {
     this.batchSource = batchSource;
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="ParserException"/> class.
 /// </summary>
 /// <param name="state">The state.</param>
 /// <param name="batchSource">The batch source.</param>
 /// <inheritdoc />
 internal ParserException(TokenizerState state, IBatchSource batchSource)
     : base(FormatTokenizerStateError(state))
 {
     this.batchSource = batchSource;
 }
Ejemplo n.º 10
0
 /// <summary>
 /// Appends the specified text to the internal buffer.
 /// </summary>
 /// <param name="text">The text.</param>
 /// <param name="source">The source.</param>
 internal void Append(string text, IBatchSource source)
 {
     this.batchItems.Add(new SqlBatchItem(text.TrimEnd(Environment.NewLine.ToCharArray()), source));
 }