Dew

Dialects & Drivers

Supported SQL dialects, placeholder styles, and database driver setup.

Dew talks to database/sql — it never touches the driver directly. You pick any driver you want, and dew handles the rest. A dialect controls how query placeholders are rendered.

Available dialects

DialectStructPlaceholdersExample
PostgreSQLdew.PostgreSQLDialect{}$1, $2, $3WHERE id = $1
MySQLdew.MySQLDialect{}?, ?, ?WHERE id = ?
SQLitedew.SQLiteDialect{}?, ?, ?WHERE id = ?
MSSQLdew.MSSQLDialect{}@p1, @p2, @p3WHERE id = @p1

Usage

Pass a dialect when opening a connection:

db, err := dew.Open("postgres", dsn, dew.PostgreSQLDialect{})

And when defining schemas:

var Users = dew.DefineSchema("users", dew.PostgreSQLDialect{}, func(t dew.Table[User]) struct {
    // ...
})

The Dialect interface

type Dialect interface {
    Placeholder(index int) string
}

The index is zero-based. PostgreSQL's Placeholder(0) returns "$1", Placeholder(1) returns "$2", etc.

Custom dialects

Implement the Dialect interface to support other databases:

type OracleDialect struct{}

func (d OracleDialect) Placeholder(index int) string {
    return fmt.Sprintf(":p%d", index+1)
}

Then use it like any built-in dialect:

db, err := dew.Open("oracle", dsn, OracleDialect{})

Database drivers

Dew works with any driver that registers itself with database/sql. The driver handles the actual connection; dew only builds and executes queries.

PostgreSQL

import _ "github.com/lib/pq"

db, err := dew.Open("postgres",
    "postgres://user:pass@localhost/mydb?sslmode=disable",
    dew.PostgreSQLDialect{},
)
import _ "github.com/jackc/pgx/v5/stdlib"

db, err := dew.Open("pgx",
    "postgres://user:pass@localhost/mydb?sslmode=disable",
    dew.PostgreSQLDialect{},
)

Or wrap an existing pgx connection pool:

import (
    "github.com/jackc/pgx/v5/pgxpool"
    "github.com/jackc/pgx/v5/stdlib"
)

pool, err := pgxpool.New(ctx, dsn)
sqlDB := stdlib.OpenDBFromPool(pool)

db := dew.NewDB(sqlDB, dew.PostgreSQLDialect{})

MySQL

import _ "github.com/go-sql-driver/mysql"

db, err := dew.Open("mysql",
    "user:pass@tcp(localhost:3306)/mydb?parseTime=true",
    dew.MySQLDialect{},
)

SQLite

import _ "modernc.org/sqlite"

db, err := dew.Open("sqlite", "file.db", dew.SQLiteDialect{})
import _ "github.com/mattn/go-sqlite3"

db, err := dew.Open("sqlite3", "file.db", dew.SQLiteDialect{})

MSSQL

import _ "github.com/microsoft/go-mssqldb"

db, err := dew.Open("sqlserver",
    "sqlserver://user:pass@localhost:1433?database=mydb",
    dew.MSSQLDialect{},
)

Switching drivers

Since dew only depends on database/sql, switching drivers is a one-line change — swap the import and the driver name. The dialect stays the same:

// Before: lib/pq
import _ "github.com/lib/pq"
db, err := dew.Open("postgres", dsn, dew.PostgreSQLDialect{})

// After: pgx
import _ "github.com/jackc/pgx/v5/stdlib"
db, err := dew.Open("pgx", dsn, dew.PostgreSQLDialect{})

All your queries, schemas, and error mappers stay unchanged.

Error mapping per driver

Each driver returns different error types. Your ErrorMapper needs to match the driver you use:

import "github.com/lib/pq"

func pqMapper(err error) error {
    var pqErr *pq.Error
    if !errors.As(err, &pqErr) {
        return err
    }
    switch pqErr.Code {
    case "23505":
        return dew.ErrUniqueViolation
    case "23503":
        return dew.ErrForeignKey
    case "23502":
        return dew.ErrNotNull
    case "23514":
        return dew.ErrCheckViolation
    default:
        return err
    }
}
import "github.com/jackc/pgx/v5/pgconn"

func pgxMapper(err error) error {
    var pgErr *pgconn.PgError
    if !errors.As(err, &pgErr) {
        return err
    }
    switch pgErr.Code {
    case "23505":
        return dew.ErrUniqueViolation
    case "23503":
        return dew.ErrForeignKey
    case "23502":
        return dew.ErrNotNull
    case "23514":
        return dew.ErrCheckViolation
    default:
        return err
    }
}
import "github.com/go-sql-driver/mysql"

func mysqlMapper(err error) error {
    var mysqlErr *mysql.MySQLError
    if !errors.As(err, &mysqlErr) {
        return err
    }
    switch mysqlErr.Number {
    case 1062:
        return dew.ErrUniqueViolation
    case 1452:
        return dew.ErrForeignKey
    case 1048:
        return dew.ErrNotNull
    case 3819:
        return dew.ErrCheckViolation
    default:
        return err
    }
}

Then pass it when creating the DB:

db, err := dew.Open("pgx", dsn, dew.PostgreSQLDialect{},
    dew.WithErrorMapper(pgxMapper),
)

On this page