duckdb

package module
v0.2.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 7, 2025 License: MIT Imports: 15 Imported by: 0

README

GORM DuckDB Driver

A comprehensive DuckDB driver for GORM, following the same patterns and conventions used by other official GORM drivers.

Features

  • Full GORM compatibility
  • Auto-migration support
  • All standard SQL operations (CRUD)
  • Transaction support with savepoints
  • Index management
  • Constraint support
  • Comprehensive data type mapping
  • Connection pooling support

Quick Start

Install

Step 1: Add the dependencies to your project:

go get -u gorm.io/gorm
go get -u github.com/greysquirr3l/gorm-duckdb-driver

Step 2: Add a replace directive to your go.mod file:

module your-project

go 1.21

require (
    gorm.io/driver/duckdb v1.0.0
    gorm.io/gorm v1.25.12
)

// Replace directive to use this implementation
replace gorm.io/driver/duckdb => github.com/greysquirr3l/gorm-duckdb-driver v0.2.4

📝 Note: The replace directive is necessary because this driver uses the future official module path gorm.io/driver/duckdb but is currently hosted at github.com/greysquirr3l/gorm-duckdb-driver. This allows for seamless migration once this becomes the official GORM driver.

Step 3: Run go mod tidy to update dependencies:

go mod tidy
Connect to Database
import (
  "github.com/greysquirr3l/gorm-duckdb-driver"
  "gorm.io/gorm"
)

// In-memory database
db, err := gorm.Open(duckdb.Open(":memory:"), &gorm.Config{})

// File-based database
db, err := gorm.Open(duckdb.Open("test.db"), &gorm.Config{})

// With custom configuration
db, err := gorm.Open(duckdb.New(duckdb.Config{
  DSN: "test.db",
  DefaultStringSize: 256,
}), &gorm.Config{})

Data Type Mapping

Go Type DuckDB Type
bool BOOLEAN
int8 TINYINT
int16 SMALLINT
int32 INTEGER
int64 BIGINT
uint8 UTINYINT
uint16 USMALLINT
uint32 UINTEGER
uint64 UBIGINT
float32 REAL
float64 DOUBLE
string VARCHAR(n) / TEXT
time.Time TIMESTAMP
[]byte BLOB

Usage Examples

Define Models
type User struct {
  ID        uint      `gorm:"primarykey"`
  Name      string    `gorm:"size:100;not null"`
  Email     string    `gorm:"size:255;uniqueIndex"`
  Age       uint8
  Birthday  *time.Time
  CreatedAt time.Time
  UpdatedAt time.Time
}
Auto Migration
db.AutoMigrate(&User{})
CRUD Operations
// Create
user := User{Name: "John", Email: "john@example.com", Age: 30}
db.Create(&user)

// Read
var user User
db.First(&user, 1)                 // find user with integer primary key
db.First(&user, "name = ?", "John") // find user with name John

// Update
db.Model(&user).Update("name", "John Doe")
db.Model(&user).Updates(User{Name: "John Doe", Age: 31})

// Delete
db.Delete(&user, 1)
Advanced Queries
// Where
db.Where("name = ?", "John").Find(&users)
db.Where("age > ?", 18).Find(&users)

// Order
db.Order("age desc, name").Find(&users)

// Limit & Offset
db.Limit(3).Find(&users)
db.Offset(3).Limit(3).Find(&users)

// Group & Having
db.Model(&User{}).Group("name").Having("count(id) > ?", 1).Find(&users)
Transactions
db.Transaction(func(tx *gorm.DB) error {
  // do some database operations in the transaction
  if err := tx.Create(&User{Name: "John"}).Error; err != nil {
    return err
  }
  
  if err := tx.Create(&User{Name: "Jane"}).Error; err != nil {
    return err
  }
  
  return nil
})
Raw SQL
// Raw SQL
db.Raw("SELECT id, name, age FROM users WHERE name = ?", "John").Scan(&users)

