@type32/tauri-sqlite-orm 0.1.20 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -33,6 +33,9 @@ var BaseQueryBuilder = class {
33
33
  params: this.params
34
34
  };
35
35
  }
36
+ toSQL() {
37
+ return this.build();
38
+ }
36
39
  };
37
40
 
38
41
  // src/operators.ts
@@ -43,6 +46,13 @@ var eq = (column, value, tableAlias) => {
43
46
  params: [value]
44
47
  };
45
48
  };
49
+ var ne = (column, value, tableAlias) => {
50
+ const columnName = tableAlias ? `${tableAlias}.${column._.name}` : column._.name;
51
+ return {
52
+ sql: `${columnName} != ?`,
53
+ params: [value]
54
+ };
55
+ };
46
56
  var and = (...conditions) => ({
47
57
  sql: conditions.map((c) => `(${c.sql})`).join(" AND "),
48
58
  params: conditions.flatMap((c) => c.params)
@@ -75,6 +85,22 @@ var like = (column, pattern) => ({
75
85
  sql: `${column._.name} LIKE ?`,
76
86
  params: [pattern]
77
87
  });
88
+ var ilike = (column, pattern) => ({
89
+ sql: `${column._.name} LIKE ? COLLATE NOCASE`,
90
+ params: [pattern]
91
+ });
92
+ var startsWith = (column, value) => ({
93
+ sql: `${column._.name} LIKE ?`,
94
+ params: [`${value}%`]
95
+ });
96
+ var endsWith = (column, value) => ({
97
+ sql: `${column._.name} LIKE ?`,
98
+ params: [`%${value}`]
99
+ });
100
+ var contains = (column, value) => ({
101
+ sql: `${column._.name} LIKE ?`,
102
+ params: [`%${value}%`]
103
+ });
78
104
  var isNull = (column) => ({
79
105
  sql: `${column._.name} IS NULL`,
80
106
  params: []
@@ -83,33 +109,65 @@ var isNotNull = (column) => ({
83
109
  sql: `${column._.name} IS NOT NULL`,
84
110
  params: []
85
111
  });
86
- var inArray = (column, values) => ({
87
- sql: `${column._.name} IN (${values.map(() => "?").join(",")})`,
88
- params: values
112
+ var exists = (subquery2) => ({
113
+ sql: `EXISTS (${subquery2.sql})`,
114
+ params: subquery2.params
89
115
  });
90
- var count = (column) => ({
91
- sql: `COUNT(${column ? column._.name : "*"})`,
92
- params: []
116
+ var notExists = (subquery2) => ({
117
+ sql: `NOT EXISTS (${subquery2.sql})`,
118
+ params: subquery2.params
93
119
  });
94
- var countDistinct = (column) => ({
95
- sql: `COUNT(DISTINCT ${column._.name})`,
96
- params: []
120
+ var eqSubquery = (column, subquery2) => ({
121
+ sql: `${column._.name} = ${subquery2.sql}`,
122
+ params: subquery2.params
97
123
  });
98
- var sum = (column) => ({
99
- sql: `SUM(${column._.name})`,
100
- params: []
124
+ var neSubquery = (column, subquery2) => ({
125
+ sql: `${column._.name} != ${subquery2.sql}`,
126
+ params: subquery2.params
101
127
  });
102
- var avg = (column) => ({
103
- sql: `AVG(${column._.name})`,
104
- params: []
128
+ var gtSubquery = (column, subquery2) => ({
129
+ sql: `${column._.name} > ${subquery2.sql}`,
130
+ params: subquery2.params
105
131
  });
106
- var max = (column) => ({
107
- sql: `MAX(${column._.name})`,
108
- params: []
132
+ var gteSubquery = (column, subquery2) => ({
133
+ sql: `${column._.name} >= ${subquery2.sql}`,
134
+ params: subquery2.params
109
135
  });
110
- var min = (column) => ({
111
- sql: `MIN(${column._.name})`,
112
- params: []
136
+ var ltSubquery = (column, subquery2) => ({
137
+ sql: `${column._.name} < ${subquery2.sql}`,
138
+ params: subquery2.params
139
+ });
140
+ var lteSubquery = (column, subquery2) => ({
141
+ sql: `${column._.name} <= ${subquery2.sql}`,
142
+ params: subquery2.params
143
+ });
144
+ var inArray = (column, values) => {
145
+ if ("_isSubquery" in values && values._isSubquery) {
146
+ return {
147
+ sql: `${column._.name} IN ${values.sql}`,
148
+ params: values.params
149
+ };
150
+ }
151
+ return {
152
+ sql: `${column._.name} IN (${values.map(() => "?").join(",")})`,
153
+ params: values
154
+ };
155
+ };
156
+ var notIn = (column, values) => {
157
+ if ("_isSubquery" in values && values._isSubquery) {
158
+ return {
159
+ sql: `${column._.name} NOT IN ${values.sql}`,
160
+ params: values.params
161
+ };
162
+ }
163
+ return {
164
+ sql: `${column._.name} NOT IN (${values.map(() => "?").join(",")})`,
165
+ params: values
166
+ };
167
+ };
168
+ var between = (column, min2, max2) => ({
169
+ sql: `${column._.name} BETWEEN ? AND ?`,
170
+ params: [min2, max2]
113
171
  });
114
172
 
115
173
  // src/builders/select.ts
@@ -175,42 +233,30 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
175
233
  sql2 += ` ${join.type} JOIN ${join.table._.name} ${join.alias} ON ${join.condition.sql}`;
176
234
  params.push(...join.condition.params);
177
235
  }
178
- for (const [relationName, include] of Object.entries(this.includeRelations)) {
179
- if (!include) continue;
180
- const relation = this.table.relations[relationName];
181
- if (!relation) {
182
- console.warn(
183
- `[Tauri-ORM] Relation "${relationName}" not found on table "${this.table._.name}". Skipping include.`
184
- );
185
- continue;
236
+ const processRelations = (parentTable, parentAlias, relations2, depth = 0) => {
237
+ if (depth > 10) {
238
+ console.warn("[Tauri-ORM] Maximum relation depth (10) exceeded. Skipping deeper relations.");
239
+ return;
186
240
  }
187
- const foreignTable = relation.foreignTable;
188
- const foreignAlias = `${this.selectedTableAlias}_${relationName}`;
189
- const aliasedColumns = Object.values(foreignTable._.columns).map(
190
- (col) => `${foreignAlias}.${col._.name} AS "${foreignAlias}.${col._.name}"`
191
- );
192
- this.selectedColumns.push(...aliasedColumns);
193
- if (relation.type === "one" && relation.fields && relation.references) {
194
- const conditions = relation.fields.map((field, i) => {
195
- const localColumn = `${this.selectedTableAlias}.${field._.name}`;
196
- const foreignColumn = `${foreignAlias}.${relation.references[i]._.name}`;
197
- return {
198
- sql: `${localColumn} = ${foreignColumn}`,
199
- params: []
200
- };
201
- });
202
- const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
203
- sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
204
- params.push(...condition.params);
205
- } else if (relation.type === "many") {
206
- const refRelation = Object.entries(foreignTable.relations).find(
207
- ([_, r]) => r.foreignTable === this.table
241
+ for (const [relationName, include] of Object.entries(relations2)) {
242
+ if (!include) continue;
243
+ const relation = parentTable.relations[relationName];
244
+ if (!relation) {
245
+ console.warn(
246
+ `[Tauri-ORM] Relation "${relationName}" not found on table "${parentTable._.name}". Skipping include.`
247
+ );
248
+ continue;
249
+ }
250
+ const foreignTable = relation.foreignTable;
251
+ const foreignAlias = `${parentAlias}_${relationName}`;
252
+ const aliasedColumns = Object.values(foreignTable._.columns).map(
253
+ (col) => `${foreignAlias}.${col._.name} AS "${foreignAlias}.${col._.name}"`
208
254
  );
209
- if (refRelation && refRelation[1].fields && refRelation[1].references) {
210
- const [_, relationConfig] = refRelation;
211
- const conditions = relationConfig.fields.map((field, i) => {
212
- const localColumn = `${foreignAlias}.${field._.name}`;
213
- const foreignColumn = `${this.selectedTableAlias}.${relationConfig.references[i]._.name}`;
255
+ this.selectedColumns.push(...aliasedColumns);
256
+ if (relation.type === "one" && relation.fields && relation.references) {
257
+ const conditions = relation.fields.map((field, i) => {
258
+ const localColumn = `${parentAlias}.${field._.name}`;
259
+ const foreignColumn = `${foreignAlias}.${relation.references[i]._.name}`;
214
260
  return {
215
261
  sql: `${localColumn} = ${foreignColumn}`,
216
262
  params: []
@@ -219,9 +265,64 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
219
265
  const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
220
266
  sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
221
267
  params.push(...condition.params);
268
+ } else if (relation.type === "many") {
269
+ const refRelation = Object.entries(foreignTable.relations).find(
270
+ ([_, r]) => r.foreignTable === parentTable
271
+ );
272
+ if (refRelation && refRelation[1].fields && refRelation[1].references) {
273
+ const [_, relationConfig] = refRelation;
274
+ const conditions = relationConfig.fields.map((field, i) => {
275
+ const localColumn = `${foreignAlias}.${field._.name}`;
276
+ const foreignColumn = `${parentAlias}.${relationConfig.references[i]._.name}`;
277
+ return {
278
+ sql: `${localColumn} = ${foreignColumn}`,
279
+ params: []
280
+ };
281
+ });
282
+ const condition = conditions.length > 1 ? and(...conditions) : conditions[0];
283
+ sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${condition.sql}`;
284
+ params.push(...condition.params);
285
+ }
286
+ } else if (relation.type === "manyToMany" && relation.junctionTable && relation.junctionFields && relation.junctionReferences) {
287
+ const junctionTable = relation.junctionTable;
288
+ const junctionAlias = `${foreignAlias}_junction`;
289
+ const parentTablePks = Object.values(parentTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
290
+ if (parentTablePks.length > 0 && relation.junctionFields.length > 0) {
291
+ const junctionConditions = relation.junctionFields.map((field, i) => {
292
+ const parentPk = parentTablePks[i] || parentTablePks[0];
293
+ const localColumn = `${parentAlias}.${parentPk}`;
294
+ const junctionColumn = `${junctionAlias}.${field._.name}`;
295
+ return {
296
+ sql: `${localColumn} = ${junctionColumn}`,
297
+ params: []
298
+ };
299
+ });
300
+ const junctionCondition = junctionConditions.length > 1 ? and(...junctionConditions) : junctionConditions[0];
301
+ sql2 += ` LEFT JOIN ${junctionTable._.name} ${junctionAlias} ON ${junctionCondition.sql}`;
302
+ params.push(...junctionCondition.params);
303
+ const foreignTablePks = Object.values(foreignTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
304
+ if (foreignTablePks.length > 0 && relation.junctionReferences.length > 0) {
305
+ const foreignConditions = relation.junctionReferences.map((field, i) => {
306
+ const foreignPk = foreignTablePks[i] || foreignTablePks[0];
307
+ const junctionColumn = `${junctionAlias}.${field._.name}`;
308
+ const foreignColumn = `${foreignAlias}.${foreignPk}`;
309
+ return {
310
+ sql: `${junctionColumn} = ${foreignColumn}`,
311
+ params: []
312
+ };
313
+ });
314
+ const foreignCondition = foreignConditions.length > 1 ? and(...foreignConditions) : foreignConditions[0];
315
+ sql2 += ` LEFT JOIN ${foreignTable._.name} ${foreignAlias} ON ${foreignCondition.sql}`;
316
+ params.push(...foreignCondition.params);
317
+ }
318
+ }
319
+ }
320
+ if (typeof include === "object" && include.with) {
321
+ processRelations(foreignTable, foreignAlias, include.with, depth + 1);
222
322
  }
223
323
  }
224
- }
324
+ };
325
+ processRelations(this.table, this.selectedTableAlias, this.includeRelations, 0);
225
326
  return { sql: sql2, params };
226
327
  }
227
328
  // Enhanced execute method that handles relation data mapping
@@ -232,7 +333,6 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
232
333
  this.query += joinSql;
233
334
  this.params.push(...joinParams);
234
335
  const { sql: sql2, params } = this.build();
235
- console.log("Executing SQL:", sql2, "with params:", params);
236
336
  const rawResults = await this.db.select(sql2, params);
237
337
  const hasIncludes = Object.values(this.includeRelations).some((i) => i);
238
338
  if (hasIncludes) {
@@ -262,6 +362,36 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
262
362
  return rawResults;
263
363
  }
264
364
  const groupedResults = /* @__PURE__ */ new Map();
365
+ const parseRelationPath = (tableAlias, baseAlias) => {
366
+ if (!tableAlias.startsWith(baseAlias + "_")) {
367
+ return [];
368
+ }
369
+ const path = tableAlias.substring(baseAlias.length + 1);
370
+ return path.split("_");
371
+ };
372
+ const setNestedValue = (obj, path, value, columnName) => {
373
+ let current = obj;
374
+ for (let i = 0; i < path.length; i++) {
375
+ const key = path[i];
376
+ if (i === path.length - 1) {
377
+ if (!current[key]) current[key] = {};
378
+ current[key][columnName] = value;
379
+ } else {
380
+ if (!current[key]) current[key] = {};
381
+ current = current[key];
382
+ }
383
+ }
384
+ };
385
+ const getNestedRelation = (table, path) => {
386
+ let currentTable = table;
387
+ let currentRelation = null;
388
+ for (const relationName of path) {
389
+ currentRelation = currentTable.relations[relationName];
390
+ if (!currentRelation) return null;
391
+ currentTable = currentRelation.foreignTable;
392
+ }
393
+ return currentRelation;
394
+ };
265
395
  for (const row of rawResults) {
266
396
  const mainTableKey = mainTablePks.map((pk) => row[`${this.selectedTableAlias}.${pk}`] ?? row[pk]).join("_");
267
397
  if (!groupedResults.has(mainTableKey)) {
@@ -275,11 +405,9 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
275
405
  if (tableAlias === this.selectedTableAlias) {
276
406
  result[columnName] = value;
277
407
  } else {
278
- const parts = tableAlias.split("_");
279
- if (parts.length >= 2 && parts[0] === this.selectedTableAlias) {
280
- const relationName = parts.slice(1).join("_");
281
- if (!relations2[relationName]) relations2[relationName] = {};
282
- relations2[relationName][columnName] = value;
408
+ const relationPath = parseRelationPath(tableAlias, this.selectedTableAlias);
409
+ if (relationPath.length > 0) {
410
+ setNestedValue(relations2, relationPath, value, columnName);
283
411
  } else {
284
412
  if (!result[tableAlias]) result[tableAlias] = {};
285
413
  result[tableAlias][columnName] = value;
@@ -289,24 +417,72 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
289
417
  result[key] = value;
290
418
  }
291
419
  }
292
- for (const [relName, relData] of Object.entries(relations2)) {
293
- const relationConfig = this.table.relations[relName];
294
- if (!relationConfig) continue;
295
- const hasData = Object.values(relData).some(
296
- (v) => v !== null && v !== void 0 && v !== ""
297
- );
298
- if (!hasData) continue;
299
- if (relationConfig.type === "many") {
300
- if (!result[relName]) result[relName] = [];
301
- const relatedPks = Object.values(relationConfig.foreignTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
302
- const relDataKey = relatedPks.map((pk) => relData[pk]).join("_");
303
- if (relatedPks.length === 0 || !result[relName].some((r) => relatedPks.map((pk) => r[pk]).join("_") === relDataKey)) {
304
- result[relName].push(relData);
420
+ const attachRelations = (target, relationsData, table, pathPrefix = []) => {
421
+ for (const [relName, relData] of Object.entries(relationsData)) {
422
+ const currentPath = [...pathPrefix, relName];
423
+ const relationConfig = getNestedRelation(table, currentPath);
424
+ if (!relationConfig) continue;
425
+ const hasDirectData = typeof relData === "object" && relData !== null && Object.entries(relData).some(([k, v]) => {
426
+ return typeof v !== "object" && v !== null && v !== void 0 && v !== "";
427
+ });
428
+ if (!hasDirectData && typeof relData === "object" && relData !== null) {
429
+ const hasNestedData = Object.values(relData).some(
430
+ (v) => typeof v === "object" && v !== null && Object.keys(v).length > 0
431
+ );
432
+ if (!hasNestedData) continue;
433
+ }
434
+ if (relationConfig.type === "many" || relationConfig.type === "manyToMany") {
435
+ if (!target[relName]) target[relName] = [];
436
+ const directData = {};
437
+ const nestedData = {};
438
+ if (typeof relData === "object" && relData !== null) {
439
+ for (const [k, v] of Object.entries(relData)) {
440
+ if (typeof v === "object" && v !== null) {
441
+ nestedData[k] = v;
442
+ } else {
443
+ directData[k] = v;
444
+ }
445
+ }
446
+ }
447
+ const hasData = Object.values(directData).some(
448
+ (v) => v !== null && v !== void 0 && v !== ""
449
+ );
450
+ if (hasData) {
451
+ const relatedPks = Object.values(relationConfig.foreignTable._.columns).filter((c) => c.options.primaryKey).map((c) => c._.name);
452
+ const relDataKey = relatedPks.map((pk) => directData[pk]).join("_");
453
+ if (relatedPks.length === 0 || !target[relName].some((r) => relatedPks.map((pk) => r[pk]).join("_") === relDataKey)) {
454
+ const newItem = { ...directData };
455
+ if (Object.keys(nestedData).length > 0) {
456
+ attachRelations(newItem, nestedData, relationConfig.foreignTable, []);
457
+ }
458
+ target[relName].push(newItem);
459
+ }
460
+ }
461
+ } else {
462
+ const directData = {};
463
+ const nestedData = {};
464
+ if (typeof relData === "object" && relData !== null) {
465
+ for (const [k, v] of Object.entries(relData)) {
466
+ if (typeof v === "object" && v !== null) {
467
+ nestedData[k] = v;
468
+ } else {
469
+ directData[k] = v;
470
+ }
471
+ }
472
+ }
473
+ const hasData = Object.values(directData).some(
474
+ (v) => v !== null && v !== void 0 && v !== ""
475
+ );
476
+ if (hasData || Object.keys(nestedData).length > 0) {
477
+ target[relName] = { ...directData };
478
+ if (Object.keys(nestedData).length > 0) {
479
+ attachRelations(target[relName], nestedData, relationConfig.foreignTable, []);
480
+ }
481
+ }
305
482
  }
306
- } else {
307
- result[relName] = relData;
308
483
  }
309
- }
484
+ };
485
+ attachRelations(result, relations2, this.table);
310
486
  }
311
487
  return Array.from(groupedResults.values());
312
488
  }
@@ -319,6 +495,82 @@ var SelectQueryBuilder = class extends BaseQueryBuilder {
319
495
  const result = await this.execute();
320
496
  return result[0];
321
497
  }
498
+ toSQL() {
499
+ const { sql: joinSql, params: joinParams } = this.buildJoins();
500
+ const distinct = this.isDistinct ? "DISTINCT " : "";
501
+ const finalQuery = `SELECT ${distinct}${this.selectedColumns.join(", ")} ${this.query}${joinSql}`;
502
+ return {
503
+ sql: finalQuery,
504
+ params: [...this.params, ...joinParams]
505
+ };
506
+ }
507
+ };
508
+
509
+ // src/errors.ts
510
+ var TauriORMError = class extends Error {
511
+ constructor(message) {
512
+ super(message);
513
+ this.name = "TauriORMError";
514
+ if (Error.captureStackTrace) {
515
+ Error.captureStackTrace(this, this.constructor);
516
+ }
517
+ }
518
+ };
519
+ var QueryBuilderError = class extends TauriORMError {
520
+ constructor(message) {
521
+ super(message);
522
+ this.name = "QueryBuilderError";
523
+ }
524
+ };
525
+ var MissingWhereClauseError = class extends QueryBuilderError {
526
+ constructor(operation, tableName) {
527
+ super(
528
+ `${operation} operation on table "${tableName}" requires a WHERE clause to prevent accidental data loss. Use .where() to specify conditions, or use .allowGlobalOperation() to explicitly allow operations without WHERE.`
529
+ );
530
+ this.name = "MissingWhereClauseError";
531
+ }
532
+ };
533
+ var ValidationError = class extends TauriORMError {
534
+ constructor(message) {
535
+ super(message);
536
+ this.name = "ValidationError";
537
+ }
538
+ };
539
+ var InsertValidationError = class extends ValidationError {
540
+ constructor(message) {
541
+ super(message);
542
+ this.name = "InsertValidationError";
543
+ }
544
+ };
545
+ var UpdateValidationError = class extends ValidationError {
546
+ constructor(message) {
547
+ super(message);
548
+ this.name = "UpdateValidationError";
549
+ }
550
+ };
551
+ var MigrationError = class extends TauriORMError {
552
+ constructor(message) {
553
+ super(message);
554
+ this.name = "MigrationError";
555
+ }
556
+ };
557
+ var RelationError = class extends TauriORMError {
558
+ constructor(message) {
559
+ super(message);
560
+ this.name = "RelationError";
561
+ }
562
+ };
563
+ var ColumnNotFoundError = class extends TauriORMError {
564
+ constructor(columnName, tableName) {
565
+ super(`Column "${columnName}" does not exist on table "${tableName}"`);
566
+ this.name = "ColumnNotFoundError";
567
+ }
568
+ };
569
+ var TableNotFoundError = class extends TauriORMError {
570
+ constructor(tableName) {
571
+ super(`Table "${tableName}" not found in schema`);
572
+ this.name = "TableNotFoundError";
573
+ }
322
574
  };
323
575
 
324
576
  // src/builders/update.ts
@@ -330,10 +582,37 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
330
582
  }
331
583
  updateData = {};
332
584
  returningColumns = [];
585
+ hasWhereClause = false;
586
+ allowGlobal = false;
587
+ incrementDecrementOps = [];
333
588
  set(data) {
334
589
  this.updateData = { ...this.updateData, ...data };
335
590
  return this;
336
591
  }
592
+ where(condition) {
593
+ this.hasWhereClause = true;
594
+ return super.where(condition);
595
+ }
596
+ increment(column, value = 1) {
597
+ const col = this.table._.columns[column];
598
+ if (!col) {
599
+ throw new ColumnNotFoundError(String(column), this.table._.name);
600
+ }
601
+ this.incrementDecrementOps.push({ column: col._.name, op: "increment", value });
602
+ return this;
603
+ }
604
+ decrement(column, value = 1) {
605
+ const col = this.table._.columns[column];
606
+ if (!col) {
607
+ throw new ColumnNotFoundError(String(column), this.table._.name);
608
+ }
609
+ this.incrementDecrementOps.push({ column: col._.name, op: "decrement", value });
610
+ return this;
611
+ }
612
+ allowGlobalOperation() {
613
+ this.allowGlobal = true;
614
+ return this;
615
+ }
337
616
  returning(...columns) {
338
617
  this.returningColumns.push(...columns);
339
618
  return this;
@@ -356,24 +635,37 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
356
635
  whereClause = baseQuery.substring(whereIndex);
357
636
  }
358
637
  const entries = Object.entries(finalUpdateData);
359
- if (entries.length === 0) {
360
- throw new Error("Cannot execute an update query without a .set() call.");
361
- }
362
- const setClause = entries.map(([key]) => {
363
- const column = this.table._.columns[key];
364
- if (!column) {
365
- throw new Error(
366
- `Column ${key} does not exist on table ${this.table._.name}`
367
- );
638
+ const hasSetData = entries.length > 0;
639
+ const hasIncrementDecrement = this.incrementDecrementOps.length > 0;
640
+ if (!hasSetData && !hasIncrementDecrement) {
641
+ throw new UpdateValidationError("Cannot execute an update query without a .set(), .increment(), or .decrement() call.");
642
+ }
643
+ const setClauses = [];
644
+ const setParams = [];
645
+ if (hasSetData) {
646
+ for (const [key, value] of entries) {
647
+ const column = this.table._.columns[key];
648
+ if (!column) {
649
+ throw new ColumnNotFoundError(key, this.table._.name);
650
+ }
651
+ setClauses.push(`${column._.name} = ?`);
652
+ setParams.push(value);
368
653
  }
369
- return `${column._.name} = ?`;
370
- }).join(", ");
371
- const setParams = entries.map(([, value]) => value);
654
+ }
655
+ for (const op of this.incrementDecrementOps) {
656
+ const sign = op.op === "increment" ? "+" : "-";
657
+ setClauses.push(`${op.column} = ${op.column} ${sign} ?`);
658
+ setParams.push(op.value);
659
+ }
660
+ const setClause = setClauses.join(", ");
372
661
  const sql2 = `${tablePart} SET ${setClause}${whereClause}`;
373
662
  const params = [...setParams, ...whereParams];
374
663
  return { sql: sql2, params };
375
664
  }
376
665
  async execute() {
666
+ if (!this.hasWhereClause && !this.allowGlobal) {
667
+ throw new MissingWhereClauseError("UPDATE", this.table._.name);
668
+ }
377
669
  const { sql: updateSql, params } = this.buildUpdateClause();
378
670
  if (this.returningColumns.length > 0) {
379
671
  const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
@@ -390,6 +682,17 @@ var UpdateQueryBuilder = class extends BaseQueryBuilder {
390
682
  );
391
683
  return this.returning(...allColumns).execute();
392
684
  }
685
+ toSQL() {
686
+ const { sql: updateSql, params } = this.buildUpdateClause();
687
+ if (this.returningColumns.length > 0) {
688
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
689
+ return {
690
+ sql: `${updateSql} RETURNING ${returningNames}`,
691
+ params
692
+ };
693
+ }
694
+ return { sql: updateSql, params };
695
+ }
393
696
  };
394
697
 
395
698
  // src/builders/insert.ts
@@ -458,7 +761,7 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
458
761
  }
459
762
  async execute() {
460
763
  if (this.dataSets.length === 0) {
461
- throw new Error("No data provided for insert");
764
+ throw new InsertValidationError("No data provided for insert. Use .values() to provide data.");
462
765
  }
463
766
  const processedDataSets = this.dataSets.map(
464
767
  (data) => this.processDefaultValues(data)
@@ -516,6 +819,42 @@ var InsertQueryBuilder = class extends BaseQueryBuilder {
516
819
  );
517
820
  return this.returning(...allColumns).execute();
518
821
  }
822
+ toSQL() {
823
+ if (this.dataSets.length === 0) {
824
+ throw new InsertValidationError("No data provided for insert. Use .values() to provide data.");
825
+ }
826
+ const processedDataSets = this.dataSets.map(
827
+ (data) => this.processDefaultValues(data)
828
+ );
829
+ const dataSet = processedDataSets[0];
830
+ const columns = Object.keys(dataSet);
831
+ const columnNames = columns.map(
832
+ (key) => this.table._.columns[key]._.name
833
+ );
834
+ const placeholders = `(${columns.map(() => "?").join(", ")})`;
835
+ const valuesSql = processedDataSets.map(() => placeholders).join(", ");
836
+ const conflictClause = this.buildConflictClause();
837
+ const finalQuery = `${this.query} (${columnNames.join(
838
+ ", "
839
+ )}) VALUES ${valuesSql}${conflictClause}`;
840
+ const params = processedDataSets.flatMap(
841
+ (data) => columns.map((col) => data[col] ?? null)
842
+ );
843
+ if (this.onConflictAction === "update") {
844
+ const setValues = Object.entries(this.updateSet).map(
845
+ ([, value]) => value
846
+ );
847
+ params.push(...setValues);
848
+ }
849
+ if (this.returningColumns.length > 0) {
850
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
851
+ return {
852
+ sql: `${finalQuery} RETURNING ${returningNames}`,
853
+ params
854
+ };
855
+ }
856
+ return { sql: finalQuery, params };
857
+ }
519
858
  };
520
859
 
521
860
  // src/builders/delete.ts
@@ -526,11 +865,24 @@ var DeleteQueryBuilder = class extends BaseQueryBuilder {
526
865
  this.query = `DELETE FROM ${table._.name}`;
527
866
  }
528
867
  returningColumns = [];
868
+ hasWhereClause = false;
869
+ allowGlobal = false;
870
+ where(condition) {
871
+ this.hasWhereClause = true;
872
+ return super.where(condition);
873
+ }
874
+ allowGlobalOperation() {
875
+ this.allowGlobal = true;
876
+ return this;
877
+ }
529
878
  returning(...columns) {
530
879
  this.returningColumns.push(...columns);
531
880
  return this;
532
881
  }
533
882
  async execute() {
883
+ if (!this.hasWhereClause && !this.allowGlobal) {
884
+ throw new MissingWhereClauseError("DELETE", this.table._.name);
885
+ }
534
886
  const { sql: sql2, params } = this.build();
535
887
  if (this.returningColumns.length > 0) {
536
888
  const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
@@ -545,6 +897,17 @@ var DeleteQueryBuilder = class extends BaseQueryBuilder {
545
897
  const allColumns = Object.keys(this.table._.columns);
546
898
  return this.returning(...allColumns).execute();
547
899
  }
900
+ toSQL() {
901
+ const { sql: sql2, params } = this.build();
902
+ if (this.returningColumns.length > 0) {
903
+ const returningNames = this.returningColumns.map((col) => this.table._.columns[col]._.name).join(", ");
904
+ return {
905
+ sql: `${sql2} RETURNING ${returningNames}`,
906
+ params
907
+ };
908
+ }
909
+ return { sql: sql2, params };
910
+ }
548
911
  };
549
912
 
550
913
  // src/builders/with.ts
@@ -926,6 +1289,12 @@ var ManyRelation = class extends Relation {
926
1289
  super(foreignTable);
927
1290
  }
928
1291
  };
1292
+ var ManyToManyRelation = class extends Relation {
1293
+ constructor(foreignTable, config) {
1294
+ super(foreignTable);
1295
+ this.config = config;
1296
+ }
1297
+ };
929
1298
  var relations = (table, relationsCallback) => {
930
1299
  const builtRelations = relationsCallback({
931
1300
  one: (foreignTable, config) => {
@@ -933,6 +1302,9 @@ var relations = (table, relationsCallback) => {
933
1302
  },
934
1303
  many: (foreignTable) => {
935
1304
  return new ManyRelation(foreignTable);
1305
+ },
1306
+ manyToMany: (foreignTable, config) => {
1307
+ return new ManyToManyRelation(foreignTable, config);
936
1308
  }
937
1309
  });
938
1310
  for (const [name, relation] of Object.entries(builtRelations)) {
@@ -948,6 +1320,14 @@ var relations = (table, relationsCallback) => {
948
1320
  type: "many",
949
1321
  foreignTable: relation.foreignTable
950
1322
  };
1323
+ } else if (relation instanceof ManyToManyRelation) {
1324
+ table.relations[name] = {
1325
+ type: "manyToMany",
1326
+ foreignTable: relation.foreignTable,
1327
+ junctionTable: relation.config.junctionTable,
1328
+ junctionFields: relation.config.junctionFields,
1329
+ junctionReferences: relation.config.junctionReferences
1330
+ };
951
1331
  }
952
1332
  }
953
1333
  return builtRelations;
@@ -959,6 +1339,53 @@ var alias = (table, alias2) => {
959
1339
  return table;
960
1340
  };
961
1341
 
1342
+ // src/aggregates.ts
1343
+ var count = (column) => ({
1344
+ sql: `COUNT(${column ? column._.name : "*"})`,
1345
+ params: []
1346
+ });
1347
+ var countDistinct = (column) => ({
1348
+ sql: `COUNT(DISTINCT ${column._.name})`,
1349
+ params: []
1350
+ });
1351
+ var sum = (column) => ({
1352
+ sql: `SUM(${column._.name})`,
1353
+ params: []
1354
+ });
1355
+ var avg = (column) => ({
1356
+ sql: `AVG(${column._.name})`,
1357
+ params: []
1358
+ });
1359
+ var max = (column) => ({
1360
+ sql: `MAX(${column._.name})`,
1361
+ params: []
1362
+ });
1363
+ var min = (column) => ({
1364
+ sql: `MIN(${column._.name})`,
1365
+ params: []
1366
+ });
1367
+ var groupConcat = (column, separator = ",") => ({
1368
+ sql: `GROUP_CONCAT(${column._.name}, ?)`,
1369
+ params: [separator]
1370
+ });
1371
+ var as = (aggregate, alias2) => ({
1372
+ ...aggregate,
1373
+ alias: alias2
1374
+ });
1375
+
1376
+ // src/subquery.ts
1377
+ var subquery = (query) => {
1378
+ const { sql: sql2, params } = query.toSQL();
1379
+ return {
1380
+ sql: `(${sql2})`,
1381
+ params,
1382
+ _isSubquery: true
1383
+ };
1384
+ };
1385
+ var scalarSubquery = (query) => {
1386
+ return subquery(query);
1387
+ };
1388
+
962
1389
  // src/column-helpers.ts
963
1390
  var text = (name, config) => new SQLiteColumn(name, "TEXT", config, config?.mode);
964
1391
  var integer = (name, config) => new SQLiteColumn(name, "INTEGER", {}, config?.mode || "default");
@@ -969,47 +1396,77 @@ var numeric = (name, config) => new SQLiteColumn(name, "NUMERIC", {}, config?.mo
969
1396
  var enumType = (name, values) => text(name, { enum: values });
970
1397
  export {
971
1398
  BaseQueryBuilder,
1399
+ ColumnNotFoundError,
972
1400
  DeleteQueryBuilder,
973
1401
  InsertQueryBuilder,
1402
+ InsertValidationError,
974
1403
  ManyRelation,
1404
+ ManyToManyRelation,
1405
+ MigrationError,
1406
+ MissingWhereClauseError,
975
1407
  OneRelation,
1408
+ QueryBuilderError,
976
1409
  Relation,
1410
+ RelationError,
977
1411
  SQLiteColumn,
978
1412
  SelectQueryBuilder,
979
1413
  Table,
1414
+ TableNotFoundError,
980
1415
  TauriORM,
1416
+ TauriORMError,
981
1417
  UpdateQueryBuilder,
1418
+ UpdateValidationError,
1419
+ ValidationError,
982
1420
  WithQueryBuilder,
983
1421
  alias,
984
1422
  and,
1423
+ as,
985
1424
  asc,
986
1425
  avg,
1426
+ between,
987
1427
  blob,
988
1428
  boolean,
1429
+ contains,
989
1430
  count,
990
1431
  countDistinct,
991
1432
  desc,
1433
+ endsWith,
992
1434
  enumType,
993
1435
  eq,
1436
+ eqSubquery,
1437
+ exists,
994
1438
  getTableColumns,
1439
+ groupConcat,
995
1440
  gt,
1441
+ gtSubquery,
996
1442
  gte,
1443
+ gteSubquery,
1444
+ ilike,
997
1445
  inArray,
998
1446
  integer,
999
1447
  isNotNull,
1000
1448
  isNull,
1001
1449
  like,
1002
1450
  lt,
1451
+ ltSubquery,
1003
1452
  lte,
1453
+ lteSubquery,
1004
1454
  max,
1005
1455
  min,
1456
+ ne,
1457
+ neSubquery,
1006
1458
  not,
1459
+ notExists,
1460
+ notIn,
1007
1461
  numeric,
1008
1462
  or,
1009
1463
  real,
1010
1464
  relations,
1465
+ scalarSubquery,
1011
1466
  sql,
1012
1467
  sqliteTable,
1468
+ startsWith,
1469
+ subquery,
1013
1470
  sum,
1014
1471
  text
1015
1472
  };