graphile-build-pg 4.4.5 → 4.5.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 (41) hide show
  1. package/README.md +2 -1
  2. package/node8plus/QueryBuilder.d.ts +10 -2
  3. package/node8plus/QueryBuilder.js +80 -1
  4. package/node8plus/QueryBuilder.js.flow +75 -2
  5. package/node8plus/QueryBuilder.js.map +1 -1
  6. package/node8plus/index.d.ts +8 -0
  7. package/node8plus/index.js +7 -1
  8. package/node8plus/index.js.flow +6 -1
  9. package/node8plus/index.js.map +1 -1
  10. package/node8plus/plugins/PgBasicsPlugin.js +5 -3
  11. package/node8plus/plugins/PgBasicsPlugin.js.flow +2 -0
  12. package/node8plus/plugins/PgBasicsPlugin.js.map +1 -1
  13. package/node8plus/plugins/PgConnectionTotalCount.js +10 -4
  14. package/node8plus/plugins/PgConnectionTotalCount.js.flow +13 -9
  15. package/node8plus/plugins/PgConnectionTotalCount.js.map +1 -1
  16. package/node8plus/plugins/PgIntrospectionPlugin.d.ts +48 -11
  17. package/node8plus/plugins/PgIntrospectionPlugin.js +53 -23
  18. package/node8plus/plugins/PgIntrospectionPlugin.js.flow +81 -25
  19. package/node8plus/plugins/PgIntrospectionPlugin.js.map +1 -1
  20. package/node8plus/plugins/PgJWTPlugin.js +6 -1
  21. package/node8plus/plugins/PgJWTPlugin.js.flow +7 -1
  22. package/node8plus/plugins/PgJWTPlugin.js.map +1 -1
  23. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js +2 -1
  24. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.flow +3 -1
  25. package/node8plus/plugins/PgMutationUpdateDeletePlugin.js.map +1 -1
  26. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js +1 -0
  27. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.flow +1 -0
  28. package/node8plus/plugins/PgRecordFunctionConnectionPlugin.js.map +1 -1
  29. package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js +1 -0
  30. package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js.flow +1 -0
  31. package/node8plus/plugins/PgScalarFunctionConnectionPlugin.js.map +1 -1
  32. package/node8plus/plugins/PgTypesPlugin.js +9 -2
  33. package/node8plus/plugins/PgTypesPlugin.js.flow +19 -0
  34. package/node8plus/plugins/PgTypesPlugin.js.map +1 -1
  35. package/node8plus/plugins/introspectionQuery.js +1 -0
  36. package/node8plus/plugins/introspectionQuery.js.flow +1 -0
  37. package/node8plus/plugins/introspectionQuery.js.map +1 -1
  38. package/node8plus/queryFromResolveDataFactory.js +8 -1
  39. package/node8plus/queryFromResolveDataFactory.js.flow +13 -1
  40. package/node8plus/queryFromResolveDataFactory.js.map +1 -1
  41. package/package.json +7 -12
package/README.md CHANGED
@@ -24,7 +24,8 @@ And please give some love to our featured sponsors 🤩:
24
24
 
25
25
  <table><tr>
26
26
  <td align="center"><a href="http://chads.website/"><img src="https://www.graphile.org/images/sponsors/chadf.png" width="90" height="90" alt="Chad Furman" /><br />Chad Furman</a></td>
27
- <td align="center"><a href="https://timescale.com/"><img src="https://www.graphile.org/images/sponsors/timescale.svg" width="90" height="90" alt="Timescale" /><br />Timescale</a></td>
27
+ <td align="center"><a href="https://storyscript.io/?utm_source=postgraphile"><img src="https://www.graphile.org/images/sponsors/storyscript.png" width="90" height="90" alt="Storyscript" /><br />Storyscript</a></td>
28
+ <td align="center"><a href="http://p72.vc/"><img src="https://www.graphile.org/images/sponsors/p72.png" width="90" height="90" alt="Point72 Ventures" /><br />Point72 Ventures</a></td>
28
29
  </tr></table>
29
30
 
30
31
  <!-- SPONSORS_END -->
