Skip to content

Commit

Permalink
v0.1.0 initial unstable version
Browse files Browse the repository at this point in the history
  • Loading branch information
jalcalav committed Apr 11, 2021
1 parent 7a53528 commit ad478c3
Show file tree
Hide file tree
Showing 25 changed files with 1,280 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@

# Created by https://www.toptal.com/developers/gitignore/api/go,vscode
# Edit at https://www.toptal.com/developers/gitignore?templates=go,vscode

### Go ###
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# Dependency directories (remove the comment below to include it)
# vendor/

### Go Patch ###
/vendor/
/Godeps/

### vscode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

# End of https://www.toptal.com/developers/gitignore/api/go,vscode

5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/itagile/go-sqlb

go 1.16

require github.com/stretchr/testify v1.7.0
11 changes: 11 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
60 changes: 60 additions & 0 deletions sqlb/delete_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package sqlb

import (
"strings"
)

// DeleteBuilder generates simple UPDATE from values
type DeleteBuilder interface {
Setter
SQLBuilder
}

type deleteBuilderData struct {
engine Engine
table string
where *predicateData
}

// NewDeleteBuilder constructs an DeleteBuilder with the provided ParameterPlaceholder
func NewDeleteBuilder(engine Engine, table string) *deleteBuilderData {
return &deleteBuilderData{
engine: engine,
table: table,
}
}

// Where for simple where condition initialization with AND operator
func (d *deleteBuilderData) Where(conditions ...Condition) *predicateData {
d.where = NewAnd(conditions...)
return d.where
}

// Where for simple where condition initialization with OR operator
func (d *deleteBuilderData) WhereOr(conditions ...Condition) *predicateData {
d.where = NewOr(conditions...)
return d.where
}

// Build the UPDATE command
func (d *deleteBuilderData) Build() (query string, args []interface{}) {
if d.table == "" {
return "", nil
}
var sb strings.Builder
sb.WriteString("DELETE FROM ")
sb.WriteString(d.table)
args = d.addWhere(&sb, args)
return sb.String(), args
}

// addWhere appends WHERE clause
func (d *deleteBuilderData) addWhere(sb *strings.Builder, args []interface{}) []interface{} {
queryWhere, argsWhere := d.where.Build(d.engine)
if len(queryWhere) > 0 {
sb.WriteString("\nWHERE ")
sb.WriteString(queryWhere)
args = append(args, argsWhere...)
}
return args
}
44 changes: 44 additions & 0 deletions sqlb/delete_builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package sqlb_test

import (
"testing"

"github.com/itagile/go-sqlb/sqlb"
"github.com/stretchr/testify/require"
)

func TestNewDeleteBuilderWithoutWhere(t *testing.T) {
expected := "DELETE FROM schema.myTable"
del := sqlb.NewDeleteBuilder(sqlb.DefaultEngine(), "schema.myTable")
query, args := del.Build()
require.Equal(t, expected, query)
require.Empty(t, args)
}

func TestNewDeleteBuilderWithWhere(t *testing.T) {
expected := `DELETE FROM schema.myTable
WHERE ID = ?`
del := sqlb.NewDeleteBuilder(sqlb.DefaultEngine(), "schema.myTable")
del.Where(sqlb.Expr("ID").Eq(1))
query, args := del.Build()
require.Equal(t, expected, query)
require.Equal(t, []interface{}{1}, args)
}

func TestNewDeleteBuilderWithWhereOr(t *testing.T) {
expected := `DELETE FROM schema.myTable
WHERE Col3 LIKE ? OR Col4 = ?`
del := sqlb.NewDeleteBuilder(sqlb.DefaultEngine(), "schema.myTable")
del.WhereOr(sqlb.Expr("Col3").Like("like1"), sqlb.Expr("Col4").Eq(2))
query, args := del.Build()
require.Equal(t, expected, query)
require.Equal(t, []interface{}{"like1", 2}, args)
}

func TestEmptyDeleteBuilderWhenNoTableName(t *testing.T) {
expected := ""
ins := sqlb.NewDeleteBuilder(sqlb.DefaultEngine(), "")
query, args := ins.Build()
require.Equal(t, expected, query)
require.Nil(t, args)
}
49 changes: 49 additions & 0 deletions sqlb/engine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package sqlb

import (
"fmt"
"strings"
)

type Engine interface {
ParameterPlaceholder
ILike(expression string, like string) (query string, arg string)
}

type defaultEngine struct {
ParameterPlaceholder
}

func DefaultEngine() *defaultEngine {
return &defaultEngine{
ParameterPlaceholder: QuestionPlaceholderData,
}
}

func (e *defaultEngine) ILike(expression string, like string) (query string, arg string) {
if expression == "" {
return "", ""
}
return fmt.Sprintf("UPPER(%s) LIKE %s", expression, e.Placeholder()), strings.ToUpper(like)
}

type postgreSQLEngine defaultEngine

func PostgreSQLEngine() *postgreSQLEngine {
return &postgreSQLEngine{
ParameterPlaceholder: NewDollarPlaceholder(),
}
}

func (e *postgreSQLEngine) ILike(expression string, like string) (query string, arg string) {
if expression == "" {
return "", ""
}
return fmt.Sprintf("%s ILIKE %s", expression, e.Placeholder()), like
}

func ORACLEEngine() *defaultEngine {
return &defaultEngine{
ParameterPlaceholder: NewColonPlaceholder(),
}
}
49 changes: 49 additions & 0 deletions sqlb/engine_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package sqlb_test

