database

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Jul 28, 2026 License: MIT Imports: 8 Imported by: 0

README

Database Source · 数据库脚本来源

Go Version

基于标准 database/sql 的通用脚本来源,支持 MySQL / PostgreSQL / SQLite 等所有 SQL 数据库

中文 · English · 日本語


概述

Database Source 使用 Go 标准库 database/sql 从任意 SQL 数据库读取脚本。通过校验和列比对轮询检测热更新。支持自动生成查询和自定义 SQL 两种模式。

特性
特性 说明
底层库 Go 标准库 database/sql
热更新 校验和列比对轮询(默认 10s)
数据库 MySQL、PostgreSQL、SQLite、SQL Server 等所有 SQL 数据库
Key 前缀 支持 WithPrefix 命名空间隔离
连接池 支持自定义最大连接数、空闲连接数、连接生命周期
自定义查询 支持 WithQuery 完全自定义 SQL
接口 实现 source.ReadWatcher

安装

go get github.com/tx7do/go-scripts/source/database

需要同时安装对应数据库的驱动,例如:

# MySQL
go get github.com/go-sql-driver/mysql

# PostgreSQL (pgx)
go get github.com/jackc/pgx/v5/stdlib

# SQLite
go get modernc.org/sqlite

数据库表结构

推荐的表结构(可自定义):

CREATE TABLE scripts (
    name        VARCHAR(255) PRIMARY KEY,
    content     TEXT          NOT NULL,
    updated_at  TIMESTAMP     DEFAULT CURRENT_TIMESTAMP
);
  • name — 脚本标识符(Key 列)
  • content — 脚本内容(Value 列)
  • updated_at — 变更检测列(Checksum 列),每次更新自动变化

配置选项

选项 默认值 说明
WithDriver(driver) 必填 数据库驱动名称
WithDSN(dsn) 必填 数据源名称
WithDB(db) 注入已有 *sql.DB(优先于 Driver/DSN)
WithTable(table) scripts 表名
WithKeyColumn(col) name Key 列名
WithValueColumn(col) content 脚本内容列名
WithChecksumColumn(col) updated_at 变更检测列名
WithQuery(sql) 自动生成 自定义 SQL 查询
WithPrefix(prefix) Key 前缀
WithPollInterval(d) 10s Watch 轮询间隔
WithMaxOpenConns(n) 驱动默认 最大打开连接数
WithMaxIdleConns(n) 驱动默认 最大空闲连接数
WithConnMaxLifetime(d) 驱动默认 连接最大生命周期

快速开始

自动生成查询模式
package main

import (
    "context"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    dbSrc "github.com/tx7do/go-scripts/source/database"
)

func main() {
    ctx := context.Background()

    src, err := dbSrc.New(ctx,
        dbSrc.WithDriver("mysql"),
        dbSrc.WithDSN("user:pass@tcp(localhost:3306)/scripts"),
        dbSrc.WithTable("scripts"),
        dbSrc.WithKeyColumn("name"),
        dbSrc.WithValueColumn("content"),
        dbSrc.WithChecksumColumn("updated_at"),
    )
    if err != nil { panic(err) }
    defer src.Close()

    code, err := src.Load(ctx, "hello.lua")
    if err != nil { panic(err) }
    fmt.Println(code)
}
自定义查询模式
src, err := dbSrc.New(ctx,
    dbSrc.WithDriver("postgres"),
    dbSrc.WithDSN("host=localhost dbname=scripts"),
    dbSrc.WithQuery("SELECT body, version FROM my_scripts WHERE id = $1"),
)
共享连接池模式
import "database/sql"

db, _ := sql.Open("mysql", dsn)

src, err := dbSrc.New(ctx,
    dbSrc.WithDB(db),  // Reader 不会关闭 db
)
热更新
// 1. 先 Load 获取初始版本
code, _ := src.Load(ctx, "hello.lua")

// 2. Watch 监听变更
ch, _ := src.Watch(ctx, "hello.lua")
for range ch {
    // 3. 重新加载
    code, _ = src.Load(ctx, "hello.lua")
    fmt.Println("脚本已更新")
}

测试

cd source/database && go test -v ./...

相关文档

License

MIT License

Documentation

Overview

Package database provides a source.Reader implementation that reads scripts from any SQL database via the standard database/sql package.

Supported databases include MySQL, PostgreSQL, SQLite, SQL Server, and any driver registered with database/sql.

Construction:

