TryGetValue() 공개 메소드

Gets a value indicating whether a NpgsqlParameter with the specified parameter name exists in the collection.
public TryGetValue ( string parameterName, Npgsql.NpgsqlParameter &parameter ) : bool
parameterName string The name of the NpgsqlParameter object to find.
parameter Npgsql.NpgsqlParameter A reference to the requested parameter is returned in this out param if it is found in the list. This value is null if the parameter is not found.
리턴 bool
예제 #1
0
        void ParseRawQuery(
            ReadOnlySpan <char> sql,
            NpgsqlParameterCollection parameters,
            List <NpgsqlStatement> statements,
            bool deriveParameters)
        {
            Debug.Assert(deriveParameters == false || parameters.Count == 0);

            NpgsqlStatement statement      = null !;
            var             statementIndex = -1;

            MoveToNextStatement();

            var currCharOfs = 0;
            var end         = sql.Length;
            var ch          = '\0';
            int dollarTagStart;
            int dollarTagEnd;
            var currTokenBeg      = 0;
            var blockCommentLevel = 0;
            var parenthesisLevel  = 0;

None:
            if (currCharOfs >= end)
            {
                goto Finish;
            }
            var lastChar = ch;

            ch = sql[currCharOfs++];
NoneContinue:
            for (; ; lastChar = ch, ch = sql[currCharOfs++])
            {
                switch (ch)
                {
                case '/':
                    goto BlockCommentBegin;

                case '-':
                    goto LineCommentBegin;

                case '\'':
                    goto Quoted;

                case '$':
                    if (!IsIdentifier(lastChar))
                    {
                        goto DollarQuotedStart;
                    }
                    else
                    {
                        break;
                    }

                case '"':
                    goto DoubleQuoted;

                case ':':
                    if (lastChar != ':')
                    {
                        goto ParamStart;
                    }
                    else
                    {
                        break;
                    }

                case '@':
                    if (lastChar != '@')
                    {
                        goto ParamStart;
                    }
                    else
                    {
                        break;
                    }

                case ';':
                    if (parenthesisLevel == 0)
                    {
                        goto SemiColon;
                    }
                    break;

                case '(':
                    parenthesisLevel++;
                    break;

                case ')':
                    parenthesisLevel--;
                    break;

                case 'e':
                case 'E':
                    if (!IsLetter(lastChar))
                    {
                        goto EscapedStart;
                    }
                    else
                    {
                        break;
                    }
                }

                if (currCharOfs >= end)
                {
                    goto Finish;
                }
            }

ParamStart:
            if (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs];
                if (IsParamNameChar(ch))
                {
                    if (currCharOfs - 1 > currTokenBeg)
                    {
                        _rewrittenSql.Append(sql.Slice(currTokenBeg, currCharOfs - 1 - currTokenBeg));
                    }
                    currTokenBeg = currCharOfs++ - 1;
                    goto Param;
                }
                currCharOfs++;
                goto NoneContinue;
            }
            goto Finish;

Param:
            // We have already at least one character of the param name
            for (;;)
            {
                lastChar = ch;
                if (currCharOfs >= end || !IsParamNameChar(ch = sql[currCharOfs]))
                {
                    var paramName = sql.Slice(currTokenBeg + 1, currCharOfs - (currTokenBeg + 1)).ToString();

                    if (!_paramIndexMap.TryGetValue(paramName, out var index))
                    {
                        // Parameter hasn't been seen before in this query
                        if (!parameters.TryGetValue(paramName, out var parameter))
                        {
                            if (deriveParameters)
                            {
                                parameter = new NpgsqlParameter {
                                    ParameterName = paramName
                                };
                                parameters.Add(parameter);
                            }
                            else
                            {
                                // Parameter placeholder does not match a parameter on this command.
                                // Leave the text as it was in the SQL, it may not be a an actual placeholder
                                _rewrittenSql.Append(sql.Slice(currTokenBeg, currCharOfs - currTokenBeg));
                                currTokenBeg = currCharOfs;
                                if (currCharOfs >= end)
                                {
                                    goto Finish;
                                }

                                currCharOfs++;
                                goto NoneContinue;
                            }
                        }

                        if (!parameter.IsInputDirection)
                        {
                            throw new Exception($"Parameter '{paramName}' referenced in SQL but is an out-only parameter");
                        }

                        statement.InputParameters.Add(parameter);
                        index = _paramIndexMap[paramName] = statement.InputParameters.Count;
                    }
                    _rewrittenSql.Append('$');
                    _rewrittenSql.Append(index);
                    currTokenBeg = currCharOfs;

                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }

                    currCharOfs++;
                    goto NoneContinue;
                }

                currCharOfs++;
            }

