Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,655 changes: 1,893 additions & 1,762 deletions src/parser/bison_parser.cpp

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/parser/bison_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,11 @@ union HSQL_STYPE
hsql::RowLockWaitPolicy lock_wait_policy_t;

hsql::ImportExportOptions* import_export_option_t;
std::pair<hsql::CsvOptionType, char*>* csv_option_t;

// clang-format off

#line 351 "bison_parser.h"
#line 352 "bison_parser.h"

};
typedef union HSQL_STYPE HSQL_STYPE;
Expand Down
112 changes: 111 additions & 1 deletion src/parser/bison_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
hsql::RowLockWaitPolicy lock_wait_policy_t;

hsql::ImportExportOptions* import_export_option_t;
std::pair<hsql::CsvOptionType, char*>* csv_option_t;

// clang-format off
}
Expand Down Expand Up @@ -198,6 +199,7 @@
}
delete ($$);
} <table_vec> <table_element_vec> <update_vec> <expr_vec> <order_vec> <stmt_vec>
%destructor { free($$->second); delete ($$); } <csv_option_t>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
%destructor { free($$->second); delete ($$); } <csv_option_t>
%destructor {
free($$->second);
delete ($$);
} <csv_option_t>

%destructor { delete ($$); } <*>


Expand Down Expand Up @@ -292,6 +294,7 @@
// ImportType is used for compatibility reasons
%type <import_type_t> file_type
%type <import_export_option_t> opt_import_export_options import_export_options
%type <csv_option_t> csv_option

%type <str_vec> ident_commalist opt_column_list
%type <expr_vec> expr_list select_list opt_extended_literal_list extended_literal_list hint_list opt_hints opt_partition
Expand Down Expand Up @@ -467,6 +470,10 @@ import_statement : IMPORT FROM file_type FILE file_path INTO table_name {
$$->encoding = $5->encoding;
$5->encoding = nullptr;
}
if ($5->csv_options) {
$$->csv_options = $5->csv_options;
$5->csv_options = nullptr;
}
delete $5;
};

Expand Down Expand Up @@ -497,6 +504,11 @@ import_export_options : import_export_options ',' FORMAT file_type {
yyerror(&yyloc, result, scanner, "File type must only be provided once.");
YYERROR;
}
if ($1->csv_options && $4 != kImportCSV && $4 != kImportAuto) {
delete $1;
yyerror(&yyloc, result, scanner, "CSV options (DELIMITER, NULL, QUOTE) are only allowed for CSV files.");
YYERROR;
}
$1->format = $4;
$$ = $1;
}
Expand All @@ -517,7 +529,97 @@ import_export_options : import_export_options ',' FORMAT file_type {
| ENCODING STRING {
$$ = new ImportExportOptions{};
$$->encoding = $2;
};
}
| import_export_options ',' csv_option {
if ($1->format != kImportAuto && $1->format != kImportCSV) {
delete $1;
free($3->second);
delete $3;
yyerror(&yyloc, result, scanner, "CSV options (DELIMITER, NULL, QUOTE) are only allowed for CSV files.");
YYERROR;
}

if ($1->csv_options == nullptr) {
$1->csv_options = new CsvOptions{};
}

bool freed_ptr = false;
auto free_if_necessary = [&](char* ptr) {
if (ptr != nullptr) {
free(ptr);
freed_ptr = true;
}
};

switch ($3->first) {
case CsvOptionType::Delimiter:
free_if_necessary($1->csv_options->delimiter);
$1->csv_options->delimiter = $3->second;
break;
case CsvOptionType::Null:
free_if_necessary($1->csv_options->null);
$1->csv_options->null = $3->second;
break;
case CsvOptionType::Quote:
free_if_necessary($1->csv_options->quote);
$1->csv_options->quote = $3->second;
break;
default:
free($3->second);
delete $3;
delete $1;
yyerror(&yyloc, result, scanner, "Unknown CSV option.");
YYERROR;
}
delete $3;

if (freed_ptr) {
delete $1;
yyerror(&yyloc, result, scanner, "CSV options (DELIMITER, NULL, QUOTE) cannot be provided more than once.");
YYERROR;
}

$$ = $1;
}
| csv_option {
$$ = new ImportExportOptions{};
$$->csv_options = new CsvOptions{};

switch ($1->first) {
case CsvOptionType::Delimiter:
$$->csv_options->delimiter = $1->second;
break;
case CsvOptionType::Null:
$$->csv_options->null = $1->second;
break;
case CsvOptionType::Quote:
$$->csv_options->quote = $1->second;
break;
default:
free($1->second);
delete $1;
delete $$;
yyerror(&yyloc, result, scanner, "Unknown CSV option.");
YYERROR;
}

delete $1;
}

