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 interpreter
- import (
- "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 without
- resetting state (used for testing)
- */
- var allowMultiEval = false
- // Special flags which can be set by with statements
- type 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 interpreter
- datastructure 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 the
- columns 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 that
- specs 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,
- }
|