workers-qb 1.13.0 → 1.14.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.js CHANGED
@@ -122,8 +122,9 @@ var QueryBuilderError = class _QueryBuilderError extends Error {
122
122
  Hint: ${options.hint}`;
123
123
  }
124
124
  this.message = enhancedMessage;
125
- if (Error.captureStackTrace) {
126
- Error.captureStackTrace(this, _QueryBuilderError);
125
+ const ErrorWithCapture = Error;
126
+ if (ErrorWithCapture.captureStackTrace) {
127
+ ErrorWithCapture.captureStackTrace(this, _QueryBuilderError);
127
128
  }
128
129
  }
129
130
  };
@@ -375,7 +376,13 @@ var SelectBuilder = class _SelectBuilder {
375
376
  let builtCondition = conditionParts[0] ?? "";
376
377
  for (let j = 0; j < conditionParts.length - 1; j++) {
377
378
  if (paramIndex >= currentInputParams.length) {
378
- throw new Error('Mismatch between "?" placeholders and parameters in where clause.');
379
+ const totalPlaceholders = currentInputConditions.join(" AND ").split("?").length - 1;
380
+ throw new ParameterMismatchError({
381
+ clause: "WHERE",
382
+ query: currentInputConditions.join(" AND "),
383
+ expectedParams: totalPlaceholders,
384
+ receivedParams: currentInputParams.length
385
+ });
379
386
  }
380
387
  const currentParam = currentInputParams[paramIndex++];
381
388
  const isSubQuery = typeof currentParam === "object" && currentParam !== null && ("tableName" in currentParam || "getOptions" in currentParam) && !currentParam.hasOwnProperty("_raw") || currentParam instanceof _SelectBuilder;
@@ -394,7 +401,13 @@ var SelectBuilder = class _SelectBuilder {
394
401
  processedNewConditions.push(builtCondition);
395
402
  }
396
403
  if (paramIndex < currentInputParams.length) {
397
- throw new Error('Too many parameters provided for the given "?" placeholders in where clause.');
404
+ const totalPlaceholders = currentInputConditions.join(" AND ").split("?").length - 1;
405
+ throw new ParameterMismatchError({
406
+ clause: "WHERE",
407
+ query: currentInputConditions.join(" AND "),
408
+ expectedParams: totalPlaceholders,
409
+ receivedParams: currentInputParams.length
410
+ });
398
411
  }
399
412
  return new _SelectBuilder(
400
413
  {
@@ -410,6 +423,62 @@ var SelectBuilder = class _SelectBuilder {
410
423
  this._fetchOne
411
424
  );
412
425
  }
426
+ /**
427
+ * Add an OR WHERE condition to the query.
428
+ * All previously added WHERE conditions are grouped together and ORed with the new condition.
429
+ * Subsequent `.where()` calls after `.orWhere()` are ANDed as independent conditions.
430
+ *
431
+ * @param conditions - The SQL condition string(s) (can use ? placeholders)
432
+ * @param params - The parameter(s) to bind to the ? placeholders
433
+ *
434
+ * @example
435
+ * // Simple OR condition
436
+ * qb.select('users').where('status = ?', 'active').orWhere('status = ?', 'pending').execute()
437
+ * // SELECT * FROM users WHERE (status = ?) OR (status = ?)
438
+ *
439
+ * @example
440
+ * // Multiple where conditions ORed together
441
+ * qb.select('users')
442
+ * .where('tenant_id = ?', 1)
443
+ * .where('status = ?', 'active')
444
+ * .orWhere('role = ?', 'superadmin')
445
+ * .execute()
446
+ * // SELECT * FROM users WHERE ((tenant_id = ?) AND (status = ?)) OR (role = ?)
447
+ */
448
+ orWhere(conditions, params) {
449
+ const existingConditions = this._options.where && typeof this._options.where === "object" && "conditions" in this._options.where ? this._options.where.conditions : [];
450
+ const existingParams = this._options.where && typeof this._options.where === "object" && "params" in this._options.where && this._options.where.params ? Array.isArray(this._options.where.params) ? this._options.where.params : [this._options.where.params] : [];
451
+ if (existingConditions.length === 0) {
452
+ return this.where(conditions, params);
453
+ }
454
+ const clearedBuilder = new _SelectBuilder(
455
+ {
456
+ ...this._options,
457
+ where: void 0
458
+ },
459
+ this._fetchAll,
460
+ this._fetchOne
461
+ );
462
+ const processedBuilder = clearedBuilder.where(conditions, params);
463
+ const processedWhere = processedBuilder._options.where;
464
+ const newConditions = processedWhere && typeof processedWhere === "object" && "conditions" in processedWhere ? processedWhere.conditions : [];
465
+ const newParams = processedWhere && typeof processedWhere === "object" && "params" in processedWhere && processedWhere.params ? Array.isArray(processedWhere.params) ? processedWhere.params : [processedWhere.params] : [];
466
+ const existingCombined = existingConditions.length === 1 ? existingConditions[0] : `(${existingConditions.join(") AND (")})`;
467
+ const newCombined = newConditions.length === 1 ? newConditions[0] : `(${newConditions.join(") AND (")})`;
468
+ const orCondition = `(${existingCombined}) OR (${newCombined})`;
469
+ return new _SelectBuilder(
470
+ {
471
+ // Spread processedBuilder._options to pick up any new subQueryPlaceholders/TokenNextId
472
+ ...processedBuilder._options,
473
+ where: {
474
+ conditions: [orCondition],
475
+ params: [...existingParams, ...newParams]
476
+ }
477
+ },
478
+ this._fetchAll,
479
+ this._fetchOne
480
+ );
481
+ }
413
482
  whereIn(fields, values) {
414
483
  let whereInCondition;
415
484
  let whereInParams;
@@ -438,6 +507,229 @@ var SelectBuilder = class _SelectBuilder {
438
507
  }
439
508
  return this.where(whereInCondition, whereInParams);
440
509
  }
510
+ /**
511
+ * Conditionally apply query modifications based on a runtime value.
512
+ * If condition is truthy, the callback is invoked with the current builder
513
+ * and its return value is used. If condition is falsy and an otherwise callback
514
+ * is provided, it is invoked instead. Otherwise, the builder is returned unchanged.
515
+ *
516
+ * @param condition - A value to check for truthiness
517
+ * @param callback - Function that receives the builder and returns a modified builder
518
+ * @param otherwise - Optional function applied when condition is falsy
519
+ *
520
+ * @example
521
+ * qb.select('users')
522
+ * .when(nameFilter, q => q.where('name LIKE ?', [`%${nameFilter}%`]))
523
+ * .when(sortByDate, q => q.orderBy({ created_at: 'DESC' }))
524
+ * .execute()
525
+ *
526
+ * @example
527
+ * // With otherwise callback
528
+ * qb.select('products')
529
+ * .when(
530
+ * inStock,
531
+ * q => q.where('stock > ?', 0),
532
+ * q => q.where('stock = ?', 0)
533
+ * )
534
+ * .execute()
535
+ */
536
+ when(condition, callback, otherwise) {
537
+ if (condition) {
538
+ return callback(this);
539
+ }
540
+ if (otherwise) {
541
+ return otherwise(this);
542
+ }
543
+ return this;
544
+ }
545
+ /**
546
+ * Add a WHERE column IS NULL condition.
547
+ *
548
+ * @param column - The column name to check for NULL
549
+ *
550
+ * @example
551
+ * qb.select('users').whereNull('deleted_at').execute()
552
+ * // SELECT * FROM users WHERE deleted_at IS NULL
553
+ */
554
+ whereNull(column) {
555
+ return this.where(`${column} IS NULL`);
556
+ }
557
+ /**
558
+ * Add a WHERE column IS NOT NULL condition.
559
+ *
560
+ * @param column - The column name to check for NOT NULL
561
+ *
562
+ * @example
563
+ * qb.select('users').whereNotNull('email_verified_at').execute()
564
+ * // SELECT * FROM users WHERE email_verified_at IS NOT NULL
565
+ */
566
+ whereNotNull(column) {
567
+ return this.where(`${column} IS NOT NULL`);
568
+ }
569
+ /**
570
+ * Add a WHERE column BETWEEN min AND max condition.
571
+ *
572
+ * @param column - The column name
573
+ * @param range - Tuple of [min, max] values
574
+ *
575
+ * @example
576
+ * qb.select('products').whereBetween('price', [10, 100]).execute()
577
+ * // SELECT * FROM products WHERE price BETWEEN ? AND ?
578
+ */
579
+ whereBetween(column, range) {
580
+ return this.where(`${column} BETWEEN ? AND ?`, [range[0], range[1]]);
581
+ }
582
+ /**
583
+ * Add a WHERE column NOT BETWEEN min AND max condition.
584
+ *
585
+ * @param column - The column name
586
+ * @param range - Tuple of [min, max] values
587
+ *
588
+ * @example
589
+ * qb.select('products').whereNotBetween('price', [10, 100]).execute()
590
+ * // SELECT * FROM products WHERE price NOT BETWEEN ? AND ?
591
+ */
592
+ whereNotBetween(column, range) {
593
+ return this.where(`${column} NOT BETWEEN ? AND ?`, [range[0], range[1]]);
594
+ }
595
+ /**
596
+ * Add an OR WHERE column IS NULL condition.
597
+ *
598
+ * @param column - The column name to check for NULL
599
+ *
600
+ * @example
601
+ * qb.select('users').where('active = ?', true).orWhereNull('deleted_at').execute()
602
+ * // SELECT * FROM users WHERE (active = ?) OR (deleted_at IS NULL)
603
+ */
604
+ orWhereNull(column) {
605
+ return this.orWhere(`${column} IS NULL`);
606
+ }
607
+ /**
608
+ * Add an OR WHERE column IS NOT NULL condition.
609
+ *
610
+ * @param column - The column name to check for NOT NULL
611
+ *
612
+ * @example
613
+ * qb.select('users').whereNull('deleted_at').orWhereNotNull('verified_at').execute()
614
+ * // SELECT * FROM users WHERE (deleted_at IS NULL) OR (verified_at IS NOT NULL)
615
+ */
616
+ orWhereNotNull(column) {
617
+ return this.orWhere(`${column} IS NOT NULL`);
618
+ }
619
+ /**
620
+ * Add an OR WHERE column BETWEEN min AND max condition.
621
+ *
622
+ * @param column - The column name
623
+ * @param range - Tuple of [min, max] values
624
+ *
625
+ * @example
626
+ * qb.select('products').where('active = ?', true).orWhereBetween('price', [10, 100]).execute()
627
+ * // SELECT * FROM products WHERE (active = ?) OR (price BETWEEN ? AND ?)
628
+ */
629
+ orWhereBetween(column, range) {
630
+ return this.orWhere(`${column} BETWEEN ? AND ?`, [range[0], range[1]]);
631
+ }
632
+ /**
633
+ * Add an OR WHERE column NOT BETWEEN min AND max condition.
634
+ *
635
+ * @param column - The column name
636
+ * @param range - Tuple of [min, max] values
637
+ *
638
+ * @example
639
+ * qb.select('products').where('featured = ?', true).orWhereNotBetween('price', [10, 100]).execute()
640
+ * // SELECT * FROM products WHERE (featured = ?) OR (price NOT BETWEEN ? AND ?)
641
+ */
642
+ orWhereNotBetween(column, range) {
643
+ return this.orWhere(`${column} NOT BETWEEN ? AND ?`, [range[0], range[1]]);
644
+ }
645
+ /**
646
+ * Add a WHERE column LIKE pattern condition.
647
+ *
648
+ * @param column - The column name
649
+ * @param pattern - The LIKE pattern (e.g., '%search%')
650
+ *
651
+ * @example
652
+ * qb.select('users').whereLike('name', '%john%').execute()
653
+ * // SELECT * FROM users WHERE name LIKE ?
654
+ */
655
+ whereLike(column, pattern) {
656
+ return this.where(`${column} LIKE ?`, [pattern]);
657
+ }
658
+ /**
659
+ * Add a WHERE column NOT LIKE pattern condition.
660
+ *
661
+ * @param column - The column name
662
+ * @param pattern - The LIKE pattern
663
+ *
664
+ * @example
665
+ * qb.select('users').whereNotLike('email', '%@spam.com').execute()
666
+ * // SELECT * FROM users WHERE email NOT LIKE ?
667
+ */
668
+ whereNotLike(column, pattern) {
669
+ return this.where(`${column} NOT LIKE ?`, [pattern]);
670
+ }
671
+ /**
672
+ * Add an OR WHERE column LIKE pattern condition.
673
+ *
674
+ * @param column - The column name
675
+ * @param pattern - The LIKE pattern (e.g., '%search%')
676
+ *
677
+ * @example
678
+ * qb.select('users').whereLike('name', '%john%').orWhereLike('email', '%john%').execute()
679
+ * // SELECT * FROM users WHERE (name LIKE ?) OR (email LIKE ?)
680
+ */
681
+ orWhereLike(column, pattern) {
682
+ return this.orWhere(`${column} LIKE ?`, [pattern]);
683
+ }
684
+ /**
685
+ * Add an OR WHERE column NOT LIKE pattern condition.
686
+ *
687
+ * @param column - The column name
688
+ * @param pattern - The LIKE pattern
689
+ *
690
+ * @example
691
+ * qb.select('users').where('active = ?', true).orWhereNotLike('email', '%@spam.com').execute()
692
+ * // SELECT * FROM users WHERE (active = ?) OR (email NOT LIKE ?)
693
+ */
694
+ orWhereNotLike(column, pattern) {
695
+ return this.orWhere(`${column} NOT LIKE ?`, [pattern]);
696
+ }
697
+ /**
698
+ * Add a WHERE column NOT IN (values) condition.
699
+ *
700
+ * @param fields - Column name(s) to check
701
+ * @param values - Values to exclude
702
+ *
703
+ * @example
704
+ * qb.select('users').whereNotIn('status', ['banned', 'suspended']).execute()
705
+ * // SELECT * FROM users WHERE (status) NOT IN (VALUES (?), (?))
706
+ */
707
+ whereNotIn(fields, values) {
708
+ let whereNotInCondition;
709
+ let whereNotInParams;
710
+ const separateWithComma = (prev, next) => prev + ", " + next;
711
+ if (values.length === 0) {
712
+ return new _SelectBuilder(
713
+ { ...this._options },
714
+ this._fetchAll,
715
+ this._fetchOne
716
+ );
717
+ }
718
+ if (!Array.isArray(fields)) {
719
+ whereNotInCondition = `(${fields}) NOT IN (VALUES `;
720
+ whereNotInCondition += values.map(() => "(?)").reduce(separateWithComma);
721
+ whereNotInCondition += ")";
722
+ whereNotInParams = values;
723
+ } else {
724
+ const fieldLength = fields.length;
725
+ whereNotInCondition = `(${fields.map((val) => val).reduce(separateWithComma)}) NOT IN (VALUES `;
726
+ const valuesString = `(${[...new Array(fieldLength).keys()].map(() => "?").reduce(separateWithComma)})`;
727
+ whereNotInCondition += [...new Array(values.length).keys()].map(() => valuesString).reduce(separateWithComma);
728
+ whereNotInCondition += ")";
729
+ whereNotInParams = values.flat();
730
+ }
731
+ return this.where(whereNotInCondition, whereNotInParams);
732
+ }
441
733
  join(join) {
442
734
  const joins = Array.isArray(join) ? join : [join];
443
735
  const processedJoins = joins.map((j) => {
@@ -478,7 +770,7 @@ var SelectBuilder = class _SelectBuilder {
478
770
  * Add a CROSS JOIN to the query.
479
771
  */
480
772
  crossJoin(params) {
481
- return this.join({ ...params, type: "CROSS" /* CROSS */, on: "1=1" });
773
+ return this.join({ ...params, type: "CROSS" /* CROSS */, on: "" });
482
774
  }
483
775
  /**
484
776
  * Add a NATURAL JOIN to the query.
@@ -635,7 +927,13 @@ var SelectBuilder = class _SelectBuilder {
635
927
  let builtCondition = conditionParts[0] ?? "";
636
928
  for (let j = 0; j < conditionParts.length - 1; j++) {
637
929
  if (paramIndex >= currentInputParams.length) {
638
- throw new Error('Mismatch between "?" placeholders and parameters in having clause.');
930
+ const totalPlaceholders = currentInputConditions.join(" AND ").split("?").length - 1;
931
+ throw new ParameterMismatchError({
932
+ clause: "HAVING",
933
+ query: currentInputConditions.join(" AND "),
934
+ expectedParams: totalPlaceholders,
935
+ receivedParams: currentInputParams.length
936
+ });
639
937
  }
640
938
  const currentParam = currentInputParams[paramIndex++];
641
939
  const isSubQuery = typeof currentParam === "object" && currentParam !== null && ("tableName" in currentParam || "getOptions" in currentParam) && !currentParam.hasOwnProperty("_raw") || currentParam instanceof _SelectBuilder;
@@ -654,7 +952,13 @@ var SelectBuilder = class _SelectBuilder {
654
952
  processedNewConditions.push(builtCondition);
655
953
  }
656
954
  if (paramIndex < currentInputParams.length) {
657
- throw new Error('Too many parameters provided for the given "?" placeholders in having clause.');
955
+ const totalPlaceholders = currentInputConditions.join(" AND ").split("?").length - 1;
956
+ throw new ParameterMismatchError({
957
+ clause: "HAVING",
958
+ query: currentInputConditions.join(" AND "),
959
+ expectedParams: totalPlaceholders,
960
+ receivedParams: currentInputParams.length
961
+ });
658
962
  }
659
963
  return new _SelectBuilder(
660
964
  {
@@ -766,6 +1070,18 @@ var SelectBuilder = class _SelectBuilder {
766
1070
  */
767
1071
  paginate(options) {
768
1072
  const { page, perPage } = options;
1073
+ if (!Number.isInteger(page) || page < 1) {
1074
+ throw new InvalidConfigurationError(
1075
+ `Invalid page value: ${page}. Page must be an integer >= 1.`,
1076
+ "Pass a positive integer for the page option, e.g. paginate({ page: 1, perPage: 20 })"
1077
+ );
1078
+ }
1079
+ if (!Number.isInteger(perPage) || perPage < 1) {
1080
+ throw new InvalidConfigurationError(
1081
+ `Invalid perPage value: ${perPage}. PerPage must be an integer >= 1.`,
1082
+ "Pass a positive integer for the perPage option, e.g. paginate({ page: 1, perPage: 20 })"
1083
+ );
1084
+ }
769
1085
  const offset = (page - 1) * perPage;
770
1086
  const countQuery = this._fetchOne(this._options);
771
1087
  const dataQuery = this._fetchAll({
@@ -1009,7 +1325,7 @@ var QueryBuilder = class {
1009
1325
  insert(params) {
1010
1326
  let args = [];
1011
1327
  if (typeof params.onConflict === "object") {
1012
- if (typeof params.onConflict?.where === "object" && !Array.isArray(params.onConflict?.where) && params.onConflict?.where?.params) {
1328
+ if (typeof params.onConflict?.where === "object" && !Array.isArray(params.onConflict?.where) && params.onConflict?.where?.params != null) {
1013
1329
  args = args.concat(params.onConflict.where?.params);
1014
1330
  }
1015
1331
  if (params.onConflict.data) {
@@ -1037,7 +1353,7 @@ var QueryBuilder = class {
1037
1353
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1038
1354
  update(params) {
1039
1355
  let args = this._parse_arguments(params.data);
1040
- if (typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params) {
1356
+ if (typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params != null) {
1041
1357
  if (Array.isArray(params.where?.params)) {
1042
1358
  args = params.where?.params.concat(args);
1043
1359
  } else {
@@ -1061,7 +1377,7 @@ var QueryBuilder = class {
1061
1377
  return this.execute(q);
1062
1378
  },
1063
1379
  this._delete(params),
1064
- typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params ? Array.isArray(params.where?.params) ? params.where?.params : [params.where?.params] : void 0,
1380
+ typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params != null ? Array.isArray(params.where?.params) ? params.where?.params : [params.where?.params] : void 0,
1065
1381
  "ALL" /* ALL */
1066
1382
  );
1067
1383
  }
@@ -1104,7 +1420,7 @@ var QueryBuilder = class {
1104
1420
  let onConflict = "";
1105
1421
  if (params.onConflict && typeof params.onConflict === "object") {
1106
1422
  onConflict = this._onConflict(params.onConflict);
1107
- if (typeof params.onConflict?.where === "object" && !Array.isArray(params.onConflict?.where) && params.onConflict?.where?.params) {
1423
+ if (typeof params.onConflict?.where === "object" && !Array.isArray(params.onConflict?.where) && params.onConflict?.where?.params != null) {
1108
1424
  if (Array.isArray(params.onConflict.where?.params)) {
1109
1425
  index += (params.onConflict.where?.params).length;
1110
1426
  } else {
@@ -1132,7 +1448,7 @@ var QueryBuilder = class {
1132
1448
  return `INSERT ${orConflict} INTO ${params.tableName} (${columns}) VALUES ${rows.join(", ")}` + onConflict + this._returning(params.returning);
1133
1449
  }
1134
1450
  _update(params) {
1135
- const whereParamsLength = typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params ? Array.isArray(params.where?.params) ? Object.keys(params.where?.params).length : 1 : 0;
1451
+ const whereParamsLength = typeof params.where === "object" && !Array.isArray(params.where) && params.where?.params != null ? Array.isArray(params.where?.params) ? Object.keys(params.where?.params).length : 1 : 0;
1136
1452
  let whereString = this._where(params.where);
1137
1453
  let parameterIndex = 1;
1138
1454
  if (whereString && whereString.match(/(?<!\d)\?(?!\d)/)) {
@@ -1153,7 +1469,7 @@ var QueryBuilder = class {
1153
1469
  }
1154
1470
  _delete(params) {
1155
1471
  return `DELETE
1156
- FROM ${params.tableName}` + this._where(params.where) + this._returning(params.returning) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset);
1472
+ FROM ${params.tableName}` + this._where(params.where) + this._orderBy(params.orderBy) + this._limit(params.limit) + this._offset(params.offset) + this._returning(params.returning);
1157
1473
  }
1158
1474
  _select(params, queryArgs) {
1159
1475
  let newQueryArgs = queryArgs;
@@ -1205,7 +1521,7 @@ var QueryBuilder = class {
1205
1521
  let primitiveParams = [];
1206
1522
  if (typeof value === "object" && !Array.isArray(value)) {
1207
1523
  conditionStrings = Array.isArray(value.conditions) ? value.conditions : [value.conditions];
1208
- if (value.params) {
1524
+ if (value.params != null) {
1209
1525
  primitiveParams = Array.isArray(value.params) ? value.params : [value.params];
1210
1526
  }
1211
1527
  } else if (Array.isArray(value)) {
@@ -1297,7 +1613,7 @@ var QueryBuilder = class {
1297
1613
  } else {
1298
1614
  tableSql = `(${context.toSQLCompiler(item.table, context.queryArgs)})`;
1299
1615
  }
1300
- if (item.type === "NATURAL" /* NATURAL */ || item.type === "NATURAL") {
1616
+ if (item.type === "NATURAL" /* NATURAL */ || item.type === "NATURAL" || !item.on) {
1301
1617
  joinQuery.push(`${type}JOIN ${tableSql}${item.alias ? ` AS ${item.alias}` : ""}`);
1302
1618
  } else {
1303
1619
  joinQuery.push(`${type}JOIN ${tableSql}${item.alias ? ` AS ${item.alias}` : ""} ON ${item.on}`);
@@ -1317,7 +1633,7 @@ var QueryBuilder = class {
1317
1633
  let primitiveParams = [];
1318
1634
  if (typeof value === "object" && !Array.isArray(value)) {
1319
1635
  conditionStrings = Array.isArray(value.conditions) ? value.conditions : [value.conditions];
1320
- if (value.params) {
1636
+ if (value.params != null) {
1321
1637
  primitiveParams = Array.isArray(value.params) ? value.params : [value.params];
1322
1638
  }
1323
1639
  } else if (Array.isArray(value)) {
@@ -1414,11 +1730,11 @@ var QueryBuilder = class {
1414
1730
  return ` ORDER BY ${result.join(", ")}`;
1415
1731
  }
1416
1732
  _limit(value) {
1417
- if (!value) return "";
1733
+ if (value == null) return "";
1418
1734
  return ` LIMIT ${value}`;
1419
1735
  }
1420
1736
  _offset(value) {
1421
- if (!value) return "";
1737
+ if (value == null) return "";
1422
1738
  return ` OFFSET ${value}`;
1423
1739
  }
1424
1740
  _returning(value) {
@@ -1474,10 +1790,10 @@ var syncMigrationsBuilder = class {
1474
1790
  const appliedMigrations = [];
1475
1791
  for (const migration of this.getUnapplied()) {
1476
1792
  this._builder.raw({
1477
- query: `
1478
- ${migration.sql}
1793
+ query: `${migration.sql}
1479
1794
  INSERT INTO ${this._tableName} (name)
1480
- values ('${migration.name}');`
1795
+ values (?);`,
1796
+ args: [migration.name]
1481
1797
  }).execute();
1482
1798
  appliedMigrations.push(migration);
1483
1799
  }
@@ -1529,10 +1845,10 @@ var asyncMigrationsBuilder = class {
1529
1845
  const appliedMigrations = [];
1530
1846
  for (const migration of await this.getUnapplied()) {
1531
1847
  await this._builder.raw({
1532
- query: `
1533
- ${migration.sql}
1848
+ query: `${migration.sql}
1534
1849
  INSERT INTO ${this._tableName} (name)
1535
- values ('${migration.name}');`
1850
+ values (?);`,
1851
+ args: [migration.name]
1536
1852
  }).execute();
1537
1853
  appliedMigrations.push(migration);
1538
1854
  }
@@ -1589,7 +1905,7 @@ var D1QB = class extends QueryBuilder {
1589
1905
  }
1590
1906
  _getQueryType(sql) {
1591
1907
  const trimmed = sql.trim().toUpperCase();
1592
- if (trimmed.startsWith("SELECT")) return "SELECT";
1908
+ if (trimmed.startsWith("SELECT") || trimmed.startsWith("WITH")) return "SELECT";
1593
1909
  if (trimmed.startsWith("INSERT")) return "INSERT";
1594
1910
  if (trimmed.startsWith("UPDATE")) return "UPDATE";
1595
1911
  if (trimmed.startsWith("DELETE")) return "DELETE";
@@ -1685,7 +2001,7 @@ var DOQB = class extends QueryBuilder {
1685
2001
  const resultArray = cursor.toArray();
1686
2002
  const rowsRead = cursor.rowsRead;
1687
2003
  const rowsWritten = cursor.rowsWritten;
1688
- if (query.fetchType == "ONE" /* ONE */) {
2004
+ if (query.fetchType === "ONE" /* ONE */) {
1689
2005
  return {
1690
2006
  results: resultArray.length > 0 ? resultArray[0] : void 0,
1691
2007
  rowsRead,
@@ -1706,7 +2022,7 @@ var DOQB = class extends QueryBuilder {
1706
2022
  }
1707
2023
  _getQueryType(sql) {
1708
2024
  const trimmed = sql.trim().toUpperCase();
1709
- if (trimmed.startsWith("SELECT")) return "SELECT";
2025
+ if (trimmed.startsWith("SELECT") || trimmed.startsWith("WITH")) return "SELECT";
1710
2026
  if (trimmed.startsWith("INSERT")) return "INSERT";
1711
2027
  if (trimmed.startsWith("UPDATE")) return "UPDATE";
1712
2028
  if (trimmed.startsWith("DELETE")) return "DELETE";
@@ -1754,15 +2070,47 @@ var DOQB = class extends QueryBuilder {
1754
2070
  };
1755
2071
 
1756
2072
  // src/databases/pg.ts
2073
+ var PGMigrationsBuilder = class extends asyncMigrationsBuilder {
2074
+ async initialize() {
2075
+ await this._builder.createTable({
2076
+ tableName: this._tableName,
2077
+ schema: `id SERIAL PRIMARY KEY,
2078
+ name TEXT UNIQUE,
2079
+ applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL`,
2080
+ ifNotExists: true
2081
+ }).execute();
2082
+ }
2083
+ async apply() {
2084
+ const appliedMigrations = [];
2085
+ for (const migration of await this.getUnapplied()) {
2086
+ await this._builder.raw({ query: "BEGIN" }).execute();
2087
+ try {
2088
+ await this._builder.raw({
2089
+ query: migration.sql
2090
+ }).execute();
2091
+ await this._builder.raw({
2092
+ query: `INSERT INTO ${this._tableName} (name)
2093
+ values (?);`,
2094
+ args: [migration.name]
2095
+ }).execute();
2096
+ await this._builder.raw({ query: "COMMIT" }).execute();
2097
+ appliedMigrations.push(migration);
2098
+ } catch (error) {
2099
+ await this._builder.raw({ query: "ROLLBACK" }).execute();
2100
+ throw error;
2101
+ }
2102
+ }
2103
+ return appliedMigrations;
2104
+ }
2105
+ };
1757
2106
  var PGQB = class extends QueryBuilder {
1758
2107
  db;
1759
- _migrationsBuilder = asyncMigrationsBuilder;
1760
2108
  constructor(db, options) {
1761
2109
  super(options);
1762
2110
  this.db = db;
1763
2111
  }
1764
2112
  migrations(options) {
1765
- return new asyncMigrationsBuilder(options, this);
2113
+ return new PGMigrationsBuilder(options, this);
1766
2114
  }
1767
2115
  async connect() {
1768
2116
  await this.db.connect();
@@ -1772,7 +2120,9 @@ var PGQB = class extends QueryBuilder {
1772
2120
  }
1773
2121
  async execute(query) {
1774
2122
  return await this.loggerWrapper(query, this.options.logger, async () => {
1775
- const queryString = query.query.replaceAll("?", "$");
2123
+ const maxNumbered = Math.max(0, ...[...query.query.matchAll(/\?(\d+)/g)].map((m) => Number.parseInt(m[1], 10)));
2124
+ let paramIndex = maxNumbered;
2125
+ const queryString = query.query.replace(/\?(\d+)?/g, (_, n) => n !== void 0 ? `$${n}` : `$${++paramIndex}`);
1776
2126
  let result;
1777
2127
  if (query.arguments) {
1778
2128
  result = await this.db.query({