Skip to content

Commit dc04d3b

Browse files
[uRFsgcuC] Backport of 5.x ifNeeded quote fix (#4176)
* [uRFsgcuC] Backport of 5.x ifNeeded quote fix * [uRFsgcuC] Tests
1 parent 2919385 commit dc04d3b

File tree

4 files changed

+69
-13
lines changed

4 files changed

+69
-13
lines changed

core-it/src/test/java/apoc/core/it/ExportCsvS3Test.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ public static void teardown() {
9595
+ ",,,,,,,,3,4,NEXT_DELIVERY%n");
9696
private static final String EXPECTED_NEEDED_QUOTES =
9797
String.format("_id,_labels,age,city,kids,male,name,street,_start,_end,_type%n"
98-
+ "0,:User:User1,42,,\"[\"a\",\"b\",\"c\"]\",true,foo,,,,%n"
98+
+ "0,:User:User1,42,,\"[\"\"a\"\",\"\"b\"\",\"\"c\"\"]\",true,foo,,,,%n"
9999
+ "1,:User,42,,,,bar,,,,%n"
100100
+ "2,:User,12,,,,,,,,%n"
101101
+ "3,:Address:Address1,,Milano,,,Andrea,\"Via Garibaldi, 7\",,,%n"

core/src/main/java/apoc/export/csv/CsvFormat.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public ProgressInfo dump(SubGraph graph, ExportFileManager writer, Reporter repo
101101
private CSVWriter getCsvWriter(Writer writer, ExportConfig config) {
102102
CSVWriter out;
103103
switch (config.isQuotes()) {
104-
case ExportConfig.NONE_QUOTES:
104+
case ExportConfig.NO_QUOTES:
105105
out = new CSVWriter(
106106
writer,
107107
config.getDelimChar(),
@@ -110,12 +110,12 @@ private CSVWriter getCsvWriter(Writer writer, ExportConfig config) {
110110
CSVWriter.DEFAULT_LINE_END);
111111
applyQuotesToAll = false;
112112
break;
113-
case ExportConfig.IF_NEEDED_QUUOTES:
113+
case ExportConfig.IF_NEEDED_QUOTES:
114114
out = new CSVWriter(
115115
writer,
116116
config.getDelimChar(),
117117
ExportConfig.QUOTECHAR,
118-
'\0', // escape char
118+
CSVWriter.DEFAULT_ESCAPE_CHARACTER,
119119
CSVWriter.DEFAULT_LINE_END);
120120
applyQuotesToAll = false;
121121
break;

core/src/main/java/apoc/export/util/ExportConfig.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ public NodeConfig(Map<String, String> config) {
4646
}
4747

4848
public static final char QUOTECHAR = '"';
49-
public static final String NONE_QUOTES = "none";
49+
public static final String NO_QUOTES = "none";
5050
public static final String ALWAYS_QUOTES = "always";
51-
public static final String IF_NEEDED_QUUOTES = "ifNeeded";
51+
public static final String IF_NEEDED_QUOTES = "ifNeeded";
5252

5353
public static final int DEFAULT_BATCH_SIZE = 20000;
5454
private static final int DEFAULT_UNWIND_BATCH_SIZE = 20;
@@ -196,12 +196,12 @@ private void exportQuotes(Map<String, Object> config) {
196196
try {
197197
this.quotes = (String) config.getOrDefault("quotes", DEFAULT_QUOTES);
198198

199-
if (!quotes.equals(ALWAYS_QUOTES) && !quotes.equals(NONE_QUOTES) && !quotes.equals(IF_NEEDED_QUUOTES)) {
199+
if (!quotes.equals(ALWAYS_QUOTES) && !quotes.equals(NO_QUOTES) && !quotes.equals(IF_NEEDED_QUOTES)) {
200200
throw new RuntimeException("The string value of the field quote is not valid");
201201
}
202202

203203
} catch (ClassCastException e) { // backward compatibility
204-
this.quotes = toBoolean(config.get("quotes")) ? ALWAYS_QUOTES : NONE_QUOTES;
204+
this.quotes = toBoolean(config.get("quotes")) ? ALWAYS_QUOTES : NO_QUOTES;
205205
}
206206
}
207207

core/src/test/java/apoc/export/csv/ExportCsvTest.java

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ public class ExportCsvTest {
9191
+ "\"Andrea\",\"Milano\",\"Via Garibaldi, 7\",\"[\"\"Address1\"\",\"\"Address\"\"]\"%n"
9292
+ "\"Bar Sport\",\"\",\"\",\"[\"\"Address\"\"]\"%n"
9393
+ "\"\",\"\",\"via Benni\",\"[\"\"Address\"\"]\"%n");
94-
private static final String EXPECTED_QUERY_QUOTES_NEEDED = String.format(
95-
"a.name,a.city,a.street,labels(a)%n" + "Andrea,Milano,\"Via Garibaldi, 7\",\"[\"Address1\",\"Address\"]\"%n"
96-
+ "Bar Sport,,,\"[\"Address\"]\"%n"
97-
+ ",,via Benni,\"[\"Address\"]\"%n");
94+
private static final String EXPECTED_QUERY_QUOTES_NEEDED = String.format("a.name,a.city,a.street,labels(a)%n"
95+
+ "Andrea,Milano,\"Via Garibaldi, 7\",\"[\"\"Address1\"\",\"\"Address\"\"]\"%n"
96+
+ "Bar Sport,,,\"[\"\"Address\"\"]\"%n"
97+
+ ",,via Benni,\"[\"\"Address\"\"]\"%n");
9898
private static final String EXPECTED = String.format(
9999
"\"_id\",\"_labels\",\"age\",\"city\",\"kids\",\"male\",\"name\",\"street\",\"_start\",\"_end\",\"_type\"%n"
100100
+ "\"0\",\":User:User1\",\"42\",\"\",\"[\"\"a\"\",\"\"b\"\",\"\"c\"\"]\",\"true\",\"foo\",\"\",,,%n"
@@ -134,14 +134,24 @@ public class ExportCsvTest {
134134
+ ",,,,,,,,3,4,NEXT_DELIVERY%n");
135135
private static final String EXPECTED_NEEDED_QUOTES =
136136
String.format("_id,_labels,age,city,kids,male,name,street,_start,_end,_type%n"
137-
+ "0,:User:User1,42,,\"[\"a\",\"b\",\"c\"]\",true,foo,,,,%n"
137+
+ "0,:User:User1,42,,\"[\"\"a\"\",\"\"b\"\",\"\"c\"\"]\",true,foo,,,,%n"
138138
+ "1,:User,42,,,,bar,,,,%n"
139139
+ "2,:User,12,,,,,,,,%n"
140140
+ "3,:Address:Address1,,Milano,,,Andrea,\"Via Garibaldi, 7\",,,%n"
141141
+ "4,:Address,,,,,Bar Sport,,,,%n"
142142
+ "5,:Address,,,,,,via Benni,,,%n"
143143
+ ",,,,,,,,0,1,KNOWS%n"
144144
+ ",,,,,,,,3,4,NEXT_DELIVERY%n");
145+
private static final String EXPECTED_QUOTES_ALWAYS =
146+
"\"_id\",\"_labels\",\"age\",\"city\",\"kids\",\"male\",\"name\",\"street\",\"_start\",\"_end\",\"_type\"\n"
147+
+ "\"0\",\":User:User1\",\"42\",\"\",\"[\"\"a\"\",\"\"b\"\",\"\"c\"\"]\",\"true\",\"foo\",\"\",,,\n"
148+
+ "\"1\",\":User\",\"42\",\"\",\"\",\"\",\"bar\",\"\",,,\n"
149+
+ "\"2\",\":User\",\"12\",\"\",\"\",\"\",\"\",\"\",,,\n"
150+
+ "\"3\",\":Address:Address1\",\"\",\"Milano\",\"\",\"\",\"Andrea\",\"Via Garibaldi, 7\",,,\n"
151+
+ "\"4\",\":Address\",\"\",\"\",\"\",\"\",\"Bar Sport\",\"\",,,\n"
152+
+ "\"5\",\":Address\",\"\",\"\",\"\",\"\",\"\",\"via Benni\",,,\n"
153+
+ ",,,,,,,,\"0\",\"1\",\"KNOWS\"\n"
154+
+ ",,,,,,,,\"3\",\"4\",\"NEXT_DELIVERY\"\n";
145155

146156
private static final File directory = new File("target/import");
147157

@@ -367,6 +377,17 @@ public void testExportAllCsvNeededQuotes() {
367377
assertEquals(EXPECTED_NEEDED_QUOTES, readFile(fileName));
368378
}
369379

380+
@Test
381+
public void testExportAllCsvAlwaysQuotes() {
382+
String fileName = "all.csv";
383+
TestUtil.testCall(
384+
db,
385+
"CALL apoc.export.csv.all($file,{quotes: 'always'})",
386+
map("file", fileName),
387+
(r) -> assertResults(fileName, r, "database"));
388+
assertEquals(EXPECTED_QUOTES_ALWAYS, readFile(fileName));
389+
}
390+
370391
@Test
371392
public void testExportGraphCsv() {
372393
String fileName = "graph.csv";
@@ -698,6 +719,41 @@ public void testExportWgsPoint() {
698719
db.executeTransactionally("MATCH (n:Position) DETACH DELETE n");
699720
}
700721

722+
@Test
723+
public void testGithubIssue4120() {
724+
String query = "UNWIND [\"\\\"Jan\\\" Apoc User\", \"\\\"Jan\\\" Apoc\\\"\\\" User\"] AS name RETURN name";
725+
String expected = "name\n" + "\"\"\"Jan\"\" Apoc User\"\n" + "\"\"\"Jan\"\" Apoc\"\"\"\" User\"\n";
726+
StringBuilder sb = new StringBuilder();
727+
testResult(
728+
db,
729+
"CALL apoc.export.csv.query($query,null,{quotes: 'ifNeeded', stream:true,batchSize:2})",
730+
map("query", query),
731+
(res) -> {
732+
Map<String, Object> r = res.next();
733+
assertEquals(2L, r.get("batchSize"));
734+
assertEquals(1L, r.get("batches"));
735+
assertEquals(0L, r.get("nodes"));
736+
assertEquals(2L, r.get("rows"));
737+
assertEquals(0L, r.get("relationships"));
738+
assertEquals(2L, r.get("properties"));
739+
assertNull("Should get file", r.get("file"));
740+
assertEquals("csv", r.get("format"));
741+
assertTrue("Should get time greater than 0", ((long) r.get("time")) >= 0);
742+
sb.append(r.get("data"));
743+
r = res.next();
744+
assertEquals(2L, r.get("batchSize"));
745+
assertEquals(2L, r.get("batches"));
746+
assertEquals(0L, r.get("nodes"));
747+
assertEquals(2L, r.get("rows"));
748+
assertEquals(0L, r.get("relationships"));
749+
assertEquals(2L, r.get("properties"));
750+
assertTrue("Should get time greater than 0", ((long) r.get("time")) >= 0);
751+
sb.append(r.get("data"));
752+
});
753+
754+
assertEquals(expected, sb.toString());
755+
}
756+
701757
private Consumer<Result> getAndCheckStreamingMetadataQueryMatchAddress(StringBuilder sb) {
702758
return (res) -> {
703759
Map<String, Object> r = res.next();

0 commit comments

Comments
 (0)