@bunnykit/orm 0.1.4 → 0.1.6

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.
Files changed (32) hide show
  1. package/README.md +420 -81
  2. package/dist/bin/bunny.js +133 -19
  3. package/dist/src/config/BunnyConfig.d.ts +28 -0
  4. package/dist/src/config/BunnyConfig.js +14 -0
  5. package/dist/src/connection/Connection.d.ts +20 -1
  6. package/dist/src/connection/Connection.js +80 -2
  7. package/dist/src/connection/ConnectionManager.d.ts +40 -0
  8. package/dist/src/connection/ConnectionManager.js +104 -0
  9. package/dist/src/connection/TenantContext.d.ts +15 -0
  10. package/dist/src/connection/TenantContext.js +22 -0
  11. package/dist/src/index.d.ts +7 -0
  12. package/dist/src/index.js +4 -0
  13. package/dist/src/model/BelongsToMany.js +9 -6
  14. package/dist/src/model/Model.d.ts +41 -0
  15. package/dist/src/model/Model.js +217 -18
  16. package/dist/src/model/ModelNotFoundError.d.ts +5 -0
  17. package/dist/src/model/ModelNotFoundError.js +13 -0
  18. package/dist/src/model/MorphRelations.js +10 -10
  19. package/dist/src/query/Builder.d.ts +85 -7
  20. package/dist/src/query/Builder.js +489 -68
  21. package/dist/src/query/grammars/Grammar.d.ts +19 -0
  22. package/dist/src/query/grammars/Grammar.js +47 -0
  23. package/dist/src/query/grammars/MySqlGrammar.d.ts +13 -0
  24. package/dist/src/query/grammars/MySqlGrammar.js +59 -0
  25. package/dist/src/query/grammars/PostgresGrammar.d.ts +13 -0
  26. package/dist/src/query/grammars/PostgresGrammar.js +62 -0
  27. package/dist/src/query/grammars/SQLiteGrammar.d.ts +14 -0
  28. package/dist/src/query/grammars/SQLiteGrammar.js +63 -0
  29. package/dist/src/schema/Schema.js +44 -26
  30. package/dist/src/typegen/TypeGenerator.js +4 -2
  31. package/dist/src/types/index.d.ts +10 -0
  32. package/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ export declare abstract class Grammar {
2
+ abstract wrap(value: string): string;
3
+ wrapArray(values: string[]): string[];
4
+ escape(value: any): string;
5
+ abstract compileRandomOrder(): string;
6
+ compileOffset(offset: number, _limit?: number): string;
7
+ compileLock(lockMode?: string): string;
8
+ abstract compileDateWhere(type: string, column: string, operator: string, value: any): string;
9
+ abstract compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
10
+ abstract compileUpsert(table: string, columns: string[], values: string[], uniqueBy: string[], updateColumns: string[]): string;
11
+ compileUpdate(table: string, sets: string[], wheres: string, joins?: string[]): string;
12
+ compileDelete(table: string, wheres: string, joins?: string[], limit?: number): string;
13
+ abstract compileJsonContains(column: string, value: any): string;
14
+ abstract compileJsonLength(column: string, operator: string, value: any): string;
15
+ compileLike(column: string, value: string, not: boolean): string;
16
+ abstract compileRegexp(column: string, value: string, not: boolean): string;
17
+ abstract compileFullText(columns: string[], value: string): string;
18
+ abstract compileExplain(sql: string): string;
19
+ }
@@ -0,0 +1,47 @@
1
+ export class Grammar {
2
+ wrapArray(values) {
3
+ return values.map((v) => this.wrap(v));
4
+ }
5
+ escape(value) {
6
+ if (value === null)
7
+ return "NULL";
8
+ if (typeof value === "boolean")
9
+ return value ? "1" : "0";
10
+ if (typeof value === "number")
11
+ return String(value);
12
+ if (typeof value === "string" && value.toUpperCase().includes("CURRENT_TIMESTAMP"))
13
+ return value;
14
+ return `'${String(value).replace(/'/g, "''")}'`;
15
+ }
16
+ compileOffset(offset, _limit) {
17
+ return `OFFSET ${offset}`;
18
+ }
19
+ compileLock(lockMode) {
20
+ return lockMode ? ` ${lockMode}` : "";
21
+ }
22
+ compileUpdate(table, sets, wheres, joins) {
23
+ let sql = `UPDATE ${table}`;
24
+ if (joins && joins.length > 0) {
25
+ sql += ` ${joins.join(" ")}`;
26
+ }
27
+ sql += ` SET ${sets.join(", ")}`;
28
+ if (wheres)
29
+ sql += ` ${wheres}`;
30
+ return sql.trim();
31
+ }
32
+ compileDelete(table, wheres, joins, limit) {
33
+ let sql = `DELETE FROM ${table}`;
34
+ if (joins && joins.length > 0) {
35
+ sql += ` ${joins.join(" ")}`;
36
+ }
37
+ if (wheres)
38
+ sql += ` ${wheres}`;
39
+ if (limit !== undefined)
40
+ sql += ` LIMIT ${limit}`;
41
+ return sql.trim();
42
+ }
43
+ compileLike(column, value, not) {
44
+ const op = not ? "NOT LIKE" : "LIKE";
45
+ return `${column} ${op} ${this.escape(value)}`;
46
+ }
47
+ }
@@ -0,0 +1,13 @@
1
+ import { Grammar } from "./Grammar.js";
2
+ export declare class MySqlGrammar extends Grammar {
3
+ wrap(value: string): string;
4
+ compileRandomOrder(): string;
5
+ compileDateWhere(type: string, column: string, operator: string, value: any): string;
6
+ compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
7
+ compileUpsert(table: string, columns: string[], values: string[], _uniqueBy: string[], updateColumns: string[]): string;
8
+ compileJsonContains(column: string, value: any): string;
9
+ compileJsonLength(column: string, operator: string, value: any): string;
10
+ compileRegexp(column: string, value: string, not: boolean): string;
11
+ compileFullText(columns: string[], value: string): string;
12
+ compileExplain(sql: string): string;
13
+ }
@@ -0,0 +1,59 @@
1
+ import { Grammar } from "./Grammar.js";
2
+ export class MySqlGrammar extends Grammar {
3
+ wrap(value) {
4
+ if (value.includes(" as ")) {
5
+ const [column, alias] = value.split(/\s+as\s+/i);
6
+ return `${this.wrap(column)} AS ${this.wrap(alias)}`;
7
+ }
8
+ if (value.includes(".")) {
9
+ return value.split(".").map((v) => this.wrap(v)).join(".");
10
+ }
11
+ if (value === "*")
12
+ return value;
13
+ return `\`${value}\``;
14
+ }
15
+ compileRandomOrder() {
16
+ return "ORDER BY RAND()";
17
+ }
18
+ compileDateWhere(type, column, operator, value) {
19
+ switch (type) {
20
+ case "date":
21
+ return `DATE(${column}) ${operator} ${this.escape(value)}`;
22
+ case "day":
23
+ return `DAY(${column}) ${operator} ${this.escape(value)}`;
24
+ case "month":
25
+ return `MONTH(${column}) ${operator} ${this.escape(value)}`;
26
+ case "year":
27
+ return `YEAR(${column}) ${operator} ${this.escape(value)}`;
28
+ case "time":
29
+ return `TIME(${column}) ${operator} ${this.escape(value)}`;
30
+ default:
31
+ return `${column} ${operator} ${this.escape(value)}`;
32
+ }
33
+ }
34
+ compileInsertOrIgnore(table, columns, values) {
35
+ return `INSERT IGNORE INTO ${table} (${columns.map((c) => this.wrap(c)).join(", ")}) VALUES ${values.join(", ")}`;
36
+ }
37
+ compileUpsert(table, columns, values, _uniqueBy, updateColumns) {
38
+ const updateCols = updateColumns
39
+ .map((c) => `${this.wrap(c)} = VALUES(${this.wrap(c)})`)
40
+ .join(", ");
41
+ return `INSERT INTO ${table} (${columns.map((c) => this.wrap(c)).join(", ")}) VALUES ${values.join(", ")} ON DUPLICATE KEY UPDATE ${updateCols}`;
42
+ }
43
+ compileJsonContains(column, value) {
44
+ return `JSON_CONTAINS(${column}, ${this.escape(JSON.stringify(value))})`;
45
+ }
46
+ compileJsonLength(column, operator, value) {
47
+ return `JSON_LENGTH(${column}) ${operator} ${this.escape(value)}`;
48
+ }
49
+ compileRegexp(column, value, not) {
50
+ const op = not ? "NOT REGEXP" : "REGEXP";
51
+ return `${column} ${op} ${this.escape(value)}`;
52
+ }
53
+ compileFullText(columns, value) {
54
+ return `MATCH (${columns.join(", ")}) AGAINST (${this.escape(value)})`;
55
+ }
56
+ compileExplain(sql) {
57
+ return `EXPLAIN ${sql}`;
58
+ }
59
+ }
@@ -0,0 +1,13 @@
1
+ import { Grammar } from "./Grammar.js";
2
+ export declare class PostgresGrammar extends Grammar {
3
+ wrap(value: string): string;
4
+ compileRandomOrder(): string;
5
+ compileDateWhere(type: string, column: string, operator: string, value: any): string;
6
+ compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
7
+ compileUpsert(table: string, columns: string[], values: string[], uniqueBy: string[], updateColumns: string[]): string;
8
+ compileJsonContains(column: string, value: any): string;
9
+ compileJsonLength(column: string, operator: string, value: any): string;
10
+ compileRegexp(column: string, value: string, not: boolean): string;
11
+ compileFullText(columns: string[], value: string): string;
12
+ compileExplain(sql: string): string;
13
+ }
@@ -0,0 +1,62 @@
1
+ import { Grammar } from "./Grammar.js";
2
+ export class PostgresGrammar extends Grammar {
3
+ wrap(value) {
4
+ if (value.includes(" as ")) {
5
+ const [column, alias] = value.split(/\s+as\s+/i);
6
+ return `${this.wrap(column)} AS ${this.wrap(alias)}`;
7
+ }
8
+ if (value.includes(".")) {
9
+ return value.split(".").map((v) => this.wrap(v)).join(".");
10
+ }
11
+ if (value === "*")
12
+ return value;
13
+ return `"${value}"`;
14
+ }
15
+ compileRandomOrder() {
16
+ return "ORDER BY RANDOM()";
17
+ }
18
+ compileDateWhere(type, column, operator, value) {
19
+ switch (type) {
20
+ case "date":
21
+ return `(${column})::date ${operator} ${this.escape(value)}`;
22
+ case "day":
23
+ return `EXTRACT(DAY FROM ${column}) ${operator} ${this.escape(value)}`;
24
+ case "month":
25
+ return `EXTRACT(MONTH FROM ${column}) ${operator} ${this.escape(value)}`;
26
+ case "year":
27
+ return `EXTRACT(YEAR FROM ${column}) ${operator} ${this.escape(value)}`;
28
+ case "time":
29
+ return `(${column})::time ${operator} ${this.escape(value)}`;
30
+ default:
31
+ return `${column} ${operator} ${this.escape(value)}`;
32
+ }
33
+ }
34
+ compileInsertOrIgnore(table, columns, values) {
35
+ return `INSERT INTO ${table} (${columns.map((c) => this.wrap(c)).join(", ")}) VALUES ${values.join(", ")} ON CONFLICT DO NOTHING`;
36
+ }
37
+ compileUpsert(table, columns, values, uniqueBy, updateColumns) {
38
+ const updateCols = updateColumns
39
+ .map((c) => `${this.wrap(c)} = EXCLUDED.${this.wrap(c)}`)
40
+ .join(", ");
41
+ return `INSERT INTO ${table} (${columns.map((c) => this.wrap(c)).join(", ")}) VALUES ${values.join(", ")} ON CONFLICT (${uniqueBy.map((c) => this.wrap(c)).join(", ")}) DO UPDATE SET ${updateCols}`;
42
+ }
43
+ compileJsonContains(column, value) {
44
+ return `${column} @> ${this.escape(JSON.stringify([value]))}`;
45
+ }
46
+ compileJsonLength(column, operator, value) {
47
+ return `jsonb_array_length(${column}) ${operator} ${this.escape(value)}`;
48
+ }
49
+ compileRegexp(column, value, not) {
50
+ const op = not ? "!~" : "~";
51
+ return `${column} ${op} ${this.escape(value)}`;
52
+ }
53
+ compileFullText(columns, value) {
54
+ const cols = columns.length > 1
55
+ ? `concat_ws(' ', ${columns.join(", ")})`
56
+ : columns[0];
57
+ return `to_tsvector('english', ${cols}) @@ plainto_tsquery('english', ${this.escape(value)})`;
58
+ }
59
+ compileExplain(sql) {
60
+ return `EXPLAIN (FORMAT JSON) ${sql}`;
61
+ }
62
+ }
@@ -0,0 +1,14 @@
1
+ import { Grammar } from "./Grammar.js";
2
+ export declare class SQLiteGrammar extends Grammar {
3
+ wrap(value: string): string;
4
+ compileRandomOrder(): string;
5
+ compileOffset(offset: number, limit?: number): string;
6
+ compileDateWhere(type: string, column: string, operator: string, value: any): string;
7
+ compileInsertOrIgnore(table: string, columns: string[], values: string[]): string;
8
+ compileUpsert(table: string, columns: string[], values: string[], uniqueBy: string[], updateColumns: string[]): string;
9
+ compileJsonContains(column: string, value: any): string;
10
+ compileJsonLength(column: string, operator: string, value: any): string;
11
+ compileRegexp(column: string, value: string, not: boolean): string;
12
+ compileFullText(columns: string[], value: string): string;
13
+ compileExplain(sql: string): string;
14
+ }
@@ -0,0 +1,63 @@
1
+ import { Grammar } from "./Grammar.js";
2
+ export class SQLiteGrammar extends Grammar {
3
+ wrap(value) {
4
+ if (value.includes(" as ")) {
5
+ const [column, alias] = value.split(/\s+as\s+/i);
6
+ return `${this.wrap(column)} AS ${this.wrap(alias)}`;
7
+ }
8
+ if (value.includes(".")) {
9
+ return value.split(".").map((v) => this.wrap(v)).join(".");
10
+ }
11
+ if (value === "*")
12
+ return value;
13
+ return `"${value}"`;
14
+ }
15
+ compileRandomOrder() {
16
+ return "ORDER BY RANDOM()";
17
+ }
18
+ compileOffset(offset, limit) {
19
+ const limitSql = limit === undefined ? "LIMIT -1 " : "";
20
+ return `${limitSql}OFFSET ${offset}`;
21
+ }
22
+ compileDateWhere(type, column, operator, value) {
23
+ switch (type) {
24
+ case "date":
25
+ return `date(${column}) ${operator} ${this.escape(value)}`;
26
+ case "day":
27
+ return `CAST(strftime('%d', ${column}) AS INTEGER) ${operator} ${this.escape(value)}`;
28
+ case "month":
29
+ return `CAST(strftime('%m', ${column}) AS INTEGER) ${operator} ${this.escape(value)}`;
30
+ case "year":
31
+ return `CAST(strftime('%Y', ${column}) AS INTEGER) ${operator} ${this.escape(value)}`;
32
+ case "time":
33
+ return `time(${column}) ${operator} ${this.escape(value)}`;
34
+ default:
35
+ return `${column} ${operator} ${this.escape(value)}`;
36
+ }
37
+ }
38
+ compileInsertOrIgnore(table, columns, values) {
39
+ return `INSERT OR IGNORE INTO ${table} (${columns.map((c) => this.wrap(c)).join(", ")}) VALUES ${values.join(", ")}`;
40
+ }
41
+ compileUpsert(table, columns, values, uniqueBy, updateColumns) {
42
+ const updateCols = updateColumns
43
+ .map((c) => `${this.wrap(c)} = excluded.${this.wrap(c)}`)
44
+ .join(", ");
45
+ return `INSERT INTO ${table} (${columns.map((c) => this.wrap(c)).join(", ")}) VALUES ${values.join(", ")} ON CONFLICT(${uniqueBy.map((c) => this.wrap(c)).join(", ")}) DO UPDATE SET ${updateCols}`;
46
+ }
47
+ compileJsonContains(column, value) {
48
+ return `${column} IN (SELECT value FROM json_each(${column})) AND ${this.escape(value)} IN (SELECT value FROM json_each(${column}))`;
49
+ }
50
+ compileJsonLength(column, operator, value) {
51
+ return `(SELECT COUNT(*) FROM json_each(${column})) ${operator} ${this.escape(value)}`;
52
+ }
53
+ compileRegexp(column, value, not) {
54
+ const op = not ? "NOT REGEXP" : "REGEXP";
55
+ return `${column} ${op} ${this.escape(value)}`;
56
+ }
57
+ compileFullText(columns, value) {
58
+ return columns.map((c) => `${this.wrap(c)} LIKE ${this.escape(`%${value}%`)}`).join(" OR ");
59
+ }
60
+ compileExplain(sql) {
61
+ return `EXPLAIN QUERY PLAN ${sql}`;
62
+ }
63
+ }
@@ -2,16 +2,21 @@ import { Blueprint } from "./Blueprint.js";
2
2
  import { SQLiteGrammar } from "./grammars/SQLiteGrammar.js";