@@ -42,8 +42,8 @@ export default class QueryBuilder {
42
42
  public setOrderIsUnique(): void;
43
43
  public orderBy(
44
44
  exprGen: SQLGen,
45
- ascending: boolean,
46
- nullsFirst: boolean | null
45
+ ascending?: boolean,
46
+ nullsFirst?: boolean | null
47
47
  ): void;
48
48
  public limit(limitGen: NumberGen): void;
49
49
  public offset(offsetGen: NumberGen): void;
@@ -93,6 +93,14 @@ export default class QueryBuilder {
93
93
  addNotDistinctFromNullCase?: boolean;
94
94
  useAsterisk?: boolean;
95
95
  }): SQL;
96
+ public buildChild(): QueryBuilder;
97
+ public buildNamedChildSelecting(
98
+ name: RawAlias,
99
+ from: SQLGen,
100
+ selectExpression: SQLGen,
101
+ alias?: SQLAlias
102
+ ): QueryBuilder;
103
+ public getNamedChild(name: RawAlias): QueryBuilder | undefined;
96
104
 
97
105
  // ----------------------------------------
98
106
 
@@ -70,6 +70,7 @@ class QueryBuilder {
70
70
  // As a performance optimisation, we're going to list a number of lock
71
71
  // types so that V8 doesn't need to mutate the object too much
72
72
  cursorComparator: false,
73
+ fixedSelectExpression: false,
73
74
  select: false,
74
75
  selectCursor: false,
75
76
  from: false,
@@ -88,6 +89,7 @@ class QueryBuilder {
88
89
  this.data = {
89
90
  // TODO: refactor `cursorPrefix`, it shouldn't be here (or should at least have getters/setters)
90
91
  cursorPrefix: ["natural"],
92
+ fixedSelectExpression: null,
91
93
  select: [],
92
94
  selectCursor: null,
93
95
  from: null,
@@ -107,6 +109,7 @@ class QueryBuilder {
107
109
  // As a performance optimisation, we're going to list a number of lock
108
110
  // types so that V8 doesn't need to mutate the object too much
109
111
  cursorComparator: [],
112
+ fixedSelectExpression: [],
110
113
  select: [],
111
114
  selectCursor: [],
112
115
  from: [],
@@ -125,6 +128,7 @@ class QueryBuilder {
125
128
  };
126
129
  this.compiledData = {
127
130
  cursorPrefix: ["natural"],
131
+ fixedSelectExpression: null,
128
132
  select: [],
129
133
  selectCursor: null,
130
134
  from: null,
@@ -142,6 +146,7 @@ class QueryBuilder {
142
146
  last: null,
143
147
  cursorComparator: null
144
148
  };
149
+ this._children = new Map();
145
150
  this.beforeLock("select", () => {
146
151
  this.lock("selectCursor");
147
152
 
@@ -259,9 +264,28 @@ ${sql.join(Object.keys(allRequirements).map(key => sql.fragment`, ${sql.literal(
259
264
  this.compiledData.cursorComparator(cursorValue, isAfter);
260
265
  });
261
266
  }
267
+ /** this method is experimental */
268
+
269
+
270
+ fixedSelectExpression(exprGen) {
271
+ this.checkLock("fixedSelectExpression");
272
+ this.lock("select");
273
+ this.lock("selectCursor");
274
+
275
+ if (this.data.select.length > 0) {
276
+ throw new Error("Cannot use .fixedSelectExpression() with .select()");
277
+ }
278
+
279
+ if (this.data.selectCursor) {
280
+ throw new Error("Cannot use .fixedSelectExpression() with .selectCursor()");
281
+ }
282
+
283
+ this.data.fixedSelectExpression = exprGen;
284
+ }
262
285
 
263
286
  select(exprGen, alias) {
264
287
  this.checkLock("select");
288
+ this.lock("fixedSelectExpression");
265
289
 
266
290
  if (typeof alias === "string") {
267
291
  // To protect against vulnerabilities such as
@@ -293,6 +317,7 @@ ${sql.join(Object.keys(allRequirements).map(key => sql.fragment`, ${sql.literal(
293
317
 
294
318
  selectCursor(exprGen) {
295
319
  this.checkLock("selectCursor");
320
+ this.lock("fixedSelectExpression");
296
321
  this.data.selectCursor = exprGen;
297
322
  }
298
323
 
@@ -489,6 +514,11 @@ ${sql.join(Object.keys(allRequirements).map(key => sql.fragment`, ${sql.literal(
489
514
 
490
515
  buildSelectFields() {
491
516
  this.lockEverything();
517
+
518
+ if (this.compiledData.fixedSelectExpression) {
519
+ return this.compiledData.fixedSelectExpression;
520
+ }
521
+
492
522
  return sql.join(this.compiledData.select.map(([sqlFragment, alias]) => sql.fragment`to_json(${sqlFragment}) as ${sql.identifier(alias)}`), ", ");
493
523
  }
494
524
 
@@ -565,6 +595,14 @@ ${sql.join(Object.keys(allRequirements).map(key => sql.fragment`, ${sql.literal(
565
595
  }
566
596
 
567
597
  build(options = {}) {
598
+ this.lockEverything();
599
+
600
+ if (this.compiledData.fixedSelectExpression) {
601
+ if (Object.keys(options).length > 0) {
602
+ throw new Error("Do not pass options to QueryBuilder.build() when using `buildNamedChildSelecting`");
603
+ }
604
+ }
605
+
568
606
  const {
569
607
  asJson = false,
570
608
  asJsonAggregate = false,
@@ -573,7 +611,6 @@ ${sql.join(Object.keys(allRequirements).map(key => sql.fragment`, ${sql.literal(
573
611
  addNotDistinctFromNullCase = false,
574
612
  useAsterisk = false
575
613
  } = options;
576
- this.lockEverything();
577
614
 
578
615
  if (onlyJsonField) {
579
616
  return this.buildSelectJson({
@@ -609,9 +646,15 @@ with ${sql.identifier(flipAlias)} as (
609
646
  select *
610
647
  from ${sql.identifier(flipAlias)}
611
648
  order by (row_number() over (partition by 1)) desc`;
649
+ /* We don't need to factor useAsterisk into this row_number() usage */
612
650
  }
613
651
 
614
652
  if (useAsterisk) {
653
+ /*
654
+ * NOTE[useAsterisk/row_number]: since LIMIT/OFFSET is inside this
655
+ * subquery, row_number() outside of this subquery WON'T include the
656
+ * offset. We must add it back wherever row_number() is used.
657
+ */
615
658
  fragment = sql.fragment`select ${fields} from (${fragment}) ${this.getTableAlias()}`;
616
659
  }
617
660
 
@@ -656,6 +699,8 @@ order by (row_number() over (partition by 1)) desc`;
656
699
  // Handle properties separately
657
700
  this.compiledData[type].lower = callIfNecessaryArray(this.data[type].lower, context);
658
701
  this.compiledData[type].upper = callIfNecessaryArray(this.data[type].upper, context);
702
+ } else if (type === "fixedSelectExpression") {
703
+ this.compiledData[type] = callIfNecessary(this.data[type], context);
659
704
  } else if (type === "select") {
660
705
  /*
661
706
  * NOTICE: locking select can cause additional selects to be added, so the
@@ -743,9 +788,43 @@ order by (row_number() over (partition by 1)) desc`;
743
788
  this.lock("first");
744
789
  this.lock("last"); // We must execute select after orderBy otherwise we cannot generate a cursor
745
790
 
791
+ this.lock("fixedSelectExpression");
746
792
  this.lock("selectCursor");
747
793
  this.lock("select");
748
794
  }
795
+ /** this method is experimental */
796
+
797
+
798
+ buildChild() {
799
+ const options = {
800
+ supportsJSONB: this.supportsJSONB
801
+ };
802
+ const child = new QueryBuilder(options, this.context, this.rootValue);
803
+ child.parentQueryBuilder = this;
804
+ return child;
805
+ }
806
+ /** this method is experimental */
807
+
808
+
809
+ buildNamedChildSelecting(name, from, selectExpression, alias) {
810
+ if (this._children.has(name)) {
811
+ throw new Error(`QueryBuilder already has a child named ${name.toString()}`);
812
+ }
813
+
814
+ const child = this.buildChild();
815
+ child.from(from, alias);
816
+ child.fixedSelectExpression(selectExpression);
817
+
818
+ this._children.set(name, child);
819
+
820
+ return child;
821
+ }
822
+ /** this method is experimental */
823
+
824
+
825
+ getNamedChild(name) {
826
+ return this._children.get(name);
827
+ }
749
828
 
750
829
  }
751
830
 
@@ -80,6 +80,7 @@ class QueryBuilder {
80
80
  selectedIdentifiers: boolean;
81
81
  data: {
82
82
  cursorPrefix: Array<string>,
83
+ fixedSelectExpression: ?SQLGen,
83
84
  select: Array<[SQLGen, RawAlias]>,
84
85
  selectCursor: ?SQLGen,
85
86
  from: ?[SQLGen, SQLAlias],
@@ -106,6 +107,7 @@ class QueryBuilder {
106
107
  };
107
108
  compiledData: {
108
109
  cursorPrefix: Array<string>,
110
+ fixedSelectExpression: ?SQL,
109
111
  select: Array<[SQL, RawAlias]>,
110
112
  selectCursor: ?SQL,
111
113
  from: ?[SQL, SQLAlias],
@@ -126,6 +128,7 @@ class QueryBuilder {
126
128
  lockContext: {
127
129
  queryBuilder: QueryBuilder,
128
130
  };
131
+ _children: Map<RawAlias, QueryBuilder>;
129
132
 
130
133
  constructor(
131
134
  options: QueryBuilderOptions = {},
@@ -144,6 +147,7 @@ class QueryBuilder {
144
147
  // As a performance optimisation, we're going to list a number of lock
145
148
  // types so that V8 doesn't need to mutate the object too much
146
149
  cursorComparator: false,
150
+ fixedSelectExpression: false,
147
151
  select: false,
148
152
  selectCursor: false,
149
153
  from: false,
@@ -162,6 +166,7 @@ class QueryBuilder {
162
166
  this.data = {
163
167
  // TODO: refactor `cursorPrefix`, it shouldn't be here (or should at least have getters/setters)
164
168
  cursorPrefix: ["natural"],
169
+ fixedSelectExpression: null,
165
170
  select: [],
166
171
  selectCursor: null,
167
172
  from: null,
@@ -181,6 +186,7 @@ class QueryBuilder {
181
186
  // As a performance optimisation, we're going to list a number of lock
182
187
  // types so that V8 doesn't need to mutate the object too much
183
188
  cursorComparator: [],
189
+ fixedSelectExpression: [],
184
190
  select: [],
185
191
  selectCursor: [],
186
192
  from: [],
@@ -199,6 +205,7 @@ class QueryBuilder {
199
205
  };
200
206
  this.compiledData = {
201
207
  cursorPrefix: ["natural"],
208
+ fixedSelectExpression: null,
202
209
  select: [],
203
210
  selectCursor: null,
204
211
  from: null,
@@ -216,6 +223,7 @@ class QueryBuilder {
216
223
  last: null,
217
224
  cursorComparator: null,
218
225
  };
226
+ this._children = new Map();
219
227
  this.beforeLock("select", () => {
220
228
  this.lock("selectCursor");
221
229
  if (this.compiledData.selectCursor) {
@@ -363,8 +371,24 @@ ${sql.join(
363
371
  this.compiledData.cursorComparator(cursorValue, isAfter);
364
372
  });
365
373
  }
374
+ /** this method is experimental */
375
+ fixedSelectExpression(exprGen: SQLGen) {
376
+ this.checkLock("fixedSelectExpression");
377
+ this.lock("select");
378
+ this.lock("selectCursor");
379
+ if (this.data.select.length > 0) {
380
+ throw new Error("Cannot use .fixedSelectExpression() with .select()");
381
+ }
382
+ if (this.data.selectCursor) {
383
+ throw new Error(
384
+ "Cannot use .fixedSelectExpression() with .selectCursor()"
385
+ );
386
+ }
387
+ this.data.fixedSelectExpression = exprGen;
388
+ }
366
389
  select(exprGen: SQLGen, alias: RawAlias) {
367
390
  this.checkLock("select");
391
+ this.lock("fixedSelectExpression");
368
392
  if (typeof alias === "string") {
369
393
  // To protect against vulnerabilities such as
370
394
  //
@@ -403,6 +427,7 @@ ${sql.join(
403
427
  }
404
428
  selectCursor(exprGen: SQLGen) {
405
429
  this.checkLock("selectCursor");
430
+ this.lock("fixedSelectExpression");
406
431
  this.data.selectCursor = exprGen;
407
432
  }
408
433
  from(expr: SQLGen, alias?: SQLAlias = sql.identifier(Symbol())) {
@@ -572,6 +597,9 @@ ${sql.join(
572
597
  }
573
598
  buildSelectFields() {
574
599
  this.lockEverything();
600
+ if (this.compiledData.fixedSelectExpression) {
601
+ return this.compiledData.fixedSelectExpression;
602
+ }
575
603
  return sql.join(
576
604
  this.compiledData.select.map(
577
605
  ([sqlFragment, alias]) =>
@@ -675,6 +703,15 @@ ${sql.join(
675
703
  useAsterisk?: boolean,
676
704
  } = {}
677
705
  ) {
706
+ this.lockEverything();
707
+
708
+ if (this.compiledData.fixedSelectExpression) {
709
+ if (Object.keys(options).length > 0) {
710
+ throw new Error(
711
+ "Do not pass options to QueryBuilder.build() when using `buildNamedChildSelecting`"
712
+ );
713
+ }
714
+ }
678
715
  const {
679
716
  asJson = false,
680
717
  asJsonAggregate = false,
@@ -684,7 +721,6 @@ ${sql.join(
684
721
  useAsterisk = false,
685
722
  } = options;
686
723
 
687
- this.lockEverything();
688
724
  if (onlyJsonField) {
689
725
  return this.buildSelectJson({ addNullCase, addNotDistinctFromNullCase });
690
726
  }
@@ -734,9 +770,14 @@ with ${sql.identifier(flipAlias)} as (
734
770
  )
735
771
  select *
736
772
  from ${sql.identifier(flipAlias)}
737
- order by (row_number() over (partition by 1)) desc`;
773
+ order by (row_number() over (partition by 1)) desc`; /* We don't need to factor useAsterisk into this row_number() usage */
738
774
  }
739
775
  if (useAsterisk) {
776
+ /*
777
+ * NOTE[useAsterisk/row_number]: since LIMIT/OFFSET is inside this
778
+ * subquery, row_number() outside of this subquery WON'T include the
779
+ * offset. We must add it back wherever row_number() is used.
780
+ */
740
781
  fragment = sql.fragment`select ${fields} from (${fragment}) ${this.getTableAlias()}`;
741
782
  }
742
783
  if (asJsonAggregate) {
@@ -782,6 +823,8 @@ order by (row_number() over (partition by 1)) desc`;
782
823
  this.data[type].upper,
783
824
  context
784
825
  );
826
+ } else if (type === "fixedSelectExpression") {
827
+ this.compiledData[type] = callIfNecessary(this.data[type], context);
785
828
  } else if (type === "select") {
786
829
  /*
787
830
  * NOTICE: locking select can cause additional selects to be added, so the
@@ -871,9 +914,39 @@ order by (row_number() over (partition by 1)) desc`;
871
914
  this.lock("first");
872
915
  this.lock("last");
873
916
  // We must execute select after orderBy otherwise we cannot generate a cursor
917
+ this.lock("fixedSelectExpression");
874
918
  this.lock("selectCursor");
875
919
  this.lock("select");
876
920
  }
921
+ /** this method is experimental */
922
+ buildChild() {
923
+ const options = { supportsJSONB: this.supportsJSONB };
924
+ const child = new QueryBuilder(options, this.context, this.rootValue);
925
+ child.parentQueryBuilder = this;
926
+ return child;
927
+ }
928
+ /** this method is experimental */
929
+ buildNamedChildSelecting(
930
+ name: RawAlias,
931
+ from: SQLGen,
932
+ selectExpression: SQLGen,
933
+ alias?: SQLAlias
934
+ ) {
935
+ if (this._children.has(name)) {
936
+ throw new Error(
937
+ `QueryBuilder already has a child named ${name.toString()}`
938
+ );
939
+ }
940
+ const child = this.buildChild();
941
+ child.from(from, alias);
942
+ child.fixedSelectExpression(selectExpression);
943
+ this._children.set(name, child);
944
+ return child;
945
+ }
946
+ /** this method is experimental */
947
+ getNamedChild(name: string) {
948
+ return this._children.get(name);
949
+ }
877
950
  }
878
951
 
879
952
  export default QueryBuilder;