@atscript/db-sqlite 0.1.151 → 0.1.153

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.cjs CHANGED
@@ -334,6 +334,7 @@ const sqliteDialect = {
334
334
  calendarBucket: sqliteCalendarBucket,
335
335
  jsonExtract: sqliteJsonExtract,
336
336
  castDouble: (expr) => `CAST(${expr} AS REAL)`,
337
+ nullsPlacementSyntax: true,
337
338
  createViewPrefix: "CREATE VIEW IF NOT EXISTS"
338
339
  };
339
340
  /**
@@ -793,6 +794,10 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
793
794
  supportsAggregateExpressions() {
794
795
  return true;
795
796
  }
797
+ /** `$nulls` / `@db.sort.nulls` NULL placement — `NULLS FIRST` / `NULLS LAST`. */
798
+ supportsNullsPlacement() {
799
+ return true;
800
+ }
796
801
  /** Computed view columns and first-row joins. */
797
802
  viewCapabilities() {
798
803
  return _atscript_db.ALL_VIEW_CAPABILITIES;
@@ -818,7 +823,9 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
818
823
  super();
819
824
  this.driver = driver;
820
825
  this._gate = getSqliteTxGate(driver);
821
- this._txOptions = { ...options };
826
+ const { viewJoinPruning, ...txOptions } = options ?? {};
827
+ this._txOptions = txOptions;
828
+ this.viewJoinPruning = viewJoinPruning !== false;
822
829
  this.driver.exec("PRAGMA foreign_keys = ON");
823
830
  this._hasBucketFn = registerBucketFunction(driver);
824
831
  }
@@ -955,6 +962,17 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
955
962
  return super.resolveTableName(false);
956
963
  }
957
964
  /**
965
+ * The FROM source of a read of this adapter's table or view: a managed
966
+ * view read that needs only some of its LEFT joins reads an inline,
967
+ * pruned definition (`viewReadSource`, since 0.1.153) — SQLite omits an
968
+ * unused LEFT JOIN only in a non-aggregate query, so a view's `COUNT(*)`
969
+ * and aggregates otherwise probe every join. Off with `viewJoinPruning: false`.
970
+ */
971
+ _readSource(query, kind, partitionBy) {
972
+ const name = this.resolveTableName();
973
+ return this.viewJoinPruning ? (0, _atscript_db_sql_tools.viewReadSource)(sqliteDialect, this._table, name, query, kind, partitionBy) : name;
974
+ }
975
+ /**
958
976
  * Relational predicates (`$some` / `$none`) render as correlated
959
977
  * `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
960
978
  *
@@ -1057,13 +1075,13 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
1057
1075
  ...query.controls,
1058
1076
  $limit: 1
1059
1077
  };
1060
- const { sql, params } = buildSelect(this.resolveTableName(), where, controls);
1078
+ const { sql, params } = buildSelect(this._readSource(query, "rows"), where, controls);
1061
1079
  this._log(sql, params);
1062
1080
  return this._stmt(() => this.driver.get(sql, params));
1063
1081
  }
1064
1082
  async findMany(query) {
1065
1083
  const where = buildWhere(query.filter);
1066
- const { sql, params } = buildSelect(this.resolveTableName(), where, query.controls);
1084
+ const { sql, params } = buildSelect(this._readSource(query, "rows"), where, query.controls);
1067
1085
  this._log(sql, params);
1068
1086
  return this._stmt(() => this.driver.all(sql, params));
1069
1087
  }
@@ -1073,7 +1091,7 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
1073
1091
  */
1074
1092
  async findManyPerPartition(query, partitionBy) {
1075
1093
  const where = buildWhere(query.filter);
1076
- const { sql, params } = (0, _atscript_db_sql_tools.buildPartitionedSelect)(sqliteDialect, this.resolveTableName(), where, query.controls, partitionBy);
1094
+ const { sql, params } = (0, _atscript_db_sql_tools.buildPartitionedSelect)(sqliteDialect, this._readSource(query, "rows", partitionBy), where, query.controls, partitionBy);
1077
1095
  this._log(sql, params);
1078
1096
  return (0, _atscript_db_sql_tools.stripPartitionRowNumber)(await this._stmt(() => this.driver.all(sql, params)));
1079
1097
  }