Quoted:
            while (currCharOfs < end)
            {
                if (sql[currCharOfs++] == '\'')
                {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

DoubleQuoted:
            while (currCharOfs < end)
            {
                if (sql[currCharOfs++] == '"')
                {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

EscapedStart:
            if (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs++];
                if (ch == '\'')
                {
                    goto Escaped;
                }
                goto NoneContinue;
            }
            goto Finish;

Escaped:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                switch (ch)
                {
                case '\'':
                    goto MaybeConcatenatedEscaped;

                case '\\':
                {
                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }
                    currCharOfs++;
                    break;
                }
                }
            }
            goto Finish;

MaybeConcatenatedEscaped:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                switch (ch)
                {
                case '\r':
                case '\n':
                    goto MaybeConcatenatedEscaped2;

                case ' ':
                case '\t':
                case '\f':
                    continue;

                default:
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

MaybeConcatenatedEscaped2:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                switch (ch)
                {
                case '\'':
                    goto Escaped;

                case '-':
                {
                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }
                    ch = sql[currCharOfs++];
                    if (ch == '-')
                    {
                        goto MaybeConcatenatedEscapeAfterComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;
                }

                case ' ':
                case '\t':
                case '\n':
                case '\r':
                case '\f':
                    continue;

                default:
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

MaybeConcatenatedEscapeAfterComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto MaybeConcatenatedEscaped2;
                }
            }
            goto Finish;

DollarQuotedStart:
            if (currCharOfs < end)
            {
                ch = sql[currCharOfs];
                if (ch == '$')
                {
                    // Empty tag
                    dollarTagStart = dollarTagEnd = currCharOfs;
                    currCharOfs++;
                    goto DollarQuoted;
                }
                if (IsIdentifierStart(ch))
                {
                    dollarTagStart = currCharOfs;
                    currCharOfs++;
                    goto DollarQuotedInFirstDelim;
                }
                lastChar = '$';
                currCharOfs++;
                goto NoneContinue;
            }
            goto Finish;

DollarQuotedInFirstDelim:
            while (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs++];
                if (ch == '$')
                {
                    dollarTagEnd = currCharOfs - 1;
                    goto DollarQuoted;
                }
                if (!IsDollarTagIdentifier(ch))
                {
                    goto NoneContinue;
                }
            }
            goto Finish;

DollarQuoted:
            var tag = sql.Slice(dollarTagStart - 1, dollarTagEnd - dollarTagStart + 2);
            var pos = sql.Slice(dollarTagEnd + 1).IndexOf(tag);

            if (pos == -1)
            {
                currCharOfs = end;
                goto Finish;
            }
            pos        += dollarTagEnd + 1; // If the substring is found adjust the position to be relative to the entire span
            currCharOfs = pos + dollarTagEnd - dollarTagStart + 2;
            ch          = '\0';
            goto None;

LineCommentBegin:
            if (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '-')
                {
                    goto LineComment;
                }
                lastChar = '\0';
                goto NoneContinue;
            }
            goto Finish;

LineComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto None;
                }
            }
            goto Finish;

BlockCommentBegin:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '*')
                {
                    blockCommentLevel++;
                    goto BlockComment;
                }
                if (ch != '/')
                {
                    if (blockCommentLevel > 0)
                    {
                        goto BlockComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

BlockComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                switch (ch)
                {
                case '*':
                    goto BlockCommentEnd;

                case '/':
                    goto BlockCommentBegin;
                }
            }
            goto Finish;

BlockCommentEnd:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '/')
                {
                    if (--blockCommentLevel > 0)
                    {
                        goto BlockComment;
                    }
                    goto None;
                }
                if (ch != '*')
                {
                    goto BlockComment;
                }
            }
            goto Finish;

SemiColon:
            _rewrittenSql.Append(sql.Slice(currTokenBeg, currCharOfs - currTokenBeg - 1));
            statement.SQL = _rewrittenSql.ToString();
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs];
                if (char.IsWhiteSpace(ch))
                {
                    currCharOfs++;
                    continue;
                }
                // TODO: Handle end of line comment? Although psql doesn't seem to handle them...

                currTokenBeg = currCharOfs;
                if (_rewrittenSql.Length > 0)
                {
                    MoveToNextStatement();
                }
                goto None;
            }
            if (statements.Count > statementIndex + 1)
            {
                statements.RemoveRange(statementIndex + 1, statements.Count - (statementIndex + 1));
            }
            return;

