From 5b202fea877bc5f05f36cd51c58f172d23d6d30c Mon Sep 17 00:00:00 2001 From: Tim Sehn Date: Tue, 12 Aug 2025 14:44:21 -0700 Subject: [PATCH 1/4] Made LOAD DATA handle NULL values and defaults correctly --- enginetest/queries/load_queries.go | 13 +++++++++++++ enginetest/testdata/load_defaults_null.csv | 1 + sql/rowexec/ddl_iters.go | 16 ++++++++++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 enginetest/testdata/load_defaults_null.csv diff --git a/enginetest/queries/load_queries.go b/enginetest/queries/load_queries.go index a3faedab3e..6ae996be2a 100644 --- a/enginetest/queries/load_queries.go +++ b/enginetest/queries/load_queries.go @@ -22,6 +22,19 @@ import ( ) var LoadDataScripts = []ScriptTest{ + { + Name: "LOAD DATA applies column defaults when \\N provided", + SetUpScript: []string{ + "create table t (pk int primary key, c1 int default 1, c2 int)", + "LOAD DATA INFILE './testdata/load_defaults_null.csv' INTO TABLE t FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select * from t", + Expected: []sql.Row{{1, 1, 1}}, + }, + }, + }, { Name: "Basic load data with enclosed values.", SetUpScript: []string{ diff --git a/enginetest/testdata/load_defaults_null.csv b/enginetest/testdata/load_defaults_null.csv new file mode 100644 index 0000000000..328fe091bd --- /dev/null +++ b/enginetest/testdata/load_defaults_null.csv @@ -0,0 +1 @@ +1,\N,1 diff --git a/sql/rowexec/ddl_iters.go b/sql/rowexec/ddl_iters.go index ebf32fca20..5d166d0e7a 100644 --- a/sql/rowexec/ddl_iters.go +++ b/sql/rowexec/ddl_iters.go @@ -206,8 +206,20 @@ func (l *loadDataIter) parseFields(ctx *sql.Context, line string) ([]sql.Express exprs[exprIdx] = expression.NewLiteral(nil, types.Null) } } - case "NULL": - exprs[exprIdx] = expression.NewLiteral(nil, types.Null) + case "NULL": + // For MySQL LOAD DATA semantics, \N (mapped to NULL here) should use the column default + // if one exists; otherwise insert NULL. + destIdx := l.fieldToColMap[fieldIdx] + if destIdx >= 0 { + destCol := l.destSch[destIdx] + if destCol.Default != nil { + exprs[exprIdx] = destCol.Default + } else { + exprs[exprIdx] = expression.NewLiteral(nil, types.Null) + } + } else { + exprs[exprIdx] = expression.NewLiteral(nil, types.Null) + } default: exprs[exprIdx] = expression.NewLiteral(field, types.LongText) } From d1d4f8dfc0cc3b0fc788b2af50dcbbf1aa13954d Mon Sep 17 00:00:00 2001 From: timsehn Date: Tue, 12 Aug 2025 21:46:47 +0000 Subject: [PATCH 2/4] [ga-format-pr] Run ./format_repo.sh to fix formatting --- sql/rowexec/ddl_iters.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sql/rowexec/ddl_iters.go b/sql/rowexec/ddl_iters.go index 5d166d0e7a..becf91167e 100644 --- a/sql/rowexec/ddl_iters.go +++ b/sql/rowexec/ddl_iters.go @@ -206,20 +206,20 @@ func (l *loadDataIter) parseFields(ctx *sql.Context, line string) ([]sql.Express exprs[exprIdx] = expression.NewLiteral(nil, types.Null) } } - case "NULL": - // For MySQL LOAD DATA semantics, \N (mapped to NULL here) should use the column default - // if one exists; otherwise insert NULL. - destIdx := l.fieldToColMap[fieldIdx] - if destIdx >= 0 { - destCol := l.destSch[destIdx] - if destCol.Default != nil { - exprs[exprIdx] = destCol.Default - } else { - exprs[exprIdx] = expression.NewLiteral(nil, types.Null) - } - } else { - exprs[exprIdx] = expression.NewLiteral(nil, types.Null) - } + case "NULL": + // For MySQL LOAD DATA semantics, \N (mapped to NULL here) should use the column default + // if one exists; otherwise insert NULL. + destIdx := l.fieldToColMap[fieldIdx] + if destIdx >= 0 { + destCol := l.destSch[destIdx] + if destCol.Default != nil { + exprs[exprIdx] = destCol.Default + } else { + exprs[exprIdx] = expression.NewLiteral(nil, types.Null) + } + } else { + exprs[exprIdx] = expression.NewLiteral(nil, types.Null) + } default: exprs[exprIdx] = expression.NewLiteral(field, types.LongText) } From ca8ac2a96d1b08019e8f306f104258b2114bae57 Mon Sep 17 00:00:00 2001 From: Tim Sehn Date: Tue, 12 Aug 2025 15:00:22 -0700 Subject: [PATCH 3/4] Fix test on Windows --- enginetest/queries/load_queries.go | 27 +++++++++++----------- enginetest/testdata/load_defaults_null.csv | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/enginetest/queries/load_queries.go b/enginetest/queries/load_queries.go index 6ae996be2a..4db6e20882 100644 --- a/enginetest/queries/load_queries.go +++ b/enginetest/queries/load_queries.go @@ -22,19 +22,20 @@ import ( ) var LoadDataScripts = []ScriptTest{ - { - Name: "LOAD DATA applies column defaults when \\N provided", - SetUpScript: []string{ - "create table t (pk int primary key, c1 int default 1, c2 int)", - "LOAD DATA INFILE './testdata/load_defaults_null.csv' INTO TABLE t FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n'", - }, - Assertions: []ScriptTestAssertion{ - { - Query: "select * from t", - Expected: []sql.Row{{1, 1, 1}}, - }, - }, - }, + { + Name: "LOAD DATA applies column defaults when \\N provided", + SetUpScript: []string{ + "create table t (pk int primary key, c1 int default 1, c2 int)", + // Explicitly use Windows-style line endings to be robust on Windows CI + "LOAD DATA INFILE './testdata/load_defaults_null.csv' INTO TABLE t FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n'", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select * from t", + Expected: []sql.Row{{1, 1, 1}}, + }, + }, + }, { Name: "Basic load data with enclosed values.", SetUpScript: []string{ diff --git a/enginetest/testdata/load_defaults_null.csv b/enginetest/testdata/load_defaults_null.csv index 328fe091bd..3fa94d7434 100644 --- a/enginetest/testdata/load_defaults_null.csv +++ b/enginetest/testdata/load_defaults_null.csv @@ -1 +1 @@ -1,\N,1 +1,\N,1 From fb4b16dcbbec83b998054aa3267840a9ba80645c Mon Sep 17 00:00:00 2001 From: timsehn Date: Tue, 12 Aug 2025 22:01:50 +0000 Subject: [PATCH 4/4] [ga-format-pr] Run ./format_repo.sh to fix formatting --- enginetest/queries/load_queries.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/enginetest/queries/load_queries.go b/enginetest/queries/load_queries.go index 4db6e20882..00f6cef701 100644 --- a/enginetest/queries/load_queries.go +++ b/enginetest/queries/load_queries.go @@ -22,20 +22,20 @@ import ( ) var LoadDataScripts = []ScriptTest{ - { - Name: "LOAD DATA applies column defaults when \\N provided", - SetUpScript: []string{ - "create table t (pk int primary key, c1 int default 1, c2 int)", - // Explicitly use Windows-style line endings to be robust on Windows CI - "LOAD DATA INFILE './testdata/load_defaults_null.csv' INTO TABLE t FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n'", - }, - Assertions: []ScriptTestAssertion{ - { - Query: "select * from t", - Expected: []sql.Row{{1, 1, 1}}, - }, - }, - }, + { + Name: "LOAD DATA applies column defaults when \\N provided", + SetUpScript: []string{ + "create table t (pk int primary key, c1 int default 1, c2 int)", + // Explicitly use Windows-style line endings to be robust on Windows CI + "LOAD DATA INFILE './testdata/load_defaults_null.csv' INTO TABLE t FIELDS TERMINATED BY ',' LINES TERMINATED BY '\r\n'", + }, + Assertions: []ScriptTestAssertion{ + { + Query: "select * from t", + Expected: []sql.Row{{1, 1, 1}}, + }, + }, + }, { Name: "Basic load data with enclosed values.", SetUpScript: []string{