@bunnykit/orm 0.1.15 → 0.1.16

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.
@@ -20,8 +20,8 @@ export declare class Connection {
20
20
  withoutSchema(): Connection;
21
21
  qualifyTable(table: string): string;
22
22
  private quoteIdentifier;
23
- query(sqlString: string): Promise<any[]>;
24
- run(sqlString: string): Promise<any>;
23
+ query(sqlString: string, bindings?: any[]): Promise<any[]>;
24
+ run(sqlString: string, bindings?: any[]): Promise<any>;
25
25
  beginTransaction(): Promise<void>;
26
26
  commit(): Promise<void>;
27
27
  rollback(): Promise<void>;
@@ -75,11 +75,11 @@ export class Connection {
75
75
  quoteIdentifier(value) {
76
76
  return `"${value.replace(/"/g, '""')}"`;
77
77
  }
78
- async query(sqlString) {
79
- return (await this.driver.unsafe(sqlString));
78
+ async query(sqlString, bindings) {
79
+ return (await this.driver.unsafe(sqlString, bindings));
80
80
  }
81
- async run(sqlString) {
82
- return await this.driver.unsafe(sqlString);
81
+ async run(sqlString, bindings) {
82
+ return await this.driver.unsafe(sqlString, bindings);
83
83
  }
84
84
  async beginTransaction() {
85
85
  await this.driver.unsafe("BEGIN");
@@ -105,7 +105,7 @@ export class Connection {
105
105
  return await this.transaction(callback);
106
106
  }
107
107
  return await this.transaction(async (connection) => {
108
- await connection.run(`SET LOCAL ${setting} = ${connection.getGrammar().escape(tenantId)}`);
108
+ await connection.run(`SET LOCAL ${setting} = ${connection.getGrammar().placeholder(1)}`, [tenantId]);
109
109
  return await callback(connection);
110
110
  });
111
111
  }
@@ -31,6 +31,7 @@ export declare class Builder<T = Record<string, any>> {
31
31
  fromRaw?: string;
32
32
  updateJoins: string[];
33
33
  bindings: any[];
34
+ private parameterize;
34
35
  constructor(connection: Connection, table: string);
35
36
  private get grammar();
36
37
  setModel(model: ModelConstructor): this;
@@ -130,6 +131,7 @@ export declare class Builder<T = Record<string, any>> {
130
131
  clone(): Builder<T>;
131
132
  wrapColumn(value: string): string;
132
133
  escapeValue(value: any): string;
134
+ private addBinding;
133
135
  private compileWhereClause;
134
136
  private compileWheres;
135
137
  private compileNestedWheres;
@@ -19,6 +19,7 @@ export class Builder {
19
19
  fromRaw;
20
20
  updateJoins = [];
21
21
  bindings = [];
22
+ parameterize = false;
22
23
  constructor(connection, table) {
23
24
  this.connection = connection;
24
25
  this.tableName = table;
@@ -258,11 +259,11 @@ export class Builder {
258
259
  return this;
259
260
  }
260
261
  having(column, operator, value) {
261
- this.havings.push({ sql: `${this.grammar.wrap(column)} ${operator} ${this.grammar.escape(value)}`, boolean: "and" });
262
+ this.havings.push({ column, operator, value, boolean: "and" });
262
263
  return this;
263
264
  }
264
265
  orHaving(column, operator, value) {
265
- this.havings.push({ sql: `${this.grammar.wrap(column)} ${operator} ${this.grammar.escape(value)}`, boolean: "or" });
266
+ this.havings.push({ column, operator, value, boolean: "or" });
266
267
  return this;
267
268
  }
268
269
  havingRaw(sql, boolean = "and") {
@@ -465,13 +466,21 @@ export class Builder {
465
466
  escapeValue(value) {
466
467
  return this.grammar.escape(value);
467
468
  }
469
+ addBinding(value) {
470
+ this.bindings.push(value);
471
+ return this.grammar.placeholder(this.bindings.length);
472
+ }
468
473
  compileWhereClause(where, prefix) {
469
474
  if (where.type === "basic") {
470
- return `${prefix} ${this.grammar.wrap(where.column)} ${where.operator} ${this.grammar.escape(where.value)}`;
475
+ const value = this.parameterize ? this.addBinding(where.value) : this.grammar.escape(where.value);
476
+ return `${prefix} ${this.grammar.wrap(where.column)} ${where.operator} ${value}`;
471
477
  }
472
478
  else if (where.type === "in") {
473
479
  const op = where.operator === "NOT IN" ? "NOT IN" : "IN";
474
- return `${prefix} ${this.grammar.wrap(where.column)} ${op} (${where.value.map((v) => this.grammar.escape(v)).join(", ")})`;
480
+ const values = this.parameterize
481
+ ? where.value.map((v) => this.addBinding(v)).join(", ")
482
+ : where.value.map((v) => this.grammar.escape(v)).join(", ");
483
+ return `${prefix} ${this.grammar.wrap(where.column)} ${op} (${values})`;
475
484
  }
476
485
  else if (where.type === "null") {
477
486
  const op = where.operator === "NOT NULL" ? "IS NOT NULL" : "IS NULL";
@@ -479,7 +488,9 @@ export class Builder {
479
488
  }
480
489
  else if (where.type === "between") {
481
490
  const op = where.operator === "NOT BETWEEN" ? "NOT BETWEEN" : "BETWEEN";
482
- return `${prefix} ${this.grammar.wrap(where.column)} ${op} ${this.grammar.escape(where.value[0])} AND ${this.grammar.escape(where.value[1])}`;
491
+ const low = this.parameterize ? this.addBinding(where.value[0]) : this.grammar.escape(where.value[0]);
492
+ const high = this.parameterize ? this.addBinding(where.value[1]) : this.grammar.escape(where.value[1]);
493
+ return `${prefix} ${this.grammar.wrap(where.column)} ${op} ${low} AND ${high}`;
483
494
  }
484
495
  else if (where.type === "raw") {
485
496
  return `${prefix} ${where.column}`;
@@ -528,7 +539,11 @@ export class Builder {
528
539
  return "";
529
540
  const clauses = this.havings.map((h, index) => {
530
541
  const prefix = index === 0 ? "" : h.boolean.toUpperCase() + " ";
531
- return prefix + h.sql;
542
+ if (h.sql) {
543
+ return prefix + h.sql;
544
+ }
545
+ const value = this.parameterize ? this.addBinding(h.value) : this.grammar.escape(h.value);
546
+ return prefix + `${this.grammar.wrap(h.column)} ${h.operator} ${value}`;
532
547
  });
533
548
  return `HAVING ${clauses.join(" ")}`;
534
549
  }
@@ -567,7 +582,11 @@ export class Builder {
567
582
  return sql.replace(/\s+/g, " ").trim();
568
583
  }
569
584
  async get() {
570
- const results = await this.connection.query(this.toSql());
585
+ this.bindings = [];
586
+ this.parameterize = true;
587
+ const sql = this.toSql();
588
+ this.parameterize = false;
589
+ const results = await this.connection.query(sql, this.bindings);
571
590
  const rows = Array.from(results);
572
591
  if (this.model) {
573
592
  const models = rows.map((row) => {
@@ -748,11 +767,15 @@ export class Builder {
748
767
  if (records.length === 0)
749
768
  return;
750
769
  const columns = Object.keys(records[0]);
770
+ const bindings = [];
751
771
  const values = records.map((record) => {
752
- return `(${columns.map((col) => this.grammar.escape(record[col])).join(", ")})`;
772
+ return `(${columns.map((col) => {
773
+ bindings.push(record[col]);
774
+ return this.grammar.placeholder(bindings.length);
775
+ }).join(", ")})`;
753
776
  });
754
777
  const sql = `INSERT INTO ${this.grammar.wrap(this.tableName)} (${columns.map((c) => this.grammar.wrap(c)).join(", ")}) VALUES ${values.join(", ")}`;
755
- return await this.connection.run(sql);
778
+ return await this.connection.run(sql, bindings);
756
779
  }
757
780
  async insertGetId(data, idColumn = "id") {
758
781
  const result = await this.insert(data);
@@ -763,43 +786,65 @@ export class Builder {
763
786
  if (records.length === 0)
764
787
  return;
765
788
  const columns = Object.keys(records[0]);
789
+ const bindings = [];
766
790
  const values = records.map((record) => {
767
- return `(${columns.map((col) => this.grammar.escape(record[col])).join(", ")})`;
791
+ return `(${columns.map((col) => {
792
+ bindings.push(record[col]);
793
+ return this.grammar.placeholder(bindings.length);
794
+ }).join(", ")})`;
768
795
  });
769
796
  const sql = this.grammar.compileInsertOrIgnore(this.grammar.wrap(this.tableName), columns, values);
770
- return await this.connection.run(sql);
797
+ return await this.connection.run(sql, bindings);
771
798
  }
772
799
  async upsert(data, uniqueBy, updateColumns) {
773
800
  const records = Array.isArray(data) ? data : [data];
774
801
  if (records.length === 0)
775
802
  return;
776
803
  const columns = Object.keys(records[0]);
804
+ const bindings = [];
777
805
  const values = records.map((record) => {
778
- return `(${columns.map((col) => this.grammar.escape(record[col])).join(", ")})`;
806
+ return `(${columns.map((col) => {
807
+ bindings.push(record[col]);
808
+ return this.grammar.placeholder(bindings.length);
809
+ }).join(", ")})`;
779
810
  });
780
811
  const uniqueCols = Array.isArray(uniqueBy) ? uniqueBy : [uniqueBy];
781
812
  const updateCols = updateColumns ?? columns.filter((c) => !uniqueCols.includes(c));
782
813
  const sql = this.grammar.compileUpsert(this.grammar.wrap(this.tableName), columns, values, uniqueCols, updateCols);
783
- return await this.connection.run(sql);
814
+ return await this.connection.run(sql, bindings);
784
815
  }
785
816
  async update(data) {
786
- const sets = Object.entries(data)
787
- .map(([key, value]) => `${this.grammar.wrap(key)} = ${this.grammar.escape(value)}`)
788
- .join(", ");
789
- const sql = this.grammar.compileUpdate(this.grammar.wrap(this.tableName), sets.split(", "), this.compileWheres(), this.updateJoins);
790
- return await this.connection.run(sql);
817
+ this.bindings = [];
818
+ this.parameterize = true;
819
+ const sets = Object.entries(data).map(([key, value]) => {
820
+ this.bindings.push(value);
821
+ return `${this.grammar.wrap(key)} = ${this.grammar.placeholder(this.bindings.length)}`;
822
+ });
823
+ const whereSql = this.compileWheres();
824
+ this.parameterize = false;
825
+ const sql = this.grammar.compileUpdate(this.grammar.wrap(this.tableName), sets, whereSql, this.updateJoins);
826
+ return await this.connection.run(sql, this.bindings);
791
827
  }
792
828
  async delete() {
793
- const sql = this.grammar.compileDelete(this.grammar.wrap(this.tableName), this.compileWheres(), this.updateJoins, this.limitValue);
794
- return await this.connection.run(sql);
829
+ this.bindings = [];
830
+ this.parameterize = true;
831
+ const whereSql = this.compileWheres();
832
+ this.parameterize = false;
833
+ const sql = this.grammar.compileDelete(this.grammar.wrap(this.tableName), whereSql, this.updateJoins, this.limitValue);
834
+ return await this.connection.run(sql, this.bindings);
795
835
  }
796
836
  async increment(column, amount = 1, extra = {}) {
837
+ this.bindings = [];
838
+ this.parameterize = true;
797
839
  const sets = [`${this.grammar.wrap(column)} = ${this.grammar.wrap(column)} + ${amount}`];
798
840
  for (const [key, value] of Object.entries(extra)) {
799
- sets.push(`${this.grammar.wrap(key)} = ${this.grammar.escape(value)}`);
841
+ this.bindings.push(value);
842
+ sets.push(`${this.grammar.wrap(key)} = ${this.grammar.placeholder(this.bindings.length)}`);
800
843
  }
801
- const sql = `UPDATE ${this.grammar.wrap(this.tableName)} SET ${sets.join(", ")} ${this.compileWheres()}`;
802
- return await this.connection.run(sql.trim());
844
+ const whereSql = this.compileWheres();
845
+ this.parameterize = false;
846
+ const sql = `UPDATE ${this.grammar.wrap(this.tableName)} SET ${sets.join(", ")} ${whereSql}`;
847
+ return await this.connection.run(sql.trim(), this.bindings);
803
848
  }
804
849
  async decrement(column, amount = 1, extra = {}) {
805
850
  return this.increment(column, -amount, extra);
@@ -841,8 +886,11 @@ export class Builder {
841
886
  throw new Error("dd() called — execution halted.");
842
887
  }
843
888
  async explain() {
889
+ this.bindings = [];
890
+ this.parameterize = true;
844
891
  const sql = this.grammar.compileExplain(this.toSql());
845
- const results = await this.connection.query(sql);
892
+ this.parameterize = false;
893
+ const results = await this.connection.query(sql, this.bindings);
846
894
  return Array.from(results);
847
895
  }
848
896
  take(count) {
@@ -1,6 +1,7 @@
1
1
  export declare abstract class Grammar {
2
2
  abstract wrap(value: string): string;
3
3
  wrapArray(values: string[]): string[];
4
+ abstract placeholder(index: number): string;
4
5
  escape(value: any): string;
5
6
  abstract compileRandomOrder(): string;
6
7
  compileOffset(offset: number, _limit?: number): string;
@@ -1,6 +1,7 @@
1
1
  import { Grammar } from "./Grammar.js";
2
2
  export declare class MySqlGrammar extends Grammar {
3
3
  wrap(value: string): string;
4
+ placeholder(_index: number): string;
4
5
  compileRandomOrder(): string;
5
6
  compileDateWhere(type: string, column: string, operator: string, value: any): string;
6
7
  compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
@@ -12,6 +12,9 @@ export class MySqlGrammar extends Grammar {
12
12
  return value;
13
13
  return `\`${value}\``;
14
14
  }
15
+ placeholder(_index) {
16
+ return "?";
17
+ }
15
18
  compileRandomOrder() {
16
19
  return "ORDER BY RAND()";
17
20
  }
@@ -1,6 +1,7 @@
1
1
  import { Grammar } from "./Grammar.js";
2
2
  export declare class PostgresGrammar extends Grammar {
3
3
  wrap(value: string): string;
4
+ placeholder(index: number): string;
4
5
  compileRandomOrder(): string;
5
6
  compileDateWhere(type: string, column: string, operator: string, value: any): string;
6
7
  compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
@@ -12,6 +12,9 @@ export class PostgresGrammar extends Grammar {
12
12
  return value;
13
13
  return `"${value}"`;
14
14
  }
15
+ placeholder(index) {
16
+ return `$${index}`;
17
+ }
15
18
  compileRandomOrder() {
16
19
  return "ORDER BY RANDOM()";
17
20
  }
@@ -1,6 +1,7 @@
1
1
  import { Grammar } from "./Grammar.js";
2
2
  export declare class SQLiteGrammar extends Grammar {
3
3
  wrap(value: string): string;
4
+ placeholder(_index: number): string;
4
5
  compileRandomOrder(): string;
5
6
  compileOffset(offset: number, limit?: number): string;
6
7
  compileDateWhere(type: string, column: string, operator: string, value: any): string;
@@ -12,6 +12,9 @@ export class SQLiteGrammar extends Grammar {
12
12
  return value;
13
13
  return `"${value}"`;
14
14
  }
15
+ placeholder(_index) {
16
+ return "?";
17
+ }
15
18
  compileRandomOrder() {
16
19
  return "ORDER BY RANDOM()";
17
20
  }
@@ -41,7 +41,10 @@ export interface OrderClause {
41
41
  direction: "asc" | "desc";
42
42
  }
43
43
  export interface HavingClause {
44
- sql: string;
44
+ column?: string;
45
+ operator?: string;
46
+ value?: any;
47
+ sql?: string;
45
48
  boolean: "and" | "or";
46
49
  }
47
50
  export interface UnionClause {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnykit/orm",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "An Eloquent-inspired ORM for Bun's native SQL client supporting SQLite, MySQL, and PostgreSQL.",
5
5
  "license": "MIT",
6
6
  "packageManager": "bun@1.3.12",