fix(raft): distinguish put-with-empty-value from delete in write path
This commit is contained in:
@@ -45,7 +45,7 @@ proc violatesUniqueIndex*(ctx: ExecutionContext, table: string, fields: seq[stri
|
||||
return ""
|
||||
|
||||
proc execInsert*(ctx: ExecutionContext, table: string, fields: seq[string], values: seq[seq[string]],
|
||||
kvPairs: var seq[(string, seq[byte])]): int =
|
||||
kvPairs: var seq[tuple[key: string, value: seq[byte], deleted: bool]]): int =
|
||||
if not hasPrivilege(ctx, table, "INSERT"):
|
||||
return 0
|
||||
let tblDef = if table in ctx.tables: ctx.tables[table] else: TableDef()
|
||||
@@ -87,7 +87,7 @@ proc execInsert*(ctx: ExecutionContext, table: string, fields: seq[string], valu
|
||||
discard ctx.txnManager.write(ctx.pendingTxn, fullKey, cast[seq[byte]](valStr))
|
||||
else:
|
||||
ctx.db.put(fullKey, cast[seq[byte]](valStr))
|
||||
kvPairs.add((fullKey, cast[seq[byte]](valStr)))
|
||||
kvPairs.add((fullKey, cast[seq[byte]](valStr), false))
|
||||
|
||||
for colName in ctx.btrees.keys.toSeq():
|
||||
if colName.startsWith(table & "."):
|
||||
@@ -221,7 +221,7 @@ proc execInsert*(ctx: ExecutionContext, table: string, fields: seq[string], valu
|
||||
return count
|
||||
|
||||
proc execDelete*(ctx: ExecutionContext, table: string, key: string,
|
||||
kvPairs: var seq[(string, seq[byte])]): int =
|
||||
kvPairs: var seq[tuple[key: string, value: seq[byte], deleted: bool]]): int =
|
||||
if not hasPrivilege(ctx, table, "DELETE"):
|
||||
return 0
|
||||
let fullKey = table & "." & key
|
||||
@@ -238,7 +238,7 @@ proc execDelete*(ctx: ExecutionContext, table: string, key: string,
|
||||
discard ctx.txnManager.delete(ctx.pendingTxn, fullKey)
|
||||
else:
|
||||
ctx.db.delete(fullKey)
|
||||
kvPairs.add((fullKey, @[]))
|
||||
kvPairs.add((fullKey, @[], true))
|
||||
# Update BTree indexes
|
||||
for colName in ctx.btrees.keys.toSeq():
|
||||
if colName.startsWith(table & "."):
|
||||
@@ -264,7 +264,7 @@ proc execDelete*(ctx: ExecutionContext, table: string, key: string,
|
||||
return 0
|
||||
|
||||
proc execUpdateRow*(ctx: ExecutionContext, table: string, key: string, sets: Table[string, string],
|
||||
kvPairs: var seq[(string, seq[byte])]): int =
|
||||
kvPairs: var seq[tuple[key: string, value: seq[byte], deleted: bool]]): int =
|
||||
if not hasPrivilege(ctx, table, "UPDATE"):
|
||||
return 0
|
||||
let fullKey = table & "." & key
|
||||
@@ -313,7 +313,7 @@ proc execUpdateRow*(ctx: ExecutionContext, table: string, key: string, sets: Tab
|
||||
discard ctx.txnManager.write(ctx.pendingTxn, fullKey, cast[seq[byte]](newVal))
|
||||
else:
|
||||
ctx.db.put(fullKey, cast[seq[byte]](newVal))
|
||||
kvPairs.add((fullKey, cast[seq[byte]](newVal)))
|
||||
kvPairs.add((fullKey, cast[seq[byte]](newVal), false))
|
||||
# Update FTS indexes: remove old doc, add new
|
||||
for ftsKey, ftsIdx in ctx.ftsIndexes:
|
||||
if ftsKey.startsWith(table & "."):
|
||||
|
||||
@@ -923,7 +923,6 @@ proc evalExprOld*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContex
|
||||
ddl.add("\n")
|
||||
|
||||
# Sample data
|
||||
var kvPairs: seq[(string, seq[byte])] = @[]
|
||||
let rows = requireExecScanHook()(ctx, table)
|
||||
let sampleLimit = min(5, rows.len)
|
||||
if sampleLimit > 0:
|
||||
|
||||
@@ -30,14 +30,14 @@ proc enforceFkOnDelete*(ctx: ExecutionContext, parentTable: string, parentCol: s
|
||||
of "CASCADE":
|
||||
for refRow in refs:
|
||||
if "$key" in refRow:
|
||||
var dummy: seq[(string, seq[byte])] = @[]
|
||||
var dummy: seq[tuple[key: string, value: seq[byte], deleted: bool]] = @[]
|
||||
discard execDelete(ctx, childTblName, valueToString(refRow["$key"]), dummy)
|
||||
of "SET NULL":
|
||||
for refRow in refs:
|
||||
if "$key" in refRow:
|
||||
var sets = initTable[string, string]()
|
||||
sets[col.name] = "\\N"
|
||||
var dummy: seq[(string, seq[byte])] = @[]
|
||||
var dummy: seq[tuple[key: string, value: seq[byte], deleted: bool]] = @[]
|
||||
discard execUpdateRow(ctx, childTblName, valueToString(refRow["$key"]), sets, dummy)
|
||||
of "RESTRICT", "NO ACTION":
|
||||
return (false, "FOREIGN KEY violation: row is referenced by " & childTblName & "." & col.name)
|
||||
@@ -56,14 +56,14 @@ proc enforceFkOnUpdate*(ctx: ExecutionContext, parentTable: string, parentCol: s
|
||||
if "$key" in refRow:
|
||||
var sets = initTable[string, string]()
|
||||
sets[col.name] = newVal
|
||||
var dummy: seq[(string, seq[byte])] = @[]
|
||||
var dummy: seq[tuple[key: string, value: seq[byte], deleted: bool]] = @[]
|
||||
discard execUpdateRow(ctx, childTblName, valueToString(refRow["$key"]), sets, dummy)
|
||||
of "SET NULL":
|
||||
for refRow in refs:
|
||||
if "$key" in refRow:
|
||||
var sets = initTable[string, string]()
|
||||
sets[col.name] = "\\N"
|
||||
var dummy: seq[(string, seq[byte])] = @[]
|
||||
var dummy: seq[tuple[key: string, value: seq[byte], deleted: bool]] = @[]
|
||||
discard execUpdateRow(ctx, childTblName, valueToString(refRow["$key"]), sets, dummy)
|
||||
of "RESTRICT", "NO ACTION":
|
||||
return (false, "FOREIGN KEY violation: row is referenced by " & childTblName & "." & col.name)
|
||||
|
||||
@@ -136,13 +136,13 @@ type
|
||||
rows*: seq[Row]
|
||||
affectedRows*: int
|
||||
message*: string
|
||||
keyValuePairs*: seq[(string, seq[byte])]
|
||||
keyValuePairs*: seq[tuple[key: string, value: seq[byte], deleted: bool]]
|
||||
|
||||
proc `==`*(a, b: IndexEntry): bool =
|
||||
a.lsmKey == b.lsmKey and a.rowValue == b.rowValue
|
||||
|
||||
proc okResult*(rows: seq[Row] = @[], cols: seq[string] = @[], affected: int = 0, msg: string = "",
|
||||
kvPairs: seq[(string, seq[byte])] = @[]): ExecResult =
|
||||
kvPairs: seq[tuple[key: string, value: seq[byte], deleted: bool]] = @[]): ExecResult =
|
||||
ExecResult(success: true, columns: cols, rows: rows, affectedRows: affected, message: msg,
|
||||
keyValuePairs: kvPairs)
|
||||
|
||||
|
||||
@@ -574,7 +574,7 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
||||
row[f] = mutableValues[0][i]
|
||||
fireTriggers(ctx, stmt.insTarget, "before", "insert", row)
|
||||
|
||||
var kvPairs: seq[(string, seq[byte])]
|
||||
var kvPairs: seq[tuple[key: string, value: seq[byte], deleted: bool]]
|
||||
let count = execInsert(ctx, stmt.insTarget, mutableFields, mutableValues, kvPairs)
|
||||
|
||||
# Fire AFTER INSERT triggers
|
||||
@@ -626,7 +626,7 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
||||
# Scan and apply
|
||||
let rows = execScan(ctx, stmt.updTarget)
|
||||
var count = 0
|
||||
var kvPairs: seq[(string, seq[byte])]
|
||||
var kvPairs: seq[tuple[key: string, value: seq[byte], deleted: bool]]
|
||||
for row in rows:
|
||||
# Compute sets for this row (expressions may reference columns)
|
||||
var sets = initTable[string, string]()
|
||||
@@ -701,7 +701,7 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
||||
# Delete all rows matching WHERE
|
||||
let rows = execScan(ctx, stmt.delTarget)
|
||||
var count = 0
|
||||
var kvPairs: seq[(string, seq[byte])]
|
||||
var kvPairs: seq[tuple[key: string, value: seq[byte], deleted: bool]]
|
||||
for row in rows:
|
||||
if stmt.delWhere != nil and stmt.delWhere.whereExpr != nil:
|
||||
let whereExpr = lowerExpr(stmt.delWhere.whereExpr)
|
||||
@@ -744,7 +744,7 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
||||
|
||||
let targetRows = execScan(ctx, stmt.mergeTarget)
|
||||
var count = 0
|
||||
var kvPairs: seq[(string, seq[byte])]
|
||||
var kvPairs: seq[tuple[key: string, value: seq[byte], deleted: bool]]
|
||||
|
||||
for srcRow in sourceRows:
|
||||
var matched = false
|
||||
@@ -793,7 +793,7 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
||||
for i, f in fields:
|
||||
if i < values.len: row[f] = Value(kind: vkString, strVal: values[i])
|
||||
fireTriggers(ctx, stmt.mergeTarget, "before", "insert", row)
|
||||
var insKvPairs: seq[(string, seq[byte])]
|
||||
var insKvPairs: seq[tuple[key: string, value: seq[byte], deleted: bool]]
|
||||
count += execInsert(ctx, stmt.mergeTarget, fields, @[values], insKvPairs)
|
||||
for kv in insKvPairs: kvPairs.add(kv)
|
||||
fireTriggers(ctx, stmt.mergeTarget, "after", "insert", row)
|
||||
@@ -982,16 +982,17 @@ proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValu
|
||||
|
||||
of nkCommitTxn:
|
||||
if ctx.pendingTxn != nil and ctx.pendingTxn.state == tsActive:
|
||||
var kvPairs: seq[(string, seq[byte])]
|
||||
var kvPairs: seq[tuple[key: string, value: seq[byte], deleted: bool]]
|
||||
for key, version in ctx.pendingTxn.writeSet:
|
||||
if version.isDelete:
|
||||
ctx.db.delete(key)
|
||||
# Empty value is the raft/replication "delete" convention — never
|
||||
# ship a non-empty body for isDelete or followers will resurrect.
|
||||
kvPairs.add((key, @[]))
|
||||
# Empty value + deleted=true is the raft/replication "delete"
|
||||
# convention — never ship a non-empty body for isDelete or
|
||||
# followers will resurrect.
|
||||
kvPairs.add((key, @[], true))
|
||||
else:
|
||||
ctx.db.put(key, version.value)
|
||||
kvPairs.add((key, version.value))
|
||||
kvPairs.add((key, version.value, false))
|
||||
discard ctx.txnManager.commit(ctx.pendingTxn)
|
||||
ctx.pendingTxn = nil
|
||||
return okResult(msg="Transaction committed", kvPairs=kvPairs)
|
||||
|
||||
Reference in New Issue
Block a user