diff --git a/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs b/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs index 0524aeebde..2f33084dce 100644 --- a/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs +++ b/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs @@ -558,7 +558,8 @@ private enum TokenType None, Quoted, Param, - Colon + Colon, + FullTextMatchOp } /// @@ -579,6 +580,7 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int int currTokenBeg = begin; int currTokenLen = 0; Dictionary paramOrdinalMap = null; + int end = begin + length; if (prepare) { @@ -590,7 +592,7 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int } } - for (int currCharOfs = begin ; currCharOfs < begin + length ; currCharOfs++) + for (int currCharOfs = begin ; currCharOfs < end ; currCharOfs++) { char ch = src[currCharOfs]; @@ -602,7 +604,7 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int case TokenType.None : switch (ch) { - case '\'': + case '\'' : if (currTokenLen > 0) { dest.WriteString(src.Substring(currTokenBeg, currTokenLen)); @@ -615,7 +617,8 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int break; - case ':': + case ':' : + if (currTokenLen > 0) { dest.WriteString(src.Substring(currTokenBeg, currTokenLen)); } @@ -627,20 +630,21 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int break; - case '@': + case '<' : + case '@' : + if (currTokenLen > 0) { dest.WriteString(src.Substring(currTokenBeg, currTokenLen)); } - currTokenType = TokenType.Param; + currTokenType = TokenType.FullTextMatchOp; - currTokenBeg = currCharOfs + 1; - currTokenLen = 0; - paramMarker = '@'; + currTokenBeg = currCharOfs; + currTokenLen = 1; break; - default: + default : currTokenLen++; break; @@ -656,40 +660,33 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int } else { - if (currTokenLen == 0) - { - dest.WriteBytes((byte)ASCIIBytes.Colon); - } - else - { - string paramName = src.Substring(currTokenBeg, currTokenLen); - NpgsqlParameter parameter; - bool wroteParam = false; + string paramName = src.Substring(currTokenBeg, currTokenLen); + NpgsqlParameter parameter; + bool wroteParam = false; - if (parameters.TryGetValue(paramName, out parameter)) + if (parameters.TryGetValue(paramName, out parameter)) + { + if ( + (parameter.Direction == ParameterDirection.Input) || + (parameter.Direction == ParameterDirection.InputOutput) + ) { - if ( - (parameter.Direction == ParameterDirection.Input) || - (parameter.Direction == ParameterDirection.InputOutput) - ) + if (prepare) { - if (prepare) - { - AppendParameterPlaceHolder(dest, parameter, paramOrdinalMap[parameter]); - } - else - { - AppendParameterValue(dest, parameter); - } + AppendParameterPlaceHolder(dest, parameter, paramOrdinalMap[parameter]); + } + else + { + AppendParameterValue(dest, parameter); } - - wroteParam = true; } - if (! wroteParam) - { - dest.WriteString("{0}{1}", paramMarker, paramName); - } + wroteParam = true; + } + + if (! wroteParam) + { + dest.WriteString("{0}{1}", paramMarker, paramName); } currTokenType = TokenType.None; @@ -705,12 +702,12 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int case TokenType.Quoted : switch (ch) { - case '\'': + case '\'' : currTokenLen++; break; - default: + default : if (currTokenLen > 1 && lastChar == '\'') { dest.WriteString(src.Substring(currTokenBeg, currTokenLen)); @@ -734,40 +731,43 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int break; case TokenType.Colon : - switch (ch) + if (IsParamNameChar(ch)) { - case ':': - currTokenLen++; - - break; - - default: - if (currTokenLen == 1) - { - currTokenType = TokenType.Param; - - currTokenBeg = currCharOfs; - currTokenLen = 0; - paramMarker = ':'; - } - else - { - dest.WriteString(src.Substring(currTokenBeg, currTokenLen)); + // Switch to parameter name token, include this character. + currTokenType = TokenType.Param; - currTokenType = TokenType.None; + currTokenBeg = currCharOfs; + currTokenLen = 1; + paramMarker = ':'; + } + else + { + // Demote to the unknown token type and continue. + currTokenType = TokenType.None; + currTokenLen++; + } - currTokenBeg = currCharOfs; - currTokenLen = 0; - } + break; - // Re-evaluate this character - goto ProcessCharacter; + case TokenType.FullTextMatchOp : + if (lastChar == '@' && IsParamNameChar(ch)) + { + // Switch to parameter name token, include this character. + currTokenType = TokenType.Param; + currTokenBeg = currCharOfs; + currTokenLen = 1; + paramMarker = '@'; + } + else + { + // Demote to the unknown token type and continue. + currTokenType = TokenType.None; + currTokenLen++; } break; - } lastChar = ch; @@ -776,40 +776,33 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int switch (currTokenType) { case TokenType.Param : - if (currTokenLen == 0) - { - dest.WriteBytes((byte)ASCIIBytes.Colon); - } - else - { - string paramName = src.Substring(currTokenBeg, currTokenLen); - NpgsqlParameter parameter; - bool wroteParam = false; + string paramName = src.Substring(currTokenBeg, currTokenLen); + NpgsqlParameter parameter; + bool wroteParam = false; - if (parameters.TryGetValue(paramName, out parameter)) + if (parameters.TryGetValue(paramName, out parameter)) + { + if ( + (parameter.Direction == ParameterDirection.Input) || + (parameter.Direction == ParameterDirection.InputOutput) + ) { - if ( - (parameter.Direction == ParameterDirection.Input) || - (parameter.Direction == ParameterDirection.InputOutput) - ) + if (prepare) { - if (prepare) - { - AppendParameterPlaceHolder(dest, parameter, paramOrdinalMap[parameter]); - } - else - { - AppendParameterValue(dest, parameter); - } + AppendParameterPlaceHolder(dest, parameter, paramOrdinalMap[parameter]); + } + else + { + AppendParameterValue(dest, parameter); } - - wroteParam = true; } - if (! wroteParam) - { - dest.WriteString("{0}{1}", paramMarker, paramName); - } + wroteParam = true; + } + + if (! wroteParam) + { + dest.WriteString("{0}{1}", paramMarker, paramName); } break; diff --git a/tests/CommandTests.cs b/tests/CommandTests.cs index 7b66d35227..3ddc35fea8 100644 --- a/tests/CommandTests.cs +++ b/tests/CommandTests.cs @@ -3481,5 +3481,32 @@ public void DataTypeTests() Assert.AreEqual(typeof(NpgsqlTimeTZ), result.GetType()); */ } + + [Test] + // Target NpgsqlCommand.AppendCommandReplacingParameterValues()'s handling of operator @@. + public void Operator_At_At_RewriteTest() + { + NpgsqlCommand cmd = new NpgsqlCommand("SELECT to_tsvector('fat cats ate rats') @@ to_tsquery('cat & rat')", Conn); + + Assert.IsTrue((bool)cmd.ExecuteScalar()); + } + + [Test] + // Target NpgsqlCommand.AppendCommandReplacingParameterValues()'s handling of operator @>. + public void Operator_At_GT_RewriteTest() + { + NpgsqlCommand cmd = new NpgsqlCommand("SELECT 'cat'::tsquery @> 'cat & rat'::tsquery", Conn); + + Assert.IsFalse((bool)cmd.ExecuteScalar()); + } + + [Test] + // Target NpgsqlCommand.AppendCommandReplacingParameterValues()'s handling of operator <@. + public void Operator_LT_At_RewriteTest() + { + NpgsqlCommand cmd = new NpgsqlCommand("SELECT 'cat'::tsquery <@ 'cat & rat'::tsquery", Conn); + + Assert.IsTrue((bool)cmd.ExecuteScalar()); + } } }