DuckDB の Go クライアントで Google Sheets のデータを SQL で取得する

golang

DuckDB は SQL で CSV や JSON、Parquet などを高速に読み書きできる OSS。

$ brew install duckdb
$ duckdb
v1.1.3 19864453f7
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.

D SELECT * FROM read_csv_auto('test.csv');
┌───────┬─────────┬─────────┐
│   a   │    b    │    c    │
│ int64 │ varchar │ varchar │
├───────┼─────────┼─────────┤
10 │ aaa     │ bbb     │
20 │ aaa     │ ccc     │
30 │ bbb     │ ddd     │
40 │ bbb     │ eee     │
└───────┴─────────┴─────────┘

CLI ほか Go や Swift、WASM などのクライアント が実装されており、様々な環境で利用することができる。

package main

import (
	"database/sql"
	"errors"
	"fmt"
	"log"

	_ "github.com/marcboeker/go-duckdb"
)

func main() {
	db, err := sql.Open("duckdb", "")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	rows, err = db.Exec(`SELECT * FROM read_csv_auto('test.csv');`)
	if err != nil {
		log.Fatal(err)
	}

	for rows.Next() {
		var (
			a int64
			b string
			c string
		)
		err = row.Scan(&a, &b, &c)
		if errors.Is(err, sql.ErrNoRows) {
			log.Println("no rows")
		} else if err != nil {
			log.Fatal(err)
		}
	}
}

/*
10 aaa bbb
20 aaa ccc
30 bbb ddd
40 bbb eee
*/

URL を渡すこともできて、次のようにして公開されている Sheet にアクセスできる。

sheetExportURL := "https://docs.google.com/spreadsheets/export?format=csv&id=..."
rows, err := db.Query(fmt.Sprintf(`SELECT * FROM read_csv_auto('%s');`, sheetExportURL))

DuckDB には extension で機能を拡張する仕組みがある。

D SELECT extension_name, installed, description
  FROM duckdb_extensions();
┌──────────────────┬───────────┬────────────────────────────────────────────────────────────────────────────────────┐
│  extension_name  │ installed │                                    description                                     │
│     varchar      │  boolean  │                                      varchar                                       │
├──────────────────┼───────────┼────────────────────────────────────────────────────────────────────────────────────┤
│ arrow            │ false     │ A zero-copy data integration between Apache Arrow and DuckDB                       │
│ autocomplete     │ true      │ Adds support for autocomplete in the shell                                         │
│ aws              │ false     │ Provides features that depend on the AWS SDK                                       │
│ azure            │ false     │ Adds a filesystem abstraction for Azure blob storage to DuckDB                     │
│ delta            │ false     │ Adds support for Delta Lake                                                        │
│ excel            │ false     │ Adds support for Excel-like format strings                                         │
│ fts              │ false     │ Adds support for Full-Text Search Indexes                                          │
│ httpfs           │ false     │ Adds support for reading and writing files over a HTTP(S) connection               │
│ iceberg          │ false     │ Adds support for Apache Iceberg                                                    │
│ icu              │ true      │ Adds support for time zones and collations using the ICU library                   │
│ inet             │ false     │ Adds support for IP-related data types and functions                               │
│ jemalloc         │ false     │ Overwrites system allocator with JEMalloc                                          │
│ json             │ true      │ Adds support for JSON operations                                                   │
│ motherduck       │ false     │ Enables motherduck integration with the system                                     │
│ mysql_scanner    │ false     │ Adds support for connecting to a MySQL database                                    │
│ parquet          │ true      │ Adds support for reading and writing parquet files                                 │
│ postgres_scanner │ false     │ Adds support for connecting to a Postgres database                                 │
│ shell            │ true      │ Adds CLI-specific support and functionalities                                      │
│ spatial          │ false     │ Geospatial extension that adds support for working with spatial data and functions │
│ sqlite_scanner   │ false     │ Adds support for reading and writing SQLite database files                         │
│ substrait        │ false     │ Adds support for the Substrait integration                                         │
│ tpcds            │ false     │ Adds TPC-DS data generation and query support                                      │
│ tpch             │ false     │ Adds TPC-H data generation and query support                                       │
│ vss              │ false     │ Adds indexing support to accelerate Vector Similarity Search                       │
├──────────────────┴───────────┴────────────────────────────────────────────────────────────────────────────────────┤
24 rows                                                                                                 3 columns │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

コミュニティの GSheets extension を用いると非公開の Sheet にもアクセスできる。この extension は CREATE SECRET 時に access_token を受け取るか oauth による認証を行いSecrets Manager保存してread_gsheet が token を取得し、Authorization header に乗せてリクエストする。

D INSTALL gsheets FROM community;
D LOAD gsheets;
D CREATE PERSISTENT SECRET (TYPE gsheet);
D SELECT * FROM read_gsheet(...);

extension と secret が保存されている。

$ ls ~/.duckdb/extensions/v1.1.3/osx_arm64/
gsheets.duckdb_extension
gsheets.duckdb_extension.info
httpfs.duckdb_extension
httpfs.duckdb_extension.info

$ ls ~/.duckdb/stored_secrets/__default_gsheet.duckdb_secret

Go から呼ぶ。

if _, err := db.Exec("LOAD gsheets;"); err != nil {
        log.Fatal(err)
}

sheetURL := "https://docs.google.com/spreadsheets/d/.../edit"
rows, err := db.Query(fmt.Sprintf(`SELECT * FROM read_gsheet('%s');`, sheetURL))
if err != nil {
        log.Fatal(err)
}

参考

DuckDBでGoogle Sheetsのデータをクエリしてみる