Finish:
            _rewrittenSql.Append(sql.Slice(currTokenBeg, end - currTokenBeg));
            statement.SQL = _rewrittenSql.ToString();
            if (statements.Count > statementIndex + 1)
            {
                statements.RemoveRange(statementIndex + 1, statements.Count - (statementIndex + 1));
            }

            void MoveToNextStatement()
            {
                statementIndex++;
                if (statements.Count > statementIndex)
                {
                    statement = statements[statementIndex];
                    statement.Reset();
                }
                else
                {
                    statement = new NpgsqlStatement();
                    statements.Add(statement);
                }
                _paramIndexMap.Clear();
                _rewrittenSql.Clear();
            }
        }
예제 #2
0
        /// <summary>
        /// Receives a raw SQL query as passed in by the user, and performs some processing necessary
        /// before sending to the backend.
        /// This includes doing parameter placebolder processing (@p => $1), and splitting the query
        /// up by semicolons if needed (SELECT 1; SELECT 2)
        /// </summary>
        /// <param name="sql">Raw user-provided query.</param>
        /// <param name="standardConformantStrings">Whether the PostgreSQL session is configured to use standard conformant strings.</param>
        /// <param name="parameters">The parameters configured on the <see cref="NpgsqlCommand"/> of this query.</param>
        /// <param name="queries">An empty list to be populated with the queries parsed by this method</param>
        static internal void ParseRawQuery(string sql, bool standardConformantStrings, NpgsqlParameterCollection parameters, List <QueryDetails> queries)
        {
            Contract.Requires(sql != null);
            Contract.Requires(queries != null && !queries.Any());

            var currCharOfs       = 0;
            var end               = sql.Length;
            var ch                = '\0';
            var lastChar          = '\0';
            var dollarTagStart    = 0;
            var dollarTagEnd      = 0;
            var currTokenBeg      = 0;
            var blockCommentLevel = 0;

            queries.Clear();
            // TODO: Recycle
            var paramIndexMap     = new Dictionary <string, int>();
            var currentSql        = new StringWriter();
            var currentParameters = new List <NpgsqlParameter>();

None:
            if (currCharOfs >= end)
            {
                goto Finish;
            }
            lastChar = ch;
            ch       = sql[currCharOfs++];
NoneContinue:
            for (; ; lastChar = ch, ch = sql[currCharOfs++])
            {
                switch (ch)
                {
                case '/':
                    goto BlockCommentBegin;

                case '-':
                    goto LineCommentBegin;

                case '\'':
                    if (standardConformantStrings)
                    {
                        goto Quoted;
                    }
                    else
                    {
                        goto Escaped;
                    }

                case '$':
                    if (!IsIdentifier(lastChar))
                    {
                        goto DollarQuotedStart;
                    }
                    else
                    {
                        break;
                    }

                case '"':
                    goto DoubleQuoted;

                case ':':
                    if (lastChar != ':')
                    {
                        goto ParamStart;
                    }
                    else
                    {
                        break;
                    }

                case '@':
                    if (lastChar != '@')
                    {
                        goto ParamStart;
                    }
                    else
                    {
                        break;
                    }

                case ';':
                    goto SemiColon;

                case 'e':
                case 'E':
                    if (!IsLetter(lastChar))
                    {
                        goto EscapedStart;
                    }
                    else
                    {
                        break;
                    }
                }

                if (currCharOfs >= end)
                {
                    goto Finish;
                }
            }

ParamStart:
            if (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs];
                if (IsParamNameChar(ch))
                {
                    if (currCharOfs - 1 > currTokenBeg)
                    {
                        currentSql.Write(sql.Substring(currTokenBeg, currCharOfs - 1 - currTokenBeg));
                    }
                    currTokenBeg = currCharOfs++ - 1;
                    goto Param;
                }
                else
                {
                    currCharOfs++;
                    goto NoneContinue;
                }
            }
            goto Finish;

Param:
            // We have already at least one character of the param name
            for (; ;)
            {
                lastChar = ch;
                if (currCharOfs >= end || !IsParamNameChar(ch = sql[currCharOfs]))
                {
                    var paramName = sql.Substring(currTokenBeg, currCharOfs - currTokenBeg);

                    int index;
                    if (!paramIndexMap.TryGetValue(paramName, out index))
                    {
                        // Parameter hasn't been seen before in this query
                        NpgsqlParameter parameter;
                        if (!parameters.TryGetValue(paramName, out parameter))
                        {
                            throw new Exception(String.Format("Parameter '{0}' referenced in SQL but not found in parameter list", paramName));
                        }

                        if (!parameter.IsInputDirection)
                        {
                            throw new Exception(String.Format("Parameter '{0}' referenced in SQL but is an out-only parameter", paramName));
                        }

                        currentParameters.Add(parameter);
                        index = paramIndexMap[paramName] = currentParameters.Count;
                    }
                    currentSql.Write('$');
                    currentSql.Write(index);
                    currTokenBeg = currCharOfs;

                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }

                    currCharOfs++;
                    goto NoneContinue;
                }
                else
                {
                    currCharOfs++;
                }
            }

