From 78eefd87967a8b2f90ec857ee0aa961cb90d7149 Mon Sep 17 00:00:00 2001 From: liquidaty Date: Fri, 9 Dec 2022 14:01:18 -0500 Subject: [PATCH] quote-pending bug fix (#89) * fix bug whereby a quote falling on exactly the last byte of a chunk embedded in a quoted value can cause incorrect subsequent cell parsing * add test coverage --- app/Makefile | 1 - app/test/Makefile | 15 +++++++-- app/test/expected/test-serialize-quoted.out | 1 + data/quoted5.csv.bz2 | Bin 0 -> 32707 bytes include/zsv/common.h | 1 + src/zsv.c | 6 +--- src/zsv_internal.c | 35 +++++++++----------- src/zsv_scan_delim.c | 19 ++++++----- 8 files changed, 43 insertions(+), 35 deletions(-) create mode 100644 app/test/expected/test-serialize-quoted.out create mode 100644 data/quoted5.csv.bz2 diff --git a/app/Makefile b/app/Makefile index e540a31d..507474bf 100755 --- a/app/Makefile +++ b/app/Makefile @@ -333,7 +333,6 @@ ${JQ_SRC}: ${JQ_TARBALL} mv $@-tmp/jq-1.6 $@ rm -rf $@-tmp - lib-jq: ${JQ_LIB} @echo "Using jq library ${JQ_LIB}" diff --git a/app/test/Makefile b/app/test/Makefile index da4676ec..c675739d 100644 --- a/app/test/Makefile +++ b/app/test/Makefile @@ -232,15 +232,26 @@ test-2tsv test-sql test-flatten test-pretty : test-%: ${BUILD_DIR}/bin/zsv_%${EX ${CMP} ${TMP_DIR}/$@.out expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}) # @if [ "$@" = "test-2tsv" ] ; then echo "TO DO: update 2tsv to output Excel format-- see data/Excel.tsv"; fi -test-serialize : test-%: ${BUILD_DIR}/bin/zsv_%${EXE} +${THIS_MAKEFILE_DIR}/../../data/quoted5.csv: ${THIS_MAKEFILE_DIR}/../../data/quoted5.csv.bz2 + bzip2 -d -c $< > $@ + +test-serialize-quoted: ${BUILD_DIR}/bin/zsv_serialize${EXE} ${THIS_MAKEFILE_DIR}/../../data/quoted5.csv + @${TEST_INIT} + @rm -f ${TMP_DIR}/$@.out + @(${PREFIX} $< ${THIS_MAKEFILE_DIR}/../../data/quoted5.csv | grep xxxxxx | grep Produktinfo ${REDIRECT1} ${TMP_DIR}/$@.out && \ + ${CMP} ${TMP_DIR}/$@.out expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}) + +test-serialize : test-%: ${BUILD_DIR}/bin/zsv_%${EXE} test-serialize-quoted @${TEST_INIT} @( ( ! [ -s "${TEST_DATA_DIR}/test/$*.csv" ] ) && echo "No test input for $*") || \ (${PREFIX} $< < ${TEST_DATA_DIR}/test/$*.csv ${REDIRECT1} ${TMP_DIR}/$@.out && \ ${CMP} ${TMP_DIR}/$@.out expected/$@.out && ${TEST_PASS} || ${TEST_FAIL}) - (${PREFIX} $< -p < ${TEST_DATA_DIR}/test/$*.csv ${REDIRECT1} ${TMP_DIR}/$@-2.out && \ + @(${PREFIX} $< -p < ${TEST_DATA_DIR}/test/$*.csv ${REDIRECT1} ${TMP_DIR}/$@-2.out && \ ${CMP} ${TMP_DIR}/$@-2.out expected/$@-2.out && ${TEST_PASS} || ${TEST_FAIL}) + @(${PREFIX} $< -p < ${TEST_DATA_DIR}/test/$*.csv ${REDIRECT1} ${TMP_DIR}/$@-2.out && \ + ${CMP} ${TMP_DIR}/$@-2.out expected/$@-2.out && ${TEST_PASS} || ${TEST_FAIL}) test-sql: test-sql2 test-sql3 test-sql4 test-sql2: ${BUILD_DIR}/bin/zsv_sql${EXE} diff --git a/app/test/expected/test-serialize-quoted.out b/app/test/expected/test-serialize-quoted.out new file mode 100644 index 00000000..d75efb7f --- /dev/null +++ b/app/test/expected/test-serialize-quoted.out @@ -0,0 +1 @@ +xxxxxxyy,Produktinfo,"4__________________________________________________________________a________=""____-____________________________________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-_______________________b____________-_____,""_______________________-_________________/______/______/______/______/________/_________________=""____-_______________________c____________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-_________________________d__________-_____,""____________,_____,__________________,_______________,__________,_______________,___.__/______/______/______/______/_________________=""____-________________________e___________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-_________________________f__________-_____,""_________________,_____________________________,____________/______/______/______/________________=""____""____________=""____-_________________________g__________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-_________________________h__________-_____,""___________________________________________________,________________%___________________%____________/______/______/______/______/________________=""____-_________________________i__________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-_________________________j__________-_____,""__________-_______________________________________.____/__/______/______/______/______/______________=""____-_________________________k__________-_____,""____________=""____-________________________l___________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-_________________________m__________-_____,""__/______/______/______/______/__________________________=""____-____________________________________-_____,""_______________________/______/___________________=""____-____________________________________-_____,""___________________/______/___________________=""____-____________________________________-_____,""__________________/______(_________________,_______)_/___________________=""____-____________________________________-_____,""______________,____(_______________,_)____/__/______/____________________________,___________,_/_______________________,________________________=""____-____________________________________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________________-_____,""________________=""____-_______-_____,""__/______/_____/______/______/______/______/_____/__________________=""____-____________________________________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________________-_____,""____________=""____-____________________________________-_____,""__________,__________,__________________________-_________._/______/______/______/______/______/_________________=""____-____________________________________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________-_____,""____________=""____-____________________________________-_____,""____________=""____-____________________________________-_____,""________!_____________,__________________________._/______/______/______/______/______/____/______/______/______/______/______/______/______/______/____" diff --git a/data/quoted5.csv.bz2 b/data/quoted5.csv.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..0f8ad1cb4ee7f883ceae7ef52c0ac556b597098f GIT binary patch literal 32707 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 zN+w08elast = scanner->buff.buff[scanner->old_bytes_read-1]; if(scanner->row_start < scanner->old_bytes_read) { size_t len = scanner->old_bytes_read - scanner->row_start; - if(len < scanner->row_start) - memcpy(scanner->buff.buff, scanner->buff.buff + scanner->row_start, len); - else - memmove(scanner->buff.buff, scanner->buff.buff + scanner->row_start, len); + memmove(scanner->buff.buff, scanner->buff.buff + scanner->row_start, len); scanner->partial_row_length = len; } else { scanner->cell_start = 0; @@ -79,7 +76,6 @@ inline static size_t scanner_pre_parse(struct zsv_scanner *scanner) { return capacity; } - /** * Read the next chunk of data from our input stream and parse it, calling our * custom handlers as each cell and row are parsed diff --git a/src/zsv_internal.c b/src/zsv_internal.c index 5856e230..ae705254 100644 --- a/src/zsv_internal.c +++ b/src/zsv_internal.c @@ -526,26 +526,23 @@ static void collate_header_row(void *ctx) { if(!scanner->opts.header_span) { // finished with header; combine all rows into a single row set_callbacks(scanner); -// if(VERY_LIKELY(scanner->opts.row_handler != NULL || scanner->opts.cell_handler != NULL -// || scanner->mode == ZSV_MODE_DELIM_PULL)) { - if(scanner->collate_header) { - size_t offset = 0; - for(size_t i = 0; i < scanner->collate_header->column_count; i++) { - size_t len_plus1 = scanner->collate_header->lengths[i]; - scanner->row.cells[i].str = scanner->collate_header->buff.buff + offset; - if(len_plus1) { - scanner->row.cells[i].len = len_plus1 - 1; - scanner->row.cells[i].quoted = 1; - } else - scanner->row.cells[i].len = 0; - offset += len_plus1; - } + if(scanner->collate_header) { + size_t offset = 0; + for(size_t i = 0; i < scanner->collate_header->column_count; i++) { + size_t len_plus1 = scanner->collate_header->lengths[i]; + scanner->row.cells[i].str = scanner->collate_header->buff.buff + offset; + if(len_plus1) { + scanner->row.cells[i].len = len_plus1 - 1; + scanner->row.cells[i].quoted = 1; + } else + scanner->row.cells[i].len = 0; + offset += len_plus1; } + } - apply_callbacks(scanner); - if(scanner->mode != ZSV_MODE_DELIM_PULL) - collate_header_destroy(&scanner->collate_header); -// } + apply_callbacks(scanner); + if(scanner->mode != ZSV_MODE_DELIM_PULL) + collate_header_destroy(&scanner->collate_header); } } @@ -580,7 +577,7 @@ static void zsv_throwaway_row(void *ctx) { static int zsv_scanner_init(struct zsv_scanner *scanner, struct zsv_opts *opts) { - size_t need_buff_size = 0; // opts->buffsize + size_t need_buff_size = 0; if(opts->buffsize < opts->max_row_size * 2) need_buff_size = opts->max_row_size * 2; opts->delimiter = opts->delimiter ? opts->delimiter : ','; diff --git a/src/zsv_scan_delim.c b/src/zsv_scan_delim.c index caef8a02..7fe9f1aa 100644 --- a/src/zsv_scan_delim.c +++ b/src/zsv_scan_delim.c @@ -72,7 +72,6 @@ static enum zsv_status ZSV_SCAN_DELIM(struct zsv_scanner *scanner, skip_next_delim = 0; bytes_chunk_end = bytes_read >= sizeof(zsv_uc_vector) ? bytes_read - sizeof(zsv_uc_vector) + 1 : 0; delimiter = scanner->opts.delimiter; - scanner->partial_row_length = 0; // to do: move into one-time execution code? @@ -82,12 +81,15 @@ static enum zsv_status ZSV_SCAN_DELIM(struct zsv_scanner *scanner, memset(&v.nl, '\n', sizeof(zsv_uc_vector)); // ascii code 10 memset(&v.cr, '\r', sizeof(zsv_uc_vector)); // ascii code 13 memset(&v.qt, scanner->opts.no_quotes > 0 ? 0 : '"', sizeof(v.qt)); - // case "hel"|"o": check if we have an embedded dbl-quote past the initial opening quote, which was - // split between the last buffer and this one e.g. "hel""o" where the last buffer ended - // with "hel" and this one starts with "o" - if((scanner->quoted & ZSV_PARSER_QUOTE_UNCLOSED) - && i > scanner->cell_start + 1 // case "|hello": need the + 1 in case split after first char of quoted value e.g. "hello" => " and hello" - && scanner->last == quote) { + + if(scanner->quoted & ZSV_PARSER_QUOTE_PENDING) { + // if we're here, then the last chunk we read ended with a lone quote char inside + // a quoted cell, and we are waiting to find out whether it is followed by + // another dbl-quote e.g. if the end of the last chunk is |, we had: + // ...,"hel"|"o" + // ...,"hel"|,... + // ...,"hel"|p,... + scanner->quoted -= ZSV_PARSER_QUOTE_PENDING; if(buff[i] != quote) { scanner->quoted |= ZSV_PARSER_QUOTE_CLOSED; scanner->quoted -= ZSV_PARSER_QUOTE_UNCLOSED; @@ -229,7 +231,8 @@ static enum zsv_status ZSV_SCAN_DELIM(struct zsv_scanner *scanner, scanner->quoted |= ZSV_PARSER_QUOTE_EMBEDDED; skip_next_delim = 1; } - } + } else // we are at the end of this input chunk + scanner->quoted |= ZSV_PARSER_QUOTE_PENDING; } else { // cell_length > 0 and cell did not start w quote, so // we have a quote in middle of an unquoted cell