import (
"strings"
"testing"

"github.com/itagile/go-sqlb/sqlb"
"github.com/stretchr/testify/require"
)

func TestDefaultEngineILike(t *testing.T) {
expected := "UPPER(Col1) LIKE ?"
engine := sqlb.DefaultEngine()
query, arg := engine.ILike("Col1", likeTest)
require.Equal(t, expected, query)
require.Equal(t, strings.ToUpper(likeTest), arg)
}

func TestDefaultEngineILikeEmptyExpression(t *testing.T) {
expected := ""
engine := sqlb.DefaultEngine()
query, arg := engine.ILike("", likeTest)
require.Equal(t, expected, query)
require.Equal(t, expected, arg)
}

func TestPostgreSQLEngineILike(t *testing.T) {
expected := "Col1 ILIKE $1"
engine := sqlb.PostgreSQLEngine()
query, arg := engine.ILike("Col1", likeTest)
require.Equal(t, expected, query)
require.Equal(t, likeTest, arg)
}

func TestPostgreSQLEngineILikeEmptyExpression(t *testing.T) {
expected := ""
engine := sqlb.PostgreSQLEngine()
query, arg := engine.ILike("", likeTest)
require.Equal(t, expected, query)
require.Equal(t, expected, arg)
}

func TestORACLEEngineILike(t *testing.T) {
expected := "UPPER(Col1) LIKE :v1"
engine := sqlb.ORACLEEngine()
query, arg := engine.ILike("Col1", likeTest)
require.Equal(t, expected, query)
require.Equal(t, strings.ToUpper(likeTest), arg)
}
39 changes: 39 additions & 0 deletions sqlb/expression.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package sqlb

import "strings"

type expressionData []string

func Expr(expressions ...string) expressionData {
return expressions
}

type ExpressionBuilder func(engine Engine, expression string, sb *strings.Builder) []interface{}

func (e expressionData) Build(engine Engine) (query string, args []interface{}) {
return BuildExpression(e, engine, func(engine Engine, expression string, sb *strings.Builder) []interface{} {
sb.WriteString(expression)
return nil
})
}

func BuildExpression(e expressionData, engine Engine, handler ExpressionBuilder) (query string, args []interface{}) {
var sb strings.Builder
args = make([]interface{}, 0, len(e))
enclose := false
for _, expression := range e {
if expression != "" {
if sb.Len() > 0 {
sb.WriteString(" OR ")
enclose = true
}
argsHandler := handler(engine, expression, &sb)
args = append(args, argsHandler...)
}
}
query = sb.String()
if enclose {
query = "(" + query + ")"
}
return query, args
}
31 changes: 31 additions & 0 deletions sqlb/expression_eq.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package sqlb

import "strings"

type expresisonEqData struct {
expressionData
value interface{}
}

func (e expresisonEqData) Build(engine Engine) (query string, args []interface{}) {
return BuildExpression(e.expressionData, engine, e.buildHandler)
}

func (e expresisonEqData) buildHandler(engine Engine, expression string, sb *strings.Builder) (args []interface{}) {
sb.WriteString(expression)
if e.value == nil {
sb.WriteString(" IS NULL")
} else {
sb.WriteString(" = ")
sb.WriteString(engine.Placeholder())
args = append(args, e.value)
}
return args
}

func (e expressionData) Eq(value interface{}) Condition {
return &expresisonEqData{
expressionData: e,
value: value,
}
}
40 changes: 40 additions & 0 deletions sqlb/expression_eq_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package sqlb_test

import (
"testing"

"github.com/itagile/go-sqlb/sqlb"
"github.com/stretchr/testify/require"
)

func TestSingleEq(t *testing.T) {
expected := "Col1 = ?"
expr := sqlb.Expr("Col1").Eq(1)
query, args := expr.Build(sqlb.DefaultEngine())
require.Equal(t, expected, query)
require.Equal(t, []interface{}{1}, args)
}

func TestMultipleEq(t *testing.T) {
expected := "(Col1 = ? OR Col2 = ?)"
expr := sqlb.Expr("Col1", "Col2").Eq(1)
query, args := expr.Build(sqlb.DefaultEngine())
require.Equal(t, expected, query)
require.Equal(t, []interface{}{1, 1}, args)
}

func TestSingleEqIsNull(t *testing.T) {
expected := "Col1 IS NULL"
expr := sqlb.Expr("Col1").Eq(nil)
query, args := expr.Build(sqlb.DefaultEngine())
require.Equal(t, expected, query)
require.Empty(t, args)
}

func TestMultipleEqIsNull(t *testing.T) {
expected := "(Col1 IS NULL OR Col2 IS NULL)"
expr := sqlb.Expr("Col1", "Col2").Eq(nil)
query, args := expr.Build(sqlb.DefaultEngine())
require.Equal(t, expected, query)
require.Empty(t, args)
}
26 changes: 26 additions & 0 deletions sqlb/expression_ilike.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package sqlb

import "strings"

type expresisonILikeData struct {
expressionData
like string
}

func (e expresisonILikeData) Build(engine Engine) (query string, args []interface{}) {
return BuildExpression(e.expressionData, engine, e.buildHandler)
}

func (e expresisonILikeData) buildHandler(engine Engine, expression string, sb *strings.Builder) (args []interface{}) {
query, arg := engine.ILike(expression, e.like)
sb.WriteString(query)
args = append(args, arg)
return args
}

func (e expressionData) ILike(like string) Condition {
return &expresisonILikeData{
expressionData: e,
like: like,
}
}
Loading

0 comments on commit ad478c3

Please sign in to comment.