3
3
  import { MySqlGrammar } from "./grammars/MySqlGrammar.js";
4
4
  import { PostgresGrammar } from "./grammars/PostgresGrammar.js";
5
+ import { ConnectionManager } from "../connection/ConnectionManager.js";
6
+ import { TenantContext } from "../connection/TenantContext.js";
5
7
  export class Schema {
6
8
  static connection;
7
9
  static setConnection(connection) {
8
10
  this.connection = connection;
11
+ ConnectionManager.setDefault(connection);
9
12
  }
10
13
  static getConnection() {
11
- if (!this.connection) {
14
+ const tenantConnection = TenantContext.current()?.connection;
15
+ const connection = tenantConnection || this.connection || ConnectionManager.getDefault();
16
+ if (!connection) {
12
17
  throw new Error("No database connection set.");
13
18
  }
14
- return this.connection;
19
+ return connection;
15
20
  }
16
21
  static getGrammar() {
17
22
  const driver = this.getConnection().getDriverName();
@@ -28,9 +33,10 @@ export class Schema {
28
33
  const blueprint = new Blueprint(table);
29
34
  callback(blueprint);
30
35
  const grammar = this.getGrammar();
31
- const sql = grammar.compileCreate(blueprint, table);
36
+ const connection = this.getConnection();
37
+ const sql = grammar.compileCreate(blueprint, connection.qualifyTable(table));
32
38
  await this.getConnection().run(sql);
33
- const indexes = grammar.compileIndexes(blueprint, table);
39
+ const indexes = grammar.compileIndexes(blueprint, connection.qualifyTable(table));
34
40
  for (const indexSql of indexes) {
35
41
  await this.getConnection().run(indexSql);
36
42
  }
@@ -39,9 +45,10 @@ export class Schema {
39
45
  const blueprint = new Blueprint(table);
40
46
  callback(blueprint);
41
47
  const grammar = this.getGrammar();
42
- const sql = grammar.compileCreateIfNotExists(blueprint, table);
48
+ const connection = this.getConnection();
49
+ const sql = grammar.compileCreateIfNotExists(blueprint, connection.qualifyTable(table));
43
50
  await this.getConnection().run(sql);
44
- const indexes = grammar.compileIndexes(blueprint, table);
51
+ const indexes = grammar.compileIndexes(blueprint, connection.qualifyTable(table));
45
52
  for (const indexSql of indexes) {
46
53
  await this.getConnection().run(indexSql);
47
54
  }
@@ -50,9 +57,11 @@ export class Schema {
50
57
  const blueprint = new Blueprint(table);
51
58
  callback(blueprint);
52
59
  const grammar = this.getGrammar();
60
+ const connection = this.getConnection();
61
+ const qualifiedTable = connection.qualifyTable(table);
53
62
  for (const command of blueprint.commands) {
54
63
  if (command.name === "dropColumn") {
55
- const sql = grammar.compileDropColumn(table, command.parameters.column);
64
+ const sql = grammar.compileDropColumn(qualifiedTable, command.parameters.column);
56
65
  if (Array.isArray(sql)) {
57
66
  for (const s of sql)
58
67
  await this.getConnection().run(s);
@@ -62,7 +71,7 @@ export class Schema {
62
71
  }
63
72
  }
64
73
  else if (command.name === "renameColumn") {
65
- const sql = grammar.compileColumnRename(table, command.parameters.from, command.parameters.to);
74
+ const sql = grammar.compileColumnRename(qualifiedTable, command.parameters.from, command.parameters.to);
66
75
  await this.getConnection().run(sql);
67
76
  }
68
77
  else if (command.name === "dropIndex") {
@@ -75,33 +84,38 @@ export class Schema {
75
84
  await this.getConnection().run(`ALTER TABLE ${grammar.wrap(table)} DROP CONSTRAINT ${grammar.wrap(command.parameters.name)}`);
76
85
  }
77
86
  }
78
- const addSqls = grammar.compileAdd(blueprint, table);
87
+ const addSqls = grammar.compileAdd(blueprint, qualifiedTable);
79
88
  for (const sql of addSqls) {
80
89
  await this.getConnection().run(sql);
81
90
  }
82
- const indexes = grammar.compileIndexes(blueprint, table);
91
+ const indexes = grammar.compileIndexes(blueprint, qualifiedTable);
83
92
  for (const indexSql of indexes) {
84
93
  await this.getConnection().run(indexSql);
85
94
  }
86
- const fks = grammar.compileForeignKeys(blueprint, table);
95
+ const fks = grammar.compileForeignKeys(blueprint, qualifiedTable);
87
96
  for (const fkSql of fks) {
88
97
  await this.getConnection().run(fkSql);
89
98
  }
90
99
  }
91
100
  static async drop(table) {
92
101
  const grammar = this.getGrammar();
93
- await this.getConnection().run(grammar.compileDrop(table));
102
+ const connection = this.getConnection();
103
+ await connection.run(grammar.compileDrop(connection.qualifyTable(table)));
94
104
  }
95
105
  static async dropIfExists(table) {
96
106
  const grammar = this.getGrammar();
97
- await this.getConnection().run(grammar.compileDropIfExists(table));
107
+ const connection = this.getConnection();
108
+ await connection.run(grammar.compileDropIfExists(connection.qualifyTable(table)));
98
109
  }
99
110
  static async rename(from, to) {
100
111
  const grammar = this.getGrammar();
101
- await this.getConnection().run(grammar.compileRename(from, to));
112
+ const connection = this.getConnection();
113
+ await connection.run(grammar.compileRename(connection.qualifyTable(from), connection.qualifyTable(to)));
102
114
  }
103
115
  static async hasTable(table) {
104
- const driver = this.getConnection().getDriverName();
116
+ const connection = this.getConnection();
117
+ const driver = connection.getDriverName();
118
+ const schema = connection.getSchema() || "public";
105
119
  let sql;
106
120
  if (driver === "sqlite") {
107
121
  sql = `SELECT name FROM sqlite_master WHERE type='table' AND name='${table}'`;
@@ -110,41 +124,45 @@ export class Schema {
110
124
  sql = `SHOW TABLES LIKE '${table}'`;
111
125
  }
112
126
  else {
113
- sql = `SELECT * FROM information_schema.tables WHERE table_name = '${table}'`;
127
+ sql = `SELECT * FROM information_schema.tables WHERE table_schema = '${schema}' AND table_name = '${table}'`;
114
128
  }
115
- const result = await this.getConnection().query(sql);
129
+ const result = await connection.query(sql);
116
130
  return result.length > 0;
117
131
  }
118
132
  static async hasColumn(table, column) {
119
- const driver = this.getConnection().getDriverName();
133
+ const connection = this.getConnection();
134
+ const driver = connection.getDriverName();
135
+ const schema = connection.getSchema() || "public";
120
136
  let sql;
121
137
  if (driver === "sqlite") {
122
138
  sql = `PRAGMA table_info(${table})`;
123
- const result = await this.getConnection().query(sql);
139
+ const result = await connection.query(sql);
124
140
  return result.some((row) => row.name === column);
125
141
  }
126
142
  else if (driver === "mysql") {
127
143
  sql = `SHOW COLUMNS FROM ${table} LIKE '${column}'`;
128
144
  }
129
145
  else {
130
- sql = `SELECT column_name FROM information_schema.columns WHERE table_name = '${table}' AND column_name = '${column}'`;
146
+ sql = `SELECT column_name FROM information_schema.columns WHERE table_schema = '${schema}' AND table_name = '${table}' AND column_name = '${column}'`;
131
147
  }
132
- const result = await this.getConnection().query(sql);
148
+ const result = await connection.query(sql);
133
149
  return result.length > 0;
134
150
  }
135
151
  static async getColumn(table, column) {
136
- const driver = this.getConnection().getDriverName();
152
+ const connection = this.getConnection();
153
+ const driver = connection.getDriverName();
154
+ const schema = connection.getSchema() || "public";
137
155
  if (driver === "sqlite") {
138
- const rows = await this.getConnection().query(`PRAGMA table_info(${table})`);
156
+ const rows = await connection.query(`PRAGMA table_info(${table})`);
139
157
  const row = rows.find((item) => item.name === column);
140
158
  return row ? { name: row.name, type: row.type, primary: row.pk > 0, autoIncrement: false } : null;
141
159
  }
142
160
  if (driver === "mysql") {
143
- const rows = await this.getConnection().query(`SHOW COLUMNS FROM ${table} LIKE '${column}'`);
161
+ const rows = await connection.query(`SHOW COLUMNS FROM ${table} LIKE '${column}'`);
144
162
  const row = rows[0];
145
163
  return row ? { name: row.Field, type: row.Type, primary: row.Key === "PRI", autoIncrement: String(row.Extra || "").toLowerCase().includes("auto_increment") } : null;
146
164
  }
147
- const rows = await this.getConnection().query(`SELECT c.column_name, c.data_type, COALESCE(tc.constraint_type = 'PRIMARY KEY', false) AS primary_key
165
+ const rows = await connection.query(`SELECT c.column_name, c.data_type, COALESCE(tc.constraint_type = 'PRIMARY KEY', false) AS primary_key
148
166
  FROM information_schema.columns c
149
167
  LEFT JOIN information_schema.key_column_usage kcu
150
168
  ON c.table_schema = kcu.table_schema
@@ -153,7 +171,7 @@ export class Schema {
153
171
  LEFT JOIN information_schema.table_constraints tc
154
172
  ON kcu.table_schema = tc.table_schema
155
173
  AND kcu.constraint_name = tc.constraint_name
156
- WHERE c.table_schema = 'public'
174
+ WHERE c.table_schema = '${schema}'
157
175
  AND c.table_name = '${table}'
158
176
  AND c.column_name = '${column}'`);
159
177
  const row = rows[0];
@@ -119,7 +119,8 @@ export class TypeGenerator {
119
119
  sql = "SHOW TABLES";
120
120
  }
121
121
  else {
122
- sql = `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' AND table_type = 'BASE TABLE'`;
122
+ const schema = this.connection.getSchema() || "public";
123
+ sql = `SELECT table_name FROM information_schema.tables WHERE table_schema = '${schema}' AND table_type = 'BASE TABLE'`;
123
124
  }
124
125
  const rows = await this.connection.query(sql);
125
126
  if (driver === "sqlite") {
@@ -174,7 +175,8 @@ export class TypeGenerator {
174
175
  }));
175
176
  }
176
177
  // postgres
177
- const rows = await this.connection.query(`SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = '${table}' AND table_schema = 'public' ORDER BY ordinal_position`);
178
+ const schema = this.connection.getSchema() || "public";
179
+ const rows = await this.connection.query(`SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = '${table}' AND table_schema = '${schema}' ORDER BY ordinal_position`);
178
180
  return rows.map((r) => ({
179
181
  name: r.column_name,
180
182
  type: r.data_type,
@@ -40,8 +40,17 @@ export interface OrderClause {
40
40
  column: string;
41
41
  direction: "asc" | "desc";
42
42
  }
43
+ export interface HavingClause {
44
+ sql: string;
45
+ boolean: "and" | "or";
46
+ }
47
+ export interface UnionClause {
48
+ query: string;
49
+ all: boolean;
50
+ }
43
51
  export type ConnectionConfig = {
44
52
  url: string;
53
+ schema?: string;
45
54
  } | {
46
55
  driver: "sqlite" | "mysql" | "postgres";
47
56
  host?: string;
@@ -50,4 +59,5 @@ export type ConnectionConfig = {
50
59
  username?: string;
51
60
  password?: string;
52
61
  filename?: string;
62
+ schema?: string;
53
63
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunnykit/orm",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
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",