@objectstack/driver-sql 7.8.0 → 8.0.0

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/index.mjs CHANGED
@@ -102,6 +102,10 @@ var SqlDriver = class {
102
102
  jsonFields: true,
103
103
  arrayFields: true,
104
104
  vectorSearch: false,
105
+ // Persistent, atomic autonumber sequences via `_objectstack_sequences`
106
+ // (see fillAutoNumberFields / getNextSequenceValue). The engine defers
107
+ // autonumber generation to this driver — it is the single source of truth.
108
+ autonumber: true,
105
109
  // Schema Management
106
110
  schemaSync: true,
107
111
  batchSchemaSync: false,
@@ -509,7 +513,10 @@ var SqlDriver = class {
509
513
  async bulkCreate(object, data, options) {
510
514
  this.auditMissingTenant(object, "bulkCreate", options);
511
515
  for (const row of data) {
512
- if (row && typeof row === "object") this.injectTenantOnInsert(object, row, options);
516
+ if (row && typeof row === "object") {
517
+ this.injectTenantOnInsert(object, row, options);
518
+ await this.fillAutoNumberFields(object, row, options);
519
+ }
513
520
  }
514
521
  const builder = this.getBuilder(object, options);
515
522
  return await builder.insert(data).returning("*");
@@ -804,7 +811,8 @@ var SqlDriver = class {
804
811
  ((_b = this.datetimeFields)[tableName] ?? (_b[tableName] = /* @__PURE__ */ new Set())).add(name);
805
812
  }
806
813
  if (type === "auto_number" || type === "autonumber") {
807
- const fmt = typeof field.format === "string" && field.format ? field.format : "{0000}";
814
+ const rawFmt = typeof field.autonumberFormat === "string" && field.autonumberFormat ? field.autonumberFormat : typeof field.format === "string" && field.format ? field.format : "";
815
+ const fmt = rawFmt || "{0000}";
808
816
  const m = fmt.match(/\{(0+)\}/);
809
817
  const padWidth = m ? m[1].length : 4;
810
818
  const prefix = m ? fmt.slice(0, m.index ?? 0) : fmt;
@@ -1192,7 +1200,7 @@ var SqlDriver = class {
1192
1200
  const methodIn = nextJoin === "or" ? "orWhereIn" : "whereIn";
1193
1201
  const methodNotIn = nextJoin === "or" ? "orWhereNotIn" : "whereNotIn";
1194
1202
  if (op === "contains") {
1195
- b[method](field, "like", `%${value}%`);
1203
+ this.applyContainsLike(b, method, field, value);
1196
1204
  return;
1197
1205
  }
1198
1206
  switch (op) {
@@ -1223,6 +1231,19 @@ var SqlDriver = class {
1223
1231
  }
1224
1232
  }
1225
1233
  }
1234
+ /**
1235
+ * Apply a `contains` substring match as a parameterized `LIKE '%…%'`, escaping
1236
+ * the LIKE metacharacters `%` / `_` (and the escape char `\`) in the user value
1237
+ * so they match literally instead of acting as wildcards — otherwise a value of
1238
+ * `%` matches every row (a filter-bypass, P0). Binds an explicit `ESCAPE '\'`
1239
+ * because SQLite does not honour a default escape character (MySQL/Postgres do,
1240
+ * but the explicit clause is correct for all three).
1241
+ */
1242
+ applyContainsLike(builder, method, field, value) {
1243
+ const escaped = String(value).replace(/[\\%_]/g, "\\$&");
1244
+ const rawMethod = method.startsWith("or") ? "orWhereRaw" : "whereRaw";
1245
+ builder[rawMethod]("?? LIKE ? ESCAPE ?", [field, `%${escaped}%`, "\\"]);
1246
+ }
1226
1247
  applyFilterCondition(builder, condition, logicalOp = "and", tableHint) {
1227
1248
  if (!condition || typeof condition !== "object") return;
1228
1249
  const table = tableHint ?? this.tableNameForBuilder(builder);
@@ -1279,7 +1300,7 @@ var SqlDriver = class {
1279
1300
  break;
1280
1301
  }
1281
1302
  case "$contains":
1282
- builder[method](field, "like", `%${opValue}%`);
1303
+ this.applyContainsLike(builder, method, field, opValue);
1283
1304
  break;
1284
1305
  default:
1285
1306
  builder[method](field, coerced);