graphile-build-pg 4.12.0 → 4.12.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.
Files changed (89) hide show
  1. package/README.md +10 -3
  2. package/node8plus/GraphQLJSON.js.map +1 -1
  3. package/node8plus/QueryBuilder.js +3 -4
  4. package/node8plus/QueryBuilder.js.map +1 -1
  5. package/node8plus/index.d.ts +2 -6
  6. package/node8plus/index.js +84 -83
  7. package/node8plus/index.js.map +1 -1
  8. package/node8plus/inflections.js +1 -1
  9. package/node8plus/inflections.js.flow +1 -3
  10. package/node8plus/inflections.js.map +1 -1
  11. package/node8plus/omit.js +2 -2
  12. package/node8plus/omit.js.flow +1 -1
  13. package/node8plus/omit.js.map +1 -1
  14. package/node8plus/plugins/PgAllRows.js.flow +2 -3
  15. package/node8plus/plugins/PgAllRows.js.map +1 -1
  16. package/node8plus/plugins/PgBackwardRelationPlugin.js +2 -2
  17. package/node8plus/plugins/PgBackwardRelationPlugin.js.flow +22 -18
  18. package/node8plus/plugins/PgBackwardRelationPlugin.js.map +1 -1
  19. package/node8plus/plugins/PgBasicsPlugin.js +3 -3
  20. package/node8plus/plugins/PgBasicsPlugin.js.flow +120 -120
  21. package/node8plus/plugins/PgBasicsPlugin.js.map +1 -1
  22. package/node8plus/plugins/PgColumnsPlugin.js +1 -1
  23. package/node8plus/plugins/PgColumnsPlugin.js.flow +5 -2
  24. package/node8plus/plugins/PgColumnsPlugin.js.map +1 -1
  25. package/node8plus/plugins/PgComputedColumnsPlugin.js +4 -4
  26. package/node8plus/plugins/PgComputedColumnsPlugin.js.flow +1 -1
  27. package/node8plus/plugins/PgComputedColumnsPlugin.js.map +1 -1
  28. package/node8plus/plugins/PgConnectionArgCondition.js.flow +4 -4
  29. package/node8plus/plugins/PgConnectionArgCondition.js.map +1 -1
  30. package/node8plus/plugins/PgConnectionArgOrderBy.js +1 -1
  31. package/node8plus/plugins/PgConnectionArgOrderBy.js.flow +1 -1
  32. package/node8plus/plugins/PgConnectionArgOrderBy.js.map +1 -1
  33. package/node8plus/plugins/PgConnectionArgOrderByDefaultValue.js +2 -1
  34. package/node8plus/plugins/PgConnectionArgOrderByDefaultValue.js.flow +2 -1
  35. package/node8plus/plugins/PgConnectionArgOrderByDefaultValue.js.map +1 -1
  36. package/node8plus/plugins/PgForwardRelationPlugin.js.flow +9 -8
  37. package/node8plus/plugins/PgForwardRelationPlugin.js.map +1 -1
  38. package/node8plus/plugins/PgIntrospectionPlugin.js +5 -5
  39. package/node8plus/plugins/PgIntrospectionPlugin.js.flow +13 -12
  40. package/node8plus/plugins/PgIntrospectionPlugin.js.map +1 -1
  41. package/node8plus/plugins/PgJWTPlugin.js.flow +2 -3
  42. package/node8plus/plugins/PgJWTPlugin.js.map +1 -1
  43. package/node8plus/plugins/PgMutationCreatePlugin.js.flow +2 -3
  44. package/node8plus/plugins/PgMutationCreatePlugin.js.map +1 -1
  45. package/node8plus/plugins/PgMutationPayloadEdgePlugin.js +2 -1
  46. package/node8plus/plugins/PgMutationPayloadEdgePlugin.js.flow +2 -1
  47. package/node8plus/plugins/PgMutationPayloadEdgePlugin.js.map +1 -1
  48. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js +4 -4
  49. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.flow +14 -19
  50. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.map +1 -1
  51. package/node8plus/plugins/PgOrderByPrimaryKeyPlugin.js +4 -3
  52. package/node8plus/plugins/PgOrderByPrimaryKeyPlugin.js.flow +3 -3
  53. package/node8plus/plugins/PgOrderByPrimaryKeyPlugin.js.map +1 -1
  54. package/node8plus/plugins/PgOrderComputedColumnsPlugin.js.flow +3 -5
  55. package/node8plus/plugins/PgOrderComputedColumnsPlugin.js.map +1 -1
  56. package/node8plus/plugins/PgQueryProceduresPlugin.js +2 -2
  57. package/node8plus/plugins/PgQueryProceduresPlugin.js.map +1 -1
  58. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.flow +4 -6
  59. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.map +1 -1
  60. package/node8plus/plugins/PgRecordReturnTypesPlugin.js +3 -3
  61. package/node8plus/plugins/PgRecordReturnTypesPlugin.js.flow +5 -5
  62. package/node8plus/plugins/PgRecordReturnTypesPlugin.js.map +1 -1
  63. package/node8plus/plugins/PgRowByUniqueConstraint.js.flow +2 -3
  64. package/node8plus/plugins/PgRowByUniqueConstraint.js.map +1 -1
  65. package/node8plus/plugins/PgRowNode.js.flow +9 -11
  66. package/node8plus/plugins/PgRowNode.js.map +1 -1
  67. package/node8plus/plugins/PgTablesPlugin.js +13 -13
  68. package/node8plus/plugins/PgTablesPlugin.js.flow +8 -13
  69. package/node8plus/plugins/PgTablesPlugin.js.map +1 -1
  70. package/node8plus/plugins/PgTypesPlugin.js +7 -7
  71. package/node8plus/plugins/PgTypesPlugin.js.flow +28 -34
  72. package/node8plus/plugins/PgTypesPlugin.js.map +1 -1
  73. package/node8plus/plugins/debugSql.js +1 -1
  74. package/node8plus/plugins/introspectionQuery.js.map +1 -1
  75. package/node8plus/plugins/makeProcField.js +5 -5
  76. package/node8plus/plugins/makeProcField.js.flow +2 -6
  77. package/node8plus/plugins/makeProcField.js.map +1 -1
  78. package/node8plus/plugins/pgField.js.flow +2 -4
  79. package/node8plus/plugins/pgField.js.map +1 -1
  80. package/node8plus/plugins/viaTemporaryTable.js +2 -2
  81. package/node8plus/plugins/viaTemporaryTable.js.map +1 -1
  82. package/node8plus/queryFromResolveDataFactory.js +3 -4
  83. package/node8plus/queryFromResolveDataFactory.js.flow +474 -464
  84. package/node8plus/queryFromResolveDataFactory.js.map +1 -1
  85. package/node8plus/utils.js +1 -1
  86. package/node8plus/utils.js.flow +1 -1
  87. package/node8plus/utils.js.map +1 -1
  88. package/node8plus/withPgClient.js +1 -1
  89. package/package.json +4 -4
