// 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" "sync" "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 mu sync.Mutex 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 }