Skip to content

Commit

Permalink
Merge pull request #63 from mharrisb1/fix/62-single-cell-ranges
Browse files Browse the repository at this point in the history
fix(reader): handle header-only results
  • Loading branch information
archiewood authored Feb 10, 2025
2 parents 0d2ecd0 + 871570a commit cb2f5ca
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 16 deletions.
1 change: 0 additions & 1 deletion docs/pages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ This token will periodically expire - you can re-run the above command again to

- DuckDB WASM is not (yet) supported.
- Google Sheets has a limit of 10,000,000 cells per spreadsheet.
- Writing data to a sheet starting from a cell other than A1 is not yet supported.
- Sheets must already exist to COPY TO them.

## Support
Expand Down
13 changes: 7 additions & 6 deletions src/gsheets_read.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,17 @@ unique_ptr<FunctionData> ReadSheetBind(ClientContext &context, TableFunctionBind
json cleanJson = parseJson(bind_data->response);
SheetData sheet_data = getSheetData(cleanJson);

// Prefering early return style to reduce nesting
// Throw error ourselves to give user a better error message
if (sheet_data.values.empty()) {
return bind_data;
throw duckdb::InvalidInputException("Sheet %s is empty", sheet_name);
}

idx_t start_index = header ? 1 : 0;
if (start_index >= sheet_data.values.size()) {
return bind_data;
}

const auto& first_data_row = sheet_data.values[start_index];
// Use empty row for first row if results are header-only
const std::vector<string> empty_row = {};
const auto& first_data_row = start_index >= sheet_data.values.size() ? empty_row : sheet_data.values[start_index];

// If we have a header, we want the width of the result to be the max of:
// the width of the header row
// or the width of the first row of data
Expand Down
16 changes: 8 additions & 8 deletions src/gsheets_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,18 +111,18 @@ json parseJson(const std::string& json_str) {

SheetData getSheetData(const json& j) {
SheetData result;
if (j.contains("range") && j.contains("majorDimension") && j.contains("values")) {
if (j.contains("range") && j.contains("majorDimension")) {
result.range = j["range"].get<std::string>();
result.majorDimension = j["majorDimension"].get<std::string>();
result.values = j["values"].get<std::vector<std::vector<std::string>>>();
// NOTE: no need to hard fail on empty sheet values. We can handle this more naturally further down the call chain.
// Just default to empty 2D vec.
result.values = j.contains("values") ? j["values"].get<std::vector<std::vector<std::string>>>() : std::vector<std::vector<std::string>>{};
} else if (j.contains("error")) {
string message = j["error"]["message"].get<std::string>();
int code = j["error"]["code"].get<int>();
throw std::runtime_error("Google Sheets API error: " + std::to_string(code) + " - " + message);
} else {
std::cerr << "JSON does not contain expected fields" << std::endl;
std::cerr << "Raw JSON string: " << j.dump() << std::endl;
throw;
int code = j["error"]["code"].get<int>();
throw std::runtime_error("Google Sheets API error: " + std::to_string(code) + " - " + message);
} else {
throw duckdb::InvalidInputException("JSON does not contain expected fields.\nRaw JSON string: %s", j.dump());
}
return result;
}
Expand Down
17 changes: 16 additions & 1 deletion test/sql/read_gsheet.test
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ NULL NULL
Archie 99.0

# Test single value from range
# NOTE: *must* use `header=false` to avoid uncaught bind error
query I
FROM read_gsheet('11QdEasMWbETbFVxry-SsD8jVcdYIT1zBQszcF84MdE8', sheet='Sheet1', range='A2');
----

# Test single value from range (no headers)
query I
FROM read_gsheet('11QdEasMWbETbFVxry-SsD8jVcdYIT1zBQszcF84MdE8', sheet='Sheet1', range='A2', header=false);
----
Expand Down Expand Up @@ -168,6 +172,17 @@ FROM read_gsheet('https://docs.google.com/spreadsheets/d/11QdEasMWbETbFVxry-SsD8
woot blah NULL should get this!
more wooting more blah NULL should get this!

# Header only results
query II
FROM read_gsheet('11QdEasMWbETbFVxry-SsD8jVcdYIT1zBQszcF84MdE8', sheet='62-header_only');
----

# Empty sheet
statement error
FROM read_gsheet('11QdEasMWbETbFVxry-SsD8jVcdYIT1zBQszcF84MdE8', sheet='62-empty');
----
Invalid Input Error: Sheet 62-empty is empty

# Drop the secret
statement ok
drop secret test_secret;

0 comments on commit cb2f5ca

Please sign in to comment.