Compare · 001

Your first query: DuckDB and ClickHouse

Every file of the DuckDB and ClickHouse versions side by side, with the lines that differ marked. 3 parameter or column types differ in Go.

Configuration

sgl.yamldiffers
DuckDB
ClickHouse
1version: 1
1version: 1
2
2
3databases:
3databases:
4 - name: tasks
4 - name: tasks
5 dialect: duckdb
5 dialect: clickhouse
6 migrations: db/migrations
6 migrations: db/migrations
7 queries: db/queries
7 queries: db/queries
8 go:
8 go:
9 package: store
9 package: store
10 out: internal/store
10 out: internal/store

Migrations

db/migrations/0001_init.up.sqldiffers
DuckDB
ClickHouse
1CREATE SEQUENCE task_ids;
2
3CREATE TABLE tasks (
1CREATE TABLE tasks (
4 id BIGINT PRIMARY KEY DEFAULT nextval('task_ids'),
2 id UInt64,
5 title VARCHAR NOT NULL,
3 title String,
6 completed BOOLEAN NOT NULL DEFAULT false,
4 completed UInt8 DEFAULT 0,
7 created_at TIMESTAMP NOT NULL DEFAULT current_timestamp
5 created_at DateTime DEFAULT now()
8);
6) ENGINE = MergeTree() ORDER BY id;
db/migrations/0001_init.down.sqldiffers
DuckDB
ClickHouse
1DROP TABLE tasks;
1DROP TABLE tasks;
2DROP SEQUENCE task_ids;

Queries

CreateTaskdiffers
DuckDB · db/queries/tasks.sql
ClickHouse · db/queries/tasks.sql
1-- sgl.query(CreateTask, one)
1-- sgl.query(CreateTask, exec)
2INSERT INTO tasks (title) VALUES ($1) RETURNING id, title, completed, created_at;
2INSERT INTO tasks (id, title) VALUES ({id:UInt64}, {title:String});
ListTasksidentical
DuckDB · db/queries/tasks.sql
ClickHouse · db/queries/tasks.sql
1-- sgl.query(ListTasks, many)
1-- sgl.query(ListTasks, many)
2SELECT id, title, completed, created_at FROM tasks ORDER BY id;
2SELECT id, title, completed, created_at FROM tasks ORDER BY id;
CompleteTaskdiffers
DuckDB · db/queries/tasks.sql
ClickHouse · db/queries/tasks.sql
1-- sgl.query(CompleteTask, execrows)
1-- sgl.query(CompleteTask, asyncexec)
2UPDATE tasks SET completed = true WHERE id = $1;
2ALTER TABLE tasks UPDATE completed = 1 WHERE id = {id:UInt64};
GetTaskonly ClickHouse
DuckDB · not in this version
ClickHouse · db/queries/tasks.sql
1-- sgl.query(GetTask, one)
2SELECT id, title, completed, created_at FROM tasks WHERE id = {id:UInt64};

What sgl generates

CreateTaskdiffers
DuckDB
ClickHouse
1func (queries *Queries) CreateTask(ctx context.Context, title string) (CreateTaskRow, error)
1func (queries *Queries) CreateTask(ctx context.Context, params CreateTaskParams) error
2
2
3type CreateTaskRow struct {
3type CreateTaskParams struct {
4 ID int64 `json:"id"`
4 ID uint64
5 Title string `json:"title"`
5 Title string
6 Completed bool `json:"completed"`
7 CreatedAt time.Time `json:"created_at"`
8}
6}
ListTasksdiffers
DuckDB
ClickHouse
1func (queries *Queries) ListTasks(ctx context.Context) ([]ListTasksRow, error)
1func (queries *Queries) ListTasks(ctx context.Context) ([]ListTasksRow, error)
2
2
3type ListTasksRow struct {
3type ListTasksRow struct {
4 ID int64 `json:"id"`
4 ID uint64 `json:"id"`
5 Title string `json:"title"`
5 Title string `json:"title"`
6 Completed bool `json:"completed"`
6 Completed uint8 `json:"completed"`
7 CreatedAt time.Time `json:"created_at"`
7 CreatedAt time.Time `json:"created_at"`
8}
8}
CompleteTaskdiffers
DuckDB
ClickHouse
1func (queries *Queries) CompleteTask(ctx context.Context, id int64) (int64, error)
1func (queries *Queries) CompleteTask(ctx context.Context, id uint64) error
GetTaskonly ClickHouse
DuckDB · not in this version
ClickHouse
1func (queries *Queries) GetTask(ctx context.Context, id uint64) (GetTaskRow, error)
2
3type GetTaskRow struct {
4 ID uint64 `json:"id"`
5 Title string `json:"title"`
6 Completed uint8 `json:"completed"`
7 CreatedAt time.Time `json:"created_at"`
8}

