@bunnykit/orm 0.1.21 → 0.1.23
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/src/connection/Connection.d.ts +1 -0
- package/dist/src/connection/Connection.js +29 -5
- package/dist/src/migration/Migrator.js +10 -10
- package/dist/src/model/Model.js +6 -5
- package/dist/src/query/Builder.d.ts +4 -1
- package/dist/src/query/Builder.js +81 -38
- package/dist/src/query/grammars/MySqlGrammar.js +1 -1
- package/dist/src/query/grammars/PostgresGrammar.js +1 -1
- package/dist/src/query/grammars/SQLiteGrammar.js +1 -1
- package/dist/src/schema/Schema.js +22 -13
- package/dist/src/schema/grammars/Grammar.js +1 -1
- package/dist/src/types/index.d.ts +2 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ export class Connection {
|
|
|
10
10
|
schema;
|
|
11
11
|
ownsDriver;
|
|
12
12
|
transactionDepth = 0;
|
|
13
|
+
savepointId = 0;
|
|
13
14
|
constructor(config, options = {}) {
|
|
14
15
|
this.config = config;
|
|
15
16
|
this.schema = options.schema || ("schema" in config ? config.schema : undefined);
|
|
@@ -83,16 +84,36 @@ export class Connection {
|
|
|
83
84
|
return await this.driver.unsafe(sqlString, bindings);
|
|
84
85
|
}
|
|
85
86
|
async beginTransaction() {
|
|
86
|
-
|
|
87
|
+
if (this.transactionDepth === 0) {
|
|
88
|
+
await this.driver.unsafe("BEGIN");
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
await this.driver.unsafe(`SAVEPOINT bunny_trans_${++this.savepointId}`);
|
|
92
|
+
}
|
|
87
93
|
this.transactionDepth++;
|
|
88
94
|
}
|
|
89
95
|
async commit() {
|
|
90
|
-
|
|
91
|
-
|
|
96
|
+
if (this.transactionDepth <= 0)
|
|
97
|
+
return;
|
|
98
|
+
if (this.transactionDepth === 1) {
|
|
99
|
+
await this.driver.unsafe("COMMIT");
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
await this.driver.unsafe(`RELEASE SAVEPOINT bunny_trans_${this.savepointId--}`);
|
|
103
|
+
}
|
|
104
|
+
this.transactionDepth--;
|
|
92
105
|
}
|
|
93
106
|
async rollback() {
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
if (this.transactionDepth <= 0)
|
|
108
|
+
return;
|
|
109
|
+
if (this.transactionDepth === 1) {
|
|
110
|
+
await this.driver.unsafe("ROLLBACK");
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await this.driver.unsafe(`ROLLBACK TO SAVEPOINT bunny_trans_${this.savepointId}`);
|
|
114
|
+
await this.driver.unsafe(`RELEASE SAVEPOINT bunny_trans_${this.savepointId--}`);
|
|
115
|
+
}
|
|
116
|
+
this.transactionDepth--;
|
|
96
117
|
}
|
|
97
118
|
isInTransaction() {
|
|
98
119
|
return this.transactionDepth > 0;
|
|
@@ -111,6 +132,9 @@ export class Connection {
|
|
|
111
132
|
if (this.driverName !== "postgres") {
|
|
112
133
|
return await this.transaction(callback);
|
|
113
134
|
}
|
|
135
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(setting)) {
|
|
136
|
+
throw new Error(`Invalid PostgreSQL setting name: ${setting}`);
|
|
137
|
+
}
|
|
114
138
|
return await this.transaction(async (connection) => {
|
|
115
139
|
await connection.run(`SET LOCAL ${setting} = ${connection.getGrammar().placeholder(1)}`, [tenantId]);
|
|
116
140
|
return await callback(connection);
|
|
@@ -195,30 +195,30 @@ export class Migrator {
|
|
|
195
195
|
const statements = [];
|
|
196
196
|
for (const row of tables) {
|
|
197
197
|
const table = row[key];
|
|
198
|
-
const createRows = await this.connection.query(`SHOW CREATE TABLE ${table}`);
|
|
198
|
+
const createRows = await this.connection.query(`SHOW CREATE TABLE ${this.connection.getGrammar().wrap(table)}`);
|
|
199
199
|
statements.push(`${createRows[0]["Create Table"]};`);
|
|
200
200
|
}
|
|
201
201
|
return statements.join("\n\n") + "\n";
|
|
202
202
|
}
|
|
203
203
|
const schema = this.connection.getSchema() || "public";
|
|
204
|
-
const tables = await this.connection.query(
|
|
204
|
+
const tables = await this.connection.query("SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_type = 'BASE TABLE' ORDER BY table_name", [schema]);
|
|
205
205
|
const statements = [];
|
|
206
206
|
for (const tableRow of tables) {
|
|
207
207
|
const table = tableRow.table_name;
|
|
208
208
|
const columns = await this.connection.query(`SELECT column_name, data_type, is_nullable, column_default, character_maximum_length, numeric_precision, numeric_scale
|
|
209
209
|
FROM information_schema.columns
|
|
210
|
-
WHERE table_schema =
|
|
211
|
-
ORDER BY ordinal_position
|
|
210
|
+
WHERE table_schema = $1 AND table_name = $2
|
|
211
|
+
ORDER BY ordinal_position`, [schema, table]);
|
|
212
212
|
const primaryKeys = await this.connection.query(`SELECT kcu.column_name
|
|
213
213
|
FROM information_schema.table_constraints tc
|
|
214
214
|
JOIN information_schema.key_column_usage kcu
|
|
215
215
|
ON tc.constraint_name = kcu.constraint_name
|
|
216
216
|
AND tc.table_schema = kcu.table_schema
|
|
217
217
|
AND tc.table_name = kcu.table_name
|
|
218
|
-
WHERE tc.table_schema =
|
|
219
|
-
AND tc.table_name =
|
|
218
|
+
WHERE tc.table_schema = $1
|
|
219
|
+
AND tc.table_name = $2
|
|
220
220
|
AND tc.constraint_type = 'PRIMARY KEY'
|
|
221
|
-
ORDER BY kcu.ordinal_position
|
|
221
|
+
ORDER BY kcu.ordinal_position`, [schema, table]);
|
|
222
222
|
const pkColumns = primaryKeys.map((row) => row.column_name);
|
|
223
223
|
const columnSql = columns.map((column) => {
|
|
224
224
|
let type = String(column.data_type).toUpperCase();
|
|
@@ -228,7 +228,7 @@ export class Migrator {
|
|
|
228
228
|
else if ((type === "NUMERIC" || type === "DECIMAL") && column.numeric_precision) {
|
|
229
229
|
type = `${type}(${column.numeric_precision}${column.numeric_scale ? `, ${column.numeric_scale}` : ""})`;
|
|
230
230
|
}
|
|
231
|
-
let sql = `
|
|
231
|
+
let sql = ` ${this.connection.getGrammar().wrap(column.column_name)} ${type}`;
|
|
232
232
|
if (column.is_nullable === "NO")
|
|
233
233
|
sql += " NOT NULL";
|
|
234
234
|
if (column.column_default !== null && column.column_default !== undefined)
|
|
@@ -236,9 +236,9 @@ export class Migrator {
|
|
|
236
236
|
return sql;
|
|
237
237
|
});
|
|
238
238
|
if (pkColumns.length > 0) {
|
|
239
|
-
columnSql.push(` PRIMARY KEY (${pkColumns.map((column) =>
|
|
239
|
+
columnSql.push(` PRIMARY KEY (${pkColumns.map((column) => this.connection.getGrammar().wrap(column)).join(", ")})`);
|
|
240
240
|
}
|
|
241
|
-
statements.push(`CREATE TABLE
|
|
241
|
+
statements.push(`CREATE TABLE ${this.connection.getGrammar().wrap(`${schema}.${table}`)} (\n${columnSql.join(",\n")}\n);`);
|
|
242
242
|
}
|
|
243
243
|
return statements.join("\n\n") + "\n";
|
|
244
244
|
}
|
package/dist/src/model/Model.js
CHANGED
|
@@ -815,21 +815,22 @@ export class Model {
|
|
|
815
815
|
async save() {
|
|
816
816
|
const constructor = this.constructor;
|
|
817
817
|
if (this.$exists) {
|
|
818
|
-
await ObserverRegistry.dispatch("updating", this);
|
|
819
818
|
await ObserverRegistry.dispatch("saving", this);
|
|
820
|
-
|
|
819
|
+
let dirty = this.getDirty();
|
|
820
|
+
if (Object.keys(dirty).length > 0 && constructor.timestamps) {
|
|
821
821
|
this.$attributes["updated_at"] = this.freshTimestamp();
|
|
822
|
+
dirty = this.getDirty();
|
|
822
823
|
}
|
|
823
|
-
const dirty = this.getDirty();
|
|
824
824
|
if (Object.keys(dirty).length > 0) {
|
|
825
|
+
await ObserverRegistry.dispatch("updating", this);
|
|
825
826
|
const pk = this.getAttribute(constructor.primaryKey);
|
|
826
827
|
const connection = this.getConnection();
|
|
827
828
|
await new Builder(connection, connection.qualifyTable(constructor.getTable()))
|
|
828
829
|
.where(constructor.primaryKey, pk)
|
|
829
830
|
.update(dirty);
|
|
831
|
+
await ObserverRegistry.dispatch("updated", this);
|
|
830
832
|
}
|
|
831
833
|
this.$original = { ...this.$attributes };
|
|
832
|
-
await ObserverRegistry.dispatch("updated", this);
|
|
833
834
|
await ObserverRegistry.dispatch("saved", this);
|
|
834
835
|
}
|
|
835
836
|
else {
|
|
@@ -930,10 +931,10 @@ export class Model {
|
|
|
930
931
|
}
|
|
931
932
|
async delete() {
|
|
932
933
|
const constructor = this.constructor;
|
|
933
|
-
await ObserverRegistry.dispatch("deleting", this);
|
|
934
934
|
const pk = this.getAttribute(constructor.primaryKey);
|
|
935
935
|
if (!pk)
|
|
936
936
|
return false;
|
|
937
|
+
await ObserverRegistry.dispatch("deleting", this);
|
|
937
938
|
if (constructor.softDeletes) {
|
|
938
939
|
const deletedAt = this.freshTimestamp();
|
|
939
940
|
const connection = this.getConnection();
|
|
@@ -134,7 +134,7 @@ export declare class Builder<T = Record<string, any>> {
|
|
|
134
134
|
private addBinding;
|
|
135
135
|
private compileWhereClause;
|
|
136
136
|
private compileWheres;
|
|
137
|
-
private
|
|
137
|
+
private compileWhereClauses;
|
|
138
138
|
private compileOrders;
|
|
139
139
|
private compileGroups;
|
|
140
140
|
private compileHavings;
|
|
@@ -161,11 +161,14 @@ export declare class Builder<T = Record<string, any>> {
|
|
|
161
161
|
chunk(count: number, callback: (items: T[]) => void | Promise<void>): Promise<void>;
|
|
162
162
|
each(count: number, callback: (item: T) => void | Promise<void>): Promise<void>;
|
|
163
163
|
cursor(chunkSize?: number): AsyncGenerator<T>;
|
|
164
|
+
private getResultAccessColumn;
|
|
165
|
+
private compileCursorWheres;
|
|
164
166
|
lazy(count?: number): AsyncGenerator<T>;
|
|
165
167
|
insert(data: ModelAttributeInput<T> | ModelAttributeInput<T>[]): Promise<any>;
|
|
166
168
|
insertGetId(data: ModelAttributeInput<T>, idColumn?: ModelColumn<T>): Promise<any>;
|
|
167
169
|
insertOrIgnore(data: ModelAttributeInput<T> | ModelAttributeInput<T>[]): Promise<any>;
|
|
168
170
|
upsert(data: ModelAttributeInput<T> | ModelAttributeInput<T>[], uniqueBy: ModelColumn<T> | ModelColumn<T>[], updateColumns?: ModelColumn<T>[]): Promise<any>;
|
|
171
|
+
private getUniformColumns;
|
|
169
172
|
update(data: ModelAttributeInput<T>): Promise<any>;
|
|
170
173
|
delete(): Promise<any>;
|
|
171
174
|
increment(column: ModelColumn<T>, amount?: number, extra?: ModelAttributeInput<T>): Promise<any>;
|
|
@@ -65,8 +65,7 @@ export class Builder {
|
|
|
65
65
|
const nested = new Builder(this.connection, this.tableName);
|
|
66
66
|
callback(nested);
|
|
67
67
|
if (nested.wheres.length > 0) {
|
|
68
|
-
|
|
69
|
-
this.wheres.push({ type: "raw", column: `(${sql})`, boolean, scope: undefined });
|
|
68
|
+
this.wheres.push({ type: "nested", column: "", query: nested.wheres, boolean, scope: undefined });
|
|
70
69
|
}
|
|
71
70
|
return this;
|
|
72
71
|
}
|
|
@@ -484,6 +483,10 @@ export class Builder {
|
|
|
484
483
|
else if (where.type === "raw") {
|
|
485
484
|
return `${prefix} ${where.column}`;
|
|
486
485
|
}
|
|
486
|
+
else if (where.type === "nested") {
|
|
487
|
+
const sql = this.compileWhereClauses(where.query || [], "");
|
|
488
|
+
return `${prefix} (${sql})`;
|
|
489
|
+
}
|
|
487
490
|
else if (where.type === "like") {
|
|
488
491
|
const sql = this.grammar.compileLike(this.grammar.wrap(where.column), where.value, !!where.not, this.parameterize ? (v) => this.addBinding(v) : undefined);
|
|
489
492
|
return `${prefix} ${sql}`;
|
|
@@ -540,20 +543,15 @@ export class Builder {
|
|
|
540
543
|
return "";
|
|
541
544
|
}
|
|
542
545
|
compileWheres() {
|
|
543
|
-
|
|
544
|
-
return "";
|
|
545
|
-
const clauses = this.wheres.map((where, index) => {
|
|
546
|
-
const prefix = index === 0 ? "WHERE" : where.boolean.toUpperCase();
|
|
547
|
-
return this.compileWhereClause(where, prefix);
|
|
548
|
-
});
|
|
549
|
-
return clauses.join(" ");
|
|
546
|
+
return this.compileWhereClauses(this.wheres, "WHERE");
|
|
550
547
|
}
|
|
551
|
-
|
|
552
|
-
if (
|
|
548
|
+
compileWhereClauses(wheres, firstPrefix) {
|
|
549
|
+
if (wheres.length === 0)
|
|
553
550
|
return "";
|
|
554
|
-
const clauses =
|
|
555
|
-
const prefix = index === 0 ? "" : where.boolean.toUpperCase();
|
|
556
|
-
|
|
551
|
+
const clauses = wheres.map((where, index) => {
|
|
552
|
+
const prefix = index === 0 ? "WHERE" : where.boolean.toUpperCase();
|
|
553
|
+
const adjustedPrefix = index === 0 ? firstPrefix : prefix;
|
|
554
|
+
return this.compileWhereClause(where, adjustedPrefix);
|
|
557
555
|
});
|
|
558
556
|
return clauses.join(" ").trim();
|
|
559
557
|
}
|
|
@@ -719,10 +717,15 @@ export class Builder {
|
|
|
719
717
|
return results.map((row) => row[column]);
|
|
720
718
|
}
|
|
721
719
|
async aggregate(sql, alias) {
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
720
|
+
const query = this.clone();
|
|
721
|
+
query.model = undefined;
|
|
722
|
+
query.columns = [`${sql} as ${alias}`];
|
|
723
|
+
query.orders = [];
|
|
724
|
+
query.limitValue = undefined;
|
|
725
|
+
query.offsetValue = undefined;
|
|
726
|
+
query.eagerLoads = [];
|
|
727
|
+
query.lockMode = undefined;
|
|
728
|
+
const result = await query.first();
|
|
726
729
|
return result ? result[alias] : null;
|
|
727
730
|
}
|
|
728
731
|
async count(column = "*") {
|
|
@@ -741,7 +744,11 @@ export class Builder {
|
|
|
741
744
|
return await this.aggregate(`MAX(${column})`, "max");
|
|
742
745
|
}
|
|
743
746
|
async paginate(perPage = 15, page = 1) {
|
|
744
|
-
const
|
|
747
|
+
const countQuery = this.clone();
|
|
748
|
+
countQuery.limitValue = undefined;
|
|
749
|
+
countQuery.offsetValue = undefined;
|
|
750
|
+
countQuery.orders = [];
|
|
751
|
+
const total = await countQuery.count();
|
|
745
752
|
const data = await this.clone().forPage(page, perPage).get();
|
|
746
753
|
return {
|
|
747
754
|
data,
|
|
@@ -781,9 +788,7 @@ export class Builder {
|
|
|
781
788
|
}
|
|
782
789
|
const orderColumn = this.orders[0]?.column || primaryKey;
|
|
783
790
|
const orderDirection = this.orders[0]?.direction || "asc";
|
|
784
|
-
|
|
785
|
-
const accessColumn = orderColumn.includes(".") ? orderColumn.split(".")[1] : orderColumn;
|
|
786
|
-
let lastValue = undefined;
|
|
791
|
+
let lastValues = undefined;
|
|
787
792
|
while (true) {
|
|
788
793
|
const builder = this.clone();
|
|
789
794
|
// Preserve multi-column ORDER BY, appending PK tie-breaker if not present
|
|
@@ -794,23 +799,15 @@ export class Builder {
|
|
|
794
799
|
}
|
|
795
800
|
builder.offsetValue = undefined;
|
|
796
801
|
builder.limitValue = chunkSize;
|
|
797
|
-
if (
|
|
798
|
-
const op = orderDirection === "asc" ? ">" : "<";
|
|
802
|
+
if (lastValues !== undefined) {
|
|
799
803
|
// Parenthesize existing wheres when appending cursor condition to preserve OR precedence
|
|
800
804
|
if (builder.wheres.length > 0) {
|
|
801
805
|
const hasOr = builder.wheres.some((w) => w.boolean === "or");
|
|
802
806
|
if (hasOr) {
|
|
803
|
-
builder.wheres = [{ type: "
|
|
807
|
+
builder.wheres = [{ type: "nested", column: "", query: builder.wheres, boolean: "and", scope: undefined }];
|
|
804
808
|
}
|
|
805
809
|
}
|
|
806
|
-
builder.wheres.push({
|
|
807
|
-
type: "basic",
|
|
808
|
-
column: orderColumn,
|
|
809
|
-
operator: op,
|
|
810
|
-
value: lastValue,
|
|
811
|
-
boolean: "and",
|
|
812
|
-
scope: undefined,
|
|
813
|
-
});
|
|
810
|
+
builder.wheres.push({ type: "nested", column: "", query: this.compileCursorWheres(builder.orders, lastValues), boolean: "and", scope: undefined });
|
|
814
811
|
}
|
|
815
812
|
const items = await builder.get();
|
|
816
813
|
if (items.length === 0)
|
|
@@ -821,11 +818,46 @@ export class Builder {
|
|
|
821
818
|
if (items.length < chunkSize)
|
|
822
819
|
break;
|
|
823
820
|
const lastItem = items[items.length - 1];
|
|
824
|
-
|
|
825
|
-
? lastItem[
|
|
821
|
+
lastValues = lastItem && typeof lastItem === "object"
|
|
822
|
+
? builder.orders.map((order) => lastItem[this.getResultAccessColumn(order.column)])
|
|
826
823
|
: undefined;
|
|
827
824
|
}
|
|
828
825
|
}
|
|
826
|
+
getResultAccessColumn(column) {
|
|
827
|
+
return column.includes(".") ? column.split(".").at(-1) : column;
|
|
828
|
+
}
|
|
829
|
+
compileCursorWheres(orders, values, index = 0) {
|
|
830
|
+
const order = orders[index];
|
|
831
|
+
const op = order.direction === "asc" ? ">" : "<";
|
|
832
|
+
const clauses = [{
|
|
833
|
+
type: "basic",
|
|
834
|
+
column: order.column,
|
|
835
|
+
operator: op,
|
|
836
|
+
value: values[index],
|
|
837
|
+
boolean: "and",
|
|
838
|
+
scope: undefined,
|
|
839
|
+
}];
|
|
840
|
+
if (index < orders.length - 1) {
|
|
841
|
+
clauses.push({
|
|
842
|
+
type: "nested",
|
|
843
|
+
column: "",
|
|
844
|
+
query: [
|
|
845
|
+
{
|
|
846
|
+
type: "basic",
|
|
847
|
+
column: order.column,
|
|
848
|
+
operator: "=",
|
|
849
|
+
value: values[index],
|
|
850
|
+
boolean: "and",
|
|
851
|
+
scope: undefined,
|
|
852
|
+
},
|
|
853
|
+
...this.compileCursorWheres(orders, values, index + 1),
|
|
854
|
+
],
|
|
855
|
+
boolean: "or",
|
|
856
|
+
scope: undefined,
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return clauses;
|
|
860
|
+
}
|
|
829
861
|
async *lazy(count = 1000) {
|
|
830
862
|
let page = 1;
|
|
831
863
|
while (true) {
|
|
@@ -844,7 +876,7 @@ export class Builder {
|
|
|
844
876
|
const records = Array.isArray(data) ? data : [data];
|
|
845
877
|
if (records.length === 0)
|
|
846
878
|
return;
|
|
847
|
-
const columns =
|
|
879
|
+
const columns = this.getUniformColumns(records);
|
|
848
880
|
const bindings = [];
|
|
849
881
|
const values = records.map((record) => {
|
|
850
882
|
return `(${columns.map((col) => {
|
|
@@ -863,7 +895,7 @@ export class Builder {
|
|
|
863
895
|
const records = Array.isArray(data) ? data : [data];
|
|
864
896
|
if (records.length === 0)
|
|
865
897
|
return;
|
|
866
|
-
const columns =
|
|
898
|
+
const columns = this.getUniformColumns(records);
|
|
867
899
|
const bindings = [];
|
|
868
900
|
const values = records.map((record) => {
|
|
869
901
|
return `(${columns.map((col) => {
|
|
@@ -878,7 +910,7 @@ export class Builder {
|
|
|
878
910
|
const records = Array.isArray(data) ? data : [data];
|
|
879
911
|
if (records.length === 0)
|
|
880
912
|
return;
|
|
881
|
-
const columns =
|
|
913
|
+
const columns = this.getUniformColumns(records);
|
|
882
914
|
const bindings = [];
|
|
883
915
|
const values = records.map((record) => {
|
|
884
916
|
return `(${columns.map((col) => {
|
|
@@ -891,6 +923,17 @@ export class Builder {
|
|
|
891
923
|
const sql = this.grammar.compileUpsert(this.grammar.wrap(this.tableName), columns, values, uniqueCols, updateCols);
|
|
892
924
|
return await this.connection.run(sql, bindings);
|
|
893
925
|
}
|
|
926
|
+
getUniformColumns(records) {
|
|
927
|
+
const columns = Object.keys(records[0]);
|
|
928
|
+
const signature = [...columns].sort().join("\0");
|
|
929
|
+
for (let i = 1; i < records.length; i++) {
|
|
930
|
+
const recordSignature = Object.keys(records[i]).sort().join("\0");
|
|
931
|
+
if (recordSignature !== signature) {
|
|
932
|
+
throw new Error("Bulk insert records must have the same columns.");
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
return columns;
|
|
936
|
+
}
|
|
894
937
|
async update(data) {
|
|
895
938
|
this.bindings = [];
|
|
896
939
|
this.parameterize = true;
|
|
@@ -135,48 +135,57 @@ export class Schema {
|
|
|
135
135
|
const driver = connection.getDriverName();
|
|
136
136
|
const schema = connection.getSchema() || "public";
|
|
137
137
|
let sql;
|
|
138
|
+
let bindings = [];
|
|
138
139
|
if (driver === "sqlite") {
|
|
139
|
-
sql =
|
|
140
|
+
sql = "SELECT name FROM sqlite_master WHERE type='table' AND name = ?";
|
|
141
|
+
bindings = [table];
|
|
140
142
|
}
|
|
141
143
|
else if (driver === "mysql") {
|
|
142
|
-
sql =
|
|
144
|
+
sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?";
|
|
145
|
+
bindings = [table];
|
|
143
146
|
}
|
|
144
147
|
else {
|
|
145
|
-
sql =
|
|
148
|
+
sql = "SELECT table_name FROM information_schema.tables WHERE table_schema = $1 AND table_name = $2";
|
|
149
|
+
bindings = [schema, table];
|
|
146
150
|
}
|
|
147
|
-
const result = await connection.query(sql);
|
|
151
|
+
const result = await connection.query(sql, bindings);
|
|
148
152
|
return result.length > 0;
|
|
149
153
|
}
|
|
150
154
|
static async hasColumn(table, column) {
|
|
151
155
|
const connection = this.getConnection();
|
|
152
156
|
const driver = connection.getDriverName();
|
|
153
157
|
const schema = connection.getSchema() || "public";
|
|
158
|
+
const grammar = this.getGrammar();
|
|
154
159
|
let sql;
|
|
160
|
+
let bindings = [];
|
|
155
161
|
if (driver === "sqlite") {
|
|
156
|
-
sql = `PRAGMA table_info(${table})`;
|
|
162
|
+
sql = `PRAGMA table_info(${grammar.wrap(table)})`;
|
|
157
163
|
const result = await connection.query(sql);
|
|
158
164
|
return result.some((row) => row.name === column);
|
|
159
165
|
}
|
|
160
166
|
else if (driver === "mysql") {
|
|
161
|
-
sql =
|
|
167
|
+
sql = "SELECT column_name FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?";
|
|
168
|
+
bindings = [table, column];
|
|
162
169
|
}
|
|
163
170
|
else {
|
|
164
|
-
sql =
|
|
171
|
+
sql = "SELECT column_name FROM information_schema.columns WHERE table_schema = $1 AND table_name = $2 AND column_name = $3";
|
|
172
|
+
bindings = [schema, table, column];
|
|
165
173
|
}
|
|
166
|
-
const result = await connection.query(sql);
|
|
174
|
+
const result = await connection.query(sql, bindings);
|
|
167
175
|
return result.length > 0;
|
|
168
176
|
}
|
|
169
177
|
static async getColumn(table, column) {
|
|
170
178
|
const connection = this.getConnection();
|
|
171
179
|
const driver = connection.getDriverName();
|
|
172
180
|
const schema = connection.getSchema() || "public";
|
|
181
|
+
const grammar = this.getGrammar();
|
|
173
182
|
if (driver === "sqlite") {
|
|
174
|
-
const rows = await connection.query(`PRAGMA table_info(${table})`);
|
|
183
|
+
const rows = await connection.query(`PRAGMA table_info(${grammar.wrap(table)})`);
|
|
175
184
|
const row = rows.find((item) => item.name === column);
|
|
176
185
|
return row ? { name: row.name, type: row.type, primary: row.pk > 0, autoIncrement: false } : null;
|
|
177
186
|
}
|
|
178
187
|
if (driver === "mysql") {
|
|
179
|
-
const rows = await connection.query(`
|
|
188
|
+
const rows = await connection.query("SELECT column_name AS Field, column_type AS Type, column_key AS `Key`, extra AS Extra FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = ? AND column_name = ?", [table, column]);
|
|
180
189
|
const row = rows[0];
|
|
181
190
|
return row ? { name: row.Field, type: row.Type, primary: row.Key === "PRI", autoIncrement: String(row.Extra || "").toLowerCase().includes("auto_increment") } : null;
|
|
182
191
|
}
|
|
@@ -189,9 +198,9 @@ export class Schema {
|
|
|
189
198
|
LEFT JOIN information_schema.table_constraints tc
|
|
190
199
|
ON kcu.table_schema = tc.table_schema
|
|
191
200
|
AND kcu.constraint_name = tc.constraint_name
|
|
192
|
-
WHERE c.table_schema =
|
|
193
|
-
AND c.table_name =
|
|
194
|
-
AND c.column_name =
|
|
201
|
+
WHERE c.table_schema = $1
|
|
202
|
+
AND c.table_name = $2
|
|
203
|
+
AND c.column_name = $3`, [schema, table, column]);
|
|
195
204
|
const row = rows[0];
|
|
196
205
|
return row ? { name: row.column_name, type: row.data_type, primary: !!row.primary_key, autoIncrement: false } : null;
|
|
197
206
|
}
|
|
@@ -5,7 +5,7 @@ export class Grammar {
|
|
|
5
5
|
return value.split(".").map((v) => this.wrap(v)).join(".");
|
|
6
6
|
}
|
|
7
7
|
const { prefix, suffix } = this.wrappers;
|
|
8
|
-
return `${prefix}${value}${suffix}`;
|
|
8
|
+
return `${prefix}${value.replaceAll(suffix, `${suffix}${suffix}`)}${suffix}`;
|
|
9
9
|
}
|
|
10
10
|
wrapArray(values) {
|
|
11
11
|
return values.map((v) => this.wrap(v));
|
|
@@ -29,7 +29,7 @@ export interface ForeignKeyDefinition {
|
|
|
29
29
|
onUpdate?: string;
|
|
30
30
|
}
|
|
31
31
|
export interface WhereClause {
|
|
32
|
-
type: "basic" | "in" | "null" | "raw" | "between" | "column" | "exists" | "like" | "regexp" | "fulltext" | "json_contains" | "json_length" | "date" | "all" | "any";
|
|
32
|
+
type: "basic" | "in" | "null" | "raw" | "nested" | "between" | "column" | "exists" | "like" | "regexp" | "fulltext" | "json_contains" | "json_length" | "date" | "all" | "any";
|
|
33
33
|
column: string;
|
|
34
34
|
columns?: string[];
|
|
35
35
|
operator?: string;
|
|
@@ -38,6 +38,7 @@ export interface WhereClause {
|
|
|
38
38
|
scope?: string;
|
|
39
39
|
not?: boolean;
|
|
40
40
|
dateType?: string;
|
|
41
|
+
query?: WhereClause[];
|
|
41
42
|
}
|
|
42
43
|
export interface OrderClause {
|
|
43
44
|
column: string;
|
package/package.json
CHANGED