Quoted:
            while (currCharOfs < end)
            {
                if (sql[currCharOfs++] == '\'')
                {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

DoubleQuoted:
            while (currCharOfs < end)
            {
                if (sql[currCharOfs++] == '"')
                {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

EscapedStart:
            if (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs++];
                if (ch == '\'')
                {
                    goto Escaped;
                }
                goto NoneContinue;
            }
            goto Finish;

Escaped:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\'')
                {
                    goto MaybeConcatenatedEscaped;
                }
                if (ch == '\\')
                {
                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }
                    currCharOfs++;
                }
            }
            goto Finish;

MaybeConcatenatedEscaped:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto MaybeConcatenatedEscaped2;
                }
                if (ch != ' ' && ch != '\t' && ch != '\f')
                {
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

MaybeConcatenatedEscaped2:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\'')
                {
                    goto Escaped;
                }
                if (ch == '-')
                {
                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }
                    ch = sql[currCharOfs++];
                    if (ch == '-')
                    {
                        goto MaybeConcatenatedEscapeAfterComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;
                }
                if (ch != ' ' && ch != '\t' && ch != '\n' & ch != '\r' && ch != '\f')
                {
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

MaybeConcatenatedEscapeAfterComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto MaybeConcatenatedEscaped2;
                }
            }
            goto Finish;

DollarQuotedStart:
            if (currCharOfs < end)
            {
                ch = sql[currCharOfs];
                if (ch == '$')
                {
                    // Empty tag
                    dollarTagStart = dollarTagEnd = currCharOfs;
                    currCharOfs++;
                    goto DollarQuoted;
                }
                if (IsIdentifierStart(ch))
                {
                    dollarTagStart = currCharOfs;
                    currCharOfs++;
                    goto DollarQuotedInFirstDelim;
                }
                lastChar = '$';
                currCharOfs++;
                goto NoneContinue;
            }
            goto Finish;

DollarQuotedInFirstDelim:
            while (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs++];
                if (ch == '$')
                {
                    dollarTagEnd = currCharOfs - 1;
                    goto DollarQuoted;
                }
                if (!IsDollarTagIdentifier(ch))
                {
                    goto NoneContinue;
                }
            }
            goto Finish;

            DollarQuoted : {
                var tag = sql.Substring(dollarTagStart - 1, dollarTagEnd - dollarTagStart + 2);
                var pos = sql.IndexOf(tag, dollarTagEnd + 1); // Not linear time complexity, but that's probably not a problem, since PostgreSQL backend's isn't either
                if (pos == -1)
                {
                    currCharOfs = end;
                    goto Finish;
                }
                currCharOfs = pos + dollarTagEnd - dollarTagStart + 2;
                ch          = '\0';
                goto None;
            }

LineCommentBegin:
            if (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '-')
                {
                    goto LineComment;
                }
                lastChar = '\0';
                goto NoneContinue;
            }
            goto Finish;

LineComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto None;
                }
            }
            goto Finish;

BlockCommentBegin:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '*')
                {
                    blockCommentLevel++;
                    goto BlockComment;
                }
                if (ch != '/')
                {
                    if (blockCommentLevel > 0)
                    {
                        goto BlockComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

BlockComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '*')
                {
                    goto BlockCommentEnd;
                }
                if (ch == '/')
                {
                    goto BlockCommentBegin;
                }
            }
            goto Finish;

BlockCommentEnd:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '/')
                {
                    if (--blockCommentLevel > 0)
                    {
                        goto BlockComment;
                    }
                    goto None;
                }
                if (ch != '*')
                {
                    goto BlockComment;
                }
            }
            goto Finish;

SemiColon:
            currentSql.Write(sql.Substring(currTokenBeg, currCharOfs - currTokenBeg - 1));
            queries.Add(new QueryDetails(currentSql.ToString(), currentParameters));
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs];
                if (Char.IsWhiteSpace(ch))
                {
                    currCharOfs++;
                    continue;
                }
                // TODO: Handle end of line comment? Although psql doesn't seem to handle them...

                currTokenBeg = currCharOfs;
                paramIndexMap.Clear();
                if (queries.Count > NpgsqlCommand.MaxQueriesInMultiquery)
                {
                    throw new NotSupportedException(String.Format("A single command cannot contain more than {0} queries", NpgsqlCommand.MaxQueriesInMultiquery));
                }
                currentSql        = new StringWriter();
                currentParameters = new List <NpgsqlParameter>();
                goto None;
            }
            return;

Finish:
            currentSql.Write(sql.Substring(currTokenBeg, end - currTokenBeg));
            queries.Add(new QueryDetails(currentSql.ToString(), currentParameters));
        }
