Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
- "httpserver"
- "log"
- "orm"
- "sql"
- "trace"
- "worker"
- "fxcore"
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/sql-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: "sql-ci"

on:
push:
branches:
- "feat**"
- "fix**"
- "hotfix**"
- "chore**"
paths:
- "sql/**.go"
- "sql/go.mod"
- "sql/go.sum"
pull_request:
types:
- opened
- synchronize
- reopened
branches:
- main
paths:
- "sql/**.go"
- "sql/go.mod"
- "sql/go.sum"

jobs:
ci:
uses: ./.github/workflows/common-ci.yml
secrets: inherit
with:
module: "sql"
5 changes: 5 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
"component": "orm",
"tag-separator": "/"
},
"sql": {
"release-type": "go",
"component": "sql",
"tag-separator": "/"
},
"trace": {
"release-type": "go",
"component": "trace",
Expand Down
65 changes: 65 additions & 0 deletions sql/.golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
run:
timeout: 5m
concurrency: 8

linters:
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- containedctx
- contextcheck
- cyclop
- decorder
- dogsled
- durationcheck
- errcheck
- errchkjson
- errname
- errorlint
- exhaustive
- forbidigo
- forcetypeassert
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- gofmt
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosimple
- govet
- grouper
- importas
- ineffassign
- interfacebloat
- logrlint
- maintidx
- makezero
- misspell
- nestif
- nilerr
- nilnil
- nlreturn
- nolintlint
- nosprintfhostport
- prealloc
- predeclared
- promlinter
- reassign
- staticcheck
- tenv
- thelper
- tparallel
- typecheck
- unconvert
- unparam
- unused
- usestdlibvars
- whitespace
199 changes: 199 additions & 0 deletions sql/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# SQL Module

[![ci](https://github.com/ankorstore/yokai/actions/workflows/sql-ci.yml/badge.svg)](https://github.com/ankorstore/yokai/actions/workflows/sql-ci.yml)
[![go report](https://goreportcard.com/badge/github.com/ankorstore/yokai/sql)](https://goreportcard.com/report/github.com/ankorstore/yokai/sql)
[![codecov](https://codecov.io/gh/ankorstore/yokai/graph/badge.svg?token=ghUBlFsjhR&flag=sql)](https://app.codecov.io/gh/ankorstore/yokai/tree/main/sql)
[![Deps](https://img.shields.io/badge/osi-deps-blue)](https://deps.dev/go/github.com%2Fankorstore%2Fyokai%2Fsql)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/ankorstore/yokai/sql)](https://pkg.go.dev/github.com/ankorstore/yokai/sql)

> SQL module based on [database/sql](https://pkg.go.dev/database/sql).

<!-- TOC -->
* [Installation](#installation)
* [Documentation](#documentation)
* [Usage](#usage)
* [Hooks](#hooks)
* [Log hook](#log-hook)
* [Trace hook](#trace-hook)
* [Custom hook](#custom-hook)
* [Healthcheck](#healthcheck)
<!-- TOC -->

## Installation

```shell
go get github.com/ankorstore/yokai/sql
```

## Documentation

This module provides a [Driver](driver.go), decorating `database/sql` compatible drivers, with a [hooking mechanism](hook.go).

### Usage

The following database systems are [supported](system.go):

- `mysql` with [go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
- `postgres` with [lib/pq](https://github.com/lib/pq)
- `sqlite` with [mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)

To create a `*sql.DB` with the [tracing](hook/trace/hook.go) and [logging](hook/log/hook.go) hooks:

```go
package main

import (
"database/sql"

yokaisql "github.com/ankorstore/yokai/sql"
"github.com/ankorstore/yokai/sql/hook/log"
"github.com/ankorstore/yokai/sql/hook/trace"
)

func main() {
// MySQL
driver, _ := yokaisql.Register("mysql", trace.NewTraceHook(), log.NewLogHook())
db, _ := sql.Open(driver, "user:password@tcp(localhost:3306)/db?parseTime=true")

// Postgres
driver, _ := yokaisql.Register("postgres", trace.NewTraceHook(), log.NewLogHook())
db, _ := sql.Open(driver, "host=host port=5432 user=user password=password dbname=db sslmode=disable")

// SQLite
driver, _ := yokaisql.Register("sqlite", trace.NewTraceHook(), log.NewLogHook())
db, _ := sql.Open(driver, ":memory:")
}
```

See [database/sql](https://pkg.go.dev/database/sql) documentation for more details.

### Hooks

This module provides a [hooking mechanism](hook.go) to add logic around the [SQL operations](operation.go).

#### Log hook

This module provides an [LogHook](hook/log/hook.go), that you can use to automatically `log` the [SQL operations](operation.go):

```go
package main

import (
"database/sql"

yokaisql "github.com/ankorstore/yokai/sql"
"github.com/ankorstore/yokai/sql/hook/log"
"github.com/rs/zerolog"
)

func main() {

logHook := log.NewLogHook(
log.WithLevel(zerolog.DebugLevel), // SQL logs level, debug by default
log.WithArguments(true), // SQL logs with SQL arguments, false by default
log.WithExcludedOperations( // SQL operations to exclude from logging, empty by default
yokaisql.ConnectionPingOperation,
yokaisql.ConnectionResetSessionOperation,
),
)

driver, _ := yokaisql.Register("sqlite", logHook)
db, _ := sql.Open(driver, ":memory:")
}
```

#### Trace hook

This module provides an [TraceHook](hook/trace/hook.go), that you can use to automatically `trace` the [SQL operations](operation.go):

```go
package main

import (
"database/sql"

yokaisql "github.com/ankorstore/yokai/sql"
"github.com/ankorstore/yokai/sql/hook/log"
"github.com/ankorstore/yokai/sql/hook/trace"
"github.com/rs/zerolog"
)

func main() {

traceHook := trace.NewTraceHook(
trace.WithArguments(true), // SQL traces with SQL arguments, false by default
trace.WithExcludedOperations( // SQL operations to exclude from tracing, empty by default
yokaisql.ConnectionPingOperation,
yokaisql.ConnectionResetSessionOperation,
),
)

driver, _ := yokaisql.Register("sqlite", traceHook)
db, _ := sql.Open(driver, ":memory:")
}
```

#### Custom hook

This module provides a [Hook](hook.go) interface, that you can implement to extend the logic around [SQL operations](operation.go):

```go
package main

import (
"context"
"database/sql"

yokaisql "github.com/ankorstore/yokai/sql"
"github.com/ankorstore/yokai/sql/hook/log"
"github.com/ankorstore/yokai/sql/hook/trace"
"github.com/rs/zerolog"
)

type CustomHook struct{}

func (h *CustomHook) Before(ctx context.Context, event *yokaisql.HookEvent) context.Context {
// your custom logic before SQL operation

return ctx
}

func (h *CustomHook) After(ctx context.Context, event *yokaisql.HookEvent) {
// your custom logic after SQL operation
}

func main() {
driver, _ := yokaisql.Register("sqlite", &CustomHook{})
db, _ := sql.Open(driver, ":memory:")
}
```

### Healthcheck

This module provides an [SQLProbe](healthcheck/probe.go), compatible with
the [healthcheck module](https://github.com/ankorstore/yokai/tree/main/healthcheck):

```go
package main

import (
"context"

yokaihc "github.com/ankorstore/yokai/healthcheck"
yokaisql "github.com/ankorstore/yokai/sql"
"github.com/ankorstore/yokai/sql/healthcheck"
)

func main() {
driver, _ := yokaisql.Register("sqlite")
db, _ := sql.Open(driver, ":memory:")

checker, _ := yokaihc.NewDefaultCheckerFactory().Create(
yokaihc.WithProbe(healthcheck.NewSQLProbe(db)),
)

checker.Check(context.Background(), yokaihc.Readiness)
}
```

This probe performs a `ping` to the configured database connection.
25 changes: 25 additions & 0 deletions sql/configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package sql

// Configuration is the SQL components (driver, connector, connection, etc) configuration.
type Configuration struct {
system System
hooks []Hook
}

// NewConfiguration returns a new Configuration.
func NewConfiguration(system System, hooks ...Hook) *Configuration {
return &Configuration{
system: system,
hooks: hooks,
}
}

// System returns the Configuration System.
func (c *Configuration) System() System {
return c.system
}

// Hooks returns the Configuration list of Hook.
func (c *Configuration) Hooks() []Hook {
return c.hooks
}
24 changes: 24 additions & 0 deletions sql/configuration_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package sql_test

import (
"testing"

"github.com/ankorstore/yokai/sql"
"github.com/ankorstore/yokai/sql/hook/log"
"github.com/ankorstore/yokai/sql/hook/trace"
"github.com/stretchr/testify/assert"
)

func TestNewConfiguration(t *testing.T) {
t.Parallel()

hooks := []sql.Hook{
trace.NewTraceHook(),
log.NewLogHook(),
}

config := sql.NewConfiguration(sql.SqliteSystem, hooks...)

assert.Equal(t, sql.SqliteSystem, config.System())
assert.Equal(t, hooks, config.Hooks())
}
Loading