From 19876b22d0d83e4f6ffe26646b68608ce542ab0d Mon Sep 17 00:00:00 2001 From: Glen Parker Date: Sat, 21 Dec 2013 15:18:54 -0800 Subject: [PATCH 1/2] NpgsqlCommand.AppendCommandReplacingParameterValues() and operators @@, @> and <@. Fix bug that would turn operators @@, @> and <@ in ::, :>, and <: respectively. Add tests for said operator butchery. --- Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs | 81 +++++++++++++++----------- tests/CommandTests.cs | 27 +++++++++ 2 files changed, 73 insertions(+), 35 deletions(-) diff --git a/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs b/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs index 0524aeebde..647e8bc363 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; @@ -705,12 +709,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 +738,47 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int break; case TokenType.Colon : - switch (ch) + if (IsParamNameChar(ch)) { - case ':': - currTokenLen++; - - break; + currTokenType = TokenType.Param; - default: - if (currTokenLen == 1) - { - currTokenType = TokenType.Param; + currTokenBeg = currCharOfs; + currTokenLen = 0; + paramMarker = ':'; - currTokenBeg = currCharOfs; - currTokenLen = 0; - paramMarker = ':'; - } - else - { - dest.WriteString(src.Substring(currTokenBeg, currTokenLen)); + // Re-evaluate this character + goto ProcessCharacter; + } + else + { + // Demote to the unknown token type and continue. + currTokenType = TokenType.None; + currTokenLen++; + } - currTokenType = TokenType.None; + break; - currTokenBeg = currCharOfs; - currTokenLen = 0; - } + case TokenType.FullTextMatchOp : + if (lastChar == '@' && IsParamNameChar(ch)) + { + currTokenType = TokenType.Param; - // Re-evaluate this character - goto ProcessCharacter; + currTokenBeg = currCharOfs; + currTokenLen = 0; + paramMarker = '@'; + // Re-evaluate this character + goto ProcessCharacter; + } + else + { + // Demote to the unknown token type and continue. + currTokenType = TokenType.None; + currTokenLen++; } break; - } lastChar = ch; 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()); + } } } From 79e1ebcd23c7e42fa06447dff61af7fbd0e53c79 Mon Sep 17 00:00:00 2001 From: Glen Parker Date: Sun, 22 Dec 2013 13:35:19 -0800 Subject: [PATCH 2/2] NpgsqlCommand.AppendCommandReplacingParameterValues() Remove a couple bits of redundant and ineffective code in the scanner. --- Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs | 110 +++++++++++-------------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs b/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs index 647e8bc363..2f33084dce 100644 --- a/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs +++ b/Npgsql/Npgsql/NpgsqlCommand.Rewrite.cs @@ -660,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; @@ -740,14 +733,12 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int case TokenType.Colon : if (IsParamNameChar(ch)) { + // Switch to parameter name token, include this character. currTokenType = TokenType.Param; currTokenBeg = currCharOfs; - currTokenLen = 0; + currTokenLen = 1; paramMarker = ':'; - - // Re-evaluate this character - goto ProcessCharacter; } else { @@ -761,14 +752,12 @@ private void AppendCommandReplacingParameterValues(Stream dest, string src, int case TokenType.FullTextMatchOp : if (lastChar == '@' && IsParamNameChar(ch)) { + // Switch to parameter name token, include this character. currTokenType = TokenType.Param; currTokenBeg = currCharOfs; - currTokenLen = 0; + currTokenLen = 1; paramMarker = '@'; - - // Re-evaluate this character - goto ProcessCharacter; } else { @@ -787,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;