| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 | /* * EliasDB * * Copyright 2016 Matthias Ladkau. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */package interpreterimport (	"fmt"	"strconv"	"strings"	"devt.de/krotik/eliasdb/eql/parser"	"devt.de/krotik/eliasdb/graph"	"devt.de/krotik/eliasdb/graph/data")/*allowMultiEval allows multiple calls to eval of runtime components withoutresetting state (used for testing)*/var allowMultiEval = false// Special flags which can be set by with statementstype withFlags struct {	ordering     []byte // Result ordering	orderingCol  []int  // Columns which should be ordered	notnullCol   []int  // Columns which must not be null	uniqueCol    []int  // Columns which will only contain unique values	uniqueColCnt []bool // Flag if unique values should be counted}const (	withOrderingAscending  = 0x1	withOrderingDescending = 0x2)/*GroupNodeKind is a special group node kind*/const GroupNodeKind = "group"// General runtime provider// ========================/*eqlRuntimeProvider defines the main interpreterdatastructure and all functions for general evaluation.*/type eqlRuntimeProvider struct {	name       string         // Name to identify the input	part       string         // Graph partition to query	gm         *graph.Manager // GraphManager to operate on	ni         NodeInfo       // NodeInfo to use for formatting	groupScope string         // Group scope for query	allowNilTraversal bool       // Flag if empty traversals should be included in the result	withFlags         *withFlags // Special flags which can be set by with statements	primaryKind  string                 // Primary node kind	nextStartKey func() (string, error) // Function to get the next start key	traversals []*parser.ASTNode // Array of all top level query traversals	where      *parser.ASTNode   // First where clause	show       *parser.ASTNode   // Show clause node	specs      []string            // Flat list of traversals of this query	attrsNodes []map[string]string // Attributes for nodes to query on each traversal	attrsEdges []map[string]string // Attributes for nodes to query on each traversal	rowNode    []data.Node         // Current row of nodes which is evaluated	rowEdge    []data.Edge         // Current row of edges which is evaluated	colLabels []string   // Labels for columns	colFormat []string   // Format for columns	colData   []string   // Data for columns	colFunc   []FuncShow // Function to transform column value	_attrsNodesFetch [][]string // Internal copy of attrsNodes better suited for fetchPart calls	_attrsEdgesFetch [][]string // Internal copy of attrsEdges better suited for fetchPart calls}/*Initialise and validate data structures.*/func (p *eqlRuntimeProvider) init(startKind string,	rootChildren []*parser.ASTNode) error {	// By default we don't include empty traversals in the result	p.allowNilTraversal = false	// Clear any with flags	p.withFlags = &withFlags{make([]byte, 0), make([]int, 0), make([]int, 0),		make([]int, 0), make([]bool, 0)}	// Reinitialise datastructures	p.groupScope = ""	p.traversals = make([]*parser.ASTNode, 0)	p.where = nil	p.show = nil	p.specs = make([]string, 0)	p.attrsNodes = make([]map[string]string, 0)	p.attrsEdges = make([]map[string]string, 0)	p.rowNode = nil	p.rowEdge = nil	p._attrsNodesFetch = nil	p._attrsEdgesFetch = nil	p.colLabels = make([]string, 0)	p.colFormat = make([]string, 0)	p.colData = make([]string, 0)	p.colFunc = make([]FuncShow, 0)	p.primaryKind = ""	p.specs = append(p.specs, startKind)	p.attrsNodes = append(p.attrsNodes, make(map[string]string))	p.attrsEdges = append(p.attrsEdges, make(map[string]string))	// With clause is interpreted straight after finishing the columns	var withChild *parser.ASTNode	// Go through the children, check if they are valid and initialise them	for _, child := range rootChildren {		if child.Name == parser.NodeWHERE {			// Check if the show clause or some traversals are already populated			if p.show != nil || len(p.traversals) > 0 {				return p.newRuntimeError(ErrInvalidConstruct,					"condition must be before show clause and traversals", child)			}			// Reset state of where and store it			if err := child.Runtime.Validate(); err != nil {				return err			}			p.where = child		} else if child.Name == parser.NodeTRAVERSE {			// Check if show clause or where clause is already populated			if p.show != nil {				return p.newRuntimeError(ErrInvalidConstruct,					"traversals must be before show clause", child)			}			// Reset state of traversal and add it to the traversal list			if err := child.Runtime.Validate(); err != nil {				return err			}			p.traversals = append(p.traversals, child)		} else if child.Name == parser.NodeSHOW {			p.show = child		} else if child.Name == parser.NodeFROM {			// Set the group state			p.groupScope = child.Children[0].Children[0].Token.Val		} else if child.Name == parser.NodePRIMARY {			pk := child.Children[0].Token.Val			for _, nk := range p.gm.NodeKinds() {				if nk == pk {					p.primaryKind = pk				}			}			if p.primaryKind == "" {				return p.newRuntimeError(ErrUnknownNodeKind, pk, child.Children[0])			}		} else if child.Name == parser.NodeWITH {			withChild = child		} else {			return p.newRuntimeError(ErrInvalidConstruct, child.Name, child)		}	}	// Populate column related attributes	nodeKindPos, edgeKindPos, err := p.initCols()	if err != nil {		return err	}	// Interpret with clause straight after populating the columns	if withChild != nil {		if err := p.initWithFlags(withChild, nodeKindPos, edgeKindPos); err != nil {			return err		}	}	if p.primaryKind == "" {		p.primaryKind = startKind	}	return nil}/*initWithFlags populates the withFlags datastructure. It is assumed that thecolumns have been populated before calling this function.*/func (p *eqlRuntimeProvider) initWithFlags(withNode *parser.ASTNode,	nodeKindPos map[string][]int, edgeKindPos map[string][]int) error {	// Helper function to find a specified column	findColumn := func(colData string, node *parser.ASTNode) (int, error) {		col := -1		colDataSplit := strings.SplitN(colData, ":", 3)		switch len(colDataSplit) {		case 1:			// Find the first column which displays the given attribute			for i, cd := range p.colData {				cds := strings.SplitN(cd, ":", 3)				if cds[2] == colDataSplit[0] {					col = i				}			}		case 2:			// Search for first kind / attribute occurrence			kind := colDataSplit[0]			attr := colDataSplit[1]			searchColData := func(pos int, t string) {				cstr := fmt.Sprint(pos+1, ":", t, ":", attr)				for i, c := range p.colData {					if c == cstr {						col = i					}				}			}			if poslist, ok := nodeKindPos[kind]; ok {				searchColData(poslist[0], "n")			} else if poslist, ok := edgeKindPos[kind]; ok {				searchColData(poslist[0], "e")			} else {				return -1, p.newRuntimeError(ErrInvalidConstruct,					"Cannot determine column for with term: "+colData, node)			}		case 3:			// Search for exact specification			for i, c := range p.colData {				if c == colData {					col = i				}			}		}		if col == -1 {			return -1, p.newRuntimeError(ErrInvalidConstruct,				"Cannot determine column for with term: "+colData, node)		}		return col, nil	}	// Go through all children and initialise the withFlags data structure	for _, child := range withNode.Children {		if child.Name == parser.NodeNULLTRAVERSAL && child.Children[0].Name == parser.NodeTRUE {			p.allowNilTraversal = true		} else if child.Name == parser.NodeFILTERING {			for _, child := range child.Children {				if child.Name == parser.NodeISNOTNULL || child.Name == parser.NodeUNIQUE || child.Name == parser.NodeUNIQUECOUNT {					c, err := findColumn(child.Children[0].Token.Val, child)					if err != nil {						return err					}					if child.Name == parser.NodeISNOTNULL {						p.withFlags.notnullCol = append(p.withFlags.notnullCol, c)					} else if child.Name == parser.NodeUNIQUE {						p.withFlags.uniqueCol = append(p.withFlags.uniqueCol, c)						p.withFlags.uniqueColCnt = append(p.withFlags.uniqueColCnt, false)					} else if child.Name == parser.NodeUNIQUECOUNT {						p.withFlags.uniqueCol = append(p.withFlags.uniqueCol, c)						p.withFlags.uniqueColCnt = append(p.withFlags.uniqueColCnt, true)					}				} else {					return p.newRuntimeError(ErrInvalidConstruct, child.Token.Val, child)				}			}		} else if child.Name == parser.NodeORDERING {			for _, child := range child.Children {				if child.Name == parser.NodeASCENDING || child.Name == parser.NodeDESCENDING {					c, err := findColumn(child.Children[0].Token.Val, child)					if err != nil {						return err					}					if child.Name == parser.NodeASCENDING {						p.withFlags.ordering = append(p.withFlags.ordering, withOrderingAscending)					} else {						p.withFlags.ordering = append(p.withFlags.ordering, withOrderingDescending)					}					p.withFlags.orderingCol = append(p.withFlags.orderingCol, c)				} else {					return p.newRuntimeError(ErrInvalidConstruct, child.Token.Val, child)				}			}		} else {			return p.newRuntimeError(ErrInvalidConstruct, child.Token.Val, child)		}	}	return nil}/*initCols populates the column related attributes. This function assumes thatspecs is filled with all necessary traversals.The following formats for a show term are allowed:<step>:<type>:<attr>  - Attribute from whatever is at the given traversal step<kind>:<attr>         - First matching kind in a row provides the attribute<attr>                - Show attribute from root node kind*/func (p *eqlRuntimeProvider) initCols() (map[string][]int, map[string][]int, error) {	// Fill lookup maps for traversal kind positions	// Show term match by kind uses these	nodeKindPos := make(map[string][]int)	edgeKindPos := make(map[string][]int)	addPos := func(kmap map[string][]int, kind string, pos int) {		if l, ok := kmap[kind]; ok {			kmap[kind] = append(l, pos)		} else {			kmap[kind] = []int{pos}		}	}	for i, spec := range p.specs {		if i == 0 {			addPos(nodeKindPos, spec, i)		} else {			sspec := strings.Split(spec, ":")			if sspec[1] != "" {				addPos(edgeKindPos, sspec[1], i)			}			if sspec[3] != "" {				addPos(nodeKindPos, sspec[3], i)			}		}	}	// Fill up column lists	if p.show == nil || len(p.show.Children) == 0 {		// If no show clause is defined ask the NodeInfo to provide a summary list		for i, spec := range p.specs {			sspec := strings.Split(spec, ":")			kind := sspec[len(sspec)-1]			for _, attr := range p.ni.SummaryAttributes(kind) {				// Make sure the attribute is in attrsNodes				p.attrsNodes[i][attr] = ""				// Fill col attributes (we only show nodes)				p.colLabels = append(p.colLabels, p.ni.AttributeDisplayString(kind, attr))				p.colFormat = append(p.colFormat, "auto")				p.colData = append(p.colData, fmt.Sprintf("%v:n:%s", i+1, attr))				p.colFunc = append(p.colFunc, nil)			}		}	} else {		var err error		var attr, label, colData string		var pos int		var isNode bool		var colFunc FuncShow		// Go through the elements of the provided show clause		for _, col := range p.show.Children {			if col.Name != parser.NodeSHOWTERM {				return nil, nil, p.newRuntimeError(ErrInvalidConstruct, col.Name, col)			}			// Reset label value			label = ""			colFunc = nil			// Create the correct colData value			if col.Token.ID == parser.TokenAT {				// We have a function get the attribute which it operates on				funcName := col.Children[0].Children[0].Token.Val				funcInst, ok := showFunc[funcName]				if !ok {					return nil, nil, p.newRuntimeError(ErrInvalidConstruct,						"Unknown function: "+funcName, col)				}				colFunc, colData, label, err = funcInst(col.Children[0], p)				if err != nil {					return nil, nil, p.newRuntimeError(ErrInvalidConstruct,						err.Error(), col)				}			} else {				colData = col.Token.Val			}			colDataSplit := strings.SplitN(colData, ":", 3)			switch len(colDataSplit) {			case 1:				// Show attribute from root node kind				attr = colDataSplit[0]				pos = 0				isNode = true				colData = "1:n:" + attr				if label == "" {					label = p.ni.AttributeDisplayString(p.specs[0], attr)				}			case 2:				// First matching kind in a row provides the attribute				kind := colDataSplit[0]				if poslist, ok := nodeKindPos[kind]; ok {					attr = colDataSplit[1]					pos = poslist[0]					isNode = true					colData = fmt.Sprint(pos+1) + ":n:" + attr				} else if poslist, ok := edgeKindPos[kind]; ok {					attr = colDataSplit[1]					pos = poslist[0]					isNode = false					colData = fmt.Sprint(pos+1) + ":e:" + attr				} else {					return nil, nil, p.newRuntimeError(ErrInvalidConstruct,						"Cannot determine data position for kind: "+kind, col)				}				if label == "" {					label = p.ni.AttributeDisplayString(kind, attr)				}			case 3:				// Attribute from whatever is at the given traversal step				attr = colDataSplit[2]				pos, err = strconv.Atoi(colDataSplit[0])				if err != nil {					return nil, nil, p.newRuntimeError(ErrInvalidConstruct,						"Invalid data index: "+colData+" ("+err.Error()+")", col)				} else if pos < 1 {					return nil, nil, p.newRuntimeError(ErrInvalidConstruct,						"Invalid data index: "+colData+" (index must be greater than 0)", col)				}				pos--				if colDataSplit[1] == "n" {					isNode = true				} else if colDataSplit[1] == "e" {					isNode = false				} else {					return nil, nil, p.newRuntimeError(ErrInvalidConstruct,						"Invalid data source '"+colDataSplit[1]+"' (either n - Node or e - Edge)", col)				}				if label == "" {					label = p.ni.AttributeDisplayString("", attr)				}			}			if pos >= len(p.attrsNodes) {				return nil, nil, p.newRuntimeError(ErrInvalidColData,					fmt.Sprintf("Data index out of range: %v", pos+1), col)			}			// Determine label and format			colLabel := label			colFormat := "auto"			for _, t := range col.Children {				if t.Name == parser.NodeAS {					colLabel = t.Children[0].Token.Val				} else if t.Name == parser.NodeFORMAT {					colFormat = t.Children[0].Token.Val				} else if t.Name != parser.NodeFUNC {					return nil, nil, p.newRuntimeError(ErrInvalidConstruct, t.Name, t)				}			}			// Fill col attributes			p.colLabels = append(p.colLabels, colLabel)			p.colFormat = append(p.colFormat, colFormat)			p.colData = append(p.colData, colData)			p.colFunc = append(p.colFunc, colFunc)			// Populate attrsNodes and attrsEdges			if isNode {				p.attrsNodes[pos][attr] = ""			} else {				p.attrsEdges[pos][attr] = ""			}		}	}	return nodeKindPos, edgeKindPos, nil}/*next advances to the next query row. Returns false if no more rows are available.It is assumed that all traversal specs and query attrs have been filled.*/func (p *eqlRuntimeProvider) next() (bool, error) {	// Create fetch lists if it is the first next() call	if p._attrsNodesFetch == nil {		makeFetchList := func(attrs []map[string]string, isEdge bool) [][]string {			var fetchlist [][]string			for _, attrs := range attrs {				var attrsFetch []string				for attr := range attrs {					// Condition needs to be different for nodes and edges					if !isEdge && attr != "" && attr != data.NodeKey && attr != data.NodeKind {						attrsFetch = append(attrsFetch, attr)					} else if attr != "" && attr != data.NodeKey && attr != data.NodeKind &&						attr != data.EdgeEnd1Key && attr != data.EdgeEnd1Kind &&						attr != data.EdgeEnd1Role && attr != data.EdgeEnd1Cascading &&						attr != data.EdgeEnd2Key && attr != data.EdgeEnd2Kind &&						attr != data.EdgeEnd2Role && attr != data.EdgeEnd2Cascading {						attrsFetch = append(attrsFetch, attr)					}				}				fetchlist = append(fetchlist, attrsFetch)			}			return fetchlist		}		p._attrsNodesFetch = makeFetchList(p.attrsNodes, false)		p._attrsEdgesFetch = makeFetchList(p.attrsEdges, true)	}	// Make sure we have the row and rowEdge arrays	if p.rowNode == nil {		p.rowNode = make([]data.Node, 0)		p.rowEdge = make([]data.Edge, 0)	}	// Check if a traversal can handle the call	for _, child := range p.traversals {		childRuntime := child.Runtime.(*traversalRuntime)		if childRuntime.hasMoreNodes() {			_, err := childRuntime.Eval()			return err == nil, err		}	}	// Get next root node	startKey, err := p.nextStartKey()	if err != nil || startKey == "" {		return false, err	}	// Fetch node - always require the key attribute	// to make sure we get a node back if it exists	node, err := p.gm.FetchNodePart(p.part, startKey, p.specs[0],		append(p._attrsNodesFetch[0], "key"))	if err != nil || node == nil {		return false, err	}	// Decide if this node should be added	addNode := true	if p.where != nil {		res, err := p.where.Runtime.(CondRuntime).CondEval(node, nil)		if err != nil {			return false, err		}		addNode = res.(bool)	}	if addNode {		// Add node and the first traversal		if len(p.rowNode) == 0 {			p.rowNode = append(p.rowNode, node)			p.rowEdge = append(p.rowEdge, nil)		} else {			// Clear out the row			for i := range p.rowNode {				p.rowNode[i] = nil				p.rowEdge[i] = nil			}			// Fill in the first node			p.rowNode[0] = node			p.rowEdge[0] = nil		}		// Give the new source to the children and let them evaluate		for _, child := range p.traversals {			childRuntime := child.Runtime.(*traversalRuntime)			if err := childRuntime.newSource(node); err == ErrEmptyTraversal {				// If an empty traversal error comes back advance until				// there is an element or the end				p.rowNode[0] = nil				p.rowEdge[0] = nil				return p.next()			} else if err != nil {				return false, err			}		}	} else {		// Recursively call next until there is a condition-matching node or		// there are no more start keys available		return p.next()	}	return true, nil}/*Instance function for general components*/type generalInst func(*eqlRuntimeProvider, *parser.ASTNode) parser.Runtime/*Runtime map for general components*/var generalProviderMap = map[string]generalInst{	parser.NodeEOF:      invalidRuntimeInst,	parser.NodeVALUE:    valueRuntimeInst,	parser.NodeTRUE:     valueRuntimeInst,	parser.NodeFALSE:    valueRuntimeInst,	parser.NodeNULL:     valueRuntimeInst,	parser.NodeLIST:     valueRuntimeInst,	parser.NodeFUNC:     valueRuntimeInst,	parser.NodeTRAVERSE: traversalRuntimeInst,	parser.NodeWHERE:    whereRuntimeInst,	// Condition components	// ====================	parser.NodeEQ:  equalRuntimeInst,	parser.NodeNEQ: notEqualRuntimeInst,	parser.NodeLT:  lessThanRuntimeInst,	parser.NodeLEQ: lessThanEqualsRuntimeInst,	parser.NodeGT:  greaterThanRuntimeInst,	parser.NodeGEQ: greaterThanEqualsRuntimeInst,	parser.NodeNOT: notRuntimeInst,	parser.NodeAND: andRuntimeInst,	parser.NodeOR:  orRuntimeInst,	// Simple arithmetic expressions	parser.NodePLUS:   plusRuntimeInst,	parser.NodeMINUS:  minusRuntimeInst,	parser.NodeTIMES:  timesRuntimeInst,	parser.NodeDIV:    divRuntimeInst,	parser.NodeMODINT: modIntRuntimeInst,	parser.NodeDIVINT: divIntRuntimeInst,	// List operations	parser.NodeIN:    inRuntimeInst,	parser.NodeNOTIN: notInRuntimeInst,	// String operations	parser.NodeLIKE:        likeRuntimeInst,	parser.NodeCONTAINS:    containsRuntimeInst,	parser.NodeCONTAINSNOT: containsNotRuntimeInst,	parser.NodeBEGINSWITH:  beginsWithRuntimeInst,	parser.NodeENDSWITH:    endsWithRuntimeInst,}
 |