예제 #3
0
        /// <summary>
        /// Receives a raw SQL query as passed in by the user, and performs some processing necessary
        /// before sending to the backend.
        /// This includes doing parameter placebolder processing (@p => $1), and splitting the query
        /// up by semicolons if needed (SELECT 1; SELECT 2)
        /// </summary>
        /// <param name="sql">Raw user-provided query.</param>
        /// <param name="standardConformantStrings">Whether the PostgreSQL session is configured to use standard conformant strings.</param>
        /// <param name="parameters">The parameters configured on the <see cref="NpgsqlCommand"/> of this query.</param>
        /// <param name="queries">An empty list to be populated with the queries parsed by this method</param>
        static internal void ParseRawQuery(string sql, bool standardConformantStrings, NpgsqlParameterCollection parameters, List<QueryDetails> queries)
        {
            Contract.Requires(sql != null);
            Contract.Requires(queries != null && !queries.Any());

            var currCharOfs = 0;
            var end = sql.Length;
            var ch = '\0';
            var lastChar = '\0';
            var dollarTagStart = 0;
            var dollarTagEnd = 0;
            var currTokenBeg = 0;
            var blockCommentLevel = 0;

            queries.Clear();
            // TODO: Recycle
            var paramIndexMap = new Dictionary<string, int>();
            var currentSql = new StringWriter();
            var currentParameters = new List<NpgsqlParameter>();

        None:
            if (currCharOfs >= end) {
                goto Finish;
            }
            lastChar = ch;
            ch = sql[currCharOfs++];
        NoneContinue:
            for (; ; lastChar = ch, ch = sql[currCharOfs++]) {
                switch (ch) {
                case '/':
                    goto BlockCommentBegin;
                case '-':
                    goto LineCommentBegin;
                case '\'':
                    if (standardConformantStrings)
                        goto Quoted;
                    else
                        goto Escaped;
                case '$':
                    if (!IsIdentifier(lastChar))
                        goto DollarQuotedStart;
                    else
                        break;
                case '"':
                    goto DoubleQuoted;
                case ':':
                    if (lastChar != ':')
                        goto ParamStart;
                    else
                        break;
                case '@':
                    if (lastChar != '@')
                        goto ParamStart;
                    else
                        break;
                case ';':
                    goto SemiColon;

                case 'e':
                case 'E':
                    if (!IsLetter(lastChar))
                        goto EscapedStart;
                    else
                        break;
                }

                if (currCharOfs >= end) {
                    goto Finish;
                }
            }

        ParamStart:
            if (currCharOfs < end) {
                lastChar = ch;
                ch = sql[currCharOfs];
                if (IsParamNameChar(ch)) {
                    if (currCharOfs - 1 > currTokenBeg) {
                        currentSql.Write(sql.Substring(currTokenBeg, currCharOfs - 1 - currTokenBeg));
                    }
                    currTokenBeg = currCharOfs++ - 1;
                    goto Param;
                } else {
                    currCharOfs++;
                    goto NoneContinue;
                }
            }
            goto Finish;

        Param:
            // We have already at least one character of the param name
            for (; ; ) {
                lastChar = ch;
                if (currCharOfs >= end || !IsParamNameChar(ch = sql[currCharOfs])) {
                    var paramName = sql.Substring(currTokenBeg, currCharOfs - currTokenBeg);

                    int index;
                    if (!paramIndexMap.TryGetValue(paramName, out index)) {
                        // Parameter hasn't been seen before in this query
                        NpgsqlParameter parameter;
                        if (!parameters.TryGetValue(paramName, out parameter)) {
                            throw new Exception(String.Format("Parameter '{0}' referenced in SQL but not found in parameter list", paramName));
                        }

                        if (!parameter.IsInputDirection) {
                            throw new Exception(String.Format("Parameter '{0}' referenced in SQL but is an out-only parameter", paramName));
                        }

                        currentParameters.Add(parameter);
                        index = paramIndexMap[paramName] = currentParameters.Count;
                    }
                    currentSql.Write('$');
                    currentSql.Write(index);
                    currTokenBeg = currCharOfs;

                    if (currCharOfs >= end) {
                        goto Finish;
                    }

                    currCharOfs++;
                    goto NoneContinue;
                } else {
                    currCharOfs++;
                }
            }

        Quoted:
            while (currCharOfs < end) {
                if (sql[currCharOfs++] == '\'') {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

        DoubleQuoted:
            while (currCharOfs < end) {
                if (sql[currCharOfs++] == '"') {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

        EscapedStart:
            if (currCharOfs < end) {
                lastChar = ch;
                ch = sql[currCharOfs++];
                if (ch == '\'') {
                    goto Escaped;
                }
                goto NoneContinue;
            }
            goto Finish;

        Escaped:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '\'') {
                    goto MaybeConcatenatedEscaped;
                }
                if (ch == '\\') {
                    if (currCharOfs >= end) {
                        goto Finish;
                    }
                    currCharOfs++;
                }
            }
            goto Finish;

        MaybeConcatenatedEscaped:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n') {
                    goto MaybeConcatenatedEscaped2;
                }
                if (ch != ' ' && ch != '\t' && ch != '\f') {
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

        MaybeConcatenatedEscaped2:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '\'') {
                    goto Escaped;
                }
                if (ch == '-') {
                    if (currCharOfs >= end) {
                        goto Finish;
                    }
                    ch = sql[currCharOfs++];
                    if (ch == '-') {
                        goto MaybeConcatenatedEscapeAfterComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;

                }
                if (ch != ' ' && ch != '\t' && ch != '\n' & ch != '\r' && ch != '\f') {
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

        MaybeConcatenatedEscapeAfterComment:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n') {
                    goto MaybeConcatenatedEscaped2;
                }
            }
            goto Finish;

        DollarQuotedStart:
            if (currCharOfs < end) {
                ch = sql[currCharOfs];
                if (ch == '$') {
                    // Empty tag
                    dollarTagStart = dollarTagEnd = currCharOfs;
                    currCharOfs++;
                    goto DollarQuoted;
                }
                if (IsIdentifierStart(ch)) {
                    dollarTagStart = currCharOfs;
                    currCharOfs++;
                    goto DollarQuotedInFirstDelim;
                }
                lastChar = '$';
                currCharOfs++;
                goto NoneContinue;
            }
            goto Finish;

        DollarQuotedInFirstDelim:
            while (currCharOfs < end) {
                lastChar = ch;
                ch = sql[currCharOfs++];
                if (ch == '$') {
                    dollarTagEnd = currCharOfs - 1;
                    goto DollarQuoted;
                }
                if (!IsDollarTagIdentifier(ch)) {
                    goto NoneContinue;
                }
            }
            goto Finish;

        DollarQuoted: {
                var tag = sql.Substring(dollarTagStart - 1, dollarTagEnd - dollarTagStart + 2);
                var pos = sql.IndexOf(tag, dollarTagEnd + 1); // Not linear time complexity, but that's probably not a problem, since PostgreSQL backend's isn't either
                if (pos == -1) {
                    currCharOfs = end;
                    goto Finish;
                }
                currCharOfs = pos + dollarTagEnd - dollarTagStart + 2;
                ch = '\0';
                goto None;
            }

        LineCommentBegin:
            if (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '-') {
                    goto LineComment;
                }
                lastChar = '\0';
                goto NoneContinue;
            }
            goto Finish;

        LineComment:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n') {
                    goto None;
                }
            }
            goto Finish;

        BlockCommentBegin:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '*') {
                    blockCommentLevel++;
                    goto BlockComment;
                }
                if (ch != '/') {
                    if (blockCommentLevel > 0) {
                        goto BlockComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

        BlockComment:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '*') {
                    goto BlockCommentEnd;
                }
                if (ch == '/') {
                    goto BlockCommentBegin;
                }
            }
            goto Finish;

        BlockCommentEnd:
            while (currCharOfs < end) {
                ch = sql[currCharOfs++];
                if (ch == '/') {
                    if (--blockCommentLevel > 0) {
                        goto BlockComment;
                    }
                    goto None;
                }
                if (ch != '*') {
                    goto BlockComment;
                }
            }
            goto Finish;

        SemiColon:
            currentSql.Write(sql.Substring(currTokenBeg, currCharOfs - currTokenBeg - 1));
            queries.Add(new QueryDetails(currentSql.ToString(), currentParameters));
            while (currCharOfs < end) {
                ch = sql[currCharOfs];
                if (Char.IsWhiteSpace(ch)) {
                    currCharOfs++;
                    continue;
                }
                // TODO: Handle end of line comment? Although psql doesn't seem to handle them...

                currTokenBeg = currCharOfs;
                paramIndexMap.Clear();
                if (queries.Count > NpgsqlCommand.MaxQueriesInMultiquery) {
                    throw new NotSupportedException(String.Format("A single command cannot contain more than {0} queries", NpgsqlCommand.MaxQueriesInMultiquery));
                }
                currentSql = new StringWriter();
                currentParameters = new List<NpgsqlParameter>();
                goto None;
            }
            return;

        Finish:
            currentSql.Write(sql.Substring(currTokenBeg, end - currTokenBeg));
            queries.Add(new QueryDetails(currentSql.ToString(), currentParameters));
        }