// Exec
db.Exec("UPDATE users SET age = ? WHERE name = ?", 30, "John")

Migration Features

The DuckDB driver supports all GORM migration features:

Table Operations
// Create table
db.Migrator().CreateTable(&User{})

// Drop table  
db.Migrator().DropTable(&User{})

// Check if table exists
db.Migrator().HasTable(&User{})

// Rename table
db.Migrator().RenameTable(&User{}, &Admin{})
Column Operations
// Add column
db.Migrator().AddColumn(&User{}, "nickname")

// Drop column
db.Migrator().DropColumn(&User{}, "nickname")

// Alter column
db.Migrator().AlterColumn(&User{}, "name")

// Check if column exists
db.Migrator().HasColumn(&User{}, "name")

// Rename column
db.Migrator().RenameColumn(&User{}, "name", "full_name")

// Get column types
columnTypes, _ := db.Migrator().ColumnTypes(&User{})
Index Operations
// Create index
db.Migrator().CreateIndex(&User{}, "idx_user_name")

// Drop index
db.Migrator().DropIndex(&User{}, "idx_user_name")

// Check if index exists
db.Migrator().HasIndex(&User{}, "idx_user_name")

// Rename index
db.Migrator().RenameIndex(&User{}, "old_idx", "new_idx")
Constraint Operations
// Create constraint
db.Migrator().CreateConstraint(&User{}, "fk_user_company")

// Drop constraint
db.Migrator().DropConstraint(&User{}, "fk_user_company")

// Check if constraint exists
db.Migrator().HasConstraint(&User{}, "fk_user_company")

Configuration Options

type Config struct {
    DriverName        string        // Driver name, default: "duckdb"
    DSN               string        // Database source name
    Conn              gorm.ConnPool // Custom connection pool
    DefaultStringSize uint          // Default size for VARCHAR columns, default: 256
}

Notes

  • DuckDB is an embedded analytical database that excels at OLAP workloads
  • The driver supports both in-memory and file-based databases
  • All standard GORM features are supported including associations, hooks, and scopes
  • The driver follows DuckDB's SQL dialect and capabilities
  • For production use, consider DuckDB's performance characteristics for your specific use case

Known Limitations

While this driver provides full GORM compatibility, there are some DuckDB-specific limitations to be aware of:

Migration Schema Validation

Issue: DuckDB's PRAGMA table_info() returns slightly different column metadata format than PostgreSQL/MySQL.

Symptoms:

  • GORM AutoMigrate occasionally reports false schema differences
  • Unnecessary migration attempts on startup
  • Warnings in logs about column type mismatches

Example Warning:

[WARN] column type mismatch: expected 'VARCHAR', got 'STRING'

Workaround:

// Disable automatic migration validation for specific cases
db.AutoMigrate(&YourModel{})
// Add manual validation if needed

Impact: Low - Cosmetic warnings, doesn't affect functionality

Transaction Isolation Levels

Issue: DuckDB has limited transaction isolation level support compared to traditional databases.

Symptoms:

  • db.Begin().Isolation() methods have limited options
  • Some GORM transaction patterns may not work as expected
  • Read phenomena behavior differs from PostgreSQL

Workaround:

// Use simpler transaction patterns
tx := db.Begin()
defer func() {
    if r := recover(); r != nil {
        tx.Rollback()
    }
}()

// Perform operations...
if err := tx.Commit().Error; err != nil {
    return err
}

Impact: Low - Simple transactions work fine, complex isolation scenarios need adjustment

Time Pointer Conversion

Issue: Current implementation has limitations with *time.Time pointer conversion in some edge cases.

Symptoms:

  • Potential issues when working with nullable time fields
  • Some time pointer operations may not behave identically to other GORM drivers

Workaround:

// Use time.Time instead of *time.Time when possible
type Model struct {
    ID        uint      `gorm:"primarykey"`
    CreatedAt time.Time // Preferred
    UpdatedAt time.Time // Preferred
    DeletedAt gorm.DeletedAt `gorm:"index"` // This works fine
}

