2d310a33a1
Comprehensive bug fix pass across all subsystems: - Critical: lexer infinite loop, WAL lock discipline, checkpoint safety, compaction verification, HMAC key truncation, healthCheck leak - High: MVCC lock safety, B-Tree delete rebalancing, Raft stale term handling, replication socket leaks and ack cleanup, sharding migration with old assignments, 2PC recovery, JWT/SCRAM auth fixes, wire protocol bounds checks - Medium: config error handling, MERGE parser completeness, IR/codegen correctness, UDF bounds checks, LIMIT cost accuracy, mmap safety - Low: timeout handling, float parsing edge cases, uint32 truncation, cache entry cleanup Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
354 lines
9.0 KiB
Nim
354 lines
9.0 KiB
Nim
## BaraQL IR — Intermediate Representation for compilation
|
|
import std/tables
|
|
import std/strutils
|
|
import ../core/types
|
|
|
|
type
|
|
IRTypeKind* = enum
|
|
itkScalar
|
|
itkObject
|
|
itkArray
|
|
itkSet
|
|
itkOptional
|
|
itkFunction
|
|
|
|
IRType* = ref object
|
|
name*: string
|
|
kind*: IRTypeKind
|
|
fields*: Table[string, IRType]
|
|
isNullable*: bool
|
|
elementType*: IRType
|
|
|
|
IROperator* = enum
|
|
irAdd, irSub, irMul, irDiv, irMod, irPow
|
|
irEq, irNeq, irLt, irLte, irGt, irGte
|
|
irAnd, irOr, irNot, irNeg
|
|
irIn, irNotIn
|
|
irLike, irILike
|
|
irBetween
|
|
irIsNull, irIsNotNull
|
|
irFtsMatch
|
|
irDistance
|
|
irJsonContains
|
|
irJsonContainedBy
|
|
irJsonHasAny
|
|
irJsonHasAll
|
|
|
|
IRAggregate* = enum
|
|
irCount, irSum, irAvg, irMin, irMax
|
|
irArrayAgg, irStringAgg
|
|
|
|
IRLiteral* = object
|
|
case kind*: ValueKind
|
|
of vkNull: discard
|
|
of vkBool: boolVal*: bool
|
|
of vkInt64: int64Val*: int64
|
|
of vkFloat64: float64Val*: float64
|
|
of vkString: strVal*: string
|
|
else: discard
|
|
|
|
IRExprKind* = enum
|
|
irekLiteral
|
|
irekField
|
|
irekUnary
|
|
irekBinary
|
|
irekAggregate
|
|
irekFuncCall
|
|
irekCast
|
|
irekConditional
|
|
irekExists
|
|
irekSubquery
|
|
irekStar
|
|
irekJsonPath
|
|
irekWindowFunc
|
|
|
|
IRJoinKind* = enum
|
|
irjkInner
|
|
irjkLeft
|
|
irjkRight
|
|
irjkFull
|
|
irjkCross
|
|
|
|
IRJoinStrategy* = enum
|
|
irjsNestedLoop
|
|
irjsHash
|
|
irjsIndexNestedLoop
|
|
|
|
IRPlanKind* = enum
|
|
irpkScan
|
|
irpkFilter
|
|
irpkProject
|
|
irpkGroupBy
|
|
irpkJoin
|
|
irpkSort
|
|
irpkLimit
|
|
irpkInsert
|
|
irpkUpdate
|
|
irpkDelete
|
|
irpkMerge
|
|
irpkCreateType
|
|
irpkUnion
|
|
irpkCTE
|
|
irpkValues
|
|
irpkExplain
|
|
irpkWindow
|
|
irpkPivot
|
|
irpkUnpivot
|
|
irpkGraphTraversal
|
|
|
|
IRGroupingSetsKind* = enum
|
|
irgskNone
|
|
irgskGroupingSets
|
|
irgskRollup
|
|
irgskCube
|
|
|
|
IRPlan* = ref object
|
|
case kind*: IRPlanKind
|
|
of irpkScan:
|
|
scanTable*: string
|
|
scanAlias*: string
|
|
of irpkFilter:
|
|
filterSource*: IRPlan
|
|
filterCond*: IRExpr
|
|
of irpkProject:
|
|
projectSource*: IRPlan
|
|
projectExprs*: seq[IRExpr]
|
|
projectAliases*: seq[string]
|
|
of irpkGroupBy:
|
|
groupSource*: IRPlan
|
|
groupKeys*: seq[IRExpr]
|
|
groupAggs*: seq[IRExpr]
|
|
groupHaving*: IRExpr
|
|
groupingSetsKind*: IRGroupingSetsKind
|
|
groupingSets*: seq[seq[IRExpr]]
|
|
of irpkJoin:
|
|
joinKind*: IRJoinKind
|
|
joinLeft*: IRPlan
|
|
joinRight*: IRPlan
|
|
joinCond*: IRExpr
|
|
joinAlias*: string
|
|
joinLateral*: bool
|
|
joinStrategy*: IRJoinStrategy
|
|
joinHashCol*: string
|
|
of irpkSort:
|
|
sortSource*: IRPlan
|
|
sortExprs*: seq[IRExpr]
|
|
sortDirs*: seq[bool]
|
|
of irpkLimit:
|
|
limitSource*: IRPlan
|
|
limitCount*: int64
|
|
limitOffset*: int64
|
|
of irpkInsert:
|
|
insertTable*: string
|
|
insertFields*: seq[string]
|
|
insertValues*: seq[seq[IRExpr]]
|
|
of irpkUpdate:
|
|
updateTable*: string
|
|
updateAlias*: string
|
|
updateSets*: seq[(string, IRExpr)]
|
|
updateSource*: IRPlan
|
|
of irpkDelete:
|
|
deleteTable*: string
|
|
deleteAlias*: string
|
|
deleteSource*: IRPlan
|
|
of irpkMerge:
|
|
mergeTarget*: string
|
|
mergeTargetAlias*: string
|
|
mergeSourcePlan*: IRPlan
|
|
mergeOnCond*: IRExpr
|
|
mergeUpdateSets*: seq[(string, IRExpr)]
|
|
mergeInsertFields*: seq[string]
|
|
mergeInsertValues*: seq[seq[IRExpr]]
|
|
of irpkCreateType:
|
|
createTypeName*: string
|
|
createTypeDef*: IRType
|
|
of irpkUnion:
|
|
unionLeft*: IRPlan
|
|
unionRight*: IRPlan
|
|
unionAll*: bool
|
|
of irpkCTE:
|
|
cteName*: string
|
|
cteQuery*: IRPlan
|
|
cteMain*: IRPlan
|
|
of irpkValues:
|
|
valuesRows*: seq[seq[IRExpr]]
|
|
of irpkExplain:
|
|
explainPlan*: IRPlan
|
|
of irpkWindow:
|
|
winSource*: IRPlan
|
|
winFuncs*: seq[IRExpr]
|
|
winPartition*: seq[IRExpr]
|
|
winOrderBy*: seq[IRExpr]
|
|
winOrderDirs*: seq[bool]
|
|
winFrameMode*: string
|
|
winFrameStart*: string
|
|
winFrameEnd*: string
|
|
of irpkPivot:
|
|
pivotSource*: IRPlan
|
|
pivotAgg*: IRExpr
|
|
pivotForCol*: string
|
|
pivotInValues*: seq[string]
|
|
of irpkUnpivot:
|
|
unpivotSource*: IRPlan
|
|
unpivotValueCol*: string
|
|
unpivotForCol*: string
|
|
unpivotInCols*: seq[string]
|
|
of irpkGraphTraversal:
|
|
graphName*: string
|
|
graphAlgo*: string # "bfs", "dfs", "shortest", "pagerank"
|
|
graphStartNode*: string
|
|
graphEndNode*: string
|
|
graphEdgeLabel*: string
|
|
graphMaxDepth*: int
|
|
graphFilter*: IRExpr
|
|
graphReturnCols*: seq[string]
|
|
|
|
IRExpr* = ref object
|
|
valueKind*: ValueKind
|
|
case kind*: IRExprKind
|
|
of irekLiteral:
|
|
literal*: IRLiteral
|
|
of irekField:
|
|
fieldPath*: seq[string]
|
|
of irekUnary:
|
|
unOp*: IROperator
|
|
unExpr*: IRExpr
|
|
of irekBinary:
|
|
binOp*: IROperator
|
|
binLeft*: IRExpr
|
|
binRight*: IRExpr
|
|
of irekAggregate:
|
|
aggOp*: IRAggregate
|
|
aggArgs*: seq[IRExpr]
|
|
aggDistinct*: bool
|
|
aggFilter*: IRExpr
|
|
of irekFuncCall:
|
|
irFunc*: string
|
|
irFuncArgs*: seq[IRExpr]
|
|
of irekCast:
|
|
irCastType*: IRType
|
|
irCastExpr*: IRExpr
|
|
of irekConditional:
|
|
cond*: IRExpr
|
|
thenExpr*: IRExpr
|
|
elseExpr*: IRExpr
|
|
of irekExists:
|
|
existsSubquery*: IRPlan
|
|
of irekSubquery:
|
|
subqueryPlan*: IRPlan
|
|
of irekStar:
|
|
discard
|
|
of irekJsonPath:
|
|
jpExpr*: IRExpr
|
|
jpKey*: string
|
|
jpAsText*: bool
|
|
of irekWindowFunc:
|
|
wfName*: string
|
|
wfArgs*: seq[IRExpr]
|
|
wfPartition*: seq[IRExpr]
|
|
wfOrderBy*: seq[IRExpr]
|
|
wfOrderDirs*: seq[bool]
|
|
wfFrameMode*: string
|
|
wfFrameStart*: string
|
|
wfFrameEnd*: string
|
|
|
|
type
|
|
TypeChecker* = ref object
|
|
schemas: Table[string, IRType]
|
|
|
|
proc newTypeChecker*(): TypeChecker =
|
|
TypeChecker(schemas: initTable[string, IRType]())
|
|
|
|
proc registerType*(tc: TypeChecker, name: string, typ: IRType) =
|
|
tc.schemas[name] = typ
|
|
|
|
proc getType*(tc: TypeChecker, name: string): IRType =
|
|
tc.schemas.getOrDefault(name, nil)
|
|
|
|
proc inferExpr*(tc: TypeChecker, expr: IRExpr, context: Table[string, IRType]): IRType =
|
|
case expr.kind
|
|
of irekLiteral:
|
|
case expr.literal.kind
|
|
of vkBool: return IRType(name: "bool", kind: itkScalar)
|
|
of vkInt64: return IRType(name: "int64", kind: itkScalar)
|
|
of vkFloat64: return IRType(name: "float64", kind: itkScalar)
|
|
of vkString: return IRType(name: "str", kind: itkScalar)
|
|
of vkNull: return IRType(name: "null", kind: itkScalar, isNullable: true)
|
|
else: return IRType(name: "unknown", kind: itkScalar)
|
|
of irekField:
|
|
if expr.fieldPath.len == 0:
|
|
return nil
|
|
let rootName = expr.fieldPath[0]
|
|
if rootName in context:
|
|
var current = context[rootName]
|
|
for i in 1..<expr.fieldPath.len:
|
|
if expr.fieldPath[i] in current.fields:
|
|
current = current.fields[expr.fieldPath[i]]
|
|
else:
|
|
return nil
|
|
return current
|
|
return nil
|
|
of irekUnary:
|
|
let operandType = tc.inferExpr(expr.unExpr, context)
|
|
if operandType == nil:
|
|
return nil
|
|
case expr.unOp
|
|
of irNot, irIsNull, irIsNotNull:
|
|
return IRType(name: "bool", kind: itkScalar)
|
|
of irNeg:
|
|
return operandType
|
|
else:
|
|
return nil
|
|
of irekBinary:
|
|
let leftType = tc.inferExpr(expr.binLeft, context)
|
|
let rightType = tc.inferExpr(expr.binRight, context)
|
|
if leftType == nil or rightType == nil:
|
|
return nil
|
|
case expr.binOp
|
|
of irAdd, irSub, irMul, irDiv, irMod, irPow:
|
|
return leftType
|
|
of irEq, irNeq, irLt, irLte, irGt, irGte, irAnd, irOr,
|
|
irIn, irNotIn, irLike, irILike, irBetween:
|
|
return IRType(name: "bool", kind: itkScalar)
|
|
else:
|
|
return nil
|
|
of irekAggregate:
|
|
case expr.aggOp
|
|
of irCount: return IRType(name: "int64", kind: itkScalar)
|
|
of irSum, irAvg: return IRType(name: "float64", kind: itkScalar)
|
|
of irMin, irMax:
|
|
if expr.aggArgs.len > 0:
|
|
return tc.inferExpr(expr.aggArgs[0], context)
|
|
return nil
|
|
of irArrayAgg:
|
|
return IRType(name: "array", kind: itkArray)
|
|
of irStringAgg:
|
|
return IRType(name: "text", kind: itkScalar)
|
|
of irekFuncCall:
|
|
return IRType(name: "unknown", kind: itkScalar)
|
|
of irekCast:
|
|
return expr.irCastType
|
|
of irekConditional:
|
|
let thenType = tc.inferExpr(expr.thenExpr, context)
|
|
return thenType
|
|
of irekExists:
|
|
return IRType(name: "bool", kind: itkScalar)
|
|
of irekSubquery:
|
|
return IRType(name: "set", kind: itkSet)
|
|
of irekStar:
|
|
return IRType(name: "star", kind: itkScalar)
|
|
of irekJsonPath:
|
|
if expr.jpAsText: return IRType(name: "str", kind: itkScalar)
|
|
return IRType(name: "json", kind: itkScalar)
|
|
of irekWindowFunc:
|
|
# Window functions return int64 for ranking, or the type of the argument for value functions
|
|
case expr.wfName.toLower()
|
|
of "row_number", "rank", "dense_rank", "ntile":
|
|
return IRType(name: "int64", kind: itkScalar)
|
|
of "lead", "lag", "first_value", "last_value":
|
|
if expr.wfArgs.len > 0:
|
|
return tc.inferExpr(expr.wfArgs[0], context)
|
|
return nil
|
|
else:
|
|
return IRType(name: "unknown", kind: itkScalar)
|