1- #include < vector >
1+ #include < utility >
22
3+ #include " duckdb/common/case_insensitive_map.hpp"
34#include " duckdb/common/exception.hpp"
45#include " duckdb/common/exception/binder_exception.hpp"
56#include " duckdb/common/file_system.hpp"
7+ #include " duckdb/common/types.hpp"
68#include " duckdb/common/types/value.hpp"
79
810#include " gsheets_copy.hpp"
@@ -24,131 +26,62 @@ GSheetCopyFunction::GSheetCopyFunction() : CopyFunction("gsheet") {
2426 copy_to_sink = GSheetWriteSink;
2527}
2628
29+ static std::string GetStringOption (const case_insensitive_map_t <vector<Value>> &options, const std::string &name,
30+ const std::string &default_value = " " ) {
31+ const auto it = options.find (name);
32+ if (it == options.end ()) {
33+ return default_value;
34+ }
35+ std::string err;
36+ Value val;
37+ if (!it->second .back ().DefaultTryCastAs (LogicalType::VARCHAR , val, &err)) {
38+ throw BinderException (name + " option must be VARCHAR" );
39+ }
40+ if (val.IsNull ()) {
41+ throw BinderException (name + " option must not be NULL" );
42+ }
43+ return StringValue::Get (val);
44+ }
45+
46+ // NOTE: the second value in pair is a flag indicating if the value was set by the user
47+ static std::pair<bool , bool > GetBoolOption (const case_insensitive_map_t <vector<Value>> &options,
48+ const std::string &name, bool default_value = false ) {
49+ const auto it = options.find (name);
50+ if (it == options.end ()) {
51+ return std::make_pair (default_value, false );
52+ }
53+ if (it->second .size () != 1 ) {
54+ throw BinderException (name + " option must be a single boolean value" );
55+ }
56+ std::string err;
57+ Value val;
58+ if (!it->second .back ().DefaultTryCastAs (LogicalType::BOOLEAN , val, &err)) {
59+ throw BinderException (name + " option must be a single boolean value" );
60+ }
61+ if (val.IsNull ()) {
62+ throw BinderException (name + " option must be a single boolean value" );
63+ }
64+ return std::make_pair (BooleanValue::Get (val), true );
65+ }
66+
2767unique_ptr<FunctionData> GSheetCopyFunction::GSheetWriteBind (ClientContext &context, CopyFunctionBindInput &input,
2868 const vector<string> &names,
2969 const vector<LogicalType> &sql_types) {
3070
31- // TODO(mh): refactor option resolution
3271 string file_path = input.info .file_path ;
3372 auto options = input.info .options ;
3473
35- const auto sheet_opt = options.find (" sheet" );
36- std::string sheet;
37- if (sheet_opt != options.end ()) {
38- string error_msg;
39- Value sheet_val;
40- if (!sheet_opt->second .back ().DefaultTryCastAs (LogicalType::VARCHAR , sheet_val, &error_msg)) {
41- throw BinderException (" sheet option must be a VARCHAR" );
42- }
43- if (sheet_val.IsNull ()) {
44- throw BinderException (" sheet option must be a non-null VARCHAR" );
45- }
46- sheet = StringValue::Get (sheet_val);
47- } else {
48- sheet = " " ;
49- }
50-
51- const auto range_opt = options.find (" range" );
52- std::string range;
53- if (range_opt != options.end ()) {
54- string error_msg;
55- Value range_val;
56- if (!range_opt->second .back ().DefaultTryCastAs (LogicalType::VARCHAR , range_val, &error_msg)) {
57- throw BinderException (" range option must be a VARCHAR" );
58- }
59- if (range_val.IsNull ()) {
60- throw BinderException (" range option must be a non-null VARCHAR" );
61- }
62- range = StringValue::Get (range_val);
63- } else {
64- range = " " ;
65- }
66-
67- const auto overwrite_sheet_opt = options.find (" overwrite_sheet" );
68- bool overwrite_sheet;
69- if (overwrite_sheet_opt != options.end ()) {
70- if (overwrite_sheet_opt->second .size () != 1 ) {
71- throw BinderException (" overwrite_sheet option must be a single boolean value" );
72- }
73- string error_msg;
74- Value overwrite_sheet_bool_val;
75- if (!overwrite_sheet_opt->second .back ().DefaultTryCastAs (LogicalType::BOOLEAN , overwrite_sheet_bool_val,
76- &error_msg)) {
77- throw BinderException (" overwrite_sheet option must be a single boolean value" );
78- }
79- if (overwrite_sheet_bool_val.IsNull ()) {
80- throw BinderException (" overwrite_sheet option must be a single boolean value" );
81- }
82- overwrite_sheet = BooleanValue::Get (overwrite_sheet_bool_val);
83- } else {
84- overwrite_sheet = true ; // Default to overwrite_sheet to maintain prior behavior
85- }
86-
87- const auto overwrite_range_opt = options.find (" overwrite_range" );
88- bool overwrite_range;
89- if (overwrite_range_opt != options.end ()) {
90- if (overwrite_range_opt->second .size () != 1 ) {
91- throw BinderException (" overwrite_range option must be a single boolean value" );
92- }
93- string error_msg;
94- Value overwrite_range_bool_val;
95- if (!overwrite_range_opt->second .back ().DefaultTryCastAs (LogicalType::BOOLEAN , overwrite_range_bool_val,
96- &error_msg)) {
97- throw BinderException (" overwrite_range option must be a single boolean value" );
98- }
99- if (overwrite_range_bool_val.IsNull ()) {
100- throw BinderException (" overwrite_range option must be a single boolean value" );
101- }
102- overwrite_range = BooleanValue::Get (overwrite_range_bool_val);
103- } else {
104- overwrite_range = false ;
105- }
106-
107- const auto header_opt = options.find (" header" );
108- bool header;
109- if (header_opt != options.end ()) {
110- if (header_opt->second .size () != 1 ) {
111- throw BinderException (" header option must be a single boolean value" );
112- }
113- string error_msg;
114- Value header_bool_val;
115- if (!header_opt->second .back ().DefaultTryCastAs (LogicalType::BOOLEAN , header_bool_val, &error_msg)) {
116- throw BinderException (" header option must be a single boolean value" );
117- }
118- if (header_bool_val.IsNull ()) {
119- throw BinderException (" header option must be a single boolean value" );
120- }
121- header = BooleanValue::Get (header_bool_val);
122- } else {
123- header = true ;
124- // If we are in the append case, default to no header instead.
125- if (!overwrite_sheet && !overwrite_range) {
126- header = false ;
127- }
128- }
74+ auto sheet = GetStringOption (options, " sheet" );
75+ auto range = GetStringOption (options, " range" );
76+ bool overwrite_sheet = GetBoolOption (options, " overwrite_sheet" , true ).first ;
77+ bool overwrite_range = GetBoolOption (options, " overwrite_range" , false ).first ;
78+ bool create_if_not_exists = GetBoolOption (options, " create_if_not_exists" , false ).first ;
12979
130- const auto create_if_not_exists_opt = options. find ( " create_if_not_exists " );
131- bool create_if_not_exists ;
80+ auto header_result = GetBoolOption (options, " header " , true );
81+ bool header = header_result. second ? header_result. first : (overwrite_range || overwrite_sheet) ;
13282
133- if (create_if_not_exists_opt != options.end ()) {
134- if (create_if_not_exists_opt->second .size () != 1 ) {
135- throw BinderException (" create_if_not_exists option must be a single boolean value" );
136- }
137- string error_msg;
138- Value create_if_not_exists_val;
139- if (!create_if_not_exists_opt->second .back ().DefaultTryCastAs (LogicalType::BOOLEAN , create_if_not_exists_val,
140- &error_msg)) {
141- throw BinderException (" Failed to cast option to bool: " , error_msg);
142- }
143- if (create_if_not_exists_val.IsNull ()) {
144- throw BinderException (" Option must not be null" );
145- }
146- create_if_not_exists = BooleanValue::Get (create_if_not_exists_val);
147- if (create_if_not_exists && sheet.empty ()) {
148- throw BinderException (" Must provide sheet name" );
149- }
150- } else {
151- create_if_not_exists = false ;
83+ if (create_if_not_exists && sheet.empty ()) {
84+ throw BinderException (" Must provide sheet name" );
15285 }
15386
15487 return make_uniq<GSheetWriteBindData>(file_path, sql_types, names, sheet, range, overwrite_sheet, overwrite_range,
0 commit comments