feat: Session 10.3 MCP Server + Session 11 Graph Engine Deep Integration
MCP Server (10.3): - STDIO JSON-RPC 2.0 transport with 3 AI tools: query, vector_search, schema_inspect - Multi-tenant session vars (tenant_id, user_id) with RLS support - Standalone binary: build/baramcp - Tested with all 3 tools + parameterized queries + vector search + schema inspect Graph Engine Deep Integration (Session 11): - CREATE GRAPH / DROP GRAPH DDL support - Graph engine wired to SQL executor via GRAPH_TABLE() function - 7 algorithms: BFS, DFS, PageRank, ShortestPath, Dijkstra, Louvain, Community - INSERT into _nodes/_edges tables auto-syncs with native Graph adjacency lists - Optional MATCH pattern, ALGORITHM, START, END, MAXDEPTH in GRAPH_TABLE syntax - All 340+ existing tests pass
This commit is contained in:
+113
-34
@@ -42,6 +42,8 @@ proc parseOverClause(p: var Parser): Node
|
||||
proc parseFrameSpec(p: var Parser): Node
|
||||
proc parseFrameBoundary(p: var Parser): string
|
||||
proc parseSetVar(p: var Parser): Node
|
||||
proc parseCreateGraph(p: var Parser): Node
|
||||
proc parseDropGraph(p: var Parser): Node
|
||||
|
||||
proc parsePrimary(p: var Parser): Node =
|
||||
let tok = p.peek()
|
||||
@@ -464,60 +466,108 @@ proc parseSelect(p: var Parser): Node =
|
||||
discard p.advance()
|
||||
discard p.expect(tkLParen)
|
||||
let graphName = p.expect(tkIdent).value
|
||||
discard p.expect(tkMatch)
|
||||
# Parse pattern: (node)-[edge]->(node)
|
||||
var hasMatch = p.match(tkMatch)
|
||||
var patternNodes: seq[string]
|
||||
var patternEdges: seq[string]
|
||||
# First node
|
||||
discard p.expect(tkLParen)
|
||||
if p.peek().kind == tkIdent:
|
||||
patternNodes.add(p.advance().value)
|
||||
discard p.expect(tkRParen)
|
||||
# Edge and next node(s)
|
||||
while p.peek().kind == tkMinus or p.peek().kind == tkArrowR:
|
||||
if p.match(tkArrowR):
|
||||
discard
|
||||
elif p.match(tkMinus):
|
||||
if p.peek().kind == tkLBracket:
|
||||
if hasMatch:
|
||||
# First node
|
||||
discard p.expect(tkLParen)
|
||||
if p.peek().kind == tkIdent:
|
||||
patternNodes.add(p.advance().value)
|
||||
discard p.expect(tkRParen)
|
||||
# Edge and next node(s)
|
||||
while p.peek().kind == tkMinus or p.peek().kind == tkArrowR:
|
||||
if p.match(tkArrowR):
|
||||
discard
|
||||
elif p.match(tkMinus):
|
||||
if p.peek().kind == tkLBracket:
|
||||
discard p.advance()
|
||||
if p.peek().kind == tkIdent:
|
||||
patternEdges.add(p.advance().value)
|
||||
discard p.expect(tkRBracket)
|
||||
discard p.expect(tkArrowR)
|
||||
else:
|
||||
discard
|
||||
if p.peek().kind == tkLParen:
|
||||
discard p.advance()
|
||||
if p.peek().kind == tkIdent:
|
||||
patternEdges.add(p.advance().value)
|
||||
discard p.expect(tkRBracket)
|
||||
discard p.expect(tkArrowR)
|
||||
else:
|
||||
discard
|
||||
if p.peek().kind == tkLParen:
|
||||
discard p.advance()
|
||||
if p.peek().kind == tkIdent:
|
||||
patternNodes.add(p.advance().value)
|
||||
discard p.expect(tkRParen)
|
||||
patternNodes.add(p.advance().value)
|
||||
discard p.expect(tkRParen)
|
||||
# COLUMNS (col1, col2, ...)
|
||||
var algo = "bfs"
|
||||
var maxDepth = -1
|
||||
var startId = ""
|
||||
var endId = ""
|
||||
|
||||
let startVar = if patternNodes.len > 0: patternNodes[0] else: ""
|
||||
let endVar = if patternNodes.len > 1: patternNodes[1] else: ""
|
||||
|
||||
if p.peek().kind == tkIdent and p.peek().value.toLower() == "algorithm":
|
||||
discard p.advance()
|
||||
algo = p.advance().value.toLower()
|
||||
|
||||
if p.peek().kind == tkIdent and p.peek().value.toLower() == "start":
|
||||
discard p.advance()
|
||||
if p.peek().kind == tkIntLit:
|
||||
startId = p.advance().value
|
||||
elif p.peek().kind == tkIdent:
|
||||
startId = p.advance().value
|
||||
|
||||
if p.peek().kind == tkIdent and p.peek().value.toLower() == "end" or p.peek().kind == tkEnd:
|
||||
discard p.advance()
|
||||
if p.peek().kind == tkIntLit:
|
||||
endId = p.advance().value
|
||||
elif p.peek().kind == tkIdent:
|
||||
endId = p.advance().value
|
||||
|
||||
if p.peek().kind == tkIdent and p.peek().value.toLower() == "maxdepth":
|
||||
discard p.advance()
|
||||
if p.peek().kind == tkIntLit:
|
||||
maxDepth = parseInt(p.advance().value)
|
||||
|
||||
var returnCols: seq[string]
|
||||
if p.match(tkColumns):
|
||||
discard p.expect(tkLParen)
|
||||
if p.peek().kind == tkIdent:
|
||||
if p.peek().kind in {tkIdent, tkLabels, tkEdge, tkGraph, tkRank, tkEnd, tkMatch, tkColumns, tkSrc, tkDst, tkBfs, tkDfs, tkMerge}:
|
||||
var colName = p.advance().value
|
||||
# Handle dotted names: e.name
|
||||
while p.peek().kind == tkDot:
|
||||
discard p.advance() # skip dot
|
||||
colName &= "." & p.expect(tkIdent).value
|
||||
discard p.advance()
|
||||
if p.peek().kind in {tkIdent, tkLabels, tkEdge, tkGraph, tkRank, tkBfs, tkDfs, tkMatch, tkColumns, tkEnd, tkSrc, tkDst, tkMerge}:
|
||||
colName &= "." & p.advance().value
|
||||
else:
|
||||
colName &= "." & p.expect(tkIdent).value
|
||||
returnCols.add(colName)
|
||||
while p.match(tkComma):
|
||||
if p.peek().kind == tkIdent:
|
||||
if p.peek().kind in {tkIdent, tkLabels, tkEdge, tkGraph, tkRank, tkEnd, tkMatch, tkColumns, tkSrc, tkDst, tkBfs, tkDfs, tkMerge}:
|
||||
colName = p.advance().value
|
||||
while p.peek().kind == tkDot:
|
||||
discard p.advance()
|
||||
colName &= "." & p.expect(tkIdent).value
|
||||
if p.peek().kind in {tkIdent, tkLabels, tkEdge, tkGraph, tkRank, tkBfs, tkDfs, tkMatch, tkColumns, tkEnd, tkSrc, tkDst, tkMerge}:
|
||||
colName &= "." & p.advance().value
|
||||
else:
|
||||
colName &= "." & p.expect(tkIdent).value
|
||||
returnCols.add(colName)
|
||||
if p.match(tkAs):
|
||||
discard p.advance() # skip alias
|
||||
discard p.advance()
|
||||
discard p.expect(tkRParen)
|
||||
discard p.expect(tkRParen)
|
||||
# Create a graph traversal node
|
||||
|
||||
var startNode: Node = nil
|
||||
if startId.len > 0:
|
||||
startNode = Node(kind: nkIntLit, intVal: parseInt(startId))
|
||||
elif patternNodes.len > 0:
|
||||
startNode = Node(kind: nkIdent, identName: patternNodes[0])
|
||||
|
||||
var endNode: Node = nil
|
||||
if endId.len > 0:
|
||||
endNode = Node(kind: nkIntLit, intVal: parseInt(endId))
|
||||
elif patternNodes.len > 1:
|
||||
endNode = Node(kind: nkIdent, identName: patternNodes[1])
|
||||
|
||||
result.selFrom = Node(kind: nkGraphTraversal, gtGraphName: graphName,
|
||||
gtStart: nil, gtEdge: if patternEdges.len > 0: patternEdges[0] else: "",
|
||||
gtDirection: "out", gtEnd: nil, gtMaxDepth: -1,
|
||||
gtReturnCols: returnCols, line: tok.line, col: tok.col)
|
||||
gtStart: startNode, gtEdge: if patternEdges.len > 0: patternEdges[0] else: "",
|
||||
gtDirection: "out", gtEnd: endNode, gtMaxDepth: maxDepth,
|
||||
gtReturnCols: returnCols, gtAlgo: algo, line: tok.line, col: tok.col)
|
||||
else:
|
||||
let tableTok = p.expect(tkIdent)
|
||||
var alias = ""
|
||||
@@ -1542,6 +1592,31 @@ proc parseSetVar(p: var Parser): Node =
|
||||
result = Node(kind: nkSetVar, svName: varName, svValue: valStr,
|
||||
line: tok.line, col: tok.col)
|
||||
|
||||
proc parseCreateGraph(p: var Parser): Node =
|
||||
let tok = p.expect(tkCreate)
|
||||
discard p.expect(tkGraph)
|
||||
var ifNotExists = false
|
||||
if p.peek().kind == tkIdent and p.peek().value.toLower() == "if":
|
||||
discard p.advance()
|
||||
discard p.expect(tkNot)
|
||||
discard p.expect(tkExists)
|
||||
ifNotExists = true
|
||||
let name = p.expect(tkIdent).value
|
||||
result = Node(kind: nkCreateGraph, cgName: name, cgIfNotExists: ifNotExists,
|
||||
line: tok.line, col: tok.col)
|
||||
|
||||
proc parseDropGraph(p: var Parser): Node =
|
||||
let tok = p.expect(tkDrop)
|
||||
discard p.expect(tkGraph)
|
||||
var ifExists = false
|
||||
if p.peek().kind == tkIdent and p.peek().value.toLower() == "if":
|
||||
discard p.advance()
|
||||
discard p.expect(tkExists)
|
||||
ifExists = true
|
||||
let name = p.expect(tkIdent).value
|
||||
result = Node(kind: nkDropGraph, dgName: name, dgIfExists: ifExists,
|
||||
line: tok.line, col: tok.col)
|
||||
|
||||
proc parseStatement*(p: var Parser): Node =
|
||||
case p.peek().kind
|
||||
of tkWith, tkSelect: p.parseSelect()
|
||||
@@ -1568,6 +1643,8 @@ proc parseStatement*(p: var Parser): Node =
|
||||
p.parseCreateUser()
|
||||
elif next.kind == tkPolicy:
|
||||
p.parseCreatePolicy()
|
||||
elif next.kind == tkGraph:
|
||||
p.parseCreateGraph()
|
||||
else:
|
||||
p.parseCreateType()
|
||||
else:
|
||||
@@ -1587,6 +1664,8 @@ proc parseStatement*(p: var Parser): Node =
|
||||
p.parseDropUser()
|
||||
elif next.kind == tkPolicy:
|
||||
p.parseDropPolicy()
|
||||
elif next.kind == tkGraph:
|
||||
p.parseDropGraph()
|
||||
else:
|
||||
let tok = p.advance()
|
||||
Node(kind: nkNullLit, line: tok.line, col: tok.col)
|
||||
|
||||
Reference in New Issue
Block a user