From SQL types to Go types

On this page

On DuckDB:

  • Every column has an exact type, so the mapping follows the declared type directly
  • REAL is a 4-byte float and maps to float32; DOUBLE maps to float64
  • Unsigned types map to Go unsigned integers, e.g. UTINYINT to uint8
  • HUGEINT maps to *big.Int, the type duckdb-go returns, even when the column is NOT NULL
  • DATE and TIMESTAMP map to time.Time, and UUID to uuid.UUID
  • DECIMAL and INTERVAL are left out: their default Go types, apd.Decimal and string, do not round-trip through duckdb-go yet, which returns an INTERVAL as its own duckdb.Interval

Seagull reads each column's declared type from your migrations and picks the Go type that holds it without loss. Integers keep the width and sign the engine stores, floats their precision, exact decimals become a decimal type rather than a float, dates and times become time.Time, and a column that can be NULL becomes a pointer. You never choose a Go type for a column by hand, and you never scan a NUMERIC into a float64 by accident.

This example records one landing of fish, with a column of each common kind, reads it back, and prints the Go type every column became.

The schema

db/migrations/0001_landings.up.sql

CREATE SEQUENCE landing_ids;

CREATE TABLE landings (
    id          BIGINT    PRIMARY KEY DEFAULT nextval('landing_ids'),
    species     VARCHAR   NOT NULL,
    quay        UTINYINT  NOT NULL,
    boxes       SMALLINT  NOT NULL,
    fish        INTEGER   NOT NULL,
    weight_g    BIGINT    NOT NULL,
    length_cm   DOUBLE    NOT NULL,
    water_temp  REAL      NOT NULL,
    on_ice      BOOLEAN   NOT NULL,
    landed_at   TIMESTAMP NOT NULL,
    landed_on   DATE      NOT NULL,
    catch_id    UUID      NOT NULL,
    auction_lot HUGEINT   NOT NULL,
    tag         BLOB      NOT NULL,
    notes       VARCHAR
);

What sgl generates

The row type for GetLanding is the mapping, field by field:

internal/store

type GetLandingRow struct {
	ID         int64     `json:"id"`
	Species    string    `json:"species"`
	Quay       uint8     `json:"quay"`
	Boxes      int16     `json:"boxes"`
	Fish       int32     `json:"fish"`
	WeightG    int64     `json:"weight_g"`
	LengthCm   float64   `json:"length_cm"`
	WaterTemp  float32   `json:"water_temp"`
	OnIce      bool      `json:"on_ice"`
	LandedAt   time.Time `json:"landed_at"`
	LandedOn   time.Time `json:"landed_on"`
	CatchID    uuid.UUID `json:"catch_id"`
	AuctionLot *big.Int  `json:"auction_lot"`
	Tag        []byte    `json:"tag"`
	Notes      *string   `json:"notes"`
}

func (queries *Queries) GetLanding(ctx context.Context, id int64) (GetLandingRow, error) {
	var row GetLandingRow
	err := queries.reader.QueryRowContext(ctx, getLandingSQL, id).Scan(&row.ID, &row.Species, &row.Quay, &row.Boxes, &row.Fish, &row.WeightG, &row.LengthCm, &row.WaterTemp, &row.OnIce, &row.LandedAt, &row.LandedOn, &row.CatchID, &row.AuctionLot, &row.Tag, &row.Notes)
	if err != nil {
		return GetLandingRow{}, err
	}
	return row, nil
}
  • SMALLINT, INTEGER and BIGINT give int16, int32 and int64, and the unsigned UTINYINT gives uint8.
  • HUGEINT is a 128-bit integer with no Go equivalent, so it gives *big.Int, the type the DuckDB driver returns. It is a pointer even though the column is NOT NULL.
  • REAL is DuckDB's 4-byte float, so it gives float32; DOUBLE gives float64.
  • TIMESTAMP and DATE give time.Time, and UUID gives uuid.UUID from github.com/google/uuid.
  • BLOB gives []byte, and the nullable notes gives *string.

Parameters follow the same rules, so RecordLanding takes the same Go types it hands back.

Using it

The program records a landing and reads it back, then prints each column's name, the Go type of its field (with %T) and its value. %T prints []byte as []uint8, since byte is an alias for uint8.

example.go

package main

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"io"
	"math/big"
	"time"

	"github.com/google/uuid"

	"seagull.sh/seagull/examples/duckdb/005_type_mapping/internal/store"
)

// run records a landing of fish and reads it back, printing the Go type sgl chose for
// each column. DuckDB declares every column's type exactly, and Seagull maps each one:
//
//   - SMALLINT, INTEGER and BIGINT give int16, int32 and int64, and the unsigned UTINYINT
//     gives uint8;
//   - DOUBLE gives float64, but REAL is DuckDB's 4-byte float, so it gives float32;
//   - TIMESTAMP and DATE give time.Time, and UUID gives uuid.UUID from
//     github.com/google/uuid;
//   - HUGEINT is a 128-bit integer with no Go equivalent, so it gives *big.Int, the type
//     duckdb-go returns, even when the column is NOT NULL;
//   - BLOB gives []byte, and a nullable VARCHAR gives *string.
//
// Takes database (*sql.DB) which is a migrated database.
// Takes output (io.Writer) which receives one line per column.
//
// Returns error when a query fails.
func run(ctx context.Context, database *sql.DB, output io.Writer) error {
	queries := store.New(database)

	// The auction lot number has 20 digits, too many for int64 or uint64.
	lot, ok := new(big.Int).SetString("20260501000000000001", 10)
	if !ok {
		return errors.New("parsing the auction lot number")
	}
	recorded, err := queries.RecordLanding(ctx, store.RecordLandingParams{
		Species:    "mackerel",
		Quay:       3,
		Boxes:      12,
		Fish:       480,
		WeightG:    163_200,
		LengthCm:   31.5,
		WaterTemp:  11.25,
		OnIce:      true,
		LandedAt:   time.Date(2026, time.May, 1, 5, 30, 0, 0, time.UTC),
		LandedOn:   time.Date(2026, time.May, 1, 0, 0, 0, 0, time.UTC),
		CatchID:    uuid.MustParse("6f1c2a5e-4d3b-4c2a-9e8f-1a2b3c4d5e6f"),
		AuctionLot: lot,
		Tag:        []byte{0x04, 0xa2, 0x19, 0xf3},
		Notes:      new("landed at Newlyn"),
	})
	if err != nil {
		return fmt.Errorf("recording a landing: %w", err)
	}

	landing, err := queries.GetLanding(ctx, recorded.ID)
	if err != nil {
		return fmt.Errorf("getting landing %d: %w", recorded.ID, err)
	}
	fmt.Fprintf(output, "id           %-10T %d\n", landing.ID, landing.ID)
	fmt.Fprintf(output, "species      %-10T %s\n", landing.Species, landing.Species)
	fmt.Fprintf(output, "quay         %-10T %d\n", landing.Quay, landing.Quay)
	fmt.Fprintf(output, "boxes        %-10T %d\n", landing.Boxes, landing.Boxes)
	fmt.Fprintf(output, "fish         %-10T %d\n", landing.Fish, landing.Fish)
	fmt.Fprintf(output, "weight_g     %-10T %d\n", landing.WeightG, landing.WeightG)
	fmt.Fprintf(output, "length_cm    %-10T %.1f\n", landing.LengthCm, landing.LengthCm)
	fmt.Fprintf(output, "water_temp   %-10T %.2f\n", landing.WaterTemp, landing.WaterTemp)
	fmt.Fprintf(output, "on_ice       %-10T %t\n", landing.OnIce, landing.OnIce)
	fmt.Fprintf(output, "landed_at    %-10T %s\n", landing.LandedAt, landing.LandedAt.Format(time.RFC3339))
	fmt.Fprintf(output, "landed_on    %-10T %s\n", landing.LandedOn, landing.LandedOn.Format(time.DateOnly))
	fmt.Fprintf(output, "catch_id     %-10T %s\n", landing.CatchID, landing.CatchID)
	fmt.Fprintf(output, "auction_lot  %-10T %s\n", landing.AuctionLot, landing.AuctionLot)
	// %T prints []byte as []uint8, since byte is an alias for uint8.
	fmt.Fprintf(output, "tag          %-10T % x\n", landing.Tag, landing.Tag)
	fmt.Fprintf(output, "notes        %-10T %s\n", landing.Notes, *landing.Notes)
	return nil
}

What it prints

id           int64      1
species      string     mackerel
quay         uint8      3
boxes        int16      12
fish         int32      480
weight_g     int64      163200
length_cm    float64    31.5
water_temp   float32    11.25
on_ice       bool       true
landed_at    time.Time  2026-05-01T05:30:00Z
landed_on    time.Time  2026-05-01
catch_id     uuid.UUID  6f1c2a5e-4d3b-4c2a-9e8f-1a2b3c4d5e6f
auction_lot  *big.Int   20260501000000000001
tag          []uint8    04 a2 19 f3
notes        *string    landed at Newlyn

Changing a type

The defaults are a starting point. A go_type directive on a column, or an override in sgl.yaml, swaps in a type of your own, such as a named Pence type for money; Type overrides shows how. The type mapping reference lists every default, and the order in which overrides apply.

Across engines

Only these Go types of GetLanding differ between engines; the rest are the same everywhere.

DuckDBSQLitePostgreSQLMySQLClickHouse
id parameterint64int64int64int64uint64
id columnint64int64int64int64uint64
boxes columnint16int64int16int16uint16
fish columnint32int64int32int32uint32
length_cm columnfloat64float64float32float64float32
tag column[]byte[]byte[]byte[]bytestring
catch_id columnuuid.UUID·uuid.UUID·uuid.UUID
sea_temp column··float64··
price column··apd.Decimal·decimal.Decimal
landed_on columntime.Time·time.Time··
harbour_ip column··any··
berth column···uint8·
price_per_kg column···apd.Decimal·
quay columnuint8····
water_temp columnfloat32····
auction_lot column*big.Int····

A · marks a parameter or column that version of the example does not have.

TimescaleDB runs the PostgreSQL version unchanged.

MariaDB runs the MySQL version unchanged.

Compare the whole example side by side with SQLite, PostgreSQL, MySQL and ClickHouse.

Run it locally

From the directory you cloned the Seagull repository into:

cd seagull/examples/duckdb && go run ./005_type_mapping

Its test runs it too, and checks what it prints against output.txt:

cd seagull/examples/duckdb && go test ./005_type_mapping

Needs cgo and a C compiler (the DuckDB driver).

The files

Every file of the example, as it is in the Seagull repository.

sgl.yaml

# Copyright 2026 PolitePixels Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This project stands against fascism, authoritarianism, and all forms of
# oppression. We built this to empower people, not to enable those who would
# strip others of their rights and dignity.

version: 1

databases:
  - name: harbour
    dialect: duckdb
    migrations: db/migrations
    queries: db/queries
    go:
      package: store
      out: internal/store

db/migrations/0001_landings.up.sql

CREATE SEQUENCE landing_ids;

CREATE TABLE landings (
    id          BIGINT    PRIMARY KEY DEFAULT nextval('landing_ids'),
    species     VARCHAR   NOT NULL,
    quay        UTINYINT  NOT NULL,
    boxes       SMALLINT  NOT NULL,
    fish        INTEGER   NOT NULL,
    weight_g    BIGINT    NOT NULL,
    length_cm   DOUBLE    NOT NULL,
    water_temp  REAL      NOT NULL,
    on_ice      BOOLEAN   NOT NULL,
    landed_at   TIMESTAMP NOT NULL,
    landed_on   DATE      NOT NULL,
    catch_id    UUID      NOT NULL,
    auction_lot HUGEINT   NOT NULL,
    tag         BLOB      NOT NULL,
    notes       VARCHAR
);

db/migrations/0001_landings.down.sql

DROP TABLE landings;
DROP SEQUENCE landing_ids;

db/queries/landings.sql

-- sgl.query(RecordLanding, one)
INSERT INTO landings (species, quay, boxes, fish, weight_g, length_cm, water_temp, on_ice, landed_at, landed_on, catch_id, auction_lot, tag, notes)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
RETURNING id;