csv_option : IDENTIFIER STRING {
if (strcasecmp($1, "DELIMITER") == 0) {
$$ = new std::pair<CsvOptionType, char*>(CsvOptionType::Delimiter, $2);
} else if (strcasecmp($1, "QUOTE") == 0) {
$$ = new std::pair<CsvOptionType, char*>(CsvOptionType::Quote, $2);
} else {
free($1);
free($2);
yyerror(&yyloc, result, scanner, "Unknown CSV option.");
YYERROR;
}
free($1);
}
| NULL STRING { $$ = new std::pair<CsvOptionType, char*>(CsvOptionType::Null, $2); }

/******************************
* Export Statement
Expand All @@ -533,6 +635,10 @@ export_statement : COPY table_name TO file_path opt_import_export_options {
$$->encoding = $5->encoding;
$5->encoding = nullptr;
}
if ($5->csv_options) {
$$->csv_options = $5->csv_options;
$5->csv_options = nullptr;
}
delete $5;
}
| COPY select_with_paren TO file_path opt_import_export_options {
Expand All @@ -543,6 +649,10 @@ export_statement : COPY table_name TO file_path opt_import_export_options {
$$->encoding = $5->encoding;
$5->encoding = nullptr;
}
if ($5->csv_options) {
$$->csv_options = $5->csv_options;
$5->csv_options = nullptr;
}
delete $5;
};

Expand Down
1 change: 1 addition & 0 deletions src/sql/ExportStatement.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct ExportStatement : SQLStatement {
char* tableName;
SelectStatement* select;
char* encoding;
CsvOptions* csv_options;
};

} // namespace hsql
Expand Down
16 changes: 16 additions & 0 deletions src/sql/ImportExportOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,28 @@ enum ImportType {
kImportAuto
};

enum CsvOptionType {
Delimiter,
Null,
Quote,
};

struct CsvOptions {
CsvOptions();
~CsvOptions();

char* delimiter;
char* null;
char* quote;
};

struct ImportExportOptions {
ImportExportOptions();
~ImportExportOptions();

ImportType format;
char* encoding;
CsvOptions* csv_options;
};

} // namespace hsql
Expand Down
1 change: 1 addition & 0 deletions src/sql/ImportStatement.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct ImportStatement : SQLStatement {
char* tableName;
Expr* whereClause;
char* encoding;
CsvOptions* csv_options;
};

} // namespace hsql
Expand Down
22 changes: 18 additions & 4 deletions src/sql/statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,19 +140,31 @@ ExportStatement::ExportStatement(ImportType type)
schema(nullptr),
tableName(nullptr),
select(nullptr),
encoding(nullptr) {}
encoding(nullptr),
csv_options(nullptr) {}

ExportStatement::~ExportStatement() {
free(filePath);
free(schema);
free(tableName);
delete select;
free(encoding);
delete csv_options;
}

ImportExportOptions::ImportExportOptions() : format(kImportAuto), encoding(nullptr) {}
CsvOptions::CsvOptions() : delimiter(nullptr), null(nullptr), quote(nullptr) {}
CsvOptions::~CsvOptions() {
free(delimiter);
free(null);
free(quote);
}

ImportExportOptions::ImportExportOptions() : format(kImportAuto), encoding(nullptr), csv_options(nullptr) {}

ImportExportOptions::~ImportExportOptions() { free(encoding); }
ImportExportOptions::~ImportExportOptions() {
free(encoding);
delete csv_options;
}

// ImportStatement
ImportStatement::ImportStatement(ImportType type)
Expand All @@ -162,14 +174,16 @@ ImportStatement::ImportStatement(ImportType type)
schema(nullptr),
tableName(nullptr),
whereClause(nullptr),
encoding(nullptr) {}
encoding(nullptr),
csv_options(nullptr) {}

ImportStatement::~ImportStatement() {
free(filePath);
free(schema);
free(tableName);
delete whereClause;
free(encoding);
delete csv_options;
}

// InsertStatement
Expand Down
8 changes: 8 additions & 0 deletions test/queries/queries-bad.sql
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,11 @@
!SELECT * FROM foo WITH HINT (?);
!SELECT * FROM foo WITH HINT (CAST(column_a AS INT));
!SELECT * FROM foo WITH HINT (AVG(another_column));
# CSV options
!COPY students FROM 'file_path' WITH (FORMAT TBL, DELIMITER '|', NULL '', QUOTE '"');
!COPY students FROM 'file_path' WITH (DELIMITER '|', NULL '', QUOTE '"', FORMAT TBL);
!COPY students FROM 'file_path' WITH (DELIMITER '|', NULL '', FORMAT TBL, QUOTE '"');
!COPY students FROM 'file_path' WITH (DELIMITER '|', NULL '', QUOTE '"', NULL 'a');
!COPY students FROM 'file_path' WITH (NULL '', QUOTE '"', DELIMITER '|', DELIMITER '/');
!COPY students FROM 'file_path' WITH (QUOTE '"', NULL '', DELIMITER '/', QUOTE '_',);
!COPY students FROM 'file_path' WITH (FORMAT CSV, QUOTE '"', DELIMINIMITER '|');
4 changes: 4 additions & 0 deletions test/queries/queries-good.sql
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ COPY students FROM 'file_path' WITH (FORMAT TBL);
COPY students FROM 'file_path' WITH (FORMAT CSV);
COPY students FROM 'file_path' WITH (FORMAT BIN);
COPY students FROM 'file_path' WITH (FORMAT BINARY);
COPY students FROM 'file_path' WITH (FORMAT CSV, DELIMITER '|', NULL '', QUOTE '"');
COPY students FROM 'file_path' WITH (DELIMITER '|', NULL '', FORMAT CSV, QUOTE '"');
COPY students FROM 'file_path' WITH (DELIMITER '|', NULL '', QUOTE '"');
COPY students FROM 'file_path' WITH (DELIMITER '|', FORMAT CSV);
COPY students FROM 'file_path' (FORMAT TBL);
COPY good_students FROM 'file_path' WHERE grade > (SELECT AVG(grade) from alumni);
COPY students TO 'student.tbl';
Expand Down
20 changes: 15 additions & 5 deletions test/sql_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -466,16 +466,23 @@ TEST(ImportStatementTest) {
}

TEST(CopyStatementTest) {
TEST_PARSE_SINGLE_SQL("COPY students FROM 'students_file' WITH (FORMAT BINARY);", kStmtImport, ImportStatement,
import_result, import_stmt);
TEST_PARSE_SINGLE_SQL("COPY students FROM 'students_file' WITH (FORMAT CSV, DELIMITER '|', NULL '', QUOTE '\"');",
kStmtImport, ImportStatement, import_result, import_stmt);

ASSERT_EQ(import_stmt->type, kImportBinary);
ASSERT_EQ(import_stmt->type, kImportCSV);
ASSERT_NOTNULL(import_stmt->tableName);
ASSERT_STREQ(import_stmt->tableName, "students");
ASSERT_NOTNULL(import_stmt->filePath);
ASSERT_STREQ(import_stmt->filePath, "students_file");
ASSERT_NULL(import_stmt->whereClause);
ASSERT_NULL(import_stmt->encoding);
ASSERT_NOTNULL(import_stmt->csv_options);
ASSERT_NOTNULL(import_stmt->csv_options->delimiter);
ASSERT_STREQ(import_stmt->csv_options->delimiter, "|");
ASSERT_NOTNULL(import_stmt->csv_options->null);
ASSERT_STREQ(import_stmt->csv_options->null, "");
ASSERT_NOTNULL(import_stmt->csv_options->quote);
ASSERT_STREQ(import_stmt->csv_options->quote, "\"");

TEST_PARSE_SINGLE_SQL("COPY students FROM 'students_file' WHERE lastname = 'Potter';", kStmtImport, ImportStatement,
import_filter_result, import_filter_stmt);
Expand All @@ -492,17 +499,19 @@ TEST(CopyStatementTest) {
ASSERT_EQ(import_filter_stmt->whereClause->expr2->type, kExprLiteralString);
ASSERT_STREQ(import_filter_stmt->whereClause->expr2->name, "Potter");
ASSERT_NULL(import_filter_stmt->encoding);
ASSERT_NULL(import_filter_stmt->csv_options);

TEST_PARSE_SINGLE_SQL("COPY students TO 'students_file' WITH (ENCODING 'FSST', FORMAT CSV);", kStmtExport,
TEST_PARSE_SINGLE_SQL("COPY students TO 'students_file' WITH (ENCODING 'FSST', FORMAT BINARY);", kStmtExport,
ExportStatement, export_table_result, export_table_stmt);

ASSERT_EQ(export_table_stmt->type, kImportCSV);
ASSERT_EQ(export_table_stmt->type, kImportBinary);
ASSERT_NOTNULL(export_table_stmt->tableName);
ASSERT_STREQ(export_table_stmt->tableName, "students");
ASSERT_NOTNULL(export_table_stmt->filePath);
ASSERT_STREQ(export_table_stmt->filePath, "students_file");
ASSERT_NULL(export_table_stmt->select);
ASSERT_STREQ(export_table_stmt->encoding, "FSST");
ASSERT_NULL(export_table_stmt->csv_options);

TEST_PARSE_SINGLE_SQL(
"COPY (SELECT firstname, lastname FROM students) TO 'students_file' WITH (ENCODING 'Dictionary');", kStmtExport,
Expand All @@ -513,6 +522,7 @@ TEST(CopyStatementTest) {
ASSERT_NOTNULL(export_select_stmt->filePath);
ASSERT_STREQ(export_select_stmt->filePath, "students_file");
ASSERT_STREQ(export_select_stmt->encoding, "Dictionary");
ASSERT_NULL(export_select_stmt->csv_options);

ASSERT_NOTNULL(export_select_stmt->select);
const auto& select_stmt = export_select_stmt->select;
Expand Down
Loading