[ref] name things what they are

Signed-off-by: Anton Nesterov <anton@demiurg.io>
This commit is contained in:
Anton Nesterov 2024-08-11 21:49:19 +02:00
parent 25f3e18f4a
commit 43dd4e9234
No known key found for this signature in database
GPG key ID: 59121E8AE2851FB5
33 changed files with 127 additions and 64 deletions

View file

@ -0,0 +1,53 @@
package tests
import (
"fmt"
"testing"
_ "github.com/mattn/go-sqlite3"
"l12.xyz/dal/adapter"
"l12.xyz/dal/builder"
)
func TestBuilderBasic(t *testing.T) {
a := adapter.DBAdapter{Type: "sqlite3"}
db, err := a.Open("file::memory:?cache=shared")
if err != nil {
t.Fatalf("failed to open db: %v", err)
}
defer db.Close()
_, err = db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY, name BLOB)")
if err != nil {
t.Fatalf("failed to create table: %v", err)
}
insert, values := builder.New(adapter.SQLite{}).In("test t").Insert([]builder.Map{
{"name": "a"},
{"name": 'b'},
}).Sql()
fmt.Println(insert, values)
_, err = db.Exec(insert, values...)
if err != nil {
t.Fatalf("failed to insert data: %v", err)
}
expr, _ := builder.New(adapter.SQLite{}).In("test t").Find(builder.Find{"name": builder.Is{"$in": []interface{}{"a", 'b'}}}).Sql()
fmt.Println(expr)
rows, err := a.Query(adapter.Query{
Db: "file::memory:?cache=shared",
Expression: expr,
})
if err != nil {
t.Fatalf("failed to query data: %v", err)
}
defer rows.Close()
for rows.Next() {
var id int
var name string
err = rows.Scan(&id, &name)
if err != nil {
t.Fatalf("failed to scan row: %v", err)
}
fmt.Printf("id: %d, name: %s\n", id, name)
}
}

View file

@ -2,6 +2,7 @@ module l12.xyz/dal/tests
go 1.22.6
require l12.xyz/dal/builder v0.0.0
replace l12.xyz/dal/builder v0.0.0 => ../builder
replace l12.xyz/dal/utils v0.0.0 => ../utils
@ -9,9 +10,11 @@ replace l12.xyz/dal/utils v0.0.0 => ../utils
require l12.xyz/dal/adapter v0.0.0
replace l12.xyz/dal/adapter v0.0.0 => ../adapter
replace l12.xyz/dal/filters v0.0.0 => ../filters
require (
github.com/mattn/go-sqlite3 v1.14.22
github.com/pkg/errors v0.9.1 // indirect
l12.xyz/dal/utils v0.0.0 // indirect
l12.xyz/dal/filters v0.0.0 // indirect
)

View file

