Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add database adapter pkg #30

Open
wants to merge 46 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
653ee08
Add database adapter pkg
kanataxa Mar 2, 2020
87b934d
remove unnecessary field
kanataxa Mar 2, 2020
fe6a916
fix lint
kanataxa Mar 2, 2020
b8d379a
refactor
kanataxa Mar 2, 2020
340490b
rename package
kanataxa Mar 4, 2020
17a7ca6
modified database.Adapter defs
kanataxa Mar 12, 2020
9217362
Add adapter args for FLC and SLC
kanataxa Mar 12, 2020
2ba2929
add DBType option
kanataxa Mar 12, 2020
1330938
use adapter in QueryBuilder
kanataxa Mar 12, 2020
00e9fae
fix test
kanataxa Mar 12, 2020
7ab8584
Merge branch 'master' into feature/support-postgres-ddl
kanataxa May 1, 2020
c0a8bb4
fix placeholders string
kanataxa May 1, 2020
eb2022e
refactor and init adapter in query builder
kanataxa May 7, 2020
c9bb971
use testdata .sql file, initializing table schema
kanataxa May 7, 2020
5ab2b9a
get driver name from environment value
kanataxa May 7, 2020
d57b1a7
add RAPIDASH_DB_DRIVER env
kanataxa May 7, 2020
3a60fe1
fix exit code
kanataxa May 7, 2020
c6bb71a
undo remove initEmptyTable func
kanataxa May 7, 2020
ecacdb6
add adapter arg to new query builder func
kanataxa May 8, 2020
8072193
fix Placeholder idx number
kanataxa May 8, 2020
8e89221
refactor test, can test both mysql and postgres by global driver val
kanataxa May 8, 2020
949ff93
add Escape string function
kanataxa May 8, 2020
168d182
escape table and column by adapter instead of back quote
kanataxa May 8, 2020
042e69a
rename from Escapte to Quote
kanataxa May 8, 2020
0c5930b
add table definition sql for test with postgres
kanataxa May 8, 2020
e24d88b
update DataType handling
kanataxa May 8, 2020
d703d86
fix test for multiple db type
kanataxa May 15, 2020
fe88c42
fix placeholder
kanataxa May 19, 2020
285b8da
use RETURNING col_name expr if not support last_insert_id()
kanataxa May 19, 2020
622256c
add testdata
kanataxa May 19, 2020
1235657
run ALTER query by sql file for multiple db type
kanataxa May 19, 2020
ef0e82a
switch by DBType, expecting to be generated SQL
kanataxa May 19, 2020
c9cdd43
fix placeholder
kanataxa May 20, 2020
56b6e35
fix receiver name
kanataxa May 21, 2020
39d9f22
fix database package interfaces to support incremental placeholder
kanataxa May 22, 2020
83a8c0b
fix test
kanataxa May 22, 2020
a54a900
fix the column name used in RETURNING syntax to id
kanataxa Jun 1, 2020
0f9d131
refactor database pkg. rename struct type, and add DBType() function
kanataxa Jun 3, 2020
f18fcc1
ignore cache, if call SQL() function in other than mysql
kanataxa Jun 3, 2020
688c2d1
fix warm up users table tests
kanataxa Jun 3, 2020
9ec0c8e
fix TestPointerType
kanataxa Jun 3, 2020
ef3fdaa
add unit tests by postgres
kanataxa Jun 14, 2020
302cbb4
update dependents pkgs
kanataxa Jun 14, 2020
3b6a8b2
add postgres image for ci
kanataxa Jun 14, 2020
3334687
add env for postgres image
kanataxa Jun 14, 2020
a59e6c6
fix postgres image envs
kanataxa Jun 14, 2020
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
13 changes: 11 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ jobs:
environment:
GO111MODULE: "on"
- image: circleci/mysql:5.7
- image: circleci/postgres:12.2
environment:
POSTGRES_USER: root
POSTGRES_DB: rapidash
POSTGRES_HOST_AUTH_METHOD: trust
- image: memcached:1.5
- image: redis:5.0
steps:
Expand All @@ -25,11 +30,15 @@ jobs:
golangci-lint run
- run:
name: Wait for DB
command: dockerize -wait tcp://127.0.0.1:3306 -timeout 30s
command: dockerize -wait tcp://127.0.0.1:3306 -wait tcp://127.0.0.1:5432 -timeout 30s
- run:
name: Run unit tests for postgres
command: |
RAPIDASH_DB_DRIVER=postgres go test -v ./...
- run:
name: Run unit tests and measure coverage
command: |
go test -v -coverprofile=coverage.out ./...
RAPIDASH_DB_DRIVER=mysql go test -v -coverprofile=coverage.out ./...
bash <(curl -s https://codecov.io/bash) -P ${CIRCLE_PULL_REQUEST##*/}
workflows:
version: 2
Expand Down
14 changes: 7 additions & 7 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ func BenchmarkGetByPrimaryKey_RapidashWorst(b *testing.B) {
if err != nil {
panic(err)
}
builder := NewQueryBuilder("a").Eq("id", id)
builder := NewQueryBuilder("a", driver.Adapter).Eq("id", id)
var a A
if err := tx.FindByQueryBuilder(builder, &a); err != nil {
panic(err)
Expand Down Expand Up @@ -243,7 +243,7 @@ func BenchmarkGetByPrimaryKey_RapidashBest(b *testing.B) {
if err != nil {
panic(err)
}
builder := NewQueryBuilder("a").Eq("id", id)
builder := NewQueryBuilder("a", driver.Adapter).Eq("id", id)
var a A
if err := tx.FindByQueryBuilder(builder, &a); err != nil {
panic(err)
Expand All @@ -261,7 +261,7 @@ func BenchmarkGetByPrimaryKey_RapidashBest(b *testing.B) {
if err != nil {
panic(err)
}
builder := NewQueryBuilder("a").Eq("id", id)
builder := NewQueryBuilder("a", driver.Adapter).Eq("id", id)
var a A
if err := tx.FindByQueryBuilder(builder, &a); err != nil {
panic(err)
Expand Down Expand Up @@ -462,7 +462,7 @@ func BenchmarkUpdateByPrimaryKey_RapidashWorst(b *testing.B) {
if err != nil {
panic(err)
}
builder := NewQueryBuilder("a").Eq("id", id)
builder := NewQueryBuilder("a", driver.Adapter).Eq("id", id)
if err := tx.UpdateByQueryBuilder(builder, map[string]interface{}{
"name": "bench2",
}); err != nil {
Expand Down Expand Up @@ -503,7 +503,7 @@ func BenchmarkUpdateByPrimaryKey_RapidashBest(b *testing.B) {
if err != nil {
panic(err)
}
builder := NewQueryBuilder("a").Eq("id", id)
builder := NewQueryBuilder("a", driver.Adapter).Eq("id", id)
var a A
if err := tx.FindByQueryBuilder(builder, &a); err != nil {
panic(err)
Expand All @@ -524,7 +524,7 @@ func BenchmarkUpdateByPrimaryKey_RapidashBest(b *testing.B) {
if err != nil {
panic(err)
}
builder := NewQueryBuilder("a").Eq("id", id)
builder := NewQueryBuilder("a", driver.Adapter).Eq("id", id)
if err := tx.UpdateByQueryBuilder(builder, map[string]interface{}{
"name": "bench2",
}); err != nil {
Expand Down Expand Up @@ -622,7 +622,7 @@ func BenchmarkDeleteByPrimaryKey_Rapidash(b *testing.B) {
if err != nil {
panic(err)
}
builder := NewQueryBuilder("a").Eq("id", id)
builder := NewQueryBuilder("a", driver.Adapter).Eq("id", id)
if err := tx.DeleteByQueryBuilder(builder); err != nil {
panic(err)
}
Expand Down
10 changes: 5 additions & 5 deletions config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import (
func TestConfig(t *testing.T) {
cfg, err := NewConfig("testdata/cache.yml")
NoError(t, err)
cache, err := New(cfg.Options()...)
cache, err := New(append([]OptionFunc{DatabaseAdapter(driver.DBType)}, cfg.Options()...)...)
NoError(t, err)
NoError(t, cache.Flush())
conn, err := sql.Open("mysql", "root:@tcp(localhost:3306)/rapidash?parseTime=true")
conn, err := sql.Open(driver.Name, driver.Source)
NoError(t, err)
NoError(t, cache.WarmUp(conn, userLoginType(), false))
t.Run("create new records", func(t *testing.T) {
Expand Down Expand Up @@ -43,7 +43,7 @@ func TestConfig(t *testing.T) {
tx, err := cache.Begin(txConn)
NoError(t, err)
for i := 1001; i <= 1005; i++ {
builder := NewQueryBuilder("user_logins").
builder := NewQueryBuilder("user_logins", driver.Adapter).
Eq("user_id", uint64(i)).
Eq("user_session_id", uint64(i))
var foundUserLogin UserLogin
Expand Down Expand Up @@ -173,7 +173,7 @@ func TestConfig(t *testing.T) {

t.Run("should retrieve each handled data", func(t *testing.T) {
key := fmt.Sprintf("key_%d", time.Now().UnixNano())
var resultFirst int
var resultFirst int
var resultSecond int
expectFirst := 1
expectSecond := 2
Expand Down Expand Up @@ -213,7 +213,7 @@ func TestConfig(t *testing.T) {
t.Run("implicit cache control", func(t *testing.T) {
t.Run("should retrieve each handled data", func(t *testing.T) {
key := fmt.Sprintf("key_%d", time.Now().UnixNano())
var resultFirst int
var resultFirst int
var resultSecond int
expectFirst := 1
expectSecond := 2
Expand Down
124 changes: 124 additions & 0 deletions database/database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package database

import (
"database/sql"

"go.knocknote.io/rapidash/database/mysql"
"go.knocknote.io/rapidash/database/postgres"
)

type DBType int

const (
None DBType = iota
MySQL
Postgres
)

const (
mysqlPlugin = "mysql"
postgresPlugin = "postgres"
)

type Database interface {
database
Placeholder(int) string
Placeholders(int, int) string
}

type database interface {
TableDDL(*sql.DB, string) (string, error)
Quote(string) string
SupportLastInsertID() bool
}

type QueryHelper struct {
count int
adapter *adapter
}

func (qh *QueryHelper) DBType() DBType {
return qh.adapter.DBType
}

func (qh *QueryHelper) Placeholder() string {
qh.count++
return qh.adapter.Placeholder(qh.count)
}

func (qh *QueryHelper) Placeholders(n int) string {
start := qh.count + 1
end := start + n - 1
qh.count += n
return qh.adapter.Placeholders(start, end)
}

func (qh *QueryHelper) Quote(str string) string {
return qh.adapter.Quote(str)
}

func (qh *QueryHelper) SupportLastInsertID() bool {
return qh.adapter.SupportLastInsertID()
}

func (qh *QueryHelper) ClearCount() {
qh.count = 0
}

type Adapter interface {
database
QueryHelper() *QueryHelper
}

type adapter struct {
DBType DBType
Database
}

func (d *adapter) QueryHelper() *QueryHelper {
return &QueryHelper{
count: 0,
adapter: d,
}
}

func NewAdapter() *adapter {
drivers := sql.Drivers()
if len(drivers) == 0 {
return nil
}
dbType := toDBType(drivers[0])
return &adapter{
DBType: dbType,
Database: NewDatabase(dbType),
}
}

func NewAdapterWithDBType(dbType DBType) *adapter {
return &adapter{
DBType: dbType,
Database: NewDatabase(dbType),
}
}

func NewDatabase(dbType DBType) Database {
switch dbType {
case MySQL:
return &mysql.MySQL{}
case Postgres:
return &postgres.Postgres{}
}
return nil
}

func toDBType(pluginName string) DBType {
switch pluginName {
case mysqlPlugin:
return MySQL
case postgresPlugin:
return Postgres
}
return None
}

var _ Adapter = (*adapter)(nil)
46 changes: 46 additions & 0 deletions database/mysql/mysql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package mysql

import (
"database/sql"
"fmt"
"strings"

"golang.org/x/xerrors"
)

type MySQL struct{}

func (ms *MySQL) TableDDL(conn *sql.DB, tableName string) (string, error) {
var (
tbl string
ddl string
)
if err := conn.QueryRow(fmt.Sprintf("SHOW CREATE TABLE `%s`", tableName)).Scan(&tbl, &ddl); err != nil {
return "", xerrors.Errorf("failed to execute 'SHOW CREATE TABLE `%s`': %w", tableName, err)
}
return ddl, nil
}

func (ms *MySQL) Placeholder(_ int) string {
return "?"
}

func (ms *MySQL) Placeholders(start, end int) string {
sb := &strings.Builder{}
sb.Grow((len(ms.Placeholder(0)) + 1) * (end - start + 1))
for i := start; i <= end; i++ {
sb.WriteString(ms.Placeholder(0))
if i < end {
sb.WriteString(",")
}
}
return sb.String()
}

func (ms *MySQL) Quote(s string) string {
return fmt.Sprintf("`%s`", s)
}

func (ms *MySQL) SupportLastInsertID() bool {
return true
}
Loading