syncthing/vendor/github.com/cznic/ql/stmt.go

1267 lines
28 KiB
Go
Raw Normal View History

2016-05-31 20:35:35 +00:00
// Copyright (c) 2014 ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ql
import (
"bytes"
"fmt"
"strings"
"github.com/cznic/strutil"
)
// NOTE: all stmt implementations must be safe for concurrent use by multiple
// goroutines. If the exec method requires any execution domain local data,
// they must be held out of the implementing instance.
var (
_ stmt = (*alterTableAddStmt)(nil)
_ stmt = (*alterTableDropColumnStmt)(nil)
_ stmt = (*createIndexStmt)(nil)
_ stmt = (*createTableStmt)(nil)
_ stmt = (*deleteStmt)(nil) //TODO optimizer plan
_ stmt = (*dropIndexStmt)(nil)
_ stmt = (*dropTableStmt)(nil)
_ stmt = (*explainStmt)(nil)
_ stmt = (*insertIntoStmt)(nil)
_ stmt = (*selectStmt)(nil)
_ stmt = (*truncateTableStmt)(nil)
_ stmt = (*updateStmt)(nil) //TODO optimizer plan
_ stmt = beginTransactionStmt{}
_ stmt = commitStmt{}
_ stmt = rollbackStmt{}
)
var (
createColumn2 = mustCompile(`
create table if not exists __Column2 (
TableName string,
Name string,
NotNull bool,
ConstraintExpr string,
DefaultExpr string,
);
create index if not exists __Column2TableName on __Column2(TableName);
`)
insertColumn2 = mustCompile(`insert into __Column2 values($1, $2, $3, $4, $5)`)
selectColumn2 = MustCompile(`
select Name, NotNull, ConstraintExpr, DefaultExpr
from __Column2
where TableName == $1
`)
deleteColumn2 = mustCompile(`
delete from __Column2
where TableName == $1 && Name == $2
`)
createIndex2 = mustCompile(`
// Index register 2.
create table if not exists __Index2(
TableName string,
IndexName string,
IsUnique bool,
IsSimple bool, // Just a column name or id().
Root int64, // BTree handle
);
// Expressions for given index. Compared in order of id(__Index2_Expr).
create table if not exists __Index2_Expr(
Index2_ID int,
Expr string,
);
create index if not exists __xIndex2_TableName on __Index2(TableName);
create unique index if not exists __xIndex2_IndexName on __Index2(IndexName);
create index if not exists __xIndex2_ID on __Index2(id());
create index if not exists __xIndex2_Expr_Index2_ID on __Index2_Expr(Index2_ID);
`)
insertIndex2 = mustCompile("insert into __Index2 values($1, $2, $3, $4, $5)")
insertIndex2Expr = mustCompile("insert into __Index2_Expr values($1, $2)")
deleteIndex2ByIndexName = mustCompile(`
delete from __Index2_Expr
where Index2_ID in (
select id() from __Index2 where IndexName == $1;
);
delete from __Index2
where IndexName == $1;
`)
deleteIndex2ByTableName = mustCompile(`
delete from __Index2_Expr
where Index2_ID in (
select id() from __Index2 where TableName == $1;
);
delete from __Index2
where TableName == $1;
`)
)
type stmt interface {
// never invoked for
// - beginTransactionStmt
// - commitStmt
// - rollbackStmt
exec(ctx *execCtx) (Recordset, error)
explain(ctx *execCtx, w strutil.Formatter)
// return value ignored for
// - beginTransactionStmt
// - commitStmt
// - rollbackStmt
isUpdating() bool
String() string
}
type execCtx struct { //LATER +shared temp
db *DB
arg []interface{}
}
type explainStmt struct {
s stmt
}
func (s *explainStmt) explain(ctx *execCtx, w strutil.Formatter) {
for {
x, ok := s.s.(*explainStmt)
if !ok {
s.s.explain(ctx, w)
return
}
s = x
}
}
func (s *explainStmt) String() string {
return "EXPLAIN " + s.s.String()
}
func (*explainStmt) isUpdating() bool { return false }
func (s *explainStmt) exec(ctx *execCtx) (_ Recordset, err error) {
return recordset{ctx, &explainDefaultPlan{s.s}, ctx.db.cc}, nil
}
type updateStmt struct {
tableName string
list []assignment
where expression
}
func (s *updateStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *updateStmt) String() string {
u := fmt.Sprintf("UPDATE %s", s.tableName)
a := make([]string, len(s.list))
for i, v := range s.list {
a[i] = v.String()
}
w := ""
if s.where != nil {
w = fmt.Sprintf(" WHERE %s", s.where)
}
return fmt.Sprintf("%s %s%s;", u, strings.Join(a, ", "), w)
}
func (s *updateStmt) exec(ctx *execCtx) (_ Recordset, err error) {
t, ok := ctx.db.root.tables[s.tableName]
if !ok {
return nil, fmt.Errorf("UPDATE: table %s does not exist", s.tableName)
}
tcols := make([]*col, len(s.list))
for i, asgn := range s.list {
col := findCol(t.cols, asgn.colName)
if col == nil {
return nil, fmt.Errorf("UPDATE: unknown column %s", asgn.colName)
}
tcols[i] = col
}
m := map[interface{}]interface{}{}
var nh int64
expr := s.where
blobCols := t.blobCols()
cc := ctx.db.cc
var old []interface{}
var touched []bool
if t.hasIndices() {
old = make([]interface{}, len(t.cols0))
touched = make([]bool, len(t.cols0))
}
for h := t.head; h != 0; h = nh {
// Read can return lazily expanded chunks
data, err := t.store.Read(nil, h, t.cols...)
if err != nil {
return nil, err
}
nh = data[0].(int64)
for _, col := range t.cols {
m[col.name] = data[2+col.index]
}
id := data[1].(int64)
m["$id"] = id
if expr != nil {
val, err := s.where.eval(ctx, m)
if err != nil {
return nil, err
}
if val == nil {
continue
}
x, ok := val.(bool)
if !ok {
return nil, fmt.Errorf("invalid WHERE expression %s (value of type %T)", val, val)
}
if !x {
continue
}
}
// hit
for _, ix := range t.indices2 {
vlist, err := ix.eval(ctx, t.cols, id, data[2:])
if err != nil {
return nil, err
}
if err := ix.x.Delete(vlist, h); err != nil {
return nil, err
}
}
for i, asgn := range s.list {
val, err := asgn.expr.eval(ctx, m)
if err != nil {
return nil, err
}
colIndex := tcols[i].index
if t.hasIndices() {
old[colIndex] = data[2+colIndex]
touched[colIndex] = true
}
data[2+colIndex] = val
}
if err = typeCheck(data[2:], t.cols); err != nil {
return nil, err
}
if err = t.checkConstraintsAndDefaults(ctx, data[2:], m); err != nil {
return nil, err
}
for i, v := range t.indices {
if i == 0 { // id() N/A
continue
}
if v == nil || !touched[i-1] {
continue
}
if err = v.x.Delete([]interface{}{old[i-1]}, h); err != nil {
return nil, err
}
}
if err = t.store.UpdateRow(h, blobCols, data...); err != nil { //LATER detect which blobs are actually affected
return nil, err
}
for i, v := range t.indices {
if i == 0 { // id() N/A
continue
}
if v == nil || !touched[i-1] {
continue
}
if err = v.x.Create([]interface{}{data[2+i-1]}, h); err != nil {
return nil, err
}
}
for _, ix := range t.indices2 {
vlist, err := ix.eval(ctx, t.cols, id, data[2:])
if err != nil {
return nil, err
}
if err := ix.x.Create(vlist, h); err != nil {
return nil, err
}
}
cc.RowsAffected++
}
return
}
func (s *updateStmt) isUpdating() bool { return true }
type deleteStmt struct {
tableName string
where expression
}
func (s *deleteStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *deleteStmt) String() string {
switch {
case s.where == nil:
return fmt.Sprintf("DELETE FROM %s;", s.tableName)
default:
return fmt.Sprintf("DELETE FROM %s WHERE %s;", s.tableName, s.where)
}
}
func (s *deleteStmt) exec(ctx *execCtx) (_ Recordset, err error) {
t, ok := ctx.db.root.tables[s.tableName]
if !ok {
return nil, fmt.Errorf("DELETE FROM: table %s does not exist", s.tableName)
}
m := map[interface{}]interface{}{}
var ph, h, nh int64
var data []interface{}
blobCols := t.blobCols()
cc := ctx.db.cc
for h = t.head; h != 0; ph, h = h, nh {
for i, v := range data {
c, ok := v.(chunk)
if !ok {
continue
}
data[i] = c.b
}
// Read can return lazily expanded chunks
data, err = t.store.Read(nil, h, t.cols...)
if err != nil {
return nil, err
}
nh = data[0].(int64)
for _, col := range t.cols {
m[col.name] = data[2+col.index]
}
id := data[1].(int64)
m["$id"] = id
val, err := s.where.eval(ctx, m)
if err != nil {
return nil, err
}
if val == nil {
continue
}
x, ok := val.(bool)
if !ok {
return nil, fmt.Errorf("invalid WHERE expression %s (value of type %T)", val, val)
}
if !x {
continue
}
// hit
for i, v := range t.indices {
if v == nil {
continue
}
// overflow chunks left in place
if err = v.x.Delete([]interface{}{data[i+1]}, h); err != nil {
return nil, err
}
}
for _, ix := range t.indices2 {
vlist, err := ix.eval(ctx, t.cols, id, data[2:])
if err != nil {
return nil, err
}
if err := ix.x.Delete(vlist, h); err != nil {
return nil, err
}
}
// overflow chunks freed here
if err = t.store.Delete(h, blobCols...); err != nil {
return nil, err
}
cc.RowsAffected++
switch {
case ph == 0 && nh == 0: // "only"
fallthrough
case ph == 0 && nh != 0: // "first"
if err = t.store.Update(t.hhead, nh); err != nil {
return nil, err
}
t.head, h = nh, 0
case ph != 0 && nh == 0: // "last"
fallthrough
case ph != 0 && nh != 0: // "inner"
pdata, err := t.store.Read(nil, ph, t.cols...)
if err != nil {
return nil, err
}
for i, v := range pdata {
if x, ok := v.(chunk); ok {
pdata[i] = x.b
}
}
pdata[0] = nh
if err = t.store.Update(ph, pdata...); err != nil {
return nil, err
}
h = ph
}
}
return
}
func (s *deleteStmt) isUpdating() bool { return true }
type truncateTableStmt struct {
tableName string
}
func (s *truncateTableStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *truncateTableStmt) String() string { return fmt.Sprintf("TRUNCATE TABLE %s;", s.tableName) }
func (s *truncateTableStmt) exec(ctx *execCtx) (Recordset, error) {
t, ok := ctx.db.root.tables[s.tableName]
if !ok {
return nil, fmt.Errorf("TRUNCATE TABLE: table %s does not exist", s.tableName)
}
return nil, t.truncate()
}
func (s *truncateTableStmt) isUpdating() bool { return true }
type dropIndexStmt struct {
ifExists bool
indexName string
}
func (s *dropIndexStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *dropIndexStmt) String() string { return fmt.Sprintf("DROP INDEX %s;", s.indexName) }
func (s *dropIndexStmt) exec(ctx *execCtx) (Recordset, error) {
t, x := ctx.db.root.findIndexByName(s.indexName)
if x == nil {
if s.ifExists {
return nil, nil
}
return nil, fmt.Errorf("DROP INDEX: index %s does not exist", s.indexName)
}
if ctx.db.hasAllIndex2() {
if err := ctx.db.deleteIndex2ByIndexName(s.indexName); err != nil {
return nil, err
}
}
switch ix := x.(type) {
case *indexedCol:
for i, v := range t.indices {
if v == nil || v.name != s.indexName {
continue
}
return nil, t.dropIndex(i)
}
case *index2:
delete(t.indices2, s.indexName)
return nil, ix.x.Drop()
}
panic("internal error 058")
}
func (s *dropIndexStmt) isUpdating() bool { return true }
type dropTableStmt struct {
ifExists bool
tableName string
}
func (s *dropTableStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *dropTableStmt) String() string { return fmt.Sprintf("DROP TABLE %s;", s.tableName) }
func (s *dropTableStmt) exec(ctx *execCtx) (Recordset, error) {
t, ok := ctx.db.root.tables[s.tableName]
if !ok {
if s.ifExists {
return nil, nil
}
return nil, fmt.Errorf("DROP TABLE: table %s does not exist", s.tableName)
}
if ctx.db.hasAllIndex2() {
if err := ctx.db.deleteIndex2ByTableName(s.tableName); err != nil {
return nil, err
}
}
return nil, ctx.db.root.dropTable(t)
}
func (s *dropTableStmt) isUpdating() bool { return true }
type alterTableDropColumnStmt struct {
tableName, colName string
}
func (s *alterTableDropColumnStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *alterTableDropColumnStmt) String() string {
return fmt.Sprintf("ALTER TABLE %s DROP COLUMN %s;", s.tableName, s.colName)
}
func (s *alterTableDropColumnStmt) exec(ctx *execCtx) (Recordset, error) {
t, ok := ctx.db.root.tables[s.tableName]
if !ok {
return nil, fmt.Errorf("ALTER TABLE: table %s does not exist", s.tableName)
}
cols := t.cols
for _, c := range cols {
if c.name == s.colName {
if len(cols) == 1 {
return nil, fmt.Errorf("ALTER TABLE %s DROP COLUMN: cannot drop the only column: %s", s.tableName, s.colName)
}
if _, ok := ctx.db.root.tables["__Column2"]; ok {
if _, err := deleteColumn2.l[0].exec(&execCtx{db: ctx.db, arg: []interface{}{s.tableName, c.name}}); err != nil {
return nil, err
}
}
c.name = ""
t.cols0[c.index].name = ""
if t.hasIndices() {
if len(t.indices) != 0 {
if v := t.indices[c.index+1]; v != nil {
if err := t.dropIndex(c.index + 1); err != nil {
return nil, err
}
if ctx.db.hasAllIndex2() {
if err := ctx.db.deleteIndex2ByIndexName(v.name); err != nil {
return nil, err
}
}
}
}
for nm, ix := range t.indices2 {
for _, e := range ix.exprList {
m := mentionedColumns(e)
if _, ok := m[s.colName]; ok {
if err := ctx.db.deleteIndex2ByIndexName(nm); err != nil {
return nil, err
}
if err := ix.x.Drop(); err != nil {
return nil, err
}
delete(t.indices2, nm)
break
}
}
}
}
if err := t.constraintsAndDefaults(ctx); err != nil {
return nil, err
}
return nil, t.updated()
}
}
return nil, fmt.Errorf("ALTER TABLE %s DROP COLUMN: column %s does not exist", s.tableName, s.colName)
}
func (s *alterTableDropColumnStmt) isUpdating() bool { return true }
type alterTableAddStmt struct {
tableName string
c *col
}
func (s *alterTableAddStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *alterTableAddStmt) String() string {
r := fmt.Sprintf("ALTER TABLE %s ADD %s %s", s.tableName, s.c.name, typeStr(s.c.typ))
c := s.c
if x := c.constraint; x != nil { //TODO add (*col).String()
switch e := x.expr; {
case e != nil:
r += " " + e.String()
default:
r += " NOT NULL"
}
}
if x := c.dflt; x != nil {
r += " DEFAULT " + x.String()
}
return r + ";"
}
func (s *alterTableAddStmt) exec(ctx *execCtx) (Recordset, error) {
t, ok := ctx.db.root.tables[s.tableName]
if !ok {
return nil, fmt.Errorf("ALTER TABLE: table %s does not exist", s.tableName)
}
hasRecords := t.head != 0
c := s.c
if c.constraint != nil && hasRecords {
return nil, fmt.Errorf("ALTER TABLE %s ADD %s: cannot add constrained column to table with existing data", s.tableName, c.name)
}
cols := t.cols
for _, c := range cols {
nm := c.name
if nm == s.c.name {
return nil, fmt.Errorf("ALTER TABLE %s ADD: column %s exists", s.tableName, nm)
}
}
if len(t.indices) != 0 {
t.indices = append(t.indices, nil)
t.xroots = append(t.xroots, 0)
if err := t.store.Update(t.hxroots, t.xroots...); err != nil {
return nil, err
}
}
if c.constraint != nil || c.dflt != nil {
for _, s := range createColumn2.l {
_, err := s.exec(&execCtx{db: ctx.db})
if err != nil {
return nil, err
}
}
notNull := c.constraint != nil && c.constraint.expr == nil
var co, d string
if c.constraint != nil && c.constraint.expr != nil {
co = c.constraint.expr.String()
}
if e := c.dflt; e != nil {
d = e.String()
}
if _, err := insertColumn2.l[0].exec(&execCtx{db: ctx.db, arg: []interface{}{s.tableName, c.name, notNull, co, d}}); err != nil {
return nil, err
}
}
t.cols0 = append(t.cols0, s.c)
if err := t.constraintsAndDefaults(ctx); err != nil {
return nil, err
}
return nil, t.updated()
}
func (s *alterTableAddStmt) isUpdating() bool { return true }
type selectStmt struct {
distinct bool
flds []*fld
from *joinRset
group *groupByRset
hasAggregates bool
limit *limitRset
offset *offsetRset
order *orderByRset
where *whereRset
}
func (s *selectStmt) explain(ctx *execCtx, w strutil.Formatter) {
p, err := s.plan(ctx)
if err != nil {
w.Format("ERROR: %v\n", err)
return
}
p.explain(w)
}
func (s *selectStmt) String() string {
var b bytes.Buffer
b.WriteString("SELECT")
if s.distinct {
b.WriteString(" DISTINCT")
}
switch {
case len(s.flds) == 0:
b.WriteString(" *")
default:
a := make([]string, len(s.flds))
for i, v := range s.flds {
s := v.expr.String()
if v.name != "" && v.name != s {
s += " AS " + v.name
}
a[i] = s
}
b.WriteString(" " + strings.Join(a, ", "))
}
b.WriteString(" FROM ")
b.WriteString(s.from.String())
if s.where != nil {
b.WriteString(" WHERE ")
b.WriteString(s.where.expr.String())
}
if s.group != nil {
b.WriteString(" GROUP BY ")
b.WriteString(strings.Join(s.group.colNames, ", "))
}
if s.order != nil {
b.WriteString(" ORDER BY ")
b.WriteString(s.order.String())
}
if s.limit != nil {
b.WriteString(" LIMIT ")
b.WriteString(s.limit.expr.String())
}
if s.offset != nil {
b.WriteString(" OFFSET ")
b.WriteString(s.offset.expr.String())
}
b.WriteRune(';')
return b.String()
}
func (s *selectStmt) plan(ctx *execCtx) (plan, error) { //LATER overlapping goroutines/pipelines
r, err := s.from.plan(ctx)
if err != nil {
return nil, err
}
if w := s.where; w != nil {
if r, err = (&whereRset{expr: w.expr, src: r}).plan(ctx); err != nil {
return nil, err
}
}
switch {
case !s.hasAggregates && s.group == nil: // nop
case !s.hasAggregates && s.group != nil:
if r, err = (&groupByRset{colNames: s.group.colNames, src: r}).plan(ctx); err != nil {
return nil, err
}
case s.hasAggregates && s.group == nil:
if r, err = (&groupByRset{src: r}).plan(ctx); err != nil {
return nil, err
}
case s.hasAggregates && s.group != nil:
if r, err = (&groupByRset{colNames: s.group.colNames, src: r}).plan(ctx); err != nil {
return nil, err
}
}
if r, err = (&selectRset{flds: s.flds, src: r}).plan(ctx); err != nil {
return nil, err
}
if s.distinct {
if r, err = (&distinctRset{src: r}).plan(ctx); err != nil {
return nil, err
}
}
if s := s.order; s != nil {
if r, err = (&orderByRset{asc: s.asc, by: s.by, src: r}).plan(ctx); err != nil {
return nil, err
}
}
if s := s.offset; s != nil {
if r, err = (&offsetRset{s.expr, r}).plan(ctx); err != nil {
return nil, err
}
}
if s := s.limit; s != nil {
if r, err = (&limitRset{s.expr, r}).plan(ctx); err != nil {
return nil, err
}
}
return r, nil
}
func (s *selectStmt) exec(ctx *execCtx) (rs Recordset, err error) {
r, err := s.plan(ctx)
if err != nil {
return nil, err
}
return recordset{ctx, r, nil}, nil
}
func (s *selectStmt) isUpdating() bool { return false }
type insertIntoStmt struct {
colNames []string
lists [][]expression
sel *selectStmt
tableName string
}
func (s *insertIntoStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *insertIntoStmt) String() string {
cn := ""
if len(s.colNames) != 0 {
cn = fmt.Sprintf(" (%s)", strings.Join(s.colNames, ", "))
}
switch {
case s.sel != nil:
return fmt.Sprintf("INSERT INTO %s%s %s;", s.tableName, cn, s.sel)
default:
a := make([]string, len(s.lists))
for i, v := range s.lists {
b := make([]string, len(v))
for i, v := range v {
b[i] = v.String()
}
a[i] = fmt.Sprintf("(%s)", strings.Join(b, ", "))
}
return fmt.Sprintf("INSERT INTO %s%s VALUES %s;", s.tableName, cn, strings.Join(a, ", "))
}
}
func (s *insertIntoStmt) execSelect(t *table, cols []*col, ctx *execCtx) (_ Recordset, err error) {
//TODO missing rs column number eq check
r, err := s.sel.plan(ctx)
if err != nil {
return nil, err
}
h := t.head
data0 := make([]interface{}, len(t.cols0)+2)
cc := ctx.db.cc
m := map[interface{}]interface{}{}
if err = r.do(ctx, func(_ interface{}, data []interface{}) (more bool, err error) {
for i, d := range data {
data0[cols[i].index+2] = d
}
if err = typeCheck(data0[2:], cols); err != nil {
return
}
if err = t.checkConstraintsAndDefaults(ctx, data0[2:], m); err != nil {
return false, err
}
id, err := t.store.ID()
if err != nil {
return false, err
}
data0[0] = h
data0[1] = id
// Any overflow chunks are written here.
if h, err = t.store.Create(data0...); err != nil {
return false, err
}
for i, v := range t.indices {
if v == nil {
continue
}
// Any overflow chunks are shared with the BTree key
if err = v.x.Create([]interface{}{data0[i+1]}, h); err != nil {
return false, err
}
}
for _, ix := range t.indices2 {
vlist, err := ix.eval(ctx, t.cols, id, data0[2:])
if err != nil {
return false, err
}
if err := ix.x.Create(vlist, h); err != nil {
return false, err
}
}
cc.RowsAffected++
ctx.db.root.lastInsertID = id
return true, nil
}); err != nil {
return nil, err
}
t.head = h
return nil, t.store.Update(t.hhead, h)
}
func (s *insertIntoStmt) exec(ctx *execCtx) (_ Recordset, err error) {
root := ctx.db.root
t, ok := root.tables[s.tableName]
if !ok {
return nil, fmt.Errorf("INSERT INTO %s: table does not exist", s.tableName)
}
var cols []*col
switch len(s.colNames) {
case 0:
cols = t.cols
default:
for _, colName := range s.colNames {
if col := findCol(t.cols, colName); col != nil {
cols = append(cols, col)
continue
}
return nil, fmt.Errorf("INSERT INTO %s: unknown column %s", s.tableName, colName)
}
}
if s.sel != nil {
return s.execSelect(t, cols, ctx)
}
for _, list := range s.lists {
if g, e := len(list), len(cols); g != e {
return nil, fmt.Errorf("INSERT INTO %s: expected %d value(s), have %d", s.tableName, e, g)
}
}
cc := ctx.db.cc
r := make([]interface{}, len(t.cols0))
m := map[interface{}]interface{}{}
for _, list := range s.lists {
for i, expr := range list {
val, err := expr.eval(ctx, m)
if err != nil {
return nil, err
}
r[cols[i].index] = val
}
if err = typeCheck(r, cols); err != nil {
return nil, err
}
if err = t.checkConstraintsAndDefaults(ctx, r, m); err != nil {
return nil, err
}
id, err := t.addRecord(ctx, r)
if err != nil {
return nil, err
}
cc.RowsAffected++
root.lastInsertID = id
}
return nil, nil
}
func (s *insertIntoStmt) isUpdating() bool { return true }
type beginTransactionStmt struct{}
func (s beginTransactionStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (beginTransactionStmt) String() string { return "BEGIN TRANSACTION;" }
func (beginTransactionStmt) exec(*execCtx) (Recordset, error) {
panic("internal error 059")
}
func (beginTransactionStmt) isUpdating() bool {
panic("internal error 060")
}
type commitStmt struct{}
func (s commitStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (commitStmt) String() string { return "COMMIT;" }
func (commitStmt) exec(*execCtx) (Recordset, error) {
panic("internal error 061")
}
func (commitStmt) isUpdating() bool {
panic("internal error 062")
}
type rollbackStmt struct{}
func (s rollbackStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (rollbackStmt) String() string { return "ROLLBACK;" }
func (rollbackStmt) exec(*execCtx) (Recordset, error) {
panic("internal error 063")
}
func (rollbackStmt) isUpdating() bool {
panic("internal error 064")
}
type createIndexStmt struct {
colName string // alt. "id()" for simple index on id()
ifNotExists bool
indexName string
tableName string
unique bool
exprList []expression
}
func (s *createIndexStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *createIndexStmt) isSimpleIndex() bool { return s.colName != "" }
func (s *createIndexStmt) String() string {
u := ""
if s.unique {
u = "UNIQUE "
}
e := ""
if s.ifNotExists {
e = "IF NOT EXISTS "
}
expr := s.colName
if !s.isSimpleIndex() {
var a []string
for _, v := range s.exprList {
a = append(a, v.String())
}
expr = strings.Join(a, ", ")
}
return fmt.Sprintf("CREATE %sINDEX %s%s ON %s (%s);", u, e, s.indexName, s.tableName, expr)
}
func (s *createIndexStmt) exec(ctx *execCtx) (Recordset, error) {
root := ctx.db.root
if t, i := root.findIndexByName(s.indexName); i != nil {
if s.ifNotExists {
return nil, nil
}
return nil, fmt.Errorf("CREATE INDEX: table %s already has an index named %s", t.name, s.indexName)
}
if root.tables[s.indexName] != nil {
return nil, fmt.Errorf("CREATE INDEX: index name collision with existing table: %s", s.indexName)
}
t, ok := root.tables[s.tableName]
if !ok {
return nil, fmt.Errorf("CREATE INDEX: table does not exist %s", s.tableName)
}
if findCol(t.cols, s.indexName) != nil {
return nil, fmt.Errorf("CREATE INDEX: index name collision with existing column: %s", s.indexName)
}
var h int64
var err error
switch {
case s.isSimpleIndex():
colIndex := -1
if s.colName != "id()" {
c := findCol(t.cols, s.colName)
if c == nil {
return nil, fmt.Errorf("CREATE INDEX: column does not exist: %s", s.colName)
}
colIndex = c.index
}
if h, err = t.addIndex(s.unique, s.indexName, colIndex); err != nil {
return nil, fmt.Errorf("CREATE INDEX: %v", err)
}
if err = t.updated(); err != nil {
return nil, err
}
default:
for _, e := range s.exprList {
m := mentionedColumns(e)
for colName := range m {
c := findCol(t.cols, colName)
if c == nil {
return nil, fmt.Errorf("CREATE INDEX: column does not exist: %s", colName)
}
}
}
if h, err = t.addIndex2(ctx, s.unique, s.indexName, s.exprList); err != nil {
return nil, fmt.Errorf("CREATE INDEX: %v", err)
}
}
switch ctx.db.hasIndex2 {
case 0:
if err := ctx.db.createIndex2(); err != nil {
return nil, err
}
if s.isSimpleIndex() {
return nil, nil
}
case 1:
return nil, nil
case 2:
if s.isSimpleIndex() {
return nil, ctx.db.insertIndex2(s.tableName, s.indexName, []string{s.colName}, s.unique, true, h)
}
default:
panic("internal error 011")
}
exprList := make([]string, 0, len(s.exprList))
for _, e := range s.exprList {
exprList = append(exprList, e.String())
}
return nil, ctx.db.insertIndex2(s.tableName, s.indexName, exprList, s.unique, false, h)
}
func (s *createIndexStmt) isUpdating() bool { return true }
type createTableStmt struct {
ifNotExists bool
tableName string
cols []*col
}
func (s *createTableStmt) explain(ctx *execCtx, w strutil.Formatter) {
w.Format("%s\n", s)
}
func (s *createTableStmt) String() string {
a := make([]string, len(s.cols))
for i, v := range s.cols {
var c, d string
if x := v.constraint; x != nil {
switch e := x.expr; {
case e != nil:
c = " " + e.String()
default:
c = " NOT NULL"
}
}
if x := v.dflt; x != nil {
d = " DEFAULT " + x.String()
}
a[i] = fmt.Sprintf("%s %s%s%s", v.name, typeStr(v.typ), c, d)
}
e := ""
if s.ifNotExists {
e = "IF NOT EXISTS "
}
return fmt.Sprintf("CREATE TABLE %s%s (%s);", e, s.tableName, strings.Join(a, ", "))
}
func (s *createTableStmt) exec(ctx *execCtx) (_ Recordset, err error) {
var cols []*col
for _, v := range s.cols {
cols = append(cols, v.clone())
}
root := ctx.db.root
if _, ok := root.tables[s.tableName]; ok {
if s.ifNotExists {
return nil, nil
}
return nil, fmt.Errorf("CREATE TABLE: table exists %s", s.tableName)
}
if t, x := root.findIndexByName(s.tableName); x != nil {
return nil, fmt.Errorf("CREATE TABLE: table %s has index %s", t.name, s.tableName)
}
m := map[string]bool{}
mustCreateColumn2 := true
for i, c := range cols {
nm := c.name
if m[nm] {
return nil, fmt.Errorf("CREATE TABLE: duplicate column %s", nm)
}
m[nm] = true
c.index = i
if c.constraint != nil || c.dflt != nil {
if mustCreateColumn2 {
for _, stmt := range createColumn2.l {
_, err := stmt.exec(&execCtx{db: ctx.db})
if err != nil {
return nil, err
}
}
}
mustCreateColumn2 = false
notNull := c.constraint != nil && c.constraint.expr == nil
var co, d string
if c.constraint != nil && c.constraint.expr != nil {
co = c.constraint.expr.String()
}
if e := c.dflt; e != nil {
d = e.String()
}
if _, err := insertColumn2.l[0].exec(&execCtx{db: ctx.db, arg: []interface{}{s.tableName, c.name, notNull, co, d}}); err != nil {
return nil, err
}
}
}
t, err := root.createTable(s.tableName, cols)
if err != nil {
return nil, err
}
return nil, t.constraintsAndDefaults(ctx)
}
func (s *createTableStmt) isUpdating() bool { return true }