fix: deadlock detector, graph/query/lexer бъгове
Поправени проблеми: - Deadlock detector: cycle reconstruction вече проверява пълен цикъл - Graph cypher: executeCypher acquire-ва lock при четене - Graph engine: loadFromFile acquire-ва lock при зареждане - Graph engine: lock поле е exported за външен достъп - Query: parseRowData поддържа escape-ване на , и = в стойности - Query: execUpdateRow escape-ва стойности при сериализация - Lexer: readNumber спира при втора точка (1.2.3 вече не е валиден float) - Lexer: skipBlockComment хвърля ValueError при незатворен коментар 292 теста, 0 failure-а.
This commit is contained in:
@@ -73,7 +73,14 @@ proc detectCycle*(dd: DeadlockDetector): seq[uint64] =
|
||||
while parent.getOrDefault(current, 0'u64) != neighbor and
|
||||
parent.getOrDefault(current, 0'u64) != 0:
|
||||
current = parent[current]
|
||||
if current == 0: break
|
||||
cycle.add(current)
|
||||
# Verify we actually closed the cycle back to neighbor
|
||||
if cycle[^1] != neighbor and parent.getOrDefault(cycle[^1], 0'u64) == neighbor:
|
||||
cycle.add(neighbor)
|
||||
elif cycle[^1] != neighbor:
|
||||
# Incomplete cycle — should not happen with valid parent chain
|
||||
return @[]
|
||||
cycle.reverse()
|
||||
return cycle
|
||||
if neighbor notin visited:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
## Cypher-like Graph Query Extension for BaraQL
|
||||
import std/tables
|
||||
import std/strutils
|
||||
import std/locks
|
||||
import engine
|
||||
|
||||
type
|
||||
@@ -179,6 +180,9 @@ proc executeCypher*(g: Graph, query: CypherQuery): CypherResult =
|
||||
if query.pattern.nodes.len == 0:
|
||||
return
|
||||
|
||||
acquire(g.lock)
|
||||
defer: release(g.lock)
|
||||
|
||||
# Basic MATCH execution
|
||||
if query.kind == "MATCH":
|
||||
# For each node matching the pattern, collect results
|
||||
|
||||
@@ -38,7 +38,7 @@ type
|
||||
reverseAdj*: Table[NodeId, seq[AdjacencyEntry]] # incoming
|
||||
nextNodeId: uint64
|
||||
nextEdgeId: uint64
|
||||
lock: Lock
|
||||
lock*: Lock
|
||||
|
||||
proc `==`*(a, b: EdgeId): bool = uint64(a) == uint64(b)
|
||||
proc `==`*(a, b: NodeId): bool = uint64(a) == uint64(b)
|
||||
@@ -352,6 +352,7 @@ proc loadFromFile*(path: string): Graph =
|
||||
lock: Lock(),
|
||||
)
|
||||
initLock(result.lock)
|
||||
acquire(result.lock)
|
||||
|
||||
for i in 0 ..< nodeCount:
|
||||
let id = NodeId(s.readUint64())
|
||||
@@ -385,4 +386,5 @@ proc loadFromFile*(path: string): Graph =
|
||||
result.reverseAdj[dst].add(AdjacencyEntry(edgeId: id, neighbor: src,
|
||||
weight: weight, label: label))
|
||||
|
||||
release(result.lock)
|
||||
s.close()
|
||||
|
||||
@@ -406,14 +406,49 @@ proc getValue(values: seq[string], fields: seq[string], colName: string): string
|
||||
proc isNull*(value: string): bool =
|
||||
value.len == 0 or value.toLower() == "null"
|
||||
|
||||
proc escapeRowVal(v: string): string =
|
||||
v.replace("\\", "\\\\").replace(",", "\\,").replace("=", "\\=")
|
||||
|
||||
proc unescapeRowVal(v: string): string =
|
||||
result = ""
|
||||
var i = 0
|
||||
while i < v.len:
|
||||
if v[i] == '\\' and i + 1 < v.len:
|
||||
case v[i+1]
|
||||
of '\\', ',', '=':
|
||||
result &= v[i+1]
|
||||
i += 2
|
||||
continue
|
||||
else: discard
|
||||
result &= v[i]
|
||||
inc i
|
||||
|
||||
proc parseRowData(valStr: string): Table[string, string] =
|
||||
## Parse "col1=val1,col2=val2" into a table
|
||||
result = initTable[string, string]()
|
||||
for part in valStr.split(","):
|
||||
var i = 0
|
||||
var part = ""
|
||||
while i < valStr.len:
|
||||
if valStr[i] == '\\' and i + 1 < valStr.len:
|
||||
part &= valStr[i]
|
||||
part &= valStr[i+1]
|
||||
i += 2
|
||||
continue
|
||||
if valStr[i] == ',':
|
||||
let eqPos = part.find('=')
|
||||
if eqPos >= 0:
|
||||
let k = part[0..<eqPos].strip()
|
||||
let v = unescapeRowVal(part[eqPos+1..^1].strip())
|
||||
result[k] = v
|
||||
part = ""
|
||||
else:
|
||||
part &= valStr[i]
|
||||
inc i
|
||||
if part.len > 0:
|
||||
let eqPos = part.find('=')
|
||||
if eqPos >= 0:
|
||||
let k = part[0..<eqPos].strip()
|
||||
let v = part[eqPos+1..^1].strip()
|
||||
let v = unescapeRowVal(part[eqPos+1..^1].strip())
|
||||
result[k] = v
|
||||
|
||||
proc executePlan*(ctx: ExecutionContext, plan: IRPlan): seq[Row]
|
||||
@@ -844,7 +879,7 @@ proc execUpdateRow*(ctx: ExecutionContext, table: string, key: string, sets: Tab
|
||||
return 0
|
||||
var parts: seq[string] = @[]
|
||||
for col, val in parsed:
|
||||
parts.add(col & "=" & val)
|
||||
parts.add(col & "=" & escapeRowVal(val))
|
||||
let newVal = parts.join(",")
|
||||
# Update indexes: remove old, insert new
|
||||
for colName in ctx.btrees.keys.toSeq():
|
||||
|
||||
@@ -363,6 +363,7 @@ proc skipBlockComment(l: var Lexer) =
|
||||
discard l.advance()
|
||||
return
|
||||
discard l.advance()
|
||||
raise newException(ValueError, "Unclosed block comment at line " & $l.line & ", col " & $l.col)
|
||||
|
||||
proc readString(l: var Lexer, quote: char): string =
|
||||
result = ""
|
||||
@@ -389,6 +390,8 @@ proc readNumber(l: var Lexer, startLine, startCol: int): Token =
|
||||
var isFloat = false
|
||||
while l.pos < l.input.len and (l.input[l.pos] in Digits or l.input[l.pos] == '.'):
|
||||
if l.input[l.pos] == '.':
|
||||
if isFloat:
|
||||
break # second dot ends the number
|
||||
isFloat = true
|
||||
numStr.add(l.input[l.pos])
|
||||
discard l.advance()
|
||||
|
||||
Reference in New Issue
Block a user