package exec
import (
chickpeas "github.com/freeeve/gochickpeas"
"github.com/freeeve/gochickpeas/gql/internal/eval"
"github.com/freeeve/gochickpeas/gql/internal/graph"
"github.com/freeeve/gochickpeas/gql/internal/plan"
"github.com/freeeve/gochickpeas/gql/value"
)
type rowSink interface {
push(row []value .Value ) bool
close()
}
const (
arenaChunkValues = 16384
arenaChunkStart = 512
)
type rowArena struct {
width int
chunkValues int
nextChunk int
chunk []value .Value
off int
}
func (a *rowArena ) alloc () []value .Value {
if a .off +a .width > len (a .chunk ) {
cv := a .chunkValues
if cv == 0 {
if a .nextChunk == 0 {
a .nextChunk = arenaChunkStart
}
cv = a .nextChunk
if a .nextChunk < arenaChunkValues {
a .nextChunk *= 2
}
}
n := max (cv , a .width )
a .chunk = make ([]value .Value , n )
a .off = 0
}
r := a .chunk [a .off : a .off +a .width : a .off +a .width ]
a .off += a .width
return r
}
func (a *rowArena ) rollback () {
a .off -= a .width
}
func (a *rowArena ) copyRow (row []value .Value ) []value .Value {
r := a .alloc ()
copy (r , row )
return r
}
type matchSink struct {
ctx *eval .Ctx
stage *plan .MatchStage
comp *stageComp
slots map [string ]int
buf, orig []value .Value
scratch genScratch
next rowSink
opRows []uint64
profScratch []uint64
fired bool
pathFilters []RowEval
uniq *uniqEnv
emitFn func ([]value .Value ) bool
}
func (m *matchSink ) push (row []value .Value ) bool {
for i := range m .comp .gates {
if !m .comp .gates [i ].pass (row ) {
return true
}
}
copy (m .buf , row )
if m .stage .Optional {
copy (m .orig , row )
m .fired = false
more := genMatches (m .ctx , m .comp .ops , m .buf , m .comp , m .slots , m .uniq , m .emitFn , &m .scratch , m .profRows ())
m .scatterProf ()
if more && !m .fired {
return m .forward (m .orig )
}
return more
}
more := genMatches (m .ctx , m .comp .ops , m .buf , m .comp , m .slots , m .uniq , m .emitFn , &m .scratch , m .profRows ())
m .scatterProf ()
return more
}
func (m *matchSink ) profRows () []uint64 {
if m .opRows == nil || m .comp .profMap == nil {
return m .opRows
}
if m .profScratch == nil {
m .profScratch = make ([]uint64 , len (m .comp .ops )+1 )
}
clear (m .profScratch )
return m .profScratch
}
func (m *matchSink ) scatterProf () {
if m .opRows == nil || m .comp .profMap == nil {
return
}
for e , orig := range m .comp .profMap {
m .opRows [orig ] += m .profScratch [e ]
}
m .opRows [len (m .opRows )-1 ] += m .profScratch [len (m .profScratch )-1 ]
}
func (m *matchSink ) emit (r []value .Value ) bool {
m .fired = true
return m .forward (r )
}
func (m *matchSink ) forward (r []value .Value ) bool {
if pb := m .stage .PathBind ; pb != nil {
rels := pathRelPositionsOf (r [pb .RelsSlot ])
var nodes []graph .NodeID
if start , ok := r [pb .FromSlot ].AsNode (); ok {
nodes = reconstructPathNodes (m .ctx , start , rels )
}
r [pb .PathSlot ] = value .Path (nodes , rels )
for _ , f := range m .pathFilters {
if !f .Eval (m .ctx , r , m .slots ).IsTruthy () {
return true
}
}
}
return m .next .push (r )
}
func (m *matchSink ) close () { m .next .close () }
type unwindSink struct {
ctx *eval .Ctx
list RowEval
slots map [string ]int
out int
buf []value .Value
next rowSink
count *uint64
}
func (u *unwindSink ) push (row []value .Value ) bool {
v := u .list .Eval (u .ctx , row , u .slots )
if items , ok := v .AsList (); ok {
for _ , item := range items {
copy (u .buf , row )
u .buf [u .out ] = item
u .bump ()
if !u .next .push (u .buf ) {
return false
}
}
return true
}
if v .IsNull () {
return true
}
copy (u .buf , row )
u .buf [u .out ] = v
u .bump ()
return u .next .push (u .buf )
}
func (u *unwindSink ) bump () {
if u .count != nil {
*u .count ++
}
}
func (u *unwindSink ) close () { u .next .close () }
type callSink struct {
ctx *eval .Ctx
cs *plan .CallStage
values []value .Value
hits func (yield func (graph .NodeID ) bool )
prop []chickpeas .PropagateResult
argEvals []RowEval
slots map [string ]int
g *chickpeas .Snapshot
native bool
buf []value .Value
next rowSink
count *uint64
}
func (c *callSink ) push (row []value .Value ) bool {
if !c .native {
c .bump ()
return c .next .push (row )
}
values , hits , prop := c .values , c .hits , c .prop
if c .argEvals != nil {
args := make ([]value .Value , len (c .argEvals ))
for i , ev := range c .argEvals {
args [i ] = ev .Eval (c .ctx , row , c .slots )
}
proc , err := plan .ResolveCallProc (c .cs .ProcName , args )
if err != nil {
return true
}
values , hits , prop = callResults (&proc , c .g )
}
if c .cs .Proc .Kind == plan .ProcPropagate {
for _ , r := range prop {
copy (c .buf , row )
if c .cs .NodeSlot != plan .NoSlot {
c .buf [c .cs .NodeSlot ] = value .Node (r .Node )
}
if c .cs .ValueSlot != plan .NoSlot {
c .buf [c .cs .ValueSlot ] = value .Float (r .Value )
}
if c .cs .DepthSlot != plan .NoSlot {
c .buf [c .cs .DepthSlot ] = value .Int (int64 (r .Depth ))
}
c .bump ()
if !c .next .push (c .buf ) {
return false
}
}
return true
}
if values != nil {
for i , v := range values {
if !c .g .NodeExists (chickpeas .NodeID (i )) {
continue
}
copy (c .buf , row )
if c .cs .NodeSlot != plan .NoSlot {
c .buf [c .cs .NodeSlot ] = value .Node (graph .NodeID (i ))
}
if c .cs .ValueSlot != plan .NoSlot {
c .buf [c .cs .ValueSlot ] = v
}
c .bump ()
if !c .next .push (c .buf ) {
return false
}
}
return true
}
if hits == nil {
return true
}
more := true
hits (func (nid graph .NodeID ) bool {
copy (c .buf , row )
if c .cs .NodeSlot != plan .NoSlot {
c .buf [c .cs .NodeSlot ] = value .Node (nid )
}
c .bump ()
more = c .next .push (c .buf )
return more
})
return more
}
func (c *callSink ) bump () {
if c .count != nil {
*c .count ++
}
}
func (c *callSink ) close () { c .next .close () }
type subquerySink struct {
ctx *eval .Ctx
cs *plan .CallSubqueryStage
buf []value .Value
seed []value .Value
subRows [][]value .Value
subRun bool
next rowSink
count *uint64
}
func (s *subquerySink ) push (row []value .Value ) bool {
var sub [][]value .Value
if len (s .cs .ImportSlots ) == 0 {
if !s .subRun {
s .subRows = runSubplan (s .ctx , s .cs .Sub , nil )
s .subRun = true
}
sub = s .subRows
} else {
for i , slot := range s .cs .ImportSlots {
s .seed [i ] = row [slot ]
}
sub = runSubplan (s .ctx , s .cs .Sub , s .seed )
}
for _ , sr := range sub {
copy (s .buf , row )
for i , slot := range s .cs .OutSlots {
s .buf [slot ] = sr [i ]
}
if s .count != nil {
*s .count ++
}
if !s .next .push (s .buf ) {
return false
}
}
return true
}
func (s *subquerySink ) close () { s .next .close () }
type spSink struct {
ctx *eval .Ctx
sp *plan .SpStage
rows [][]value .Value
arena rowArena
next rowSink
count *uint64
}
func (s *spSink ) push (row []value .Value ) bool {
s .rows = append (s .rows , s .arena .copyRow (row ))
return true
}
func (s *spSink ) close () {
out := runSPStage (s .ctx , s .sp , s .rows )
if s .count != nil {
*s .count = uint64 (len (out ))
}
for _ , r := range out {
if !s .next .push (r ) {
break
}
}
s .next .close ()
}
type gateSink struct {
ctx *eval .Ctx
gs *plan .GateStage
slots map [string ]int
derived []RowEval
where RowEval
rows [][]value .Value
arena rowArena
next rowSink
count *uint64
}
func (s *gateSink ) push (row []value .Value ) bool {
s .rows = append (s .rows , s .arena .copyRow (row ))
return true
}
func (s *gateSink ) close () {
out := runSPStage (s .ctx , &s .gs .Sp , s .rows )
for _ , r := range out {
for i , d := range s .gs .Derived {
r [d .Slot ] = s .derived [i ].Eval (s .ctx , r , s .slots )
}
if !s .where .Eval (s .ctx , r , s .slots ).IsTruthy () {
continue
}
if s .count != nil {
*s .count ++
}
if !s .next .push (r ) {
break
}
}
s .next .close ()
}
The pages are generated with Golds v0.8.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .