@powersync/service-sync-rules 0.0.0-dev-20260129132348 → 0.0.0-dev-20260202103935
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/BucketParameterQuerier.d.ts +28 -5
- package/dist/BucketParameterQuerier.js +12 -4
- package/dist/BucketParameterQuerier.js.map +1 -1
- package/dist/SqlParameterQuery.js +0 -2
- package/dist/SqlParameterQuery.js.map +1 -1
- package/dist/StaticSqlParameterQuery.js +0 -1
- package/dist/StaticSqlParameterQuery.js.map +1 -1
- package/dist/TablePattern.d.ts +4 -1
- package/dist/TablePattern.js +11 -0
- package/dist/TablePattern.js.map +1 -1
- package/dist/TableValuedFunctionSqlParameterQuery.js +0 -1
- package/dist/TableValuedFunctionSqlParameterQuery.js.map +1 -1
- package/dist/compiler/bucket_resolver.d.ts +70 -0
- package/dist/compiler/bucket_resolver.js +131 -0
- package/dist/compiler/bucket_resolver.js.map +1 -0
- package/dist/compiler/compatibility.d.ts +16 -0
- package/dist/compiler/compatibility.js +12 -0
- package/dist/compiler/compatibility.js.map +1 -0
- package/dist/compiler/compiler.d.ts +91 -0
- package/dist/compiler/compiler.js +113 -0
- package/dist/compiler/compiler.js.map +1 -0
- package/dist/compiler/equality.d.ts +99 -0
- package/dist/compiler/equality.js +284 -0
- package/dist/compiler/equality.js.map +1 -0
- package/dist/compiler/expression.d.ts +77 -0
- package/dist/compiler/expression.js +122 -0
- package/dist/compiler/expression.js.map +1 -0
- package/dist/compiler/filter.d.ts +71 -0
- package/dist/compiler/filter.js +110 -0
- package/dist/compiler/filter.js.map +1 -0
- package/dist/compiler/filter_simplifier.d.ts +26 -0
- package/dist/compiler/filter_simplifier.js +119 -0
- package/dist/compiler/filter_simplifier.js.map +1 -0
- package/dist/compiler/ir_to_sync_plan.d.ts +37 -0
- package/dist/compiler/ir_to_sync_plan.js +163 -0
- package/dist/compiler/ir_to_sync_plan.js.map +1 -0
- package/dist/compiler/parser.d.ts +99 -0
- package/dist/compiler/parser.js +547 -0
- package/dist/compiler/parser.js.map +1 -0
- package/dist/compiler/querier_graph.d.ts +42 -0
- package/dist/compiler/querier_graph.js +365 -0
- package/dist/compiler/querier_graph.js.map +1 -0
- package/dist/compiler/rows.d.ts +113 -0
- package/dist/compiler/rows.js +156 -0
- package/dist/compiler/rows.js.map +1 -0
- package/dist/compiler/scope.d.ts +22 -0
- package/dist/compiler/scope.js +47 -0
- package/dist/compiler/scope.js.map +1 -0
- package/dist/compiler/sqlite.d.ts +77 -0
- package/dist/compiler/sqlite.js +394 -0
- package/dist/compiler/sqlite.js.map +1 -0
- package/dist/compiler/table.d.ts +50 -0
- package/dist/compiler/table.js +57 -0
- package/dist/compiler/table.js.map +1 -0
- package/dist/errors.d.ts +4 -2
- package/dist/errors.js +16 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/sql_functions.d.ts +1 -1
- package/dist/streams/variant.js +0 -1
- package/dist/streams/variant.js.map +1 -1
- package/dist/sync_plan/expression.d.ts +109 -0
- package/dist/sync_plan/expression.js +85 -0
- package/dist/sync_plan/expression.js.map +1 -0
- package/dist/sync_plan/expression_to_sql.d.ts +41 -0
- package/dist/sync_plan/expression_to_sql.js +185 -0
- package/dist/sync_plan/expression_to_sql.js.map +1 -0
- package/dist/sync_plan/expression_visitor.d.ts +57 -0
- package/dist/sync_plan/expression_visitor.js +171 -0
- package/dist/sync_plan/expression_visitor.js.map +1 -0
- package/dist/sync_plan/plan.d.ts +196 -0
- package/dist/sync_plan/plan.js +2 -0
- package/dist/sync_plan/plan.js.map +1 -0
- package/dist/sync_plan/serialize.d.ts +68 -0
- package/dist/sync_plan/serialize.js +110 -0
- package/dist/sync_plan/serialize.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Expr, NodeLocation, PGNode } from 'pgsql-ast-parser';
|
|
2
|
+
import { SourceResultSet } from './table.js';
|
|
3
|
+
import { EqualsIgnoringResultSet } from './compatibility.js';
|
|
4
|
+
import { StableHasher } from './equality.js';
|
|
5
|
+
import { ConnectionParameterSource } from '../sync_plan/plan.js';
|
|
6
|
+
import { SqlExpression } from '../sync_plan/expression.js';
|
|
7
|
+
/**
|
|
8
|
+
* An analyzed SQL expression tracking dependencies on non-static data (i.e. rows or connection sources).
|
|
9
|
+
*
|
|
10
|
+
* Consider the sync stream `SELECT * FROM issues WHERE is_public OR auth.param('is_admin')`. To be able to explicitly
|
|
11
|
+
* track dependencies referenced in expressions, we transform them into a {@link SyncExpression}. For the `WHERE` clause
|
|
12
|
+
* in that example, the {@link sqlExpression} would be `?1 OR (?2 ->> 'is_admin')`, where `?1` is a {@link ColumnInRow}
|
|
13
|
+
* and `?2` is a {@link ConnectionParameter}.
|
|
14
|
+
*
|
|
15
|
+
* Once in this form, it's easy to reason about dependencies in expressions (used to later generate parameter match
|
|
16
|
+
* clauses) and to evaluate expressions at runtime (by preparing them as a statement and binding external values).
|
|
17
|
+
*/
|
|
18
|
+
export declare class SyncExpression implements EqualsIgnoringResultSet {
|
|
19
|
+
#private;
|
|
20
|
+
/**
|
|
21
|
+
* The AST node backing {@link sql}.
|
|
22
|
+
*
|
|
23
|
+
* We use this to be able to compose expressions, e.g. to possibly merge them.
|
|
24
|
+
*/
|
|
25
|
+
readonly node: SqlExpression<ExpressionInput>;
|
|
26
|
+
readonly locations: NodeLocations;
|
|
27
|
+
/**
|
|
28
|
+
* The original expression, where references to row or connection parameters have been replaced with SQL variables
|
|
29
|
+
* that are tracked through {@link instantiation}.
|
|
30
|
+
*
|
|
31
|
+
* This is only used to compute hash codes and to check instances for equality. {@link node} is the canonical
|
|
32
|
+
* representation of this expression.
|
|
33
|
+
*/
|
|
34
|
+
get sql(): string;
|
|
35
|
+
/**
|
|
36
|
+
* The values to instantiate parameters in {@link sqlExpression} with to retain original semantics of the
|
|
37
|
+
* expression.
|
|
38
|
+
*/
|
|
39
|
+
get instantiation(): readonly ExpressionInput[];
|
|
40
|
+
get location(): NodeLocation;
|
|
41
|
+
constructor(
|
|
42
|
+
/**
|
|
43
|
+
* The AST node backing {@link sql}.
|
|
44
|
+
*
|
|
45
|
+
* We use this to be able to compose expressions, e.g. to possibly merge them.
|
|
46
|
+
*/
|
|
47
|
+
node: SqlExpression<ExpressionInput>, locations: NodeLocations);
|
|
48
|
+
equalsAssumingSameResultSet(other: EqualsIgnoringResultSet): boolean;
|
|
49
|
+
assumingSameResultSetEqualityHashCode(hasher: StableHasher): void;
|
|
50
|
+
}
|
|
51
|
+
export type ExpressionInput = ColumnInRow | ConnectionParameter;
|
|
52
|
+
export declare class ColumnInRow implements EqualsIgnoringResultSet {
|
|
53
|
+
readonly syntacticOrigin: Expr;
|
|
54
|
+
readonly resultSet: SourceResultSet;
|
|
55
|
+
readonly column: string;
|
|
56
|
+
constructor(syntacticOrigin: Expr, resultSet: SourceResultSet, column: string);
|
|
57
|
+
equalsAssumingSameResultSet(other: EqualsIgnoringResultSet): boolean;
|
|
58
|
+
assumingSameResultSetEqualityHashCode(hasher: StableHasher): void;
|
|
59
|
+
}
|
|
60
|
+
export declare class ConnectionParameter implements EqualsIgnoringResultSet {
|
|
61
|
+
readonly syntacticOrigin: Expr;
|
|
62
|
+
readonly source: ConnectionParameterSource;
|
|
63
|
+
constructor(syntacticOrigin: Expr, source: ConnectionParameterSource);
|
|
64
|
+
equalsAssumingSameResultSet(other: EqualsIgnoringResultSet): boolean;
|
|
65
|
+
assumingSameResultSetEqualityHashCode(hasher: StableHasher): void;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Tracks the original source location for translated {@link SqlExpression} nodes.
|
|
69
|
+
*
|
|
70
|
+
* We want to serialize translated expressions for sync plan, so embedding source offsets in them expands the size of
|
|
71
|
+
* sync plans and is tedious. We only need access to node locations while compiling sync streams, which we store in this
|
|
72
|
+
* in-memory map.
|
|
73
|
+
*/
|
|
74
|
+
export declare class NodeLocations {
|
|
75
|
+
readonly sourceForNode: Map<SqlExpression<unknown>, PGNode | NodeLocation>;
|
|
76
|
+
locationFor(source: SqlExpression<unknown>): NodeLocation;
|
|
77
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { equalsIgnoringResultSetList } from './compatibility.js';
|
|
2
|
+
import { ExpressionToSqlite } from '../sync_plan/expression_to_sql.js';
|
|
3
|
+
import { RecursiveExpressionVisitor } from '../sync_plan/expression_visitor.js';
|
|
4
|
+
import { getLocation } from '../errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* An analyzed SQL expression tracking dependencies on non-static data (i.e. rows or connection sources).
|
|
7
|
+
*
|
|
8
|
+
* Consider the sync stream `SELECT * FROM issues WHERE is_public OR auth.param('is_admin')`. To be able to explicitly
|
|
9
|
+
* track dependencies referenced in expressions, we transform them into a {@link SyncExpression}. For the `WHERE` clause
|
|
10
|
+
* in that example, the {@link sqlExpression} would be `?1 OR (?2 ->> 'is_admin')`, where `?1` is a {@link ColumnInRow}
|
|
11
|
+
* and `?2` is a {@link ConnectionParameter}.
|
|
12
|
+
*
|
|
13
|
+
* Once in this form, it's easy to reason about dependencies in expressions (used to later generate parameter match
|
|
14
|
+
* clauses) and to evaluate expressions at runtime (by preparing them as a statement and binding external values).
|
|
15
|
+
*/
|
|
16
|
+
export class SyncExpression {
|
|
17
|
+
node;
|
|
18
|
+
locations;
|
|
19
|
+
#sql;
|
|
20
|
+
#instantiation;
|
|
21
|
+
/**
|
|
22
|
+
* The original expression, where references to row or connection parameters have been replaced with SQL variables
|
|
23
|
+
* that are tracked through {@link instantiation}.
|
|
24
|
+
*
|
|
25
|
+
* This is only used to compute hash codes and to check instances for equality. {@link node} is the canonical
|
|
26
|
+
* representation of this expression.
|
|
27
|
+
*/
|
|
28
|
+
get sql() {
|
|
29
|
+
return (this.#sql ??= ExpressionToSqlite.toSqlite(this.node));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The values to instantiate parameters in {@link sqlExpression} with to retain original semantics of the
|
|
33
|
+
* expression.
|
|
34
|
+
*/
|
|
35
|
+
get instantiation() {
|
|
36
|
+
if (this.#instantiation != null) {
|
|
37
|
+
return this.#instantiation;
|
|
38
|
+
}
|
|
39
|
+
const instantiation = [];
|
|
40
|
+
FindExternalData.instance.visit(this.node, instantiation);
|
|
41
|
+
return (this.#instantiation = instantiation);
|
|
42
|
+
}
|
|
43
|
+
get location() {
|
|
44
|
+
return this.locations.locationFor(this.node);
|
|
45
|
+
}
|
|
46
|
+
constructor(
|
|
47
|
+
/**
|
|
48
|
+
* The AST node backing {@link sql}.
|
|
49
|
+
*
|
|
50
|
+
* We use this to be able to compose expressions, e.g. to possibly merge them.
|
|
51
|
+
*/
|
|
52
|
+
node, locations) {
|
|
53
|
+
this.node = node;
|
|
54
|
+
this.locations = locations;
|
|
55
|
+
}
|
|
56
|
+
equalsAssumingSameResultSet(other) {
|
|
57
|
+
return (other instanceof SyncExpression &&
|
|
58
|
+
other.sql == this.sql &&
|
|
59
|
+
equalsIgnoringResultSetList.equals(other.instantiation, this.instantiation));
|
|
60
|
+
}
|
|
61
|
+
assumingSameResultSetEqualityHashCode(hasher) {
|
|
62
|
+
hasher.addString(this.sql);
|
|
63
|
+
equalsIgnoringResultSetList.hash(hasher, this.instantiation);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
class FindExternalData extends RecursiveExpressionVisitor {
|
|
67
|
+
defaultExpression(expr, arg) {
|
|
68
|
+
this.visitChildren(expr, arg);
|
|
69
|
+
}
|
|
70
|
+
visitExternalData(expr, arg) {
|
|
71
|
+
arg.push(expr.source);
|
|
72
|
+
}
|
|
73
|
+
static instance = new FindExternalData();
|
|
74
|
+
}
|
|
75
|
+
export class ColumnInRow {
|
|
76
|
+
syntacticOrigin;
|
|
77
|
+
resultSet;
|
|
78
|
+
column;
|
|
79
|
+
constructor(syntacticOrigin, resultSet, column) {
|
|
80
|
+
this.syntacticOrigin = syntacticOrigin;
|
|
81
|
+
this.resultSet = resultSet;
|
|
82
|
+
this.column = column;
|
|
83
|
+
}
|
|
84
|
+
equalsAssumingSameResultSet(other) {
|
|
85
|
+
return other instanceof ColumnInRow && other.column == this.column;
|
|
86
|
+
}
|
|
87
|
+
assumingSameResultSetEqualityHashCode(hasher) {
|
|
88
|
+
hasher.addString(this.column);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export class ConnectionParameter {
|
|
92
|
+
syntacticOrigin;
|
|
93
|
+
source;
|
|
94
|
+
constructor(syntacticOrigin, source) {
|
|
95
|
+
this.syntacticOrigin = syntacticOrigin;
|
|
96
|
+
this.source = source;
|
|
97
|
+
}
|
|
98
|
+
equalsAssumingSameResultSet(other) {
|
|
99
|
+
return other instanceof ConnectionParameter && other.source == this.source;
|
|
100
|
+
}
|
|
101
|
+
assumingSameResultSetEqualityHashCode(hasher) {
|
|
102
|
+
hasher.addString(this.source);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Tracks the original source location for translated {@link SqlExpression} nodes.
|
|
107
|
+
*
|
|
108
|
+
* We want to serialize translated expressions for sync plan, so embedding source offsets in them expands the size of
|
|
109
|
+
* sync plans and is tedious. We only need access to node locations while compiling sync streams, which we store in this
|
|
110
|
+
* in-memory map.
|
|
111
|
+
*/
|
|
112
|
+
export class NodeLocations {
|
|
113
|
+
sourceForNode = new Map();
|
|
114
|
+
locationFor(source) {
|
|
115
|
+
const location = getLocation(this.sourceForNode.get(source));
|
|
116
|
+
if (location == null) {
|
|
117
|
+
throw new Error('Missing location');
|
|
118
|
+
}
|
|
119
|
+
return location;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=expression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression.js","sourceRoot":"","sources":["../../src/compiler/expression.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAI1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,OAAO,cAAc;IAuCd;IACA;IAvCX,IAAI,CAAU;IACd,cAAc,CAA8B;IAE5C;;;;;;OAMG;IACH,IAAI,GAAG;QACL,OAAO,CAAC,IAAI,CAAC,IAAI,KAAK,kBAAkB,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,CAAC;IAED;;;OAGG;IACH,IAAI,aAAa;QACf,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,cAAc,CAAC;QAC7B,CAAC;QAED,MAAM,aAAa,GAAsB,EAAE,CAAC;QAC5C,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/C,CAAC;IAED;IACE;;;;OAIG;IACM,IAAoC,EACpC,SAAwB;QADxB,SAAI,GAAJ,IAAI,CAAgC;QACpC,cAAS,GAAT,SAAS,CAAe;IAChC,CAAC;IAEJ,2BAA2B,CAAC,KAA8B;QACxD,OAAO,CACL,KAAK,YAAY,cAAc;YAC/B,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG;YACrB,2BAA2B,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,CAC5E,CAAC;IACJ,CAAC;IAED,qCAAqC,CAAC,MAAoB;QACxD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3B,2BAA2B,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC/D,CAAC;CACF;AAED,MAAM,gBAAiB,SAAQ,0BAAoE;IACjG,iBAAiB,CAAC,IAAoC,EAAE,GAAsB;QAC5E,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC;IAED,iBAAiB,CAAC,IAAmC,EAAE,GAAsB;QAC3E,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,MAAM,CAAU,QAAQ,GAAqB,IAAI,gBAAgB,EAAE,CAAC;;AAKtE,MAAM,OAAO,WAAW;IAEX;IACA;IACA;IAHX,YACW,eAAqB,EACrB,SAA0B,EAC1B,MAAc;QAFd,oBAAe,GAAf,eAAe,CAAM;QACrB,cAAS,GAAT,SAAS,CAAiB;QAC1B,WAAM,GAAN,MAAM,CAAQ;IACtB,CAAC;IAEJ,2BAA2B,CAAC,KAA8B;QACxD,OAAO,KAAK,YAAY,WAAW,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IACrE,CAAC;IAED,qCAAqC,CAAC,MAAoB;QACxD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;CACF;AAED,MAAM,OAAO,mBAAmB;IAEnB;IACA;IAFX,YACW,eAAqB,EACrB,MAAiC;QADjC,oBAAe,GAAf,eAAe,CAAM;QACrB,WAAM,GAAN,MAAM,CAA2B;IACzC,CAAC;IAEJ,2BAA2B,CAAC,KAA8B;QACxD,OAAO,KAAK,YAAY,mBAAmB,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IAC7E,CAAC;IAED,qCAAqC,CAAC,MAAoB;QACxD,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IACf,aAAa,GAAG,IAAI,GAAG,EAAiD,CAAC;IAElF,WAAW,CAAC,MAA8B;QACxC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7D,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { NodeLocation } from 'pgsql-ast-parser';
|
|
2
|
+
import { ExpressionInput, SyncExpression } from './expression.js';
|
|
3
|
+
import { SourceResultSet } from './table.js';
|
|
4
|
+
import { EqualsIgnoringResultSet } from './compatibility.js';
|
|
5
|
+
import { StableHasher } from './equality.js';
|
|
6
|
+
export interface Or {
|
|
7
|
+
terms: And[];
|
|
8
|
+
}
|
|
9
|
+
export interface And {
|
|
10
|
+
terms: BaseTerm[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A boolean expression that doesn't have boolean operators with subexpressions referencing different tables in it.
|
|
14
|
+
*
|
|
15
|
+
* These basic terms form a filter by being composed into a distributive normal form. For analysis, we just need to know
|
|
16
|
+
* how terms from different tables relate to each other. So `users.id = auth.user_id() AND issues.is_public` need to be
|
|
17
|
+
* two terms in an {@link And}, but `users.is_deleted OR users.is_admin` can be represented as a single base term.
|
|
18
|
+
*/
|
|
19
|
+
export type BaseTerm = SingleDependencyExpression | EqualsClause;
|
|
20
|
+
export declare function isBaseTerm(value: unknown): value is BaseTerm;
|
|
21
|
+
/**
|
|
22
|
+
* A {@link SyncExpression} that only depends on a single result set or connection data.
|
|
23
|
+
*/
|
|
24
|
+
export declare class SingleDependencyExpression implements EqualsIgnoringResultSet {
|
|
25
|
+
readonly expression: SyncExpression;
|
|
26
|
+
/**
|
|
27
|
+
* The single result set on which the expression depends on.
|
|
28
|
+
*
|
|
29
|
+
* If null, the expression is allowed to depend on subscription parameters instead.
|
|
30
|
+
*/
|
|
31
|
+
readonly resultSet: SourceResultSet | null;
|
|
32
|
+
/**
|
|
33
|
+
* Whether this expression depends on data derived from the connection or subscription.
|
|
34
|
+
*/
|
|
35
|
+
readonly dependsOnConnection: boolean;
|
|
36
|
+
constructor(expression: SyncExpression | SingleDependencyExpression);
|
|
37
|
+
equalsAssumingSameResultSet(other: EqualsIgnoringResultSet): boolean;
|
|
38
|
+
assumingSameResultSetEqualityHashCode(hasher: StableHasher): void;
|
|
39
|
+
/**
|
|
40
|
+
* If all inputs have a single dependency, returns either the result set they all depend on or `null` and whether they
|
|
41
|
+
* depend on connection data.
|
|
42
|
+
*
|
|
43
|
+
* If the inputs have more than a single distinct dependency, returns null.
|
|
44
|
+
*/
|
|
45
|
+
static extractSingleDependency(inputs: Iterable<ExpressionInput>): [SourceResultSet | null, boolean] | null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A {@link SyncExpression} that only depends on data from a single row.
|
|
49
|
+
*/
|
|
50
|
+
export declare class RowExpression extends SingleDependencyExpression {
|
|
51
|
+
readonly resultSet: SourceResultSet;
|
|
52
|
+
constructor(expression: SyncExpression | SingleDependencyExpression);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A {@link SyncExpression} that only depends on connection data.
|
|
56
|
+
*/
|
|
57
|
+
export declare class RequestExpression extends SingleDependencyExpression {
|
|
58
|
+
constructor(expression: SyncExpression | SingleDependencyExpression);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* An expression of the form `foo = bar`, where `foo` and `bar` are expressions allowed to depend on different sources.
|
|
62
|
+
*/
|
|
63
|
+
export declare class EqualsClause {
|
|
64
|
+
readonly left: SingleDependencyExpression;
|
|
65
|
+
readonly right: SingleDependencyExpression;
|
|
66
|
+
constructor(left: SingleDependencyExpression, right: SingleDependencyExpression);
|
|
67
|
+
get location(): NodeLocation | undefined;
|
|
68
|
+
}
|
|
69
|
+
export declare class InvalidExpressionError extends Error {
|
|
70
|
+
constructor(message: string);
|
|
71
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { ColumnInRow, SyncExpression } from './expression.js';
|
|
2
|
+
import { expandNodeLocations } from '../errors.js';
|
|
3
|
+
export function isBaseTerm(value) {
|
|
4
|
+
return value instanceof SingleDependencyExpression || value instanceof EqualsClause;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* A {@link SyncExpression} that only depends on a single result set or connection data.
|
|
8
|
+
*/
|
|
9
|
+
export class SingleDependencyExpression {
|
|
10
|
+
expression;
|
|
11
|
+
/**
|
|
12
|
+
* The single result set on which the expression depends on.
|
|
13
|
+
*
|
|
14
|
+
* If null, the expression is allowed to depend on subscription parameters instead.
|
|
15
|
+
*/
|
|
16
|
+
resultSet;
|
|
17
|
+
/**
|
|
18
|
+
* Whether this expression depends on data derived from the connection or subscription.
|
|
19
|
+
*/
|
|
20
|
+
dependsOnConnection;
|
|
21
|
+
constructor(expression) {
|
|
22
|
+
if (expression instanceof SyncExpression) {
|
|
23
|
+
const checked = SingleDependencyExpression.extractSingleDependency(expression.instantiation);
|
|
24
|
+
if (checked == null) {
|
|
25
|
+
throw new InvalidExpressionError('Expression with multiple dependencies passed to SingleDependencyExpression');
|
|
26
|
+
}
|
|
27
|
+
this.expression = expression;
|
|
28
|
+
this.resultSet = checked[0];
|
|
29
|
+
this.dependsOnConnection = checked[1];
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
this.expression = expression.expression;
|
|
33
|
+
this.resultSet = expression.resultSet;
|
|
34
|
+
this.dependsOnConnection = expression.dependsOnConnection;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
equalsAssumingSameResultSet(other) {
|
|
38
|
+
return other instanceof SingleDependencyExpression && other.expression.equalsAssumingSameResultSet(this.expression);
|
|
39
|
+
}
|
|
40
|
+
assumingSameResultSetEqualityHashCode(hasher) {
|
|
41
|
+
this.expression.assumingSameResultSetEqualityHashCode(hasher);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* If all inputs have a single dependency, returns either the result set they all depend on or `null` and whether they
|
|
45
|
+
* depend on connection data.
|
|
46
|
+
*
|
|
47
|
+
* If the inputs have more than a single distinct dependency, returns null.
|
|
48
|
+
*/
|
|
49
|
+
static extractSingleDependency(inputs) {
|
|
50
|
+
let resultSet = null;
|
|
51
|
+
let hasSubscriptionDependency = false;
|
|
52
|
+
for (const dependency of inputs) {
|
|
53
|
+
if (dependency instanceof ColumnInRow) {
|
|
54
|
+
if ((resultSet != null && resultSet !== dependency.resultSet) || hasSubscriptionDependency) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
resultSet = dependency.resultSet;
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
if (resultSet != null) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
hasSubscriptionDependency = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return [resultSet, hasSubscriptionDependency];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* A {@link SyncExpression} that only depends on data from a single row.
|
|
71
|
+
*/
|
|
72
|
+
export class RowExpression extends SingleDependencyExpression {
|
|
73
|
+
constructor(expression) {
|
|
74
|
+
super(expression);
|
|
75
|
+
if (this.resultSet == null) {
|
|
76
|
+
throw new InvalidExpressionError('Does not depend on a single result set');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* A {@link SyncExpression} that only depends on connection data.
|
|
82
|
+
*/
|
|
83
|
+
export class RequestExpression extends SingleDependencyExpression {
|
|
84
|
+
constructor(expression) {
|
|
85
|
+
super(expression);
|
|
86
|
+
if (this.resultSet != null) {
|
|
87
|
+
throw new InvalidExpressionError('Does not depend on connection data');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* An expression of the form `foo = bar`, where `foo` and `bar` are expressions allowed to depend on different sources.
|
|
93
|
+
*/
|
|
94
|
+
export class EqualsClause {
|
|
95
|
+
left;
|
|
96
|
+
right;
|
|
97
|
+
constructor(left, right) {
|
|
98
|
+
this.left = left;
|
|
99
|
+
this.right = right;
|
|
100
|
+
}
|
|
101
|
+
get location() {
|
|
102
|
+
return expandNodeLocations([this.left.expression.location, this.right.expression.location]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export class InvalidExpressionError extends Error {
|
|
106
|
+
constructor(message) {
|
|
107
|
+
super(message);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/compiler/filter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAmB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAqBnD,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,KAAK,YAAY,0BAA0B,IAAI,KAAK,YAAY,YAAY,CAAC;AACtF,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,0BAA0B;IAC5B,UAAU,CAAiB;IACpC;;;;OAIG;IACM,SAAS,CAAyB;IAE3C;;OAEG;IACM,mBAAmB,CAAU;IAEtC,YAAY,UAAuD;QACjE,IAAI,UAAU,YAAY,cAAc,EAAE,CAAC;YACzC,MAAM,OAAO,GAAG,0BAA0B,CAAC,uBAAuB,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;YAC7F,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;gBACpB,MAAM,IAAI,sBAAsB,CAAC,4EAA4E,CAAC,CAAC;YACjH,CAAC;YAED,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;YAC7B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC5B,IAAI,CAAC,mBAAmB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU,CAAC;YACxC,IAAI,CAAC,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;YACtC,IAAI,CAAC,mBAAmB,GAAG,UAAU,CAAC,mBAAmB,CAAC;QAC5D,CAAC;IACH,CAAC;IAED,2BAA2B,CAAC,KAA8B;QACxD,OAAO,KAAK,YAAY,0BAA0B,IAAI,KAAK,CAAC,UAAU,CAAC,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtH,CAAC;IAED,qCAAqC,CAAC,MAAoB;QACxD,IAAI,CAAC,UAAU,CAAC,qCAAqC,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;IAED;;;;;OAKG;IACH,MAAM,CAAC,uBAAuB,CAAC,MAAiC;QAC9D,IAAI,SAAS,GAA2B,IAAI,CAAC;QAC7C,IAAI,yBAAyB,GAAG,KAAK,CAAC;QAEtC,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC;YAChC,IAAI,UAAU,YAAY,WAAW,EAAE,CAAC;gBACtC,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,SAAS,KAAK,UAAU,CAAC,SAAS,CAAC,IAAI,yBAAyB,EAAE,CAAC;oBAC3F,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,SAAS,GAAG,UAAU,CAAC,SAAS,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;oBACtB,OAAO,IAAI,CAAC;gBACd,CAAC;gBAED,yBAAyB,GAAG,IAAI,CAAC;YACnC,CAAC;QACH,CAAC;QAED,OAAO,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;IAChD,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,0BAA0B;IAG3D,YAAY,UAAuD;QACjE,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;YAC3B,MAAM,IAAI,sBAAsB,CAAC,wCAAwC,CAAC,CAAC;QAC7E,CAAC;IACH,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,0BAA0B;IAC/D,YAAY,UAAuD;QACjE,KAAK,CAAC,UAAU,CAAC,CAAC;QAClB,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;YAC3B,MAAM,IAAI,sBAAsB,CAAC,oCAAoC,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,YAAY;IAEZ;IACA;IAFX,YACW,IAAgC,EAChC,KAAiC;QADjC,SAAI,GAAJ,IAAI,CAA4B;QAChC,UAAK,GAAL,KAAK,CAA4B;IACzC,CAAC;IAEJ,IAAI,QAAQ;QACV,OAAO,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9F,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Or } from './filter.js';
|
|
2
|
+
import { NodeLocations } from './expression.js';
|
|
3
|
+
import { BinaryOperator, SqlExpression } from '../sync_plan/expression.js';
|
|
4
|
+
export declare class FilterConditionSimplifier {
|
|
5
|
+
simplifyOr(or: Or): Or;
|
|
6
|
+
private simplifyAnd;
|
|
7
|
+
/**
|
|
8
|
+
* Re-orders terms based on their dependencies, and then uses the given operator to merge expressions with the same
|
|
9
|
+
* dependency set.
|
|
10
|
+
*
|
|
11
|
+
* This is used to push boolean expressions down if they can be evaluated on the same table. For instance, the and
|
|
12
|
+
* terms `[row.foo, row.bar]` can be represented as a single base term `row.foo AND row.bar`. Pushing operators down
|
|
13
|
+
* into the expression generally reduces the complexity of the sync plan. In particular for `OR` terms, it can also
|
|
14
|
+
* reduce the amount of buckets since each term in an `OR` is implemented as another bucket.
|
|
15
|
+
*/
|
|
16
|
+
private mergeByCommonDependencies;
|
|
17
|
+
private simplifyBase;
|
|
18
|
+
/**
|
|
19
|
+
* Reduces expressions through a chain of binary operators.
|
|
20
|
+
*
|
|
21
|
+
* For instance, `composeExpressions('AND', a, b, c)` returns `a AND b AND c` as a single expression. All expressions
|
|
22
|
+
* must have compatible dependencies.
|
|
23
|
+
*/
|
|
24
|
+
private composeExpressions;
|
|
25
|
+
}
|
|
26
|
+
export declare function composeExpressionNodes<T>(locations: NodeLocations, operator: BinaryOperator, terms: SqlExpression<T>[]): SqlExpression<T>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { EqualsClause, isBaseTerm, SingleDependencyExpression } from './filter.js';
|
|
2
|
+
import { SyncExpression } from './expression.js';
|
|
3
|
+
import { expandNodeLocations } from '../errors.js';
|
|
4
|
+
export class FilterConditionSimplifier {
|
|
5
|
+
simplifyOr(or) {
|
|
6
|
+
const andTerms = [];
|
|
7
|
+
let baseTerms = [];
|
|
8
|
+
for (const term of or.terms) {
|
|
9
|
+
const simplified = this.simplifyAnd(term);
|
|
10
|
+
if (isBaseTerm(simplified)) {
|
|
11
|
+
baseTerms.push(simplified);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
andTerms.push(simplified);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
baseTerms = this.mergeByCommonDependencies('or', baseTerms);
|
|
18
|
+
for (const term of baseTerms) {
|
|
19
|
+
andTerms.push({ terms: [term] });
|
|
20
|
+
}
|
|
21
|
+
return { terms: andTerms };
|
|
22
|
+
}
|
|
23
|
+
simplifyAnd(and) {
|
|
24
|
+
const merged = this.mergeByCommonDependencies('and', and.terms);
|
|
25
|
+
if (merged.length == 1) {
|
|
26
|
+
return merged[0];
|
|
27
|
+
}
|
|
28
|
+
return { terms: merged };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Re-orders terms based on their dependencies, and then uses the given operator to merge expressions with the same
|
|
32
|
+
* dependency set.
|
|
33
|
+
*
|
|
34
|
+
* This is used to push boolean expressions down if they can be evaluated on the same table. For instance, the and
|
|
35
|
+
* terms `[row.foo, row.bar]` can be represented as a single base term `row.foo AND row.bar`. Pushing operators down
|
|
36
|
+
* into the expression generally reduces the complexity of the sync plan. In particular for `OR` terms, it can also
|
|
37
|
+
* reduce the amount of buckets since each term in an `OR` is implemented as another bucket.
|
|
38
|
+
*/
|
|
39
|
+
mergeByCommonDependencies(operator, baseTerms) {
|
|
40
|
+
const byResultSet = new Map();
|
|
41
|
+
const noResultSet = [];
|
|
42
|
+
const rest = [];
|
|
43
|
+
for (const term of baseTerms) {
|
|
44
|
+
const simplified = this.simplifyBase(term);
|
|
45
|
+
if (simplified instanceof SingleDependencyExpression) {
|
|
46
|
+
if (simplified.resultSet != null) {
|
|
47
|
+
if (byResultSet.has(simplified.resultSet)) {
|
|
48
|
+
byResultSet.get(simplified.resultSet).push(simplified);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
byResultSet.set(simplified.resultSet, [simplified]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
noResultSet.push(simplified);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
rest.push(term);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const addMerged = (elements) => {
|
|
63
|
+
if (elements.length == 0) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
rest.push(this.composeExpressions(operator, ...elements));
|
|
67
|
+
};
|
|
68
|
+
addMerged(noResultSet);
|
|
69
|
+
for (const terms of byResultSet.values()) {
|
|
70
|
+
addMerged(terms);
|
|
71
|
+
}
|
|
72
|
+
return rest;
|
|
73
|
+
}
|
|
74
|
+
simplifyBase(base) {
|
|
75
|
+
if (base instanceof EqualsClause) {
|
|
76
|
+
// If the left and right terms have shared dependencies, we shouldn't represent this as an equals clause. For
|
|
77
|
+
// instance, terms like `notes.state = 'public'` are generated as an equals clause initially but they can just be
|
|
78
|
+
// a row condition that is much cheaper to compute than a static bucket parameter. Similarly, `row.foo = row.bar`
|
|
79
|
+
// must be a row condition since it can't be represented as parameters that could be instantiated.
|
|
80
|
+
if (SingleDependencyExpression.extractSingleDependency([
|
|
81
|
+
...base.left.expression.instantiation,
|
|
82
|
+
...base.right.expression.instantiation
|
|
83
|
+
])) {
|
|
84
|
+
return this.composeExpressions('=', base.left, base.right);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return base;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Reduces expressions through a chain of binary operators.
|
|
91
|
+
*
|
|
92
|
+
* For instance, `composeExpressions('AND', a, b, c)` returns `a AND b AND c` as a single expression. All expressions
|
|
93
|
+
* must have compatible dependencies.
|
|
94
|
+
*/
|
|
95
|
+
composeExpressions(operator, ...terms) {
|
|
96
|
+
if (terms.length == 0) {
|
|
97
|
+
throw new Error("Can't compose zero expressions");
|
|
98
|
+
}
|
|
99
|
+
const locations = terms[0].expression.locations;
|
|
100
|
+
const inner = composeExpressionNodes(locations, operator, terms.map((t) => t.expression.node));
|
|
101
|
+
return new SingleDependencyExpression(new SyncExpression(inner, locations));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function composeExpressionNodes(locations, operator, terms) {
|
|
105
|
+
if (terms.length == 0) {
|
|
106
|
+
throw new Error("Can't compose zero expressions");
|
|
107
|
+
}
|
|
108
|
+
const [first, ...rest] = terms;
|
|
109
|
+
let inner = first;
|
|
110
|
+
for (const additional of rest) {
|
|
111
|
+
inner = { type: 'binary', operator, left: inner, right: additional };
|
|
112
|
+
}
|
|
113
|
+
const location = expandNodeLocations(terms.map((e) => locations.locationFor(e)));
|
|
114
|
+
if (location) {
|
|
115
|
+
locations.sourceForNode.set(inner, location);
|
|
116
|
+
}
|
|
117
|
+
return inner;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=filter_simplifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter_simplifier.js","sourceRoot":"","sources":["../../src/compiler/filter_simplifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as plan from '../sync_plan/plan.js';
|
|
2
|
+
import { CompiledStreamQueries } from './compiler.js';
|
|
3
|
+
export declare class CompilerModelToSyncPlan {
|
|
4
|
+
private static readonly evaluatorHash;
|
|
5
|
+
private mappedObjects;
|
|
6
|
+
private buckets;
|
|
7
|
+
/**
|
|
8
|
+
* Mapping of row evaluators to buckets.
|
|
9
|
+
*
|
|
10
|
+
* One might expect one stream to result in one bucket, but that is not generally the case. First, a stream might
|
|
11
|
+
* define multiple buckets. For instance, `SELECT * FROM notes WHERE notes.is_public OR auth.parameter('admin')`
|
|
12
|
+
* requires two buckets since there are different ways a row in `notes` might be synced (we have one bucket with
|
|
13
|
+
* public notes and one bucket of all notes, and then decide which ones a given user has access to by inspecting the
|
|
14
|
+
* token).
|
|
15
|
+
*
|
|
16
|
+
* Further, a stream might have multiple evaluators but only a single bucket. A stream with queries
|
|
17
|
+
* `SELECT * FROM foo` and `SELECT * FROM bar` is an example for that, we want to merge `foo` and `bar` into the same
|
|
18
|
+
* bucket in this case. This is represented by a {@link resolver.StreamResolver} having multiple evaluators attached
|
|
19
|
+
* to it.
|
|
20
|
+
*
|
|
21
|
+
* Finally, we may even be able to re-use buckets between streams. This is not possible in many cases, but can be done
|
|
22
|
+
* if e.g. one stream is `SELECT * FROM profiles WHERE user = auth.user_id()` and another one is
|
|
23
|
+
* `SELECT * FROM profiles WHERE user IN (SELECT member FROM orgs WHERE id = auth.parameter('org'))`. Because the
|
|
24
|
+
* partitioning on `profiles` is the same in both cases, it doesn't matter how the buckets are instantiated.
|
|
25
|
+
*/
|
|
26
|
+
private evaluatorsToBuckets;
|
|
27
|
+
private translateStatefulObject;
|
|
28
|
+
translate(source: CompiledStreamQueries): plan.SyncPlan;
|
|
29
|
+
private createBucketSource;
|
|
30
|
+
private translatePartitionKey;
|
|
31
|
+
private translateRowEvaluator;
|
|
32
|
+
private translatePointLookup;
|
|
33
|
+
private translateExpression;
|
|
34
|
+
private translateStreamResolver;
|
|
35
|
+
private translateExpandingLookup;
|
|
36
|
+
private translateParameterValue;
|
|
37
|
+
}
|