On this page

Read ClickHouse arrays, maps and tuples

Read ClickHouse arrays, maps and tuples without serialising them to text. This example uses the database/sql emitter; the native emitter has a different representation for named tuples.

Declare the composite columns

Add the map, array and named tuple to your migration:

CREATE TABLE kiosks (
    id       UInt64,
    name     String,
    town     LowCardinality(String),
    till     IPv4,
    prices   Map(String, UInt32),
    tags     Array(String),
    location Tuple(lat Float64, lon Float64)
) ENGINE = MergeTree() ORDER BY id;

Read one kiosk, or turn its array into rows with ARRAY JOIN:

-- sgl.query(GetKiosk, one)
SELECT id, name, town, till, prices, tags, location FROM kiosks WHERE id = {id:UInt64};
-- sgl.query(KiosksByTag, many)
SELECT tag, name
FROM kiosks
ARRAY JOIN tags AS tag
ORDER BY tag, name;

Read the generated composite fields

Run sgl generate with go.driver: database/sql, then use store.New(database). The generated row exposes Prices as a typed map, Tags as a slice and Location as map[string]any:

	first, err := queries.GetKiosk(ctx, 1)
	if err != nil {
		return fmt.Errorf("getting kiosk 1: %w", err)
	}
	fmt.Fprintf(output, "town      %-24T %s\n", first.Town, first.Town)
	fmt.Fprintf(output, "tags      %-24T %v\n", first.Tags, first.Tags)
	fmt.Fprintf(output, "prices    %-24T %v\n", first.Prices, first.Prices)
	fmt.Fprintf(output, "location  %-24T %v\n", first.Location, first.Location)
	fmt.Fprintf(output, "till      %-24T %v\n", first.Till, first.Till)

When you select the native clickhouse emitter, named tuple fields become an anonymous struct instead. Use the native tuple reference when adapting the caller.

Check the composite values

From the Seagull repository root, run the composite-type example:

cd examples/clickhouse/035_clickhouse_arrays_maps
go test .

Set SGL_EXAMPLE_DSN to a disposable database before running this server-backed test; without it, the test skips. See running examples for connection setup.

The complete example produces:

town      string                   Padstow
tags      []string                 [chips takeaway]
prices    map[string]uint32        map[chips:350 mushy peas:150]
location  map[string]interface {}  map[lat:50.5413 lon:-4.9389]
till      net.IP                   10.0.0.11
kiosks by tag, one row per tag:
  cafe       Gull's Rest
  chips      Chips Ahoy
  ice cream  The Ice Cream Hut
  seating    Gull's Rest
  takeaway   Chips Ahoy
  takeaway   The Ice Cream Hut
kiosks in Padstow:
  #1 Chips Ahoy
  #2 The Ice Cream Hut

Match the caller to the emitter

Keep the emitter choice consistent with the caller. A database/sql tuple map is keyed by the SQL field names; a native named tuple uses generated Go fields. Nullable scalars and nullable array elements need values that can represent NULL. Cast a result or add a type override only when the selected driver can read that representation.

See the dialects/clickhouse reference for the complete contract. The example browser exposes other engine variants; use their actual SQL rather than changing only the dialect name.

Examples for this page