-- sgl.query(GetLanding, one)
SELECT id, species, quay, boxes, fish, weight_g, length_cm, water_temp, on_ice, landed_at, landed_on, catch_id, auction_lot, tag, notes
FROM landings
WHERE id = $1;

example.go

package main

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"io"
	"math/big"
	"time"

	"github.com/google/uuid"

	"seagull.sh/seagull/examples/duckdb/005_type_mapping/internal/store"
)

// run records a landing of fish and reads it back, printing the Go type sgl chose for
// each column. DuckDB declares every column's type exactly, and Seagull maps each one:
//
//   - SMALLINT, INTEGER and BIGINT give int16, int32 and int64, and the unsigned UTINYINT
//     gives uint8;
//   - DOUBLE gives float64, but REAL is DuckDB's 4-byte float, so it gives float32;
//   - TIMESTAMP and DATE give time.Time, and UUID gives uuid.UUID from
//     github.com/google/uuid;
//   - HUGEINT is a 128-bit integer with no Go equivalent, so it gives *big.Int, the type
//     duckdb-go returns, even when the column is NOT NULL;
//   - BLOB gives []byte, and a nullable VARCHAR gives *string.
//
// Takes database (*sql.DB) which is a migrated database.
// Takes output (io.Writer) which receives one line per column.
//
// Returns error when a query fails.
func run(ctx context.Context, database *sql.DB, output io.Writer) error {
	queries := store.New(database)

	// The auction lot number has 20 digits, too many for int64 or uint64.
	lot, ok := new(big.Int).SetString("20260501000000000001", 10)
	if !ok {
		return errors.New("parsing the auction lot number")
	}
	recorded, err := queries.RecordLanding(ctx, store.RecordLandingParams{
		Species:    "mackerel",
		Quay:       3,
		Boxes:      12,
		Fish:       480,
		WeightG:    163_200,
		LengthCm:   31.5,
		WaterTemp:  11.25,
		OnIce:      true,
		LandedAt:   time.Date(2026, time.May, 1, 5, 30, 0, 0, time.UTC),
		LandedOn:   time.Date(2026, time.May, 1, 0, 0, 0, 0, time.UTC),
		CatchID:    uuid.MustParse("6f1c2a5e-4d3b-4c2a-9e8f-1a2b3c4d5e6f"),
		AuctionLot: lot,
		Tag:        []byte{0x04, 0xa2, 0x19, 0xf3},
		Notes:      new("landed at Newlyn"),
	})
	if err != nil {
		return fmt.Errorf("recording a landing: %w", err)
	}

	landing, err := queries.GetLanding(ctx, recorded.ID)
	if err != nil {
		return fmt.Errorf("getting landing %d: %w", recorded.ID, err)
	}
	fmt.Fprintf(output, "id           %-10T %d\n", landing.ID, landing.ID)
	fmt.Fprintf(output, "species      %-10T %s\n", landing.Species, landing.Species)
	fmt.Fprintf(output, "quay         %-10T %d\n", landing.Quay, landing.Quay)
	fmt.Fprintf(output, "boxes        %-10T %d\n", landing.Boxes, landing.Boxes)
	fmt.Fprintf(output, "fish         %-10T %d\n", landing.Fish, landing.Fish)
	fmt.Fprintf(output, "weight_g     %-10T %d\n", landing.WeightG, landing.WeightG)
	fmt.Fprintf(output, "length_cm    %-10T %.1f\n", landing.LengthCm, landing.LengthCm)
	fmt.Fprintf(output, "water_temp   %-10T %.2f\n", landing.WaterTemp, landing.WaterTemp)
	fmt.Fprintf(output, "on_ice       %-10T %t\n", landing.OnIce, landing.OnIce)
	fmt.Fprintf(output, "landed_at    %-10T %s\n", landing.LandedAt, landing.LandedAt.Format(time.RFC3339))
	fmt.Fprintf(output, "landed_on    %-10T %s\n", landing.LandedOn, landing.LandedOn.Format(time.DateOnly))
	fmt.Fprintf(output, "catch_id     %-10T %s\n", landing.CatchID, landing.CatchID)
	fmt.Fprintf(output, "auction_lot  %-10T %s\n", landing.AuctionLot, landing.AuctionLot)
	// %T prints []byte as []uint8, since byte is an alias for uint8.
	fmt.Fprintf(output, "tag          %-10T % x\n", landing.Tag, landing.Tag)
	fmt.Fprintf(output, "notes        %-10T %s\n", landing.Notes, *landing.Notes)
	return nil
}

