dataply 0.0.26-alpha.8 → 0.0.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cjs/index.js CHANGED
@@ -2191,13 +2191,7 @@ function insertOp(ops, ctx, key, value, comparator) {
2191
2191
  }
2192
2192
  function deleteEntry(ops, ctx, node, key, comparator) {
2193
2193
  if (!node.leaf) {
2194
- let keyIndex = -1;
2195
- for (let i = 0, len = node.keys.length; i < len; i++) {
2196
- if (node.keys[i] === key) {
2197
- keyIndex = i;
2198
- break;
2199
- }
2200
- }
2194
+ let keyIndex = node.keys.indexOf(key);
2201
2195
  if (keyIndex !== -1) {
2202
2196
  node = cloneNode(node);
2203
2197
  node.keys.splice(keyIndex, 1);
@@ -2236,17 +2230,15 @@ function deleteEntry(ops, ctx, node, key, comparator) {
2236
2230
  let nextNode = null;
2237
2231
  let prevValue = null;
2238
2232
  let postValue = null;
2239
- for (let i = 0, len = parentNode.keys.length; i < len; i++) {
2240
- const nKey = parentNode.keys[i];
2241
- if (nKey === node.id) {
2242
- if (i > 0) {
2243
- prevNode = ops.getNode(parentNode.keys[i - 1]);
2244
- prevValue = parentNode.values[i - 1];
2245
- }
2246
- if (i < parentNode.keys.length - 1) {
2247
- nextNode = ops.getNode(parentNode.keys[i + 1]);
2248
- postValue = parentNode.values[i];
2249
- }
2233
+ let keyIndex = parentNode.keys.indexOf(node.id);
2234
+ if (keyIndex !== -1) {
2235
+ if (keyIndex > 0) {
2236
+ prevNode = ops.getNode(parentNode.keys[keyIndex - 1]);
2237
+ prevValue = parentNode.values[keyIndex - 1];
2238
+ }
2239
+ if (keyIndex < parentNode.keys.length - 1) {
2240
+ nextNode = ops.getNode(parentNode.keys[keyIndex + 1]);
2241
+ postValue = parentNode.values[keyIndex];
2250
2242
  }
2251
2243
  }
2252
2244
  let siblingNode;
@@ -2279,7 +2271,7 @@ function deleteEntry(ops, ctx, node, key, comparator) {
2279
2271
  siblingNode = node;
2280
2272
  node = pTemp;
2281
2273
  }
2282
- siblingNode.keys.push(...node.keys);
2274
+ siblingNode.keys = siblingNode.keys.concat(node.keys);
2283
2275
  if (!node.leaf) {
2284
2276
  siblingNode.values.push(guess);
2285
2277
  } else {
@@ -2290,7 +2282,7 @@ function deleteEntry(ops, ctx, node, key, comparator) {
2290
2282
  ops.updateNode(n);
2291
2283
  }
2292
2284
  }
2293
- siblingNode.values.push(...node.values);
2285
+ siblingNode.values = siblingNode.values.concat(node.values);
2294
2286
  if (!siblingNode.leaf) {
2295
2287
  const keys = siblingNode.keys;
2296
2288
  for (let i = 0, len = keys.length; i < len; i++) {
@@ -2338,8 +2330,8 @@ function deleteEntry(ops, ctx, node, key, comparator) {
2338
2330
  if (!node.leaf) {
2339
2331
  pointerP0 = siblingNode.keys.splice(0, 1)[0];
2340
2332
  pointerK0 = siblingNode.values.splice(0, 1)[0];
2341
- node.keys = [...node.keys, pointerP0];
2342
- node.values = [...node.values, guess];
2333
+ node.keys = node.keys.concat(pointerP0);
2334
+ node.values = node.values.concat(guess);
2343
2335
  parentNode = cloneNode(ops.getNode(node.parent));
2344
2336
  const pointerIndex = parentNode.keys.indexOf(siblingNode.id);
2345
2337
  if (pointerIndex > 0) {
@@ -2430,6 +2422,13 @@ function deleteOp(ops, ctx, key, comparator, value) {
2430
2422
  break;
2431
2423
  }
2432
2424
  }
2425
+ function batchDeleteOp(ops, ctx, entries, comparator) {
2426
+ if (entries.length === 0) return;
2427
+ for (let i = 0, len = entries.length; i < len; i++) {
2428
+ const [key, value] = entries[i];
2429
+ deleteOp(ops, ctx, key, comparator, value);
2430
+ }
2431
+ }
2433
2432
  function batchInsertOp(ops, ctx, entries, comparator) {
2434
2433
  if (entries.length === 0) return;
2435
2434
  const sorted = [...entries].sort((a, b) => comparator.asc(a[1], b[1]));
@@ -2985,27 +2984,12 @@ var BPTreeTransaction = class _BPTreeTransaction {
2985
2984
  static CheckConflicts(transactions) {
2986
2985
  return MVCCTransaction.CheckConflicts(transactions.map((tx) => tx.mvcc));
2987
2986
  }
2988
- /**
2989
- * Returns the ID of the root node.
2990
- * @returns The root node ID.
2991
- */
2992
2987
  getRootId() {
2993
2988
  return this.rootId;
2994
2989
  }
2995
- /**
2996
- * Returns the order of the B+Tree.
2997
- * @returns The order of the tree.
2998
- */
2999
2990
  getOrder() {
3000
2991
  return this.order;
3001
2992
  }
3002
- /**
3003
- * Verified if the value satisfies the condition.
3004
- *
3005
- * @param nodeValue The value to verify.
3006
- * @param condition The condition to verify against.
3007
- * @returns Returns true if the value satisfies the condition.
3008
- */
3009
2993
  verify(nodeValue, condition) {
3010
2994
  for (const key in condition) {
3011
2995
  const verify2 = this.verifierMap[key];
@@ -3264,6 +3248,9 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
3264
3248
  this._verifierMapCached = createVerifierMap(this.comparator, this._cachedRegexp, ensureValues);
3265
3249
  this._searchConfigsCached = createSearchConfigs(this.comparator, ensureValues);
3266
3250
  }
3251
+ getRootNode() {
3252
+ return this.getNode(this.rootId);
3253
+ }
3267
3254
  // ─── Legacy protected methods (delegating to ops) ────────────────
3268
3255
  getNode(id) {
3269
3256
  return this._ops.getNode(id);
@@ -3413,6 +3400,9 @@ var BPTreeSyncTransaction = class extends BPTreeTransaction {
3413
3400
  delete(key, value) {
3414
3401
  deleteOp(this._ops, this._ctx, key, this.comparator, value);
3415
3402
  }
3403
+ batchDelete(entries) {
3404
+ batchDeleteOp(this._ops, this._ctx, entries, this.comparator);
3405
+ }
3416
3406
  // ─── Head Data ───────────────────────────────────────────────────
3417
3407
  getHeadData() {
3418
3408
  const head = this._readHead();
@@ -3534,6 +3524,14 @@ var BPTreeSync = class extends BPTreeSyncTransaction {
3534
3524
  throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
3535
3525
  }
3536
3526
  }
3527
+ batchDelete(entries) {
3528
+ const tx = this.createTransaction();
3529
+ tx.batchDelete(entries);
3530
+ const result = tx.commit();
3531
+ if (!result.success) {
3532
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
3533
+ }
3534
+ }
3537
3535
  batchInsert(entries) {
3538
3536
  const tx = this.createTransaction();
3539
3537
  tx.batchInsert(entries);
@@ -4218,13 +4216,7 @@ async function insertOpAsync(ops, ctx, key, value, comparator) {
4218
4216
  }
4219
4217
  async function deleteEntryAsync(ops, ctx, node, key, comparator) {
4220
4218
  if (!node.leaf) {
4221
- let keyIndex = -1;
4222
- for (let i = 0, len = node.keys.length; i < len; i++) {
4223
- if (node.keys[i] === key) {
4224
- keyIndex = i;
4225
- break;
4226
- }
4227
- }
4219
+ let keyIndex = node.keys.indexOf(key);
4228
4220
  if (keyIndex !== -1) {
4229
4221
  node = cloneNode(node);
4230
4222
  node.keys.splice(keyIndex, 1);
@@ -4253,16 +4245,15 @@ async function deleteEntryAsync(ops, ctx, node, key, comparator) {
4253
4245
  let nextNode = null;
4254
4246
  let prevValue = null;
4255
4247
  let postValue = null;
4256
- for (let i = 0, len = parentNode.keys.length; i < len; i++) {
4257
- if (parentNode.keys[i] === node.id) {
4258
- if (i > 0) {
4259
- prevNode = await ops.getNode(parentNode.keys[i - 1]);
4260
- prevValue = parentNode.values[i - 1];
4261
- }
4262
- if (i < parentNode.keys.length - 1) {
4263
- nextNode = await ops.getNode(parentNode.keys[i + 1]);
4264
- postValue = parentNode.values[i];
4265
- }
4248
+ let keyIndex = parentNode.keys.indexOf(node.id);
4249
+ if (keyIndex !== -1) {
4250
+ if (keyIndex > 0) {
4251
+ prevNode = await ops.getNode(parentNode.keys[keyIndex - 1]);
4252
+ prevValue = parentNode.values[keyIndex - 1];
4253
+ }
4254
+ if (keyIndex < parentNode.keys.length - 1) {
4255
+ nextNode = await ops.getNode(parentNode.keys[keyIndex + 1]);
4256
+ postValue = parentNode.values[keyIndex];
4266
4257
  }
4267
4258
  }
4268
4259
  let siblingNode;
@@ -4289,11 +4280,11 @@ async function deleteEntryAsync(ops, ctx, node, key, comparator) {
4289
4280
  siblingNode = cloneNode(siblingNode);
4290
4281
  if (node.values.length + siblingNode.values.length < ctx.order) {
4291
4282
  if (!isPredecessor) {
4292
- const t = siblingNode;
4283
+ const pTemp = siblingNode;
4293
4284
  siblingNode = node;
4294
- node = t;
4285
+ node = pTemp;
4295
4286
  }
4296
- siblingNode.keys.push(...node.keys);
4287
+ siblingNode.keys = siblingNode.keys.concat(node.keys);
4297
4288
  if (!node.leaf) {
4298
4289
  siblingNode.values.push(guess);
4299
4290
  } else {
@@ -4304,7 +4295,7 @@ async function deleteEntryAsync(ops, ctx, node, key, comparator) {
4304
4295
  await ops.updateNode(n);
4305
4296
  }
4306
4297
  }
4307
- siblingNode.values.push(...node.values);
4298
+ siblingNode.values = siblingNode.values.concat(node.values);
4308
4299
  if (!siblingNode.leaf) {
4309
4300
  for (let i = 0, len = siblingNode.keys.length; i < len; i++) {
4310
4301
  const n = cloneNode(await ops.getNode(siblingNode.keys[i]));
@@ -4348,8 +4339,8 @@ async function deleteEntryAsync(ops, ctx, node, key, comparator) {
4348
4339
  if (!node.leaf) {
4349
4340
  pointerP0 = siblingNode.keys.splice(0, 1)[0];
4350
4341
  pointerK0 = siblingNode.values.splice(0, 1)[0];
4351
- node.keys = [...node.keys, pointerP0];
4352
- node.values = [...node.values, guess];
4342
+ node.keys = node.keys.concat(pointerP0);
4343
+ node.values = node.values.concat(guess);
4353
4344
  parentNode = cloneNode(await ops.getNode(node.parent));
4354
4345
  const pi = parentNode.keys.indexOf(siblingNode.id);
4355
4346
  if (pi > 0) {
@@ -4432,6 +4423,13 @@ async function deleteOpAsync(ops, ctx, key, comparator, value) {
4432
4423
  break;
4433
4424
  }
4434
4425
  }
4426
+ async function batchDeleteOpAsync(ops, ctx, entries, comparator) {
4427
+ if (entries.length === 0) return;
4428
+ for (let i = 0, len = entries.length; i < len; i++) {
4429
+ const [key, value] = entries[i];
4430
+ await deleteOpAsync(ops, ctx, key, comparator, value);
4431
+ }
4432
+ }
4435
4433
  async function batchInsertOpAsync(ops, ctx, entries, comparator) {
4436
4434
  if (entries.length === 0) return;
4437
4435
  const sorted = [...entries].sort((a, b) => comparator.asc(a[1], b[1]));
@@ -4692,6 +4690,9 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
4692
4690
  this.lock.writeUnlock(lockId);
4693
4691
  });
4694
4692
  }
4693
+ async getRootNode() {
4694
+ return this.getNode(this.rootId);
4695
+ }
4695
4696
  // ─── Legacy protected methods (delegating to ops) ────────────────
4696
4697
  async getNode(id) {
4697
4698
  return this._ops.getNode(id);
@@ -4851,6 +4852,12 @@ var BPTreeAsyncTransaction = class extends BPTreeTransaction {
4851
4852
  await deleteOpAsync(this._ops, this._ctx, key, this.comparator, value);
4852
4853
  });
4853
4854
  }
4855
+ async batchDelete(entries) {
4856
+ if (entries.length === 0) return;
4857
+ return this.writeLock(0, async () => {
4858
+ await batchDeleteOpAsync(this._ops, this._ctx, entries, this.comparator);
4859
+ });
4860
+ }
4854
4861
  // ─── Head Data ───────────────────────────────────────────────────
4855
4862
  async getHeadData() {
4856
4863
  const head = await this._readHead();
@@ -4976,6 +4983,16 @@ var BPTreeAsync = class extends BPTreeAsyncTransaction {
4976
4983
  }
4977
4984
  });
4978
4985
  }
4986
+ async batchDelete(entries) {
4987
+ return this.writeLock(1, async () => {
4988
+ const tx = await this.createTransaction();
4989
+ await tx.batchDelete(entries);
4990
+ const result = await tx.commit();
4991
+ if (!result.success) {
4992
+ throw new Error(`Transaction failed: ${result.error || "Commit failed due to conflict"}`);
4993
+ }
4994
+ });
4995
+ }
4979
4996
  async batchInsert(entries) {
4980
4997
  return this.writeLock(1, async () => {
4981
4998
  const tx = await this.createTransaction();
@@ -5002,8 +5019,6 @@ var BPTreePureSync = class {
5002
5019
  comparator;
5003
5020
  option;
5004
5021
  _cachedRegexp = /* @__PURE__ */ new Map();
5005
- _ctx;
5006
- _ops;
5007
5022
  _verifierMap;
5008
5023
  _searchConfigs;
5009
5024
  constructor(strategy, comparator, option) {
@@ -5017,11 +5032,16 @@ var BPTreePureSync = class {
5017
5032
  _ensureValues(v) {
5018
5033
  return Array.isArray(v) ? v : [v];
5019
5034
  }
5020
- _createOps() {
5035
+ _createReadOps() {
5021
5036
  const strategy = this.strategy;
5037
+ const readBuffer = /* @__PURE__ */ new Map();
5022
5038
  return {
5023
5039
  getNode(id) {
5024
- return strategy.read(id);
5040
+ const buffered = readBuffer.get(id);
5041
+ if (buffered) return buffered;
5042
+ const node = strategy.read(id);
5043
+ readBuffer.set(id, node);
5044
+ return node;
5025
5045
  },
5026
5046
  createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5027
5047
  const id = strategy.id(leaf);
@@ -5041,6 +5061,7 @@ var BPTreePureSync = class {
5041
5061
  }
5042
5062
  _createBufferedOps() {
5043
5063
  const strategy = this.strategy;
5064
+ const readBuffer = /* @__PURE__ */ new Map();
5044
5065
  const writeBuffer = /* @__PURE__ */ new Map();
5045
5066
  const deleteBuffer = /* @__PURE__ */ new Set();
5046
5067
  let headBuffer = null;
@@ -5048,7 +5069,11 @@ var BPTreePureSync = class {
5048
5069
  getNode(id) {
5049
5070
  const buffered = writeBuffer.get(id);
5050
5071
  if (buffered) return buffered;
5051
- return strategy.read(id);
5072
+ const read = readBuffer.get(id);
5073
+ if (read) return read;
5074
+ const node = strategy.read(id);
5075
+ readBuffer.set(id, node);
5076
+ return node;
5052
5077
  },
5053
5078
  createNode(leaf, keys, values, parent = null, next = null, prev = null) {
5054
5079
  const id = strategy.id(leaf);
@@ -5073,6 +5098,7 @@ var BPTreePureSync = class {
5073
5098
  };
5074
5099
  function flush() {
5075
5100
  for (const id of deleteBuffer) {
5101
+ writeBuffer.delete(id);
5076
5102
  strategy.delete(id);
5077
5103
  }
5078
5104
  for (const [id, node] of writeBuffer) {
@@ -5081,19 +5107,42 @@ var BPTreePureSync = class {
5081
5107
  if (headBuffer) {
5082
5108
  strategy.writeHead(headBuffer);
5083
5109
  }
5110
+ readBuffer.clear();
5111
+ writeBuffer.clear();
5112
+ deleteBuffer.clear();
5113
+ headBuffer = null;
5084
5114
  }
5085
5115
  return { ops, flush };
5086
5116
  }
5117
+ _readCtx() {
5118
+ const head = this.strategy.readHead();
5119
+ if (head === null) {
5120
+ throw new Error("Tree not initialized. Call init() first.");
5121
+ }
5122
+ return { rootId: head.root, order: head.order };
5123
+ }
5124
+ _createCtx() {
5125
+ const strategy = this.strategy;
5126
+ const head = strategy.readHead();
5127
+ if (head === null) {
5128
+ throw new Error("Tree not initialized. Call init() first.");
5129
+ }
5130
+ return {
5131
+ rootId: head.root,
5132
+ order: head.order,
5133
+ headData: () => strategy.head.data
5134
+ };
5135
+ }
5087
5136
  init() {
5088
5137
  const { ops, flush } = this._createBufferedOps();
5089
- this._ctx = {
5138
+ const ctx = {
5090
5139
  rootId: "",
5091
5140
  order: this.strategy.order,
5092
5141
  headData: () => this.strategy.head.data
5093
5142
  };
5094
5143
  initOp(
5095
5144
  ops,
5096
- this._ctx,
5145
+ ctx,
5097
5146
  this.strategy.order,
5098
5147
  this.strategy.head,
5099
5148
  (head) => {
@@ -5101,23 +5150,19 @@ var BPTreePureSync = class {
5101
5150
  }
5102
5151
  );
5103
5152
  flush();
5104
- this._ops = this._createOps();
5105
5153
  }
5106
- /**
5107
- * Returns the ID of the root node.
5108
- */
5154
+ getRootNode() {
5155
+ const ctx = this._readCtx();
5156
+ return this.strategy.read(ctx.rootId);
5157
+ }
5109
5158
  getRootId() {
5110
- return this._ctx.rootId;
5159
+ const ctx = this._readCtx();
5160
+ return ctx.rootId;
5111
5161
  }
5112
- /**
5113
- * Returns the order of the B+Tree.
5114
- */
5115
5162
  getOrder() {
5116
- return this._ctx.order;
5163
+ const ctx = this._readCtx();
5164
+ return ctx.order;
5117
5165
  }
5118
- /**
5119
- * Verified if the value satisfies the condition.
5120
- */
5121
5166
  verify(nodeValue, condition) {
5122
5167
  for (const key in condition) {
5123
5168
  const verifyFn = this._verifierMap[key];
@@ -5130,15 +5175,18 @@ var BPTreePureSync = class {
5130
5175
  }
5131
5176
  // ─── Query ───────────────────────────────────────────────────────
5132
5177
  get(key) {
5133
- return getOp(this._ops, this._ctx.rootId, key);
5178
+ const ctx = this._readCtx();
5179
+ return getOp(this._createReadOps(), ctx.rootId, key);
5134
5180
  }
5135
5181
  exists(key, value) {
5136
- return existsOp(this._ops, this._ctx.rootId, key, value, this.comparator);
5182
+ const ctx = this._readCtx();
5183
+ return existsOp(this._createReadOps(), ctx.rootId, key, value, this.comparator);
5137
5184
  }
5138
5185
  *keysStream(condition, options) {
5186
+ const ctx = this._readCtx();
5139
5187
  yield* keysStreamOp(
5140
- this._ops,
5141
- this._ctx.rootId,
5188
+ this._createReadOps(),
5189
+ ctx.rootId,
5142
5190
  condition,
5143
5191
  this.comparator,
5144
5192
  this._verifierMap,
@@ -5148,9 +5196,10 @@ var BPTreePureSync = class {
5148
5196
  );
5149
5197
  }
5150
5198
  *whereStream(condition, options) {
5199
+ const ctx = this._readCtx();
5151
5200
  yield* whereStreamOp(
5152
- this._ops,
5153
- this._ctx.rootId,
5201
+ this._createReadOps(),
5202
+ ctx.rootId,
5154
5203
  condition,
5155
5204
  this.comparator,
5156
5205
  this._verifierMap,
@@ -5176,27 +5225,37 @@ var BPTreePureSync = class {
5176
5225
  // ─── Mutation ────────────────────────────────────────────────────
5177
5226
  insert(key, value) {
5178
5227
  const { ops, flush } = this._createBufferedOps();
5179
- insertOp(ops, this._ctx, key, value, this.comparator);
5228
+ const ctx = this._createCtx();
5229
+ insertOp(ops, ctx, key, value, this.comparator);
5180
5230
  flush();
5181
5231
  }
5182
5232
  delete(key, value) {
5183
5233
  const { ops, flush } = this._createBufferedOps();
5184
- deleteOp(ops, this._ctx, key, this.comparator, value);
5234
+ const ctx = this._createCtx();
5235
+ deleteOp(ops, ctx, key, this.comparator, value);
5236
+ flush();
5237
+ }
5238
+ batchDelete(entries) {
5239
+ const { ops, flush } = this._createBufferedOps();
5240
+ const ctx = this._createCtx();
5241
+ batchDeleteOp(ops, ctx, entries, this.comparator);
5185
5242
  flush();
5186
5243
  }
5187
5244
  batchInsert(entries) {
5188
5245
  const { ops, flush } = this._createBufferedOps();
5189
- batchInsertOp(ops, this._ctx, entries, this.comparator);
5246
+ const ctx = this._createCtx();
5247
+ batchInsertOp(ops, ctx, entries, this.comparator);
5190
5248
  flush();
5191
5249
  }
5192
5250
  bulkLoad(entries) {
5193
5251
  const { ops, flush } = this._createBufferedOps();
5194
- bulkLoadOp(ops, this._ctx, entries, this.comparator);
5252
+ const ctx = this._createCtx();
5253
+ bulkLoadOp(ops, ctx, entries, this.comparator);
5195
5254
  flush();
5196
5255
  }
5197
5256
  // ─── Head Data ───────────────────────────────────────────────────
5198
5257
  getHeadData() {
5199
- const head = this._ops.readHead();
5258
+ const head = this.strategy.readHead();
5200
5259
  if (head === null) {
5201
5260
  throw new Error("Head not found");
5202
5261
  }
@@ -5224,8 +5283,6 @@ var BPTreePureAsync = class {
5224
5283
  option;
5225
5284
  lock = new Ryoiki2();
5226
5285
  _cachedRegexp = /* @__PURE__ */ new Map();
5227
- _ctx;
5228
- _ops;
5229
5286
  _verifierMap;
5230
5287
  _searchConfigs;
5231
5288
  constructor(strategy, comparator, option) {
@@ -5239,8 +5296,9 @@ var BPTreePureAsync = class {
5239
5296
  _ensureValues(v) {
5240
5297
  return Array.isArray(v) ? v : [v];
5241
5298
  }
5242
- _createOps() {
5299
+ _createReadOps() {
5243
5300
  const strategy = this.strategy;
5301
+ let headBuffer = null;
5244
5302
  return {
5245
5303
  async getNode(id) {
5246
5304
  return await strategy.read(id);
@@ -5255,9 +5313,12 @@ var BPTreePureAsync = class {
5255
5313
  async deleteNode() {
5256
5314
  },
5257
5315
  async readHead() {
5258
- return await strategy.readHead();
5316
+ if (headBuffer) return headBuffer;
5317
+ headBuffer = await strategy.readHead();
5318
+ return headBuffer;
5259
5319
  },
5260
- async writeHead() {
5320
+ async writeHead(head) {
5321
+ headBuffer = head;
5261
5322
  }
5262
5323
  };
5263
5324
  }
@@ -5287,7 +5348,8 @@ var BPTreePureAsync = class {
5287
5348
  },
5288
5349
  async readHead() {
5289
5350
  if (headBuffer) return headBuffer;
5290
- return await strategy.readHead();
5351
+ headBuffer = await strategy.readHead();
5352
+ return headBuffer;
5291
5353
  },
5292
5354
  async writeHead(head) {
5293
5355
  headBuffer = head;
@@ -5295,6 +5357,7 @@ var BPTreePureAsync = class {
5295
5357
  };
5296
5358
  async function flush() {
5297
5359
  for (const id of deleteBuffer) {
5360
+ writeBuffer.delete(id);
5298
5361
  await strategy.delete(id);
5299
5362
  }
5300
5363
  for (const [id, node] of writeBuffer) {
@@ -5303,9 +5366,31 @@ var BPTreePureAsync = class {
5303
5366
  if (headBuffer) {
5304
5367
  await strategy.writeHead(headBuffer);
5305
5368
  }
5369
+ writeBuffer.clear();
5370
+ deleteBuffer.clear();
5371
+ headBuffer = null;
5306
5372
  }
5307
5373
  return { ops, flush };
5308
5374
  }
5375
+ async _readCtx() {
5376
+ const head = await this.strategy.readHead();
5377
+ if (head === null) {
5378
+ throw new Error("Tree not initialized. Call init() first.");
5379
+ }
5380
+ return { rootId: head.root, order: head.order };
5381
+ }
5382
+ async _createCtx() {
5383
+ const strategy = this.strategy;
5384
+ const head = await strategy.readHead();
5385
+ if (head === null) {
5386
+ throw new Error("Tree not initialized. Call init() first.");
5387
+ }
5388
+ return {
5389
+ rootId: head.root,
5390
+ order: head.order,
5391
+ headData: () => strategy.head.data
5392
+ };
5393
+ }
5309
5394
  async writeLock(fn) {
5310
5395
  let lockId;
5311
5396
  return this.lock.writeLock(async (_lockId) => {
@@ -5318,14 +5403,14 @@ var BPTreePureAsync = class {
5318
5403
  async init() {
5319
5404
  return this.writeLock(async () => {
5320
5405
  const { ops, flush } = this._createBufferedOps();
5321
- this._ctx = {
5406
+ const ctx = {
5322
5407
  rootId: "",
5323
5408
  order: this.strategy.order,
5324
5409
  headData: () => this.strategy.head.data
5325
5410
  };
5326
5411
  await initOpAsync(
5327
5412
  ops,
5328
- this._ctx,
5413
+ ctx,
5329
5414
  this.strategy.order,
5330
5415
  this.strategy.head,
5331
5416
  (head) => {
@@ -5333,14 +5418,19 @@ var BPTreePureAsync = class {
5333
5418
  }
5334
5419
  );
5335
5420
  await flush();
5336
- this._ops = this._createOps();
5337
5421
  });
5338
5422
  }
5339
- getRootId() {
5340
- return this._ctx.rootId;
5423
+ async getRootNode() {
5424
+ const ctx = await this._readCtx();
5425
+ return await this.strategy.read(ctx.rootId);
5341
5426
  }
5342
- getOrder() {
5343
- return this._ctx.order;
5427
+ async getRootId() {
5428
+ const ctx = await this._readCtx();
5429
+ return ctx.rootId;
5430
+ }
5431
+ async getOrder() {
5432
+ const ctx = await this._readCtx();
5433
+ return ctx.order;
5344
5434
  }
5345
5435
  verify(nodeValue, condition) {
5346
5436
  for (const key in condition) {
@@ -5352,18 +5442,21 @@ var BPTreePureAsync = class {
5352
5442
  }
5353
5443
  // ─── Query ───────────────────────────────────────────────────────
5354
5444
  async get(key) {
5355
- return getOpAsync(this._ops, this._ctx.rootId, key);
5445
+ const ctx = await this._readCtx();
5446
+ return getOpAsync(this._createReadOps(), ctx.rootId, key);
5356
5447
  }
5357
5448
  async exists(key, value) {
5358
- return existsOpAsync(this._ops, this._ctx.rootId, key, value, this.comparator);
5449
+ const ctx = await this._readCtx();
5450
+ return existsOpAsync(this._createReadOps(), ctx.rootId, key, value, this.comparator);
5359
5451
  }
5360
5452
  async *keysStream(condition, options) {
5361
5453
  let lockId;
5362
5454
  try {
5363
5455
  lockId = await this.lock.readLock([0, 0.1], async (id) => id);
5456
+ const ctx = await this._readCtx();
5364
5457
  yield* keysStreamOpAsync(
5365
- this._ops,
5366
- this._ctx.rootId,
5458
+ this._createReadOps(),
5459
+ ctx.rootId,
5367
5460
  condition,
5368
5461
  this.comparator,
5369
5462
  this._verifierMap,
@@ -5379,9 +5472,10 @@ var BPTreePureAsync = class {
5379
5472
  let lockId;
5380
5473
  try {
5381
5474
  lockId = await this.lock.readLock([0, 0.1], async (id) => id);
5475
+ const ctx = await this._readCtx();
5382
5476
  yield* whereStreamOpAsync(
5383
- this._ops,
5384
- this._ctx.rootId,
5477
+ this._createReadOps(),
5478
+ ctx.rootId,
5385
5479
  condition,
5386
5480
  this.comparator,
5387
5481
  this._verifierMap,
@@ -5411,34 +5505,46 @@ var BPTreePureAsync = class {
5411
5505
  async insert(key, value) {
5412
5506
  return this.writeLock(async () => {
5413
5507
  const { ops, flush } = this._createBufferedOps();
5414
- await insertOpAsync(ops, this._ctx, key, value, this.comparator);
5508
+ const ctx = await this._createCtx();
5509
+ await insertOpAsync(ops, ctx, key, value, this.comparator);
5415
5510
  await flush();
5416
5511
  });
5417
5512
  }
5418
5513
  async delete(key, value) {
5419
5514
  return this.writeLock(async () => {
5420
5515
  const { ops, flush } = this._createBufferedOps();
5421
- await deleteOpAsync(ops, this._ctx, key, this.comparator, value);
5516
+ const ctx = await this._createCtx();
5517
+ await deleteOpAsync(ops, ctx, key, this.comparator, value);
5422
5518
  await flush();
5423
5519
  });
5424
5520
  }
5425
5521
  async batchInsert(entries) {
5426
5522
  return this.writeLock(async () => {
5427
5523
  const { ops, flush } = this._createBufferedOps();
5428
- await batchInsertOpAsync(ops, this._ctx, entries, this.comparator);
5524
+ const ctx = await this._createCtx();
5525
+ await batchInsertOpAsync(ops, ctx, entries, this.comparator);
5526
+ await flush();
5527
+ });
5528
+ }
5529
+ async batchDelete(entries) {
5530
+ return this.writeLock(async () => {
5531
+ const { ops, flush } = this._createBufferedOps();
5532
+ const ctx = await this._createCtx();
5533
+ await batchDeleteOpAsync(ops, ctx, entries, this.comparator);
5429
5534
  await flush();
5430
5535
  });
5431
5536
  }
5432
5537
  async bulkLoad(entries) {
5433
5538
  return this.writeLock(async () => {
5434
5539
  const { ops, flush } = this._createBufferedOps();
5435
- await bulkLoadOpAsync(ops, this._ctx, entries, this.comparator);
5540
+ const ctx = await this._createCtx();
5541
+ await bulkLoadOpAsync(ops, ctx, entries, this.comparator);
5436
5542
  await flush();
5437
5543
  });
5438
5544
  }
5439
5545
  // ─── Head Data ───────────────────────────────────────────────────
5440
5546
  async getHeadData() {
5441
- const head = await this._ops.readHead();
5547
+ const head = await this.strategy.readHead();
5442
5548
  if (head === null) throw new Error("Head not found");
5443
5549
  return head.data;
5444
5550
  }
@@ -9859,7 +9965,7 @@ var PageFileSystem = class {
9859
9965
  * @param tx Transaction
9860
9966
  */
9861
9967
  async updateBitmap(pageId, isFree, tx) {
9862
- const metadata = await this.getMetadata(tx);
9968
+ const metadata = await this.getMetadata(false, tx);
9863
9969
  const metadataManager = this.pageFactory.getManager(metadata);
9864
9970
  const bitmapPageId = metadataManager.getBitmapPageId(metadata);
9865
9971
  const headerSize = PageManager.CONSTANT.SIZE_PAGE_HEADER;
@@ -9867,7 +9973,7 @@ var PageFileSystem = class {
9867
9973
  let currentBitmapPageId = bitmapPageId;
9868
9974
  let targetBitIndex = pageId;
9869
9975
  while (targetBitIndex >= capacityPerBitmapPage) {
9870
- const currentBitmapPage = await this.get(currentBitmapPageId, tx);
9976
+ const currentBitmapPage = await this.get(currentBitmapPageId, true, tx);
9871
9977
  const manager = this.pageFactory.getManager(currentBitmapPage);
9872
9978
  targetBitIndex -= capacityPerBitmapPage;
9873
9979
  const nextPageId = manager.getNextPageId(currentBitmapPage);
@@ -9884,7 +9990,7 @@ var PageFileSystem = class {
9884
9990
  }
9885
9991
  }
9886
9992
  await tx.__acquireWriteLock(currentBitmapPageId);
9887
- const targetBitmapPage = await this.get(currentBitmapPageId, tx);
9993
+ const targetBitmapPage = await this.get(currentBitmapPageId, true, tx);
9888
9994
  const bitmapManager = this.pageFactory.getManager(targetBitmapPage);
9889
9995
  bitmapManager.setBit(targetBitmapPage, targetBitIndex, isFree);
9890
9996
  await this.setPage(currentBitmapPageId, targetBitmapPage, tx);
@@ -9903,20 +10009,22 @@ var PageFileSystem = class {
9903
10009
  }
9904
10010
  /**
9905
10011
  * @param pageIndex 페이지 인덱스
10012
+ * @param copy Copy-on-Read
9906
10013
  * @param tx 트랜잭션
9907
10014
  * @returns 페이지 버퍼
9908
10015
  */
9909
- async get(pageIndex, tx) {
9910
- return tx.__readPage(pageIndex);
10016
+ async get(pageIndex, copy, tx) {
10017
+ return tx.__readPage(pageIndex, copy);
9911
10018
  }
9912
10019
  /**
9913
10020
  * Reads the page header.
9914
10021
  * @param pageIndex Page index
10022
+ * @param copy Copy-on-Read
9915
10023
  * @param tx Transaction
9916
10024
  * @returns Page header buffer
9917
10025
  */
9918
- async getHeader(pageIndex, tx) {
9919
- const page = await this.get(pageIndex, tx);
10026
+ async getHeader(pageIndex, copy, tx) {
10027
+ const page = await this.get(pageIndex, copy, tx);
9920
10028
  return page.subarray(0, PageManager.CONSTANT.SIZE_PAGE_HEADER);
9921
10029
  }
9922
10030
  /**
@@ -9927,7 +10035,7 @@ var PageFileSystem = class {
9927
10035
  * @returns Page body buffer
9928
10036
  */
9929
10037
  async getBody(pageIndex, recursive = false, tx) {
9930
- const page = await this.get(pageIndex, tx);
10038
+ const page = await this.get(pageIndex, false, tx);
9931
10039
  const manager = this.pageFactory.getManager(page);
9932
10040
  const fullBody = manager.getBody(page);
9933
10041
  if (!recursive) {
@@ -9945,11 +10053,12 @@ var PageFileSystem = class {
9945
10053
  }
9946
10054
  /**
9947
10055
  * Returns the metadata page.
10056
+ * @param copy Copy-on-Read
9948
10057
  * @param tx Transaction
9949
10058
  * @returns Metadata page
9950
10059
  */
9951
- async getMetadata(tx) {
9952
- const page = await this.get(0, tx);
10060
+ async getMetadata(copy, tx) {
10061
+ const page = await this.get(0, copy, tx);
9953
10062
  if (!MetadataPageManager.IsMetadataPage(page)) {
9954
10063
  throw new Error("Invalid metadata page");
9955
10064
  }
@@ -9961,20 +10070,21 @@ var PageFileSystem = class {
9961
10070
  * @returns Number of pages
9962
10071
  */
9963
10072
  async getPageCount(tx) {
9964
- const metadata = await this.getMetadata(tx);
10073
+ const metadata = await this.getMetadata(false, tx);
9965
10074
  const manager = this.pageFactory.getManager(metadata);
9966
10075
  return manager.getPageCount(metadata);
9967
10076
  }
9968
10077
  /**
9969
10078
  * Returns the root index page.
10079
+ * @param copy Copy-on-Read
9970
10080
  * @param tx Transaction
9971
10081
  * @returns Root index page
9972
10082
  */
9973
- async getRootIndex(tx) {
9974
- const metadata = await this.getMetadata(tx);
10083
+ async getRootIndex(copy, tx) {
10084
+ const metadata = await this.getMetadata(false, tx);
9975
10085
  const manager = this.pageFactory.getManager(metadata);
9976
10086
  const rootIndexPageId = manager.getRootIndexPageId(metadata);
9977
- const rootIndexPage = await this.get(rootIndexPageId, tx);
10087
+ const rootIndexPage = await this.get(rootIndexPageId, copy, tx);
9978
10088
  if (!IndexPageManager.IsIndexPage(rootIndexPage)) {
9979
10089
  throw new Error("Invalid root index page");
9980
10090
  }
@@ -10009,12 +10119,12 @@ var PageFileSystem = class {
10009
10119
  async appendNewPage(pageType = PageManager.CONSTANT.PAGE_TYPE_EMPTY, tx) {
10010
10120
  this.logger.debug(`Appending new page of type ${pageType}`);
10011
10121
  await tx.__acquireWriteLock(0);
10012
- const metadata = await this.getMetadata(tx);
10122
+ const metadata = await this.getMetadata(true, tx);
10013
10123
  const metadataManager = this.pageFactory.getManager(metadata);
10014
10124
  const freePageId = metadataManager.getFreePageId(metadata);
10015
10125
  if (freePageId !== -1) {
10016
10126
  const reusedPageId = freePageId;
10017
- const reusedPage = await this.get(reusedPageId, tx);
10127
+ const reusedPage = await this.get(reusedPageId, false, tx);
10018
10128
  const reusedPageManager = this.pageFactory.getManager(reusedPage);
10019
10129
  const nextFreePageId = reusedPageManager.getNextPageId(reusedPage);
10020
10130
  metadataManager.setFreePageId(metadata, nextFreePageId);
@@ -10059,7 +10169,7 @@ var PageFileSystem = class {
10059
10169
  let currentOffset = offset;
10060
10170
  let dataOffset = 0;
10061
10171
  while (dataOffset < data.length) {
10062
- const page = await this.get(currentPageId, tx);
10172
+ const page = await this.get(currentPageId, true, tx);
10063
10173
  const manager = this.pageFactory.getManager(page);
10064
10174
  const bodyStart = PageManager.CONSTANT.SIZE_PAGE_HEADER;
10065
10175
  const bodySize = this.pageSize - bodyStart;
@@ -10119,7 +10229,7 @@ var PageFileSystem = class {
10119
10229
  break;
10120
10230
  }
10121
10231
  visited.add(currentPageId);
10122
- const page = await this.get(currentPageId, tx);
10232
+ const page = await this.get(currentPageId, false, tx);
10123
10233
  const nextPageId = this.pageFactory.getManager(page).getNextPageId(page);
10124
10234
  await this.setFreePage(currentPageId, tx);
10125
10235
  currentPageId = nextPageId;
@@ -10135,7 +10245,7 @@ var PageFileSystem = class {
10135
10245
  if (pageId <= 0) return;
10136
10246
  await tx.__acquireWriteLock(0);
10137
10247
  await tx.__acquireWriteLock(pageId);
10138
- const metadata = await this.getMetadata(tx);
10248
+ const metadata = await this.getMetadata(true, tx);
10139
10249
  const metadataManager = this.pageFactory.getManager(metadata);
10140
10250
  const currentHeadFreePageId = metadataManager.getFreePageId(metadata);
10141
10251
  const emptyPageManager = this.pageFactory.getManagerFromType(PageManager.CONSTANT.PAGE_TYPE_EMPTY);
@@ -10218,12 +10328,12 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
10218
10328
  async id(isLeaf) {
10219
10329
  const tx = this.txContext.get();
10220
10330
  await tx.__acquireWriteLock(0);
10221
- const metadata = await this.pfs.getMetadata(tx);
10331
+ const metadata = await this.pfs.getMetadata(true, tx);
10222
10332
  const metadataManager = this.factory.getManager(metadata);
10223
10333
  const freePageId = metadataManager.getFreePageId(metadata);
10224
10334
  let pageId;
10225
10335
  if (freePageId !== -1) {
10226
- const freePage = await this.pfs.get(freePageId, tx);
10336
+ const freePage = await this.pfs.get(freePageId, false, tx);
10227
10337
  const freePageManager = this.factory.getManager(freePage);
10228
10338
  const nextFreePageId = freePageManager.getNextPageId(freePage);
10229
10339
  metadataManager.setFreePageId(metadata, nextFreePageId);
@@ -10239,7 +10349,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
10239
10349
  async read(id) {
10240
10350
  const tx = this.txContext.get();
10241
10351
  const pageId = +id;
10242
- const page = await this.pfs.get(pageId, tx);
10352
+ const page = await this.pfs.get(pageId, false, tx);
10243
10353
  if (!IndexPageManager.IsIndexPage(page)) {
10244
10354
  throw new Error(`Node ${id} does not exist - not a valid index page`);
10245
10355
  }
@@ -10274,7 +10384,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
10274
10384
  async write(id, node) {
10275
10385
  const tx = this.txContext.get();
10276
10386
  const pageId = +id;
10277
- let page = await this.pfs.get(pageId, tx);
10387
+ let page = await this.pfs.get(pageId, true, tx);
10278
10388
  if (!IndexPageManager.IsIndexPage(page)) {
10279
10389
  page = this.indexPageManger.create(this.pfs.pageSize, pageId);
10280
10390
  }
@@ -10322,7 +10432,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
10322
10432
  }
10323
10433
  async readHead() {
10324
10434
  const tx = this.txContext.get();
10325
- const metadataPage = await this.pfs.getMetadata(tx);
10435
+ const metadataPage = await this.pfs.getMetadata(false, tx);
10326
10436
  const manager = this.factory.getManager(metadataPage);
10327
10437
  const rootIndexPageId = manager.getRootIndexPageId(metadataPage);
10328
10438
  if (rootIndexPageId === -1) {
@@ -10342,7 +10452,7 @@ var RowIdentifierStrategy = class extends SerializeStrategyAsync {
10342
10452
  if (root === null) {
10343
10453
  throw new Error("");
10344
10454
  }
10345
- const metadataPage = await this.pfs.getMetadata(tx);
10455
+ const metadataPage = await this.pfs.getMetadata(true, tx);
10346
10456
  const manager = this.factory.getManager(metadataPage);
10347
10457
  manager.setRootIndexPageId(metadataPage, +root);
10348
10458
  manager.setRootIndexOrder(metadataPage, order);
@@ -10503,7 +10613,7 @@ var RowTableEngine = class {
10503
10613
  if (!this.initialized) {
10504
10614
  throw new Error("RowTableEngine instance is not initialized");
10505
10615
  }
10506
- const metadataPage = await this.pfs.getMetadata(tx);
10616
+ const metadataPage = await this.pfs.getMetadata(false, tx);
10507
10617
  const manager = this.factory.getManagerFromType(MetadataPageManager.CONSTANT.PAGE_TYPE_METADATA);
10508
10618
  const pageSize = manager.getPageSize(metadataPage);
10509
10619
  const pageCount = manager.getPageCount(metadataPage);
@@ -10531,10 +10641,10 @@ var RowTableEngine = class {
10531
10641
  }
10532
10642
  await tx.__acquireWriteLock(0);
10533
10643
  const pks = [];
10534
- const metadataPage = await this.pfs.getMetadata(tx);
10644
+ const metadataPage = await this.pfs.getMetadata(false, tx);
10535
10645
  let lastPk = this.metadataPageManager.getLastRowPk(metadataPage);
10536
10646
  let lastInsertDataPageId = this.metadataPageManager.getLastInsertPageId(metadataPage);
10537
- let lastInsertDataPage = await this.pfs.get(lastInsertDataPageId, tx);
10647
+ let lastInsertDataPage = await this.pfs.get(lastInsertDataPageId, true, tx);
10538
10648
  if (!this.factory.isDataPage(lastInsertDataPage)) {
10539
10649
  throw new Error(`Last insert page is not data page`);
10540
10650
  }
@@ -10556,7 +10666,7 @@ var RowTableEngine = class {
10556
10666
  await this.pfs.setPage(lastInsertDataPageId, lastInsertDataPage, tx);
10557
10667
  } else {
10558
10668
  const newPageId = await this.pfs.appendNewPage(this.dataPageManager.pageType, tx);
10559
- const newPage = await this.pfs.get(newPageId, tx);
10669
+ const newPage = await this.pfs.get(newPageId, true, tx);
10560
10670
  this.dataPageManager.insert(newPage, row);
10561
10671
  this.setRID(newPageId, 0);
10562
10672
  lastInsertDataPageId = newPageId;
@@ -10571,7 +10681,7 @@ var RowTableEngine = class {
10571
10681
  this.rowManager.setBody(row, data);
10572
10682
  if (slotIndex === -1) {
10573
10683
  const newPageId = await this.pfs.appendNewPage(this.dataPageManager.pageType, tx);
10574
- const newPage = await this.pfs.get(newPageId, tx);
10684
+ const newPage = await this.pfs.get(newPageId, true, tx);
10575
10685
  this.dataPageManager.insert(newPage, row);
10576
10686
  this.setRID(newPageId, 0);
10577
10687
  lastInsertDataPageId = newPageId;
@@ -10587,7 +10697,7 @@ var RowTableEngine = class {
10587
10697
  pks.push(pk);
10588
10698
  }
10589
10699
  await this.bptree.batchInsert(batchInsertData);
10590
- const freshMetadataPage = await this.pfs.getMetadata(tx);
10700
+ const freshMetadataPage = await this.pfs.getMetadata(true, tx);
10591
10701
  this.metadataPageManager.setLastInsertPageId(freshMetadataPage, lastInsertDataPageId);
10592
10702
  this.metadataPageManager.setLastRowPk(freshMetadataPage, lastPk);
10593
10703
  if (incrementRowCount) {
@@ -10633,7 +10743,7 @@ var RowTableEngine = class {
10633
10743
  this.keyManager.setBufferFromKey(rid, this.ridBuffer);
10634
10744
  const pageId = this.keyManager.getPageId(this.ridBuffer);
10635
10745
  const slotIndex = this.keyManager.getSlotIndex(this.ridBuffer);
10636
- const page = await this.pfs.get(pageId, tx);
10746
+ const page = await this.pfs.get(pageId, true, tx);
10637
10747
  if (!this.factory.isDataPage(page)) {
10638
10748
  throw new Error(`RID not found for PK: ${pk}`);
10639
10749
  }
@@ -10669,22 +10779,22 @@ var RowTableEngine = class {
10669
10779
  this.rowManager.setBodySize(newRow, newBodySize);
10670
10780
  this.rowManager.setBody(newRow, data);
10671
10781
  await tx.__acquireWriteLock(0);
10672
- const metadataPage = await this.pfs.getMetadata(tx);
10782
+ const metadataPage = await this.pfs.getMetadata(false, tx);
10673
10783
  let lastInsertDataPageId = this.metadataPageManager.getLastInsertPageId(metadataPage);
10674
- let lastInsertDataPage = await this.pfs.get(lastInsertDataPageId, tx);
10784
+ let lastInsertDataPage = await this.pfs.get(lastInsertDataPageId, true, tx);
10675
10785
  if (!this.factory.isDataPage(lastInsertDataPage)) {
10676
10786
  throw new Error("Last insert page is not data page");
10677
10787
  }
10678
10788
  let newSlotIndex = this.dataPageManager.getNextSlotIndex(lastInsertDataPage, newRow);
10679
10789
  if (newSlotIndex === -1) {
10680
10790
  const newPageId = await this.pfs.appendNewPage(this.dataPageManager.pageType, tx);
10681
- lastInsertDataPage = await this.pfs.get(newPageId, tx);
10791
+ lastInsertDataPage = await this.pfs.get(newPageId, true, tx);
10682
10792
  lastInsertDataPageId = newPageId;
10683
10793
  newSlotIndex = 0;
10684
10794
  }
10685
10795
  this.dataPageManager.insert(lastInsertDataPage, newRow);
10686
10796
  await this.pfs.setPage(lastInsertDataPageId, lastInsertDataPage, tx);
10687
- const targetPage = await this.pfs.get(pageId, tx);
10797
+ const targetPage = await this.pfs.get(pageId, true, tx);
10688
10798
  if (!this.factory.isDataPage(targetPage)) {
10689
10799
  throw new Error("Target page is not data page");
10690
10800
  }
@@ -10697,7 +10807,7 @@ var RowTableEngine = class {
10697
10807
  const newRidNumeric = this.getRID();
10698
10808
  await this.bptree.delete(oldRidNumeric, pk);
10699
10809
  await this.bptree.insert(newRidNumeric, pk);
10700
- const freshMetadataPage = await this.pfs.getMetadata(tx);
10810
+ const freshMetadataPage = await this.pfs.getMetadata(true, tx);
10701
10811
  this.metadataPageManager.setLastInsertPageId(freshMetadataPage, lastInsertDataPageId);
10702
10812
  await this.pfs.setMetadata(freshMetadataPage, tx);
10703
10813
  }
@@ -10717,7 +10827,7 @@ var RowTableEngine = class {
10717
10827
  this.keyManager.setBufferFromKey(rid, this.ridBuffer);
10718
10828
  const pageId = this.keyManager.getPageId(this.ridBuffer);
10719
10829
  const slotIndex = this.keyManager.getSlotIndex(this.ridBuffer);
10720
- const page = await this.pfs.get(pageId, tx);
10830
+ const page = await this.pfs.get(pageId, true, tx);
10721
10831
  if (!this.factory.isDataPage(page)) {
10722
10832
  throw new Error(`RID not found for PK: ${pk}`);
10723
10833
  }
@@ -10733,14 +10843,14 @@ var RowTableEngine = class {
10733
10843
  await this.pfs.setPage(pageId, page, tx);
10734
10844
  await this.bptree.delete(rid, pk);
10735
10845
  if (decrementRowCount) {
10736
- const metadataPage2 = await this.pfs.getMetadata(tx);
10846
+ const metadataPage2 = await this.pfs.getMetadata(true, tx);
10737
10847
  const currentRowCount = this.metadataPageManager.getRowCount(metadataPage2);
10738
10848
  this.metadataPageManager.setRowCount(metadataPage2, currentRowCount - 1);
10739
10849
  await this.pfs.setMetadata(metadataPage2, tx);
10740
10850
  }
10741
10851
  const insertedRowCount = this.dataPageManager.getInsertedRowCount(page);
10742
10852
  let allDeleted = true;
10743
- const metadataPage = await this.pfs.getMetadata(tx);
10853
+ const metadataPage = await this.pfs.getMetadata(false, tx);
10744
10854
  const lastInsertPageId = this.metadataPageManager.getLastInsertPageId(metadataPage);
10745
10855
  if (pageId === lastInsertPageId) {
10746
10856
  allDeleted = false;
@@ -10759,6 +10869,82 @@ var RowTableEngine = class {
10759
10869
  await this.pfs.freeChain(pageId, tx);
10760
10870
  }
10761
10871
  }
10872
+ /**
10873
+ * Deletes multiple data in batch.
10874
+ * @param pks Array of PKs to delete
10875
+ * @param decrementRowCount Whether to decrement the row count to metadata
10876
+ * @param tx Transaction
10877
+ */
10878
+ async deleteBatch(pks, decrementRowCount, tx) {
10879
+ this.logger.debug(`Batch deleting ${pks.length} rows`);
10880
+ if (pks.length === 0) {
10881
+ return;
10882
+ }
10883
+ await tx.__acquireWriteLock(0);
10884
+ const collections = await this.collectItemsByPage(pks, tx);
10885
+ if (collections.size === 0) {
10886
+ return;
10887
+ }
10888
+ const metadataPage = await this.pfs.getMetadata(false, tx);
10889
+ const lastInsertPageId = this.metadataPageManager.getLastInsertPageId(metadataPage);
10890
+ const batchDeleteData = [];
10891
+ const pagesToFree = [];
10892
+ let deletedCount = 0;
10893
+ for (const [pageId, items] of collections) {
10894
+ const page = await this.pfs.get(pageId, true, tx);
10895
+ if (!this.factory.isDataPage(page)) {
10896
+ continue;
10897
+ }
10898
+ let pageModified = false;
10899
+ for (const item of items) {
10900
+ const row = this.dataPageManager.getRow(page, item.slotIndex);
10901
+ if (this.rowManager.getDeletedFlag(row)) {
10902
+ continue;
10903
+ }
10904
+ if (this.rowManager.getOverflowFlag(row)) {
10905
+ const overflowPageId = bytesToNumber(this.rowManager.getBody(row));
10906
+ await this.pfs.freeChain(overflowPageId, tx);
10907
+ }
10908
+ this.rowManager.setDeletedFlag(row, true);
10909
+ pageModified = true;
10910
+ this.setRID(pageId, item.slotIndex);
10911
+ batchDeleteData.push([this.getRID(), item.pk]);
10912
+ deletedCount++;
10913
+ }
10914
+ if (pageModified) {
10915
+ await this.pfs.setPage(pageId, page, tx);
10916
+ }
10917
+ if (pageId !== lastInsertPageId) {
10918
+ const insertedRowCount = this.dataPageManager.getInsertedRowCount(page);
10919
+ let allDeleted = true;
10920
+ let i = 0;
10921
+ while (i < insertedRowCount) {
10922
+ const slotRow = this.dataPageManager.getRow(page, i);
10923
+ if (!this.rowManager.getDeletedFlag(slotRow)) {
10924
+ allDeleted = false;
10925
+ break;
10926
+ }
10927
+ i++;
10928
+ }
10929
+ if (allDeleted) {
10930
+ pagesToFree.push(pageId);
10931
+ }
10932
+ }
10933
+ }
10934
+ if (batchDeleteData.length === 0) {
10935
+ return;
10936
+ }
10937
+ await this.bptree.batchDelete(batchDeleteData);
10938
+ if (decrementRowCount) {
10939
+ const freshMetadataPage = await this.pfs.getMetadata(true, tx);
10940
+ const currentRowCount = this.metadataPageManager.getRowCount(freshMetadataPage);
10941
+ this.metadataPageManager.setRowCount(freshMetadataPage, currentRowCount - deletedCount);
10942
+ await this.pfs.setMetadata(freshMetadataPage, tx);
10943
+ }
10944
+ for (const pageId of pagesToFree) {
10945
+ await this.pfs.freeChain(pageId, tx);
10946
+ }
10947
+ }
10762
10948
  /**
10763
10949
  * Looks up the RID corresponding to the PK in the B+ Tree and returns the actual row.
10764
10950
  * @param pk Primary key of the row
@@ -10846,7 +11032,7 @@ var RowTableEngine = class {
10846
11032
  const sortedPageIds = Array.from(collections.keys()).sort((a, b) => a - b);
10847
11033
  await Promise.all(sortedPageIds.map(async (pageId) => {
10848
11034
  const items = collections.get(pageId);
10849
- const page = await this.pfs.get(pageId, tx);
11035
+ const page = await this.pfs.get(pageId, false, tx);
10850
11036
  if (!this.factory.isDataPage(page)) {
10851
11037
  throw new Error(`Page ${pageId} is not a data page`);
10852
11038
  }
@@ -10871,7 +11057,7 @@ var RowTableEngine = class {
10871
11057
  this.keyManager.setBufferFromKey(rid, this.ridBuffer);
10872
11058
  const pageId = this.keyManager.getPageId(this.ridBuffer);
10873
11059
  const slotIndex = this.keyManager.getSlotIndex(this.ridBuffer);
10874
- const page = await this.pfs.get(pageId, tx);
11060
+ const page = await this.pfs.get(pageId, false, tx);
10875
11061
  if (!this.factory.isDataPage(page)) {
10876
11062
  throw new Error(`RID not found for PK: ${pk}`);
10877
11063
  }
@@ -10881,7 +11067,7 @@ var RowTableEngine = class {
10881
11067
  return null;
10882
11068
  } else if (this.rowManager.getOverflowFlag(row)) {
10883
11069
  const overflowPageId = bytesToNumber(this.rowManager.getBody(row));
10884
- const overflowPage = await this.pfs.get(overflowPageId, tx);
11070
+ const overflowPage = await this.pfs.get(overflowPageId, false, tx);
10885
11071
  if (!this.factory.isOverflowPage(overflowPage)) {
10886
11072
  throw new Error(`Overflow page not found for RID: ${rid}`);
10887
11073
  }
@@ -10895,16 +11081,11 @@ var RowTableEngine = class {
10895
11081
  * @returns Row count
10896
11082
  */
10897
11083
  async getRowCount(tx) {
10898
- const metadataPage = await this.pfs.getMetadata(tx);
11084
+ const metadataPage = await this.pfs.getMetadata(false, tx);
10899
11085
  return this.metadataPageManager.getRowCount(metadataPage);
10900
11086
  }
10901
11087
  };
10902
11088
 
10903
- // src/utils/catchPromise.ts
10904
- async function catchPromise(promise) {
10905
- return promise.then((res) => [void 0, res]).catch((reason) => [reason]);
10906
- }
10907
-
10908
11089
  // src/core/transaction/LockManager.ts
10909
11090
  var LockManager = class {
10910
11091
  lock;
@@ -11006,16 +11187,20 @@ var Transaction = class {
11006
11187
  /**
11007
11188
  * Reads a page through the MVCC transaction.
11008
11189
  * @param pageId Page ID
11190
+ * @param copy Copy-on-Read
11009
11191
  * @returns Page data
11010
11192
  */
11011
- async __readPage(pageId) {
11193
+ async __readPage(pageId, copy) {
11012
11194
  const data = await this.mvccTx.read(pageId);
11013
11195
  if (data === null) {
11014
11196
  return new Uint8Array(this.pfs.pageSize);
11015
11197
  }
11016
- const copy = new Uint8Array(data.length);
11017
- copy.set(data);
11018
- return copy;
11198
+ if (copy) {
11199
+ const copy2 = new Uint8Array(data.length);
11200
+ copy2.set(data);
11201
+ return copy2;
11202
+ }
11203
+ return data;
11019
11204
  }
11020
11205
  /**
11021
11206
  * Writes a page through the MVCC transaction.
@@ -11025,9 +11210,7 @@ var Transaction = class {
11025
11210
  async __writePage(pageId, data) {
11026
11211
  const exists = await this.mvccTx.exists(pageId);
11027
11212
  if (exists) {
11028
- const copy = new Uint8Array(data.length);
11029
- copy.set(data);
11030
- await this.mvccTx.write(pageId, copy);
11213
+ await this.mvccTx.write(pageId, data);
11031
11214
  } else {
11032
11215
  await this.mvccTx.create(pageId, data);
11033
11216
  }
@@ -11433,46 +11616,46 @@ var DataplyAPI = class {
11433
11616
  * @returns The result of the callback.
11434
11617
  */
11435
11618
  async withWriteTransaction(callback, tx) {
11436
- this.logger.debug("Running with default write transaction");
11619
+ this.logger.debug("Running with write transaction");
11437
11620
  if (!tx) {
11438
11621
  const release = await this.acquireWriteLock();
11439
11622
  const internalTx = this.createTransaction();
11440
11623
  internalTx.__setWriteLockRelease(release);
11441
- const [error2, result2] = await catchPromise(this.txContext.run(internalTx, () => callback(internalTx)));
11442
- if (error2) {
11624
+ try {
11625
+ const result = await this.txContext.run(internalTx, () => callback(internalTx));
11626
+ await internalTx.commit();
11627
+ return result;
11628
+ } catch (error) {
11443
11629
  await internalTx.rollback();
11444
- throw error2;
11630
+ throw error;
11445
11631
  }
11446
- await internalTx.commit();
11447
- return result2;
11448
11632
  }
11449
11633
  if (!tx.__hasWriteLockRelease()) {
11450
11634
  const release = await this.acquireWriteLock();
11451
11635
  tx.__setWriteLockRelease(release);
11452
11636
  }
11453
- const [error, result] = await catchPromise(this.txContext.run(tx, () => callback(tx)));
11454
- if (error) {
11455
- throw error;
11637
+ if (this.txContext.get() === tx) {
11638
+ return callback(tx);
11456
11639
  }
11457
- return result;
11640
+ return this.txContext.run(tx, () => callback(tx));
11458
11641
  }
11459
11642
  async withReadTransaction(callback, tx) {
11460
- this.logger.debug("Running with default transaction");
11461
- const isInternalTx = !tx;
11643
+ this.logger.debug("Running with read transaction");
11462
11644
  if (!tx) {
11463
- tx = this.createTransaction();
11464
- }
11465
- const [error, result] = await catchPromise(this.txContext.run(tx, () => callback(tx)));
11466
- if (error) {
11467
- if (isInternalTx) {
11468
- await tx.rollback();
11645
+ const internalTx = this.createTransaction();
11646
+ try {
11647
+ const result = await this.txContext.run(internalTx, () => callback(internalTx));
11648
+ await internalTx.commit();
11649
+ return result;
11650
+ } catch (error) {
11651
+ await internalTx.rollback();
11652
+ throw error;
11469
11653
  }
11470
- throw error;
11471
11654
  }
11472
- if (isInternalTx) {
11473
- await tx.commit();
11655
+ if (this.txContext.get() === tx) {
11656
+ return callback(tx);
11474
11657
  }
11475
- return result;
11658
+ return this.txContext.run(tx, () => callback(tx));
11476
11659
  }
11477
11660
  /**
11478
11661
  * Runs a generator callback function within a transaction context.
@@ -11616,6 +11799,23 @@ var DataplyAPI = class {
11616
11799
  await this.rowTableEngine.delete(pk, decrementRowCount, tx2);
11617
11800
  }, tx);
11618
11801
  }
11802
+ /**
11803
+ * Deletes multiple data in batch.
11804
+ * If a transaction is not provided, it internally creates a single transaction to process.
11805
+ * @param pks Array of PKs to delete
11806
+ * @param decrementRowCount Whether to decrement the row count to metadata
11807
+ * @param tx Transaction
11808
+ */
11809
+ async deleteBatch(pks, decrementRowCount, tx) {
11810
+ this.logger.debug(`Deleting batch data: ${pks.length} items`);
11811
+ if (!this.initialized) {
11812
+ throw new Error("Dataply instance is not initialized");
11813
+ }
11814
+ return this.withWriteTransaction(async (tx2) => {
11815
+ decrementRowCount = decrementRowCount ?? true;
11816
+ await this.rowTableEngine.deleteBatch(pks, decrementRowCount, tx2);
11817
+ }, tx);
11818
+ }
11619
11819
  async select(pk, asRaw = false, tx) {
11620
11820
  this.logger.debug(`Selecting data for PK: ${pk}`);
11621
11821
  if (!this.initialized) {
@@ -11672,15 +11872,6 @@ var Dataply = class {
11672
11872
  get options() {
11673
11873
  return this.api.options;
11674
11874
  }
11675
- /**
11676
- * Creates a transaction.
11677
- * The created transaction object can be used to add or modify data.
11678
- * A transaction must be terminated by calling either `commit` or `rollback`.
11679
- * @returns Transaction object
11680
- */
11681
- createTransaction() {
11682
- return this.api.createTransaction();
11683
- }
11684
11875
  /**
11685
11876
  * Runs a write callback within a transaction context.
11686
11877
  */
@@ -11750,6 +11941,15 @@ var Dataply = class {
11750
11941
  async delete(pk, tx) {
11751
11942
  return this.api.delete(pk, true, tx);
11752
11943
  }
11944
+ /**
11945
+ * Deletes multiple data in batch.
11946
+ * If a transaction is not provided, it internally creates a single transaction to process.
11947
+ * @param pks Array of PKs to delete
11948
+ * @param tx Transaction
11949
+ */
11950
+ async deleteBatch(pks, tx) {
11951
+ return this.api.deleteBatch(pks, true, tx);
11952
+ }
11753
11953
  async select(pk, asRaw = false, tx) {
11754
11954
  return this.api.select(pk, asRaw, tx);
11755
11955
  }