This is an automated email from the ASF dual-hosted git repository.

strongduanmu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new fec1f87710d Fix PostgreSQL external parser input loading (#40192)
fec1f87710d is described below

commit fec1f87710d658e43eac95815e223a5108341dff
Author: ZhangCheng <[email protected]>
AuthorDate: Fri Oct 16 08:57:37 2026 +0800

    Fix PostgreSQL external parser input loading (#40192)
    
    * Fix PostgreSQL external parser input loading
    
    * Treat plain psql variables as opaque input
---
 ...ostgreSQLExternalTestParameterLoadTemplate.java | 105 +++++++++++++++++++--
 ...reSQLExternalTestParameterLoadTemplateTest.java |  83 +++++++++++++---
 2 files changed, 168 insertions(+), 20 deletions(-)

diff --git 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplate.java
 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplate.java
index 6c1f48507d7..233f56d812f 100644
--- 
a/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplate.java
+++ 
b/test/it/parser/src/main/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplate.java
@@ -46,6 +46,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
@@ -125,6 +127,9 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
     
     private static final class ScriptReader extends BaseErrorListener {
         
+        private static final Pattern PSQL_VARIABLE_PATTERN =
+                
Pattern.compile(":(?:[A-Za-z_0-9\\x80-\\uFFFF]+|'[A-Za-z_0-9\\x80-\\uFFFF]+'|\"[A-Za-z_0-9\\x80-\\uFFFF]+\"|\\{\\?[A-Za-z_0-9\\x80-\\uFFFF]+\\})");
+        
         private final List<String> lines;
         
         private final DatabaseType databaseType;
@@ -139,10 +144,18 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
         
         private final List<ScriptStatement> statements = new ArrayList<>();
         
+        private final StringBuilder normalizedSQL = new StringBuilder();
+        
         private int start = -1;
         
+        private int variableEnd = -1;
+        
         private int parentheses;
         
+        private int jsonObjectParentheses;
+        
+        private int brackets;
+        
         private int atomicBlocks;
         
         private int caseBlocks;
@@ -161,6 +174,8 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
         
         private boolean clientBuffer;
         
+        private boolean clientStatement;
+        
         private boolean echoAll = true;
         
         private boolean clientStateKnown = true;
@@ -203,9 +218,12 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
                 }
                 readBlock(kind, token);
                 readCopy(kind);
-                previousKind = kind;
+                previousKind = "IDENTIFIER_".equals(kind) && 
isJsonObjectConstructor(token) ? "JSON_OBJECT" : kind;
                 if ("COLON_".equals(kind)) {
                     unsafe = true;
+                    if (0 == brackets && 0 == jsonObjectParentheses) {
+                        readClientVariable(token.getStartIndex());
+                    }
                 }
                 if ("SEMI_".equals(kind) && (clientBuffer || 0 == parentheses 
&& 0 == atomicBlocks)) {
                     submit(token.getStopIndex() + 1, token.getStopIndex() + 1, 
true);
@@ -237,17 +255,23 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
             if (command.startsWith(";")) {
                 unsafe = true;
                 clientBuffer = true;
+                clientStatement = true;
                 firstToken = true;
                 previousKind = "";
                 seek(offset + 2);
                 return true;
             }
             String name = command.split("\\s|\\\\", 2)[0];
-            if (command.indexOf('\\') >= 0) {
+            boolean compoundCommand = command.indexOf('\\') >= 0;
+            if (start >= 0 && compoundCommand) {
                 unsafe = true;
                 clientBuffer = true;
+                clientStatement = true;
                 return false;
             }
+            if (compoundCommand && !"copy".equals(name)) {
+                clientStateKnown = false;
+            }
             if (Arrays.asList("g", "gx", "gset", "gexec").contains(name)) {
                 boolean copy = pendingCopy;
                 if (start >= 0) {
@@ -263,11 +287,13 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
             if (start >= 0) {
                 unsafe = true;
                 clientBuffer = true;
+                clientStatement = true;
                 return false;
             }
             if ("copy".equals(name)) {
                 start = offset;
                 unsafe = true;
+                clientStatement = true;
                 boolean copy = isCopyFromStdin(command);
                 pendingCopy = copy;
                 submit(end, end, false);
@@ -322,10 +348,12 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
         }
         
         private void readBlock(final String kind, final Token token) {
-            if ("LP_".equals(kind)) {
-                parentheses++;
-            } else if ("RP_".equals(kind) && parentheses > 0) {
-                parentheses--;
+            if ("LP_".equals(kind) || "RP_".equals(kind)) {
+                readParentheses(kind);
+            } else if ("LBT_".equals(kind)) {
+                brackets++;
+            } else if ("RBT_".equals(kind) && brackets > 0) {
+                brackets--;
             } else if ("BEGIN".equals(previousKind) && 
"ATOMIC".equalsIgnoreCase(token.getText())) {
                 atomicBlocks++;
             } else if (atomicBlocks > 0 && "CASE".equals(kind)) {
@@ -339,6 +367,20 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
             }
         }
         
+        private void readParentheses(final String kind) {
+            if ("LP_".equals(kind)) {
+                parentheses++;
+                if (0 == jsonObjectParentheses && 
"JSON_OBJECT".equals(previousKind)) {
+                    jsonObjectParentheses = parentheses;
+                }
+            } else if (parentheses > 0) {
+                if (jsonObjectParentheses == parentheses) {
+                    jsonObjectParentheses = 0;
+                }
+                parentheses--;
+            }
+        }
+        
         private void readCopy(final String kind) {
             if (firstToken) {
                 copyStatement = "COPY".equals(kind);
@@ -352,6 +394,39 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
             }
         }
         
+        private boolean isJsonObjectConstructor(final Token token) {
+            String identifier = token.getText();
+            return "JSON_OBJECT".equalsIgnoreCase(identifier) || 
"JSON_OBJECTAGG".equalsIgnoreCase(identifier);
+        }
+        
+        private void readClientVariable(final int offset) {
+            int line = lineAt(offset);
+            Matcher matcher = 
PSQL_VARIABLE_PATTERN.matcher(lines.get(line)).region(offset - 
lineStarts[line], lines.get(line).length());
+            if (!matcher.lookingAt()) {
+                return;
+            }
+            String variable = matcher.group();
+            String replacement;
+            switch (variable.charAt(1)) {
+                case '\'':
+                    replacement = "'foo'";
+                    break;
+                case '"':
+                    replacement = "\"foo\"";
+                    break;
+                case '{':
+                    replacement = "TRUE";
+                    break;
+                default:
+                    clientStatement = true;
+                    seek(offset + variable.length());
+                    return;
+            }
+            normalizedSQL.append(input.getText(Interval.of(variableEnd < 0 ? 
start : variableEnd, offset - 1))).append(replacement);
+            variableEnd = offset + variable.length();
+            seek(variableEnd);
+        }
+        
         private void submit(final int sqlEnd, final int echoEnd, final boolean 
filterable) {
             int end = sqlEnd;
             int firstLine = lineAt(start);
@@ -368,14 +443,27 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
                     seek(Math.min(input.size(), lineStarts[marker] + 
lines.get(marker).length() + 1));
                 }
             }
-            statements.add(new 
ScriptStatement(input.getText(Interval.of(start, end - 1)).trim(), 
getEcho(firstLine, lastLine),
-                    filterable && exclusive && !unsafe && !copyOutput && 
echoAll && clientStateKnown));
+            statements.add(new ScriptStatement(getSQL(end), getEcho(firstLine, 
lastLine), filterable && exclusive && !unsafe && !copyOutput && echoAll && 
clientStateKnown));
             reset();
         }
         
+        private String getSQL(final int end) {
+            if (clientStatement) {
+                return "";
+            }
+            if (variableEnd < 0) {
+                return input.getText(Interval.of(start, end - 1)).trim();
+            }
+            return normalizedSQL.append(input.getText(Interval.of(variableEnd, 
end - 1))).toString().trim();
+        }
+        
         private void reset() {
             start = -1;
+            variableEnd = -1;
+            normalizedSQL.setLength(0);
             parentheses = 0;
+            jsonObjectParentheses = 0;
+            brackets = 0;
             atomicBlocks = 0;
             caseBlocks = 0;
             previousKind = "";
@@ -385,6 +473,7 @@ public final class 
PostgreSQLExternalTestParameterLoadTemplate implements Extern
             pendingCopy = false;
             unsafe = false;
             clientBuffer = false;
+            clientStatement = false;
         }
         
         @Override
diff --git 
a/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplateTest.java
 
b/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplateTest.java
index f442f4c3da7..0a6c04086bc 100644
--- 
a/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplateTest.java
+++ 
b/test/it/parser/src/test/java/org/apache/shardingsphere/test/it/sql/parser/external/loader/template/dialect/PostgreSQLExternalTestParameterLoadTemplateTest.java
@@ -83,19 +83,64 @@ class PostgreSQLExternalTestParameterLoadTemplateTest {
                 Arguments.of("copy payload until eof", "COPY foo FROM 
STDIN;\nfoo';\n$x$;", "", Collections.singletonList("COPY foo FROM 
STDIN;\nfoo';\n$x$;")),
                 Arguments.of("copy submitted with g", "COPY foo FROM STDIN 
\\g\nfoo';\n$x$;\n\\.\nSELECT 2;", "", Arrays.asList("COPY foo FROM STDIN", 
"SELECT 2;")),
                 Arguments.of("copy submitted with g until eof", "COPY foo FROM 
STDIN \\g\nfoo';\n$x$;", "", Collections.singletonList("COPY foo FROM STDIN 
\\g\nfoo';\n$x$;")),
-                Arguments.of("client copy payload", "\\copy foo FROM 
STDIN\nfoo';\n$x$;\n\\.\nSELECT 2;", "", Arrays.asList("\\copy foo FROM STDIN", 
"SELECT 2;")),
-                Arguments.of("client copy payload until eof", "\\copy foo FROM 
STDIN\nfoo';\n$x$;", "", Collections.singletonList("\\copy foo FROM 
STDIN\nfoo';\n$x$;")),
+                Arguments.of("client copy payload", "\\copy foo FROM 
STDIN\nfoo';\n$x$;\n\\.\nSELECT 2;", "", Collections.singletonList("SELECT 
2;")),
+                Arguments.of("client copy payload until eof", "\\copy foo FROM 
STDIN\nfoo';\n$x$;", "", Collections.emptyList()),
                 Arguments.of("client copy query with stdin table", "\\copy 
(SELECT * FROM stdin /*comment*/) TO STDOUT;\nSELECT 2;", "",
-                        Arrays.asList("\\copy (SELECT * FROM stdin 
/*comment*/) TO STDOUT;", "SELECT 2;")),
-                Arguments.of("client copy stdin table", "\\copy stdin TO 
STDOUT\nSELECT 2;", "", Arrays.asList("\\copy stdin TO STDOUT", "SELECT 2;")),
-                Arguments.of("copy in multiple query buffer", "SELECT 1 \\; 
COPY foo FROM STDIN;\nfoo';\n$x$;\n\\.\nSELECT 2;", "", Arrays.asList("SELECT 1 
\\; COPY foo FROM STDIN;", "SELECT 2;")),
-                Arguments.of("copy in multiple query buffer until eof", 
"SELECT 1 \\; COPY foo FROM STDIN;\nfoo';\n$x$;", "",
-                        Collections.singletonList("SELECT 1 \\; COPY foo FROM 
STDIN;\nfoo';\n$x$;")),
+                        Collections.singletonList("SELECT 2;")),
+                Arguments.of("client copy stdin table", "\\copy stdin TO 
STDOUT\nSELECT 2;", "", Collections.singletonList("SELECT 2;")),
+                Arguments.of("client copy escaped option", "\\copy foo TO 
STDOUT (ESCAPE E'\\\\')\n\\copy foo TO STDOUT\nSELECT 2;", "", 
Collections.singletonList("SELECT 2;")),
+                Arguments.of("client copy escaped stdin option", "\\copy foo 
FROM STDIN (NULL E'\\\\N')\nfoo';\n\\.\nSELECT 2;", "", 
Collections.singletonList("SELECT 2;")),
+                Arguments.of("copy in multiple query buffer", "SELECT 1 \\; 
COPY foo FROM STDIN;\nfoo';\n$x$;\n\\.\nSELECT 2;", "", 
Collections.singletonList("SELECT 2;")),
+                Arguments.of("copy in multiple query buffer until eof", 
"SELECT 1 \\; COPY foo FROM STDIN;\nfoo';\n$x$;", "", Collections.emptyList()),
                 Arguments.of("stdin table in copy query", "COPY (SELECT * FROM 
stdin) TO STDOUT;\nSELECT 2;", "", Arrays.asList("COPY (SELECT * FROM stdin) TO 
STDOUT;", "SELECT 2;")),
                 Arguments.of("copy file paths", "COPY foo FROM 
'/tmp/in';\nCOPY foo TO '/tmp/out';\nSELECT 3;", "", Arrays.asList("COPY foo 
FROM '/tmp/in';", "COPY foo TO '/tmp/out';", "SELECT 3;")),
-                Arguments.of("psql variables", "SELECT :foo, :'bar', :\"baz\", 
:{?qux}, $1, 1::integer;", "", Collections.singletonList("SELECT :foo, :'bar', 
:\"baz\", :{?qux}, $1, 1::integer;")),
-                Arguments.of("unknown command in buffer", "SELECT 1\n\\unknown 
foo\nSELECT 2;", "", Collections.singletonList("SELECT 1\n\\unknown foo\nSELECT 
2;")),
-                Arguments.of("mixed commands in buffer", "SELECT 1 \\g \\echo 
foo;\nSELECT 2;", "", Arrays.asList("SELECT 1 \\g \\echo foo;", "SELECT 2;")),
+                Arguments.of("psql variables", "SELECT :foo, :'bar', :\"baz\", 
:{?qux}, $1, 1::integer;", "", Collections.emptyList()),
+                Arguments.of("unquoted psql variable", "SELECT :foo; SELECT 
2;", "", Collections.singletonList("SELECT 2;")),
+                Arguments.of("quoted psql variable", "SELECT :'foo'; SELECT 
2;", "", Arrays.asList("SELECT 'foo';", "SELECT 2;")),
+                Arguments.of("identifier psql variable", "SELECT :\"foo\"; 
SELECT 2;", "", Arrays.asList("SELECT \"foo\";", "SELECT 2;")),
+                Arguments.of("psql variable existence", "SELECT :{?foo}; 
SELECT 2;", "", Arrays.asList("SELECT TRUE;", "SELECT 2;")),
+                Arguments.of("unicode psql variables", "SELECT :变量, :'变量', 
:\"变量\", :{?变量}; SELECT 2;", "", Collections.singletonList("SELECT 2;")),
+                Arguments.of("quoted unicode variables", "SELECT '😀', :'变量', 
:\"变量\", :{?变量};", "", Collections.singletonList("SELECT '😀', 'foo', \"foo\", 
TRUE;")),
+                Arguments.of("multiple typed variables", "SELECT :'bar', 
:\"baz\", :{?qux}, $1::integer;", "", Collections.singletonList("SELECT 'foo', 
\"foo\", TRUE, $1::integer;")),
+                Arguments.of("adjacent typed variables", "SELECT 
:'foo'||:'bar';", "", Collections.singletonList("SELECT 'foo'||'foo';")),
+                Arguments.of("multiline typed variables", "SELECT\n :'foo',\n 
:\"bar\";", "", Collections.singletonList("SELECT\n 'foo',\n \"foo\";")),
+                Arguments.of("typed variable at eof", "SELECT :'foo'", "", 
Collections.singletonList("SELECT 'foo'")),
+                Arguments.of("typed variable submitted with g", "SELECT :'foo' 
\\g\nSELECT 2;", "", Arrays.asList("SELECT 'foo'", "SELECT 2;")),
+                Arguments.of("typed variable in copy filename", "COPY foo FROM 
:'filename'; SELECT 2;", "", Arrays.asList("COPY foo FROM 'foo';", "SELECT 
2;")),
+                Arguments.of("typed identifier in table name", "SELECT * FROM 
:\"table\";", "", Collections.singletonList("SELECT * FROM \"foo\";")),
+                Arguments.of("typed variable followed by raw variable", 
"SELECT :'foo', :bar; SELECT 2;", "", Collections.singletonList("SELECT 2;")),
+                Arguments.of("typed variable after raw statement", "SELECT 
:foo; SELECT :'bar';", "", Collections.singletonList("SELECT 'foo';")),
+                Arguments.of("typed variable in client buffer", "SELECT :'foo' 
\\g \\echo bar;\nSELECT 2;", "", Collections.singletonList("SELECT 2;")),
+                Arguments.of("typed variable in atomic body", "CREATE FUNCTION 
foo() RETURNS boolean BEGIN ATOMIC SELECT :{?CASE}; END; SELECT 2;", "",
+                        Arrays.asList("CREATE FUNCTION foo() RETURNS boolean 
BEGIN ATOMIC SELECT TRUE; END;", "SELECT 2;")),
+                Arguments.of("raw case variable in atomic body", "CREATE 
FUNCTION foo() RETURNS integer LANGUAGE SQL BEGIN ATOMIC SELECT :case; END; 
SELECT 3;", "",
+                        Collections.singletonList("SELECT 3;")),
+                Arguments.of("uppercase case variable in atomic body", "CREATE 
FUNCTION foo() RETURNS integer LANGUAGE SQL BEGIN ATOMIC SELECT :CASE; END; 
SELECT 3;", "",
+                        Collections.singletonList("SELECT 3;")),
+                Arguments.of("raw end variable in atomic body", "CREATE 
FUNCTION foo() RETURNS integer LANGUAGE SQL BEGIN ATOMIC SELECT :end; END; 
SELECT 3;", "",
+                        Collections.singletonList("SELECT 3;")),
+                Arguments.of("uppercase end variable in atomic body", "CREATE 
FUNCTION foo() RETURNS integer LANGUAGE SQL BEGIN ATOMIC SELECT :END; END; 
SELECT 3;", "",
+                        Collections.singletonList("SELECT 3;")),
+                Arguments.of("raw variable before atomic alias", "SELECT 
:begin ATOMIC; SELECT 3;", "", Collections.singletonList("SELECT 3;")),
+                Arguments.of("raw variable in atomic case expression", "CREATE 
FUNCTION foo() RETURNS integer LANGUAGE SQL BEGIN ATOMIC SELECT CASE WHEN TRUE 
THEN :end ELSE 2 END; END; SELECT 3;", "",
+                        Collections.singletonList("SELECT 3;")),
+                Arguments.of("literal psql variable", "SELECT ':foo', 
$$:'foo'$$, \":foo\";", "", Collections.singletonList("SELECT ':foo', 
$$:'foo'$$, \":foo\";")),
+                Arguments.of("comment psql variable", "SELECT /* :foo */ 1; -- 
:bar\nSELECT 2;", "", Arrays.asList("SELECT /* :foo */ 1;", "SELECT 2;")),
+                Arguments.of("array slices", "SELECT a[1:2], a[foo:bar], 
a[:bar], a[1:2][3:4]; SELECT $1::integer;", "",
+                        Arrays.asList("SELECT a[1:2], a[foo:bar], a[:bar], 
a[1:2][3:4];", "SELECT $1::integer;")),
+                Arguments.of("array slice literal separator", "SELECT 
a[1:'bar']; SELECT :'baz';", "", Arrays.asList("SELECT a[1:'bar'];", "SELECT 
'foo';")),
+                Arguments.of("variable after array slice", "SELECT a[1:2]; 
SELECT :foo; SELECT 3;", "", Arrays.asList("SELECT a[1:2];", "SELECT 3;")),
+                Arguments.of("json object separator", "SELECT 
JSON_OBJECT('foo':1, 'bar':value);", "", Collections.singletonList("SELECT 
JSON_OBJECT('foo':1, 'bar':value);")),
+                Arguments.of("json literal separator and typed variable", 
"SELECT JSON_OBJECT('foo':'bar'), :'baz';", "", 
Collections.singletonList("SELECT JSON_OBJECT('foo':'bar'), 'foo';")),
+                Arguments.of("json object aggregate separator", "SELECT 
JSON_OBJECTAGG(foo:bar) FROM baz;", "", Collections.singletonList("SELECT 
JSON_OBJECTAGG(foo:bar) FROM baz;")),
+                Arguments.of("nested json object separator", "SELECT 
json_object('foo':json_object('bar':1), 'baz':2);", "",
+                        Collections.singletonList("SELECT 
json_object('foo':json_object('bar':1), 'baz':2);")),
+                Arguments.of("variable after json object", "SELECT 
JSON_OBJECT('foo':1); SELECT :foo; SELECT 3;", "", Arrays.asList("SELECT 
JSON_OBJECT('foo':1);", "SELECT 3;")),
+                Arguments.of("variable after json object expression", "SELECT 
JSON_OBJECT('foo':1), :foo; SELECT 2;", "", Collections.singletonList("SELECT 
2;")),
+                Arguments.of("unknown command in buffer", "SELECT 1\n\\unknown 
foo\nSELECT 2;", "", Collections.emptyList()),
+                Arguments.of("mixed commands in buffer", "SELECT 1 \\g \\echo 
foo;\nSELECT 2;", "", Collections.singletonList("SELECT 2;")),
+                Arguments.of("standalone escaped command", "\\echo 
foo\\bar\nSELECT 2;", "", Collections.singletonList("SELECT 2;")),
+                Arguments.of("standalone mixed commands", "\\g \\echo 
foo;\nSELECT 2;", "", Collections.singletonList("SELECT 2;")),
                 Arguments.of("unterminated sql", "SELECT\n  1", "", 
Collections.singletonList("SELECT\n  1")),
                 Arguments.of("unclosed parenthesis at eof", "SELECT 
(1;\nSELECT 2;", "", Collections.singletonList("SELECT (1;\nSELECT 2;")),
                 Arguments.of("unclosed atomic body at eof", "CREATE FUNCTION 
foo() RETURNS integer BEGIN ATOMIC SELECT 1;\nSELECT 2;", "",
@@ -133,14 +178,28 @@ class PostgreSQLExternalTestParameterLoadTemplateTest {
                         "COPY (SELECT 'ERROR: client text') TO STDOUT;\nERROR: 
client text\nSELECT 2;", Arrays.asList("COPY (SELECT 'ERROR: client text') TO 
STDOUT;", "SELECT 2;")),
                 Arguments.of("error in leading block comment", "SELECT 
1;\n/*\nERROR: literal\n*/\nSELECT 2;", "SELECT 1;\n/*\nERROR: 
literal\n*/\nSELECT 2;", Arrays.asList("SELECT 1;", "SELECT 2;")),
                 Arguments.of("unresolved variables with error", "SELECT :foo, 
:'foo', :\"foo\", :{?foo};\nSELECT 2;", "SELECT :foo, :'foo', :\"foo\", 
:{?foo};\nERROR: bar\nSELECT 2;",
-                        Arrays.asList("SELECT :foo, :'foo', :\"foo\", 
:{?foo};", "SELECT 2;")),
+                        Collections.singletonList("SELECT 2;")),
+                Arguments.of("typed variable original echo boundary", "SELECT 
1;\nSELECT :'bar';\nSELECT 3;",
+                        "SELECT 1;\nERROR: foo\nSELECT :'bar';\nERROR: 
bar\nSELECT 3;", Arrays.asList("SELECT 'foo';", "SELECT 3;")),
+                Arguments.of("typed variable error state reset", "SELECT 
:'bar';\nSELECT 2;\nSELECT 3;",
+                        "SELECT :'bar';\nERROR: foo\nSELECT 2;\nERROR: 
bar\nSELECT 3;", Arrays.asList("SELECT 'foo';", "SELECT 3;")),
+                Arguments.of("client statement echo boundary", "SELECT 
1;\n\\copy foo TO STDOUT\nSELECT 2;", "SELECT 1;\n\\copy foo TO STDOUT\nERROR: 
client text\nSELECT 2;",
+                        Arrays.asList("SELECT 1;", "SELECT 2;")),
+                Arguments.of("standalone mixed format command", "\\g \\pset 
format unaligned\nSELECT 'ERROR: client text';",
+                        "\\g \\pset format unaligned\nSELECT 'ERROR: client 
text';\n?column?\nERROR: client text\n(1 row)", 
Collections.singletonList("SELECT 'ERROR: client text';")),
+                Arguments.of("mixed echo format command", "\\echo foo \\pset 
format unaligned\n\\set ECHO all\nSELECT 'ERROR: client text';",
+                        "\\echo foo \\pset format unaligned\n\\set ECHO 
all\nSELECT 'ERROR: client text';\n?column?\nERROR: client text\n(1 row)",
+                        Collections.singletonList("SELECT 'ERROR: client 
text';")),
                 Arguments.of("cast and parameter native error", "SELECT 
$1::integer;\nSELECT 2;", "SELECT $1::integer;\nERROR: foo\nSELECT 2;", 
Collections.singletonList("SELECT 2;")),
                 Arguments.of("array slice in rule action", "CREATE RULE foo AS 
ON INSERT TO bar DO ALSO (SELECT a[1:2] FROM baz; SELECT 2);\nSELECT 3;",
                         "CREATE RULE foo AS ON INSERT TO bar DO ALSO (SELECT 
a[1:2] FROM baz; SELECT 2);\nERROR: foo\nSELECT 3;",
                         Arrays.asList("CREATE RULE foo AS ON INSERT TO bar DO 
ALSO (SELECT a[1:2] FROM baz; SELECT 2);", "SELECT 3;")),
                 Arguments.of("variable in atomic body", "CREATE FUNCTION foo() 
RETURNS integer BEGIN ATOMIC SELECT :foo; SELECT 2; END;\nSELECT 3;",
                         "CREATE FUNCTION foo() RETURNS integer BEGIN ATOMIC 
SELECT :foo; SELECT 2; END;\nERROR: foo\nSELECT 3;",
-                        Arrays.asList("CREATE FUNCTION foo() RETURNS integer 
BEGIN ATOMIC SELECT :foo; SELECT 2; END;", "SELECT 3;")),
+                        Collections.singletonList("SELECT 3;")),
+                Arguments.of("raw keyword variable echo boundary", "CREATE 
FUNCTION foo() RETURNS integer LANGUAGE SQL BEGIN ATOMIC SELECT :case; 
END;\nSELECT 2;\nSELECT 3;",
+                        "CREATE FUNCTION foo() RETURNS integer LANGUAGE SQL 
BEGIN ATOMIC SELECT :case; END;\nERROR: foo\nSELECT 2;\nERROR: bar\nSELECT 3;",
+                        Collections.singletonList("SELECT 3;")),
                 Arguments.of("missing echo", "SELECT 1;\nSELECT 2;", "ERROR: 
foo\nSELECT 2;", Arrays.asList("SELECT 1;", "SELECT 2;")),
                 Arguments.of("reversed echo order", "SELECT 1;\nSELECT 2;", 
"SELECT 2;\nERROR: foo\nSELECT 1;\nERROR: bar", Arrays.asList("SELECT 1;", 
"SELECT 2;")),
                 Arguments.of("incomplete multiline echo", "SELECT\n  
1;\nSELECT 2;", "SELECT", Arrays.asList("SELECT\n  1;", "SELECT 2;")),

Reply via email to