pqb 0.43.1 → 0.43.3

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.js CHANGED
@@ -1247,11 +1247,13 @@ class TextColumn extends TextBaseColumn {
1247
1247
  return textColumnToCode(this, ctx, key);
1248
1248
  }
1249
1249
  }
1250
+ const byteaParse = (val) => typeof val === "string" ? Buffer.from(val.slice(2), "hex") : val;
1250
1251
  class ByteaColumn extends ColumnType {
1251
1252
  constructor(schema) {
1252
1253
  super(schema, schema.buffer());
1253
1254
  this.dataType = "bytea";
1254
1255
  this.operators = Operators.text;
1256
+ setColumnDefaultParse(this, byteaParse);
1255
1257
  }
1256
1258
  toCode(ctx, key) {
1257
1259
  return columnCode(this, ctx, key, `bytea()`);
@@ -4468,7 +4470,7 @@ function maybeWrappedThen(resolve, reject) {
4468
4470
  let afterHooks;
4469
4471
  let afterCommitHooks;
4470
4472
  if (q.type) {
4471
- if (q.type === "insert") {
4473
+ if (q.type === "insert" || q.type === "upsert") {
4472
4474
  beforeHooks = q.beforeCreate;
4473
4475
  afterHooks = q.afterCreate;
4474
4476
  afterCommitHooks = q.afterCreateCommit;
@@ -4546,14 +4548,18 @@ const then = async (q, adapter, trx, beforeHooks, afterHooks, afterCommitHooks,
4546
4548
  if (log) {
4547
4549
  logData = log.beforeQuery(sql);
4548
4550
  }
4549
- queryResult = await adapter[queryMethodByReturnType[tempReturnType]](sql);
4550
- if (query.patchResult) {
4551
- await query.patchResult(q, queryResult);
4552
- }
4551
+ queryResult = await execQuery(
4552
+ adapter,
4553
+ queryMethodByReturnType[tempReturnType],
4554
+ sql
4555
+ );
4553
4556
  if (log) {
4554
4557
  log.afterQuery(sql, logData);
4555
4558
  sql = void 0;
4556
4559
  }
4560
+ if (query.patchResult) {
4561
+ await query.patchResult(q, hookSelect, queryResult);
4562
+ }
4557
4563
  result = query.handleResult(q, tempReturnType, queryResult);
4558
4564
  } else {
4559
4565
  const queryMethod = queryMethodByReturnType[tempReturnType];
@@ -4567,7 +4573,7 @@ const then = async (q, adapter, trx, beforeHooks, afterHooks, afterCommitHooks,
4567
4573
  if (log) {
4568
4574
  logData = log.beforeQuery(sql);
4569
4575
  }
4570
- const result2 = await adapter[queryMethod](sql);
4576
+ const result2 = await execQuery(adapter, queryMethod, sql);
4571
4577
  if (queryResult) {
4572
4578
  queryResult.rowCount += result2.rowCount;
4573
4579
  queryResult.rows.push(...result2.rows);
@@ -4585,7 +4591,7 @@ const then = async (q, adapter, trx, beforeHooks, afterHooks, afterCommitHooks,
4585
4591
  if (log) log.afterQuery(commitSql$1, logData);
4586
4592
  }
4587
4593
  if (query.patchResult) {
4588
- await query.patchResult(q, queryResult);
4594
+ await query.patchResult(q, hookSelect, queryResult);
4589
4595
  }
4590
4596
  result = query.handleResult(q, tempReturnType, queryResult);
4591
4597
  }
@@ -4713,6 +4719,17 @@ const then = async (q, adapter, trx, beforeHooks, afterHooks, afterCommitHooks,
4713
4719
  return reject?.(error);
4714
4720
  }
4715
4721
  };
4722
+ const execQuery = (adapter, method, sql) => {
4723
+ return adapter[method](sql).then(
4724
+ (result) => {
4725
+ if (result.rowCount && !result.rows.length) {
4726
+ result.rows.length = result.rowCount;
4727
+ result.rows.fill({});
4728
+ }
4729
+ return result;
4730
+ }
4731
+ );
4732
+ };
4716
4733
  const assignError = (to, from) => {
4717
4734
  to.message = from.message;
4718
4735
  to.length = from.length;
@@ -5652,6 +5669,305 @@ function queryJson(self, coalesce) {
5652
5669
  return q;
5653
5670
  }
5654
5671
 
5672
+ const MAX_BINDING_PARAMS = 65536;
5673
+
5674
+ const quotedColumns = [];
5675
+ const makeInsertSql = (ctx, q, query, quotedAs) => {
5676
+ const { columns, shape, inCTE } = query;
5677
+ quotedColumns.length = columns.length;
5678
+ for (let i = 0, len = columns.length; i < len; i++) {
5679
+ quotedColumns[i] = `"${shape[columns[i]]?.data.name || columns[i]}"`;
5680
+ }
5681
+ let runtimeDefaults;
5682
+ if (q.internal.runtimeDefaultColumns) {
5683
+ runtimeDefaults = [];
5684
+ for (const key of q.internal.runtimeDefaultColumns) {
5685
+ if (!columns.includes(key)) {
5686
+ const column = shape[key];
5687
+ quotedColumns.push(`"${column.data.name || key}"`);
5688
+ runtimeDefaults.push(column.data.runtimeDefault);
5689
+ }
5690
+ }
5691
+ }
5692
+ let values = query.values;
5693
+ if (quotedColumns.length === 0) {
5694
+ const key = Object.keys(q.shape)[0];
5695
+ const column = q.shape[key];
5696
+ quotedColumns[0] = `"${column?.data.name || key}"`;
5697
+ if (Array.isArray(values) && Array.isArray(values[0])) {
5698
+ values = values.map(() => [void 0]);
5699
+ }
5700
+ }
5701
+ ctx.sql.push(
5702
+ `INSERT INTO ${quotedAs}(${quotedColumns.join(", ")})`,
5703
+ null
5704
+ );
5705
+ const QueryClass = ctx.queryBuilder.constructor;
5706
+ if (query.onConflict) {
5707
+ ctx.sql.push("ON CONFLICT");
5708
+ const { target } = query.onConflict;
5709
+ if (target) {
5710
+ if (typeof target === "string") {
5711
+ ctx.sql.push(`("${shape[target]?.data.name || target}")`);
5712
+ } else if (Array.isArray(target)) {
5713
+ ctx.sql.push(
5714
+ `(${target.reduce(
5715
+ (sql, item, i) => sql + (i ? ", " : "") + `"${shape[item]?.data.name || item}"`,
5716
+ ""
5717
+ )})`
5718
+ );
5719
+ } else if ("toSQL" in target) {
5720
+ ctx.sql.push(target.toSQL(ctx, quotedAs));
5721
+ } else {
5722
+ ctx.sql.push(`ON CONSTRAINT "${target.constraint}"`);
5723
+ }
5724
+ }
5725
+ if ("merge" in query.onConflict) {
5726
+ let sql;
5727
+ const { merge } = query.onConflict;
5728
+ if (merge) {
5729
+ if (typeof merge === "string") {
5730
+ const name = shape[merge]?.data.name || merge;
5731
+ sql = `DO UPDATE SET "${name}" = excluded."${name}"`;
5732
+ } else if ("except" in merge) {
5733
+ sql = mergeColumnsSql(columns, quotedColumns, target, merge.except);
5734
+ } else {
5735
+ sql = `DO UPDATE SET ${merge.reduce((sql2, item, i) => {
5736
+ const name = shape[item]?.data.name || item;
5737
+ return sql2 + (i ? ", " : "") + `"${name}" = excluded."${name}"`;
5738
+ }, "")}`;
5739
+ }
5740
+ } else {
5741
+ sql = mergeColumnsSql(columns, quotedColumns, target);
5742
+ }
5743
+ ctx.sql.push(sql);
5744
+ } else if (query.onConflict.set) {
5745
+ let sql;
5746
+ const { set } = query.onConflict;
5747
+ if (orchidCore.isExpression(set)) {
5748
+ sql = set.toSQL(ctx, quotedAs);
5749
+ } else {
5750
+ const arr = [];
5751
+ for (const key in set) {
5752
+ arr.push(
5753
+ `"${shape[key]?.data.name || key}" = ${orchidCore.addValue(
5754
+ ctx.values,
5755
+ set[key]
5756
+ )}`
5757
+ );
5758
+ }
5759
+ sql = arr.join(", ");
5760
+ }
5761
+ ctx.sql.push("DO UPDATE SET", sql);
5762
+ } else {
5763
+ ctx.sql.push("DO NOTHING");
5764
+ }
5765
+ }
5766
+ pushWhereStatementSql(ctx, q, query, quotedAs);
5767
+ let returning;
5768
+ if (inCTE) {
5769
+ const select = inCTE.returning?.select;
5770
+ returning = {
5771
+ select: inCTE.selectNum || !select ? select ? "1, " + select : "1" : select,
5772
+ hookSelect: inCTE.returning?.hookSelect
5773
+ };
5774
+ } else {
5775
+ returning = makeReturningSql(ctx, q, query, quotedAs, 2);
5776
+ }
5777
+ if (returning.select) ctx.sql.push("RETURNING", returning.select);
5778
+ if (query.kind === "object") {
5779
+ const valuesSql = [];
5780
+ let ctxValues = ctx.values;
5781
+ const restValuesLen = ctxValues.length;
5782
+ let currentValuesLen = restValuesLen;
5783
+ let batch;
5784
+ for (let i = 0; i < values.length; i++) {
5785
+ let encodedRow = encodeRow(
5786
+ ctx,
5787
+ ctxValues,
5788
+ q,
5789
+ QueryClass,
5790
+ values[i],
5791
+ runtimeDefaults,
5792
+ quotedAs
5793
+ );
5794
+ if (!inCTE) encodedRow = "(" + encodedRow + ")";
5795
+ if (ctxValues.length > MAX_BINDING_PARAMS) {
5796
+ if (ctxValues.length - currentValuesLen > MAX_BINDING_PARAMS) {
5797
+ throw new Error(
5798
+ `Too many parameters for a single insert row, max is ${MAX_BINDING_PARAMS}`
5799
+ );
5800
+ }
5801
+ ctx.sql[1] = (inCTE ? "SELECT " : "VALUES ") + valuesSql.join(", ");
5802
+ ctxValues.length = currentValuesLen;
5803
+ batch = orchidCore.pushOrNewArray(batch, {
5804
+ text: ctx.sql.join(" "),
5805
+ values: ctxValues
5806
+ });
5807
+ ctxValues = ctx.values = [];
5808
+ valuesSql.length = 0;
5809
+ i--;
5810
+ } else {
5811
+ currentValuesLen = ctxValues.length;
5812
+ valuesSql.push(encodedRow);
5813
+ }
5814
+ }
5815
+ if (batch) {
5816
+ ctx.sql[1] = (inCTE ? "SELECT " : "VALUES ") + valuesSql.join(", ");
5817
+ batch.push({
5818
+ text: ctx.sql.join(" "),
5819
+ values: ctxValues
5820
+ });
5821
+ return {
5822
+ hookSelect: returning.hookSelect,
5823
+ batch
5824
+ };
5825
+ } else {
5826
+ ctx.sql[1] = (inCTE ? "SELECT " : "VALUES ") + valuesSql.join(", ");
5827
+ }
5828
+ if (inCTE) {
5829
+ ctx.sql[1] += ' WHERE NOT EXISTS (SELECT 1 FROM "f")';
5830
+ }
5831
+ } else if (query.kind === "raw") {
5832
+ if (orchidCore.isExpression(values)) {
5833
+ let valuesSql = values.toSQL(ctx, quotedAs);
5834
+ if (runtimeDefaults) {
5835
+ valuesSql += `, ${runtimeDefaults.map((fn) => orchidCore.addValue(ctx.values, fn())).join(", ")}`;
5836
+ }
5837
+ ctx.sql[1] = `VALUES (${valuesSql})`;
5838
+ } else {
5839
+ let sql;
5840
+ if (runtimeDefaults) {
5841
+ const { values: v } = ctx;
5842
+ sql = values.map(
5843
+ (raw) => (
5844
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
5845
+ `(${raw.toSQL(ctx, quotedAs)}, ${runtimeDefaults.map((fn) => orchidCore.addValue(v, fn())).join(", ")})`
5846
+ )
5847
+ ).join(", ");
5848
+ } else {
5849
+ sql = values.map((raw) => `(${raw.toSQL(ctx, quotedAs)})`).join(", ");
5850
+ }
5851
+ ctx.sql[1] = `VALUES ${sql}`;
5852
+ }
5853
+ } else {
5854
+ const { from, values: v } = values;
5855
+ const q2 = from.clone();
5856
+ if (v) {
5857
+ pushQueryValue(
5858
+ q2,
5859
+ "select",
5860
+ new RawSQL(
5861
+ encodeRow(
5862
+ ctx,
5863
+ ctx.values,
5864
+ q2,
5865
+ QueryClass,
5866
+ v[0],
5867
+ runtimeDefaults,
5868
+ quotedAs
5869
+ )
5870
+ )
5871
+ );
5872
+ }
5873
+ ctx.sql[1] = getSqlText(makeSQL(q2, { values: ctx.values }));
5874
+ }
5875
+ return {
5876
+ hookSelect: returning.hookSelect,
5877
+ text: ctx.sql.join(" "),
5878
+ values: ctx.values
5879
+ };
5880
+ };
5881
+ const mergeColumnsSql = (columns, quotedColumns2, target, except) => {
5882
+ const notExcluded = [];
5883
+ const exclude = typeof target === "string" ? [target] : Array.isArray(target) ? [...target] : [];
5884
+ if (except) {
5885
+ if (typeof except === "string") {
5886
+ exclude.push(except);
5887
+ } else {
5888
+ exclude.push(...except);
5889
+ }
5890
+ }
5891
+ for (let i = 0; i < columns.length; i++) {
5892
+ if (!exclude.includes(columns[i])) {
5893
+ notExcluded.push(quotedColumns2[i]);
5894
+ }
5895
+ }
5896
+ return notExcluded.length ? `DO UPDATE SET ${notExcluded.map((column) => `${column} = excluded.${column}`).join(", ")}` : "DO NOTHING";
5897
+ };
5898
+ const encodeRow = (ctx, values, q, QueryClass, row, runtimeDefaults, quotedAs) => {
5899
+ const arr = row.map((value) => {
5900
+ if (value && typeof value === "object") {
5901
+ if (value instanceof orchidCore.Expression) {
5902
+ return value.toSQL(ctx, quotedAs);
5903
+ } else if (value instanceof QueryClass) {
5904
+ return `(${getSqlText(joinSubQuery(q, value).toSQL(ctx))})`;
5905
+ }
5906
+ }
5907
+ return value === void 0 ? "DEFAULT" : orchidCore.addValue(values, value);
5908
+ });
5909
+ if (runtimeDefaults) {
5910
+ for (const fn of runtimeDefaults) {
5911
+ arr.push(orchidCore.addValue(values, fn()));
5912
+ }
5913
+ }
5914
+ return arr.join(", ");
5915
+ };
5916
+ const hookSelectKeys = [
5917
+ null,
5918
+ "afterUpdateSelect",
5919
+ "afterCreateSelect",
5920
+ "afterDeleteSelect"
5921
+ ];
5922
+ const makeReturningSql = (ctx, q, data, quotedAs, hookSelectI, addHookSelectI) => {
5923
+ if (data.inCTE) {
5924
+ if (hookSelectI !== 2) {
5925
+ const returning = makeReturningSql(
5926
+ ctx,
5927
+ q,
5928
+ data,
5929
+ quotedAs,
5930
+ 2,
5931
+ hookSelectI
5932
+ );
5933
+ if (returning.hookSelect) {
5934
+ for (const [key, value] of returning.hookSelect) {
5935
+ data.inCTE.targetHookSelect.set(key, value);
5936
+ }
5937
+ }
5938
+ return data.inCTE.returning = returning;
5939
+ }
5940
+ if (data.inCTE.returning) {
5941
+ return data.inCTE.returning;
5942
+ }
5943
+ }
5944
+ const hookSelect = hookSelectI && data[hookSelectKeys[hookSelectI]];
5945
+ const { select } = data;
5946
+ if (!q.q.hookSelect && !hookSelect?.size && !select?.length && !addHookSelectI) {
5947
+ return { hookSelect: hookSelect && /* @__PURE__ */ new Map() };
5948
+ }
5949
+ const otherCTEHookSelect = addHookSelectI && data[hookSelectKeys[addHookSelectI]];
5950
+ let tempSelect;
5951
+ if (q.q.hookSelect || hookSelect || otherCTEHookSelect) {
5952
+ tempSelect = new Map(q.q.hookSelect);
5953
+ if (hookSelect) {
5954
+ for (const column of hookSelect) {
5955
+ tempSelect.set(column, { select: column });
5956
+ }
5957
+ }
5958
+ if (otherCTEHookSelect) {
5959
+ for (const column of otherCTEHookSelect) {
5960
+ tempSelect.set(column, { select: column });
5961
+ }
5962
+ }
5963
+ }
5964
+ let sql;
5965
+ if (tempSelect?.size || select?.length) {
5966
+ sql = selectToSql(ctx, q, data, quotedAs, tempSelect, void 0, true);
5967
+ }
5968
+ return { select: sql, hookSelect: tempSelect };
5969
+ };
5970
+
5655
5971
  const pushSelectSql = (ctx, table, query, quotedAs, aliases) => {
5656
5972
  const sql = selectToSql(
5657
5973
  ctx,
@@ -5663,7 +5979,16 @@ const pushSelectSql = (ctx, table, query, quotedAs, aliases) => {
5663
5979
  );
5664
5980
  if (sql) ctx.sql.push(sql);
5665
5981
  };
5666
- const selectToSql = (ctx, table, query, quotedAs, hookSelect = query.hookSelect, aliases) => {
5982
+ const selectToSql = (ctx, table, query, quotedAs, hookSelect = query.hookSelect, aliases, skipCTE) => {
5983
+ if (query.inCTE && !skipCTE) {
5984
+ const { select } = makeReturningSql(
5985
+ ctx,
5986
+ table,
5987
+ query,
5988
+ quotedAs
5989
+ );
5990
+ return query.inCTE.selectNum || !select ? select ? "0, " + select : "0" : select;
5991
+ }
5667
5992
  let selected;
5668
5993
  const list = [];
5669
5994
  if (query.select) {
@@ -6007,371 +6332,116 @@ const pushFromAndAs = (ctx, table, data, quotedAs) => {
6007
6332
  let fn;
6008
6333
  let query;
6009
6334
  if ("query" in source) {
6010
- fn = "websearch_to_tsquery";
6011
- query = source.query;
6012
- } else if ("plainQuery" in source) {
6013
- fn = "plainto_tsquery";
6014
- query = source.plainQuery;
6015
- } else if ("phraseQuery" in source) {
6016
- fn = "phraseto_tsquery";
6017
- query = source.phraseQuery;
6018
- } else {
6019
- fn = "to_tsquery";
6020
- query = source.tsQuery;
6021
- }
6022
- let querySql;
6023
- if (typeof query === "string") {
6024
- ctx.values.push(query);
6025
- querySql = `$${ctx.values.length}`;
6026
- } else {
6027
- querySql = `${query.toSQL(ctx, quotedAs)}`;
6028
- }
6029
- sql += `, ${fn}(${lang}, ${querySql}) "${as}"`;
6030
- }
6031
- ctx.sql.push(sql);
6032
- };
6033
- const getFrom = (ctx, table, data, quotedAs) => {
6034
- if (data.from) {
6035
- const { from } = data;
6036
- if (Array.isArray(from)) {
6037
- return from.map((item) => fromToSql(ctx, data, item, quotedAs)).join(", ");
6038
- }
6039
- return fromToSql(ctx, data, from, quotedAs);
6040
- }
6041
- let sql = quoteSchemaAndTable(data.schema, table.table);
6042
- if (data.only) sql = `ONLY ${sql}`;
6043
- return sql;
6044
- };
6045
- const fromToSql = (ctx, data, from, quotedAs) => {
6046
- let only;
6047
- let sql;
6048
- if (typeof from === "object") {
6049
- if (orchidCore.isExpression(from)) {
6050
- sql = from.toSQL(ctx, quotedAs);
6051
- } else {
6052
- only = from.q.only;
6053
- if (!from.table) {
6054
- sql = `(${getSqlText(makeSQL(from, ctx))})`;
6055
- } else if (!checkIfASimpleQuery(from)) {
6056
- sql = `(${getSqlText(makeSQL(from, ctx))})`;
6057
- } else {
6058
- sql = quoteSchemaAndTable(from.q.schema, from.table);
6059
- }
6060
- }
6061
- } else {
6062
- sql = quoteSchemaAndTable(data.schema, from);
6063
- }
6064
- return (only === void 0 ? data.only : only) ? `ONLY ${sql}` : sql;
6065
- };
6066
- const getSearchLang = (ctx, data, source, quotedAs) => {
6067
- return source.langSQL ?? (source.langSQL = "languageColumn" in source ? columnToSql(ctx, data, data.shape, source.languageColumn, quotedAs) : orchidCore.isRawSQL(source.language) ? source.language.toSQL(ctx) : orchidCore.addValue(ctx.values, source.language || data.language || "english"));
6068
- };
6069
- const getSearchText = (ctx, data, source, quotedAs, forHeadline) => {
6070
- let sql = source.textSQL;
6071
- if (sql) return sql;
6072
- if ("in" in source) {
6073
- if (typeof source.in === "string") {
6074
- sql = columnToSql(ctx, data, data.shape, source.in, quotedAs);
6075
- } else if (Array.isArray(source.in)) {
6076
- sql = `concat_ws(' ', ${source.in.map((column) => columnToSql(ctx, data, data.shape, column, quotedAs)).join(", ")})`;
6077
- } else {
6078
- sql = [];
6079
- for (const key in source.in) {
6080
- sql.push(columnToSql(ctx, data, data.shape, key, quotedAs));
6081
- }
6082
- }
6083
- } else if ("vector" in source) {
6084
- if (forHeadline) {
6085
- throw new Error(
6086
- "Cannot use a search based on a vector column for a search headline"
6087
- );
6088
- }
6089
- sql = columnToSql(ctx, data, data.shape, source.vector, quotedAs);
6090
- } else {
6091
- if (typeof source.text === "string") {
6092
- sql = orchidCore.addValue(ctx.values, source.text);
6093
- } else {
6094
- sql = source.text.toSQL(ctx, quotedAs);
6095
- }
6096
- }
6097
- return source.textSQL = sql;
6098
- };
6099
- const getTsVector = (ctx, data, lang, source, quotedAs) => {
6100
- const text = getSearchText(ctx, data, source, quotedAs);
6101
- if ("in" in source) {
6102
- if (typeof source.in === "string" || Array.isArray(source.in)) {
6103
- return `to_tsvector(${lang}, ${text})`;
6104
- } else {
6105
- let tsVector = "";
6106
- let i = 0;
6107
- for (const key in source.in) {
6108
- tsVector = (tsVector ? `${tsVector} || ` : "") + `setweight(to_tsvector(${lang}, ${text[i++]}), ${orchidCore.addValue(
6109
- ctx.values,
6110
- source.in[key]
6111
- )})`;
6112
- }
6113
- return tsVector;
6114
- }
6115
- } else if ("vector" in source) {
6116
- return text;
6117
- } else {
6118
- return `to_tsvector(${lang}, ${text})`;
6119
- }
6120
- };
6121
-
6122
- const MAX_BINDING_PARAMS = 65536;
6123
-
6124
- const quotedColumns = [];
6125
- const makeInsertSql = (ctx, q, query, quotedAs) => {
6126
- const { columns, shape } = query;
6127
- quotedColumns.length = columns.length;
6128
- for (let i = 0, len = columns.length; i < len; i++) {
6129
- quotedColumns[i] = `"${shape[columns[i]]?.data.name || columns[i]}"`;
6130
- }
6131
- let runtimeDefaults;
6132
- if (q.internal.runtimeDefaultColumns) {
6133
- runtimeDefaults = [];
6134
- for (const key of q.internal.runtimeDefaultColumns) {
6135
- if (!columns.includes(key)) {
6136
- const column = shape[key];
6137
- quotedColumns.push(`"${column.data.name || key}"`);
6138
- runtimeDefaults.push(column.data.runtimeDefault);
6139
- }
6140
- }
6141
- }
6142
- let values = query.values;
6143
- if (quotedColumns.length === 0) {
6144
- const key = Object.keys(q.shape)[0];
6145
- const column = q.shape[key];
6146
- quotedColumns[0] = `"${column?.data.name || key}"`;
6147
- if (Array.isArray(values) && Array.isArray(values[0])) {
6148
- values = values.map(() => [void 0]);
6149
- }
6150
- }
6151
- ctx.sql.push(
6152
- `INSERT INTO ${quotedAs}(${quotedColumns.join(", ")})`,
6153
- null
6154
- );
6155
- const QueryClass = ctx.queryBuilder.constructor;
6156
- if (query.onConflict) {
6157
- ctx.sql.push("ON CONFLICT");
6158
- const { target } = query.onConflict;
6159
- if (target) {
6160
- if (typeof target === "string") {
6161
- ctx.sql.push(`("${shape[target]?.data.name || target}")`);
6162
- } else if (Array.isArray(target)) {
6163
- ctx.sql.push(
6164
- `(${target.reduce(
6165
- (sql, item, i) => sql + (i ? ", " : "") + `"${shape[item]?.data.name || item}"`,
6166
- ""
6167
- )})`
6168
- );
6169
- } else if ("toSQL" in target) {
6170
- ctx.sql.push(target.toSQL(ctx, quotedAs));
6171
- } else {
6172
- ctx.sql.push(`ON CONSTRAINT "${target.constraint}"`);
6173
- }
6174
- }
6175
- if ("merge" in query.onConflict) {
6176
- let sql;
6177
- const { merge } = query.onConflict;
6178
- if (merge) {
6179
- if (typeof merge === "string") {
6180
- const name = shape[merge]?.data.name || merge;
6181
- sql = `DO UPDATE SET "${name}" = excluded."${name}"`;
6182
- } else if ("except" in merge) {
6183
- sql = mergeColumnsSql(columns, quotedColumns, target, merge.except);
6184
- } else {
6185
- sql = `DO UPDATE SET ${merge.reduce((sql2, item, i) => {
6186
- const name = shape[item]?.data.name || item;
6187
- return sql2 + (i ? ", " : "") + `"${name}" = excluded."${name}"`;
6188
- }, "")}`;
6189
- }
6190
- } else {
6191
- sql = mergeColumnsSql(columns, quotedColumns, target);
6192
- }
6193
- ctx.sql.push(sql);
6194
- } else if (query.onConflict.set) {
6195
- let sql;
6196
- const { set } = query.onConflict;
6197
- if (orchidCore.isExpression(set)) {
6198
- sql = set.toSQL(ctx, quotedAs);
6199
- } else {
6200
- const arr = [];
6201
- for (const key in set) {
6202
- arr.push(
6203
- `"${shape[key]?.data.name || key}" = ${orchidCore.addValue(
6204
- ctx.values,
6205
- set[key]
6206
- )}`
6207
- );
6208
- }
6209
- sql = arr.join(", ");
6210
- }
6211
- ctx.sql.push("DO UPDATE SET", sql);
6335
+ fn = "websearch_to_tsquery";
6336
+ query = source.query;
6337
+ } else if ("plainQuery" in source) {
6338
+ fn = "plainto_tsquery";
6339
+ query = source.plainQuery;
6340
+ } else if ("phraseQuery" in source) {
6341
+ fn = "phraseto_tsquery";
6342
+ query = source.phraseQuery;
6212
6343
  } else {
6213
- ctx.sql.push("DO NOTHING");
6214
- }
6215
- }
6216
- pushWhereStatementSql(ctx, q, query, quotedAs);
6217
- const hookSelect = pushReturningSql(
6218
- ctx,
6219
- q,
6220
- query,
6221
- quotedAs,
6222
- query.afterCreateSelect
6223
- );
6224
- if (query.kind === "object") {
6225
- const valuesSql = [];
6226
- let ctxValues = ctx.values;
6227
- const restValuesLen = ctxValues.length;
6228
- let currentValuesLen = restValuesLen;
6229
- let batch;
6230
- for (let i = 0; i < values.length; i++) {
6231
- const encodedRow = `(${encodeRow(
6232
- ctx,
6233
- ctxValues,
6234
- q,
6235
- QueryClass,
6236
- values[i],
6237
- runtimeDefaults,
6238
- quotedAs
6239
- )})`;
6240
- if (ctxValues.length > MAX_BINDING_PARAMS) {
6241
- if (ctxValues.length - currentValuesLen > MAX_BINDING_PARAMS) {
6242
- throw new Error(
6243
- `Too many parameters for a single insert row, max is ${MAX_BINDING_PARAMS}`
6244
- );
6245
- }
6246
- ctx.sql[1] = `VALUES ${valuesSql.join(",")}`;
6247
- ctxValues.length = currentValuesLen;
6248
- batch = orchidCore.pushOrNewArray(batch, {
6249
- text: ctx.sql.join(" "),
6250
- values: ctxValues
6251
- });
6252
- ctxValues = ctx.values = [];
6253
- valuesSql.length = 0;
6254
- i--;
6255
- } else {
6256
- currentValuesLen = ctxValues.length;
6257
- valuesSql.push(encodedRow);
6258
- }
6344
+ fn = "to_tsquery";
6345
+ query = source.tsQuery;
6259
6346
  }
6260
- if (batch) {
6261
- ctx.sql[1] = `VALUES ${valuesSql.join(",")}`;
6262
- batch.push({
6263
- text: ctx.sql.join(" "),
6264
- values: ctxValues
6265
- });
6266
- return {
6267
- hookSelect,
6268
- batch
6269
- };
6347
+ let querySql;
6348
+ if (typeof query === "string") {
6349
+ ctx.values.push(query);
6350
+ querySql = `$${ctx.values.length}`;
6270
6351
  } else {
6271
- ctx.sql[1] = `VALUES ${valuesSql.join(", ")}`;
6352
+ querySql = `${query.toSQL(ctx, quotedAs)}`;
6272
6353
  }
6273
- } else if (query.kind === "raw") {
6274
- if (orchidCore.isExpression(values)) {
6275
- let valuesSql = values.toSQL(ctx, quotedAs);
6276
- if (runtimeDefaults) {
6277
- valuesSql += `, ${runtimeDefaults.map((fn) => orchidCore.addValue(ctx.values, fn())).join(", ")}`;
6278
- }
6279
- ctx.sql[1] = `VALUES (${valuesSql})`;
6354
+ sql += `, ${fn}(${lang}, ${querySql}) "${as}"`;
6355
+ }
6356
+ ctx.sql.push(sql);
6357
+ };
6358
+ const getFrom = (ctx, table, data, quotedAs) => {
6359
+ if (data.from) {
6360
+ const { from } = data;
6361
+ if (Array.isArray(from)) {
6362
+ return from.map((item) => fromToSql(ctx, data, item, quotedAs)).join(", ");
6363
+ }
6364
+ return fromToSql(ctx, data, from, quotedAs);
6365
+ }
6366
+ let sql = quoteSchemaAndTable(data.schema, table.table);
6367
+ if (data.only) sql = `ONLY ${sql}`;
6368
+ return sql;
6369
+ };
6370
+ const fromToSql = (ctx, data, from, quotedAs) => {
6371
+ let only;
6372
+ let sql;
6373
+ if (typeof from === "object") {
6374
+ if (orchidCore.isExpression(from)) {
6375
+ sql = from.toSQL(ctx, quotedAs);
6280
6376
  } else {
6281
- let sql;
6282
- if (runtimeDefaults) {
6283
- const { values: v } = ctx;
6284
- sql = values.map(
6285
- (raw) => (
6286
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
6287
- `(${raw.toSQL(ctx, quotedAs)}, ${runtimeDefaults.map((fn) => orchidCore.addValue(v, fn())).join(", ")})`
6288
- )
6289
- ).join(", ");
6377
+ only = from.q.only;
6378
+ if (!from.table) {
6379
+ sql = `(${getSqlText(makeSQL(from, ctx))})`;
6380
+ } else if (!checkIfASimpleQuery(from)) {
6381
+ sql = `(${getSqlText(makeSQL(from, ctx))})`;
6290
6382
  } else {
6291
- sql = values.map((raw) => `(${raw.toSQL(ctx, quotedAs)})`).join(", ");
6383
+ sql = quoteSchemaAndTable(from.q.schema, from.table);
6292
6384
  }
6293
- ctx.sql[1] = `VALUES ${sql}`;
6294
6385
  }
6295
6386
  } else {
6296
- const { from, values: v } = values;
6297
- const q2 = from.clone();
6298
- if (v) {
6299
- pushQueryValue(
6300
- q2,
6301
- "select",
6302
- new RawSQL(
6303
- encodeRow(
6304
- ctx,
6305
- ctx.values,
6306
- q2,
6307
- QueryClass,
6308
- v[0],
6309
- runtimeDefaults,
6310
- quotedAs
6311
- )
6312
- )
6313
- );
6314
- }
6315
- ctx.sql[1] = getSqlText(makeSQL(q2, { values: ctx.values }));
6387
+ sql = quoteSchemaAndTable(data.schema, from);
6316
6388
  }
6317
- return {
6318
- hookSelect,
6319
- text: ctx.sql.join(" "),
6320
- values: ctx.values
6321
- };
6389
+ return (only === void 0 ? data.only : only) ? `ONLY ${sql}` : sql;
6322
6390
  };
6323
- const mergeColumnsSql = (columns, quotedColumns2, target, except) => {
6324
- const notExcluded = [];
6325
- const exclude = typeof target === "string" ? [target] : Array.isArray(target) ? [...target] : [];
6326
- if (except) {
6327
- if (typeof except === "string") {
6328
- exclude.push(except);
6329
- } else {
6330
- exclude.push(...except);
6331
- }
6332
- }
6333
- for (let i = 0; i < columns.length; i++) {
6334
- if (!exclude.includes(columns[i])) {
6335
- notExcluded.push(quotedColumns2[i]);
6336
- }
6337
- }
6338
- return notExcluded.length ? `DO UPDATE SET ${notExcluded.map((column) => `${column} = excluded.${column}`).join(", ")}` : "DO NOTHING";
6391
+ const getSearchLang = (ctx, data, source, quotedAs) => {
6392
+ return source.langSQL ?? (source.langSQL = "languageColumn" in source ? columnToSql(ctx, data, data.shape, source.languageColumn, quotedAs) : orchidCore.isRawSQL(source.language) ? source.language.toSQL(ctx) : orchidCore.addValue(ctx.values, source.language || data.language || "english"));
6339
6393
  };
6340
- const encodeRow = (ctx, values, q, QueryClass, row, runtimeDefaults, quotedAs) => {
6341
- const arr = row.map((value) => {
6342
- if (value && typeof value === "object") {
6343
- if (value instanceof orchidCore.Expression) {
6344
- return value.toSQL(ctx, quotedAs);
6345
- } else if (value instanceof QueryClass) {
6346
- return `(${getSqlText(joinSubQuery(q, value).toSQL(ctx))})`;
6394
+ const getSearchText = (ctx, data, source, quotedAs, forHeadline) => {
6395
+ let sql = source.textSQL;
6396
+ if (sql) return sql;
6397
+ if ("in" in source) {
6398
+ if (typeof source.in === "string") {
6399
+ sql = columnToSql(ctx, data, data.shape, source.in, quotedAs);
6400
+ } else if (Array.isArray(source.in)) {
6401
+ sql = `concat_ws(' ', ${source.in.map((column) => columnToSql(ctx, data, data.shape, column, quotedAs)).join(", ")})`;
6402
+ } else {
6403
+ sql = [];
6404
+ for (const key in source.in) {
6405
+ sql.push(columnToSql(ctx, data, data.shape, key, quotedAs));
6347
6406
  }
6348
6407
  }
6349
- return value === void 0 ? "DEFAULT" : orchidCore.addValue(values, value);
6350
- });
6351
- if (runtimeDefaults) {
6352
- for (const fn of runtimeDefaults) {
6353
- arr.push(orchidCore.addValue(values, fn()));
6408
+ } else if ("vector" in source) {
6409
+ if (forHeadline) {
6410
+ throw new Error(
6411
+ "Cannot use a search based on a vector column for a search headline"
6412
+ );
6413
+ }
6414
+ sql = columnToSql(ctx, data, data.shape, source.vector, quotedAs);
6415
+ } else {
6416
+ if (typeof source.text === "string") {
6417
+ sql = orchidCore.addValue(ctx.values, source.text);
6418
+ } else {
6419
+ sql = source.text.toSQL(ctx, quotedAs);
6354
6420
  }
6355
6421
  }
6356
- return arr.join(", ");
6422
+ return source.textSQL = sql;
6357
6423
  };
6358
- const pushReturningSql = (ctx, q, data, quotedAs, hookSelect, keyword = "RETURNING") => {
6359
- const { select } = data;
6360
- if (!hookSelect?.size && !select) return hookSelect && /* @__PURE__ */ new Map();
6361
- ctx.sql.push(keyword);
6362
- if (q.q.hookSelect || hookSelect) {
6363
- const tempSelect = new Map(q.q.hookSelect);
6364
- if (hookSelect) {
6365
- for (const column of hookSelect) {
6366
- tempSelect.set(column, { select: column });
6424
+ const getTsVector = (ctx, data, lang, source, quotedAs) => {
6425
+ const text = getSearchText(ctx, data, source, quotedAs);
6426
+ if ("in" in source) {
6427
+ if (typeof source.in === "string" || Array.isArray(source.in)) {
6428
+ return `to_tsvector(${lang}, ${text})`;
6429
+ } else {
6430
+ let tsVector = "";
6431
+ let i = 0;
6432
+ for (const key in source.in) {
6433
+ tsVector = (tsVector ? `${tsVector} || ` : "") + `setweight(to_tsvector(${lang}, ${text[i++]}), ${orchidCore.addValue(
6434
+ ctx.values,
6435
+ source.in[key]
6436
+ )})`;
6367
6437
  }
6438
+ return tsVector;
6368
6439
  }
6369
- ctx.sql.push(selectToSql(ctx, q, data, quotedAs, tempSelect));
6370
- return tempSelect;
6371
- } else if (select) {
6372
- ctx.sql.push(selectToSql(ctx, q, data, quotedAs));
6440
+ } else if ("vector" in source) {
6441
+ return text;
6442
+ } else {
6443
+ return `to_tsvector(${lang}, ${text})`;
6373
6444
  }
6374
- return;
6375
6445
  };
6376
6446
 
6377
6447
  const pushUpdateSql = (ctx, table, query, quotedAs) => {
@@ -6382,12 +6452,11 @@ const pushUpdateSql = (ctx, table, query, quotedAs) => {
6382
6452
  if (!query.select) {
6383
6453
  query.select = countSelect;
6384
6454
  }
6385
- const hookSelect = pushReturningSql(
6455
+ const hookSelect = pushUpdateReturning(
6386
6456
  ctx,
6387
6457
  table,
6388
6458
  query,
6389
6459
  quotedAs,
6390
- query.afterUpdateSelect,
6391
6460
  "SELECT"
6392
6461
  );
6393
6462
  ctx.sql.push(`FROM ${quotedTable}`);
@@ -6402,7 +6471,21 @@ const pushUpdateSql = (ctx, table, query, quotedAs) => {
6402
6471
  ctx.sql.push("SET");
6403
6472
  ctx.sql.push(set.join(", "));
6404
6473
  pushWhereStatementSql(ctx, table, query, quotedAs);
6405
- return pushReturningSql(ctx, table, query, quotedAs, query.afterUpdateSelect);
6474
+ return pushUpdateReturning(ctx, table, query, quotedAs, "RETURNING");
6475
+ };
6476
+ const pushUpdateReturning = (ctx, table, query, quotedAs, keyword) => {
6477
+ const { inCTE } = query;
6478
+ const { select, hookSelect } = makeReturningSql(
6479
+ ctx,
6480
+ table,
6481
+ query,
6482
+ quotedAs,
6483
+ 1,
6484
+ inCTE && 2
6485
+ );
6486
+ const s = inCTE && (inCTE.selectNum || !select) ? select ? "0, " + select : "0" : select;
6487
+ if (s) ctx.sql.push(keyword, s);
6488
+ return hookSelect;
6406
6489
  };
6407
6490
  const processData = (ctx, table, set, data, quotedAs) => {
6408
6491
  let append;
@@ -6485,7 +6568,9 @@ const pushDeleteSql = (ctx, table, query, quotedAs) => {
6485
6568
  ctx.sql.push("WHERE", conditions);
6486
6569
  }
6487
6570
  }
6488
- return pushReturningSql(ctx, table, query, quotedAs, query.afterDeleteSelect);
6571
+ const returning = makeReturningSql(ctx, table, query, quotedAs, 3);
6572
+ if (returning.select) ctx.sql.push("RETURNING", returning.select);
6573
+ return returning.hookSelect;
6489
6574
  };
6490
6575
 
6491
6576
  const pushTruncateSql = (ctx, table, query) => {
@@ -6564,7 +6649,7 @@ const makeSQL = (table, options) => {
6564
6649
  if (query.with) {
6565
6650
  pushWithSql(ctx, query.with);
6566
6651
  }
6567
- if (query.type) {
6652
+ if (query.type && query.type !== "upsert") {
6568
6653
  const tableName = table.table ?? query.as;
6569
6654
  if (!tableName) throw new Error(`Table is missing for ${query.type}`);
6570
6655
  if (query.type === "truncate") {
@@ -6600,10 +6685,11 @@ const makeSQL = (table, options) => {
6600
6685
  }
6601
6686
  const quotedAs = (query.as || table.table) && `"${query.as || table.table}"`;
6602
6687
  if (query.union) {
6603
- sql.push(`(${getSqlText(makeSQL(query.union.b, { values }))})`);
6688
+ const s = getSqlText(makeSQL(query.union.b, { values }));
6689
+ sql.push(query.union.p ? s : `(${s})`);
6604
6690
  for (const u of query.union.u) {
6605
- const itemSql = orchidCore.isExpression(u.a) ? u.a.toSQL(ctx, quotedAs) : getSqlText(makeSQL(u.a, { values }));
6606
- sql.push(`${u.k} (${itemSql})`);
6691
+ const s2 = orchidCore.isExpression(u.a) ? u.a.toSQL(ctx, quotedAs) : getSqlText(makeSQL(u.a, { values }));
6692
+ sql.push(`${u.k} ${u.p ? s2 : "(" + s2 + ")"}`);
6607
6693
  }
6608
6694
  } else {
6609
6695
  sql.push("SELECT");
@@ -9989,12 +10075,13 @@ class MergeQueryMethods {
9989
10075
  }
9990
10076
  }
9991
10077
 
9992
- const _queryUnion = (base, args, k) => {
10078
+ const _queryUnion = (base, args, k, p, m) => {
9993
10079
  const q = base.baseQuery.clone();
9994
10080
  const u = args.map(
9995
10081
  (a) => ({
9996
10082
  a: typeof a === "function" ? a(q) : a,
9997
- k
10083
+ k,
10084
+ m
9998
10085
  })
9999
10086
  );
10000
10087
  const union = q.q.union = base.q.union;
@@ -10003,7 +10090,8 @@ const _queryUnion = (base, args, k) => {
10003
10090
  } else {
10004
10091
  q.q.union = {
10005
10092
  b: base,
10006
- u
10093
+ u,
10094
+ p
10007
10095
  };
10008
10096
  }
10009
10097
  return q;
@@ -10281,7 +10369,7 @@ const _queryUpdate = (query, arg) => {
10281
10369
  }
10282
10370
  const { queries } = ctx;
10283
10371
  if (queries) {
10284
- q.patchResult = async (_, queryResult) => {
10372
+ q.patchResult = async (_, _h, queryResult) => {
10285
10373
  await Promise.all(queries.map(orchidCore.callWithThis, queryResult));
10286
10374
  if (ctx.collect) {
10287
10375
  const t = query.baseQuery.clone();
@@ -10870,26 +10958,80 @@ class SearchMethods {
10870
10958
 
10871
10959
  function orCreate(query, data, updateData, mergeData) {
10872
10960
  const { q } = query;
10873
- q.returnType = "one";
10874
- q.wrapInTransaction = true;
10961
+ q.returnsOne = true;
10962
+ if (!q.select) {
10963
+ q.returnType = "void";
10964
+ }
10875
10965
  const { handleResult } = q;
10876
10966
  let result;
10877
10967
  let created = false;
10878
10968
  q.handleResult = (q2, t, r, s) => {
10879
10969
  return created ? result : handleResult(q2, t, r, s);
10880
10970
  };
10881
- q.patchResult = async (q2, queryResult) => {
10971
+ q.hookSelect ?? (q.hookSelect = /* @__PURE__ */ new Map());
10972
+ q.patchResult = async (q2, hookSelect, queryResult) => {
10973
+ var _a, _b;
10882
10974
  if (queryResult.rowCount === 0) {
10883
10975
  if (typeof data === "function") {
10884
10976
  data = data(updateData);
10885
10977
  }
10886
10978
  if (mergeData) data = { ...mergeData, ...data };
10887
- const inner = q2.create(data);
10888
- inner.q.handleResult = (q3, t, r, s) => {
10889
- result = handleResult(q3, t, r, s);
10890
- return inner.q.hookSelect ? result.map((row) => ({ ...row })) : result;
10979
+ let hasAfterCallback = q2.q.afterCreate;
10980
+ let hasAfterCommitCallback = q2.q.afterCreateCommit;
10981
+ if (updateData) {
10982
+ hasAfterCallback = hasAfterCallback || q2.q.afterUpdate;
10983
+ hasAfterCommitCallback = hasAfterCommitCallback || q2.q.afterUpdateCommit;
10984
+ }
10985
+ const inCTE = {
10986
+ selectNum: !!(hasAfterCallback || hasAfterCommitCallback),
10987
+ targetHookSelect: hookSelect
10891
10988
  };
10892
- await inner;
10989
+ q2 = q2.clone();
10990
+ q2.q.inCTE = inCTE;
10991
+ const c = q2.create(data);
10992
+ c.q.select = q2.q.select;
10993
+ let q22 = q2.queryBuilder.with("f", q2).with("c", c);
10994
+ q22.q.returnsOne = true;
10995
+ queryFrom(q22, "f");
10996
+ q22 = _queryUnion(
10997
+ q22,
10998
+ [q2.queryBuilder.from("c")],
10999
+ "UNION ALL",
11000
+ true,
11001
+ true
11002
+ );
11003
+ let afterHooks;
11004
+ let afterCommitHooks;
11005
+ q22.q.handleResult = (a, t, r, s) => {
11006
+ if (hasAfterCallback || hasAfterCommitCallback) {
11007
+ const fieldName = r.fields[0].name;
11008
+ if (r.rows[0][fieldName]) {
11009
+ afterHooks = q2.q.afterCreate;
11010
+ afterCommitHooks = q2.q.afterCreateCommit;
11011
+ } else {
11012
+ afterHooks = q2.q.afterUpdate;
11013
+ afterCommitHooks = q2.q.afterUpdateCommit;
11014
+ }
11015
+ delete r.rows[0][fieldName];
11016
+ }
11017
+ result = handleResult(a, t, r, s);
11018
+ return a.q.hookSelect ? result.map((row) => ({ ...row })) : result;
11019
+ };
11020
+ q22.q.log = q2.q.log;
11021
+ q22.q.logger = q2.q.logger;
11022
+ q22.q.type = "upsert";
11023
+ q22.q.beforeCreate = q2.q.beforeCreate;
11024
+ if (hasAfterCallback) {
11025
+ ((_a = q22.q).afterCreate ?? (_a.afterCreate = [])).push(
11026
+ (data2, query2) => afterHooks && Promise.all([...afterHooks].map((fn) => fn(data2, query2)))
11027
+ );
11028
+ }
11029
+ if (hasAfterCommitCallback) {
11030
+ ((_b = q22.q).afterCreateCommit ?? (_b.afterCreateCommit = [])).push(
11031
+ (data2, query2) => afterCommitHooks && Promise.all([...afterCommitHooks].map((fn) => fn(data2, query2)))
11032
+ );
11033
+ }
11034
+ await q22;
10893
11035
  created = true;
10894
11036
  } else if (queryResult.rowCount > 1) {
10895
11037
  throw new MoreThanOneRowError(
@@ -10902,9 +11044,7 @@ function orCreate(query, data, updateData, mergeData) {
10902
11044
  }
10903
11045
  class QueryUpsertOrCreate {
10904
11046
  /**
10905
- * `upsert` tries to update one record, and it will perform create in case a record was not found.
10906
- *
10907
- * It will implicitly wrap queries in a transaction if it was not wrapped yet.
11047
+ * `upsert` tries to update a single record, and then it creates the record if it doesn't yet exist.
10908
11048
  *
10909
11049
  * `find` or `findBy` must precede `upsert` because it does not work with multiple updates.
10910
11050
  *
@@ -10915,7 +11055,7 @@ class QueryUpsertOrCreate {
10915
11055
  *
10916
11056
  * `data` and `update` objects are of the same type that's expected by `update` method, `create` object is of type of `create` method argument.
10917
11057
  *
10918
- * It is not returning a value by default, place `select` or `selectAll` before `upsert` to specify returning columns.
11058
+ * No values are returned by default, place `select` or `selectAll` before `upsert` to specify returning columns.
10919
11059
  *
10920
11060
  * ```ts
10921
11061
  * await User.selectAll()
@@ -10992,6 +11132,9 @@ class QueryUpsertOrCreate {
10992
11132
  * });
10993
11133
  * ```
10994
11134
  *
11135
+ * `upsert` works in the exact same way as [orCreate](#orCreate), but with `UPDATE` statement instead of `SELECT`.
11136
+ * it also performs a single query if the record exists, and two queries if there is no record yet.
11137
+ *
10995
11138
  * @param data - `update` property for the data to update, `create` property for the data to create
10996
11139
  */
10997
11140
  upsert(data) {
@@ -11006,22 +11149,16 @@ class QueryUpsertOrCreate {
11006
11149
  if (!orchidCore.isObjectEmpty(updateData)) {
11007
11150
  _queryUpdate(q, updateData);
11008
11151
  }
11009
- const c = orCreate(q, data.create, updateData, mergeData);
11010
- if (!c.q.select) {
11011
- c.q.returnType = "void";
11012
- }
11013
- return c;
11152
+ return orCreate(q, data.create, updateData, mergeData);
11014
11153
  }
11015
11154
  /**
11016
11155
  * `orCreate` creates a record only if it was not found by conditions.
11017
11156
  *
11018
- * It will implicitly wrap queries in a transaction if it was not wrapped yet.
11019
- *
11020
11157
  * `find` or `findBy` must precede `orCreate`.
11021
11158
  *
11022
11159
  * It is accepting the same argument as `create` commands.
11023
11160
  *
11024
- * By default, it is not returning columns, place `get`, `select`, or `selectAll` before `orCreate` to specify returning columns.
11161
+ * No result is returned by default, place `get`, `select`, or `selectAll` before `orCreate` to specify returning columns.
11025
11162
  *
11026
11163
  * ```ts
11027
11164
  * const user = await User.selectAll()
@@ -11032,7 +11169,7 @@ class QueryUpsertOrCreate {
11032
11169
  * });
11033
11170
  * ```
11034
11171
  *
11035
- * The data may be returned from a function, it won't be called if the record was found:
11172
+ * The data can be returned from a function, it won't be called if the record was found:
11036
11173
  *
11037
11174
  * ```ts
11038
11175
  * const user = await User.selectAll()
@@ -11043,6 +11180,35 @@ class QueryUpsertOrCreate {
11043
11180
  * }));
11044
11181
  * ```
11045
11182
  *
11183
+ * `orCreate` works by performing just a single query in the case if the record exists, and one additional query when the record does not exist.
11184
+ *
11185
+ * At first, it performs a "find" query, the query cost is exact same as if you didn't use `orCreate`.
11186
+ *
11187
+ * Then, if the record wasn't found, it performs a single query with CTE expressions to try finding it again, for the case it was already created just a moment before,
11188
+ * and then it creates the record if it's still not found. Using such CTE allows to skip using transactions, while still conforming to atomicity.
11189
+ *
11190
+ * ```sql
11191
+ * -- first query
11192
+ * SELECT * FROM "table" WHERE "key" = 'value'
11193
+ *
11194
+ * -- the record could have been created in between these two queries
11195
+ *
11196
+ * -- second query
11197
+ * WITH find_row AS (
11198
+ * SELECT * FROM "table" WHERE "key" = 'value'
11199
+ * )
11200
+ * WITH insert_row AS (
11201
+ * INSERT INTO "table" ("key")
11202
+ * SELECT 'value'
11203
+ * -- skip the insert if the row already exists
11204
+ * WHERE NOT EXISTS (SELECT 1 FROM find_row)
11205
+ * RETURNING *
11206
+ * )
11207
+ * SELECT * FROM find_row
11208
+ * UNION ALL
11209
+ * SELECT * FROM insert_row
11210
+ * ```
11211
+ *
11046
11212
  * @param data - the same data as for `create`, it may be returned from a callback
11047
11213
  */
11048
11214
  orCreate(data) {
@@ -11281,16 +11447,17 @@ class ColumnRefExpression extends orchidCore.Expression {
11281
11447
  }
11282
11448
  }
11283
11449
  class RefExpression extends orchidCore.Expression {
11284
- constructor(value, q, ref) {
11450
+ constructor(value, query, ref) {
11285
11451
  super();
11286
- this.q = q;
11287
11452
  this.ref = ref;
11288
11453
  this.result = { value };
11289
- q.expr = this;
11454
+ (this.q = query.q).expr = this;
11455
+ this.table = query.table;
11290
11456
  Object.assign(this, value.operators);
11291
11457
  }
11292
- makeSQL(ctx, quotedAs) {
11293
- return columnToSql(ctx, this.q, this.q.shape, this.ref, quotedAs);
11458
+ makeSQL(ctx) {
11459
+ const as = this.q.as || this.table;
11460
+ return columnToSql(ctx, this.q, this.q.shape, this.ref, as && `"${as}"`);
11294
11461
  }
11295
11462
  }
11296
11463
  class OrExpression extends orchidCore.Expression {
@@ -11386,7 +11553,7 @@ class ExpressionMethods {
11386
11553
  } else {
11387
11554
  column = shape[arg];
11388
11555
  }
11389
- return new RefExpression(column, q.q, arg);
11556
+ return new RefExpression(column, q, arg);
11390
11557
  }
11391
11558
  val(value) {
11392
11559
  return new orchidCore.ValExpression(value);
@@ -12101,6 +12268,9 @@ class QueryMethods {
12101
12268
  narrowType() {
12102
12269
  return () => this;
12103
12270
  }
12271
+ if(condition, fn) {
12272
+ return condition ? fn(this) : this;
12273
+ }
12104
12274
  queryRelated(relName, params) {
12105
12275
  return this.relations[relName].relationConfig.queryRelated(
12106
12276
  params