main.go

// Command 005_type_mapping applies its migrations to an in-memory DuckDB database, then
// runs the example in example.go: one landing of fish is recorded and read back, printing
// the Go type sgl chose for each column.
//
// Run it with `go run .`, or `go run . -db example.duckdb` to keep the database in a
// file.
package main

import (
	"context"
	"database/sql"
	"embed"
	"flag"
	"fmt"
	"log"
	"os"

	_ "github.com/duckdb/duckdb-go/v2"

	"seagull.sh/seagull/sdk/migrate"
	"seagull.sh/seagull/sdk/migrate/migrate_backend_duckdb"
)

// migrations holds the migration files, compiled into the binary.
//
//go:embed db/migrations
var migrations embed.FS

func main() {
	path := flag.String("db", "", "DuckDB database file; an in-memory database when empty")
	flag.Parse()
	if err := execute(context.Background(), *path); err != nil {
		log.Fatal(err)
	}
}

// execute opens and migrates the DuckDB database at path, or an in-memory one when path
// is empty, and runs the example against it.
//
// Takes path (string) which is the database file, or empty.
//
// Returns error when the database cannot be prepared or the example fails.
func execute(ctx context.Context, path string) error {
	database, err := sql.Open("duckdb", path)
	if err != nil {
		return err
	}
	defer func() { _ = database.Close() }()

	migrator, err := migrate.NewMigrator(database, migrate_backend_duckdb.New(), migrations, "db/migrations")
	if err != nil {
		return err
	}
	if _, err := migrator.Up(ctx); err != nil {
		return fmt.Errorf("migrating: %w", err)
	}
	return run(ctx, database, os.Stdout)
}

internal/store/landings.sql.go

// Code generated by sgl. DO NOT EDIT.

package store

import (
	"context"
	"math/big"
	"time"

	"github.com/google/uuid"
)

const getLandingSQL = `SELECT id, species, quay, boxes, fish, weight_g, length_cm, water_temp, on_ice, landed_at, landed_on, catch_id, auction_lot, tag, notes
FROM landings
WHERE id = $1;`

type GetLandingRow struct {
	ID         int64     `json:"id"`
	Species    string    `json:"species"`
	Quay       uint8     `json:"quay"`
	Boxes      int16     `json:"boxes"`
	Fish       int32     `json:"fish"`
	WeightG    int64     `json:"weight_g"`
	LengthCm   float64   `json:"length_cm"`
	WaterTemp  float32   `json:"water_temp"`
	OnIce      bool      `json:"on_ice"`
	LandedAt   time.Time `json:"landed_at"`
	LandedOn   time.Time `json:"landed_on"`
	CatchID    uuid.UUID `json:"catch_id"`
	AuctionLot *big.Int  `json:"auction_lot"`
	Tag        []byte    `json:"tag"`
	Notes      *string   `json:"notes"`
}

func (queries *Queries) GetLanding(ctx context.Context, id int64) (GetLandingRow, error) {
	var row GetLandingRow
	err := queries.reader.QueryRowContext(ctx, getLandingSQL, id).Scan(&row.ID, &row.Species, &row.Quay, &row.Boxes, &row.Fish, &row.WeightG, &row.LengthCm, &row.WaterTemp, &row.OnIce, &row.LandedAt, &row.LandedOn, &row.CatchID, &row.AuctionLot, &row.Tag, &row.Notes)
	if err != nil {
		return GetLandingRow{}, err
	}
	return row, nil
}

const recordLandingSQL = `INSERT INTO landings (species, quay, boxes, fish, weight_g, length_cm, water_temp, on_ice, landed_at, landed_on, catch_id, auction_lot, tag, notes)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
RETURNING id;`