Impact: Low - Standard GORM time handling works correctly

Performance Considerations

  • DuckDB is optimized for analytical workloads (OLAP) rather than transactional workloads (OLTP)
  • For high-frequency write operations, consider batching or using traditional OLTP databases
  • DuckDB excels at complex queries, aggregations, and read-heavy workloads
  • For production use, consider DuckDB's performance characteristics for your specific use case

Contributing

This DuckDB driver aims to become an official GORM driver. Contributions are welcome!

Development Setup
git clone https://github.com/greysquirr3l/gorm-duckdb-driver.git
cd gorm-duckdb-driver
go mod tidy
go test -v
Submitting to GORM

This driver follows GORM's architecture and coding standards. Once stable and well-tested by the community, it will be submitted for inclusion in the official GORM drivers under go-gorm/duckdb.

Current status:

  • ✅ Full GORM interface implementation
  • ✅ Comprehensive test suite
  • ✅ Documentation and examples
  • 🔄 Community testing phase
  • ⏳ Awaiting official GORM integration

License

This driver is released under the MIT License, consistent with GORM's licensing.

Documentation

Index

Constants

View Source
const (
	// Core Extensions (built-in)
	ExtensionJSON    = "json"
	ExtensionParquet = "parquet"
	ExtensionICU     = "icu"

	// Analytics Extensions
	ExtensionAutoComplete = "autocomplete"
	ExtensionFTS          = "fts"
	ExtensionTPC_H        = "tpch"
	ExtensionTPC_DS       = "tpcds"

	// Data Format Extensions
	ExtensionCSV    = "csv"
	ExtensionExcel  = "excel"
	ExtensionArrow  = "arrow"
	ExtensionSQLite = "sqlite"

	// Networking Extensions
	ExtensionHTTPS = "httpfs"
	ExtensionS3    = "aws"
	ExtensionAzure = "azure"

	// Geospatial Extensions
	ExtensionSpatial = "spatial"

	// Machine Learning Extensions
	ExtensionML = "ml"

	// Time Series Extensions
	ExtensionTimeSeries = "timeseries"

	// Visualization Extensions
	ExtensionVisualization = "visualization"
)

Common DuckDB extensions

Variables

This section is empty.

Functions

func New

func New(config Config) gorm.Dialector

func NewWithExtensions

func NewWithExtensions(config Config, extensionConfig *ExtensionConfig) gorm.Dialector

NewWithExtensions creates a new dialector with extension support

func Open

func Open(dsn string) gorm.Dialector

func OpenWithExtensions

func OpenWithExtensions(dsn string, extensionConfig *ExtensionConfig) gorm.Dialector

OpenWithExtensions creates a dialector with extension support using DSN

Types

type ArrayLiteral

type ArrayLiteral struct {
	Data interface{}
}

ArrayLiteral wraps a Go slice to be formatted as a DuckDB array literal

func (ArrayLiteral) Value

func (al ArrayLiteral) Value() (driver.Value, error)

Value implements driver.Valuer for DuckDB array literals

type Config

type Config struct {
	DriverName        string
	DSN               string
	Conn              gorm.ConnPool
	DefaultStringSize uint
}

type Dialector

type Dialector struct {
	*Config
}

func (Dialector) BindVarTo

func (dialector Dialector) BindVarTo(writer clause.Writer, stmt *gorm.Statement, v interface{})

func (Dialector) DataTypeOf

func (dialector Dialector) DataTypeOf(field *schema.Field) string

func (Dialector) DefaultValueOf

func (dialector Dialector) DefaultValueOf(field *schema.Field) clause.Expression

func (Dialector) Explain

func (dialector Dialector) Explain(sql string, vars ...interface{}) string

func (Dialector) Initialize

func (dialector Dialector) Initialize(db *gorm.DB) error

func (Dialector) Migrator

func (dialector Dialector) Migrator(db *gorm.DB) gorm.Migrator