@@ -1085,9 +1103,8 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
1085
1103
  async findManyWithCount(query) {
1086
1104
  if (this.findMany !== SqliteAdapter.prototype.findMany || this.count !== SqliteAdapter.prototype.count) return super.findManyWithCount(query);
1087
1105
  const where = buildWhere(query.filter);
1088
- const tableName = this.resolveTableName();
1089
- const { sql, params } = buildSelect(tableName, where, query.controls);
1090
- const countSql = `SELECT COUNT(*) as cnt FROM "${esc(tableName)}" WHERE ${where.sql}`;
1106
+ const { sql, params } = buildSelect(this._readSource(query, "rows"), where, query.controls);
1107
+ const countSql = this._countSql(query, where);
1091
1108
  this._log(sql, params);
1092
1109
  this._log(countSql, where.params);
1093
1110
  return this._stmt(() => ({
@@ -1095,15 +1112,19 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
1095
1112
  count: this.driver.get(countSql, where.params)?.cnt ?? 0
1096
1113
  }));
1097
1114
  }
1115
+ /** `SELECT COUNT(*) as cnt FROM <source> WHERE <where>` of a count read. */
1116
+ _countSql(query, where) {
1117
+ return `SELECT COUNT(*) as cnt FROM ${(0, _atscript_db_sql_tools.fromSourceSql)(sqliteDialect, this._readSource(query, "count"))} WHERE ${where.sql}`;
1118
+ }
1098
1119
  async count(query) {
1099
1120
  const where = buildWhere(query.filter);
1100
- const sql = `SELECT COUNT(*) as cnt FROM "${esc(this.resolveTableName())}" WHERE ${where.sql}`;
1121
+ const sql = this._countSql(query, where);
1101
1122
  this._log(sql, where.params);
1102
1123
  return (await this._stmt(() => this.driver.get(sql, where.params)))?.cnt ?? 0;
1103
1124
  }