type RecordLandingParams struct {
	Species    string
	Quay       uint8
	Boxes      int16
	Fish       int32
	WeightG    int64
	LengthCm   float64
	WaterTemp  float32
	OnIce      bool
	LandedAt   time.Time
	LandedOn   time.Time
	CatchID    uuid.UUID
	AuctionLot *big.Int
	Tag        []byte
	Notes      *string
}

type RecordLandingRow struct {
	ID int64 `json:"id"`
}

func (queries *Queries) RecordLanding(ctx context.Context, params RecordLandingParams) (RecordLandingRow, error) {
	var row RecordLandingRow
	err := queries.writer.QueryRowContext(ctx, recordLandingSQL, params.Species, params.Quay, params.Boxes, params.Fish, params.WeightG, params.LengthCm, params.WaterTemp, params.OnIce, params.LandedAt, params.LandedOn, params.CatchID, params.AuctionLot, params.Tag, params.Notes).Scan(&row.ID)
	if err != nil {
		return RecordLandingRow{}, err
	}
	return row, nil
}

internal/store/models.go

// Code generated by sgl. DO NOT EDIT.

package store

import (
	"math/big"
	"time"

	"github.com/google/uuid"
)

type Landings struct {
	ID         int64     `json:"id"`
	Species    string    `json:"species"`
	Quay       uint8     `json:"quay"`
	Boxes      int16     `json:"boxes"`
	Fish       int32     `json:"fish"`
	WeightG    int64     `json:"weight_g"`
	LengthCm   float64   `json:"length_cm"`
	WaterTemp  float32   `json:"water_temp"`
	OnIce      bool      `json:"on_ice"`
	LandedAt   time.Time `json:"landed_at"`
	LandedOn   time.Time `json:"landed_on"`
	CatchID    uuid.UUID `json:"catch_id"`
	AuctionLot *big.Int  `json:"auction_lot"`
	Tag        []byte    `json:"tag"`
	Notes      *string   `json:"notes"`
}

internal/store/querier.go

// Code generated by sgl. DO NOT EDIT.

package store

import (
	"context"
	"database/sql"
)

type DBTX interface {
	ExecContext(ctx context.Context, query string, args ...any) (sql.Result, error)
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...any) *sql.Row
}

type dbtxWrapper interface {
	WrapDBTX(inner any) any
}

type Queries struct {
	reader  DBTX
	writer  DBTX
	wrapper dbtxWrapper
}

func New(db DBTX) *Queries {
	wrapper, _ := db.(dbtxWrapper)
	return &Queries{reader: db, writer: db, wrapper: wrapper}
}

func NewWithReplica(writer DBTX, reader DBTX) *Queries {
	wrapper, _ := writer.(dbtxWrapper)
	return &Queries{reader: reader, writer: writer, wrapper: wrapper}
}

func (queries *Queries) WithTx(transaction *sql.Tx) *Queries {
	if queries == nil {
		return &Queries{reader: transaction, writer: transaction, wrapper: nil}
	}
	db := queries.wrapDBTX(transaction)
	return &Queries{reader: db, writer: db, wrapper: queries.wrapper}
}

func (queries *Queries) wrapDBTX(db DBTX) DBTX {
	if queries.wrapper == nil {
		return db
	}
	wrapped, ok := queries.wrapper.WrapDBTX(db).(DBTX)
	if !ok {
		return db
	}
	return wrapped
}

func (queries *Queries) RunInTx(ctx context.Context, db *sql.DB, fn func(*Queries) error) error {
	transaction, err := db.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer transaction.Rollback()
	if err = fn(queries.WithTx(transaction)); err != nil {
		return err
	}
	return transaction.Commit()
}

output.txt

id           int64      1
species      string     mackerel
quay         uint8      3
boxes        int16      12
fish         int32      480
weight_g     int64      163200
length_cm    float64    31.5
water_temp   float32    11.25
on_ice       bool       true
landed_at    time.Time  2026-05-01T05:30:00Z
landed_on    time.Time  2026-05-01
catch_id     uuid.UUID  6f1c2a5e-4d3b-4c2a-9e8f-1a2b3c4d5e6f
auction_lot  *big.Int   20260501000000000001
tag          []uint8    04 a2 19 f3
notes        *string    landed at Newlyn

Also on other engines