예제 #4
0
        /// <summary>
        /// Receives a raw SQL query as passed in by the user, and performs some processing necessary
        /// before sending to the backend.
        /// This includes doing parameter placebolder processing (@p => $1), and splitting the query
        /// up by semicolons if needed (SELECT 1; SELECT 2)
        /// </summary>
        /// <param name="sql">Raw user-provided query.</param>
        /// <param name="standardConformantStrings">Whether the PostgreSQL session is configured to use standard conformant strings.</param>
        /// <param name="parameters">The parameters configured on the <see cref="NpgsqlCommand"/> of this query.</param>
        /// <param name="statements">An empty list to be populated with the statements parsed by this method</param>
        internal void ParseRawQuery(string sql, bool standardConformantStrings, NpgsqlParameterCollection parameters, List <NpgsqlStatement> statements)
        {
            Debug.Assert(sql != null);
            Debug.Assert(statements != null);

            _statements     = statements;
            _statementIndex = -1;
            MoveToNextStatement();

            var currCharOfs = 0;
            var end         = sql.Length;
            var ch          = '\0';
            int dollarTagStart;
            int dollarTagEnd;
            var currTokenBeg      = 0;
            var blockCommentLevel = 0;
            var parenthesisLevel  = 0;

None:
            if (currCharOfs >= end)
            {
                goto Finish;
            }
            var lastChar = ch;

            ch = sql[currCharOfs++];
NoneContinue:
            for (; ; lastChar = ch, ch = sql[currCharOfs++])
            {
                switch (ch)
                {
                case '/':
                    goto BlockCommentBegin;

                case '-':
                    goto LineCommentBegin;

                case '\'':
                    if (standardConformantStrings)
                    {
                        goto Quoted;
                    }
                    else
                    {
                        goto Escaped;
                    }

                case '$':
                    if (!IsIdentifier(lastChar))
                    {
                        goto DollarQuotedStart;
                    }
                    else
                    {
                        break;
                    }

                case '"':
                    goto DoubleQuoted;

                case ':':
                    if (lastChar != ':')
                    {
                        goto ParamStart;
                    }
                    else
                    {
                        break;
                    }

                case '@':
                    if (lastChar != '@')
                    {
                        goto ParamStart;
                    }
                    else
                    {
                        break;
                    }

                case ';':
                    if (parenthesisLevel == 0)
                    {
                        goto SemiColon;
                    }
                    break;

                case '(':
                    parenthesisLevel++;
                    break;

                case ')':
                    parenthesisLevel--;
                    break;

                case 'e':
                case 'E':
                    if (!IsLetter(lastChar))
                    {
                        goto EscapedStart;
                    }
                    else
                    {
                        break;
                    }
                }

                if (currCharOfs >= end)
                {
                    goto Finish;
                }
            }

ParamStart:
            if (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs];
                if (IsParamNameChar(ch))
                {
                    if (currCharOfs - 1 > currTokenBeg)
                    {
                        _rewrittenSql.Append(sql.Substring(currTokenBeg, currCharOfs - 1 - currTokenBeg));
                    }
                    currTokenBeg = currCharOfs++ - 1;
                    goto Param;
                }
                else
                {
                    currCharOfs++;
                    goto NoneContinue;
                }
            }
            goto Finish;