@ -7,13 +7,13 @@ import (
utils "l12.xyz/dal/utils"
)
type SQLiteContext struct {
type SQLite struct {
TableName string
TableAlias string
FieldName string
}
func (c SQLiteContext) New(opts CtxOpts) Context {
func (c SQLite) New(opts DialectOpts) Dialect {
tn := opts["TableName"]
if tn == "" {
tn = c.TableName
@ -26,18 +26,18 @@ func (c SQLiteContext) New(opts CtxOpts) Context {
if fn == "" {
fn = c.FieldName
}
return SQLiteContext{
return SQLite{
TableName: tn,
TableAlias: ta,
FieldName: fn,
}
}
func (c SQLiteContext) GetTableName() string {
func (c SQLite) GetTableName() string {
return c.TableName
}
func (c SQLiteContext) GetFieldName() string {
func (c SQLite) GetFieldName() string {
if strings.Contains(c.FieldName, ".") {
return c.FieldName
}
@ -47,7 +47,7 @@ func (c SQLiteContext) GetFieldName() string {
return c.FieldName
}
func (c SQLiteContext) GetColumnName(key string) string {
func (c SQLite) GetColumnName(key string) string {
if strings.Contains(key, ".") {
return key
}
@ -57,7 +57,7 @@ func (c SQLiteContext) GetColumnName(key string) string {
return key
}
func (c SQLiteContext) NormalizeValue(value interface{}) interface{} {
func (c SQLite) NormalizeValue(value interface{}) interface{} {
str, ok := value.(string)
if utils.IsSQLFunction(str) {
return str

View file

@ -6,10 +6,10 @@ type Query struct {
Data []interface{} `json:"data"`
}
type CtxOpts map[string]string
type DialectOpts map[string]string
type Context interface {
New(opts CtxOpts) Context
type Dialect interface {
New(opts DialectOpts) Dialect
GetTableName() string
GetFieldName() string
GetColumnName(key string) string

View file

@ -9,7 +9,7 @@ type Builder struct {
TableName string
TableAlias string
Parts SQLParts
Ctx Context
Dialect Dialect
}
type SQLParts struct {
@ -28,20 +28,20 @@ type SQLParts struct {
Update UpdateData
}
func New(ctx Context) *Builder {
func New(dialect Dialect) *Builder {
return &Builder{
Parts: SQLParts{
Operation: "SELECT",
From: "FROM",
},
Ctx: ctx,
Dialect: dialect,
}
}
func (b *Builder) In(table string) *Builder {
b.TableName, b.TableAlias = getTableAlias(table)
b.Parts.FromExp = table
b.Ctx = b.Ctx.New(CtxOpts{
b.Dialect = b.Dialect.New(DialectOpts{
"TableName": b.TableName,
"TableAlias": b.TableAlias,
})
@ -50,7 +50,7 @@ func (b *Builder) In(table string) *Builder {
func (b *Builder) Find(query Find) *Builder {
b.Parts.FiterExp = covertFind(
b.Ctx,
b.Dialect,
query,
)
if b.Parts.Operation == "" {
@ -79,18 +79,18 @@ func (b *Builder) Fields(fields ...Map) *Builder {
}
func (b *Builder) Join(joins ...interface{}) *Builder {
b.Parts.JoinExps = convertJoin(b.Ctx, joins...)
b.Parts.JoinExps = convertJoin(b.Dialect, joins...)
return b
}
func (b *Builder) Group(keys ...string) *Builder {
b.Parts.HavingExp = "HAVING"
b.Parts.GroupExp = convertGroup(b.Ctx, keys)
b.Parts.GroupExp = convertGroup(b.Dialect, keys)
return b
}
func (b *Builder) Sort(sort Map) *Builder {
b.Parts.OrderExp, _ = convertSort(b.Ctx, sort)
b.Parts.OrderExp, _ = convertSort(b.Dialect, sort)
return b
}
@ -110,7 +110,7 @@ func (b *Builder) Delete() *Builder {
}
func (b *Builder) Insert(inserts []Map) *Builder {
insertData, _ := convertInsert(b.Ctx, inserts)
insertData, _ := convertInsert(b.Dialect, inserts)
b.Parts = SQLParts{
Operation: "INSERT INTO",
Insert: insertData,
@ -119,7 +119,7 @@ func (b *Builder) Insert(inserts []Map) *Builder {
}
func (b *Builder) Set(updates Map) *Builder {
updateData := convertUpdate(b.Ctx, updates)
updateData := convertUpdate(b.Dialect, updates)
b.Parts = SQLParts{
Operation: "UPDATE",
Update: updateData,
@ -133,7 +133,7 @@ func (b *Builder) Update(updates Map) *Builder {
func (b *Builder) OnConflict(fields ...string) *Builder {
if b.Parts.Operation == "UPDATE" {
b.Parts.Update.Upsert = convertConflict(b.Ctx, fields...)
b.Parts.Update.Upsert = convertConflict(b.Dialect, fields...)
b.Parts.Update.UpsertExp = "DO NOTHING"
} else {
panic("OnConflict is only available for UPDATE operation")

View file

@ -7,7 +7,7 @@ import (
utils "l12.xyz/dal/utils"
)
func convertConflict(ctx Context, fields ...string) string {
func convertConflict(ctx Dialect, fields ...string) string {
keys := utils.Map(fields, ctx.GetColumnName)
return fmt.Sprintf("ON CONFLICT (%s)", strings.Join(keys, ","))
}

View file

@ -7,11 +7,11 @@ import (
filters "l12.xyz/dal/filters"
)
func covertFind(ctx Context, find Find) string {
func covertFind(ctx Dialect, find Find) string {
return covert_find(ctx, find, "")
}
func covert_find(ctx Context, find Find, join string) string {
func covert_find(ctx Dialect, find Find, join string) string {
if join == "" {
join = " AND "
}
@ -29,7 +29,7 @@ func covert_find(ctx Context, find Find, join string) string {
expressions = append(expressions, fmt.Sprintf("(%s)", v))
continue
}
context := ctx.New(CtxOpts{
context := ctx.New(DialectOpts{
"FieldName": key,
})
values, _ := filters.Convert(context, value)

View file

@ -7,7 +7,7 @@ import (
"l12.xyz/dal/utils"
)
func convertGroup(ctx Context, keys []string) string {
func convertGroup(ctx Dialect, keys []string) string {
set := utils.Map(keys, ctx.GetColumnName)
return fmt.Sprintf("GROUP BY %s", strings.Join(set, ", "))
}

View file

@ -10,27 +10,33 @@ type InsertData struct {
Values []interface{}
}
func convertInsert(ctx Context, inserts []Map) (InsertData, error) {
func convertInsert(ctx Dialect, inserts []Map) (InsertData, error) {
keys := aggregateSortedKeys(inserts)
placeholder := make([]string, 0)
posEnum := make([]string, 0)
for range keys {
placeholder = append(placeholder, "?")
posEnum = append(posEnum, "?")
}
placeholder := strings.Join(posEnum, ",")
positional := []string{}
values := make([]interface{}, 0)
for _, insert := range inserts {
vals := make([]interface{}, 0)
for _, key := range keys {
vals = append(vals, insert[key])
}
values = append(values, vals)
values = append(values, vals...)
positional = append(
positional,
fmt.Sprintf("(%s)", placeholder),
)
}
sfmt := fmt.Sprintf(
"INSERT INTO %s (%s) VALUES (%s)",
"INSERT INTO %s (%s) VALUES %s",
ctx.GetTableName(),
strings.Join(keys, ","),
strings.Join(placeholder, ","),
strings.Join(positional, ","),
)
return InsertData{
Statement: sfmt,

View file

@ -1,6 +1,7 @@
package builder
import (
"fmt"
"testing"
)
@ -15,12 +16,12 @@ func TestConvertInsert(t *testing.T) {
}
result, _ := convertInsert(ctx, insert)
if result.Statement != `INSERT INTO test (a,b,c) VALUES (?,?,?)` {
t.Errorf(`Expected "INSERT INTO test (a,b,c) VALUES (?,?,?)", got %s`, result.Statement)
if result.Statement != `INSERT INTO test (a,b,c) VALUES (?,?,?),(?,?,?)` {
t.Errorf(`Expected "INSERT INTO test (a,b,c) VALUES (?,?,?),(?,?,?)", got %s`, result.Statement)
}
// for _, r := range result.Values {
// fmt.Println(r)
// }
for _, r := range result.Values {
fmt.Println(r)
}
}

View file

@ -11,7 +11,7 @@ type Join struct {
As string `json:"$as"`
}
func (j Join) Convert(ctx Context) string {
func (j Join) Convert(ctx Dialect) string {
if j.For == "" {
return ""
}
@ -23,7 +23,7 @@ func (j Join) Convert(ctx Context) string {
return as + fmt.Sprintf("JOIN %s ON %s", j.For, filter)
}
func convertJoin(ctx Context, joins ...interface{}) []string {
func convertJoin(ctx Dialect, joins ...interface{}) []string {
var result []string
for _, join := range joins {
jstr, ok := join.(string)

View file

@ -6,7 +6,7 @@ import (
adapter "l12.xyz/dal/adapter"
)
type SQLiteContext = adapter.SQLiteContext
type SQLiteContext = adapter.SQLite
func TestJoin(t *testing.T) {
j := Join{

View file

@ -5,7 +5,7 @@ import (
"strings"
)
func convertSort(ctx Context, sort Map) (string, error) {
func convertSort(ctx Dialect, sort Map) (string, error) {
if sort == nil {
return "", nil
}

View file

@ -12,7 +12,7 @@ type UpdateData struct {
Values []interface{}
}
func convertUpdate(ctx Context, updates Map) UpdateData {
func convertUpdate(ctx Dialect, updates Map) UpdateData {
keys := aggregateSortedKeys([]Map{updates})
set := make([]string, 0)
values := make([]interface{}, 0)

View file

@ -11,5 +11,5 @@ type Find = filters.Find
type Query = filters.Find
type Filter = filters.Filter
type Is = filters.Filter
type Context = adapter.Context
type CtxOpts = adapter.CtxOpts
type Dialect = adapter.Dialect
type DialectOpts = adapter.DialectOpts

View file

@ -9,7 +9,7 @@ type And struct {
And []string `json:"$and"`
}
func (f And) ToSQLPart(ctx Context) string {
func (f And) ToSQLPart(ctx Dialect) string {
if f.And == nil {
return ""
}

View file

@ -14,7 +14,7 @@ func (f Between) FromJSON(data interface{}) IFilter {
return FromJson[Between](data)
}
func (f Between) ToSQLPart(ctx Context) string {
func (f Between) ToSQLPart(ctx Dialect) string {
if f.Between == nil {
return ""
}

View file

@ -12,7 +12,7 @@ func (f Eq) FromJSON(data interface{}) IFilter {
return FromJson[Eq](data)
}
func (f Eq) ToSQLPart(ctx Context) string {
func (f Eq) ToSQLPart(ctx Dialect) string {
if f.Eq == nil {
return ""
}

View file

@ -10,7 +10,7 @@ func (f Glob) FromJSON(data interface{}) IFilter {
return FromJson[Glob](data)
}
func (f Glob) ToSQLPart(ctx Context) string {
func (f Glob) ToSQLPart(ctx Dialect) string {
if f.Glob == nil {
return ""
}

View file

@ -12,7 +12,7 @@ func (f Gt) FromJSON(data interface{}) IFilter {
return FromJson[Gt](data)
}
func (f Gt) ToSQLPart(ctx Context) string {
func (f Gt) ToSQLPart(ctx Dialect) string {
if f.Gt == nil {
return ""
}

View file

@ -12,7 +12,7 @@ func (f Gte) FromJSON(data interface{}) IFilter {
return FromJson[Gte](data)
}
func (f Gte) ToSQLPart(ctx Context) string {
func (f Gte) ToSQLPart(ctx Dialect) string {
if f.Gte == nil {
return ""
}

View file

@ -15,7 +15,7 @@ func (f In) FromJSON(data interface{}) IFilter {
return FromJson[In](data)
}
func (f In) ToSQLPart(ctx Context) string {
func (f In) ToSQLPart(ctx Dialect) string {
if f.In == nil {
return ""
}

View file

@ -10,7 +10,7 @@ func (f Like) FromJSON(data interface{}) IFilter {
return FromJson[Like](data)
}
func (f Like) ToSQLPart(ctx Context) string {
func (f Like) ToSQLPart(ctx Dialect) string {
if f.Like == nil {
return ""
}

View file

@ -12,7 +12,7 @@ func (f Lt) FromJSON(data interface{}) IFilter {
return FromJson[Lt](data)
}
func (f Lt) ToSQLPart(ctx Context) string {
func (f Lt) ToSQLPart(ctx Dialect) string {
if f.Lt == nil {
return ""
}

View file

@ -12,7 +12,7 @@ func (f Lte) FromJSON(data interface{}) IFilter {
return FromJson[Lte](data)
}
func (f Lte) ToSQLPart(ctx Context) string {
func (f Lte) ToSQLPart(ctx Dialect) string {
if f.Lte == nil {
return ""
}

View file

@ -10,7 +10,7 @@ func (f Ne) FromJSON(data interface{}) IFilter {
return FromJson[Ne](data)
}
func (f Ne) ToSQLPart(ctx Context) string {
func (f Ne) ToSQLPart(ctx Dialect) string {
if f.Ne == nil {
return ""
}

View file

@ -14,7 +14,7 @@ func (f NotBetween) FromJSON(data interface{}) IFilter {
return FromJson[NotBetween](data)
}
func (f NotBetween) ToSQLPart(ctx Context) string {
func (f NotBetween) ToSQLPart(ctx Dialect) string {
if f.NotBetween == nil {
return ""
}

View file

@ -15,7 +15,7 @@ func (f NotIn) FromJSON(data interface{}) IFilter {
return FromJson[NotIn](data)
}
func (f NotIn) ToSQLPart(ctx Context) string {
func (f NotIn) ToSQLPart(ctx Dialect) string {
if f.NotIn == nil {
return ""
}

View file

@ -10,7 +10,7 @@ func (f NotLike) FromJSON(data interface{}) IFilter {
return FromJson[NotLike](data)
}
func (f NotLike) ToSQLPart(ctx Context) string {
func (f NotLike) ToSQLPart(ctx Dialect) string {
if f.NotLike == nil {
return ""
}

View file

@ -9,7 +9,7 @@ type Or struct {
Or []string `json:"$or"`
}
func (f Or) ToSQLPart(ctx Context) string {
func (f Or) ToSQLPart(ctx Dialect) string {
if f.Or == nil {
return ""
}

View file

@ -22,7 +22,7 @@ var FilterRegistry = map[string]IFilter{
"NotLike": &NotLike{},
}
func Convert(ctx Context, data interface{}) (string, error) {
func Convert(ctx Dialect, data interface{}) (string, error) {
for _, impl := range FilterRegistry {
filter := impl.FromJSON(data)
if reflect.DeepEqual(impl, filter) {

View file

@ -2,11 +2,11 @@ package filters
import "l12.xyz/dal/adapter"
type CtxOpts = adapter.CtxOpts
type Context = adapter.Context
type DialectOpts = adapter.DialectOpts
type Dialect = adapter.Dialect
type IFilter interface {
ToSQLPart(ctx Context) string
ToSQLPart(ctx Dialect) string
FromJSON(interface{}) IFilter
}

View file

@ -6,7 +6,7 @@ import (
adapter "l12.xyz/dal/adapter"
)
type SQLiteContext = adapter.SQLiteContext
type SQLiteContext = adapter.SQLite
func TestEq(t *testing.T) {
ctx := SQLiteContext{