func (Dialector) Name

func (dialector Dialector) Name() string

func (Dialector) QuoteTo

func (dialector Dialector) QuoteTo(writer clause.Writer, str string)

func (Dialector) RollbackTo

func (dialector Dialector) RollbackTo(tx *gorm.DB, name string) error

func (Dialector) SavePoint

func (dialector Dialector) SavePoint(tx *gorm.DB, name string) error

type Extension

type Extension struct {
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Loaded      bool   `json:"loaded"`
	Installed   bool   `json:"installed"`
	BuiltIn     bool   `json:"built_in,omitempty"`
	Version     string `json:"version,omitempty"`
}

Extension represents a DuckDB extension with its metadata and status

type ExtensionConfig

type ExtensionConfig struct {
	// AutoInstall automatically installs extensions when loading
	AutoInstall bool

	// PreloadExtensions list of extensions to load on database connection
	PreloadExtensions []string

	// Timeout for extension operations (0 = no timeout)
	Timeout time.Duration

	// RepositoryURL custom extension repository URL
	RepositoryURL string

	// AllowUnsigned allows loading unsigned extensions (security risk)
	AllowUnsigned bool
}

ExtensionConfig holds configuration for extension management

type ExtensionHelper

type ExtensionHelper struct {
	// contains filtered or unexported fields
}

ExtensionHelper provides convenience methods for common extension operations

func NewExtensionHelper

func NewExtensionHelper(manager *ExtensionManager) *ExtensionHelper

NewExtensionHelper creates a new extension helper

func (*ExtensionHelper) EnableAnalytics

func (h *ExtensionHelper) EnableAnalytics() error

EnableAnalytics loads common analytics extensions

func (*ExtensionHelper) EnableCloudAccess

func (h *ExtensionHelper) EnableCloudAccess() error

EnableCloudAccess loads cloud storage extensions

func (*ExtensionHelper) EnableDataFormats

func (h *ExtensionHelper) EnableDataFormats() error

EnableDataFormats loads common data format extensions

func (*ExtensionHelper) EnableMachineLearning

func (h *ExtensionHelper) EnableMachineLearning() error

EnableMachineLearning loads ML extensions

func (*ExtensionHelper) EnableSpatial

func (h *ExtensionHelper) EnableSpatial() error

EnableSpatial loads geospatial extensions

func (*ExtensionHelper) EnableTimeSeries

func (h *ExtensionHelper) EnableTimeSeries() error

EnableTimeSeries loads time series extensions

type ExtensionManager

type ExtensionManager struct {
	// contains filtered or unexported fields
}

ExtensionManager handles DuckDB extension operations

func GetExtensionManager

func GetExtensionManager(db *gorm.DB) (*ExtensionManager, error)

GetExtensionManager retrieves the extension manager from a database instance

func MustGetExtensionManager

func MustGetExtensionManager(db *gorm.DB) *ExtensionManager

MustGetExtensionManager retrieves the extension manager, panics if not found

func NewExtensionManager

func NewExtensionManager(db *gorm.DB, config *ExtensionConfig) *ExtensionManager

NewExtensionManager creates a new extension manager instance

func (*ExtensionManager) GetExtension

func (m *ExtensionManager) GetExtension(name string) (*Extension, error)

GetExtension returns information about a specific extension

func (*ExtensionManager) GetLoadedExtensions

func (m *ExtensionManager) GetLoadedExtensions() ([]Extension, error)

GetLoadedExtensions returns all currently loaded extensions

func (*ExtensionManager) InstallExtension

func (m *ExtensionManager) InstallExtension(name string) error

InstallExtension installs an extension from the repository

func (*ExtensionManager) IsExtensionLoaded

func (m *ExtensionManager) IsExtensionLoaded(name string) bool

IsExtensionLoaded checks if an extension is currently loaded

func (*ExtensionManager) ListExtensions

func (m *ExtensionManager) ListExtensions() ([]Extension, error)

ListExtensions returns all available extensions

