pqb 0.67.3 → 0.67.4

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
@@ -748,6 +748,15 @@ var Column = class {
748
748
  return setColumnData(this, "explicitSelect", !value);
749
749
  }
750
750
  /**
751
+ * Set SQL to use when selecting this column.
752
+ *
753
+ * The column remains a regular writable database column. Create, update,
754
+ * filters, ordering, grouping, and migrations still use the physical column.
755
+ */
756
+ selectSql(fn) {
757
+ return setColumnData(this, "selectSqlFn", fn);
758
+ }
759
+ /**
751
760
  * Forbid the column to be used in [create](/guide/create-update-delete.html#create-insert) and [update](/guide/create-update-delete.html#update) methods.
752
761
  *
753
762
  * `readOnly` column is still can be set from a [hook](http://localhost:5173/guide/hooks.html#set-values-before-create-or-update).
@@ -3969,6 +3978,7 @@ const applyComputedColumns = (q, fn) => {
3969
3978
  q.shape[key] = col;
3970
3979
  const { data } = col;
3971
3980
  data.computed = item;
3981
+ data.selectSql = item;
3972
3982
  data.explicitSelect = true;
3973
3983
  data.readOnly = true;
3974
3984
  const parse = col._parse;
@@ -6504,6 +6514,15 @@ const columnToSql = (ctx, data, shape, column, quotedAs, select) => {
6504
6514
  if (index !== -1) return columnWithDotToSql(ctx, data, shape, column, index, quotedAs, select);
6505
6515
  return simpleColumnToSQL(ctx, column, shape[column], quotedAs);
6506
6516
  };
6517
+ const selectedColumnToSql = (ctx, data, shape, column, quotedAs) => {
6518
+ const index = column.indexOf(".");
6519
+ return index !== -1 ? selectedColumnWithDotToSql(ctx, data, shape, column, index, quotedAs) : selectedSimpleColumnToSql(ctx, column, shape[column], quotedAs);
6520
+ };
6521
+ const selectedSimpleColumnToSql = (ctx, key, column, quotedAs) => {
6522
+ if (!column) return `"${key}"`;
6523
+ const { data } = column;
6524
+ return data.selectSql ? `(${data.selectSql.toSQL(ctx, quotedAs)})` : `${quotedAs ? `${quotedAs}.` : ""}"${data.name || key}"`;
6525
+ };
6507
6526
  /**
6508
6527
  * in a case when ordering or grouping by a column which was selected as expression:
6509
6528
  * ```ts
@@ -6529,7 +6548,7 @@ const columnWithDotToSql = (ctx, data, shape, column, index, quotedAs, select) =
6529
6548
  const key = column.slice(index + 1);
6530
6549
  if (key === "*") {
6531
6550
  const shape = data.joinedShapes?.[table];
6532
- return shape ? select ? makeRowToJson(table, shape, true) : `"${table}".*` : column;
6551
+ return shape ? select ? makeRowToJson(ctx, table, shape, true) : `"${table}".*` : column;
6533
6552
  }
6534
6553
  const tableName = _getQueryAliasOrName(data, table);
6535
6554
  const quoted = `"${table}"`;
@@ -6541,6 +6560,22 @@ const columnWithDotToSql = (ctx, data, shape, column, index, quotedAs, select) =
6541
6560
  }
6542
6561
  return `"${tableName}"."${key}"`;
6543
6562
  };
6563
+ const selectedColumnWithDotToSql = (ctx, data, shape, column, index, quotedAs) => {
6564
+ const table = column.slice(0, index);
6565
+ const key = column.slice(index + 1);
6566
+ if (key === "*") {
6567
+ const shape = data.joinedShapes?.[table];
6568
+ return shape ? makeRowToJson(ctx, table, shape, true) : column;
6569
+ }
6570
+ const tableName = _getQueryAliasOrName(data, table);
6571
+ const quoted = `"${table}"`;
6572
+ const col = quoted === quotedAs ? shape[key] : data.joinedShapes?.[tableName]?.[key];
6573
+ if (col) {
6574
+ if (col.data.selectSql) return `(${col.data.selectSql.toSQL(ctx, quoted)})`;
6575
+ if (col.data.name) return `"${tableName}"."${col.data.name}"`;
6576
+ }
6577
+ return `"${tableName}"."${key}"`;
6578
+ };
6544
6579
  const columnToSqlWithAs = (ctx, data, column, as, quotedAs, select, jsonList) => {
6545
6580
  const index = column.indexOf(".");
6546
6581
  return index !== -1 ? tableColumnToSqlWithAs(ctx, data, column, column.slice(0, index), column.slice(index + 1), as, quotedAs, select, jsonList) : ownColumnToSqlWithAs(ctx, data, column, as, quotedAs, select, jsonList);
@@ -6550,7 +6585,7 @@ const tableColumnToSqlWithAs = (ctx, data, column, table, key, as, quotedAs, sel
6550
6585
  if (jsonList) jsonList[as] = void 0;
6551
6586
  const shape = data.joinedShapes?.[table];
6552
6587
  if (shape) {
6553
- if (select) return makeRowToJson(table, shape, true) + ` "${as}"`;
6588
+ if (select) return makeRowToJson(ctx, table, shape, true) + ` "${as}"`;
6554
6589
  return `"${table}"."${table}" "${as}"`;
6555
6590
  }
6556
6591
  return column;
@@ -6558,10 +6593,10 @@ const tableColumnToSqlWithAs = (ctx, data, column, table, key, as, quotedAs, sel
6558
6593
  const tableName = _getQueryAliasOrName(data, table);
6559
6594
  const quoted = `"${table}"`;
6560
6595
  const col = quoted === quotedAs ? data.shape[key] : data.joinedShapes?.[tableName][key];
6561
- if (jsonList) jsonList[as] = col;
6596
+ if (jsonList) jsonList[as] = col && getSelectedColumnData(col);
6562
6597
  if (col) {
6598
+ if (col.data.selectSql) return `(${col.data.selectSql.toSQL(ctx, quoted)}) "${as}"`;
6563
6599
  if (col.data.name && col.data.name !== key) return `"${tableName}"."${col.data.name}" "${as}"`;
6564
- if (col.data.computed) return `(${col.data.computed.toSQL(ctx, quoted)}) "${as}"`;
6565
6600
  }
6566
6601
  return `"${tableName}"."${key}"${key === as ? "" : ` "${as}"`}`;
6567
6602
  };
@@ -6571,10 +6606,10 @@ const ownColumnToSqlWithAs = (ctx, data, column, as, quotedAs, select, jsonList)
6571
6606
  return `"${column}"."${column}" "${as}"`;
6572
6607
  }
6573
6608
  const col = data.shape[column];
6574
- if (jsonList) jsonList[as] = col;
6609
+ if (jsonList) jsonList[as] = col && getSelectedColumnData(col);
6575
6610
  if (col) {
6611
+ if (col.data.selectSql) return `(${col.data.selectSql.toSQL(ctx, quotedAs)}) "${as}"`;
6576
6612
  if (col.data.name && col.data.name !== column) return `${quotedAs ? `${quotedAs}.` : ""}"${col.data.name}"${col.data.name === as ? "" : ` "${as}"`}`;
6577
- if (col.data.computed) return `(${col.data.computed.toSQL(ctx, quotedAs)}) "${as}"`;
6578
6613
  }
6579
6614
  return `${quotedAs ? `${quotedAs}.` : ""}"${column}"${column === as ? "" : ` "${as}"`}`;
6580
6615
  };
@@ -6730,7 +6765,7 @@ var SelectItemExpression = class extends Expression {
6730
6765
  }
6731
6766
  makeSQL(ctx, quotedAs) {
6732
6767
  const q = this.q;
6733
- return typeof this.item === "string" ? this.item === "*" ? selectAllSql(q, quotedAs).join(", ") : columnToSql(ctx, q, q.shape, this.item, quotedAs, true) : this.item.toSQL(ctx, quotedAs);
6768
+ return typeof this.item === "string" ? this.item === "*" ? selectAllSql(q, quotedAs, void 0, ctx).join(", ") : selectedColumnToSql(ctx, q, q.shape, this.item, quotedAs) : this.item.toSQL(ctx, quotedAs);
6734
6769
  }
6735
6770
  };
6736
6771
  const _getSelectableColumn = (q, arg) => {
@@ -7087,12 +7122,12 @@ const selectToSqlList = (ctx, table, query, quotedAs, hookSelect = query.hookSel
7087
7122
  quotedTable = `"${tableName}"`;
7088
7123
  columnName = select.slice(index + 1);
7089
7124
  col = table.q.joinedShapes?.[tableName]?.[columnName];
7090
- sql = col?.data.computed ? col.data.computed.toSQL(ctx, `"${tableName}"`) : `"${tableName}"."${col?.data.name || columnName}"`;
7125
+ sql = selectedColumnToSql(ctx, table.q, table.q.shape, select, void 0);
7091
7126
  } else {
7092
7127
  quotedTable = quotedAs;
7093
7128
  columnName = select;
7094
7129
  col = query.shape[select];
7095
- sql = simpleColumnToSQL(ctx, select, col, quotedAs);
7130
+ sql = selectedColumnToSql(ctx, table.q, query.shape, select, quotedAs);
7096
7131
  }
7097
7132
  } else {
7098
7133
  columnName = column;
@@ -7132,17 +7167,24 @@ const selectToSql = (ctx, table, query, quotedAs, hookSelect = query.hookSelect,
7132
7167
  return selectToSqlList(ctx, table, query, quotedAs, hookSelect, isSubSql, aliases, jsonList, delayedRelationSelect).join(", ");
7133
7168
  };
7134
7169
  const internalSelectAllSql = (ctx, query, quotedAs, jsonList) => {
7135
- if (jsonList) Object.assign(jsonList, query.selectAllShape);
7170
+ if (jsonList) for (const key in query.selectAllShape) {
7171
+ const column = query.selectAllShape[key];
7172
+ jsonList[key] = getSelectedColumnData(column);
7173
+ }
7136
7174
  let columnsCount;
7137
7175
  if (query.shape !== anyShape) {
7138
7176
  columnsCount = 0;
7139
7177
  for (const key in query.shape) if (!query.shape[key].data.explicitSelect) columnsCount++;
7140
7178
  ctx.selectedCount += columnsCount;
7141
7179
  }
7142
- return selectAllSql(query, quotedAs, columnsCount);
7180
+ return selectAllSql(query, quotedAs, columnsCount, ctx);
7181
+ };
7182
+ const selectAllSql = (q, quotedAs, columnsCount, ctx) => {
7183
+ return q.join?.length || q.updateFrom || q.updateMany ? q.selectAllColumns?.map((item) => selectAllColumnToSql(item, ctx, quotedAs, true)) || (isEmptySelect(q.shape, columnsCount) ? [] : [`${quotedAs}.*`]) : q.selectAllColumns ? q.selectAllColumns.map((item) => selectAllColumnToSql(item, ctx, quotedAs)) : isEmptySelect(q.shape, columnsCount) ? [] : ["*"];
7143
7184
  };
7144
- const selectAllSql = (q, quotedAs, columnsCount) => {
7145
- return q.join?.length || q.updateFrom || q.updateMany ? q.selectAllColumns?.map((item) => `${quotedAs}.${item}`) || (isEmptySelect(q.shape, columnsCount) ? [] : [`${quotedAs}.*`]) : q.selectAllColumns ? [...q.selectAllColumns] : isEmptySelect(q.shape, columnsCount) ? [] : ["*"];
7185
+ const selectAllColumnToSql = (item, ctx, quotedAs, prefix) => {
7186
+ if (typeof item !== "string") return item(ctx, quotedAs);
7187
+ return prefix ? `${quotedAs}.${item}` : item;
7146
7188
  };
7147
7189
  const isEmptySelect = (shape, columnsCount) => columnsCount === void 0 ? shape === anyShape ? false : isObjectEmpty(shape) : !columnsCount;
7148
7190
  const pushSubQuerySql = (ctx, mainQuery, query, as, list, quotedAs, aliases) => {
@@ -7176,7 +7218,7 @@ const pushSubQuerySql = (ctx, mainQuery, query, as, list, quotedAs, aliases) =>
7176
7218
  case "oneOrThrow": {
7177
7219
  const table = query.q.joinedForSelect;
7178
7220
  const shape = mainQuery.joinedShapes?.[as];
7179
- sql = makeRowToJson(table, shape, false);
7221
+ sql = makeRowToJson(ctx, table, shape, false);
7180
7222
  break;
7181
7223
  }
7182
7224
  case "all":
@@ -9644,7 +9686,7 @@ const toSql = (table, type, topCtx, isSubSql, cteName, calledByThen, dontAddTabl
9644
9686
  if (ctx.topCtx.cteHooks.hasSelect) {
9645
9687
  if (prependedSelectParenthesis) result.text += ")";
9646
9688
  const { tableHooks, ensureCount } = ctx.topCtx.cteHooks;
9647
- const keyValues = [...tableHooks ? Object.entries(tableHooks).map(([cteName, data]) => `'${cteName}', (SELECT json_agg(${makeRowToJson(cteName, data.shape, false, true)}) FROM "${cteName}")`) : emptyArray, ...ensureCount ? Object.entries(ensureCount).map(([cteName, item]) => `'#${cteName}', CASE WHEN ${"count" in item ? `(SELECT count(*) FROM "${cteName}") < ${item.count}` : `(SELECT "${cteName}"."${item.jsonNotNull}" FROM "${cteName}") IS NULL`} THEN (SELECT 'not-found')::int END`) : emptyArray];
9689
+ const keyValues = [...tableHooks ? Object.entries(tableHooks).map(([cteName, data]) => `'${cteName}', (SELECT json_agg(${makeRowToJson(ctx, cteName, data.shape, false, true)}) FROM "${cteName}")`) : emptyArray, ...ensureCount ? Object.entries(ensureCount).map(([cteName, item]) => `'#${cteName}', CASE WHEN ${"count" in item ? `(SELECT count(*) FROM "${cteName}") < ${item.count}` : `(SELECT "${cteName}"."${item.jsonNotNull}" FROM "${cteName}") IS NULL`} THEN (SELECT 'not-found')::int END`) : emptyArray];
9648
9690
  result.text += ` UNION ALL SELECT ${"NULL, ".repeat(ctx.selectedCount || 0)}json_build_object(${keyValues.join(", ")})`;
9649
9691
  }
9650
9692
  }
@@ -9772,17 +9814,23 @@ const quoteFromWithSchema = (schema, table) => {
9772
9814
  const s = typeof schema === "function" ? schema() : schema;
9773
9815
  return s ? `"${s}"."${table}"` : `"${table}"`;
9774
9816
  };
9775
- const makeRowToJson = (table, shape, aliasName, includingExplicitSelect) => {
9817
+ const makeRowToJson = (ctx, table, shape, aliasName, includingExplicitSelect) => {
9776
9818
  let isSimple = true;
9777
9819
  const list = [];
9778
9820
  for (const key in shape) {
9779
9821
  const column = shape[key];
9780
9822
  if (!includingExplicitSelect && column.data.explicitSelect) continue;
9781
- if (aliasName && column.data.name || column.data.jsonCast) isSimple = false;
9782
- list.push(`'${key}', "${table}"."${aliasName && column.data.name || key}"${column.data.jsonCast ? `::${column.data.jsonCast}` : ""}`);
9823
+ const selectSql = !column.data.computed ? column.data.selectSql : void 0;
9824
+ const outputColumn = getSelectedColumnData(column);
9825
+ if (aliasName && column.data.name || outputColumn.data.jsonCast || selectSql) isSimple = false;
9826
+ const value = selectSql ? selectSql.toSQL(ctx, `"${table}"`) : `"${table}"."${aliasName && column.data.name || key}"`;
9827
+ list.push(`'${key}', ${value}${outputColumn.data.jsonCast ? `::${outputColumn.data.jsonCast}` : ""}`);
9783
9828
  }
9784
9829
  return isSimple ? `row_to_json("${table}".*)` : `CASE WHEN to_jsonb("${table}") IS NULL THEN NULL ELSE json_build_object(` + list.join(", ") + ") END";
9785
9830
  };
9831
+ const getSelectedColumnData = (column) => {
9832
+ return column.data.selectSql?.result.value || column;
9833
+ };
9786
9834
  const getSqlText = (sql) => {
9787
9835
  if ("text" in sql) return sql.text;
9788
9836
  throw new Error(`Batch SQL is not supported in this query`);
@@ -12421,6 +12469,10 @@ var MergeQueryMethods = class {
12421
12469
  return query;
12422
12470
  }
12423
12471
  };
12472
+ const applyColumnSelectSql = (column) => {
12473
+ const { selectSqlFn } = column.data;
12474
+ if (selectSqlFn) column.data.selectSql = selectSqlFn(new ColumnRefExpression(column, column.data.key));
12475
+ };
12424
12476
  const DEFAULT_PRIVILEGE = {
12425
12477
  OBJECT_TYPES: [
12426
12478
  "TABLES",
@@ -13791,6 +13843,10 @@ var Db = class extends QueryMethods {
13791
13843
  }
13792
13844
  if (column.data.explicitSelect) prepareSelectAll = true;
13793
13845
  else selectAllCount++;
13846
+ if (column.data.selectSqlFn) {
13847
+ applyColumnSelectSql(column);
13848
+ prepareSelectAll = true;
13849
+ }
13794
13850
  const { modifyQuery: mq } = column.data;
13795
13851
  if (mq) modifyQuery = pushOrNewArray(modifyQuery, (q) => mq(q, column));
13796
13852
  if (typeof column.data.default === "function") {
@@ -13848,7 +13904,12 @@ var Db = class extends QueryMethods {
13848
13904
  for (const key in shape) {
13849
13905
  const column = shape[key];
13850
13906
  if (!column.data.explicitSelect) {
13851
- list.push(column.data.name ? `"${column.data.name}" "${key}"` : `"${key}"`);
13907
+ const { data } = column;
13908
+ const { selectSql } = data;
13909
+ let item;
13910
+ if (selectSql) item = (ctx, quotedAs) => `(${selectSql.toSQL(ctx, quotedAs)}) "${key}"`;
13911
+ else item = data.name ? `"${data.name}" "${key}"` : `"${key}"`;
13912
+ list.push(item);
13852
13913
  selectAllShape[key] = column;
13853
13914
  }
13854
13915
  }