private void ParseProcedureBody(MyCatSchemaCollection parametersTable, string body,
                                        MyCatSchemaRow row, string nameToRestrict)
        {
            List <string> modes = new List <string>(new string[3] {
                "IN", "OUT", "INOUT"
            });

            string sqlMode = row["SQL_MODE"].ToString();

            int            pos       = 1;
            MyCatTokenizer tokenizer = new MyCatTokenizer(body);

            tokenizer.AnsiQuotes       = sqlMode.IndexOf("ANSI_QUOTES") != -1;
            tokenizer.BackslashEscapes = sqlMode.IndexOf("NO_BACKSLASH_ESCAPES") == -1;
            tokenizer.ReturnComments   = false;
            string token = tokenizer.NextToken();

            // this block will scan for the opening paren while also determining
            // if this routine is a function.  If so, then we need to add a
            // parameter row for the return parameter since it is ordinal position
            // 0 and should appear first.
            while (token != "(")
            {
                if (String.Compare(token, "FUNCTION", StringComparison.OrdinalIgnoreCase) == 0 &&
                    nameToRestrict == null)
                {
                    parametersTable.AddRow();
                    InitParameterRow(row, parametersTable.Rows[0]);
                }
                token = tokenizer.NextToken();
            }
            token = tokenizer.NextToken(); // now move to the next token past the (

            while (token != ")")
            {
                MyCatSchemaRow parmRow = parametersTable.NewRow();
                InitParameterRow(row, parmRow);
                parmRow["ORDINAL_POSITION"] = pos++;

                // handle mode and name for the parameter
                string mode = StringUtility.ToUpperInvariant(token);
                if (!tokenizer.Quoted && modes.Contains(mode))
                {
                    parmRow["PARAMETER_MODE"] = mode;
                    token = tokenizer.NextToken();
                }
                if (tokenizer.Quoted)
                {
                    token = token.Substring(1, token.Length - 2);
                }
                parmRow["PARAMETER_NAME"] = token;

                // now parse data type
                token = ParseDataType(parmRow, tokenizer);
                if (token == ",")
                {
                    token = tokenizer.NextToken();
                }

                // now determine if we should include this row after all
                // we need to parse it before this check so we are correctly
                // positioned for the next parameter
                if (nameToRestrict == null ||
                    String.Compare(parmRow["PARAMETER_NAME"].ToString(), nameToRestrict, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    parametersTable.Rows.Add(parmRow);
                }
            }

            // now parse out the return parameter if there is one.
            token = StringUtility.ToUpperInvariant(tokenizer.NextToken());
            if (String.Compare(token, "RETURNS", StringComparison.OrdinalIgnoreCase) == 0)
            {
                MyCatSchemaRow parameterRow = parametersTable.Rows[0];
                parameterRow["PARAMETER_NAME"] = "RETURN_VALUE";
                ParseDataType(parameterRow, tokenizer);
            }
        }
        /// <summary>
        ///  Parses out the elements of a procedure parameter data type.
        /// </summary>
        private string ParseDataType(MyCatSchemaRow row, MyCatTokenizer tokenizer)
        {
            StringBuilder dtd = new StringBuilder(
                StringUtility.ToUpperInvariant(tokenizer.NextToken()));

            row["DATA_TYPE"] = dtd.ToString();
            string type = row["DATA_TYPE"].ToString();

            string token = tokenizer.NextToken();

            if (token == "(")
            {
                token = tokenizer.ReadParenthesis();
                dtd.AppendFormat(CultureInfo.InvariantCulture, "{0}", token);
                if (type != "ENUM" && type != "SET")
                {
                    ParseDataTypeSize(row, token);
                }
                token = tokenizer.NextToken();
            }
            else
            {
                dtd.Append(GetDataTypeDefaults(type, row));
            }

            while (token != ")" &&
                   token != "," &&
                   String.Compare(token, "begin", StringComparison.OrdinalIgnoreCase) != 0 &&
                   String.Compare(token, "return", StringComparison.OrdinalIgnoreCase) != 0)
            {
                if (String.Compare(token, "CHARACTER", StringComparison.OrdinalIgnoreCase) == 0 ||
                    String.Compare(token, "BINARY", StringComparison.OrdinalIgnoreCase) == 0)
                {
                } // we don't need to do anything with this
                else if (String.Compare(token, "SET", StringComparison.OrdinalIgnoreCase) == 0 ||
                         String.Compare(token, "CHARSET", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    row["CHARACTER_SET_NAME"] = tokenizer.NextToken();
                }
                else if (String.Compare(token, "ASCII", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    row["CHARACTER_SET_NAME"] = "latin1";
                }
                else if (String.Compare(token, "UNICODE", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    row["CHARACTER_SET_NAME"] = "ucs2";
                }
                else if (String.Compare(token, "COLLATE", StringComparison.OrdinalIgnoreCase) == 0)
                {
                    row["COLLATION_NAME"] = tokenizer.NextToken();
                }
                else
                {
                    dtd.AppendFormat(CultureInfo.InvariantCulture, " {0}", token);
                }
                token = tokenizer.NextToken();
            }

            if (dtd.Length > 0)
            {
                row["DTD_IDENTIFIER"] = dtd.ToString();
            }

            // now default the collation if one wasn't given
            if (string.IsNullOrEmpty(( string )row["COLLATION_NAME"]) &&
                !string.IsNullOrEmpty(( string )row["CHARACTER_SET_NAME"]))
            {
                row["COLLATION_NAME"] = CharSetMap.GetDefaultCollation(
                    row["CHARACTER_SET_NAME"].ToString(), connection);
            }

            // now set the octet length
            if (row["CHARACTER_MAXIMUM_LENGTH"] != null)
            {
                if (row["CHARACTER_SET_NAME"] == null)
                {
                    row["CHARACTER_SET_NAME"] = "";
                }
                row["CHARACTER_OCTET_LENGTH"] =
                    CharSetMap.GetMaxLength(( string )row["CHARACTER_SET_NAME"], connection) *
                    (int)row["CHARACTER_MAXIMUM_LENGTH"];
            }

            return(token);
        }
        /// <summary>
        /// Executes this instance.
        /// </summary>
        /// <returns>The number of statements executed as part of the script.</returns>
        public int Execute()
        {
            bool openedConnection = false;

            if (connection == null)
            {
                throw new InvalidOperationException(Resources.ConnectionNotSet);
            }
            if (query == null || query.Length == 0)
            {
                return(0);
            }

            // next we open up the connetion if it is not already open
            if (connection.State != ConnectionState.Open)
            {
                openedConnection = true;
                connection.Open();
            }

            // since we don't allow setting of parameters on a script we can
            // therefore safely allow the use of user variables.  no one should be using
            // this connection while we are using it so we can temporarily tell it
            // to allow the use of user variables
            bool allowUserVars = connection.Settings.AllowUserVariables;

            connection.Settings.AllowUserVariables = true;

            try
            {
                string mode = connection.driver.Property("sql_mode");
                mode = StringUtility.ToUpperInvariant(mode);
                bool ansiQuotes         = mode.IndexOf("ANSI_QUOTES") != -1;
                bool noBackslashEscapes = mode.IndexOf("NO_BACKSLASH_ESCAPES") != -1;

                // first we break the query up into smaller queries
                List <ScriptStatement> statements = BreakIntoStatements(ansiQuotes, noBackslashEscapes);

                int          count = 0;
                MyCatCommand cmd   = new MyCatCommand(null, connection);
                foreach (ScriptStatement statement in statements)
                {
                    if (String.IsNullOrEmpty(statement.text))
                    {
                        continue;
                    }
                    cmd.CommandText = statement.text;
                    try
                    {
                        cmd.ExecuteNonQuery();
                        count++;
                        OnQueryExecuted(statement);
                    }
                    catch (Exception ex)
                    {
                        if (Error == null)
                        {
                            throw;
                        }
                        if (!OnScriptError(ex))
                        {
                            break;
                        }
                    }
                }
                OnScriptCompleted();
                return(count);
            }
            finally
            {
                connection.Settings.AllowUserVariables = allowUserVars;
                if (openedConnection)
                {
                    connection.Close();
                }
            }
        }