This is an automated email from the ASF dual-hosted git repository.
terrymanu 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 5983662c266 Fix PostgreSQL external corpus loading for inactive psql
branches (#40231)
5983662c266 is described below
commit 5983662c26672736f633e0e999238617407d1fef
Author: Liang Zhang <[email protected]>
AuthorDate: Mon Oct 19 12:46:01 2026 +0800
Fix PostgreSQL external corpus loading for inactive psql branches (#40231)
* Fix PostgreSQL external corpus loading for inactive psql branches
* Fix PostgreSQL corpus loading after a matched psql branch
---
...ostgreSQLExternalTestParameterLoadTemplate.java | 161 ++++++++++++++++++++-
...reSQLExternalTestParameterLoadTemplateTest.java | 77 +++++++++-
2 files changed, 231 insertions(+), 7 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 7bf8a1043c4..c7540a7fbe4 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
@@ -38,12 +38,15 @@ import
org.apache.shardingsphere.test.it.sql.parser.external.ExternalSQLTestPara
import
org.apache.shardingsphere.test.it.sql.parser.external.loader.template.ExternalTestParameterLoadTemplate;
import java.nio.CharBuffer;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Deque;
import java.util.Iterator;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
@@ -144,8 +147,20 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
private final List<ScriptStatement> statements = new ArrayList<>();
+ private final Deque<ConditionalBlock> conditionals = new
ArrayDeque<>();
+
private final StringBuilder normalizedSQL = new StringBuilder();
+ private int conditionalStart = -1;
+
+ private int conditionalStatementStart;
+
+ private int conditionalReplayEnd;
+
+ private boolean conditionalEchoAll;
+
+ private boolean conditionalFallback;
+
private int start = -1;
private int variableEnd = -1;
@@ -203,7 +218,14 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
}
private List<ScriptStatement> read() {
- for (Token token = lexer.nextToken(); Token.EOF !=
token.getType(); token = lexer.nextToken()) {
+ for (Token token = lexer.nextToken();; token = lexer.nextToken()) {
+ if (Token.EOF == token.getType()) {
+ if (conditionalStart >= 0) {
+ rewindConditional(input.size());
+ continue;
+ }
+ break;
+ }
markQuotedLines(token);
if (Token.DEFAULT_CHANNEL != token.getChannel()) {
if (start < 0) {
@@ -222,11 +244,13 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
start = token.getStartIndex();
}
readBlock(kind, token);
- readCopy(kind);
+ if (!isInactive()) {
+ readCopy(kind);
+ }
previousKind = "IDENTIFIER_".equals(kind) &&
isJsonObjectConstructor(token) ? "JSON_OBJECT" : kind;
if ("COLON_".equals(kind)) {
unsafe = true;
- if (0 == brackets && 0 == jsonObjectParentheses) {
+ if (!isInactive() && 0 == brackets && 0 ==
jsonObjectParentheses) {
readClientVariable(token.getStartIndex());
}
}
@@ -240,6 +264,18 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
return statements;
}
+ private void rewindConditional(final int end) {
+ statements.subList(conditionalStatementStart,
statements.size()).clear();
+ reset();
+ conditionals.clear();
+ echoAll = conditionalEchoAll;
+ conditionalReplayEnd = end;
+ Arrays.fill(quotedLines, lineAt(conditionalStart), lineAt(end - 1)
+ 1, false);
+ lexer.reset();
+ seek(conditionalStart);
+ conditionalStart = -1;
+ }
+
private void markQuotedLines(final Token token) {
if (lexer.getLine() > token.getLine()) {
int lastLine = lineAt(token.getStopIndex());
@@ -284,6 +320,27 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
int line = lineAt(offset);
int end = lineStarts[line] + lines.get(line).length();
String command = input.getText(Interval.of(offset + 1, end - 1));
+ String name = command.split("\\s|\\\\", 2)[0];
+ boolean compoundCommand = command.indexOf('\\') >= 0;
+ if (compoundCommand && (!conditionals.isEmpty() ||
command.contains("\\if"))) {
+ if (conditionalStart >= 0) {
+ rewindConditional(input.size());
+ return true;
+ }
+ conditionalReplayEnd = input.size();
+ }
+ if (offset >= conditionalReplayEnd && readConditional(name,
command, offset, end, compoundCommand)) {
+ return true;
+ }
+ if (isInactive()) {
+ if (start >= 0) {
+ submit(offset, end, false);
+ } else {
+ statements.add(new ScriptStatement("", lines.subList(line,
line + 1), false));
+ }
+ seek(Math.min(input.size(), end + 1));
+ return true;
+ }
if (command.startsWith(";")) {
unsafe = true;
clientBuffer = true;
@@ -293,8 +350,6 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
seek(offset + 2);
return true;
}
- String name = command.split("\\s|\\\\", 2)[0];
- boolean compoundCommand = command.indexOf('\\') >= 0;
if (start >= 0 && compoundCommand) {
unsafe = true;
clientBuffer = true;
@@ -344,6 +399,86 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
return true;
}
+ private boolean readConditional(final String name, final String
command, final int offset, final int end, final boolean compoundCommand) {
+ if (!Arrays.asList("if", "elif", "else", "endif").contains(name)) {
+ return false;
+ }
+ int line = lineAt(offset);
+ boolean standalone = !compoundCommand && !clientBuffer &&
input.getText(Interval.of(lineStarts[line], offset - 1)).trim().isEmpty();
+ boolean inactive = isInactive();
+ if ("if".equals(name)) {
+ int condition =
readCondition(command.substring(name.length()).trim());
+ boolean unknown = !standalone || start >= 0 && !inactive ||
condition < 0 || !conditionals.isEmpty() && conditionals.peek().unknown;
+ if (conditionals.isEmpty() && !unknown) {
+ conditionalStart = lineStarts[line];
+ conditionalStatementStart = statements.size();
+ conditionalEchoAll = echoAll;
+ conditionalFallback = false;
+ }
+ ConditionalBlock block = new ConditionalBlock(inactive);
+ block.unknown = unknown;
+ block.matched = !unknown && 1 == condition;
+ block.inactive = inactive || !unknown && 0 == condition;
+ conditionals.push(block);
+ } else if (conditionals.isEmpty()) {
+ return false;
+ } else {
+ ConditionalBlock block = conditionals.peek();
+ if (("else".equals(name) || "endif".equals(name)) &&
!command.substring(name.length()).trim().isEmpty() || block.elseBranch &&
!"endif".equals(name)) {
+ conditionalFallback = true;
+ }
+ if ("endif".equals(name)) {
+ conditionals.pop();
+ } else if ("else".equals(name)) {
+ block.inactive = block.parentInactive || !block.unknown &&
block.matched;
+ block.elseBranch = true;
+ } else {
+ int condition = block.matched ? 0 :
readCondition(command.substring(name.length()).trim());
+ block.unknown |= condition < 0;
+ block.inactive = block.parentInactive || !block.unknown &&
(block.matched || 0 == condition);
+ block.matched |= !block.unknown && 1 == condition;
+ }
+ }
+ if (conditionalStart < 0) {
+ return false;
+ }
+ if (!standalone || start >= 0 && !inactive) {
+ conditionalFallback = true;
+ }
+ if (conditionals.isEmpty() && conditionalFallback) {
+ rewindConditional(Math.min(input.size(), end + 1));
+ return true;
+ }
+ if (start >= 0) {
+ if (inactive) {
+ statements.add(new ScriptStatement("",
getEcho(lineAt(start), line - 1), false));
+ }
+ reset();
+ }
+ statements.add(new ScriptStatement("", lines.subList(line, line +
1), false));
+ clientStateKnown = false;
+ if (conditionals.isEmpty()) {
+ conditionalStart = -1;
+ }
+ seek(Math.min(input.size(), end + 1));
+ return true;
+ }
+
+ private boolean isInactive() {
+ return !conditionals.isEmpty() && conditionals.peek().inactive;
+ }
+
+ private int readCondition(final String expression) {
+ String value = expression.toLowerCase(Locale.ENGLISH);
+ if (!value.isEmpty() && ("true".startsWith(value) ||
"yes".startsWith(value) || "on".equals(value) || "1".equals(value))) {
+ return 1;
+ }
+ if (!value.isEmpty() && ("false".startsWith(value) ||
"no".startsWith(value) || "off".startsWith(value) && value.length() > 1 ||
"0".equals(value))) {
+ return 0;
+ }
+ return -1;
+ }
+
private boolean isCopyFromStdin(final String command) {
Lexer header =
createLexer(CodePointCharStream.fromBuffer(CodePointBuffer.withChars(CharBuffer.wrap(command.toCharArray()))));
int depth = 0;
@@ -480,7 +615,7 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
}
private String getSQL(final int end) {
- if (clientStatement || emptyQuotedIdentifier) {
+ if (isInactive() || clientStatement || emptyQuotedIdentifier) {
return "";
}
if (variableEnd < 0) {
@@ -516,5 +651,19 @@ public final class
PostgreSQLExternalTestParameterLoadTemplate implements Extern
start = ((LexerNoViableAltException) ex).getStartIndex();
}
}
+
+ @RequiredArgsConstructor
+ private static final class ConditionalBlock {
+
+ private final boolean parentInactive;
+
+ private boolean inactive;
+
+ private boolean matched;
+
+ private boolean unknown;
+
+ private boolean elseBranch;
+ }
}
}
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 1ab3ff51225..d71d91db443 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
@@ -40,7 +40,7 @@ class PostgreSQLExternalTestParameterLoadTemplateTest {
private final PostgreSQLExternalTestParameterLoadTemplate template = new
PostgreSQLExternalTestParameterLoadTemplate();
@ParameterizedTest(name = "{0}")
- @MethodSource({"sqlCases", "expectedCases"})
+ @MethodSource({"sqlCases", "expectedCases", "conditionalCases"})
void assertLoadSQL(final String name, final String source, final String
expectedEcho, final Collection<String> expectedSQL) {
List<String> expectedFileContent = expectedEcho.isEmpty() ?
Collections.emptyList() : Arrays.asList(expectedEcho.split("\n", -1));
Collection<ExternalSQLTestParameter> actual = template.load("sample",
Arrays.asList(source.split("\n", -1)), expectedFileContent, "PostgreSQL",
"CSV");
@@ -265,6 +265,81 @@ class PostgreSQLExternalTestParameterLoadTemplateTest {
Collections.singletonList("SELECT 2;")));
}
+ private static Stream<Arguments> conditionalCases() {
+ return Stream.of(
+ Arguments.of("inactive psql regression branch", "\\if true\n
select 'okay';\n select 'still okay';\n\\else\n not okay;\n still not
okay\n\\endif\nSELECT 2;", "",
+ Arrays.asList("select 'okay';", "select 'still
okay';", "SELECT 2;")),
+ Arguments.of("inactive unmatched parentheses", "\\if
false\nSELECT ((1;\n\\else\nSELECT 2;\n\\endif\nSELECT 3;", "",
Arrays.asList("SELECT 2;", "SELECT 3;")),
+ Arguments.of("inactive atomic block", "\\if false\nBEGIN
ATOMIC SELECT 1;\n\\endif\nSELECT 2;", "", Collections.singletonList("SELECT
2;")),
+ Arguments.of("inactive copy without payload marker", "\\if
false\nCOPY foo FROM STDIN;\n\\endif\nSELECT 2;", "",
Collections.singletonList("SELECT 2;")),
+ Arguments.of("inactive client copy without payload marker",
"\\if false\n\\copy foo FROM STDIN\n\\endif\nSELECT 2;", "",
Collections.singletonList("SELECT 2;")),
+ Arguments.of("inactive command in pending SQL", "\\if
false\nSELECT 1 \\g\n\\endif\nSELECT 2;", "", Collections.singletonList("SELECT
2;")),
+ Arguments.of("inactive quoted conditional text", "\\if
false\nSELECT 'foo\n\\else\n\\endif\nbar';\n\\else\nSELECT 2;\n\\endif", "",
Collections.singletonList("SELECT 2;")),
+ Arguments.of("inactive dollar conditional text", "\\if
false\nDO $$\n\\else\n\\endif\n$$;\n\\else\nSELECT 2;\n\\endif", "",
Collections.singletonList("SELECT 2;")),
+ Arguments.of("inactive comment conditional text", "\\if
false\n/*\n\\else\n\\endif\n*/\n\\else\nSELECT 2;\n\\endif", "",
Collections.singletonList("SELECT 2;")),
+ Arguments.of("inactive quoted identifier conditional text",
"\\if false\nSELECT \"foo\n\\else\n\\endif\nbar\";\n\\else\nSELECT
2;\n\\endif", "",
+ Collections.singletonList("SELECT 2;")),
+ Arguments.of("nested inactive branch", "\\if true\n\\if
false\nSELECT 1;\n\\else\nSELECT 2;\n\\endif\n\\else\n\\if true\nSELECT
3;\n\\endif\n\\endif\nSELECT 4;", "",
+ Arrays.asList("SELECT 2;", "SELECT 4;")),
+ Arguments.of("elif selection", "\\if false\nSELECT 1;\n\\elif
false\nSELECT 2;\n\\elif true\nSELECT 3;\n\\elif true\nSELECT
4;\n\\else\nSELECT 5;\n\\endif", "",
+ Collections.singletonList("SELECT 3;")),
+ Arguments.of("unknown elif after matched if", "\\if
true\nSELECT 1;\n\\elif :foo\nSELECT 2;\n\\else\nSELECT 3;\n\\endif\nSELECT
4;", "", Arrays.asList("SELECT 1;", "SELECT 4;")),
+ Arguments.of("unknown elif after matched elif", "\\if
false\nSELECT 1;\n\\elif true\nSELECT 2;\n\\elif :foo\nSELECT
3;\n\\else\nSELECT 4;\n\\endif\nSELECT 5;", "",
+ Arrays.asList("SELECT 2;", "SELECT 5;")),
+ Arguments.of("unknown variable condition", "\\if :foo\nSELECT
1;\n\\else\nSELECT 2;\n\\endif", "", Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("unknown backtick condition", "\\if `foo`\nSELECT
1;\n\\else\nSELECT 2;\n\\endif", "", Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("ambiguous boolean condition", "\\if o\nSELECT
1;\n\\else\nSELECT 2;\n\\endif", "", Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("missing boolean condition", "\\if\nSELECT
1;\n\\else\nSELECT 2;\n\\endif", "", Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("unrecognized boolean condition", "\\if
foo\nSELECT 1;\n\\else\nSELECT 2;\n\\endif", "", Arrays.asList("SELECT 1;",
"SELECT 2;")),
+ Arguments.of("unknown elif condition", "\\if false\nSELECT
1;\n\\elif :foo\nSELECT 2;\n\\else\nSELECT 3;\n\\endif", "",
Arrays.asList("SELECT 2;", "SELECT 3;")),
+ Arguments.of("static condition inside unknown block", "\\if
:foo\n\\if false\nSELECT 1;\n\\endif\n\\else\nSELECT 2;\n\\endif", "",
Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("unknown condition inside inactive block", "\\if
false\n\\if :foo\nSELECT 1;\n\\else\nSELECT 2;\n\\endif\n\\else\nSELECT
3;\n\\endif", "",
+ Collections.singletonList("SELECT 3;")),
+ Arguments.of("unknown condition inside active block", "\\if
true\n\\if :foo\nSELECT 1;\n\\else\nSELECT 2;\n\\endif\n\\else\nSELECT
3;\n\\endif", "",
+ Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("query interior conditional", "SELECT\n\\if
true\n1\n\\else\n2\n\\endif\n;\nSELECT 3;", "",
Collections.singletonList("SELECT 3;")),
+ Arguments.of("pending SQL across endif", "\\if true\nSELECT
1\n\\endif\n+ 2;\nSELECT 3;", "", Collections.singletonList("SELECT 3;")),
+ Arguments.of("pending SQL across else", "\\if true\nSELECT
1\n\\else\n+ 2;\n\\endif\nSELECT 3;", "", Collections.singletonList("SELECT
3;")),
+ Arguments.of("compound conditional command", "\\if false
\\else\nSELECT 1;\n\\endif\nSELECT 2;", "", Arrays.asList("SELECT 1;", "SELECT
2;")),
+ Arguments.of("compound inactive command", "\\if false\n\\echo
ignored \\else\nSELECT 1;\n\\endif\nSELECT 2;", "", Arrays.asList("SELECT 1;",
"SELECT 2;")),
+ Arguments.of("compound active command", "\\if true\n\\echo
ignored \\if false\n\\else\nSELECT 1;\n\\endif\n\\endif\nSELECT 2;", "",
Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("compound command hides nesting", "\\if
false\n\\echo ignored \\if true\n\\endif\n\\if false\nSELECT
1;\n\\endif\n\\endif\nSELECT 2;", "",
+ Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("compound command inside unknown block", "\\if
:foo\n\\echo ignored \\if true\n\\endif\n\\if false\nSELECT
1;\n\\endif\n\\endif\nSELECT 2;", "",
+ Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("compound root hides nesting", "\\if :foo \\if
true\n\\endif\n\\if false\nSELECT 1;\n\\endif\n\\endif\nSELECT 2;", "",
Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("compound command hides unknown root", "\\echo
ignored \\if :foo\n\\if false\nSELECT 1;\n\\endif\n\\endif\nSELECT 2;", "",
Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("conditional after same line SQL", "SELECT 1;
\\if false\nSELECT 2;\n\\endif\nSELECT 3;", "", Arrays.asList("SELECT 1;",
"SELECT 2;", "SELECT 3;")),
+ Arguments.of("unterminated conditional block", "\\if
false\nSELECT 1;\nSELECT 2;", "", Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("client buffer before unterminated conditional",
"\\;\n\\if false\nSELECT 1;", "", Collections.emptyList()),
+ Arguments.of("client buffer before conditional", "\\;\n\\if
true\nSELECT 1;\n\\else\nSELECT 2;\n\\endif", "",
Collections.singletonList("SELECT 2;")),
+ Arguments.of("client buffer inside conditional", "\\if
true\n\\;\n\\else\nSELECT 1;\nSELECT 2;\n\\endif\nSELECT 3;", "",
Arrays.asList("SELECT 2;", "SELECT 3;")),
+ Arguments.of("repeated else", "\\if false\nSELECT
1;\n\\else\nSELECT 2;\n\\else\nSELECT 3;\n\\endif\nSELECT 4;", "",
Arrays.asList("SELECT 1;", "SELECT 2;", "SELECT 3;", "SELECT 4;")),
+ Arguments.of("elif after else", "\\if true\nSELECT
1;\n\\else\nSELECT 2;\n\\elif true\nSELECT 3;\n\\endif", "",
Arrays.asList("SELECT 1;", "SELECT 2;", "SELECT 3;")),
+ Arguments.of("unexpected conditional arguments", "\\if
false\nSELECT 1;\n\\else foo\nSELECT 2;\n\\endif", "", Arrays.asList("SELECT
1;", "SELECT 2;")),
+ Arguments.of("unmatched conditional commands", "\\else\nSELECT
1;\n\\endif\nSELECT 2;", "", Arrays.asList("SELECT 1;", "SELECT 2;")),
+ Arguments.of("inactive echo anchor", "SELECT\n 1;\n\\if
false\nSELECT\n 1;\n\\endif\nSELECT 2;",
+ "SELECT\n 1;\nERROR: foo\n\\if false\nSELECT\n
1;\n\\endif\nSELECT 2;", Collections.singletonList("SELECT 2;")),
+ Arguments.of("error filtering stays conservative after endif",
"\\if false\n\\set ECHO all\n\\endif\nSELECT 'ERROR: foo';",
+ "\\if false\n\\set ECHO all\n\\endif\nSELECT 'ERROR:
foo';\nERROR: foo", Collections.singletonList("SELECT 'ERROR: foo';")),
+ Arguments.of("fallback before another independent block",
"\\if true\nSELECT 1\n\\endif\n+ 2;\n\\if false\nSELECT 3;\n\\endif\nSELECT
4;", "", Collections.singletonList("SELECT 4;")),
+ Arguments.of("fallback copy payload with quoted empty lines",
"\\if true\nCOPY foo FROM STDIN;\n'foo\n\nbar';\n\\.\nSELECT 1\n\\endif\n+
2;\nSELECT 3;", "",
+ Arrays.asList("COPY foo FROM STDIN;", "SELECT 3;")));
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("conditionValues")
+ void assertLoadConditionValues(final String expression, final boolean
active) {
+ Collection<ExternalSQLTestParameter> actual = template.load("sample",
Arrays.asList("\\if " + expression, "SELECT 1;", "\\else", "SELECT 2;",
"\\endif"),
+ Collections.emptyList(), "PostgreSQL", "CSV");
+
assertThat(actual.stream().map(ExternalSQLTestParameter::getSql).collect(Collectors.toList()),
is(Collections.singletonList(active ? "SELECT 1;" : "SELECT 2;")));
+ }
+
+ private static Stream<Arguments> conditionValues() {
+ return Stream.concat(Stream.of("true", "TRUE", "t", "tr", "tru", "1",
"on", "yes", "y", "ye").map(value -> Arguments.of(value, true)),
+ Stream.of("false", "FALSE", "f", "fa", "fal", "fals", "0",
"off", "of", "no", "n").map(value -> Arguments.of(value, false)));
+ }
+
@Test
void assertLoadNativeError() {
Collection<ExternalSQLTestParameter> actual = template.load("sample",
Collections.singletonList("SELECT 1;"), Arrays.asList("SELECT 1;", "ERROR:
foo"), "PostgreSQL", "CSV");