Enums as generated Go types, and your own types with go_type
On this page
On MySQL:
- ENUM('fish', 'sides', 'puddings') is declared on the column itself; ORDER BY sorts its values in declaration order
- Each ENUM becomes a named string type in models.go, named after its table and column (store.MenuItemsSection), with a constant per value and a Valid method
- An inline ENUM has no type name for a sql: override, so a column override in types.overrides gives menu_items.status your own type, in models.go, rows and the parameters bound to it
- sgl.column(section, go_type: ...) changes only the column ItemsInSection returns; its parameter stays the generated store.MenuItemsSection
- Strict mode refuses a value the ENUM does not list, reporting it as data truncated
An enum column accepts only the labels it declares, and the database refuses anything else. Go has no enum type for Seagull to map that to, so by default an enum column arrives as a plain string: correct, but it says nothing about which strings are allowed. Seagull does not invent enum types of its own. You write the Go type you want, with a constant per label and any methods you like, and point the column at it with a go_type override. The generated code then hands you that type instead of a string.
This example is a chip shop menu. Each item has a section and a status, and both are enums.
The schema
MySQL declares an ENUM on the column itself, listing its labels.
db/migrations/0001_menu.up.sql
CREATE TABLE menu_items (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
section ENUM('fish', 'sides', 'puddings') NOT NULL,
status ENUM('available', 'sold_out') NOT NULL DEFAULT 'available'
);
Your own types
The program's own package, internal/menu, declares type Section string and type Status string, with a constant for each label: Fish, Sides and Puddings, then Available and SoldOut. The example points the columns at them in the two places an override can live.
For every query, sgl.yaml names the column in types.overrides. Any query that returns menu_items.status as it is gets a menu.Status:
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: menu
dialect: mysql
migrations: db/migrations
queries: db/queries
go:
package: store
out: internal/store
types:
overrides:
- column: menu_items.status
go: seagull.sh/seagull/examples/mysql/007_enums/internal/menu.Status
For one query, an sgl.column directive in the query's header does the same, here for section:
db/queries/menu.sql
-- sgl.query(ItemsInSection, many)
-- sgl.column(section, go_type: "seagull.sh/seagull/examples/mysql/007_enums/internal/menu.Section")
SELECT name, section, status FROM menu_items WHERE section = ? ORDER BY name;
So the two queries differ. ListMenu has no directive, which leaves Section a plain string, while its Status is typed by sgl.yaml:
internal/store
type ListMenuRow struct {
Name string `json:"name"`
Section MenuItemsSection `json:"section"`
Status menu.Status `json:"status"`
}
type MenuItemsSection string
func (queries *Queries) ListMenu(ctx context.Context) ([]ListMenuRow, error) {
rows, err := queries.reader.QueryContext(ctx, listMenuSQL)
if err != nil {
return nil, err
}
defer rows.Close()
var results []ListMenuRow
for rows.Next() {
var row ListMenuRow
if err := rows.Scan(&row.Name, &row.Section, &row.Status); err != nil {
return nil, err
}
results = append(results, row)
}
if err := rows.Err(); err != nil {
return nil, err
}
return results, nil
}
ItemsInSection has both, and the generated file imports the package for you:
internal/store
type ItemsInSectionRow struct {
Name string `json:"name"`
Section menu.Section `json:"section"`
Status menu.Status `json:"status"`
}
type MenuItemsSection string
func (queries *Queries) ItemsInSection(ctx context.Context, section MenuItemsSection) ([]ItemsInSectionRow, error) {
rows, err := queries.reader.QueryContext(ctx, itemsInSectionSQL, section)
if err != nil {
return nil, err
}
defer rows.Close()
var results []ItemsInSectionRow
for rows.Next() {
var row ItemsInSectionRow
if err := rows.Scan(&row.Name, &row.Section, &row.Status); err != nil {
return nil, err
}
results = append(results, row)
}
if err := rows.Err(); err != nil {
return nil, err
}
return results, nil
}
Parameters stay strings
An override changes the columns a query returns, not what it takes.
models.go and every Params field keep string, and so does the section that ItemsInSection filters on, so the program converts on the way in: string(menu.Fish).
The database still checks
A Go type with constants does not stop a caller passing any string it likes, but the enum does: the database refuses a label it does not list when the statement runs. The program tries it on purpose and prints the refusal.
Using it
example.go
package main
import (
"context"
"database/sql"
"errors"
"fmt"
"io"
"seagull.sh/seagull/examples/mysql/007_enums/internal/menu"
"seagull.sh/seagull/examples/mysql/007_enums/internal/store"
)
// run fills a chip shop menu whose section and status columns are MySQL ENUMs, marks an
// item sold out and reads the menu back. Each ENUM becomes a named Go type in the
// generated package unless an override names a type of your own:
//
// - section has no override, so models.go declares store.MenuItemsSection, named after
// the table and column that hold the ENUM, with a constant for each value, such as
// store.MenuItemsSectionFish, and a Valid method; ListMenuRow.Section,
// AddItemParams.Section and ItemsInSection's parameter are all a
// store.MenuItemsSection;
// - sgl.yaml's types.overrides names the column menu_items.status, so every query that
// returns it as it is gives menu.Status, as in ListMenuRow.Status, and so does every
// parameter compared with or assigned to it, as in SetStatusParams.Status; models.go
// uses it too, and the generated package declares no type for that ENUM;
// - sgl.column(section, go_type: ...) in ItemsInSection gives that one query's returned
// Section the type menu.Section; the parameter it compares with stays a
// store.MenuItemsSection.
//
// An ENUM sorts in the order its values are declared, not alphabetically, so ORDER BY
// section lists fish, then sides, then puddings. Valid reports whether a value is one the
// ENUM declares, so the shop can check a section before it binds it. In strict mode, the
// default since MySQL 5.7, a value the ENUM does not list is refused as it is stored as
// well; MySQL keeps an ENUM as the index of its value, and its error says the value was
// truncated.
//
// Takes database (*sql.DB) which is a migrated database.
// Takes output (io.Writer) which receives the menu, the fish section and the refusal.
//
// Returns error when a query fails other than as the example expects.
func run(ctx context.Context, database *sql.DB, output io.Writer) error {
queries := store.New(database)
items := []store.AddItemParams{
{Name: "cod", Section: store.MenuItemsSectionFish},
{Name: "haddock", Section: store.MenuItemsSectionFish},
{Name: "chips", Section: store.MenuItemsSectionSides},
{Name: "mushy peas", Section: store.MenuItemsSectionSides},
{Name: "sticky toffee pudding", Section: store.MenuItemsSectionPuddings},
}
for _, item := range items {
if err := queries.AddItem(ctx, item); err != nil {
return fmt.Errorf("adding %s: %w", item.Name, err)
}
}
if _, err := queries.SetStatus(ctx, store.SetStatusParams{Status: menu.SoldOut, Name: "haddock"}); err != nil {
return fmt.Errorf("selling out of haddock: %w", err)
}
listed, err := queries.ListMenu(ctx)
if err != nil {
return fmt.Errorf("listing the menu: %w", err)
}
var menuRow store.ListMenuRow
fmt.Fprintf(output, "ListMenu: section %T, status %T\n", menuRow.Section, menuRow.Status)
for _, item := range listed {
fmt.Fprintf(output, " %-9s %s%s\n", item.Section, item.Name, soldOut(item.Status))
}
fish, err := queries.ItemsInSection(ctx, store.MenuItemsSectionFish)
if err != nil {
return fmt.Errorf("listing the fish: %w", err)
}
var sectionRow store.ItemsInSectionRow
fmt.Fprintf(output, "ItemsInSection: section %T, status %T\n", sectionRow.Section, sectionRow.Status)
for _, item := range fish {
fmt.Fprintf(output, " %-9s %s%s\n", item.Section, item.Name, soldOut(item.Status))
}
drinks := store.MenuItemsSection("drinks")
fmt.Fprintf(output, "section drinks is valid: %t\n", drinks.Valid())
err = queries.AddItem(ctx, store.AddItemParams{Name: "lemonade", Section: drinks})
if err == nil {
return errors.New("added an item to a section the enum does not list")
}
fmt.Fprintf(output, "refused section drinks: %v\n", err)
return nil
}
// soldOut returns a note for an item that has sold out, comparing against the typed
// constant rather than a string.
//
// Takes status (menu.Status) which is the item's status.
//
// Returns string which is the note, or empty when the item is available.
func soldOut(status menu.Status) string {
if status == menu.SoldOut {
return " (sold out)"
}
return ""
}
What it prints
ListMenu: section store.MenuItemsSection, status menu.Status
fish cod
fish haddock (sold out)
sides chips
sides mushy peas
puddings sticky toffee pudding
ItemsInSection: section menu.Section, status menu.Status
fish cod
fish haddock (sold out)
section drinks is valid: false
refused section drinks: Error 1265 (01000): Data truncated for column 'section' at row 1
Type overrides covers go_type and the other override forms in more depth.
Across engines
Only these Go types of ItemsInSection differ between engines; the rest are the same everywhere.
| MySQL | DuckDB | |
|---|---|---|
section parameter | MenuItemsSection | Section |
MariaDB runs the MySQL version unchanged.
Compare the whole example side by side with DuckDB.
Where the example is missing:
- Not on SQLite. SQLite has no enum type. Use a CHECK constraint and a go_type override.
Run it locally
From the directory you cloned the Seagull repository into:
cd seagull/examples/mysql && go run ./007_enums
Its test runs it too, and checks what it prints against output.txt:
cd seagull/examples/mysql && SGL_EXAMPLE_DSN="$DATABASE_URL" go test ./007_enums
Needs a running MySQL server: set DATABASE_URL, or pass -dsn, to the address of an empty database.
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: menu
dialect: mysql
migrations: db/migrations
queries: db/queries
go:
package: store
out: internal/store
types:
overrides:
- column: menu_items.status
go: seagull.sh/seagull/examples/mysql/007_enums/internal/menu.Status
db/migrations/0001_menu.up.sql
CREATE TABLE menu_items (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
section ENUM('fish', 'sides', 'puddings') NOT NULL,
status ENUM('available', 'sold_out') NOT NULL DEFAULT 'available'
);
db/migrations/0001_menu.down.sql
DROP TABLE menu_items;
db/queries/menu.sql
-- sgl.query(AddItem, exec)
INSERT INTO menu_items (name, section) VALUES (?, ?);
-- sgl.query(SetStatus, execrows)
UPDATE menu_items SET status = ? WHERE name = ?;
-- sgl.query(ListMenu, many)
SELECT name, section, status FROM menu_items ORDER BY section, name;
-- sgl.query(ItemsInSection, many)
-- sgl.column(section, go_type: "seagull.sh/seagull/examples/mysql/007_enums/internal/menu.Section")
SELECT name, section, status FROM menu_items WHERE section = ? ORDER BY name;
example.go
package main
import (
"context"
"database/sql"
"errors"
"fmt"
"io"
"seagull.sh/seagull/examples/mysql/007_enums/internal/menu"
"seagull.sh/seagull/examples/mysql/007_enums/internal/store"
)
// run fills a chip shop menu whose section and status columns are MySQL ENUMs, marks an
// item sold out and reads the menu back. Each ENUM becomes a named Go type in the
// generated package unless an override names a type of your own:
//
// - section has no override, so models.go declares store.MenuItemsSection, named after
// the table and column that hold the ENUM, with a constant for each value, such as
// store.MenuItemsSectionFish, and a Valid method; ListMenuRow.Section,
// AddItemParams.Section and ItemsInSection's parameter are all a
// store.MenuItemsSection;
// - sgl.yaml's types.overrides names the column menu_items.status, so every query that
// returns it as it is gives menu.Status, as in ListMenuRow.Status, and so does every
// parameter compared with or assigned to it, as in SetStatusParams.Status; models.go
// uses it too, and the generated package declares no type for that ENUM;
// - sgl.column(section, go_type: ...) in ItemsInSection gives that one query's returned
// Section the type menu.Section; the parameter it compares with stays a
// store.MenuItemsSection.
//
// An ENUM sorts in the order its values are declared, not alphabetically, so ORDER BY
// section lists fish, then sides, then puddings. Valid reports whether a value is one the
// ENUM declares, so the shop can check a section before it binds it. In strict mode, the
// default since MySQL 5.7, a value the ENUM does not list is refused as it is stored as
// well; MySQL keeps an ENUM as the index of its value, and its error says the value was
// truncated.
//
// Takes database (*sql.DB) which is a migrated database.
// Takes output (io.Writer) which receives the menu, the fish section and the refusal.
//
// Returns error when a query fails other than as the example expects.
func run(ctx context.Context, database *sql.DB, output io.Writer) error {
queries := store.New(database)
items := []store.AddItemParams{
{Name: "cod", Section: store.MenuItemsSectionFish},
{Name: "haddock", Section: store.MenuItemsSectionFish},
{Name: "chips", Section: store.MenuItemsSectionSides},
{Name: "mushy peas", Section: store.MenuItemsSectionSides},
{Name: "sticky toffee pudding", Section: store.MenuItemsSectionPuddings},
}
for _, item := range items {
if err := queries.AddItem(ctx, item); err != nil {
return fmt.Errorf("adding %s: %w", item.Name, err)
}
}
if _, err := queries.SetStatus(ctx, store.SetStatusParams{Status: menu.SoldOut, Name: "haddock"}); err != nil {
return fmt.Errorf("selling out of haddock: %w", err)
}
listed, err := queries.ListMenu(ctx)
if err != nil {
return fmt.Errorf("listing the menu: %w", err)
}
var menuRow store.ListMenuRow
fmt.Fprintf(output, "ListMenu: section %T, status %T\n", menuRow.Section, menuRow.Status)
for _, item := range listed {
fmt.Fprintf(output, " %-9s %s%s\n", item.Section, item.Name, soldOut(item.Status))
}
fish, err := queries.ItemsInSection(ctx, store.MenuItemsSectionFish)
if err != nil {
return fmt.Errorf("listing the fish: %w", err)
}
var sectionRow store.ItemsInSectionRow
fmt.Fprintf(output, "ItemsInSection: section %T, status %T\n", sectionRow.Section, sectionRow.Status)
for _, item := range fish {
fmt.Fprintf(output, " %-9s %s%s\n", item.Section, item.Name, soldOut(item.Status))
}
drinks := store.MenuItemsSection("drinks")
fmt.Fprintf(output, "section drinks is valid: %t\n", drinks.Valid())
err = queries.AddItem(ctx, store.AddItemParams{Name: "lemonade", Section: drinks})
if err == nil {
return errors.New("added an item to a section the enum does not list")
}
fmt.Fprintf(output, "refused section drinks: %v\n", err)
return nil
}
// soldOut returns a note for an item that has sold out, comparing against the typed
// constant rather than a string.
//
// Takes status (menu.Status) which is the item's status.
//
// Returns string which is the note, or empty when the item is available.
func soldOut(status menu.Status) string {
if status == menu.SoldOut {
return " (sold out)"
}
return ""
}
internal/menu/menu.go
// Package menu holds the chip shop's own types. The generated store uses them in place of
// the enum types it would declare, through the override in sgl.yaml and the queries.
package menu
// Section is the part of the menu an item is listed in, matching the section ENUM.
type Section string
const (
// Fish lists the fried fish.
Fish Section = "fish"
// Sides lists chips, peas and sauces.
Sides Section = "sides"
// Puddings lists the desserts.
Puddings Section = "puddings"
)
// Status is whether a menu item can be ordered, matching the status ENUM.
type Status string
const (
// Available marks an item that can be ordered.
Available Status = "available"
// SoldOut marks an item that has run out for the day.
SoldOut Status = "sold_out"
)
main.go
// Command 007_enums applies its migrations to a MySQL database, then runs the example in
// example.go: a chip shop menu with two ENUM columns is read back as the enum types
// Seagull generates and as the shop's own Go types.
//
// Run it with `go run . -dsn 'user:password@tcp(localhost:3306)/menu'`, or set
// DATABASE_URL instead of passing -dsn. Give it an empty database of its own, as the
// migrations create its tables and the example adds rows each time it runs.
package main
import (
"context"
"database/sql"
"embed"
"errors"
"flag"
"fmt"
"log"
"os"
"github.com/go-sql-driver/mysql"
"seagull.sh/seagull/sdk/migrate"
"seagull.sh/seagull/sdk/migrate/migrate_backend_mysql"
)
// migrations holds the migration files, compiled into the binary.
//
//go:embed db/migrations
var migrations embed.FS
func main() {
dsn := flag.String("dsn", os.Getenv("DATABASE_URL"), "MySQL data source name; defaults to $DATABASE_URL")
flag.Parse()
if err := execute(context.Background(), *dsn); err != nil {
log.Fatal(err)
}
}
// execute opens and migrates the database dsn names, and runs the example against it.
//
// Takes dsn (string) which is the database's data source name.
//
// Returns error when the database cannot be prepared or the example fails.
func execute(ctx context.Context, dsn string) error {
if dsn == "" {
return errors.New("set -dsn or DATABASE_URL to a MySQL data source name")
}
config, err := mysql.ParseDSN(dsn)
if err != nil {
return err
}
// Seagull maps DATETIME and TIMESTAMP columns to time.Time, which the driver returns
// only when parseTime is set.
config.ParseTime = true
dsn = config.FormatDSN()
database, err := sql.Open("mysql", dsn)
if err != nil {
return err
}
defer func() { _ = database.Close() }()
migrator, err := migrate.NewMigrator(database, migrate_backend_mysql.New(migrate.WithDriverMultiStatements(config.MultiStatements)), 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/menu.sql.go
// Code generated by sgl. DO NOT EDIT.
package store
import (
"context"
"seagull.sh/seagull/examples/mysql/007_enums/internal/menu"
)
const addItemSQL = `INSERT INTO menu_items (name, section) VALUES (?, ?);`
type AddItemParams struct {
Name string
Section MenuItemsSection
}
func (queries *Queries) AddItem(ctx context.Context, params AddItemParams) error {
_, err := queries.writer.ExecContext(ctx, addItemSQL, params.Name, params.Section)
return err
}
const itemsInSectionSQL = `SELECT name, section, status FROM menu_items WHERE section = ? ORDER BY name;`
type ItemsInSectionRow struct {
Name string `json:"name"`
Section menu.Section `json:"section"`
Status menu.Status `json:"status"`
}
func (queries *Queries) ItemsInSection(ctx context.Context, section MenuItemsSection) ([]ItemsInSectionRow, error) {
rows, err := queries.reader.QueryContext(ctx, itemsInSectionSQL, section)
if err != nil {
return nil, err
}
defer rows.Close()
var results []ItemsInSectionRow
for rows.Next() {
var row ItemsInSectionRow
if err := rows.Scan(&row.Name, &row.Section, &row.Status); err != nil {
return nil, err
}
results = append(results, row)
}
if err := rows.Err(); err != nil {
return nil, err
}
return results, nil
}
const listMenuSQL = `SELECT name, section, status FROM menu_items ORDER BY section, name;`
type ListMenuRow struct {
Name string `json:"name"`
Section MenuItemsSection `json:"section"`
Status menu.Status `json:"status"`
}
func (queries *Queries) ListMenu(ctx context.Context) ([]ListMenuRow, error) {
rows, err := queries.reader.QueryContext(ctx, listMenuSQL)
if err != nil {
return nil, err
}
defer rows.Close()
var results []ListMenuRow
for rows.Next() {
var row ListMenuRow
if err := rows.Scan(&row.Name, &row.Section, &row.Status); err != nil {
return nil, err
}
results = append(results, row)
}
if err := rows.Err(); err != nil {
return nil, err
}
return results, nil
}
const setStatusSQL = `UPDATE menu_items SET status = ? WHERE name = ?;`
type SetStatusParams struct {
Status menu.Status
Name string
}
func (queries *Queries) SetStatus(ctx context.Context, params SetStatusParams) (int64, error) {
results, err := queries.writer.ExecContext(ctx, setStatusSQL, params.Status, params.Name)
if err != nil {
return 0, err
}
return results.RowsAffected()
}
internal/store/models.go
// Code generated by sgl. DO NOT EDIT.
package store
import "seagull.sh/seagull/examples/mysql/007_enums/internal/menu"
type MenuItems struct {
ID int64 `json:"id"`
Name string `json:"name"`
Section MenuItemsSection `json:"section"`
Status menu.Status `json:"status"`
}
type MenuItemsSection string
const (
MenuItemsSectionFish MenuItemsSection = "fish"
MenuItemsSectionSides MenuItemsSection = "sides"
MenuItemsSectionPuddings MenuItemsSection = "puddings"
)
// Valid reports whether the value is one the enum declares.
func (value MenuItemsSection) Valid() bool {
switch value {
case MenuItemsSectionFish, MenuItemsSectionSides, MenuItemsSectionPuddings:
return true
}
return false
}
internal/store/querier.go
// Code generated by sgl. DO NOT EDIT.
// Package store reads DATETIME, DATE and TIMESTAMP columns into time.Time, so the DSN it connects with must set parseTime=true.
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
ListMenu: section store.MenuItemsSection, status menu.Status
fish cod
fish haddock (sold out)
sides chips
sides mushy peas
puddings sticky toffee pudding
ItemsInSection: section menu.Section, status menu.Status
fish cod
fish haddock (sold out)
section drinks is valid: false
refused section drinks: Error 1265 (01000): Data truncated for column 'section' at row 1