From 5e585dd347f8c52f8c93eda5ceb30471d1e04e4c Mon Sep 17 00:00:00 2001 From: dimgigov Date: Tue, 12 May 2026 23:21:29 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20deadlock=20detector,=20graph/query/lexer?= =?UTF-8?q?=20=D0=B1=D1=8A=D0=B3=D0=BE=D0=B2=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Поправени проблеми: - 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-а. --- src/barabadb/core/deadlock.nim | 7 ++++++ src/barabadb/graph/cypher.nim | 4 ++++ src/barabadb/graph/engine.nim | 4 +++- src/barabadb/query/executor.nim | 41 ++++++++++++++++++++++++++++++--- src/barabadb/query/lexer.nim | 3 +++ 5 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/barabadb/core/deadlock.nim b/src/barabadb/core/deadlock.nim index 7bb7d10..46b3314 100644 --- a/src/barabadb/core/deadlock.nim +++ b/src/barabadb/core/deadlock.nim @@ -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: diff --git a/src/barabadb/graph/cypher.nim b/src/barabadb/graph/cypher.nim index e859e42..92b89c3 100644 --- a/src/barabadb/graph/cypher.nim +++ b/src/barabadb/graph/cypher.nim @@ -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 diff --git a/src/barabadb/graph/engine.nim b/src/barabadb/graph/engine.nim index ada118e..f320a5c 100644 --- a/src/barabadb/graph/engine.nim +++ b/src/barabadb/graph/engine.nim @@ -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() diff --git a/src/barabadb/query/executor.nim b/src/barabadb/query/executor.nim index 852c8d7..5288b07 100644 --- a/src/barabadb/query/executor.nim +++ b/src/barabadb/query/executor.nim @@ -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.. 0: let eqPos = part.find('=') if eqPos >= 0: let k = part[0..