The program

example.godiffers
DuckDB
ClickHouse
1package main
1package main
2
2
3import (
3import (
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/duckdb/001_first_query/internal/store"
9 "seagull.sh/seagull/examples/clickhouse/001_first_query/internal/store"
10)
10)
11
11
12// run adds two tasks, completes the first and lists them all, through the methods sgl
12// run adds two tasks, completes the first and lists them all, through the methods sgl
13// generated from db/queries/tasks.sql.
13// generated from db/queries/tasks.sql.
14//
14//
15// DuckDB has no auto-incrementing INTEGER PRIMARY KEY, so the migration draws each id
15// ClickHouse has no auto-increment and no RETURNING, so the caller chooses each task's id
16// from a sequence with DEFAULT nextval('task_ids'), and RETURNING hands it back.
16// and reads a task back with GetTask. CompleteTask uses ALTER TABLE ... UPDATE, which
17// queues a mutation that the server applies in the background, so the query is declared
18// asyncexec and returns only an error (concept 36 explains why). The connection sets
19// mutations_sync=2, which makes the server finish the mutation before it replies, so the
20// list below already shows the task completed.
17//
21//
18// Takes database (*sql.DB) which is a migrated database.
22// Takes database (*sql.DB) which is a migrated database.
19// Takes output (io.Writer) which receives the task list.
23// Takes output (io.Writer) which receives the task list.
20//
24//
21// Returns error when a query fails.
25// Returns error when a query fails.
22func run(ctx context.Context, database *sql.DB, output io.Writer) error {
26func run(ctx context.Context, database *sql.DB, output io.Writer) error {
23 queries := store.New(database)
27 queries := store.New(database)
24
28
25 feed, err := queries.CreateTask(ctx, "Feed the seagulls")
29 tasks := []store.CreateTaskParams{
26 if err != nil {
30 {ID: 1, Title: "Feed the seagulls"},
27 return fmt.Errorf("creating a task: %w", err)
31 {ID: 2, Title: "Count the chips"},
28 }
32 }
29 if _, err := queries.CreateTask(ctx, "Count the chips"); err != nil {
33 for _, task := range tasks {
30 return fmt.Errorf("creating a task: %w", err)
34 if err := queries.CreateTask(ctx, task); err != nil {
35 return fmt.Errorf("creating task %d: %w", task.ID, err)
36 }
31 }
37 }
32
38
33 completed, err := queries.CompleteTask(ctx, feed.ID)
39 feed, err := queries.GetTask(ctx, 1)
34 if err != nil {
40 if err != nil {
41 return fmt.Errorf("getting task 1: %w", err)
42 }
43 fmt.Fprintf(output, "created task %d, %s\n", feed.ID, feed.Title)
44
45 if err := queries.CompleteTask(ctx, feed.ID); err != nil {
35 return fmt.Errorf("completing task %d: %w", feed.ID, err)
46 return fmt.Errorf("completing task %d: %w", feed.ID, err)
36 }
47 }
37 fmt.Fprintf(output, "completed %d task\n", completed)
48 fmt.Fprintf(output, "completed task %d\n", feed.ID)
38
49
39 tasks, err := queries.ListTasks(ctx)
50 listed, err := queries.ListTasks(ctx)
40 if err != nil {
51 if err != nil {
41 return fmt.Errorf("listing tasks: %w", err)
52 return fmt.Errorf("listing tasks: %w", err)
42 }
53 }
43 for _, task := range tasks {
54 for _, task := range listed {
44 fmt.Fprintf(output, "%d\t%-18s completed=%t\n", task.ID, task.Title, task.Completed)
55 fmt.Fprintf(output, "%d\t%-18s completed=%d\n", task.ID, task.Title, task.Completed)
45 }
56 }
46 return nil
57 return nil
47}
58}

Go type differences

The same SQL types map to different Go types, because each database reports them differently.

QueryFieldDuckDBClickHouse
ListTaskscolumn idint64uint64
ListTaskscolumn completedbooluint8
CompleteTaskparameter idint64uint64