src, err := database.New(ctx,
    database.WithDriver("mysql"),
    database.WithDSN("user:pass@tcp(localhost:3306)/scripts"),
    database.WithTable("scripts"),
    database.WithKeyColumn("name"),
    database.WithValueColumn("content"),
    database.WithChecksumColumn("updated_at"),
)

Hot-reload detection uses checksum polling: the Watcher periodically re-queries the checksum column and compares it with the value recorded at the last Load.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("database source: key not found")

ErrNotFound is returned (wrapped) by Load when the requested key does not exist in the database. Detect with errors.Is(err, ErrNotFound) or the convenience helper IsNotFound.

Functions

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether err represents a "key not found" response from the database. Equivalent to errors.Is(err, ErrNotFound).

Types

type Option

type Option func(*configOptions)

Option configures a Reader. Pass to New.

func WithChecksumColumn

func WithChecksumColumn(col string) Option

WithChecksumColumn sets the column used for change detection (default "updated_at"). This should be a column that changes whenever the row is modified (e.g., updated_at, version, checksum, etag).

func WithConnMaxLifetime

func WithConnMaxLifetime(d time.Duration) Option

WithConnMaxLifetime sets the maximum lifetime of a connection.

func WithDB

func WithDB(db *sql.DB) Option

WithDB injects a pre-existing *sql.DB instance. When set, WithDriver and WithDSN are ignored. The Reader will NOT close the DB on Close().

func WithDSN

func WithDSN(dsn string) Option

WithDSN sets the data source name for the database connection. Must be used together with WithDriver unless WithDB is used.

func WithDriver

func WithDriver(driver string) Option

WithDriver sets the database driver name (e.g. "mysql", "postgres", "sqlite3"). Must be used together with WithDSN unless WithDB is used.

func WithKeyColumn

func WithKeyColumn(col string) Option

WithKeyColumn sets the column name that identifies the script (default "name").

func WithMaxIdleConns

func WithMaxIdleConns(n int) Option

WithMaxIdleConns sets the maximum number of idle connections.

func WithMaxOpenConns

func WithMaxOpenConns(n int) Option

WithMaxOpenConns sets the maximum number of open connections.

func WithPollInterval

func WithPollInterval(d time.Duration) Option

WithPollInterval sets the polling interval for Watch (default 10s).

func WithPrefix

func WithPrefix(prefix string) Option

WithPrefix sets a key prefix that is transparently prepended to every key before it is resolved against the database. Useful when all scripts share a common namespace (e.g. WithPrefix("scripts/lua/")).

Leading slashes are stripped; no other normalization is applied.

func WithQuery

func WithQuery(query string) Option

WithQuery sets a custom SQL query that overrides the auto-generated one. The query must return exactly two columns: value first, checksum second. The key is passed as the first positional parameter (? or $1).

Example for PostgreSQL:

WithQuery("SELECT content, updated_at FROM scripts WHERE name = $1")

Example for MySQL / SQLite:

WithQuery("SELECT content, updated_at FROM scripts WHERE name = ?")

func WithTable

func WithTable(table string) Option

WithTable sets the table name that stores scripts (default "scripts").

func WithValueColumn

func WithValueColumn(col string) Option

WithValueColumn sets the column name that stores the script content (default "content").

type Reader

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

Reader reads scripts from a SQL database.

All exported methods are safe for concurrent use. Reader implements the source.ReadWatcher interface.

func New

func New(_ context.Context, opts ...Option) (*Reader, error)

New creates a database-backed Reader.

At minimum, either:

  • WithDriver + WithDSN (the Reader opens and owns the connection)
  • WithDB (the Reader uses a pre-existing *sql.DB and will not close it)

must be supplied. All other settings are optional with sensible defaults.

func (*Reader) Close

func (r *Reader) Close() error

Close releases the underlying database connection if this Reader owns it.

func (*Reader) Load

func (r *Reader) Load(ctx context.Context, key string) (string, error)

Load fetches the script value from the database and returns it as a string. Context cancellation propagates to the underlying query.

A missing row (sql.ErrNoRows) is reported as a wrapped ErrNotFound. Other errors are wrapped with the key for easier debugging.

func (*Reader) Watch

func (r *Reader) Watch(ctx context.Context, key string) (<-chan struct{}, error)

Watch returns a channel that signals when the value identified by `key` changes. It polls the checksum column every pollInterval and sends a signal on the channel when the checksum differs from the one recorded during the last Load.

The returned channel is closed when the context is cancelled. Callers should re-Load the script after receiving from the channel.

Jump to

Keyboard shortcuts

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