Param:
            // We have already at least one character of the param name
            for (;;)
            {
                lastChar = ch;
                if (currCharOfs >= end || !IsParamNameChar(ch = sql[currCharOfs]))
                {
                    var paramName = sql.Substring(currTokenBeg, currCharOfs - currTokenBeg);

                    int index;
                    if (!_paramIndexMap.TryGetValue(paramName, out index))
                    {
                        // Parameter hasn't been seen before in this query
                        NpgsqlParameter parameter;
                        if (!parameters.TryGetValue(paramName, out parameter))
                        {
                            _rewrittenSql.Append(paramName);
                            currTokenBeg = currCharOfs;
                            if (currCharOfs >= end)
                            {
                                goto Finish;
                            }

                            currCharOfs++;
                            goto NoneContinue;
                        }

                        if (!parameter.IsInputDirection)
                        {
                            throw new Exception($"Parameter '{paramName}' referenced in SQL but is an out-only parameter");
                        }

                        _statement.InputParameters.Add(parameter);
                        index = _paramIndexMap[paramName] = _statement.InputParameters.Count;
                    }
                    _rewrittenSql.Append('$');
                    _rewrittenSql.Append(index);
                    currTokenBeg = currCharOfs;

                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }

                    currCharOfs++;
                    goto NoneContinue;
                }
                else
                {
                    currCharOfs++;
                }
            }

Quoted:
            while (currCharOfs < end)
            {
                if (sql[currCharOfs++] == '\'')
                {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

DoubleQuoted:
            while (currCharOfs < end)
            {
                if (sql[currCharOfs++] == '"')
                {
                    ch = '\0';
                    goto None;
                }
            }
            goto Finish;

EscapedStart:
            if (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs++];
                if (ch == '\'')
                {
                    goto Escaped;
                }
                goto NoneContinue;
            }
            goto Finish;

Escaped:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\'')
                {
                    goto MaybeConcatenatedEscaped;
                }
                if (ch == '\\')
                {
                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }
                    currCharOfs++;
                }
            }
            goto Finish;

MaybeConcatenatedEscaped:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto MaybeConcatenatedEscaped2;
                }
                if (ch != ' ' && ch != '\t' && ch != '\f')
                {
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

MaybeConcatenatedEscaped2:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\'')
                {
                    goto Escaped;
                }
                if (ch == '-')
                {
                    if (currCharOfs >= end)
                    {
                        goto Finish;
                    }
                    ch = sql[currCharOfs++];
                    if (ch == '-')
                    {
                        goto MaybeConcatenatedEscapeAfterComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;
                }
                if (ch != ' ' && ch != '\t' && ch != '\n' & ch != '\r' && ch != '\f')
                {
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

MaybeConcatenatedEscapeAfterComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto MaybeConcatenatedEscaped2;
                }
            }
            goto Finish;

DollarQuotedStart:
            if (currCharOfs < end)
            {
                ch = sql[currCharOfs];
                if (ch == '$')
                {
                    // Empty tag
                    dollarTagStart = dollarTagEnd = currCharOfs;
                    currCharOfs++;
                    goto DollarQuoted;
                }
                if (IsIdentifierStart(ch))
                {
                    dollarTagStart = currCharOfs;
                    currCharOfs++;
                    goto DollarQuotedInFirstDelim;
                }
                lastChar = '$';
                currCharOfs++;
                goto NoneContinue;
            }
            goto Finish;

DollarQuotedInFirstDelim:
            while (currCharOfs < end)
            {
                lastChar = ch;
                ch       = sql[currCharOfs++];
                if (ch == '$')
                {
                    dollarTagEnd = currCharOfs - 1;
                    goto DollarQuoted;
                }
                if (!IsDollarTagIdentifier(ch))
                {
                    goto NoneContinue;
                }
            }
            goto Finish;

            DollarQuoted : {
                var tag = sql.Substring(dollarTagStart - 1, dollarTagEnd - dollarTagStart + 2);
                var pos = sql.IndexOf(tag, dollarTagEnd + 1); // Not linear time complexity, but that's probably not a problem, since PostgreSQL backend's isn't either
                if (pos == -1)
                {
                    currCharOfs = end;
                    goto Finish;
                }
                currCharOfs = pos + dollarTagEnd - dollarTagStart + 2;
                ch          = '\0';
                goto None;
            }

LineCommentBegin:
            if (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '-')
                {
                    goto LineComment;
                }
                lastChar = '\0';
                goto NoneContinue;
            }
            goto Finish;

LineComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '\r' || ch == '\n')
                {
                    goto None;
                }
            }
            goto Finish;

BlockCommentBegin:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '*')
                {
                    blockCommentLevel++;
                    goto BlockComment;
                }
                if (ch != '/')
                {
                    if (blockCommentLevel > 0)
                    {
                        goto BlockComment;
                    }
                    lastChar = '\0';
                    goto NoneContinue;
                }
            }
            goto Finish;

BlockComment:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '*')
                {
                    goto BlockCommentEnd;
                }
                if (ch == '/')
                {
                    goto BlockCommentBegin;
                }
            }
            goto Finish;

BlockCommentEnd:
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs++];
                if (ch == '/')
                {
                    if (--blockCommentLevel > 0)
                    {
                        goto BlockComment;
                    }
                    goto None;
                }
                if (ch != '*')
                {
                    goto BlockComment;
                }
            }
            goto Finish;

SemiColon:
            _rewrittenSql.Append(sql.Substring(currTokenBeg, currCharOfs - currTokenBeg - 1));
            _statement.SQL = _rewrittenSql.ToString();
            while (currCharOfs < end)
            {
                ch = sql[currCharOfs];
                if (char.IsWhiteSpace(ch))
                {
                    currCharOfs++;
                    continue;
                }
                // TODO: Handle end of line comment? Although psql doesn't seem to handle them...

                currTokenBeg = currCharOfs;
                if (_rewrittenSql.Length > 0)
                {
                    MoveToNextStatement();
                }
                goto None;
            }
            return;

Finish:
            _rewrittenSql.Append(sql.Substring(currTokenBeg, end - currTokenBeg));
            _statement.SQL = _rewrittenSql.ToString();
            if (statements.Count > _statementIndex + 1)
            {
                statements.RemoveRange(_statementIndex + 1, statements.Count - (_statementIndex + 1));
            }
        }