Your first query: TimescaleDB and DuckDB
Every file of the TimescaleDB and DuckDB versions side by side, with the lines that differ marked.
Configuration
sgl.yamldiffers
TimescaleDB
DuckDB
1
version: 11
version: 12
2
3
databases:3
databases:4
- name: tasks4
- name: tasks5
dialect: postgres5
dialect: duckdb6
migrations: db/migrations6
migrations: db/migrations7
queries: db/queries7
queries: db/queries8
go:8
go:9
package: store9
package: store10
out: internal/store10
out: internal/store11
driver: database/sql12
doc_comments: true13
migrate:14
dsn: ${env:DATABASE_URL}Migrations
db/migrations/0001_init.up.sqldiffers
TimescaleDB
DuckDB
1
CREATE SEQUENCE task_ids;2
1
CREATE TABLE tasks (3
CREATE TABLE tasks (2
id BIGSERIAL PRIMARY KEY,4
id BIGINT PRIMARY KEY DEFAULT nextval('task_ids'),3
title TEXT NOT NULL,5
title VARCHAR NOT NULL,4
completed BOOLEAN NOT NULL DEFAULT false,6
completed BOOLEAN NOT NULL DEFAULT false,5
created_at TIMESTAMPTZ NOT NULL DEFAULT now()7
created_at TIMESTAMP NOT NULL DEFAULT current_timestamp6
);8
);7
8
COMMENT ON TABLE tasks IS 'Jobs to do around the harbour.';9
COMMENT ON COLUMN tasks.completed IS 'Whether the job is done.';db/migrations/0001_init.down.sqldiffers
TimescaleDB
DuckDB
1
DROP TABLE tasks;1
DROP TABLE tasks;2
DROP SEQUENCE task_ids;Queries
CreateTaskdiffers
TimescaleDB · db/queries/tasks.sql
DuckDB · db/queries/tasks.sql
1
-- CreateTask adds a task and returns it with the id and creation time the database2
-- gave it.3
-- sgl.query(CreateTask, one)1
-- sgl.query(CreateTask, one)4
INSERT INTO tasks (title) VALUES ($1) RETURNING id, title, completed, created_at;2
INSERT INTO tasks (title) VALUES ($1) RETURNING id, title, completed, created_at;ListTasksdiffers
TimescaleDB · db/queries/tasks.sql
DuckDB · db/queries/tasks.sql
1
-- ListTasks returns every task, oldest first.2
-- sgl.query(ListTasks, many)1
-- sgl.query(ListTasks, many)3
SELECT id, title, completed, created_at FROM tasks ORDER BY id;2
SELECT id, title, completed, created_at FROM tasks ORDER BY id;CompleteTaskdiffers
TimescaleDB · db/queries/tasks.sql
DuckDB · db/queries/tasks.sql
1
-- CompleteTask marks a task done and returns how many tasks it changed.2
-- sgl.query(CompleteTask, execrows)1
-- sgl.query(CompleteTask, execrows)3
UPDATE tasks SET completed = true WHERE id = $1;2
UPDATE tasks SET completed = true WHERE id = $1;What sgl generates
CreateTaskidentical
TimescaleDB
DuckDB
1
func (queries *Queries) CreateTask(ctx context.Context, title string) (CreateTaskRow, error)1
func (queries *Queries) CreateTask(ctx context.Context, title string) (CreateTaskRow, error)2
2
3
type CreateTaskRow struct {3
type CreateTaskRow struct {4
ID int64 `json:"id"`4
ID int64 `json:"id"`5
Title string `json:"title"`5
Title string `json:"title"`6
Completed bool `json:"completed"`6
Completed bool `json:"completed"`7
CreatedAt time.Time `json:"created_at"`7
CreatedAt time.Time `json:"created_at"`8
}8
}ListTasksidentical
TimescaleDB
DuckDB
1
func (queries *Queries) ListTasks(ctx context.Context) ([]ListTasksRow, error)1
func (queries *Queries) ListTasks(ctx context.Context) ([]ListTasksRow, error)2
2
3
type ListTasksRow struct {3
type ListTasksRow struct {4
ID int64 `json:"id"`4
ID int64 `json:"id"`5
Title string `json:"title"`5
Title string `json:"title"`6
Completed bool `json:"completed"`6
Completed bool `json:"completed"`7
CreatedAt time.Time `json:"created_at"`7
CreatedAt time.Time `json:"created_at"`8
}8
}CompleteTaskidentical
TimescaleDB
DuckDB
1
func (queries *Queries) CompleteTask(ctx context.Context, id int64) (int64, error)1
func (queries *Queries) CompleteTask(ctx context.Context, id int64) (int64, error)The program
example.godiffers
TimescaleDB
DuckDB
1
package main1
package main2
2
3
import (3
import (4
"context"4
"context"5
"database/sql"5
"database/sql"6
"fmt"6
"fmt"7
"io"7
"io"8
8
9
"seagull.sh/seagull/examples/postgres/001_first_query/internal/store"9
"seagull.sh/seagull/examples/duckdb/001_first_query/internal/store"10
)10
)11
11
12
// run adds two tasks, completes the first and lists them all, through the methods sgl12
// run adds two tasks, completes the first and lists them all, through the methods sgl13
// generated from db/queries/tasks.sql. The id column is BIGSERIAL, so ID is int64, and13
// generated from db/queries/tasks.sql.14
// created_at is TIMESTAMPTZ, so CreatedAt is a time.Time; the example leaves it out of15
// the output, as it changes on every run.16
//14
//17
// sgl.yaml sets go.doc_comments, so the comment above each query in tasks.sql becomes its15
// DuckDB has no auto-incrementing INTEGER PRIMARY KEY, so the migration draws each id18
// method's doc comment, and the migration's COMMENT ON TABLE and COMMENT ON COLUMN become16
// from a sequence with DEFAULT nextval('task_ids'), and RETURNING hands it back.19
// the doc comments of the Tasks model and its Completed field: open internal/store to see20
// them, or hover over queries.CreateTask in an editor.21
//17
//22
// Takes database (*sql.DB) which is a migrated database, opened with pgx's database/sql18
// Takes database (*sql.DB) which is a migrated database.23
// driver.24
// Takes output (io.Writer) which receives the task list.19
// Takes output (io.Writer) which receives the task list.25
//20
//26
// Returns error when a query fails.21
// Returns error when a query fails.27
func run(ctx context.Context, database *sql.DB, output io.Writer) error {22
func run(ctx context.Context, database *sql.DB, output io.Writer) error {28
queries := store.New(database)23
queries := store.New(database)29
24
30
feed, err := queries.CreateTask(ctx, "Feed the seagulls")25
feed, err := queries.CreateTask(ctx, "Feed the seagulls")31
if err != nil {26
if err != nil {32
return fmt.Errorf("creating a task: %w", err)27
return fmt.Errorf("creating a task: %w", err)33
}28
}34
if _, err := queries.CreateTask(ctx, "Count the chips"); err != nil {29
if _, err := queries.CreateTask(ctx, "Count the chips"); err != nil {35
return fmt.Errorf("creating a task: %w", err)30
return fmt.Errorf("creating a task: %w", err)36
}31
}37
32
38
completed, err := queries.CompleteTask(ctx, feed.ID)33
completed, err := queries.CompleteTask(ctx, feed.ID)39
if err != nil {34
if err != nil {40
return fmt.Errorf("completing task %d: %w", feed.ID, err)35
return fmt.Errorf("completing task %d: %w", feed.ID, err)41
}36
}42
fmt.Fprintf(output, "completed %d task\n", completed)37
fmt.Fprintf(output, "completed %d task\n", completed)43
38
44
tasks, err := queries.ListTasks(ctx)39
tasks, err := queries.ListTasks(ctx)45
if err != nil {40
if err != nil {46
return fmt.Errorf("listing tasks: %w", err)41
return fmt.Errorf("listing tasks: %w", err)47
}42
}48
for _, task := range tasks {43
for _, task := range tasks {49
fmt.Fprintf(output, "%d\t%-18s completed=%t\n", task.ID, task.Title, task.Completed)44
fmt.Fprintf(output, "%d\t%-18s completed=%t\n", task.ID, task.Title, task.Completed)50
}45
}51
return nil46
return nil52
}47
}