@atscript/db-postgres 0.1.149 → 0.1.150

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
@@ -138,6 +138,7 @@ const pgDialect = {
138
138
  params: [pattern]
139
139
  };
140
140
  },
141
+ integerText: (expr) => `CAST(CAST(${expr} AS BIGINT) AS TEXT)`,
141
142
  geoWithin(quotedCol, circle) {
142
143
  return {
143
144
  sql: `ST_DWithin(${quotedCol}, ST_SetSRID(ST_MakePoint(?, ?), 4326)::geography, ?)`,
@@ -912,16 +913,14 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
912
913
  const colList = quotedKeys.join(", ");
913
914
  return `${quotedKeys.length === 1 ? `${colList} =` : `(${colList}) IN`} (SELECT ${colList} FROM ${quotedTable} WHERE ${whereSql} LIMIT 1)`;
914
915
  }
915
- async updateOne(filter, data, ops, expectedVersion) {
916
+ async updateOne(filter, data, ops, expectedVersion, opts) {
916
917
  const where = buildWhere(filter);
917
918
  const tableName = this.resolveTableName();
918
919
  const quotedTable = quoteTableName(tableName);
919
- const limitedWhere = {
920
+ const { sql, params } = buildUpdate(tableName, data, {
920
921
  sql: this._limitOnePredicate(quotedTable, where.sql),
921
922
  params: where.params
922
- };
923
- const versionColumn = this._table.versionColumnPhysical;
924
- const { sql, params } = buildUpdate(tableName, data, limitedWhere, void 0, ops, versionColumn, expectedVersion);
923
+ }, void 0, ops, this._versionColumnFor(opts, expectedVersion), expectedVersion);
925
924
  this._log(sql, params);
926
925
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
927
926
  return {
@@ -929,9 +928,9 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
929
928
  modifiedCount: result.affectedRows
930
929
  };
931
930
  }
932
- async updateMany(filter, data, ops) {
931
+ async updateMany(filter, data, ops, opts) {
933
932
  const where = buildWhere(filter);
934
- const versionColumn = this._table.versionColumnPhysical;
933
+ const versionColumn = this._versionColumnFor(opts);
935
934
  const { sql, params } = buildUpdate(this.resolveTableName(), data, where, void 0, ops, versionColumn);
936
935
  this._log(sql, params);
937
936
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -1441,7 +1440,9 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1441
1440
  },
1442
1441
  createIndex: async (index) => {
1443
1442
  if (index.type === "fulltext") {
1444
- const tsvectorExpr = this._buildTsvectorExpr(index.fields);
1443
+ const textFields = (0, _atscript_db.splitFulltextFields)(index).text;
1444
+ if (textFields.length === 0) return;
1445
+ const tsvectorExpr = this._buildTsvectorExpr(textFields);
1445
1446
  const sql = `CREATE INDEX IF NOT EXISTS ${qi(index.key)} ON ${quoteTableName(this.resolveTableName())} USING gin(to_tsvector('english', ${tsvectorExpr}))`;
1446
1447
  this._log(sql);
1447
1448
  await this._exec().exec(sql);
@@ -1535,12 +1536,14 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1535
1536
  }
1536
1537
  getSearchIndexes() {
1537
1538
  const indexes = [];
1538
- for (const index of this._table.indexes.values()) if (index.type === "fulltext") indexes.push({
1539
+ const ftAll = [...this._table.indexes.values()].filter((i) => i.type === "fulltext");
1540
+ const ftDefault = (0, _atscript_db.defaultFulltextIndex)(ftAll);
1541
+ for (const index of ftAll) indexes.push({
1539
1542
  name: index.key,
1540
- description: `GIN tsvector index on ${index.fields.map((f) => f.name).join(", ")}`,
1543
+ description: (0, _atscript_db.describeFulltext)(index, (names) => `GIN tsvector index on ${names}`),
1541
1544
  type: "text",
1542
1545
  fields: this._indexLogicalPaths(index),
1543
- isDefault: indexes.length === 0
1546
+ isDefault: index === ftDefault
1544
1547
  });
1545
1548
  let firstVector = true;
1546
1549
  for (const [field, vec] of this._vectorFields) {
@@ -1591,12 +1594,24 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1591
1594
  _buildSearchWhere(text, query, indexName) {
1592
1595
  const fulltextIndex = this._getFulltextIndex(indexName);
1593
1596
  if (!fulltextIndex) throw new Error("No fulltext index found for search");
1594
- const tsvectorExpr = this._buildTsvectorExpr(fulltextIndex.fields);
1597
+ const { text: textFields, integer: integerFields } = (0, _atscript_db.splitFulltextFields)(fulltextIndex);
1595
1598
  const where = buildWhere(query.filter);
1596
- const tsqueryClause = `to_tsvector('english', ${tsvectorExpr}) @@ plainto_tsquery('english', ?)`;
1599
+ const n = integerFields.length > 0 ? (0, _atscript_db.searchTermInteger)(text) : void 0;
1600
+ const parts = [];
1601
+ const params = [];
1602
+ if (textFields.length > 0) {
1603
+ const tsvectorExpr = this._buildTsvectorExpr(textFields);
1604
+ parts.push(`to_tsvector('english', ${tsvectorExpr}) @@ plainto_tsquery('english', ?)`);
1605
+ params.push(text);
1606
+ }
1607
+ if (n !== void 0) for (const f of integerFields) {
1608
+ parts.push(`${qi(f.name)} = CAST(? AS BIGINT)`);
1609
+ params.push(n);
1610
+ }
1611
+ const clause = (0, _atscript_db_sql_tools.orFragment)(parts, params);
1597
1612
  return {
1598
- sql: where.sql === "1=1" ? tsqueryClause : `${where.sql} AND ${tsqueryClause}`,
1599
- params: [...where.params, text]
1613
+ sql: where.sql === "1=1" ? clause.sql : `${where.sql} AND ${clause.sql}`,
1614
+ params: [...where.params, ...clause.params]
1600
1615
  };
1601
1616
  }
1602
1617
  /** Builds the tsvector SQL expression for a fulltext index's fields. Must match between index DDL and queries. */
@@ -1604,9 +1619,9 @@ var PostgresAdapter = class PostgresAdapter extends _atscript_db.BaseDbAdapter {
1604
1619
  return fields.map((f) => `coalesce(${qi(f.name)}, '')`).join(" || ' ' || ");
1605
1620
  }
1606
1621
  _getFulltextIndex(indexName) {
1607
- for (const index of this._table.indexes.values()) if (index.type === "fulltext") {
1608
- if (!indexName || index.key === indexName) return index;
1609
- }
1622
+ const ftAll = [...this._table.indexes.values()].filter((i) => i.type === "fulltext");
1623
+ if (!indexName) return (0, _atscript_db.defaultFulltextIndex)(ftAll);
1624
+ return ftAll.find((index) => index.key === indexName);
1610
1625
  }
1611
1626
  /**
1612
1627
  * Detects pgvector support by attempting to enable the extension.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
1
+ import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateOptions, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
2
2
  import { TMetadataMap } from "@atscript/typescript/utils";
3
3
  import { TFilterVisitorOptions, TSqlFragment } from "@atscript/db-sql-tools";
4
4
 
@@ -189,8 +189,8 @@ declare class PostgresAdapter extends BaseDbAdapter {
189
189
  * existing behavior rather than guess.
190
190
  */
191
191
  private _limitOnePredicate;
192
- updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number): Promise<TDbUpdateResult>;
193
- updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
192
+ updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
193
+ updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
194
194
  replaceOne(filter: FilterExpr, data: Record<string, unknown>, expectedVersion?: number): Promise<TDbUpdateResult>;
195
195
  replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
196
196
  deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
1
+ import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateOptions, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
2
2
  import { TFilterVisitorOptions, TSqlFragment } from "@atscript/db-sql-tools";
3
3
  import { TMetadataMap } from "@atscript/typescript/utils";
4
4
 
@@ -189,8 +189,8 @@ declare class PostgresAdapter extends BaseDbAdapter {
189
189
  * existing behavior rather than guess.
190
190
  */
191
191
  private _limitOnePredicate;
192
- updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number): Promise<TDbUpdateResult>;
193
- updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
192
+ updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
193
+ updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
194
194
  replaceOne(filter: FilterExpr, data: Record<string, unknown>, expectedVersion?: number): Promise<TDbUpdateResult>;
195
195
  replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
196
196
  deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, ALL_VIEW_CAPABILITIES, BaseDbAdapter, DbError, DbSpace, bucketTimeZoneUnavailable, fkColumns, isColumnTypeChanged, uniqueKeyTuple, vectorIndexNotFoundMessage } from "@atscript/db";
1
+ import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, ALL_VIEW_CAPABILITIES, BaseDbAdapter, DbError, DbSpace, bucketTimeZoneUnavailable, defaultFulltextIndex, describeFulltext, fkColumns, isColumnTypeChanged, searchTermInteger, splitFulltextFields, uniqueKeyTuple, vectorIndexNotFoundMessage } from "@atscript/db";
2
2
  import { resolveAggregateSearch } from "@atscript/db/agg";
3
- import { SEARCH_SOURCE_ALIAS, arithOverflowError, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildInsert, buildInsertMany, buildPartitionedSelect, buildSelect, buildUpdate, buildVectorSearchCount, buildVectorSearchSelect, buildWhere as buildWhere$1, chunkInsertRows, defaultValueForType, defaultValueToSqlLiteral, derivedColumnExpr, fillReplacePayload, finalizeParams, foreignKeySql, geoWindowFromControls, mapQueryErrors, normalizeGeoPointValue, numericOutOfRangeError, parseRegexString, quotedJsonPathSegments, renameGeoDistance, replaceColumnsFor, sqlStringLiteral, sqlTimeZoneLiteral, stripPartitionRowNumber, vectorDistanceSource } from "@atscript/db-sql-tools";
3
+ import { SEARCH_SOURCE_ALIAS, arithOverflowError, buildAggregateCount, buildAggregateSelect, buildCreateView, buildDelete, buildGeoSearchCount, buildGeoSearchSelect, buildInsert, buildInsertMany, buildPartitionedSelect, buildSelect, buildUpdate, buildVectorSearchCount, buildVectorSearchSelect, buildWhere as buildWhere$1, chunkInsertRows, defaultValueForType, defaultValueToSqlLiteral, derivedColumnExpr, fillReplacePayload, finalizeParams, foreignKeySql, geoWindowFromControls, mapQueryErrors, normalizeGeoPointValue, numericOutOfRangeError, orFragment, parseRegexString, quotedJsonPathSegments, renameGeoDistance, replaceColumnsFor, sqlStringLiteral, sqlTimeZoneLiteral, stripPartitionRowNumber, vectorDistanceSource } from "@atscript/db-sql-tools";
4
4
  import { BUCKET_MAX_INSTANT, BUCKET_MIN_INSTANT } from "@uniqu/core";
5
5
  //#region src/insert-ignore.ts
6
6
  /**
@@ -137,6 +137,7 @@ const pgDialect = {
137
137
  params: [pattern]
138
138
  };
139
139
  },
140
+ integerText: (expr) => `CAST(CAST(${expr} AS BIGINT) AS TEXT)`,
140
141
  geoWithin(quotedCol, circle) {
141
142
  return {
142
143
  sql: `ST_DWithin(${quotedCol}, ST_SetSRID(ST_MakePoint(?, ?), 4326)::geography, ?)`,
@@ -911,16 +912,14 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
911
912
  const colList = quotedKeys.join(", ");
912
913
  return `${quotedKeys.length === 1 ? `${colList} =` : `(${colList}) IN`} (SELECT ${colList} FROM ${quotedTable} WHERE ${whereSql} LIMIT 1)`;
913
914
  }
914
- async updateOne(filter, data, ops, expectedVersion) {
915
+ async updateOne(filter, data, ops, expectedVersion, opts) {
915
916
  const where = buildWhere(filter);
916
917
  const tableName = this.resolveTableName();
917
918
  const quotedTable = quoteTableName(tableName);
918
- const limitedWhere = {
919
+ const { sql, params } = buildUpdate$1(tableName, data, {
919
920
  sql: this._limitOnePredicate(quotedTable, where.sql),
920
921
  params: where.params
921
- };
922
- const versionColumn = this._table.versionColumnPhysical;
923
- const { sql, params } = buildUpdate$1(tableName, data, limitedWhere, void 0, ops, versionColumn, expectedVersion);
922
+ }, void 0, ops, this._versionColumnFor(opts, expectedVersion), expectedVersion);
924
923
  this._log(sql, params);
925
924
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
926
925
  return {
@@ -928,9 +927,9 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
928
927
  modifiedCount: result.affectedRows
929
928
  };
930
929
  }
931
- async updateMany(filter, data, ops) {
930
+ async updateMany(filter, data, ops, opts) {
932
931
  const where = buildWhere(filter);
933
- const versionColumn = this._table.versionColumnPhysical;
932
+ const versionColumn = this._versionColumnFor(opts);
934
933
  const { sql, params } = buildUpdate$1(this.resolveTableName(), data, where, void 0, ops, versionColumn);
935
934
  this._log(sql, params);
936
935
  const result = await this._wrapConstraintError(() => this._exec().run(sql, params));
@@ -1440,7 +1439,9 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1440
1439
  },
1441
1440
  createIndex: async (index) => {
1442
1441
  if (index.type === "fulltext") {
1443
- const tsvectorExpr = this._buildTsvectorExpr(index.fields);
1442
+ const textFields = splitFulltextFields(index).text;
1443
+ if (textFields.length === 0) return;
1444
+ const tsvectorExpr = this._buildTsvectorExpr(textFields);
1444
1445
  const sql = `CREATE INDEX IF NOT EXISTS ${qi(index.key)} ON ${quoteTableName(this.resolveTableName())} USING gin(to_tsvector('english', ${tsvectorExpr}))`;
1445
1446
  this._log(sql);
1446
1447
  await this._exec().exec(sql);
@@ -1534,12 +1535,14 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1534
1535
  }
1535
1536
  getSearchIndexes() {
1536
1537
  const indexes = [];
1537
- for (const index of this._table.indexes.values()) if (index.type === "fulltext") indexes.push({
1538
+ const ftAll = [...this._table.indexes.values()].filter((i) => i.type === "fulltext");
1539
+ const ftDefault = defaultFulltextIndex(ftAll);
1540
+ for (const index of ftAll) indexes.push({
1538
1541
  name: index.key,
1539
- description: `GIN tsvector index on ${index.fields.map((f) => f.name).join(", ")}`,
1542
+ description: describeFulltext(index, (names) => `GIN tsvector index on ${names}`),
1540
1543
  type: "text",
1541
1544
  fields: this._indexLogicalPaths(index),
1542
- isDefault: indexes.length === 0
1545
+ isDefault: index === ftDefault
1543
1546
  });
1544
1547
  let firstVector = true;
1545
1548
  for (const [field, vec] of this._vectorFields) {
@@ -1590,12 +1593,24 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1590
1593
  _buildSearchWhere(text, query, indexName) {
1591
1594
  const fulltextIndex = this._getFulltextIndex(indexName);
1592
1595
  if (!fulltextIndex) throw new Error("No fulltext index found for search");
1593
- const tsvectorExpr = this._buildTsvectorExpr(fulltextIndex.fields);
1596
+ const { text: textFields, integer: integerFields } = splitFulltextFields(fulltextIndex);
1594
1597
  const where = buildWhere(query.filter);
1595
- const tsqueryClause = `to_tsvector('english', ${tsvectorExpr}) @@ plainto_tsquery('english', ?)`;
1598
+ const n = integerFields.length > 0 ? searchTermInteger(text) : void 0;
1599
+ const parts = [];
1600
+ const params = [];
1601
+ if (textFields.length > 0) {
1602
+ const tsvectorExpr = this._buildTsvectorExpr(textFields);
1603
+ parts.push(`to_tsvector('english', ${tsvectorExpr}) @@ plainto_tsquery('english', ?)`);
1604
+ params.push(text);
1605
+ }
1606
+ if (n !== void 0) for (const f of integerFields) {
1607
+ parts.push(`${qi(f.name)} = CAST(? AS BIGINT)`);
1608
+ params.push(n);
1609
+ }
1610
+ const clause = orFragment(parts, params);
1596
1611
  return {
1597
- sql: where.sql === "1=1" ? tsqueryClause : `${where.sql} AND ${tsqueryClause}`,
1598
- params: [...where.params, text]
1612
+ sql: where.sql === "1=1" ? clause.sql : `${where.sql} AND ${clause.sql}`,
1613
+ params: [...where.params, ...clause.params]
1599
1614
  };
1600
1615
  }
1601
1616
  /** Builds the tsvector SQL expression for a fulltext index's fields. Must match between index DDL and queries. */
@@ -1603,9 +1618,9 @@ var PostgresAdapter = class PostgresAdapter extends BaseDbAdapter {
1603
1618
  return fields.map((f) => `coalesce(${qi(f.name)}, '')`).join(" || ' ' || ");
1604
1619
  }
1605
1620
  _getFulltextIndex(indexName) {
1606
- for (const index of this._table.indexes.values()) if (index.type === "fulltext") {
1607
- if (!indexName || index.key === indexName) return index;
1608
- }
1621
+ const ftAll = [...this._table.indexes.values()].filter((i) => i.type === "fulltext");
1622
+ if (!indexName) return defaultFulltextIndex(ftAll);
1623
+ return ftAll.find((index) => index.key === indexName);
1609
1624
  }
1610
1625
  /**
1611
1626
  * Detects pgvector support by attempting to enable the extension.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-postgres",
3
- "version": "0.1.149",
3
+ "version": "0.1.150",
4
4
  "description": "PostgreSQL adapter for @atscript/db with pg (node-postgres) driver support.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -43,20 +43,20 @@
43
43
  "access": "public"
44
44
  },
45
45
  "devDependencies": {
46
- "@atscript/core": "^0.1.100",
47
- "@atscript/typescript": "^0.1.100",
46
+ "@atscript/core": "^0.1.101",
47
+ "@atscript/typescript": "^0.1.101",
48
48
  "@types/pg": "^8.11.0",
49
49
  "@uniqu/core": "^0.1.13",
50
50
  "pg": "^8.13.0",
51
- "unplugin-atscript": "^0.1.100"
51
+ "unplugin-atscript": "^0.1.101"
52
52
  },
53
53
  "peerDependencies": {
54
- "@atscript/core": "^0.1.100",
55
- "@atscript/typescript": "^0.1.100",
54
+ "@atscript/core": "^0.1.101",
55
+ "@atscript/typescript": "^0.1.101",
56
56
  "@uniqu/core": "^0.1.13",
57
57
  "pg": ">=8.0.0",
58
- "@atscript/db-sql-tools": "^0.1.149",
59
- "@atscript/db": "^0.1.149"
58
+ "@atscript/db": "^0.1.150",
59
+ "@atscript/db-sql-tools": "^0.1.150"
60
60
  },
61
61
  "peerDependenciesMeta": {
62
62
  "pg": {