func (*ExtensionManager) LoadExtension

func (m *ExtensionManager) LoadExtension(name string) error

LoadExtension loads an extension, optionally installing it first

func (*ExtensionManager) LoadExtensions

func (m *ExtensionManager) LoadExtensions(names []string) error

LoadExtensions loads multiple extensions

func (*ExtensionManager) PreloadExtensions

func (m *ExtensionManager) PreloadExtensions() error

PreloadExtensions loads all configured preload extensions

type FloatArray

type FloatArray []float64

FloatArray represents a DuckDB DOUBLE[] array type

func (FloatArray) GormDataType

func (FloatArray) GormDataType() string

GormDataType implements the GormDataTypeInterface for FloatArray

func (*FloatArray) Scan

func (a *FloatArray) Scan(value interface{}) error

Scan implements sql.Scanner interface for FloatArray

func (FloatArray) Value

func (a FloatArray) Value() (driver.Value, error)

Value implements driver.Valuer interface for FloatArray

type IntArray

type IntArray []int64

IntArray represents a DuckDB INTEGER[] array type

func (IntArray) GormDataType

func (IntArray) GormDataType() string

GormDataType implements the GormDataTypeInterface for IntArray

func (*IntArray) Scan

func (a *IntArray) Scan(value interface{}) error

Scan implements sql.Scanner interface for IntArray

func (IntArray) Value

func (a IntArray) Value() (driver.Value, error)

Value implements driver.Valuer interface for IntArray

type Migrator

type Migrator struct {
	migrator.Migrator
}

func (Migrator) AlterColumn

func (m Migrator) AlterColumn(value interface{}, field string) error

func (Migrator) CreateView

func (m Migrator) CreateView(name string, option gorm.ViewOption) error

func (Migrator) CurrentDatabase

func (m Migrator) CurrentDatabase() (name string)

func (Migrator) DropConstraint

func (m Migrator) DropConstraint(value interface{}, name string) error

func (Migrator) DropIndex

func (m Migrator) DropIndex(value interface{}, name string) error

func (Migrator) DropView

func (m Migrator) DropView(name string) error

func (Migrator) FullDataTypeOf

func (m Migrator) FullDataTypeOf(field *schema.Field) clause.Expr

Override FullDataTypeOf to prevent GORM from adding duplicate PRIMARY KEY clauses

func (Migrator) GetTables

func (m Migrator) GetTables() (tableList []string, err error)

func (Migrator) GetTypeAliases

func (m Migrator) GetTypeAliases(databaseTypeName string) []string

func (Migrator) HasColumn

func (m Migrator) HasColumn(value interface{}, field string) bool

func (Migrator) HasConstraint

func (m Migrator) HasConstraint(value interface{}, name string) bool

func (Migrator) HasIndex

func (m Migrator) HasIndex(value interface{}, name string) bool

func (Migrator) HasTable

func (m Migrator) HasTable(value interface{}) bool

func (Migrator) RenameColumn

func (m Migrator) RenameColumn(value interface{}, oldName, newName string) error

func (Migrator) RenameIndex

func (m Migrator) RenameIndex(value interface{}, oldName, newName string) error

type SimpleArrayScanner

type SimpleArrayScanner struct {
	Target interface{} // Pointer to slice
}

SimpleArrayScanner provides basic array scanning functionality

func (*SimpleArrayScanner) Scan

func (sas *SimpleArrayScanner) Scan(value interface{}) error

Scan implements sql.Scanner for basic array types

type StringArray

type StringArray []string

StringArray represents a DuckDB TEXT[] array type

func (StringArray) GormDataType

func (StringArray) GormDataType() string

GormDataType implements the GormDataTypeInterface for StringArray

func (*StringArray) Scan

func (a *StringArray) Scan(value interface{}) error

Scan implements sql.Scanner interface for StringArray

func (StringArray) Value

func (a StringArray) Value() (driver.Value, error)

Value implements driver.Valuer interface for StringArray

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL