mirror of https://github.com/VinGarcia/ksql.git
Add feature of omiting the "SELECT" part of the query
Now the 3 functions that allow you to write plain SQL queries also work if you omit the `SELECT ...` part of the query. If you do this the code will check and notice that the first token of the query is a "FROM" token and then automatically build the SELECT part of the query based on the tags of the struct. Everything is cached, so the impact on performance should be negligible. The affected functions are: - Query() - QueryOne() - QueryChunks()pull/2/head
parent
d275555df5
commit
edecbf8191
4
go.mod
4
go.mod
|
@ -3,9 +3,9 @@ module github.com/vingarcia/ksql
|
|||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/denisenkom/go-mssqldb v0.10.0 // indirect
|
||||
github.com/denisenkom/go-mssqldb v0.10.0
|
||||
github.com/ditointernet/go-assert v0.0.0-20200120164340-9e13125a7018
|
||||
github.com/go-sql-driver/mysql v1.4.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.4.0
|
||||
github.com/golang/mock v1.5.0
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/lib/pq v1.1.1
|
||||
|
|
69
ksql.go
69
ksql.go
|
@ -6,11 +6,20 @@ import (
|
|||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/vingarcia/ksql/structs"
|
||||
)
|
||||
|
||||
var selectQueryCache = map[string]map[reflect.Type]string{}
|
||||
|
||||
func init() {
|
||||
for dname := range supportedDialects {
|
||||
selectQueryCache[dname] = map[reflect.Type]string{}
|
||||
}
|
||||
}
|
||||
|
||||
// DB represents the ksql client responsible for
|
||||
// interfacing with the "database/sql" package implementing
|
||||
// the KissSQL interface `SQLProvider`.
|
||||
|
@ -124,6 +133,14 @@ func (c DB) Query(
|
|||
slice = slice.Slice(0, 0)
|
||||
}
|
||||
|
||||
if strings.ToUpper(getFirstToken(query)) == "FROM" {
|
||||
selectPrefix, err := buildSelectQuery(c.dialect, structType, selectQueryCache[c.dialect.DriverName()])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query = selectPrefix + query
|
||||
}
|
||||
|
||||
rows, err := c.db.QueryContext(ctx, query, params...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error running query: %s", err.Error())
|
||||
|
@ -189,6 +206,14 @@ func (c DB) QueryOne(
|
|||
return fmt.Errorf("ksql: expected to receive a pointer to struct, but got: %T", record)
|
||||
}
|
||||
|
||||
if strings.ToUpper(getFirstToken(query)) == "FROM" {
|
||||
selectPrefix, err := buildSelectQuery(c.dialect, t, selectQueryCache[c.dialect.DriverName()])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query = selectPrefix + query
|
||||
}
|
||||
|
||||
rows, err := c.db.QueryContext(ctx, query, params...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -243,6 +268,14 @@ func (c DB) QueryChunks(
|
|||
return err
|
||||
}
|
||||
|
||||
if strings.ToUpper(getFirstToken(parser.Query)) == "FROM" {
|
||||
selectPrefix, err := buildSelectQuery(c.dialect, structType, selectQueryCache[c.dialect.DriverName()])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parser.Query = selectPrefix + parser.Query
|
||||
}
|
||||
|
||||
rows, err := c.db.QueryContext(ctx, parser.Query, parser.Params...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -869,3 +902,39 @@ func buildCompositeKeyDeleteQuery(
|
|||
strings.Join(values, ","),
|
||||
), params
|
||||
}
|
||||
|
||||
// We implemented this function instead of using
|
||||
// a regex or strings.Fields because we wanted
|
||||
// to preserve the performance of the package.
|
||||
func getFirstToken(s string) string {
|
||||
s = strings.TrimLeftFunc(s, unicode.IsSpace)
|
||||
|
||||
var token strings.Builder
|
||||
for _, c := range s {
|
||||
if unicode.IsSpace(c) {
|
||||
break
|
||||
}
|
||||
token.WriteRune(c)
|
||||
}
|
||||
return token.String()
|
||||
}
|
||||
|
||||
func buildSelectQuery(
|
||||
dialect dialect,
|
||||
structType reflect.Type,
|
||||
selectQueryCache map[reflect.Type]string,
|
||||
) (string, error) {
|
||||
if selectQuery, found := selectQueryCache[structType]; found {
|
||||
return selectQuery, nil
|
||||
}
|
||||
|
||||
info := structs.GetTagInfo(structType)
|
||||
var fields []string
|
||||
for _, field := range info.Fields() {
|
||||
fields = append(fields, dialect.Escape(field.Name))
|
||||
}
|
||||
|
||||
query := "SELECT " + strings.Join(fields, ", ") + " "
|
||||
selectQueryCache[structType] = query
|
||||
return query, nil
|
||||
}
|
||||
|
|
1172
ksql_test.go
1172
ksql_test.go
File diff suppressed because it is too large
Load Diff
|
@ -42,6 +42,10 @@ func (s structInfo) Add(field fieldInfo) {
|
|||
s.byName[field.Name] = &field
|
||||
}
|
||||
|
||||
func (s structInfo) Fields() map[int]*fieldInfo {
|
||||
return s.byIndex
|
||||
}
|
||||
|
||||
// This cache is kept as a pkg variable
|
||||
// because the total number of types on a program
|
||||
// should be finite. So keeping a single cache here
|
||||
|
|
Loading…
Reference in New Issue