1104
1125
  async aggregate(query) {
1105
- const tableName = this.resolveTableName();
1106
1126
  const search = (0, _atscript_db_agg.resolveAggregateSearch)(query.controls);
1127
+ const tableName = search ? this.resolveTableName() : this._readSource(query, "aggregate");
1107
1128
  const where = search ? andWhere(this._buildSearchPredicate(search.text, search.indexName), buildWhere(query.filter)) : buildWhere(query.filter);
1108
1129
  if (query.controls.$count) {
1109
1130
  const { sql, params } = buildAggregateCount(tableName, where, query.controls);
@@ -1512,11 +1533,8 @@ var SqliteAdapter = class SqliteAdapter extends _atscript_db.BaseDbAdapter {
1512
1533
  if (controls.$select?.asArray?.length) cols = controls.$select.asArray.map((c) => `t."${esc(c)}"`).join(", ");
1513
1534
  let sql = `SELECT ${cols} ${base.fromWhere}`;
1514
1535
  const params = [...base.params];
1515
- if (controls.$sort) {
1516
- const orderParts = [];
1517
- for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`t."${esc(col)}" ${dir === -1 ? "DESC" : "ASC"}`);
1518
- if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
1519
- }
1536
+ const orderBy = (0, _atscript_db_sql_tools.orderByList)(sqliteDialect, controls.$sort, controls.$nulls, "t.");
1537
+ if (orderBy) sql += ` ORDER BY ${orderBy}`;
1520
1538
  if (controls.$limit !== void 0) {
1521
1539
  sql += ` LIMIT ?`;
1522
1540
  params.push(controls.$limit);
@@ -1998,11 +2016,12 @@ function normalizeSqliteDefault(value) {
1998
2016
  //#endregion
1999
2017
  //#region src/index.ts
2000
2018
  function createAdapter(connection, options) {
2001
- const { transactionWaitTimeoutMs, transactionWaitWarnMs, ...driverOptions } = options ?? {};
2019
+ const { transactionWaitTimeoutMs, transactionWaitWarnMs, viewJoinPruning, ...driverOptions } = options ?? {};
2002
2020
  const driver = new BetterSqlite3Driver(connection, driverOptions);
2003
2021
  const adapterOptions = {
2004
2022
  transactionWaitTimeoutMs,
2005
- transactionWaitWarnMs
2023
+ transactionWaitWarnMs,
2024
+ viewJoinPruning
2006
2025
  };
2007
2026
  return new _atscript_db.DbSpace(() => new SqliteAdapter(driver, adapterOptions), { onClose: () => driver.close() });
2008
2027
  }
package/dist/index.d.cts CHANGED
@@ -162,8 +162,16 @@ interface SqliteTxWaitOptions {
162
162
  /** Logger for the wait warning (the adapter passes its own). */
163
163
  logger?: TGenericLogger;
164
164
  }
165
- /** Options accepted by `SqliteAdapter` / `createAdapter` for the transaction gate. */
166
- type SqliteAdapterOptions = Pick<SqliteTxWaitOptions, "transactionWaitTimeoutMs" | "transactionWaitWarnMs">;
165
+ /** Options accepted by `SqliteAdapter` / `createAdapter`: the transaction gate's, plus `viewJoinPruning`. */
166
+ type SqliteAdapterOptions = Pick<SqliteTxWaitOptions, "transactionWaitTimeoutMs" | "transactionWaitWarnMs"> & {
167
+ /**
168
+ * Read a managed view through an inline definition without the LEFT joins
169
+ * the query does not need (default `true`) — see the views guide,
170
+ * "Performance". `false` always reads the stored view by name.
171
+ * @since 0.1.153
172
+ */
173
+ viewJoinPruning?: boolean;
174
+ };
167
175
  /**
168
176
  * One waiter for the gate, created once per `_stmt` / `acquire` call and kept
169
177
  * across wake-ups: the timeout and warn timers are armed ONCE with an absolute
@@ -289,6 +297,8 @@ declare class SqliteAdapter extends BaseDbAdapter {
289
297
  aggregateFns(): ReadonlySet<AggregateFn>;
290
298
  /** Arithmetic in an aggregate `$select` (`{ $expr }`, `{ $fn, $expr }`). */
291
299
  supportsAggregateExpressions(): boolean;
300
+ /** `$nulls` / `@db.sort.nulls` NULL placement — `NULLS FIRST` / `NULLS LAST`. */
301
+ supportsNullsPlacement(): boolean;
292
302
  /** Computed view columns and first-row joins. */
293
303
  viewCapabilities(): ReadonlySet<TViewCapability>;
294
304
  /** INSERT text per column signature (rows of one table share a handful of shapes). */
@@ -343,6 +353,14 @@ declare class SqliteAdapter extends BaseDbAdapter {
343
353
  protected _rollbackTransaction(state: unknown): Promise<void>;
344
354
  /** SQLite does not use schemas — override to always exclude schema. */
345
355
  resolveTableName(): string;
356
+ /**
357
+ * The FROM source of a read of this adapter's table or view: a managed
358
+ * view read that needs only some of its LEFT joins reads an inline,
359
+ * pruned definition (`viewReadSource`, since 0.1.153) — SQLite omits an
360
+ * unused LEFT JOIN only in a non-aggregate query, so a view's `COUNT(*)`
361
+ * and aggregates otherwise probe every join. Off with `viewJoinPruning: false`.
362
+ */
363
+ private _readSource;
346
364
  /**
347
365
  * Relational predicates (`$some` / `$none`) render as correlated
348
366
  * `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
@@ -383,6 +401,8 @@ declare class SqliteAdapter extends BaseDbAdapter {
383
401
  data: Array<Record<string, unknown>>;
384
402
  count: number;
385
403
  }>;
404
+ /** `SELECT COUNT(*) as cnt FROM <source> WHERE <where>` of a count read. */
405
+ private _countSql;
386
406
  count(query: DbQuery): Promise<number>;
387
407
  aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
388
408
  updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
package/dist/index.d.mts CHANGED
@@ -162,8 +162,16 @@ interface SqliteTxWaitOptions {
162
162
  /** Logger for the wait warning (the adapter passes its own). */
163
163
  logger?: TGenericLogger;
164
164
  }
165
- /** Options accepted by `SqliteAdapter` / `createAdapter` for the transaction gate. */
166
- type SqliteAdapterOptions = Pick<SqliteTxWaitOptions, "transactionWaitTimeoutMs" | "transactionWaitWarnMs">;
165
+ /** Options accepted by `SqliteAdapter` / `createAdapter`: the transaction gate's, plus `viewJoinPruning`. */
166
+ type SqliteAdapterOptions = Pick<SqliteTxWaitOptions, "transactionWaitTimeoutMs" | "transactionWaitWarnMs"> & {
167
+ /**
168
+ * Read a managed view through an inline definition without the LEFT joins
169
+ * the query does not need (default `true`) — see the views guide,
170
+ * "Performance". `false` always reads the stored view by name.
171
+ * @since 0.1.153
172
+ */
173
+ viewJoinPruning?: boolean;
174
+ };
167
175
  /**
168
176
  * One waiter for the gate, created once per `_stmt` / `acquire` call and kept
169
177
  * across wake-ups: the timeout and warn timers are armed ONCE with an absolute
@@ -289,6 +297,8 @@ declare class SqliteAdapter extends BaseDbAdapter {
289
297
  aggregateFns(): ReadonlySet<AggregateFn>;
290
298
  /** Arithmetic in an aggregate `$select` (`{ $expr }`, `{ $fn, $expr }`). */
291
299
  supportsAggregateExpressions(): boolean;
300
+ /** `$nulls` / `@db.sort.nulls` NULL placement — `NULLS FIRST` / `NULLS LAST`. */
301
+ supportsNullsPlacement(): boolean;
292
302
  /** Computed view columns and first-row joins. */
293
303
  viewCapabilities(): ReadonlySet<TViewCapability>;
294
304
  /** INSERT text per column signature (rows of one table share a handful of shapes). */
@@ -343,6 +353,14 @@ declare class SqliteAdapter extends BaseDbAdapter {
343
353
  protected _rollbackTransaction(state: unknown): Promise<void>;
344
354
  /** SQLite does not use schemas — override to always exclude schema. */
345
355
  resolveTableName(): string;
356
+ /**
357
+ * The FROM source of a read of this adapter's table or view: a managed
358
+ * view read that needs only some of its LEFT joins reads an inline,
359
+ * pruned definition (`viewReadSource`, since 0.1.153) — SQLite omits an
360
+ * unused LEFT JOIN only in a non-aggregate query, so a view's `COUNT(*)`
361
+ * and aggregates otherwise probe every join. Off with `viewJoinPruning: false`.
362
+ */
363
+ private _readSource;
346
364
  /**
347
365
  * Relational predicates (`$some` / `$none`) render as correlated
348
366
  * `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
@@ -383,6 +401,8 @@ declare class SqliteAdapter extends BaseDbAdapter {
383
401
  data: Array<Record<string, unknown>>;
384
402
  count: number;
385
403
  }>;
404
+ /** `SELECT COUNT(*) as cnt FROM <source> WHERE <where>` of a count read. */
405
+ private _countSql;
386
406
  count(query: DbQuery): Promise<number>;
387
407
  aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
388
408
  updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, ALL_VIEW_CAPABILITIES, BaseDbAdapter, DbError, DbSpace, defaultFulltextIndex, describeFulltext, fkColumns, searchIndexNotFoundMessage, searchTermInteger, splitFulltextFields, vectorIndexNotFoundMessage } from "@atscript/db";
3
3
  import { resolveAggregateSearch } from "@atscript/db/agg";
4
- import { EMPTY_AND, InsertSqlCache, SEARCH_SOURCE_ALIAS, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildPartitionedSelect, buildSelect, buildUpdate, buildVectorSearchCount, buildVectorSearchSelect, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, derivedColumnExpr, fillReplacePayload, geoWindowFromControls, jsonDollarPath, orFragment, parseRegexString, refActionToSql, renameGeoDistance, replaceColumnsFor, sqlTimeZoneLiteral, stripPartitionRowNumber, toSqlValue } from "@atscript/db-sql-tools";
4
+ import { EMPTY_AND, InsertSqlCache, SEARCH_SOURCE_ALIAS, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildPartitionedSelect, buildSelect, buildUpdate, buildVectorSearchCount, buildVectorSearchSelect, buildWhere as buildWhere$1, defaultValueForType, defaultValueToSqlLiteral, derivedColumnExpr, fillReplacePayload, fromSourceSql, geoWindowFromControls, jsonDollarPath, orFragment, orderByList, parseRegexString, refActionToSql, renameGeoDistance, replaceColumnsFor, sqlTimeZoneLiteral, stripPartitionRowNumber, toSqlValue, viewReadSource } from "@atscript/db-sql-tools";
5
5
  import { bucketer } from "@uniqu/core";
6
6
  import { AsyncLocalStorage } from "node:async_hooks";
7
7
  //#region src/better-sqlite3-driver.ts
@@ -333,6 +333,7 @@ const sqliteDialect = {
333
333
  calendarBucket: sqliteCalendarBucket,
334
334
  jsonExtract: sqliteJsonExtract,
335
335
  castDouble: (expr) => `CAST(${expr} AS REAL)`,
336
+ nullsPlacementSyntax: true,
336
337
  createViewPrefix: "CREATE VIEW IF NOT EXISTS"
337
338
  };
338
339
  /**
@@ -792,6 +793,10 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
792
793
  supportsAggregateExpressions() {
793
794
  return true;
794
795
  }
796
+ /** `$nulls` / `@db.sort.nulls` NULL placement — `NULLS FIRST` / `NULLS LAST`. */
797
+ supportsNullsPlacement() {
798
+ return true;
799
+ }
795
800
  /** Computed view columns and first-row joins. */
796
801
  viewCapabilities() {
797
802
  return ALL_VIEW_CAPABILITIES;
@@ -817,7 +822,9 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
817
822
  super();
818
823
  this.driver = driver;
819
824
  this._gate = getSqliteTxGate(driver);
820
- this._txOptions = { ...options };
825
+ const { viewJoinPruning, ...txOptions } = options ?? {};
826
+ this._txOptions = txOptions;
827
+ this.viewJoinPruning = viewJoinPruning !== false;
821
828
  this.driver.exec("PRAGMA foreign_keys = ON");
822
829
  this._hasBucketFn = registerBucketFunction(driver);
823
830
  }
@@ -954,6 +961,17 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
954
961
  return super.resolveTableName(false);
955
962
  }
956
963
  /**
964
+ * The FROM source of a read of this adapter's table or view: a managed
965
+ * view read that needs only some of its LEFT joins reads an inline,
966
+ * pruned definition (`viewReadSource`, since 0.1.153) — SQLite omits an
967
+ * unused LEFT JOIN only in a non-aggregate query, so a view's `COUNT(*)`
968
+ * and aggregates otherwise probe every join. Off with `viewJoinPruning: false`.
969
+ */
970
+ _readSource(query, kind, partitionBy) {
971
+ const name = this.resolveTableName();
972
+ return this.viewJoinPruning ? viewReadSource(sqliteDialect, this._table, name, query, kind, partitionBy) : name;
973
+ }
974
+ /**
957
975
  * Relational predicates (`$some` / `$none`) render as correlated
958
976
  * `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
959
977
  *
@@ -1056,13 +1074,13 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
1056
1074
  ...query.controls,
1057
1075
  $limit: 1
1058
1076
  };
1059
- const { sql, params } = buildSelect$1(this.resolveTableName(), where, controls);
1077
+ const { sql, params } = buildSelect$1(this._readSource(query, "rows"), where, controls);
1060
1078
  this._log(sql, params);
1061
1079
  return this._stmt(() => this.driver.get(sql, params));
1062
1080
  }
1063
1081
  async findMany(query) {
1064
1082
  const where = buildWhere(query.filter);
1065
- const { sql, params } = buildSelect$1(this.resolveTableName(), where, query.controls);
1083
+ const { sql, params } = buildSelect$1(this._readSource(query, "rows"), where, query.controls);
1066
1084
  this._log(sql, params);
1067
1085
  return this._stmt(() => this.driver.all(sql, params));
1068
1086
  }
@@ -1072,7 +1090,7 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
1072
1090
  */
1073
1091
  async findManyPerPartition(query, partitionBy) {
1074
1092
  const where = buildWhere(query.filter);
1075
- const { sql, params } = buildPartitionedSelect(sqliteDialect, this.resolveTableName(), where, query.controls, partitionBy);
1093
+ const { sql, params } = buildPartitionedSelect(sqliteDialect, this._readSource(query, "rows", partitionBy), where, query.controls, partitionBy);
1076
1094
  this._log(sql, params);
1077
1095
  return stripPartitionRowNumber(await this._stmt(() => this.driver.all(sql, params)));
1078
1096
  }
@@ -1084,9 +1102,8 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
1084
1102
  async findManyWithCount(query) {
1085
1103
  if (this.findMany !== SqliteAdapter.prototype.findMany || this.count !== SqliteAdapter.prototype.count) return super.findManyWithCount(query);
1086
1104
  const where = buildWhere(query.filter);
1087
- const tableName = this.resolveTableName();
1088
- const { sql, params } = buildSelect$1(tableName, where, query.controls);
1089
- const countSql = `SELECT COUNT(*) as cnt FROM "${esc(tableName)}" WHERE ${where.sql}`;
1105
+ const { sql, params } = buildSelect$1(this._readSource(query, "rows"), where, query.controls);
1106
+ const countSql = this._countSql(query, where);
1090
1107
  this._log(sql, params);
1091
1108
  this._log(countSql, where.params);
1092
1109
  return this._stmt(() => ({
@@ -1094,15 +1111,19 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
1094
1111
  count: this.driver.get(countSql, where.params)?.cnt ?? 0
1095
1112
  }));
1096
1113
  }
1114
+ /** `SELECT COUNT(*) as cnt FROM <source> WHERE <where>` of a count read. */
1115
+ _countSql(query, where) {
1116
+ return `SELECT COUNT(*) as cnt FROM ${fromSourceSql(sqliteDialect, this._readSource(query, "count"))} WHERE ${where.sql}`;
1117
+ }
1097
1118
  async count(query) {
1098
1119
  const where = buildWhere(query.filter);
1099
- const sql = `SELECT COUNT(*) as cnt FROM "${esc(this.resolveTableName())}" WHERE ${where.sql}`;
1120
+ const sql = this._countSql(query, where);
1100
1121
  this._log(sql, where.params);
1101
1122
  return (await this._stmt(() => this.driver.get(sql, where.params)))?.cnt ?? 0;
1102
1123
  }
1103
1124
  async aggregate(query) {
1104
- const tableName = this.resolveTableName();
1105
1125
  const search = resolveAggregateSearch(query.controls);
1126
+ const tableName = search ? this.resolveTableName() : this._readSource(query, "aggregate");
1106
1127
  const where = search ? andWhere(this._buildSearchPredicate(search.text, search.indexName), buildWhere(query.filter)) : buildWhere(query.filter);
1107
1128
  if (query.controls.$count) {
1108
1129
  const { sql, params } = buildAggregateCount$1(tableName, where, query.controls);
@@ -1511,11 +1532,8 @@ var SqliteAdapter = class SqliteAdapter extends BaseDbAdapter {
1511
1532
  if (controls.$select?.asArray?.length) cols = controls.$select.asArray.map((c) => `t."${esc(c)}"`).join(", ");
1512
1533
  let sql = `SELECT ${cols} ${base.fromWhere}`;
1513
1534
  const params = [...base.params];
1514
- if (controls.$sort) {
1515
- const orderParts = [];
1516
- for (const [col, dir] of Object.entries(controls.$sort)) orderParts.push(`t."${esc(col)}" ${dir === -1 ? "DESC" : "ASC"}`);
1517
- if (orderParts.length > 0) sql += ` ORDER BY ${orderParts.join(", ")}`;
1518
- }
1535
+ const orderBy = orderByList(sqliteDialect, controls.$sort, controls.$nulls, "t.");
1536
+ if (orderBy) sql += ` ORDER BY ${orderBy}`;
1519
1537
  if (controls.$limit !== void 0) {
1520
1538
  sql += ` LIMIT ?`;
1521
1539
  params.push(controls.$limit);
@@ -1997,11 +2015,12 @@ function normalizeSqliteDefault(value) {
1997
2015
  //#endregion
1998
2016
  //#region src/index.ts
1999
2017
  function createAdapter(connection, options) {
2000
- const { transactionWaitTimeoutMs, transactionWaitWarnMs, ...driverOptions } = options ?? {};
2018
+ const { transactionWaitTimeoutMs, transactionWaitWarnMs, viewJoinPruning, ...driverOptions } = options ?? {};
2001
2019
  const driver = new BetterSqlite3Driver(connection, driverOptions);
2002
2020
  const adapterOptions = {
2003
2021
  transactionWaitTimeoutMs,
2004
- transactionWaitWarnMs
2022
+ transactionWaitWarnMs,
2023
+ viewJoinPruning
2005
2024
  };
2006
2025
  return new DbSpace(() => new SqliteAdapter(driver, adapterOptions), { onClose: () => driver.close() });
2007
2026
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-sqlite",
3
- "version": "0.1.151",
3
+ "version": "0.1.153",
4
4
  "description": "SQLite adapter for @atscript/db with swappable driver support.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -36,21 +36,21 @@
36
36
  "access": "public"
37
37
  },
38
38
  "devDependencies": {
39
- "@atscript/core": "^0.1.102",
40
- "@atscript/typescript": "^0.1.102",
39
+ "@atscript/core": "^0.1.103",
40
+ "@atscript/typescript": "^0.1.103",
41
41
  "@types/better-sqlite3": "^7.6.13",
42
- "@uniqu/core": "^0.1.14",
42
+ "@uniqu/core": "^0.1.15",
43
43
  "better-sqlite3": "^12.6.2",
44
44
  "sqlite-vec": "^0.1.9",
45
- "unplugin-atscript": "^0.1.102"
45
+ "unplugin-atscript": "^0.1.103"
46
46
  },
47
47
  "peerDependencies": {
48
- "@atscript/core": "^0.1.102",
49
- "@atscript/typescript": "^0.1.102",
50
- "@uniqu/core": "^0.1.14",
48
+ "@atscript/core": "^0.1.103",
49
+ "@atscript/typescript": "^0.1.103",
50
+ "@uniqu/core": "^0.1.15",
51
51
  "better-sqlite3": ">=11.0.0",
52
- "@atscript/db": "^0.1.151",
53
- "@atscript/db-sql-tools": "^0.1.151"
52
+ "@atscript/db": "^0.1.153",
53
+ "@atscript/db-sql-tools": "^0.1.153"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "better-sqlite3": {