github.com/vescale/zgraph@v0.0.0-20230410094002-959c02d50f95/executor/insert.go (about) 1 // Copyright 2022 zGraph Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package executor 16 17 import ( 18 "context" 19 20 "github.com/vescale/zgraph/catalog" 21 "github.com/vescale/zgraph/codec" 22 "github.com/vescale/zgraph/datum" 23 "github.com/vescale/zgraph/internal/logutil" 24 "github.com/vescale/zgraph/parser/ast" 25 "github.com/vescale/zgraph/planner" 26 "github.com/vescale/zgraph/storage/kv" 27 ) 28 29 // InsertExec represents the executor of INSERT statement. 30 type InsertExec struct { 31 baseExecutor 32 33 done bool 34 graph *catalog.Graph 35 insertions []*planner.ElementInsertion 36 kvs []kv.Pair 37 buffer []byte 38 encoder *codec.PropertyEncoder 39 decoder *codec.PropertyDecoder 40 matchExec Executor 41 } 42 43 // Open implements the Executor interface. 44 func (e *InsertExec) Open(ctx context.Context) error { 45 if e.matchExec != nil { 46 if err := e.matchExec.Open(ctx); err != nil { 47 return err 48 } 49 } else { 50 var kvs int 51 // Precalculate key/value count and preallocate ID count. 52 for _, insertion := range e.insertions { 53 // Every label has a unique key. 54 kvs += len(insertion.Labels) 55 switch insertion.Type { 56 case ast.InsertionTypeVertex: 57 kvs++ 58 case ast.InsertionTypeEdge: 59 // Every edge will write two key/value: src <-> dst 60 kvs += 2 61 } 62 } 63 e.kvs = make([]kv.Pair, 0, kvs) 64 } 65 return nil 66 } 67 68 // Next implements the Executor interface. 69 func (e *InsertExec) Next(ctx context.Context) (datum.Row, error) { 70 if e.done { 71 return nil, nil 72 } 73 e.done = true 74 75 if len(e.insertions) == 0 { 76 return nil, nil 77 } 78 79 var err error 80 if e.matchExec == nil { 81 err = e.encodeInsertions(nil) 82 } else { 83 err = e.encodeInsertionsFromMatch(ctx) 84 } 85 if err != nil { 86 return nil, err 87 } 88 89 if len(e.kvs) == 0 { 90 return nil, nil 91 } 92 93 // FIXME: use transaction in stmtctx.Context 94 err = kv.Txn(e.sc.Store(), func(txn kv.Transaction) error { 95 for _, pair := range e.kvs { 96 err := txn.Set(pair.Key, pair.Val) 97 if err != nil { 98 return err 99 } 100 } 101 return nil 102 }) 103 if err != nil { 104 logutil.Errorf("Insert vertices/edges failed: %+v", e.insertions) 105 } 106 107 return nil, err 108 } 109 110 func (e *InsertExec) encodeInsertions(matchRow datum.Row) error { 111 graphID := e.graph.Meta().ID 112 idRange, err := e.sc.AllocID(e.graph, len(e.insertions)) 113 if err != nil { 114 return err 115 } 116 117 for _, insertion := range e.insertions { 118 switch insertion.Type { 119 case ast.InsertionTypeVertex: 120 vertexID, err := idRange.Next() 121 if err != nil { 122 return err 123 } 124 if err := e.encodeVertex(graphID, vertexID, insertion, matchRow); err != nil { 125 return err 126 } 127 case ast.InsertionTypeEdge: 128 if err := e.encodeEdge(graphID, insertion, matchRow); err != nil { 129 return err 130 } 131 } 132 } 133 134 return nil 135 } 136 137 func (e *InsertExec) encodeInsertionsFromMatch(ctx context.Context) error { 138 for { 139 row, err := e.matchExec.Next(ctx) 140 if err != nil { 141 return err 142 } 143 if row == nil { 144 return nil 145 } 146 if err := e.encodeInsertions(row); err != nil { 147 return err 148 } 149 } 150 } 151 152 func (e *InsertExec) encodeVertex(graphID, vertexID int64, insertion *planner.ElementInsertion, matchRow datum.Row) error { 153 key := codec.VertexKey(graphID, vertexID) 154 var ( 155 propertyIDs []uint16 156 values datum.Row 157 ) 158 for _, assignment := range insertion.Assignments { 159 value, err := assignment.Expr.Eval(e.sc, matchRow) 160 if err != nil { 161 return err 162 } 163 propertyIDs = append(propertyIDs, assignment.PropertyRef.Property.ID) 164 values = append(values, value) 165 } 166 var labelIDs []uint16 167 for _, label := range insertion.Labels { 168 labelIDs = append(labelIDs, uint16(label.Meta().ID)) 169 } 170 ret, err := e.encoder.Encode(e.buffer, labelIDs, propertyIDs, values) 171 if err != nil { 172 return err 173 } 174 val := make([]byte, len(ret)) 175 copy(val, ret) 176 e.kvs = append(e.kvs, kv.Pair{Key: key, Val: val}) 177 return nil 178 } 179 180 func (e *InsertExec) encodeEdge(graphID int64, insertion *planner.ElementInsertion, matchRow datum.Row) error { 181 // TODO: Edge also need an unique ID. How to encode and index it? See https://pgql-lang.org/spec/1.5/#id. 182 var ( 183 propertyIDs []uint16 184 values []datum.Datum 185 ) 186 for _, assignment := range insertion.Assignments { 187 value, err := assignment.Expr.Eval(e.sc, matchRow) 188 if err != nil { 189 return err 190 } 191 propertyIDs = append(propertyIDs, assignment.PropertyRef.Property.ID) 192 values = append(values, value) 193 } 194 195 srcIDVal, err := insertion.FromIDExpr.Eval(e.sc, matchRow) 196 if err != nil { 197 return err 198 } 199 dstIDVal, err := insertion.ToIDExpr.Eval(e.sc, matchRow) 200 if err != nil { 201 return err 202 } 203 204 srcID := datum.AsInt(srcIDVal) 205 dstID := datum.AsInt(dstIDVal) 206 207 var labelIDs []uint16 208 for _, label := range insertion.Labels { 209 labelIDs = append(labelIDs, uint16(label.Meta().ID)) 210 } 211 ret, err := e.encoder.Encode(e.buffer, labelIDs, propertyIDs, values) 212 if err != nil { 213 return err 214 } 215 val := make([]byte, len(ret)) 216 copy(val, ret) 217 e.kvs = append(e.kvs, kv.Pair{Key: codec.IncomingEdgeKey(graphID, srcID, dstID), Val: val}) 218 e.kvs = append(e.kvs, kv.Pair{Key: codec.OutgoingEdgeKey(graphID, srcID, dstID), Val: val}) 219 return nil 220 } 221 222 func (e *InsertExec) Close() error { 223 if e.matchExec != nil { 224 return e.matchExec.Close() 225 } 226 return nil 227 }