@@ -27,363 +27,370 @@ function assertSafeName(name: mixed) {
27
27
  }
28
28
  }
29
29
 
30
- export default (queryBuilderOptions: QueryBuilderOptions = {}) => (
31
- from: SQL,
32
- fromAlias: ?SQL,
33
- resolveData: DataForType,
34
- inOptions: {
35
- withPagination?: boolean,
36
- withPaginationAsFields?: boolean,
37
- asJson?: boolean,
38
- asJsonAggregate?: boolean,
39
- addNullCase?: boolean,
40
- addNotDistinctFromNullCase?: boolean,
41
- onlyJsonField?: boolean,
42
- useAsterisk?: boolean,
43
- withCursor?: boolean,
44
- },
45
- // TODO:v5: context is not optional
46
- withBuilder?: ((builder: QueryBuilder) => void) | null | void,
47
- context?: GraphQLContext = {},
48
- rootValue?: any // eslint-disable-line flowtype/no-weak-types
49
- ) => {
50
- const {
51
- pgQuery,
52
- pgAggregateQuery, // Shorthand for using pgNamedQueryContainer/pgNamedQuery combo
53
- pgNamedQueryContainer = [],
54
- pgNamedQuery = [],
55
- pgCursorPrefix: reallyRawCursorPrefix,
56
- pgDontUseAsterisk,
57
- calculateHasNextPage,
58
- calculateHasPreviousPage,
59
- usesCursor: explicitlyUsesCursor,
60
- } = resolveData;
61
- // Push a query container for aggregates
62
- if ((pgAggregateQuery && pgAggregateQuery.length) || pgNamedQuery.length) {
63
- pgNamedQueryContainer.push({
64
- name: "aggregates",
65
- query: ({ queryBuilder, options, innerQueryBuilder }) => sql.fragment`\
30
+ export default (queryBuilderOptions: QueryBuilderOptions = {}) =>
31
+ (
32
+ from: SQL,
33
+ fromAlias: ?SQL,
34
+ resolveData: DataForType,
35
+ inOptions: {
36
+ withPagination?: boolean,
37
+ withPaginationAsFields?: boolean,
38
+ asJson?: boolean,
39
+ asJsonAggregate?: boolean,
40
+ addNullCase?: boolean,
41
+ addNotDistinctFromNullCase?: boolean,
42
+ onlyJsonField?: boolean,
43
+ useAsterisk?: boolean,
44
+ withCursor?: boolean,
45
+ },
46
+ // TODO:v5: context is not optional
47
+ withBuilder?: ((builder: QueryBuilder) => void) | null | void,
48
+ context?: GraphQLContext = {},
49
+ rootValue?: any // eslint-disable-line flowtype/no-weak-types
50
+ ) => {
51
+ const {
52
+ pgQuery,
53
+ pgAggregateQuery, // Shorthand for using pgNamedQueryContainer/pgNamedQuery combo
54
+ pgNamedQueryContainer = [],
55
+ pgNamedQuery = [],
56
+ pgCursorPrefix: reallyRawCursorPrefix,
57
+ pgDontUseAsterisk,
58
+ calculateHasNextPage,
59
+ calculateHasPreviousPage,
60
+ usesCursor: explicitlyUsesCursor,
61
+ } = resolveData;
62
+ // Push a query container for aggregates
63
+ if ((pgAggregateQuery && pgAggregateQuery.length) || pgNamedQuery.length) {
64
+ pgNamedQueryContainer.push({
65
+ name: "aggregates",
66
+ query: ({ queryBuilder, options, innerQueryBuilder }) => sql.fragment`\
66
67
  (
67
68
  select ${innerQueryBuilder.build({ onlyJsonField: true })}
68
69
  from ${queryBuilder.getTableExpression()} as ${queryBuilder.getTableAlias()}
69
70
  where ${queryBuilder.buildWhereClause(false, false, options)}
70
71
  )`,
71
- });
72
- }
73
- // Convert pgAggregateQuery to pgNamedQueryContainer/pgNamedQuery combo
74
- if (pgAggregateQuery && pgAggregateQuery.length) {
75
- // And a query for each previous query
76
- pgAggregateQuery.forEach(query => {
77
- pgNamedQuery.push({ name: "aggregates", query });
78
- });
79
- }
72
+ });
73
+ }
74
+ // Convert pgAggregateQuery to pgNamedQueryContainer/pgNamedQuery combo
75
+ if (pgAggregateQuery && pgAggregateQuery.length) {
76
+ // And a query for each previous query
77
+ pgAggregateQuery.forEach(query => {
78
+ pgNamedQuery.push({ name: "aggregates", query });
79
+ });
80
+ }
80
81
 
81
- const preventAsterisk = pgDontUseAsterisk
82
- ? pgDontUseAsterisk.length > 0
83
- : false;
84
- const options = {
85
- ...inOptions,
86
- // Allow pgDontUseAsterisk to override useAsterisk
87
- useAsterisk: inOptions.useAsterisk && !preventAsterisk,
88
- };
82
+ const preventAsterisk = pgDontUseAsterisk
83
+ ? pgDontUseAsterisk.length > 0
84
+ : false;
85
+ const options = {
86
+ ...inOptions,
87
+ // Allow pgDontUseAsterisk to override useAsterisk
88
+ useAsterisk: inOptions.useAsterisk && !preventAsterisk,
89
+ };
89
90
 
90
- const usesCursor: boolean =
91
- (explicitlyUsesCursor && explicitlyUsesCursor.length > 0) ||
92
- (calculateHasNextPage && calculateHasNextPage.length > 0) ||
93
- (calculateHasPreviousPage && calculateHasPreviousPage.length > 0) ||
94
- false;
95
- const rawCursorPrefix =
96
- reallyRawCursorPrefix && reallyRawCursorPrefix.filter(identity);
91
+ const usesCursor: boolean =
92
+ (explicitlyUsesCursor && explicitlyUsesCursor.length > 0) ||
93
+ (calculateHasNextPage && calculateHasNextPage.length > 0) ||
94
+ (calculateHasPreviousPage && calculateHasPreviousPage.length > 0) ||
95
+ false;
96
+ const rawCursorPrefix =
97
+ reallyRawCursorPrefix && reallyRawCursorPrefix.filter(identity);
97
98
 
98
- const queryBuilder = new QueryBuilder(
99
- queryBuilderOptions,
100
- context,
101
- rootValue
102
- );
103
- queryBuilder.from(from, fromAlias ? fromAlias : undefined);
99
+ const queryBuilder = new QueryBuilder(
100
+ queryBuilderOptions,
101
+ context,
102
+ rootValue
103
+ );
104
+ queryBuilder.from(from, fromAlias ? fromAlias : undefined);
104
105
 
105
- if (withBuilder) {
106
- withBuilder(queryBuilder);
107
- }
108
- if (pgQuery) {
109
- for (let i = 0, l = pgQuery.length; i < l; i++) {
110
- pgQuery[i](queryBuilder, resolveData);
106
+ if (withBuilder) {
107
+ withBuilder(queryBuilder);
108
+ }
109
+ if (pgQuery) {
110
+ for (let i = 0, l = pgQuery.length; i < l; i++) {
111
+ pgQuery[i](queryBuilder, resolveData);
112
+ }
111
113
  }
112
- }
113
114
 
114
- function generateNextPrevPageSql(
115
- sqlQueryAlias,
116
- canHaveCursorInWhere,
117
- queryHasBefore,
118
- queryHasFirst,
119
- offset = 0,
120
- invert = false
121
- ) {
122
- /*
123
- * Strap in, 'coz this function gets hairy!
124
- *
125
- * The point of this function is to return SQL which will resolve to a
126
- * boolean true/false depending on whether or not there is a (invert ?
127
- * "previous" : "next") page.
128
- *
129
- * Connections have before, after, first, last and offset.
130
- * - Users are forbidden from setting both first and last.
131
- * - Users are forbidden from setting both offset and last.
132
- *
133
- * Further there are two main modes of paginating, one works by adding a
134
- * where clause (this is preferred, but is not always possible, and is
135
- * indicated by `canHaveCursorInWhere === true`) and the other works using
136
- * standard LIMIT/OFFSET SQL pagination (and is indicated by
137
- * `canHaveCursorInWhere === false`).
138
- *
139
- * The following diagram shows a full collection of records, #, starting at
140
- * START and ending at END. The key after, before, offset, first and last
141
- * variables are shown. One thing not show is that it's possible to have
142
- * fewer records between before and after than requested by first or last.
143
- * Another detail not clearly show is that if there is no `after` then
144
- * `START` is used, similarly if there is no `before` then `END` is used.
145
- *
146
- * #################################################### < collection
147
- * ^ ^<-offset->^<-first->^ ^<-last->^ ^
148
- * | | | | | | |
149
- * | | +---------+ +--------+ |
150
- * | | | DATA1 DATA2 | |
151
- * | | | | |
152
- * | | | | |
153
- * | | +-------------------------+ |
154
- * | | DATA3 | |
155
- * | after before |
156
- * | |
157
- * START END
158
- *
159
- * We want one of the three DATA blocks:
160
- *
161
- * - If `first` is set, then we want DATA1.
162
- * - If `last` is set then we want DATA2.
163
- * - If neither is set then we want DATA3.
164
- *
165
- * (Remember: you cannot set both `first` and `last` at the same time.)
166
- *
167
- * When invert === false:
168
- *
169
- * Note that both DATA2 and DATA3 end at the same point, and we only care
170
- * if there's data *after* the relevant DATA block, so really we only
171
- * care if the query specified `first` (`queryHasFirst`) which makes
172
- * things complex (ending at the end of DATA1), otherwise we can use
173
- * `before` as the bound (end of DATA2/DATA3).
174
- *
175
- * When invert === true:
176
- *
177
- * Similarly, DATA1 and DATA3 start at the same point, and we're going
178
- * backwards so we only care if there's data *before* the DATA block, so
179
- * really we just need to know if the query set `last` or not, but since
180
- * this is inverted we call it `queryHasFirst`.
181
- *
182
- * When `invert` is false we're calculating `hasNextPage`, when true we're
183
- * calculating `hasPreviousPage`.
184
- *
185
- * Because of the near-symmetry of requesting hasPreviousPage vs
186
- * hasNextPage we always pretend we're determining `hasNextPage`, and we
187
- * just invert everything.
188
- */
115
+ function generateNextPrevPageSql(
116
+ sqlQueryAlias,
117
+ canHaveCursorInWhere,
118
+ queryHasBefore,
119
+ queryHasFirst,
120
+ offset = 0,
121
+ invert = false
122
+ ) {
123
+ /*
124
+ * Strap in, 'coz this function gets hairy!
125
+ *
126
+ * The point of this function is to return SQL which will resolve to a
127
+ * boolean true/false depending on whether or not there is a (invert ?
128
+ * "previous" : "next") page.
129
+ *
130
+ * Connections have before, after, first, last and offset.
131
+ * - Users are forbidden from setting both first and last.
132
+ * - Users are forbidden from setting both offset and last.
133
+ *
134
+ * Further there are two main modes of paginating, one works by adding a
135
+ * where clause (this is preferred, but is not always possible, and is
136
+ * indicated by `canHaveCursorInWhere === true`) and the other works using
137
+ * standard LIMIT/OFFSET SQL pagination (and is indicated by
138
+ * `canHaveCursorInWhere === false`).
139
+ *
140
+ * The following diagram shows a full collection of records, #, starting at
141
+ * START and ending at END. The key after, before, offset, first and last
142
+ * variables are shown. One thing not show is that it's possible to have
143
+ * fewer records between before and after than requested by first or last.
144
+ * Another detail not clearly show is that if there is no `after` then
145
+ * `START` is used, similarly if there is no `before` then `END` is used.
146
+ *
147
+ * #################################################### < collection
148
+ * ^ ^<-offset->^<-first->^ ^<-last->^ ^
149
+ * | | | | | | |
150
+ * | | +---------+ +--------+ |
151
+ * | | | DATA1 DATA2 | |
152
+ * | | | | |
153
+ * | | | | |
154
+ * | | +-------------------------+ |
155
+ * | | DATA3 | |
156
+ * | after before |
157
+ * | |
158
+ * START END
159
+ *
160
+ * We want one of the three DATA blocks:
161
+ *
162
+ * - If `first` is set, then we want DATA1.
163
+ * - If `last` is set then we want DATA2.
164
+ * - If neither is set then we want DATA3.
165
+ *
166
+ * (Remember: you cannot set both `first` and `last` at the same time.)
167
+ *
168
+ * When invert === false:
169
+ *
170
+ * Note that both DATA2 and DATA3 end at the same point, and we only care
171
+ * if there's data *after* the relevant DATA block, so really we only
172
+ * care if the query specified `first` (`queryHasFirst`) which makes
173
+ * things complex (ending at the end of DATA1), otherwise we can use
174
+ * `before` as the bound (end of DATA2/DATA3).
175
+ *
176
+ * When invert === true:
177
+ *
178
+ * Similarly, DATA1 and DATA3 start at the same point, and we're going
179
+ * backwards so we only care if there's data *before* the DATA block, so
180
+ * really we just need to know if the query set `last` or not, but since
181
+ * this is inverted we call it `queryHasFirst`.
182
+ *
183
+ * When `invert` is false we're calculating `hasNextPage`, when true we're
184
+ * calculating `hasPreviousPage`.
185
+ *
186
+ * Because of the near-symmetry of requesting hasPreviousPage vs
187
+ * hasNextPage we always pretend we're determining `hasNextPage`, and we
188
+ * just invert everything.
189
+ */
189
190
 
190
- const sqlCommonUnbounded = sql.fragment`\
191
+ const sqlCommonUnbounded = sql.fragment`\
191
192
  select 1
192
193
  from ${queryBuilder.getTableExpression()} as ${queryBuilder.getTableAlias()}`;
193
- /*
194
- * This variable is a fragment to go into an `EXISTS(...)` call (after some tweaks).
195
- *
196
- * The buildWhereClause takes three arguments:
197
- *
198
- * - includeLowerBound (we want this for hasNextPage but not hasPreviousPage)
199
- * - includeUpperBound (we want this for hasPreviousPage but not hasNextPage)
200
- * - options (specifically `{addNullCase, addNotDistinctFromNullCase}`) -
201
- * we just pass this through.
202
- *
203
- * So in hasNextPage mode (invert === false), this common SQL ends up
204
- * representing the collection from `(after || START)` onwards with no
205
- * upper bound. In hasPreviousPage mode (invert === true), it represents
206
- * everything from `(before || END)` backwards, with no lower bound.
207
- */
208
- const sqlCommon = sql.fragment`\
194
+ /*
195
+ * This variable is a fragment to go into an `EXISTS(...)` call (after some tweaks).
196
+ *
197
+ * The buildWhereClause takes three arguments:
198
+ *
199
+ * - includeLowerBound (we want this for hasNextPage but not hasPreviousPage)
200
+ * - includeUpperBound (we want this for hasPreviousPage but not hasNextPage)
201
+ * - options (specifically `{addNullCase, addNotDistinctFromNullCase}`) -
202
+ * we just pass this through.
203
+ *
204
+ * So in hasNextPage mode (invert === false), this common SQL ends up
205
+ * representing the collection from `(after || START)` onwards with no
206
+ * upper bound. In hasPreviousPage mode (invert === true), it represents
207
+ * everything from `(before || END)` backwards, with no lower bound.
208
+ */
209
+ const sqlCommon = sql.fragment`\
209
210
  ${sqlCommonUnbounded}
210
211
  where ${queryBuilder.buildWhereClause(!invert, invert, options)}`;
211
212
 
212
- /*
213
- * Since the offset makes the diagram asymmetric, if offset === 0
214
- * then the diagram is symmetric and things are simplified a little.
215
- */
216
- const isForwardOrSymmetric = !invert || offset === 0;
213
+ /*
214
+ * Since the offset makes the diagram asymmetric, if offset === 0
215
+ * then the diagram is symmetric and things are simplified a little.
216
+ */
217
+ const isForwardOrSymmetric = !invert || offset === 0;
217
218
 
218
- if (!isForwardOrSymmetric) {
219
- assert(invert);
220
- assert(offset > 0);
221
- // We're looking for a previous page, and there's an offset, so lets just
222
- // assume there's a previous page where offset is smaller.
223
- return sql.literal(true);
224
- } else if (canHaveCursorInWhere) {
225
- assert(isForwardOrSymmetric);
226
- if (!queryHasBefore && !queryHasFirst) {
219
+ if (!isForwardOrSymmetric) {
220
+ assert(invert);
221
+ assert(offset > 0);
222
+ // We're looking for a previous page, and there's an offset, so lets just
223
+ // assume there's a previous page where offset is smaller.
224
+ return sql.literal(true);
225
+ } else if (canHaveCursorInWhere) {
227
226
  assert(isForwardOrSymmetric);
228
- // There can be no next page since there's no upper bound
229
- return sql.literal(false);
230
- } else if (queryHasBefore && !queryHasFirst) {
231
- /*
232
- * We invert the upper buildWhereBoundClause to only represent the data
233
- * after `before`, then check if there's at least one record in that set.
234
- *
235
- * This only works if the `before` cursor can be represented in the
236
- * SQL WHERE clause, otherwise we're doing limit/offset pagination
237
- * which requires different logic. It also only works if there's no
238
- * `first` clause, otherwise there could be a next page before the
239
- * `before` clause.
240
- */
241
- return sql.fragment`\
227
+ if (!queryHasBefore && !queryHasFirst) {
228
+ assert(isForwardOrSymmetric);
229
+ // There can be no next page since there's no upper bound
230
+ return sql.literal(false);
231
+ } else if (queryHasBefore && !queryHasFirst) {
232
+ /*
233
+ * We invert the upper buildWhereBoundClause to only represent the data
234
+ * after `before`, then check if there's at least one record in that set.
235
+ *
236
+ * This only works if the `before` cursor can be represented in the
237
+ * SQL WHERE clause, otherwise we're doing limit/offset pagination
238
+ * which requires different logic. It also only works if there's no
239
+ * `first` clause, otherwise there could be a next page before the
240
+ * `before` clause.
241
+ */
242
+ return sql.fragment`\
242
243
  exists(
243
244
  ${sqlCommonUnbounded}
244
245
  where ${queryBuilder.buildWhereClause(false, false, options)}
245
246
  and not (${queryBuilder.buildWhereBoundClause(invert)})
246
247
  )`;
247
- } else {
248
- assert(queryHasFirst);
249
- // queryHasBefore could be true or false.
250
- /*
251
- * There's a few ways that we could determine if there's a next page.
252
- *
253
- * If !queryHasBefore, we could COUNT(*) the number of rows in
254
- * `sqlCommon` and see if it's larger than `first`:
255
- * `(select count(*) > ${first} from (${sqlCommon}) __random_table_alias__)`
256
- *
257
- * If !queryHasBefore, we could build a subquery table of offsetData
258
- * from sqlCommon and see if it contains any rows:
259
- * `EXISTS(select 1 from (${sqlCommon} OFFSET ${first}) __random_table_alias__)`.
260
- *
261
- * We could see if there's at least one row in sqlCommon that's not
262
- * already in our chosen result set.
263
- *
264
- * We've chosen the latter approach here because it doesn't place a limit
265
- * on queryHasBefore.
266
- */
267
- // Drop the `first` limit, see if there are any records that aren't
268
- // already in the list we've fetched.
269
- return sql.fragment`\
248
+ } else {
249
+ assert(queryHasFirst);
250
+ // queryHasBefore could be true or false.
251
+ /*
252
+ * There's a few ways that we could determine if there's a next page.
253
+ *
254
+ * If !queryHasBefore, we could COUNT(*) the number of rows in
255
+ * `sqlCommon` and see if it's larger than `first`:
256
+ * `(select count(*) > ${first} from (${sqlCommon}) __random_table_alias__)`
257
+ *
258
+ * If !queryHasBefore, we could build a subquery table of offsetData
259
+ * from sqlCommon and see if it contains any rows:
260
+ * `EXISTS(select 1 from (${sqlCommon} OFFSET ${first}) __random_table_alias__)`.
261
+ *
262
+ * We could see if there's at least one row in sqlCommon that's not
263
+ * already in our chosen result set.
264
+ *
265
+ * We've chosen the latter approach here because it doesn't place a limit
266
+ * on queryHasBefore.
267
+ */
268
+ // Drop the `first` limit, see if there are any records that aren't
269
+ // already in the list we've fetched.
270
+ return sql.fragment`\
270
271
  exists(
271
272
  ${sqlCommon}
272
273
  and (${queryBuilder.getSelectCursor()})::text not in (select __cursor::text from ${sqlQueryAlias})
273
274
  ${offset === 0 ? sql.blank : sql.fragment`offset ${sql.value(offset)}`}
274
275
  )`;
275
- }
276
- } else {
277
- assert(!invert || offset === 0); // isForwardOrSymmetric
278
- assert(!canHaveCursorInWhere);
279
- // We're dealing with LIMIT/OFFSET pagination here, which means `natural`
280
- // cursors, so the `queryBuilder` factors the before/after, first/last
281
- // into the limit / offset.
282
- const { limit } = queryBuilder.getFinalLimitAndOffset();
283
-
284
- if (limit == null) {
285
- // If paginating backwards, then offset > 0 has already been dealt
286
- // with. Unbounded, so there's no next page.
287
- return sql.fragment`false`;
288
- } else if (invert) {
289
- assert(offset === 0);
290
- // Paginating backwards and there's no offset (which factors in before/after), so there's no previous page.
291
- return sql.fragment`false`;
276
+ }
292
277
  } else {
293
- assert(!invert);
294
- /*
295
- * We're paginating forwards; either there's a before, there's a first,
296
- * or both.
297
- *
298
- * We want to see if there's more than limit+offset records in sqlCommon.
299
- */
300
- return sql.fragment`\
278
+ assert(!invert || offset === 0); // isForwardOrSymmetric
279
+ assert(!canHaveCursorInWhere);
280
+ // We're dealing with LIMIT/OFFSET pagination here, which means `natural`
281
+ // cursors, so the `queryBuilder` factors the before/after, first/last
282
+ // into the limit / offset.
283
+ const { limit } = queryBuilder.getFinalLimitAndOffset();
284
+
285
+ if (limit == null) {
286
+ // If paginating backwards, then offset > 0 has already been dealt
287
+ // with. Unbounded, so there's no next page.
288
+ return sql.fragment`false`;
289
+ } else if (invert) {
290
+ assert(offset === 0);
291
+ // Paginating backwards and there's no offset (which factors in before/after), so there's no previous page.
292
+ return sql.fragment`false`;
293
+ } else {
294
+ assert(!invert);
295
+ /*
296
+ * We're paginating forwards; either there's a before, there's a first,
297
+ * or both.
298
+ *
299
+ * We want to see if there's more than limit+offset records in sqlCommon.
300
+ */
301
+ return sql.fragment`\
301
302
  exists(
302
303
  ${sqlCommon}
303
304
  offset ${sql.literal(limit + offset)}
304
305
  )`;
305
- }
306
- }
307
- }
308
- const getPgCursorPrefix = () =>
309
- rawCursorPrefix && rawCursorPrefix.length > 0
310
- ? rawCursorPrefix
311
- : queryBuilder.data.cursorPrefix.map(val => sql.literal(val));
312
- if (
313
- options.withPagination ||
314
- options.withPaginationAsFields ||
315
- options.withCursor
316
- ) {
317
- // Sometimes we need a __cursor even if it's not a collection; e.g. to get the edge field on a mutation
318
- if (usesCursor) {
319
- queryBuilder.selectCursor((): SQL => {
320
- const orderBy = queryBuilder
321
- .getOrderByExpressionsAndDirections()
322
- .map(([expr]) => expr);
323
- if (queryBuilder.isOrderUnique() && orderBy.length > 0) {
324
- return sql.fragment`json_build_array(${sql.join(
325
- [
326
- ...getPgCursorPrefix(),
327
- sql.fragment`json_build_array(${sql.join(orderBy, ", ")})`,
328
- ],
329
- ", "
330
- )})`;
331
- } else {
332
- return sql.fragment`json_build_array(${sql.join(
333
- getPgCursorPrefix(),
334
- ", "
335
- )}, ${
336
- /*
337
- * NOTE[useAsterisk/row_number]: If we have useAsterisk then the
338
- * query with limit offset is in a subquery, so our row_number()
339
- * call doesn't know about it. Here we add the offset back in
340
- * again. See matching NOTE in QueryBuilder.js.
341
- */
342
- options.useAsterisk
343
- ? sql.fragment`${sql.literal(
344
- queryBuilder.getFinalOffset() || 0
345
- )} + `
346
- : sql.fragment``
347
- }(row_number() over (partition by 1)))`;
348
306
  }
349
- });
307
+ }
350
308
  }
351
- }
352
- if (options.withPagination || options.withPaginationAsFields) {
353
- queryBuilder.setCursorComparator((cursorValue, isAfter) => {
354
- function badCursor() {
355
- queryBuilder.whereBound(sql.fragment`false`, isAfter);
309
+ const getPgCursorPrefix = () =>
310
+ rawCursorPrefix && rawCursorPrefix.length > 0
311
+ ? rawCursorPrefix
312
+ : queryBuilder.data.cursorPrefix.map(val => sql.literal(val));
313
+ if (
314
+ options.withPagination ||
315
+ options.withPaginationAsFields ||
316
+ options.withCursor
317
+ ) {
318
+ // Sometimes we need a __cursor even if it's not a collection; e.g. to get the edge field on a mutation
319
+ if (usesCursor) {
320
+ queryBuilder.selectCursor((): SQL => {
321
+ const orderBy = queryBuilder
322
+ .getOrderByExpressionsAndDirections()
323
+ .map(([expr]) => expr);
324
+ if (queryBuilder.isOrderUnique() && orderBy.length > 0) {
325
+ return sql.fragment`json_build_array(${sql.join(
326
+ [
327
+ ...getPgCursorPrefix(),
328
+ sql.fragment`json_build_array(${sql.join(orderBy, ", ")})`,
329
+ ],
330
+ ", "
331
+ )})`;
332
+ } else {
333
+ return sql.fragment`json_build_array(${sql.join(
334
+ getPgCursorPrefix(),
335
+ ", "
336
+ )}, ${
337
+ /*
338
+ * NOTE[useAsterisk/row_number]: If we have useAsterisk then the
339
+ * query with limit offset is in a subquery, so our row_number()
340
+ * call doesn't know about it. Here we add the offset back in
341
+ * again. See matching NOTE in QueryBuilder.js.
342
+ */
343
+ options.useAsterisk
344
+ ? sql.fragment`${sql.literal(
345
+ queryBuilder.getFinalOffset() || 0
346
+ )} + `
347
+ : sql.fragment``
348
+ }(row_number() over (partition by 1)))`;
349
+ }
350
+ });
356
351
  }
357
- const orderByExpressionsAndDirections = queryBuilder.getOrderByExpressionsAndDirections();
358
- if (orderByExpressionsAndDirections.length > 0) {
359
- if (!queryBuilder.isOrderUnique()) {
360
- throw new Error(
361
- "The order supplied is not unique, so before/after cursors cannot be used. Please ensure the supplied order includes all the columns from the primary key or a unique constraint."
362
- );
363
- }
364
- const rawPrefixes = cursorValue.slice(0, cursorValue.length - 1);
365
- const rawCursors = cursorValue[cursorValue.length - 1];
366
- if (rawPrefixes.length !== getPgCursorPrefix().length) {
367
- badCursor();
368
- return;
369
- }
370
- if (!Array.isArray(rawCursors)) {
371
- badCursor();
372
- return;
352
+ }
353
+ if (options.withPagination || options.withPaginationAsFields) {
354
+ queryBuilder.setCursorComparator((cursorValue, isAfter) => {
355
+ function badCursor() {
356
+ queryBuilder.whereBound(sql.fragment`false`, isAfter);
373
357
  }
374
- let sqlFilter = sql.fragment`false`;
375
- const sqlCursors = rawCursors.map(val => sql.value(val));
376
- for (let i = orderByExpressionsAndDirections.length - 1; i >= 0; i--) {
377
- const [sqlExpression, ascending] = orderByExpressionsAndDirections[i];
378
- // If ascending and isAfter then >
379
- // If ascending and isBefore then <
380
- const comparison =
381
- Number(ascending) ^ Number(!isAfter)
382
- ? sql.fragment`>`
383
- : sql.fragment`<`;
358
+ const orderByExpressionsAndDirections =
359
+ queryBuilder.getOrderByExpressionsAndDirections();
360
+ if (orderByExpressionsAndDirections.length > 0) {
361
+ if (!queryBuilder.isOrderUnique()) {
362
+ throw new Error(
363
+ "The order supplied is not unique, so before/after cursors cannot be used. Please ensure the supplied order includes all the columns from the primary key or a unique constraint."
364
+ );
365
+ }
366
+ const rawPrefixes = cursorValue.slice(0, cursorValue.length - 1);
367
+ const rawCursors = cursorValue[cursorValue.length - 1];
368
+ if (rawPrefixes.length !== getPgCursorPrefix().length) {
369
+ badCursor();
370
+ return;
371
+ }
372
+ if (!Array.isArray(rawCursors)) {
373
+ badCursor();
374
+ return;
375
+ }
376
+ let sqlFilter = sql.fragment`false`;
377
+ const sqlCursors = rawCursors.map(val => sql.value(val));
378
+ for (
379
+ let i = orderByExpressionsAndDirections.length - 1;
380
+ i >= 0;
381
+ i--
382
+ ) {
383
+ const [sqlExpression, ascending] =
384
+ orderByExpressionsAndDirections[i];
385
+ // If ascending and isAfter then >
386
+ // If ascending and isBefore then <
387
+ const comparison =
388
+ Number(ascending) ^ Number(!isAfter)
389
+ ? sql.fragment`>`
390
+ : sql.fragment`<`;
384
391
 
385
- const sqlOldFilter = sqlFilter;
386
- sqlFilter = sql.fragment`\
392
+ const sqlOldFilter = sqlFilter;
393
+ sqlFilter = sql.fragment`\
387
394
  (\
388
395
  (${sqlExpression} ${comparison} ${sqlCursors[i] || sql.null})
389
396
  OR\
@@ -393,164 +400,167 @@ OR\
393
400
  ${sqlOldFilter}\
394
401
  )\
395
402
  )`;
396
- }
403
+ }
397
404
 
398
- // Check the cursor prefixes apply
399
- // TODO:v5: we should be able to do this in JS-land rather than SQL-land
400
- sqlFilter = sql.fragment`(((${sql.join(
401
- getPgCursorPrefix(),
402
- ", "
403
- )}) = (${sql.join(
404
- rawPrefixes.map(val => sql.value(val)),
405
- ", "
406
- )})) AND (${sqlFilter}))`;
407
- queryBuilder.whereBound(sqlFilter, isAfter);
408
- } else if (
409
- cursorValue[0] === "natural" &&
410
- isSafeInteger(cursorValue[1]) &&
411
- // $FlowFixMe: we know this is a number
412
- cursorValue[1] >= 0
413
- ) {
414
- // $FlowFixMe: we know this is a number
415
- const cursorValue1: number = cursorValue[1];
416
- if (isAfter) {
417
- queryBuilder.offset(() => cursorValue1);
405
+ // Check the cursor prefixes apply
406
+ // TODO:v5: we should be able to do this in JS-land rather than SQL-land
407
+ sqlFilter = sql.fragment`(((${sql.join(
408
+ getPgCursorPrefix(),
409
+ ", "
410
+ )}) = (${sql.join(
411
+ rawPrefixes.map(val => sql.value(val)),
412
+ ", "
413
+ )})) AND (${sqlFilter}))`;
414
+ queryBuilder.whereBound(sqlFilter, isAfter);
415
+ } else if (
416
+ cursorValue[0] === "natural" &&
417
+ isSafeInteger(cursorValue[1]) &&
418
+ // $FlowFixMe: we know this is a number
419
+ cursorValue[1] >= 0
420
+ ) {
421
+ // $FlowFixMe: we know this is a number
422
+ const cursorValue1: number = cursorValue[1];
423
+ if (isAfter) {
424
+ queryBuilder.offset(() => cursorValue1);
425
+ } else {
426
+ queryBuilder.limit(() => {
427
+ const offset = queryBuilder.getOffset();
428
+ return Math.max(0, cursorValue1 - offset - 1);
429
+ });
430
+ }
418
431
  } else {
419
- queryBuilder.limit(() => {
420
- const offset = queryBuilder.getOffset();
421
- return Math.max(0, cursorValue1 - offset - 1);
422
- });
432
+ throw new Error(
433
+ "Cannot use 'before'/'after' without unique 'orderBy'"
434
+ );
423
435
  }
424
- } else {
425
- throw new Error("Cannot use 'before'/'after' without unique 'orderBy'");
426
- }
427
- });
436
+ });
428
437
 
429
- const query = queryBuilder.build(options);
430
- const haveFields = queryBuilder.getSelectFieldsCount() > 0;
431
- const sqlQueryAlias = sql.identifier(Symbol());
432
- const sqlSummaryAlias = sql.identifier(Symbol());
433
- //
434
- // Tables should ALWAYS push their PK onto the order stack, if this isn't
435
- // present then we're either dealing with a view or a table without a PK.
436
- // Either way, we don't have anything to guarantee uniqueness so we need to
437
- // fall back to limit/offset.
438
- //
439
- // TODO: support unique keys in PgAllRows etc
440
- // TODO: add a warning for cursor-based pagination when using the fallback
441
- // TODO: if it is a view maybe add a warning encouraging pgViewUniqueKey
442
- const canHaveCursorInWhere =
443
- queryBuilder.getOrderByExpressionsAndDirections().length > 0 &&
444
- queryBuilder.isOrderUnique();
445
- const queryHasBefore =
446
- queryBuilder.compiledData.whereBound.upper.length > 0;
447
- const queryHasAfter = queryBuilder.compiledData.whereBound.lower.length > 0;
448
- const queryHasZeroLimit = queryBuilder.getFinalLimit() === 0;
449
- const queryHasFirst = isSafeInteger(queryBuilder.compiledData.first);
450
- const queryHasLast = isSafeInteger(queryBuilder.compiledData.last);
451
- const hasNextPage = queryHasZeroLimit
452
- ? sql.literal(false)
453
- : generateNextPrevPageSql(
454
- sqlQueryAlias,
455
- canHaveCursorInWhere,
456
- queryHasBefore,
457
- queryHasFirst,
458
- queryBuilder.getFinalOffset() || 0
459
- );
460
- const hasPreviousPage = queryHasZeroLimit
461
- ? sql.literal(false)
462
- : generateNextPrevPageSql(
463
- sqlQueryAlias,
464
- canHaveCursorInWhere,
465
- queryHasAfter,
466
- queryHasLast,
467
- queryBuilder.getFinalOffset() || 0,
468
- true
469
- );
438
+ const query = queryBuilder.build(options);
439
+ const haveFields = queryBuilder.getSelectFieldsCount() > 0;
440
+ const sqlQueryAlias = sql.identifier(Symbol());
441
+ const sqlSummaryAlias = sql.identifier(Symbol());
442
+ //
443
+ // Tables should ALWAYS push their PK onto the order stack, if this isn't
444
+ // present then we're either dealing with a view or a table without a PK.
445
+ // Either way, we don't have anything to guarantee uniqueness so we need to
446
+ // fall back to limit/offset.
447
+ //
448
+ // TODO: support unique keys in PgAllRows etc
449
+ // TODO: add a warning for cursor-based pagination when using the fallback
450
+ // TODO: if it is a view maybe add a warning encouraging pgViewUniqueKey
451
+ const canHaveCursorInWhere =
452
+ queryBuilder.getOrderByExpressionsAndDirections().length > 0 &&
453
+ queryBuilder.isOrderUnique();
454
+ const queryHasBefore =
455
+ queryBuilder.compiledData.whereBound.upper.length > 0;
456
+ const queryHasAfter =
457
+ queryBuilder.compiledData.whereBound.lower.length > 0;
458
+ const queryHasZeroLimit = queryBuilder.getFinalLimit() === 0;
459
+ const queryHasFirst = isSafeInteger(queryBuilder.compiledData.first);
460
+ const queryHasLast = isSafeInteger(queryBuilder.compiledData.last);
461
+ const hasNextPage = queryHasZeroLimit
462
+ ? sql.literal(false)
463
+ : generateNextPrevPageSql(
464
+ sqlQueryAlias,
465
+ canHaveCursorInWhere,
466
+ queryHasBefore,
467
+ queryHasFirst,
468
+ queryBuilder.getFinalOffset() || 0
469
+ );
470
+ const hasPreviousPage = queryHasZeroLimit
471
+ ? sql.literal(false)
472
+ : generateNextPrevPageSql(
473
+ sqlQueryAlias,
474
+ canHaveCursorInWhere,
475
+ queryHasAfter,
476
+ queryHasLast,
477
+ queryBuilder.getFinalOffset() || 0,
478
+ true
479
+ );
470
480
 
471
- const sqlWith = haveFields
472
- ? sql.fragment`with ${sqlQueryAlias} as (${query}), ${sqlSummaryAlias} as (select json_agg(to_json(${sqlQueryAlias})) as data from ${sqlQueryAlias})`
473
- : sql.fragment``;
474
- const sqlFrom = sql.fragment``;
475
- const fields: Array<[SQL, RawAlias]> = [];
476
- if (haveFields) {
477
- fields.push([
478
- sql.fragment`coalesce((select ${sqlSummaryAlias}.data from ${sqlSummaryAlias}), '[]'::json)`,
479
- "data",
480
- ]);
481
- if (calculateHasNextPage) {
482
- fields.push([hasNextPage, "hasNextPage"]);
483
- }
484
- if (calculateHasPreviousPage) {
485
- fields.push([hasPreviousPage, "hasPreviousPage"]);
486
- }
487
- }
488
- if (pgNamedQuery && pgNamedQuery.length) {
489
- const groups = {};
490
- pgNamedQuery.forEach(({ name, query }) => {
491
- assertSafeName(name);
492
- if (!groups[name]) {
493
- groups[name] = [];
481
+ const sqlWith = haveFields
482
+ ? sql.fragment`with ${sqlQueryAlias} as (${query}), ${sqlSummaryAlias} as (select json_agg(to_json(${sqlQueryAlias})) as data from ${sqlQueryAlias})`
483
+ : sql.fragment``;
484
+ const sqlFrom = sql.fragment``;
485
+ const fields: Array<[SQL, RawAlias]> = [];
486
+ if (haveFields) {
487
+ fields.push([
488
+ sql.fragment`coalesce((select ${sqlSummaryAlias}.data from ${sqlSummaryAlias}), '[]'::json)`,
489
+ "data",
490
+ ]);
491
+ if (calculateHasNextPage) {
492
+ fields.push([hasNextPage, "hasNextPage"]);
494
493
  }
495
- groups[name].push(query);
496
- });
497
- Object.keys(groups).forEach(groupName => {
498
- const queryCallbacks = groups[groupName];
494
+ if (calculateHasPreviousPage) {
495
+ fields.push([hasPreviousPage, "hasPreviousPage"]);
496
+ }
497
+ }
498
+ if (pgNamedQuery && pgNamedQuery.length) {
499
+ const groups = {};
500
+ pgNamedQuery.forEach(({ name, query }) => {
501
+ assertSafeName(name);
502
+ if (!groups[name]) {
503
+ groups[name] = [];
504
+ }
505
+ groups[name].push(query);
506
+ });
507
+ Object.keys(groups).forEach(groupName => {
508
+ const queryCallbacks = groups[groupName];
499
509
 
500
- // Get container
501
- const containers = pgNamedQueryContainer.filter(
502
- c => c.name === groupName
503
- );
504
- if (containers.length === 0) {
505
- throw new Error(
506
- `${queryCallbacks.length} pgNamedQuery entries with name: '${groupName}' existed, but there was no matching pgNamedQueryContainer.`
510
+ // Get container
511
+ const containers = pgNamedQueryContainer.filter(
512
+ c => c.name === groupName
507
513
  );
508
- }
509
- if (containers.length > 1) {
510
- throw new Error(
511
- `${containers.length} pgNamedQueryContainer entries with name: '${groupName}' existed, but there should be exactly one.`
514
+ if (containers.length === 0) {
515
+ throw new Error(
516
+ `${queryCallbacks.length} pgNamedQuery entries with name: '${groupName}' existed, but there was no matching pgNamedQueryContainer.`
517
+ );
518
+ }
519
+ if (containers.length > 1) {
520
+ throw new Error(
521
+ `${containers.length} pgNamedQueryContainer entries with name: '${groupName}' existed, but there should be exactly one.`
522
+ );
523
+ }
524
+ const container = containers[0];
525
+
526
+ const innerQueryBuilder = new QueryBuilder(
527
+ queryBuilderOptions,
528
+ context,
529
+ rootValue
530
+ );
531
+ innerQueryBuilder.from(
532
+ queryBuilder.getTableExpression(),
533
+ queryBuilder.getTableAlias()
512
534
  );
513
- }
514
- const container = containers[0];
515
535
 
516
- const innerQueryBuilder = new QueryBuilder(
517
- queryBuilderOptions,
518
- context,
519
- rootValue
520
- );
521
- innerQueryBuilder.from(
522
- queryBuilder.getTableExpression(),
523
- queryBuilder.getTableAlias()
524
- );
536
+ for (let i = 0, l = queryCallbacks.length; i < l; i++) {
537
+ queryCallbacks[i](innerQueryBuilder);
538
+ }
525
539
 
526
- for (let i = 0, l = queryCallbacks.length; i < l; i++) {
527
- queryCallbacks[i](innerQueryBuilder);
528
- }
540
+ // Generate the SQL statement (e.g. `select ${innerQueryBuilder.build({onlyJsonField: true})} from ${queryBuilder.getTableExpression()} as ...`)
541
+ const aggregatesSql = container.query({
542
+ queryBuilder,
543
+ innerQueryBuilder,
544
+ options,
545
+ });
529
546
 
530
- // Generate the SQL statement (e.g. `select ${innerQueryBuilder.build({onlyJsonField: true})} from ${queryBuilder.getTableExpression()} as ...`)
531
- const aggregatesSql = container.query({
532
- queryBuilder,
533
- innerQueryBuilder,
534
- options,
547
+ fields.push([aggregatesSql, groupName]);
535
548
  });
536
-
537
- fields.push([aggregatesSql, groupName]);
538
- });
539
- }
540
- if (options.withPaginationAsFields) {
541
- return sql.fragment`${sqlWith} select ${sql.join(
542
- fields.map(
543
- ([expr, alias]) => sql.fragment`${expr} as ${sql.identifier(alias)}`
544
- ),
545
- ", "
546
- )} ${sqlFrom}`;
549
+ }
550
+ if (options.withPaginationAsFields) {
551
+ return sql.fragment`${sqlWith} select ${sql.join(
552
+ fields.map(
553
+ ([expr, alias]) => sql.fragment`${expr} as ${sql.identifier(alias)}`
554
+ ),
555
+ ", "
556
+ )} ${sqlFrom}`;
557
+ } else {
558
+ return sql.fragment`${sqlWith} select ${queryBuilder.jsonbBuildObject(
559
+ fields
560
+ )} ${sqlFrom}`;
561
+ }
547
562
  } else {
548
- return sql.fragment`${sqlWith} select ${queryBuilder.jsonbBuildObject(
549
- fields
550
- )} ${sqlFrom}`;
563
+ const query = queryBuilder.build(options);
564
+ return query;
551
565
  }
552
- } else {
553
- const query = queryBuilder.build(options);
554
- return query;
555
- }
556
- };
566
+ };