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  }