Documentation
¶
Overview ¶
Package sqlkit context-aware transaction과 명시적인 row mapping을 위한 작은 database/sql helper를 제공한다.
sqlkit은 SQL을 호출자 소유이자 가시적인 값으로 유지한다. schema migration 관리, database pool 소유, ORM 뒤에 driver 동작 숨기기를 수행하지 않는다.
Index ¶
- Constants
- Variables
- func QueryAll[T any](ctx context.Context, db Queryer, query string, mapper Mapper[T], args ...any) (values []T, err error)
- func QueryOne[T any](ctx context.Context, db Queryer, query string, mapper Mapper[T], args ...any) (T, error)
- func QueryOptional[T any](ctx context.Context, db Queryer, query string, mapper Mapper[T], args ...any) (T, bool, error)
- func WithTx(ctx context.Context, db Beginner, opts *sql.TxOptions, fn TxFunc) error
- type Beginner
- type DeleteBuilder
- type EncryptedBytesColumn
- type EncryptedStringColumn
- type Execer
- type InsertBuilder
- type JSONColumn
- type Mapper
- type QueryRower
- type Queryer
- type SelectBuilder
- func (b *SelectBuilder) Build() (Statement, error)
- func (b *SelectBuilder) Columns(columns ...string) *SelectBuilder
- func (b *SelectBuilder) Limit(limit int) *SelectBuilder
- func (b *SelectBuilder) OrderBy(columns ...string) *SelectBuilder
- func (b *SelectBuilder) Where(fragment string, args ...any) *SelectBuilder
- type Session
- type Statement
- type TxFunc
- type UpdateBuilder
Examples ¶
Constants ¶
const ( // DefaultEncryptedColumnMaxPlaintextBytes plaintext 기본 한도를 1 MiB로 제한한다. DefaultEncryptedColumnMaxPlaintextBytes = 1 << 20 // DefaultEncryptedColumnMaxCiphertextBytes 저장할 ciphertext 기본 한도를 2 MiB로 제한한다. DefaultEncryptedColumnMaxCiphertextBytes = 2 << 20 )
const DefaultJSONColumnMaxBytes = 1 << 20
DefaultJSONColumnMaxBytes JSON source 또는 output size의 기본 최대값이다.
Variables ¶
var ( // ErrInvalidColumnValue 지원하지 않거나 malformed이거나 잘못 설정된 column value일 때 반환된다. ErrInvalidColumnValue = errors.New("sqlkit: invalid column value") // ErrColumnValueTooLarge column source 또는 encoded value가 byte 한도를 넘을 때 반환된다. ErrColumnValueTooLarge = errors.New("sqlkit: column value too large") )
var ( // ErrInvalidArgument 호출자가 전달한 SQL helper 입력이 유효하지 않을 때 반환된다. ErrInvalidArgument = errors.New("sqlkit: invalid argument") // ErrNoRows 단일 row query가 row를 반환하지 않았을 때 반환된다. ErrNoRows = errors.New("sqlkit: no rows") // ErrTooManyRows 단일 row query가 둘 이상의 row를 반환했을 때 반환된다. ErrTooManyRows = errors.New("sqlkit: too many rows") )
Functions ¶
func QueryAll ¶
func QueryAll[T any](ctx context.Context, db Queryer, query string, mapper Mapper[T], args ...any) (values []T, err error)
QueryAll query를 실행하고 반환된 모든 row를 mapping한다.
func QueryOne ¶
func QueryOne[T any](ctx context.Context, db Queryer, query string, mapper Mapper[T], args ...any) (T, error)
QueryOne query를 실행하고 정확히 1개의 mapped row를 반환한다.
func QueryOptional ¶
func QueryOptional[T any](ctx context.Context, db Queryer, query string, mapper Mapper[T], args ...any) (T, bool, error)
QueryOptional query를 실행하고 0개 또는 1개의 mapped row를 반환한다.
func WithTx ¶
WithTx transaction을 시작하고 fn을 실행한 뒤 fn이 성공한 경우에만 commit한다.
database handle lifecycle은 호출자가 소유한다. WithTx는 자신이 시작한 transaction만 소유한다. Context cancellation은 BeginTx와 fn에 그대로 전달되며, cancellation이나 deadline error를 재시도하지 않는다.
Example (GeneratedQueryHandle) ¶
package main
import (
"context"
"database/sql"
"github.com/bluetape4k/bluetape-go/sqlkit"
)
type boundaryAccount struct {
ID int64
Name string
}
type generatedAccountQueries interface {
GetAccount(context.Context, int64) (boundaryAccount, error)
}
type generatedAccountBinder func(*sql.Tx) generatedAccountQueries
func main() {
load := func(
ctx context.Context,
db *sql.DB,
bind generatedAccountBinder,
id int64,
) (boundaryAccount, error) {
var account boundaryAccount
err := sqlkit.WithTx(ctx, db, nil, func(ctx context.Context, tx *sql.Tx) error {
queries := bind(tx)
var err error
account, err = queries.GetAccount(ctx, id)
return err
})
return account, err
}
_ = load
}
Output:
Types ¶
type Beginner ¶
type Beginner interface {
BeginTx(ctx context.Context, opts *stdsql.TxOptions) (*stdsql.Tx, error)
}
Beginner database/sql transaction을 시작한다. *sql.DB가 이 interface를 구현한다.
type DeleteBuilder ¶
type DeleteBuilder struct {
// contains filtered or unexported fields
}
DeleteBuilder 단순한 PostgreSQL DELETE statement를 구성한다.
func DeleteFrom ¶
func DeleteFrom(table string) *DeleteBuilder
DeleteFrom table을 대상으로 하는 DELETE statement 구성을 시작한다.
func (*DeleteBuilder) Build ¶
func (b *DeleteBuilder) Build() (Statement, error)
Build 검토 가능한 DELETE statement를 반환한다.
func (*DeleteBuilder) Where ¶
func (b *DeleteBuilder) Where(fragment string, args ...any) *DeleteBuilder
Where 호출자가 소유한 SQL predicate fragment를 추가한다. 값 자리에는 ? placeholder를 사용하며 Build가 PostgreSQL $n placeholder로 다시 쓴다.
type EncryptedBytesColumn ¶ added in v0.19.0
type EncryptedBytesColumn struct {
Data []byte
Valid bool
MaxPlaintextBytes int
MaxCiphertextBytes int
// contains filtered or unexported fields
}
EncryptedBytesColumn 임의 plaintext byte를 binary envelope로 암호화해 저장한다.
zero value는 SQL NULL을 안전하게 나타낸다. non-NULL data를 Scan하거나 Value로 만들기 전에 NewEncryptedBytesColumn을 사용한다. Column value는 row별 mutable state이므로 동시에 변경하면 안 된다.
Example (Sqlkit) ¶
package main
import (
"context"
"database/sql"
"github.com/bluetape4k/bluetape-go/encrypt"
"github.com/bluetape4k/bluetape-go/sqlkit"
)
func main() {
load := func(ctx context.Context, db sqlkit.Queryer, encryptor encrypt.Encryptor, id int64) ([]byte, error) {
return sqlkit.QueryOne(ctx, db, "select payload from secrets where id = $1", func(rows *sql.Rows) ([]byte, error) {
column := sqlkit.NewEncryptedBytesColumn(encryptor, []byte("table=secrets:column=payload"))
if err := rows.Scan(&column); err != nil {
return nil, err
}
return append([]byte(nil), column.Data...), nil
}, id)
}
_ = load
}
Output:
func NewEncryptedBytesColumn ¶ added in v0.19.0
func NewEncryptedBytesColumn(encryptor encrypt.Encryptor, associatedData []byte) EncryptedBytesColumn
NewEncryptedBytesColumn byte column을 생성하고 associatedData를 복사한다.
func (*EncryptedBytesColumn) Scan ¶ added in v0.19.0
func (c *EncryptedBytesColumn) Scan(src any) error
Scan nil, string, []byte database value를 복호화한다.
Scan validation 전에 기존 plaintext를 지우고 driver 소유 input을 복사하며, 복호화와 size validation이 성공한 뒤에만 Data를 공개한다.
type EncryptedStringColumn ¶ added in v0.19.0
type EncryptedStringColumn struct {
Data string
Valid bool
MaxPlaintextBytes int
MaxCiphertextBytes int
// contains filtered or unexported fields
}
EncryptedStringColumn UTF-8 plaintext를 raw URL-safe base64 text로 암호화해 저장한다.
zero value는 SQL NULL을 안전하게 나타낸다. non-NULL data를 Scan하거나 Value로 만들기 전에 NewEncryptedStringColumn을 사용한다. Column value는 row별 mutable state이므로 동시에 변경하면 안 된다.
Example (GeneratedQuery) ¶
package main
import (
"context"
"database/sql/driver"
"github.com/bluetape4k/bluetape-go/encrypt"
"github.com/bluetape4k/bluetape-go/sqlkit"
)
type generatedUpdateSecretParams struct {
ID int64
Payload driver.Valuer
}
type generatedSecretQueries interface {
UpdateSecret(context.Context, generatedUpdateSecretParams) error
}
func main() {
save := func(ctx context.Context, queries generatedSecretQueries, encryptor encrypt.Encryptor, id int64, text string) error {
column := sqlkit.NewEncryptedStringColumn(encryptor, []byte("table=secrets:column=note"))
column.Data = text
column.Valid = true
return queries.UpdateSecret(ctx, generatedUpdateSecretParams{ID: id, Payload: column})
}
_ = save
}
Output:
func NewEncryptedStringColumn ¶ added in v0.19.0
func NewEncryptedStringColumn(encryptor encrypt.Encryptor, associatedData []byte) EncryptedStringColumn
NewEncryptedStringColumn string column을 생성하고 associatedData를 복사한다.
func (*EncryptedStringColumn) Scan ¶ added in v0.19.0
func (c *EncryptedStringColumn) Scan(src any) error
Scan nil, string, []byte database value를 복호화한다.
non-NULL input은 encrypt.Encryptor.EncryptString이 생성한 raw URL-safe base64 envelope를 포함해야 한다. Scan 작업 전에 기존 plaintext를 지운다.
func (EncryptedStringColumn) Value ¶ added in v0.19.0
func (c EncryptedStringColumn) Value() (value driver.Value, err error)
Value Data를 raw URL-safe base64 text로 암호화하거나 Valid가 false이면 nil을 반환한다.
Value non-NULL 값에 대해 string을 반환한다. 반복 호출은 독립적인 random nonce를 사용하므로 같은 plaintext라도 다른 ciphertext를 반환할 수 있다.
type Execer ¶
type Execer interface {
ExecContext(ctx context.Context, query string, args ...any) (stdsql.Result, error)
}
Execer *sql.DB와 *sql.Tx가 공유하는 context-aware Exec boundary다.
type InsertBuilder ¶
type InsertBuilder struct {
// contains filtered or unexported fields
}
InsertBuilder 단순한 PostgreSQL INSERT statement를 구성한다.
func InsertInto ¶
func InsertInto(table string) *InsertBuilder
InsertInto table을 대상으로 하는 INSERT statement 구성을 시작한다.
func (*InsertBuilder) Build ¶
func (b *InsertBuilder) Build() (Statement, error)
Build 검토 가능한 INSERT statement를 반환한다.
func (*InsertBuilder) Columns ¶
func (b *InsertBuilder) Columns(columns ...string) *InsertBuilder
Columns insert 대상 column 목록을 설정한다.
func (*InsertBuilder) Returning ¶
func (b *InsertBuilder) Returning(columns ...string) *InsertBuilder
Returning PostgreSQL RETURNING column 목록을 추가한다.
func (*InsertBuilder) Values ¶
func (b *InsertBuilder) Values(values ...any) *InsertBuilder
Values insert할 단일 row 값을 설정한다.
type JSONColumn ¶ added in v0.19.0
JSONColumn typed JSON value와 SQL NULL 상태를 함께 저장한다.
zero value는 SQL NULL을 나타낸다. MaxBytes가 0이면 DefaultJSONColumnMaxBytes를 사용한다. JSON literal null은 유효한 JSON value이며 SQL NULL과 구분된다.
Example (DatabaseSQL) ¶
query := func(ctx context.Context, db *sql.DB, id int64) (sqlkit.JSONColumn[jsonProfile], error) {
var profile sqlkit.JSONColumn[jsonProfile]
err := db.QueryRowContext(ctx, "select profile from accounts where id = $1", id).Scan(&profile)
return profile, err
}
save := func(ctx context.Context, db *sql.DB, id int64, profile jsonProfile) error {
value := sqlkit.JSONColumn[jsonProfile]{Data: profile, Valid: true}
_, err := db.ExecContext(ctx, "update accounts set profile = $1 where id = $2", value, id)
return err
}
_, _ = query, save
func (*JSONColumn[T]) Scan ¶ added in v0.19.0
func (c *JSONColumn[T]) Scan(src any) (err error)
Scan nil, string, []byte database value를 새로운 T 값으로 decode한다.
Scan driver가 소유한 byte를 복사하고 decode 전에 이전 값을 지우며, decode가 성공한 뒤에만 Data를 공개한다.
type QueryRower ¶
type QueryRower interface {
QueryRowContext(ctx context.Context, query string, args ...any) *stdsql.Row
}
QueryRower *sql.DB와 *sql.Tx가 공유하는 context-aware QueryRow boundary다.
type Queryer ¶
type Queryer interface {
QueryContext(ctx context.Context, query string, args ...any) (*stdsql.Rows, error)
}
Queryer *sql.DB와 *sql.Tx가 공유하는 context-aware Query boundary다.
type SelectBuilder ¶
type SelectBuilder struct {
// contains filtered or unexported fields
}
SelectBuilder 단순한 PostgreSQL SELECT statement를 구성한다.
func SelectFrom ¶
func SelectFrom(table string) *SelectBuilder
SelectFrom table을 대상으로 하는 SELECT statement 구성을 시작한다.
func (*SelectBuilder) Build ¶
func (b *SelectBuilder) Build() (Statement, error)
Build 검토 가능한 SELECT statement를 반환한다.
func (*SelectBuilder) Columns ¶
func (b *SelectBuilder) Columns(columns ...string) *SelectBuilder
Columns projection column을 설정한다. 생략하면 SELECT는 *를 사용한다.
func (*SelectBuilder) Limit ¶
func (b *SelectBuilder) Limit(limit int) *SelectBuilder
Limit PostgreSQL LIMIT 값을 설정한다.
func (*SelectBuilder) OrderBy ¶
func (b *SelectBuilder) OrderBy(columns ...string) *SelectBuilder
OrderBy identifier 기반 ascending order clause를 추가한다.
func (*SelectBuilder) Where ¶
func (b *SelectBuilder) Where(fragment string, args ...any) *SelectBuilder
Where 호출자가 소유한 SQL predicate fragment를 추가한다. 값 자리에는 ? placeholder를 사용하며 Build가 PostgreSQL $n placeholder로 다시 쓴다.
type Session ¶
type Session interface {
Execer
Queryer
QueryRower
}
Session *sql.DB와 *sql.Tx가 구현하는 공통 context-aware execution surface다.
Example (RepositoryBoundary) ¶
package main
import (
"context"
"database/sql"
"github.com/bluetape4k/bluetape-go/sqlkit"
)
type boundaryAccount struct {
ID int64
Name string
}
func main() {
load := func(ctx context.Context, session sqlkit.Session, id int64) (boundaryAccount, error) {
var account boundaryAccount
err := session.QueryRowContext(
ctx,
"select id, name from accounts where id = $1",
id,
).Scan(&account.ID, &account.Name)
return account, err
}
fromPool := func(ctx context.Context, db *sql.DB, id int64) (boundaryAccount, error) {
return load(ctx, db, id)
}
fromTransaction := func(ctx context.Context, tx *sql.Tx, id int64) (boundaryAccount, error) {
return load(ctx, tx, id)
}
_, _, _ = load, fromPool, fromTransaction
}
Output:
type Statement ¶
Statement 검토 가능한 SQL statement와 database/sql에 순서대로 전달할 argument를 함께 보관한다.
func NewStatement ¶
NewStatement args를 defensive copy한 stmt를 반환한다.
type UpdateBuilder ¶
type UpdateBuilder struct {
// contains filtered or unexported fields
}
UpdateBuilder 단순한 PostgreSQL UPDATE statement를 구성한다.
func Update ¶
func Update(table string) *UpdateBuilder
Update table을 대상으로 하는 UPDATE statement 구성을 시작한다.
func (*UpdateBuilder) Build ¶
func (b *UpdateBuilder) Build() (Statement, error)
Build 검토 가능한 UPDATE statement를 반환한다.
func (*UpdateBuilder) Set ¶
func (b *UpdateBuilder) Set(column string, value any) *UpdateBuilder
Set column assignment를 추가한다.
func (*UpdateBuilder) Where ¶
func (b *UpdateBuilder) Where(fragment string, args ...any) *UpdateBuilder
Where 호출자가 소유한 SQL predicate fragment를 추가한다. 값 자리에는 ? placeholder를 사용하며 Build가 PostgreSQL $n placeholder로 다시 쓴다.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package mariadbgis 는 MariaDB의 Point 값과 database/sql용 공간 SQL helper를 제공한다.
|
Package mariadbgis 는 MariaDB의 Point 값과 database/sql용 공간 SQL helper를 제공한다. |
|
Package mysqlgis 는 MySQL의 Point 값과 database/sql용 공간 SQL helper를 제공한다.
|
Package mysqlgis 는 MySQL의 Point 값과 database/sql용 공간 SQL helper를 제공한다. |
|
Package postgis PostgreSQL PostGIS의 좁은 Point 값과 database/sql용 SQL helper를 제공한다.
|
Package postgis PostgreSQL PostGIS의 좁은 Point 값과 database/sql용 SQL helper를 제공한다. |


