From 6e9a93468bdb9b111e08f2fe0095a5c8f449c72f Mon Sep 17 00:00:00 2001 From: Rob Verschoor <91290800+robverschoor@users.noreply.github.com> Date: Mon, 18 Nov 2024 07:19:07 +0100 Subject: [PATCH] Support empty double-quoted string in SQL object body (#3121) This fixes the long-standing bug that a reference to a double-quoted empty string, when located in the body of a procedure, function or trigger, raises an error even though QUOTED_IDENTIFIER is OFF. This is due to the PG backend interpreting any double-quoted string as an identifier, whose length cannot be zero. It is fixed by replacing "" by '' at the ANTLR stage. Signed-off-by: Rob Verschoor --- contrib/babelfishpg_tsql/src/tsqlIface.cpp | 18 + .../empty_dq_string_in_object-vu-cleanup.out | 58 ++ .../empty_dq_string_in_object-vu-prepare.out | 31 + .../empty_dq_string_in_object-vu-verify.out | 549 ++++++++++++++++++ .../empty_dq_string_in_object-vu-cleanup.sql | 58 ++ .../empty_dq_string_in_object-vu-prepare.sql | 23 + .../empty_dq_string_in_object-vu-verify.sql | 291 ++++++++++ 7 files changed, 1028 insertions(+) create mode 100644 test/JDBC/expected/empty_dq_string_in_object-vu-cleanup.out create mode 100644 test/JDBC/expected/empty_dq_string_in_object-vu-prepare.out create mode 100644 test/JDBC/expected/empty_dq_string_in_object-vu-verify.out create mode 100644 test/JDBC/input/empty_dq_string_in_object-vu-cleanup.sql create mode 100644 test/JDBC/input/empty_dq_string_in_object-vu-prepare.sql create mode 100644 test/JDBC/input/empty_dq_string_in_object-vu-verify.sql diff --git a/contrib/babelfishpg_tsql/src/tsqlIface.cpp b/contrib/babelfishpg_tsql/src/tsqlIface.cpp index 4de42584ce..49e2d17e27 100644 --- a/contrib/babelfishpg_tsql/src/tsqlIface.cpp +++ b/contrib/babelfishpg_tsql/src/tsqlIface.cpp @@ -3349,6 +3349,24 @@ class tsqlMutator : public TSqlParserBaseListener } } + void enterChar_string(TSqlParser::Char_stringContext *ctx) override + { + std::string str = getFullText(ctx); + if ((str.front() == '"') && (str.back() == '"') && (str.length() == 2)) + { + // This means we have a double-quoted empty, zero-length string. The PG equivalent is a zero-length + // single-quoted string. + // Whenever we reference a double-quoted empty string inside the body of a T-SQL procedure, function or trigger, + // this is passed onto the PG backend where PG interprets double-quoted items as delimited identifiers. + // However those cannot be zero-length, so an error is then raised. + // By changing any double-quoted empty string to a single-quoted empty string we address this use case here. + // This also addresses any references outside those objects; those would otherwise have been rewritten as a + // single-quoted string by rewriteDoubleQuotedString() in the exitChar_string() functions, but doing it + // here preempts that and removes potential complexity from the rewriting in the mutator. + stream.setText(ctx->start->getStartIndex(), "''"); + } + } + // NB: similar code is in tsqlBuilder void exitChar_string(TSqlParser::Char_stringContext *ctx) override { diff --git a/test/JDBC/expected/empty_dq_string_in_object-vu-cleanup.out b/test/JDBC/expected/empty_dq_string_in_object-vu-cleanup.out new file mode 100644 index 0000000000..61bc654f33 --- /dev/null +++ b/test/JDBC/expected/empty_dq_string_in_object-vu-cleanup.out @@ -0,0 +1,58 @@ +drop table t1_empty_dq_string +go +drop table t2_empty_dq_string +go +drop table t3_empty_dq_string +go +drop table t4_empty_dq_string +go +drop table t5_empty_dq_string +go +drop table t6_empty_dq_string +go +drop table t7_empty_dq_string +go +drop procedure p1_empty_dq_string +go +drop function f1_empty_dq_string +go +drop procedure p2_empty_dq_string +go +drop function f2_empty_dq_string +go +drop procedure p2a_empty_dq_string +go +drop function f2a_empty_dq_string +go +drop procedure p2b_empty_dq_string +go +drop procedure p3_empty_dq_string +go +drop procedure p4_empty_dq_string +go +drop function f4_empty_dq_string +go +drop procedure p5_empty_dq_string +go +drop function f5_empty_dq_string +go +drop procedure p6_empty_dq_string +go +drop procedure p7_empty_dq_string +go +drop procedure p7a_empty_dq_string +go +drop procedure p7b_empty_dq_string +go +drop function f7b_empty_dq_string +go +drop procedure p7c_empty_dq_string +go +set quoted_identifier on +go +drop table "t8_empty_dq_string" +go +drop procedure "p8_empty_dq_string" +go +drop function "f8_empty_dq_string" +go diff --git a/test/JDBC/expected/empty_dq_string_in_object-vu-prepare.out b/test/JDBC/expected/empty_dq_string_in_object-vu-prepare.out new file mode 100644 index 0000000000..2afb2d9274 --- /dev/null +++ b/test/JDBC/expected/empty_dq_string_in_object-vu-prepare.out @@ -0,0 +1,31 @@ +create table t1_empty_dq_string(a int) +go +create table t2_empty_dq_string(a int) +go +create table t3_empty_dq_string(a int) +go +create table t4_empty_dq_string(a int) +go +create table t5_empty_dq_string(a int, b varchar(10)) +insert t5_empty_dq_string values(1, 'test 1') +go +~~ROW COUNT: 1~~ + +create table t6_empty_dq_string(a int, b varchar(10)) +insert t6_empty_dq_string values(1, 'test 1') +go +~~ROW COUNT: 1~~ + +create table t7_empty_dq_string(a int, b varchar(10)) +insert t7_empty_dq_string values(1, 'test 1') +go +~~ROW COUNT: 1~~ + + +set quoted_identifier on +go +create table "t8_empty_dq_string"("a" int, "b" varchar(10)) +insert "t8_empty_dq_string" values(1, 'test 1') +go +~~ROW COUNT: 1~~ + diff --git a/test/JDBC/expected/empty_dq_string_in_object-vu-verify.out b/test/JDBC/expected/empty_dq_string_in_object-vu-verify.out new file mode 100644 index 0000000000..517c6bb4a5 --- /dev/null +++ b/test/JDBC/expected/empty_dq_string_in_object-vu-verify.out @@ -0,0 +1,549 @@ + +-- Purpose: a double-quoted empty string inside a SQL object body should be treated as an empty string +set quoted_identifier off +go + +-- SELECT "" +select "" as empty_str +go +~~START~~ +varchar + +~~END~~ + +create procedure p1_empty_dq_string +as +select "" as empty_str +go +exec p1_empty_dq_string +go +~~START~~ +varchar + +~~END~~ + +create function f1_empty_dq_string() returns varchar(10) +as +begin +return "" +end +go +select dbo.f1_empty_dq_string(), len(dbo.f1_empty_dq_string()) +go +~~START~~ +varchar#!#int +#!#0 +~~END~~ + +create trigger tr1_empty_dq_string on t1_empty_dq_string for insert as +begin +select "" as empty_str +end +go +insert t1_empty_dq_string values(1) +go +~~START~~ +varchar + +~~END~~ + +~~ROW COUNT: 1~~ + + +-- variable declaration and assignment +declare @v varchar(10) = "" +set @v = "" +select @v +go +~~START~~ +varchar + +~~END~~ + +create procedure p2_empty_dq_string +as +declare @v varchar(10) = "" +set @v = "" +select @v +go +exec p2_empty_dq_string +go +~~START~~ +varchar + +~~END~~ + +create function f2_empty_dq_string() returns varchar(10) +as +begin +declare @v varchar(10) = "" +set @v = "" +return '['+@v+']' +end +go +select dbo.f2_empty_dq_string(), len(dbo.f2_empty_dq_string()) +go +~~START~~ +varchar#!#int +[]#!#2 +~~END~~ + +create trigger tr2_empty_dq_string on t2_empty_dq_string for insert as +begin +declare @v varchar(10) = "" +set @v = "" +select @v +end +go +insert t2_empty_dq_string values(1) +go +~~START~~ +varchar + +~~END~~ + +~~ROW COUNT: 1~~ + + +-- execute immediate +execute("") +go + +-- argument for procedure/function call +create procedure p2a_empty_dq_string @p varchar(10) +as +select '['+@p+']' as p, len(@p) as len +go +exec p2a_empty_dq_string "" +go +~~START~~ +varchar#!#int +[]#!#0 +~~END~~ + +exec p2a_empty_dq_string @p="" +go +~~START~~ +varchar#!#int +[]#!#0 +~~END~~ + +create function f2a_empty_dq_string(@p varchar(10)) returns varchar(10) +as +begin +return '['+@p+']' +end +go +select dbo.f2a_empty_dq_string(""), len(dbo.f2a_empty_dq_string("")) +go +~~START~~ +varchar#!#int +[]#!#2 +~~END~~ + + +create procedure p2b_empty_dq_string @p varchar(10) +as +exec p2a_empty_dq_string "" +exec p2a_empty_dq_string @p="" +select dbo.f2a_empty_dq_string(""), len(dbo.f2a_empty_dq_string("")) +go +exec p2b_empty_dq_string "" +go +~~START~~ +varchar#!#int +[]#!#0 +~~END~~ + +~~START~~ +varchar#!#int +[]#!#0 +~~END~~ + +~~START~~ +varchar#!#int +[]#!#2 +~~END~~ + + +-- print "" (not visible in JDBC test output) +print "" +go +create procedure p3_empty_dq_string +as +print "" +go +exec p3_empty_dq_string +go +create trigger tr3_empty_dq_string on t3_empty_dq_string for insert as +begin +select 'trigger' +print "" +end +go +insert t3_empty_dq_string values(1) +go +~~START~~ +varchar +trigger +~~END~~ + +~~ROW COUNT: 1~~ + + +-- usage in condition +if 'a' = "" select 'branch 1' else select 'branch 2' +go +~~START~~ +varchar +branch 2 +~~END~~ + +create procedure p4_empty_dq_string as +if 'a' = "" select 'branch 1' else select 'branch 2' +go +exec p4_empty_dq_string +go +~~START~~ +varchar +branch 2 +~~END~~ + +create function f4_empty_dq_string() returns int +as +begin +if 'a' = "" return 1 +return 2 +end +go +select dbo.f4_empty_dq_string() +go +~~START~~ +int +2 +~~END~~ + +create trigger tr4_empty_dq_string on t4_empty_dq_string for insert as +begin +if 'a' = "" select 'branch 1' else select 'branch 2' +end +go +insert t4_empty_dq_string values(1) +go +~~START~~ +varchar +branch 2 +~~END~~ + +~~ROW COUNT: 1~~ + + +-- usage in WHERE_clause +select * from t5_empty_dq_string where b = "" order by a +go +~~START~~ +int#!#varchar +~~END~~ + +create procedure p5_empty_dq_string as +select a, '['+b+']' as b from t5_empty_dq_string where b = "" order by a +go +exec p5_empty_dq_string +go +~~START~~ +int#!#varchar +~~END~~ + +create function f5_empty_dq_string() returns int +as +begin +declare @v int +select @v = count(*) from t5_empty_dq_string where b = "" +return '['+@v+']' +end +go +select dbo.f5_empty_dq_string() +go +~~START~~ +int +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: invalid input syntax for type integer: "[")~~ + +create trigger tr5_empty_dq_string on t5_empty_dq_string for insert as +begin +select a, '['+b+']' as b from t5_empty_dq_string where b = "" order by a +end +go +insert t5_empty_dq_string values(1, 'test 1') +go +~~START~~ +int#!#varchar +~~END~~ + +~~ROW COUNT: 1~~ + + +-- usage in insert +insert t6_empty_dq_string values (2, "") +go +~~ROW COUNT: 1~~ + +create procedure p6_empty_dq_string as +insert t6_empty_dq_string values (3, "") +go +exec p6_empty_dq_string +go +~~ROW COUNT: 1~~ + +select a, '['+b+']' as b from t6_empty_dq_string order by a +go +~~START~~ +int#!#varchar +1#!#[test 1] +2#!#[] +3#!#[] +~~END~~ + +create trigger tr6_empty_dq_string on t6_empty_dq_string for insert as +begin +insert t6_empty_dq_string values (4, "") +end +go +insert t6_empty_dq_string values(1, 'test 1') +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +select a, '['+b+']' as b from t6_empty_dq_string order by a +go +~~START~~ +int#!#varchar +1#!#[test 1] +1#!#[test 1] +2#!#[] +3#!#[] +4#!#[] +~~END~~ + + +-- usage in update +update t7_empty_dq_string set b = "" where a = 1 +go +~~ROW COUNT: 1~~ + +create procedure p7_empty_dq_string as +update t7_empty_dq_string set b = "" where a = 1 +go +exec p7_empty_dq_string +go +~~ROW COUNT: 1~~ + +select a, '['+b+']' as b from t7_empty_dq_string order by a +go +~~START~~ +int#!#varchar +1#!#[] +~~END~~ + +create trigger tr7_empty_dq_string on t7_empty_dq_string for insert as +begin +update t7_empty_dq_string set b = "" where a = 1 +end +go +insert t7_empty_dq_string values(2, 'test 2') +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +select a, '['+b+']' as b from t7_empty_dq_string order by a +go +~~START~~ +int#!#varchar +1#!#[] +2#!#[test 2] +~~END~~ + + +-- single-space string in update: not affected +create procedure p7a_empty_dq_string as +update t7_empty_dq_string set b = " " where a = 1 +go +exec p7a_empty_dq_string +go +~~ROW COUNT: 1~~ + +select a, '['+b+']' as b from t7_empty_dq_string order by a +go +~~START~~ +int#!#varchar +1#!#[ ] +2#!#[test 2] +~~END~~ + +create trigger tr7a_empty_dq_string on t7_empty_dq_string for insert as +begin +update t7_empty_dq_string set b = " " where a = 1 +end +go +insert t7_empty_dq_string values(3, 'test 3') +go +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +~~ROW COUNT: 1~~ + +select a, '['+b+']' as b from t7_empty_dq_string order by a +go +~~START~~ +int#!#varchar +1#!#[ ] +2#!#[test 2] +3#!#[test 3] +~~END~~ + + + +-- single-space string: argument for procedure/function call +create procedure p7b_empty_dq_string @p varchar(10) +as +select '['+@p+']' as p, len(@p) as len +go +exec p7b_empty_dq_string " " +go +~~START~~ +varchar#!#int +[ ]#!#0 +~~END~~ + +exec p7b_empty_dq_string @p=" " +go +~~START~~ +varchar#!#int +[ ]#!#0 +~~END~~ + +create function f7b_empty_dq_string(@p varchar(10)) returns varchar(10) +as +begin +return '['+@p+']' +end +go +select dbo.f7b_empty_dq_string(" "), len(dbo.f7b_empty_dq_string(" ")) +go +~~START~~ +varchar#!#int +[ ]#!#3 +~~END~~ + + +create procedure p7c_empty_dq_string @p varchar(10) +as +exec p7b_empty_dq_string " " +exec p7b_empty_dq_string @p=" " +select dbo.f7b_empty_dq_string(" "), len(dbo.f7b_empty_dq_string(" ")) +go +exec p7c_empty_dq_string " " +go +~~START~~ +varchar#!#int +[ ]#!#0 +~~END~~ + +~~START~~ +varchar#!#int +[ ]#!#0 +~~END~~ + +~~START~~ +varchar#!#int +[ ]#!#3 +~~END~~ + + +-- double-quoted identifier: not affected +set quoted_identifier on +go + +-- SELECT "" +select * from "t8_empty_dq_string" order by "a" +go +~~START~~ +int#!#varchar +1#!#test 1 +~~END~~ + +create procedure "p8_empty_dq_string" +as +select * from "t8_empty_dq_string" order by "a" +go +exec "p8_empty_dq_string" +go +~~START~~ +int#!#varchar +1#!#test 1 +~~END~~ + +create function "f8_empty_dq_string"() returns varchar(10) +as +begin +return '' +end +go +select dbo."f8_empty_dq_string"(), len(dbo."f8_empty_dq_string"()) +go +~~START~~ +varchar#!#int +#!#0 +~~END~~ + +create trigger "tr8_empty_dq_string" on "t8_empty_dq_string" for insert as +begin +select * from "t8_empty_dq_string" order by "a" +end +go +insert "t8_empty_dq_string" values(2, 'test 2') +go +~~START~~ +int#!#varchar +1#!#test 1 +2#!#test 2 +~~END~~ + +~~ROW COUNT: 1~~ + +select * from "t8_empty_dq_string" order by "a" +go +~~START~~ +int#!#varchar +1#!#test 1 +2#!#test 2 +~~END~~ + + +-- double-quoted identifier of length 0: invalid in T-SQL, should raise error +create table "" (a int) +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: zero-length delimited identifier at or near """")~~ + +select * from "" +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: zero-length delimited identifier at or near """")~~ + +-- execute immediate +execute("") +go +~~ERROR (Code: 33557097)~~ + +~~ERROR (Message: syntax error near '""' at line 2 and character position 8)~~ + + +set quoted_identifier off +go diff --git a/test/JDBC/input/empty_dq_string_in_object-vu-cleanup.sql b/test/JDBC/input/empty_dq_string_in_object-vu-cleanup.sql new file mode 100644 index 0000000000..c7531672ed --- /dev/null +++ b/test/JDBC/input/empty_dq_string_in_object-vu-cleanup.sql @@ -0,0 +1,58 @@ +drop table t1_empty_dq_string +go +drop table t2_empty_dq_string +go +drop table t3_empty_dq_string +go +drop table t4_empty_dq_string +go +drop table t5_empty_dq_string +go +drop table t6_empty_dq_string +go +drop table t7_empty_dq_string +go +drop procedure p1_empty_dq_string +go +drop function f1_empty_dq_string +go +drop procedure p2_empty_dq_string +go +drop function f2_empty_dq_string +go +drop procedure p2a_empty_dq_string +go +drop function f2a_empty_dq_string +go +drop procedure p2b_empty_dq_string +go +drop procedure p3_empty_dq_string +go +drop procedure p4_empty_dq_string +go +drop function f4_empty_dq_string +go +drop procedure p5_empty_dq_string +go +drop function f5_empty_dq_string +go +drop procedure p6_empty_dq_string +go +drop procedure p7_empty_dq_string +go +drop procedure p7a_empty_dq_string +go +drop procedure p7b_empty_dq_string +go +drop function f7b_empty_dq_string +go +drop procedure p7c_empty_dq_string +go +set quoted_identifier on +go +drop table "t8_empty_dq_string" +go +drop procedure "p8_empty_dq_string" +go +drop function "f8_empty_dq_string" +go \ No newline at end of file diff --git a/test/JDBC/input/empty_dq_string_in_object-vu-prepare.sql b/test/JDBC/input/empty_dq_string_in_object-vu-prepare.sql new file mode 100644 index 0000000000..84150a437a --- /dev/null +++ b/test/JDBC/input/empty_dq_string_in_object-vu-prepare.sql @@ -0,0 +1,23 @@ +create table t1_empty_dq_string(a int) +go +create table t2_empty_dq_string(a int) +go +create table t3_empty_dq_string(a int) +go +create table t4_empty_dq_string(a int) +go +create table t5_empty_dq_string(a int, b varchar(10)) +insert t5_empty_dq_string values(1, 'test 1') +go +create table t6_empty_dq_string(a int, b varchar(10)) +insert t6_empty_dq_string values(1, 'test 1') +go +create table t7_empty_dq_string(a int, b varchar(10)) +insert t7_empty_dq_string values(1, 'test 1') +go + +set quoted_identifier on +go +create table "t8_empty_dq_string"("a" int, "b" varchar(10)) +insert "t8_empty_dq_string" values(1, 'test 1') +go diff --git a/test/JDBC/input/empty_dq_string_in_object-vu-verify.sql b/test/JDBC/input/empty_dq_string_in_object-vu-verify.sql new file mode 100644 index 0000000000..08eca2a87d --- /dev/null +++ b/test/JDBC/input/empty_dq_string_in_object-vu-verify.sql @@ -0,0 +1,291 @@ +-- Purpose: a double-quoted empty string inside a SQL object body should be treated as an empty string + +set quoted_identifier off +go + +-- SELECT "" +select "" as empty_str +go +create procedure p1_empty_dq_string +as +select "" as empty_str +go +exec p1_empty_dq_string +go +create function f1_empty_dq_string() returns varchar(10) +as +begin +return "" +end +go +select dbo.f1_empty_dq_string(), len(dbo.f1_empty_dq_string()) +go +create trigger tr1_empty_dq_string on t1_empty_dq_string for insert as +begin +select "" as empty_str +end +go +insert t1_empty_dq_string values(1) +go + +-- variable declaration and assignment +declare @v varchar(10) = "" +set @v = "" +select @v +go +create procedure p2_empty_dq_string +as +declare @v varchar(10) = "" +set @v = "" +select @v +go +exec p2_empty_dq_string +go +create function f2_empty_dq_string() returns varchar(10) +as +begin +declare @v varchar(10) = "" +set @v = "" +return '['+@v+']' +end +go +select dbo.f2_empty_dq_string(), len(dbo.f2_empty_dq_string()) +go +create trigger tr2_empty_dq_string on t2_empty_dq_string for insert as +begin +declare @v varchar(10) = "" +set @v = "" +select @v +end +go +insert t2_empty_dq_string values(1) +go + +-- execute immediate +execute("") +go + +-- argument for procedure/function call +create procedure p2a_empty_dq_string @p varchar(10) +as +select '['+@p+']' as p, len(@p) as len +go +exec p2a_empty_dq_string "" +go +exec p2a_empty_dq_string @p="" +go +create function f2a_empty_dq_string(@p varchar(10)) returns varchar(10) +as +begin +return '['+@p+']' +end +go +select dbo.f2a_empty_dq_string(""), len(dbo.f2a_empty_dq_string("")) +go + +create procedure p2b_empty_dq_string @p varchar(10) +as +exec p2a_empty_dq_string "" +exec p2a_empty_dq_string @p="" +select dbo.f2a_empty_dq_string(""), len(dbo.f2a_empty_dq_string("")) +go +exec p2b_empty_dq_string "" +go + +-- print "" (not visible in JDBC test output) +print "" +go +create procedure p3_empty_dq_string +as +print "" +go +exec p3_empty_dq_string +go +create trigger tr3_empty_dq_string on t3_empty_dq_string for insert as +begin +select 'trigger' +print "" +end +go +insert t3_empty_dq_string values(1) +go + +-- usage in condition +if 'a' = "" select 'branch 1' else select 'branch 2' +go +create procedure p4_empty_dq_string as +if 'a' = "" select 'branch 1' else select 'branch 2' +go +exec p4_empty_dq_string +go +create function f4_empty_dq_string() returns int +as +begin +if 'a' = "" return 1 +return 2 +end +go +select dbo.f4_empty_dq_string() +go +create trigger tr4_empty_dq_string on t4_empty_dq_string for insert as +begin +if 'a' = "" select 'branch 1' else select 'branch 2' +end +go +insert t4_empty_dq_string values(1) +go + +-- usage in WHERE_clause +select * from t5_empty_dq_string where b = "" order by a +go +create procedure p5_empty_dq_string as +select a, '['+b+']' as b from t5_empty_dq_string where b = "" order by a +go +exec p5_empty_dq_string +go +create function f5_empty_dq_string() returns int +as +begin +declare @v int +select @v = count(*) from t5_empty_dq_string where b = "" +return '['+@v+']' +end +go +select dbo.f5_empty_dq_string() +go +create trigger tr5_empty_dq_string on t5_empty_dq_string for insert as +begin +select a, '['+b+']' as b from t5_empty_dq_string where b = "" order by a +end +go +insert t5_empty_dq_string values(1, 'test 1') +go + +-- usage in insert +insert t6_empty_dq_string values (2, "") +go +create procedure p6_empty_dq_string as +insert t6_empty_dq_string values (3, "") +go +exec p6_empty_dq_string +go +select a, '['+b+']' as b from t6_empty_dq_string order by a +go +create trigger tr6_empty_dq_string on t6_empty_dq_string for insert as +begin +insert t6_empty_dq_string values (4, "") +end +go +insert t6_empty_dq_string values(1, 'test 1') +go +select a, '['+b+']' as b from t6_empty_dq_string order by a +go + +-- usage in update +update t7_empty_dq_string set b = "" where a = 1 +go +create procedure p7_empty_dq_string as +update t7_empty_dq_string set b = "" where a = 1 +go +exec p7_empty_dq_string +go +select a, '['+b+']' as b from t7_empty_dq_string order by a +go +create trigger tr7_empty_dq_string on t7_empty_dq_string for insert as +begin +update t7_empty_dq_string set b = "" where a = 1 +end +go +insert t7_empty_dq_string values(2, 'test 2') +go +select a, '['+b+']' as b from t7_empty_dq_string order by a +go + +-- single-space string in update: not affected +create procedure p7a_empty_dq_string as +update t7_empty_dq_string set b = " " where a = 1 +go +exec p7a_empty_dq_string +go +select a, '['+b+']' as b from t7_empty_dq_string order by a +go +create trigger tr7a_empty_dq_string on t7_empty_dq_string for insert as +begin +update t7_empty_dq_string set b = " " where a = 1 +end +go +insert t7_empty_dq_string values(3, 'test 3') +go +select a, '['+b+']' as b from t7_empty_dq_string order by a +go + + +-- single-space string: argument for procedure/function call +create procedure p7b_empty_dq_string @p varchar(10) +as +select '['+@p+']' as p, len(@p) as len +go +exec p7b_empty_dq_string " " +go +exec p7b_empty_dq_string @p=" " +go +create function f7b_empty_dq_string(@p varchar(10)) returns varchar(10) +as +begin +return '['+@p+']' +end +go +select dbo.f7b_empty_dq_string(" "), len(dbo.f7b_empty_dq_string(" ")) +go + +create procedure p7c_empty_dq_string @p varchar(10) +as +exec p7b_empty_dq_string " " +exec p7b_empty_dq_string @p=" " +select dbo.f7b_empty_dq_string(" "), len(dbo.f7b_empty_dq_string(" ")) +go +exec p7c_empty_dq_string " " +go + +-- double-quoted identifier: not affected +set quoted_identifier on +go + +-- SELECT "" +select * from "t8_empty_dq_string" order by "a" +go +create procedure "p8_empty_dq_string" +as +select * from "t8_empty_dq_string" order by "a" +go +exec "p8_empty_dq_string" +go +create function "f8_empty_dq_string"() returns varchar(10) +as +begin +return '' +end +go +select dbo."f8_empty_dq_string"(), len(dbo."f8_empty_dq_string"()) +go +create trigger "tr8_empty_dq_string" on "t8_empty_dq_string" for insert as +begin +select * from "t8_empty_dq_string" order by "a" +end +go +insert "t8_empty_dq_string" values(2, 'test 2') +go +select * from "t8_empty_dq_string" order by "a" +go + +-- double-quoted identifier of length 0: invalid in T-SQL, should raise error +create table "" (a int) +go +select * from "" +go +-- execute immediate +execute("") +go + +set quoted_identifier off +go \ No newline at end of file