add insert statement

This commit is contained in:
2026-07-07 21:20:58 +01:00
parent 275c4a8cdd
commit 87517d247a
7 changed files with 258 additions and 22 deletions
+19 -19
View File
@@ -5,23 +5,23 @@ import (
)
// https://www.sqlite.org/lang_delete.html
type DeleteQuery struct {
type DeleteStatement struct {
table SelectTable
where []Where
args []any
}
func (q *DeleteQuery) Args() []any {
return q.args
func (s *DeleteStatement) Args() []any {
return s.args
}
func (q *DeleteQuery) IsValid() bool {
if !q.table.IsValid() {
func (s *DeleteStatement) IsValid() bool {
if !s.table.IsValid() {
return false
}
for _, w := range q.where {
for _, w := range s.where {
if !w.IsValid() {
return false
}
@@ -30,13 +30,13 @@ func (q *DeleteQuery) IsValid() bool {
return false
}
func (q *DeleteQuery) String() string {
strs := []string{"delete", q.table.String()}
func (s *DeleteStatement) String() string {
strs := []string{"delete", s.table.String()}
if len(q.where) > 0 {
if len(s.where) > 0 {
strs = append(strs, "where")
for _, w := range q.where {
for _, w := range s.where {
strs = append(strs, w.String())
}
}
@@ -44,26 +44,26 @@ func (q *DeleteQuery) String() string {
return strings.Join(strs, " ")
}
func (q *DeleteQuery) Where(def string, args ...any) *DeleteQuery {
q.where = append(q.where, Where(def))
func (s *DeleteStatement) Where(def string, args ...any) *DeleteStatement {
s.where = append(s.where, Where(def))
if len(args) > 0 {
if q.args == nil {
q.args = []any{}
if s.args == nil {
s.args = []any{}
}
q.args = append(q.args, args...)
s.args = append(s.args, args...)
}
return q
return s
}
func Delete(from string) *DeleteQuery {
q := &DeleteQuery{
func Delete(from string) *DeleteStatement {
s := &DeleteStatement{
table: From(from),
where: []Where{},
}
return q
return s
}
+2 -2
View File
@@ -8,7 +8,7 @@ import (
func TestDelete(t *testing.T) {
type TestCase struct {
In *DeleteQuery
In *DeleteStatement
Args []any
String string
}
@@ -24,7 +24,7 @@ func TestDelete(t *testing.T) {
},
{
In: Delete("customer as c"),
String: "select c.id, c.name from customer as c",
String: "delete from customer as c",
},
{
In: Delete("customer").Where("id = ?", 1234),
+120
View File
@@ -0,0 +1,120 @@
package sqimple
import (
"fmt"
"slices"
"strings"
)
// https://www.sqlite.org/lang_delete.html
type InsertStatement struct {
table InsertTable
or string
columns []string
values []Values
selectValues *SelectQuery
}
func (s *InsertStatement) Args() []any {
if s.selectValues != nil {
return s.selectValues.Args()
}
args := []any{}
for _, row := range s.values {
for _, column := range s.columns {
args = append(args, row[column])
}
}
return args
}
func (s *InsertStatement) IsValid() bool {
if !s.table.IsValid() {
return false
}
return false
}
func (s *InsertStatement) Columns(columns ...string) *InsertStatement {
if s.columns == nil {
s.columns = []string{}
}
for _, column := range columns {
if !slices.Contains(s.columns, column) {
s.columns = append(s.columns, columns...)
}
}
return s
}
func (s *InsertStatement) Or(or string) *InsertStatement {
s.or = or
return s
}
func (s *InsertStatement) Select(q *SelectQuery) *InsertStatement {
s.selectValues = q
return s
}
func (s *InsertStatement) String() string {
strs := []string{"insert"}
if s.or != "" {
strs = append(strs, "or", s.or)
}
strs = append(strs, s.table.String())
if len(s.columns) > 0 {
strs = append(strs, fmt.Sprintf("(%s)", strings.Join(s.columns, ", ")))
}
if s.selectValues != nil {
strs = append(strs, s.selectValues.String())
} else {
strs = append(strs, "values")
valueStrs := []string{}
for range s.values {
marks := []string{}
for range s.columns {
marks = append(marks, "?")
}
valueStrs = append(valueStrs, fmt.Sprintf("(%s)", strings.Join(marks, ", ")))
}
strs = append(strs, strings.Join(valueStrs, ", "))
}
return strings.Join(strs, " ")
}
func (s *InsertStatement) Values(values ...Values) *InsertStatement {
if s.values == nil {
s.values = []Values{}
}
for _, row := range values {
s.values = append(s.values, row)
}
return s
}
func Insert(into string) *InsertStatement {
s := &InsertStatement{
table: Into(into),
}
return s
}
+40
View File
@@ -0,0 +1,40 @@
package sqimple
import (
"testing"
"github.com/alecthomas/assert/v2"
)
func TestInsert(t *testing.T) {
type TestCase struct {
In *InsertStatement
Args []any
String string
}
testCases := []TestCase{
{
In: Insert("customer").Columns("name").Values(Values{"name": "Adam"}),
Args: []any{"Adam"},
String: "insert into customer (name) values (?)",
},
{
In: Insert("customer").Columns("name").Values(Values{"name": "Adam"}, Values{"name": "Bernard"}),
Args: []any{"Adam", "Bernard"},
String: "insert into customer (name) values (?), (?)",
},
{
In: Insert("customer").Or("update").Columns("id", "name").Values(Values{"id": 1234, "name": "Adam"}),
Args: []any{1234, "Adam"},
String: "insert or update into customer (id, name) values (?, ?)",
},
}
for _, testCase := range testCases {
t.Run(testCase.String, func(t *testing.T) {
assert.Equal(t, testCase.Args, testCase.In.Args(), "collected arguments incorrectly")
assert.Equal(t, testCase.String, testCase.In.String(), "stringified incorrectly")
})
}
}
+40 -1
View File
@@ -6,7 +6,46 @@ import (
"strings"
)
var selectTableRegexp = regexp.MustCompile("^(from|left join|inner join|right join) ([^ ]+)( (as) ([^ ]+))?( (on) ([^ ]+) (=|!=|>|>=|<|<=) ([^ ]+)( (and) ([^ ]+) (=|!=|>|>=|<|<=) ([^ ]+))*)?$")
var (
insertTableRegexp = regexp.MustCompile("^into ([^ ]+)( (as) ([^ ]+))?$")
selectTableRegexp = regexp.MustCompile("^(from|left join|inner join|right join) ([^ ]+)( (as) ([^ ]+))?( (on) ([^ ]+) (=|!=|>|>=|<|<=) ([^ ]+)( (and) ([^ ]+) (=|!=|>|>=|<|<=) ([^ ]+))*)?$")
)
type InsertTable string
func (t InsertTable) IsValid() bool {
return insertTableRegexp.MatchString(string(t))
}
func (t InsertTable) Parse() (table, as string, ok bool) {
if insertTableRegexp.MatchString(string(t)) {
ok = true
result := insertTableRegexp.FindAllStringSubmatch(string(t), -1)
for _, r := range result {
table = r[1]
if r[3] == "as" {
as = r[4]
}
}
}
return
}
func (t InsertTable) String() string {
return string(t)
}
func Into(def string) InsertTable {
if strings.Index(def, "into ") == 0 {
return InsertTable(def)
}
return InsertTable(fmt.Sprintf("into %s", def))
}
type SelectTable string
+34
View File
@@ -6,6 +6,40 @@ import (
"github.com/alecthomas/assert/v2"
)
func TestInsertTable(t *testing.T) {
type TestCase struct {
In string
IsValid bool
Table string
As string
}
testCases := []TestCase{
{
In: "into customer",
IsValid: true,
Table: "customer",
},
{
In: "into customer as c",
IsValid: true,
Table: "customer",
As: "c",
},
}
for _, testCase := range testCases {
t.Run(testCase.In, func(t *testing.T) {
it := InsertTable(testCase.In)
table, as, ok := it.Parse()
assert.Equal(t, testCase.IsValid, ok, "incorrect test case")
assert.Equal(t, testCase.Table, table, "parsed table incorrectly")
assert.Equal(t, testCase.As, as, "parsed as incorrectly")
})
}
}
func TestSelectTable(t *testing.T) {
type TestCase struct {
In string
+3
View File
@@ -0,0 +1,3 @@
package sqimple
type Values map[string]any