pipework 0.263.1 → 0.264.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.264.0
4
+
5
+ - **Added: `pipe.fn` arguments can be a column or a SQL expression of the declared type** (#928). An argument was typed as a bound value only, though a column or SQL argument was already embedded as SQL at runtime, so passing a column needed `as unknown as` casts. Each argument is now a value, a `SQL<T>`, or a column whose data type is `T`; a column or SQL of another type is a compile error, and a nullable column only fits a nullable argument. This lets an array column be unnested into a set once per statement, which Postgres can hash: `inArray(t.id, db.select({ id: u.unnest }).from(src).crossJoinLateral(unnestUuids(src.arr)))`. `uuid` and `text` fields are both `string`, so a `text[]` column still fits a `uuid().array()` argument, as a string value already did.
6
+
3
7
  ## 0.263.1
4
8
 
5
9
  - **Fixed: long lists in `filter.inList`, `filter.notInList` and `sql.inList` no longer walk the list for every row** (#926). Postgres hashes `= ANY` only when the array is a constant at plan time and both sides share a hash function, so a reused generic plan, or an integer list with no `cast` against an `int` column, went from milliseconds to over 30 seconds. Above 100 values `filter.inList` now renders `IN (SELECT unnest((SELECT $1::T[])))` and `filter.notInList` `NOT IN` of the same, hashed under any plan, at any length tested (400,000 values) and across integer types. Up to 100 values they keep `= ANY` and `<> ALL`, so short lists keep index conditions, partition pruning and estimates. `sql.inList` rendered `= ANY(ARRAY(SELECT ...))`, which was never hashed; it now renders `IN (SELECT ...)` at every length, in the same position after the column. `mail.unsubscribedAmong` uses it. No API change; the docs now say when hashing applies.
package/REFERENCE.md CHANGED
@@ -901,7 +901,7 @@ Produces a typed VALUES clause for bulk operations — the base primitive for bu
901
901
 
902
902
  ### `pipe.fn`
903
903
 
904
- A set-returning SQL function as a typed source for `.from()` and joins. `pipe.fn(name, { args, columns })` returns a callable; calling it with the argument values returns the source. `name` is `fn` or `schema.fn` (lowercase letters, digits, underscores). `args` is a tuple of `pipe.field.*` descriptors and `columns` a record of them; a wrong argument count or type, or an undeclared column, is a compile error. Each argument is a bound parameter cast to its declared SQL type, and result columns are read as `camelToSnake(key)`.
904
+ A set-returning SQL function as a typed source for `.from()` and joins. `pipe.fn(name, { args, columns })` returns a callable; calling it with the argument values returns the source. `name` is `fn` or `schema.fn` (lowercase letters, digits, underscores). `args` is a tuple of `pipe.field.*` descriptors and `columns` a record of them; a wrong argument count or type, or an undeclared column, is a compile error. Each argument is a value, a column, or a SQL expression of the declared type (a column or SQL of another type is a compile error). A value is a bound parameter; a column or SQL expression is embedded as written; every argument is cast to its declared SQL type. Result columns are read as `camelToSnake(key)`.
905
905
 
906
906
  ```ts
907
907
  const headImports = pipe.fn('canvas.head_imports', {
@@ -913,6 +913,20 @@ await db.select().from(f).where(eq(f.toPackage, pkg)).orderBy(asc(f.line))
913
913
  const g = headImports(repoId, other).as('prev') // the same function twice in one query
914
914
  ```
915
915
 
916
+ An array column unnests into a set once per statement, which Postgres can hash, through a lateral join:
917
+
918
+ ```ts
919
+ const unnestUuids = pipe.fn('unnest', {
920
+ args: [pipe.field.uuid().array()],
921
+ columns: { unnest: pipe.field.uuid() },
922
+ })
923
+ const u = unnestUuids(src.arr) // unnest("src"."arr"::uuid[])
924
+ const held = db.select({ id: u.unnest }).from(src).crossJoinLateral(u)
925
+ await db.select().from(target).where(inArray(target.id, held))
926
+ ```
927
+
928
+ A result column of one function (`unnestUuids(f.x)`) is a valid argument to another when both are in the same query.
929
+
916
930
  The default alias is the unqualified function name. Fields work with `eq`, `castUuid`, `orderBy`, joins and `select({ x: f.fromPath })`.
917
931
 
918
932
  ### `pipe.field`
@@ -1,5 +1,6 @@
1
1
  import type { SQL } from './types.js';
2
2
  import { Subquery } from '../query/subquery.js';
3
+ import type { Column } from './types.js';
3
4
  import type { InferFieldOutput } from '../domain/brand.js';
4
5
  import type { FieldDescriptor } from '../domain/types.js';
5
6
  type AnyField = FieldDescriptor<any, any>;
@@ -10,8 +11,17 @@ export interface FunctionSpec<TArgs extends readonly AnyField[], TColumns extend
10
11
  /** One `pipe.field.*` descriptor per result column; keys are camelCase, columns are their snake_case. */
11
12
  readonly columns: TColumns;
12
13
  }
14
+ /** A column whose data type is `T` (and that cannot be null unless `T` can). */
15
+ type ColumnOf<T> = Column & {
16
+ readonly _: {
17
+ readonly data: Exclude<T, null>;
18
+ readonly notNull: null extends T ? boolean : true;
19
+ };
20
+ };
21
+ /** One argument: a bound value, a SQL expression, or a column, each of the declared type. */
22
+ type ArgValue<F extends AnyField> = InferFieldOutput<F> | SQL<InferFieldOutput<F>> | ColumnOf<InferFieldOutput<F>>;
13
23
  type ArgValues<TArgs extends readonly AnyField[]> = {
14
- [K in keyof TArgs]: InferFieldOutput<TArgs[K]>;
24
+ [K in keyof TArgs]: ArgValue<TArgs[K]>;
15
25
  };
16
26
  /** The selected fields of a function source: each result column as a typed SQL expression. */
17
27
  export type FunctionSourceFields<TColumns extends Record<string, AnyField>> = {
@@ -24,9 +34,11 @@ export type FunctionSource<TAlias extends string, TColumns extends Record<string
24
34
  };
25
35
  /**
26
36
  * Declares a set-returning SQL function and returns a callable that yields a source for `.from()` and joins.
27
- * Each argument is bound as a parameter, converted the way a column of that field is on insert (jsonb as JSON text,
28
- * dates, bigints, arrays), and cast to its declared SQL type: `ext.f($1::uuid, $2::text)`. A null binds SQL NULL.
29
- * An array argument (`field.uuid().array()`) is one parameter: `ext.f($1::uuid[])`.
37
+ * Each argument is a value, a column or a SQL expression of the declared type. A value is bound as a parameter,
38
+ * converted the way a column of that field is on insert (jsonb as JSON text, dates, bigints, arrays), and cast to its
39
+ * declared SQL type: `ext.f($1::uuid, $2::text)`. A null binds SQL NULL. An array argument (`field.uuid().array()`) is
40
+ * one parameter: `ext.f($1::uuid[])`. A column or SQL expression is embedded as written, still cast:
41
+ * `unnest(t.arr::uuid[])`, so an array column can be unnested in a lateral join (`.crossJoinLateral(unnestUuids(t.arr))`).
30
42
  * Result columns are typed from `columns`; the default alias is the function's unqualified name.
31
43
  */
32
44
  export declare function fn<const TArgs extends readonly AnyField[], TColumns extends Record<string, AnyField>>(name: string, spec: FunctionSpec<TArgs, TColumns>): (...values: ArgValues<TArgs>) => FunctionSource<string, TColumns>;
@@ -1 +1 @@
1
- {"version":3,"file":"function-source.d.ts","sourceRoot":"","sources":["../../../src/data/db/function-source.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAK/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAKzD,KAAK,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAIzC,mFAAmF;AACnF,MAAM,WAAW,YAAY,CAAC,KAAK,SAAS,SAAS,QAAQ,EAAE,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IACxG,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,CAAA;IAClC,yGAAyG;IACzG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAC3B;AAED,KAAK,SAAS,CAAC,KAAK,SAAS,SAAS,QAAQ,EAAE,IAAI;KAAG,CAAC,IAAI,MAAM,KAAK,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAEtG,8FAA8F;AAC9F,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI;KAC3E,CAAC,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;CAC1D,CAAA;AAED,8FAA8F;AAC9F,MAAM,MAAM,cAAc,CAAC,MAAM,SAAS,MAAM,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IACvF,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,CAAC,GAChD,oBAAoB,CAAC,QAAQ,CAAC,GAC9B;IACA,uFAAuF;IACvF,EAAE,CAAC,IAAI,SAAS,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;CACrE,CAAA;AAUH;;;;;;GAMG;AACH,wBAAgB,EAAE,CAAC,KAAK,CAAC,KAAK,SAAS,SAAS,QAAQ,EAAE,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EACnG,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,GAClC,CAAC,GAAG,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAsDnE"}
1
+ {"version":3,"file":"function-source.d.ts","sourceRoot":"","sources":["../../../src/data/db/function-source.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAA;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAI/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAKzD,KAAK,QAAQ,GAAG,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAIzC,mFAAmF;AACnF,MAAM,WAAW,YAAY,CAAC,KAAK,SAAS,SAAS,QAAQ,EAAE,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC;IACxG,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,CAAA;IAClC,yGAAyG;IACzG,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAA;CAC3B;AAED,gFAAgF;AAChF,KAAK,QAAQ,CAAC,CAAC,IAAI,MAAM,GAAG;IAC1B,QAAQ,CAAC,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,SAAS,CAAC,GAAG,OAAO,GAAG,IAAI,CAAA;KAAE,CAAA;CACnG,CAAA;AAED,6FAA6F;AAC7F,KAAK,QAAQ,CAAC,CAAC,SAAS,QAAQ,IAAI,gBAAgB,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;AAElH,KAAK,SAAS,CAAC,KAAK,SAAS,SAAS,QAAQ,EAAE,IAAI;KAAG,CAAC,IAAI,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAE9F,8FAA8F;AAC9F,MAAM,MAAM,oBAAoB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI;KAC3E,CAAC,IAAI,MAAM,QAAQ,GAAG,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;CAC1D,CAAA;AAED,8FAA8F;AAC9F,MAAM,MAAM,cAAc,CAAC,MAAM,SAAS,MAAM,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,IACvF,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAC,QAAQ,CAAC,CAAC,GAChD,oBAAoB,CAAC,QAAQ,CAAC,GAC9B;IACA,uFAAuF;IACvF,EAAE,CAAC,IAAI,SAAS,MAAM,EAAE,KAAK,EAAE,IAAI,GAAG,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;CACrE,CAAA;AAUH;;;;;;;;GAQG;AACH,wBAAgB,EAAE,CAAC,KAAK,CAAC,KAAK,SAAS,SAAS,QAAQ,EAAE,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,EACnG,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,GAClC,CAAC,GAAG,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAsDnE"}
@@ -14,9 +14,11 @@ function quote(name) {
14
14
  }
15
15
  /**
16
16
  * Declares a set-returning SQL function and returns a callable that yields a source for `.from()` and joins.
17
- * Each argument is bound as a parameter, converted the way a column of that field is on insert (jsonb as JSON text,
18
- * dates, bigints, arrays), and cast to its declared SQL type: `ext.f($1::uuid, $2::text)`. A null binds SQL NULL.
19
- * An array argument (`field.uuid().array()`) is one parameter: `ext.f($1::uuid[])`.
17
+ * Each argument is a value, a column or a SQL expression of the declared type. A value is bound as a parameter,
18
+ * converted the way a column of that field is on insert (jsonb as JSON text, dates, bigints, arrays), and cast to its
19
+ * declared SQL type: `ext.f($1::uuid, $2::text)`. A null binds SQL NULL. An array argument (`field.uuid().array()`) is
20
+ * one parameter: `ext.f($1::uuid[])`. A column or SQL expression is embedded as written, still cast:
21
+ * `unnest(t.arr::uuid[])`, so an array column can be unnested in a lateral join (`.crossJoinLateral(unnestUuids(t.arr))`).
20
22
  * Result columns are typed from `columns`; the default alias is the function's unqualified name.
21
23
  */
22
24
  export function fn(name, spec) {
@@ -1 +1 @@
1
- {"version":3,"file":"function-source.js","sourceRoot":"","sources":["../../../src/data/db/function-source.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"function-source.js","sourceRoot":"","sources":["../../../src/data/db/function-source.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipework",
3
- "version": "0.263.1",
3
+ "version": "0.264.0",
4
4
  "description": "TypeScript framework for multi-tenant SaaS applications. PostgreSQL-only.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,7 +23,15 @@ export interface FunctionSpec<TArgs extends readonly AnyField[], TColumns extend
23
23
  readonly columns: TColumns
24
24
  }
25
25
 
26
- type ArgValues<TArgs extends readonly AnyField[]> = { [K in keyof TArgs]: InferFieldOutput<TArgs[K]> }
26
+ /** A column whose data type is `T` (and that cannot be null unless `T` can). */
27
+ type ColumnOf<T> = Column & {
28
+ readonly _: { readonly data: Exclude<T, null>; readonly notNull: null extends T ? boolean : true }
29
+ }
30
+
31
+ /** One argument: a bound value, a SQL expression, or a column, each of the declared type. */
32
+ type ArgValue<F extends AnyField> = InferFieldOutput<F> | SQL<InferFieldOutput<F>> | ColumnOf<InferFieldOutput<F>>
33
+
34
+ type ArgValues<TArgs extends readonly AnyField[]> = { [K in keyof TArgs]: ArgValue<TArgs[K]> }
27
35
 
28
36
  /** The selected fields of a function source: each result column as a typed SQL expression. */
29
37
  export type FunctionSourceFields<TColumns extends Record<string, AnyField>> = {
@@ -49,9 +57,11 @@ function quote(name: string): string {
49
57
 
50
58
  /**
51
59
  * Declares a set-returning SQL function and returns a callable that yields a source for `.from()` and joins.
52
- * Each argument is bound as a parameter, converted the way a column of that field is on insert (jsonb as JSON text,
53
- * dates, bigints, arrays), and cast to its declared SQL type: `ext.f($1::uuid, $2::text)`. A null binds SQL NULL.
54
- * An array argument (`field.uuid().array()`) is one parameter: `ext.f($1::uuid[])`.
60
+ * Each argument is a value, a column or a SQL expression of the declared type. A value is bound as a parameter,
61
+ * converted the way a column of that field is on insert (jsonb as JSON text, dates, bigints, arrays), and cast to its
62
+ * declared SQL type: `ext.f($1::uuid, $2::text)`. A null binds SQL NULL. An array argument (`field.uuid().array()`) is
63
+ * one parameter: `ext.f($1::uuid[])`. A column or SQL expression is embedded as written, still cast:
64
+ * `unnest(t.arr::uuid[])`, so an array column can be unnested in a lateral join (`.crossJoinLateral(unnestUuids(t.arr))`).
55
65
  * Result columns are typed from `columns`; the default alias is the function's unqualified name.
56
66
  */
57
67
  export function fn<const TArgs extends readonly AnyField[], TColumns extends Record<string, AnyField>>(