@powersync/service-sync-rules 0.39.0 → 0.41.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/dist/HydratedSyncConfig.d.ts +9 -2
- package/dist/HydratedSyncConfig.js +12 -1
- package/dist/HydratedSyncConfig.js.map +1 -1
- package/dist/HydrationState.d.ts +2 -0
- package/dist/HydrationState.js.map +1 -1
- package/dist/StorageVersion.js +2 -1
- package/dist/StorageVersion.js.map +1 -1
- package/dist/SyncConfig.d.ts +4 -3
- package/dist/SyncConfig.js +10 -11
- package/dist/SyncConfig.js.map +1 -1
- package/dist/TablePattern.d.ts +2 -0
- package/dist/TablePattern.js +18 -1
- package/dist/TablePattern.js.map +1 -1
- package/dist/compiler/bucket_resolver.d.ts +1 -0
- package/dist/compiler/bucket_resolver.js +22 -12
- package/dist/compiler/bucket_resolver.js.map +1 -1
- package/dist/compiler/compatibility.d.ts +38 -9
- package/dist/compiler/compatibility.js +81 -11
- package/dist/compiler/compatibility.js.map +1 -1
- package/dist/compiler/compiler.d.ts +81 -13
- package/dist/compiler/compiler.js +111 -15
- package/dist/compiler/compiler.js.map +1 -1
- package/dist/compiler/expression.d.ts +35 -12
- package/dist/compiler/expression.js +49 -13
- package/dist/compiler/expression.js.map +1 -1
- package/dist/compiler/filter.d.ts +4 -4
- package/dist/compiler/filter.js +7 -6
- package/dist/compiler/filter.js.map +1 -1
- package/dist/compiler/ir_to_sync_plan.d.ts +4 -2
- package/dist/compiler/ir_to_sync_plan.js +47 -37
- package/dist/compiler/ir_to_sync_plan.js.map +1 -1
- package/dist/compiler/parser.js +3 -3
- package/dist/compiler/parser.js.map +1 -1
- package/dist/compiler/querier_graph.js +45 -26
- package/dist/compiler/querier_graph.js.map +1 -1
- package/dist/compiler/rows.d.ts +23 -29
- package/dist/compiler/rows.js +83 -59
- package/dist/compiler/rows.js.map +1 -1
- package/dist/compiler/sqlite.d.ts +3 -2
- package/dist/compiler/sqlite.js +33 -3
- package/dist/compiler/sqlite.js.map +1 -1
- package/dist/compiler/table.d.ts +3 -2
- package/dist/compiler/table.js +9 -7
- package/dist/compiler/table.js.map +1 -1
- package/dist/events/CompiledEventSourceQuery.d.ts +51 -0
- package/dist/events/CompiledEventSourceQuery.js +119 -0
- package/dist/events/CompiledEventSourceQuery.js.map +1 -0
- package/dist/events/EventDescriptor.d.ts +26 -0
- package/dist/events/EventDescriptor.js +2 -0
- package/dist/events/EventDescriptor.js.map +1 -0
- package/dist/from_yaml.js +157 -189
- package/dist/from_yaml.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/json_schema.js +1 -1
- package/dist/json_schema.js.map +1 -1
- package/dist/sync_plan/engine/scalar_expression_engine.js +7 -0
- package/dist/sync_plan/engine/scalar_expression_engine.js.map +1 -1
- package/dist/sync_plan/evaluator/bucket_data_source.js +11 -52
- package/dist/sync_plan/evaluator/bucket_data_source.js.map +1 -1
- package/dist/sync_plan/evaluator/index.d.ts +2 -7
- package/dist/sync_plan/evaluator/index.js +6 -7
- package/dist/sync_plan/evaluator/index.js.map +1 -1
- package/dist/sync_plan/evaluator/parameter_evaluator.js +11 -7
- package/dist/sync_plan/evaluator/parameter_evaluator.js.map +1 -1
- package/dist/sync_plan/evaluator/parameter_index_lookup_creator.js +2 -2
- package/dist/sync_plan/evaluator/parameter_index_lookup_creator.js.map +1 -1
- package/dist/sync_plan/evaluator/row_projection.d.ts +22 -0
- package/dist/sync_plan/evaluator/row_projection.js +71 -0
- package/dist/sync_plan/evaluator/row_projection.js.map +1 -0
- package/dist/sync_plan/evaluator/table_processor_to_sql.d.ts +5 -2
- package/dist/sync_plan/evaluator/table_processor_to_sql.js +10 -0
- package/dist/sync_plan/evaluator/table_processor_to_sql.js.map +1 -1
- package/dist/sync_plan/plan.d.ts +49 -9
- package/dist/sync_plan/plan_equality_serialized.d.ts +12 -2
- package/dist/sync_plan/plan_equality_serialized.js +56 -0
- package/dist/sync_plan/plan_equality_serialized.js.map +1 -1
- package/dist/sync_plan/serialize.d.ts +54 -5
- package/dist/sync_plan/serialize.js +109 -19
- package/dist/sync_plan/serialize.js.map +1 -1
- package/dist/yaml_validation.d.ts +73 -0
- package/dist/yaml_validation.js +140 -0
- package/dist/yaml_validation.js.map +1 -0
- package/package.json +1 -1
- package/schema/sync_rules.json +3 -2
- package/dist/events/SqlEventDescriptor.d.ts +0 -20
- package/dist/events/SqlEventDescriptor.js +0 -51
- package/dist/events/SqlEventDescriptor.js.map +0 -1
- package/dist/events/SqlEventSourceQuery.d.ts +0 -20
- package/dist/events/SqlEventSourceQuery.js +0 -125
- package/dist/events/SqlEventSourceQuery.js.map +0 -1
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { filterJsonRow, isJsonValue } from '../../utils.js';
|
|
2
|
+
import { scalarStatementToSql } from '../engine/scalar_expression_engine.js';
|
|
3
|
+
import { resolveRowMetadata, TableProcessorToSqlHelper } from './table_processor_to_sql.js';
|
|
4
|
+
/**
|
|
5
|
+
* Prepares the shared projection/filter portion of bucket and event row evaluators.
|
|
6
|
+
*/
|
|
7
|
+
export class PendingRowProjection {
|
|
8
|
+
tablePattern;
|
|
9
|
+
outputs = [];
|
|
10
|
+
numberOfOutputExpressions;
|
|
11
|
+
numberOfParameters;
|
|
12
|
+
evaluatorInputs;
|
|
13
|
+
statement;
|
|
14
|
+
constructor(evaluator, defaultSchema) {
|
|
15
|
+
const translationHelper = new TableProcessorToSqlHelper(evaluator);
|
|
16
|
+
const outputExpressions = [];
|
|
17
|
+
for (const column of evaluator.columns) {
|
|
18
|
+
if (column === 'star') {
|
|
19
|
+
this.outputs.push('star');
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const expressionIndex = outputExpressions.length;
|
|
23
|
+
outputExpressions.push(translationHelper.mapper.transform(column.expr));
|
|
24
|
+
this.outputs.push({ index: expressionIndex, alias: column.alias });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
this.numberOfOutputExpressions = outputExpressions.length;
|
|
28
|
+
for (const parameter of evaluator.parameters) {
|
|
29
|
+
outputExpressions.push(translationHelper.mapper.transform(parameter.expr));
|
|
30
|
+
}
|
|
31
|
+
this.numberOfParameters = evaluator.parameters.length;
|
|
32
|
+
this.statement = {
|
|
33
|
+
outputs: outputExpressions,
|
|
34
|
+
filters: translationHelper.filterExpressions,
|
|
35
|
+
tableValuedFunctions: translationHelper.tableValuedFunctions
|
|
36
|
+
};
|
|
37
|
+
this.tablePattern = evaluator.sourceTable.toTablePattern(defaultSchema);
|
|
38
|
+
this.evaluatorInputs = translationHelper.mapper.instantiation;
|
|
39
|
+
}
|
|
40
|
+
get debugSql() {
|
|
41
|
+
return scalarStatementToSql(this.statement);
|
|
42
|
+
}
|
|
43
|
+
instantiate(engine) {
|
|
44
|
+
const evaluator = engine.prepareEvaluator(this.statement);
|
|
45
|
+
const pattern = this.tablePattern;
|
|
46
|
+
return (options) => {
|
|
47
|
+
const inputInstantiation = this.evaluatorInputs.map((input) => 'column' in input ? options.record[input.column] : resolveRowMetadata(input, pattern, options.sourceTable));
|
|
48
|
+
const results = [];
|
|
49
|
+
for (const source of evaluator.evaluate(inputInstantiation)) {
|
|
50
|
+
const record = {};
|
|
51
|
+
for (const output of this.outputs) {
|
|
52
|
+
if (output === 'star') {
|
|
53
|
+
Object.assign(record, filterJsonRow(options.record));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const value = source[output.index];
|
|
57
|
+
if (isJsonValue(value)) {
|
|
58
|
+
record[output.alias] = value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
results.push({
|
|
63
|
+
data: record,
|
|
64
|
+
partitionValues: source.slice(this.numberOfOutputExpressions, this.numberOfOutputExpressions + this.numberOfParameters)
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return results;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=row_projection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row_projection.js","sourceRoot":"","sources":["../../../src/sync_plan/evaluator/row_projection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { SourceTableRef } from '../../SourceTableRef.js';
|
|
2
|
+
import { TablePattern } from '../../TablePattern.js';
|
|
1
3
|
import { TableValuedFunction, TableValuedFunctionOutput } from '../engine/scalar_expression_engine.js';
|
|
2
4
|
import { SqlExpression } from '../expression.js';
|
|
3
5
|
import * as plan from '../plan.js';
|
|
@@ -9,12 +11,13 @@ import * as plan from '../plan.js';
|
|
|
9
11
|
*/
|
|
10
12
|
export declare class TableProcessorToSqlHelper {
|
|
11
13
|
mapper: {
|
|
12
|
-
instantiation: plan.ColumnSqlParameterValue[];
|
|
14
|
+
instantiation: (plan.ColumnSqlParameterValue | plan.RowMetadataSqlValue)[];
|
|
13
15
|
tableValuedFunctions: Map<plan.TableProcessorTableValuedFunction, TableValuedFunction>;
|
|
14
16
|
transform(expr: SqlExpression<plan.SqlParameterValue>): SqlExpression<number | TableValuedFunctionOutput>;
|
|
15
|
-
transformWithoutTableValued(expr: SqlExpression<plan.ColumnSqlParameterValue>): SqlExpression<number>;
|
|
17
|
+
transformWithoutTableValued(expr: SqlExpression<plan.ColumnSqlParameterValue | plan.RowMetadataSqlValue>): SqlExpression<number>;
|
|
16
18
|
};
|
|
17
19
|
readonly filterExpressions: SqlExpression<number | TableValuedFunctionOutput>[];
|
|
18
20
|
get tableValuedFunctions(): TableValuedFunction[];
|
|
19
21
|
constructor(source: plan.TableProcessor);
|
|
20
22
|
}
|
|
23
|
+
export declare function resolveRowMetadata(value: plan.RowMetadataSqlValue, pattern: TablePattern, table: SourceTableRef): string;
|
|
@@ -25,4 +25,14 @@ export class TableProcessorToSqlHelper {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
+
export function resolveRowMetadata(value, pattern, table) {
|
|
29
|
+
switch (value.metadata) {
|
|
30
|
+
case 'schema':
|
|
31
|
+
return table.schema;
|
|
32
|
+
case 'table_name':
|
|
33
|
+
return table.name;
|
|
34
|
+
case 'table_suffix':
|
|
35
|
+
return pattern.isWildcard ? pattern.suffix(table.name) : '';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
28
38
|
//# sourceMappingURL=table_processor_to_sql.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table_processor_to_sql.js","sourceRoot":"","sources":["../../../src/sync_plan/evaluator/table_processor_to_sql.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"table_processor_to_sql.js","sourceRoot":"","sources":["../../../src/sync_plan/evaluator/table_processor_to_sql.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,8BAA8B,EAG/B,MAAM,uCAAuC,CAAC;AAI/C;;;;;GAKG;AACH,MAAM,OAAO,yBAAyB;IACpC,MAAM,GAAG,8BAA8B,EAA2D,CAAC;IAC1F,iBAAiB,GAAwD,EAAE,CAAC;IAErF,IAAI,oBAAoB;QACtB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,YAAY,MAA2B;QACrC,yCAAyC;QACzC,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,oBAAoB,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAwB;gBAClC,IAAI,EAAE,EAAE,CAAC,YAAY;gBACrB,MAAM,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC,CAAC,CAAC;aACjF,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACnD,CAAC;QAED,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAChC,KAA+B,EAC/B,OAAqB,EACrB,KAAqB;IAErB,QAAQ,KAAK,CAAC,QAAQ,EAAE,CAAC;QACvB,KAAK,QAAQ;YACX,OAAO,KAAK,CAAC,MAAM,CAAC;QACtB,KAAK,YAAY;YACf,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,KAAK,cAAc;YACjB,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,CAAC;AACH,CAAC"}
|
package/dist/sync_plan/plan.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export interface SyncPlan {
|
|
|
24
24
|
buckets: StreamBucketDataSource[];
|
|
25
25
|
parameterIndexes: StreamParameterIndexLookupCreator[];
|
|
26
26
|
streams: CompiledSyncStream[];
|
|
27
|
+
events: CompiledEventDescriptor[];
|
|
27
28
|
}
|
|
28
29
|
/**
|
|
29
30
|
* Something that processes a source row if it matches filters.
|
|
@@ -88,10 +89,10 @@ export interface TableProcessorTableValuedFunctionOutput {
|
|
|
88
89
|
/**
|
|
89
90
|
* External data that can be used in expressions for {@link TableProcessor}s.
|
|
90
91
|
*
|
|
91
|
-
* This allows references to the row in the table being processed,
|
|
92
|
-
* {@link TableProcessor#tableValuedFunctions}.
|
|
92
|
+
* This allows references to the row in the table being processed, metadata about the row's source table, and
|
|
93
|
+
* references to outputs of joined {@link TableProcessor#tableValuedFunctions}.
|
|
93
94
|
*/
|
|
94
|
-
export type TableProcessorData = ColumnSqlParameterValue | TableProcessorTableValuedFunctionOutput;
|
|
95
|
+
export type TableProcessorData = ColumnSqlParameterValue | RowMetadataSqlValue | TableProcessorTableValuedFunctionOutput;
|
|
95
96
|
/**
|
|
96
97
|
* A scalar partition key evaluates to a single value in the source or parameter row. For instance, a stream definition
|
|
97
98
|
* like `SELECT * FROM users WHERE id = auth.user_id()` would generate a scalar key to partition users by the `id`
|
|
@@ -107,22 +108,52 @@ export interface PartitionKey {
|
|
|
107
108
|
* duplicate bucket data but can still share the actual processing logic between multiple streams (to avoid evaluating
|
|
108
109
|
* the same filters and expressions multiple times).
|
|
109
110
|
*/
|
|
110
|
-
export interface
|
|
111
|
+
export interface RowProjection extends TableProcessor {
|
|
112
|
+
/**
|
|
113
|
+
* Output columns produced when a matching source row is evaluated.
|
|
114
|
+
*/
|
|
115
|
+
columns: ColumnSource[];
|
|
116
|
+
}
|
|
117
|
+
export interface StreamDataSource extends RowProjection {
|
|
111
118
|
/**
|
|
112
119
|
* The name of the output table for evaluated rows.
|
|
113
120
|
*
|
|
114
121
|
* If null, the name of the table being evaluated should be used instead.
|
|
115
122
|
*/
|
|
116
123
|
outputTableName?: string;
|
|
117
|
-
/**
|
|
118
|
-
* Output columns describing the row to store in buckets.
|
|
119
|
-
*/
|
|
120
|
-
columns: ColumnSource[];
|
|
121
124
|
}
|
|
122
125
|
export type ColumnSource = 'star' | {
|
|
123
126
|
expr: SqlExpression<TableProcessorData>;
|
|
124
127
|
alias: string;
|
|
125
128
|
};
|
|
129
|
+
/**
|
|
130
|
+
* A named replication event compiled from `event_definitions`.
|
|
131
|
+
*/
|
|
132
|
+
export interface CompiledEventDescriptor {
|
|
133
|
+
name: string;
|
|
134
|
+
sourceQueries: CompiledEventSourceQuery[];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* A single payload query for an event.
|
|
138
|
+
*
|
|
139
|
+
* Event payload queries are restricted to one physical source table. A query can have multiple variants after its
|
|
140
|
+
* filter has been normalized to disjunctive normal form. Evaluation stops after the first matching variant, which may
|
|
141
|
+
* itself produce multiple payload rows, for example through a table-valued expansion.
|
|
142
|
+
*/
|
|
143
|
+
export interface CompiledEventSourceQuery {
|
|
144
|
+
/**
|
|
145
|
+
* Original SQL retained as a compatibility mirror for services using the legacy event evaluator. Compiled event
|
|
146
|
+
* evaluation uses the remaining fields.
|
|
147
|
+
*/
|
|
148
|
+
sql: string;
|
|
149
|
+
sourceTable: ImplicitSchemaTablePattern;
|
|
150
|
+
variants: EventRowEvaluator[];
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* A row projection used by one normalized filter variant of an event payload query.
|
|
154
|
+
*/
|
|
155
|
+
export interface EventRowEvaluator extends RowProjection {
|
|
156
|
+
}
|
|
126
157
|
/**
|
|
127
158
|
* A mapping describing how {@link StreamDataSource}s are combined into buckets.
|
|
128
159
|
*
|
|
@@ -197,6 +228,15 @@ export interface StreamQuerier {
|
|
|
197
228
|
export interface ColumnSqlParameterValue {
|
|
198
229
|
column: string;
|
|
199
230
|
}
|
|
231
|
+
export type RowMetadataKind = 'schema' | 'table_name' | 'table_suffix';
|
|
232
|
+
/**
|
|
233
|
+
* A value that resolves to metadata about the source table of the row being processed: the schema containing the
|
|
234
|
+
* table (`schema()`), the name of the table (`table_name()`) or, for wildcard table patterns, the matched
|
|
235
|
+
* suffix of the table name (`table_suffix()`).
|
|
236
|
+
*/
|
|
237
|
+
export interface RowMetadataSqlValue {
|
|
238
|
+
metadata: RowMetadataKind;
|
|
239
|
+
}
|
|
200
240
|
export type ConnectionParameterSource = 'auth' | 'subscription' | 'connection';
|
|
201
241
|
/**
|
|
202
242
|
* A value that resolves to either the current subscription parameters, the JWT of the connecting user or global
|
|
@@ -205,7 +245,7 @@ export type ConnectionParameterSource = 'auth' | 'subscription' | 'connection';
|
|
|
205
245
|
export interface RequestSqlParameterValue {
|
|
206
246
|
request: ConnectionParameterSource;
|
|
207
247
|
}
|
|
208
|
-
export type SqlParameterValue = ColumnSqlParameterValue | RequestSqlParameterValue | TableProcessorTableValuedFunctionOutput;
|
|
248
|
+
export type SqlParameterValue = ColumnSqlParameterValue | RowMetadataSqlValue | RequestSqlParameterValue | TableProcessorTableValuedFunctionOutput;
|
|
209
249
|
/**
|
|
210
250
|
* A lookup returning multiple rows when instantiated.
|
|
211
251
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Equality } from '../compiler/equality.js';
|
|
2
|
-
import { SerializedBucketDataSource, SerializedDataSource, SerializedParameterIndexLookupCreator } from './serialize.js';
|
|
1
|
+
import type { Equality } from '../compiler/equality.js';
|
|
2
|
+
import type { SerializedBucketDataSource, SerializedDataSource, SerializedEventDescriptor, SerializedParameterIndexLookupCreator } from './serialize.js';
|
|
3
3
|
export interface SerializedBucketDataSourceWithDataSources {
|
|
4
4
|
bucket: SerializedBucketDataSource;
|
|
5
5
|
dataSources: readonly SerializedDataSource[];
|
|
@@ -13,6 +13,16 @@ export interface SerializedBucketDataSourceWithDataSources {
|
|
|
13
13
|
* These are only considered equal if the lookupName remains the same, so may be affected by changing order of queries.
|
|
14
14
|
*/
|
|
15
15
|
export declare const serializedStreamParameterIndexLookupCreatorEquality: Equality<SerializedParameterIndexLookupCreator>;
|
|
16
|
+
/**
|
|
17
|
+
* Equality for persisted replication events.
|
|
18
|
+
*
|
|
19
|
+
* Raw SQL is a rolling-upgrade compatibility mirror and cached evaluator hashes are not equality checks, so neither is
|
|
20
|
+
* part of event behavior. Payload queries are unordered because runtime evaluates every query matching the source
|
|
21
|
+
* table and treats the resulting payloads as an unordered collection. Normalized variants are unordered because they
|
|
22
|
+
* share a projection and only determine whether a row matches. The remaining serialized plan is a stable,
|
|
23
|
+
* self-contained behavior representation: Expression ASTs include both their shape and external-data bindings.
|
|
24
|
+
*/
|
|
25
|
+
export declare const serializedEventDefinitionEquality: Equality<SerializedEventDescriptor>;
|
|
16
26
|
/**
|
|
17
27
|
* SerializedBucketDataSource is not safe to compare _directly_, since it contains index references to SerializedDataSource
|
|
18
28
|
* in the serialized sync plan. However, each SerializedDataSource is self-contained and safe to compare directly.
|
|
@@ -7,6 +7,23 @@
|
|
|
7
7
|
* These are only considered equal if the lookupName remains the same, so may be affected by changing order of queries.
|
|
8
8
|
*/
|
|
9
9
|
export const serializedStreamParameterIndexLookupCreatorEquality = jsonEquality();
|
|
10
|
+
/**
|
|
11
|
+
* Equality for persisted replication events.
|
|
12
|
+
*
|
|
13
|
+
* Raw SQL is a rolling-upgrade compatibility mirror and cached evaluator hashes are not equality checks, so neither is
|
|
14
|
+
* part of event behavior. Payload queries are unordered because runtime evaluates every query matching the source
|
|
15
|
+
* table and treats the resulting payloads as an unordered collection. Normalized variants are unordered because they
|
|
16
|
+
* share a projection and only determine whether a row matches. The remaining serialized plan is a stable,
|
|
17
|
+
* self-contained behavior representation: Expression ASTs include both their shape and external-data bindings.
|
|
18
|
+
*/
|
|
19
|
+
export const serializedEventDefinitionEquality = {
|
|
20
|
+
hash(hasher, value) {
|
|
21
|
+
hasher.addString(persistedJson(eventIdentity(value)));
|
|
22
|
+
},
|
|
23
|
+
equals(a, b) {
|
|
24
|
+
return a === b || persistedJson(eventIdentity(a)) == persistedJson(eventIdentity(b));
|
|
25
|
+
}
|
|
26
|
+
};
|
|
10
27
|
/**
|
|
11
28
|
* SerializedBucketDataSource is not safe to compare _directly_, since it contains index references to SerializedDataSource
|
|
12
29
|
* in the serialized sync plan. However, each SerializedDataSource is self-contained and safe to compare directly.
|
|
@@ -33,6 +50,45 @@ function bucketIdentity(value) {
|
|
|
33
50
|
sources: bucket.sources.map((index) => JSON.stringify(dataSources[index])).sort()
|
|
34
51
|
};
|
|
35
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Builds a normalized identity for comparing serialized event descriptors from persisted sync plans.
|
|
55
|
+
*
|
|
56
|
+
* Raw SQL is excluded, while source queries and variants are sorted because their order does not affect event
|
|
57
|
+
* behavior.
|
|
58
|
+
*/
|
|
59
|
+
function eventIdentity(event) {
|
|
60
|
+
return {
|
|
61
|
+
name: event.name,
|
|
62
|
+
sourceQueries: event.sourceQueries
|
|
63
|
+
.map((query) => persistedJson({
|
|
64
|
+
table: query.table,
|
|
65
|
+
variants: query.variants.map((variant) => persistedJson(eventRowEvaluatorIdentity(variant))).sort()
|
|
66
|
+
}))
|
|
67
|
+
.sort()
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Selects the behavioral fields used when comparing row evaluators within serialized event descriptors.
|
|
72
|
+
*
|
|
73
|
+
* The cached evaluator hash is deliberately omitted. Ordering inside an evaluator is retained conservatively so
|
|
74
|
+
* changes to projected column order or the compiler's filter-expression order invalidate persisted compatibility.
|
|
75
|
+
*/
|
|
76
|
+
function eventRowEvaluatorIdentity(evaluator) {
|
|
77
|
+
return {
|
|
78
|
+
table: evaluator.table,
|
|
79
|
+
tableValuedFunctions: evaluator.tableValuedFunctions,
|
|
80
|
+
filters: evaluator.filters,
|
|
81
|
+
partitionBy: evaluator.partitionBy,
|
|
82
|
+
columns: evaluator.columns
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* MongoDB persists undefined object properties as null. Treat the two forms as the same so a plan compares equal
|
|
87
|
+
* before and after it has been stored and loaded again.
|
|
88
|
+
*/
|
|
89
|
+
function persistedJson(value) {
|
|
90
|
+
return JSON.stringify(value, (_key, item) => (item === undefined ? null : item));
|
|
91
|
+
}
|
|
36
92
|
function jsonEquality() {
|
|
37
93
|
return {
|
|
38
94
|
hash(hasher, value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan_equality_serialized.js","sourceRoot":"","sources":["../../src/sync_plan/plan_equality_serialized.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plan_equality_serialized.js","sourceRoot":"","sources":["../../src/sync_plan/plan_equality_serialized.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ParameterLookupDefinitionId } from '../HydrationState.js';
|
|
2
2
|
import { SqlExpression } from './expression.js';
|
|
3
|
-
import { ColumnSqlParameterValue, RequestSqlParameterValue, StreamOptions, SyncPlan, TableProcessorTableValuedFunction } from './plan.js';
|
|
3
|
+
import { ColumnSqlParameterValue, RequestSqlParameterValue, RowMetadataSqlValue, StreamOptions, SyncPlan, TableProcessorTableValuedFunction } from './plan.js';
|
|
4
4
|
/**
|
|
5
5
|
* Serializes a sync plan into a simple JSON object.
|
|
6
6
|
*
|
|
@@ -8,14 +8,40 @@ import { ColumnSqlParameterValue, RequestSqlParameterValue, StreamOptions, SyncP
|
|
|
8
8
|
* queriers to bucket creators. To represent this efficiently, we assign numbers to referenced elements while
|
|
9
9
|
* serializing instead of duplicating definitions.
|
|
10
10
|
*/
|
|
11
|
-
export declare function serializeSyncPlan(plan: SyncPlan):
|
|
11
|
+
export declare function serializeSyncPlan(plan: SyncPlan): SerializedSyncPlan;
|
|
12
12
|
export declare function deserializeSyncPlan(serialized: unknown): SyncPlan;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Changes to {@link SerializedSyncPlan} require a version bump when older services would interpret the plan
|
|
15
|
+
* incorrectly. Optional additive fields are only safe without a bump when older readers can ignore them while another
|
|
16
|
+
* persisted representation preserves equivalent behavior.
|
|
17
|
+
*
|
|
18
|
+
* Compiled `events` are an explicit additive exception: service-core continues to persist raw event SQL alongside the
|
|
19
|
+
* plan for the legacy evaluator. Older readers ignore `events` and use that legacy mirror. Removing the mirror or
|
|
20
|
+
* relying on compiled-only event semantics will require a version bump.
|
|
21
|
+
*
|
|
22
|
+
* ### Version 2
|
|
23
|
+
*
|
|
24
|
+
* - Add {@link RowMetadataSqlValue} to data for row and parameter evaluators, exposing the exact table and schema name
|
|
25
|
+
* when matching on wildcard table patterns.
|
|
26
|
+
* The deserialization logic can remain the same for v1 and v2.
|
|
27
|
+
*
|
|
28
|
+
* ### Version 1
|
|
29
|
+
*
|
|
30
|
+
* - Initial version
|
|
31
|
+
*/
|
|
32
|
+
export type SerializedSyncPlanVersion = 1 | 2;
|
|
33
|
+
export declare const maxSupportedSyncPlanVersion: SerializedSyncPlanVersion;
|
|
34
|
+
export interface SerializedSyncPlan {
|
|
35
|
+
version: SerializedSyncPlanVersion;
|
|
15
36
|
dataSources: SerializedDataSource[];
|
|
16
37
|
buckets: SerializedBucketDataSource[];
|
|
17
38
|
parameterIndexes: SerializedParameterIndexLookupCreator[];
|
|
18
39
|
streams: SerializedStream[];
|
|
40
|
+
/**
|
|
41
|
+
* Optional additive compiled event definitions. Older readers safely ignore this because service-core dual-writes
|
|
42
|
+
* equivalent raw SQL in its legacy `eventDescriptors` field.
|
|
43
|
+
*/
|
|
44
|
+
events?: SerializedEventDescriptor[];
|
|
19
45
|
}
|
|
20
46
|
export interface SerializedBucketDataSource {
|
|
21
47
|
hash: number;
|
|
@@ -31,7 +57,7 @@ export interface SerializedTableProcessorTableValuedFunctionOutput {
|
|
|
31
57
|
function: number;
|
|
32
58
|
outputName: string;
|
|
33
59
|
}
|
|
34
|
-
export type SerializedTableProcessorData = ColumnSqlParameterValue | SerializedTableProcessorTableValuedFunctionOutput;
|
|
60
|
+
export type SerializedTableProcessorData = ColumnSqlParameterValue | RowMetadataSqlValue | SerializedTableProcessorTableValuedFunctionOutput;
|
|
35
61
|
export interface SerializedPartitionKey {
|
|
36
62
|
expr: SqlExpression<SerializedTableProcessorData>;
|
|
37
63
|
}
|
|
@@ -48,6 +74,29 @@ export interface SerializedDataSource {
|
|
|
48
74
|
tableValuedFunctions: TableProcessorTableValuedFunction[];
|
|
49
75
|
partitionBy: SerializedPartitionKey[];
|
|
50
76
|
}
|
|
77
|
+
export interface SerializedEventDescriptor {
|
|
78
|
+
name: string;
|
|
79
|
+
sourceQueries: SerializedEventSourceQuery[];
|
|
80
|
+
}
|
|
81
|
+
export interface SerializedEventSourceQuery {
|
|
82
|
+
/** Raw SQL retained for the legacy compatibility mirror. */
|
|
83
|
+
sql: string;
|
|
84
|
+
table: SerializedTablePattern;
|
|
85
|
+
variants: SerializedEventRowEvaluator[];
|
|
86
|
+
}
|
|
87
|
+
export interface SerializedEventRowEvaluator {
|
|
88
|
+
table: SerializedTablePattern;
|
|
89
|
+
/**
|
|
90
|
+
* The compiler's structural hash, retained only for round-trip symmetry with data sources (which reuse the same
|
|
91
|
+
* projection shape). It is not part of event identity (the compiler model compares behavior directly), and events
|
|
92
|
+
* are never deduplicated by it at runtime.
|
|
93
|
+
*/
|
|
94
|
+
hash: number;
|
|
95
|
+
columns: SerializedColumnSource[];
|
|
96
|
+
filters: SqlExpression<SerializedTableProcessorData>[];
|
|
97
|
+
tableValuedFunctions: TableProcessorTableValuedFunction[];
|
|
98
|
+
partitionBy: SerializedPartitionKey[];
|
|
99
|
+
}
|
|
51
100
|
export interface SerializedParameterIndexLookupCreator {
|
|
52
101
|
table: SerializedTablePattern;
|
|
53
102
|
hash: number;
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import { ImplicitSchemaTablePattern, TablePattern } from '../TablePattern.js';
|
|
2
2
|
import { MapSourceVisitor, visitExpr } from './expression_visitor.js';
|
|
3
|
-
|
|
4
|
-
* Serializes a sync plan into a simple JSON object.
|
|
5
|
-
*
|
|
6
|
-
* While {@link SyncPlan}s are already serializable for the most part, it contains a graph of references from e.g.
|
|
7
|
-
* queriers to bucket creators. To represent this efficiently, we assign numbers to referenced elements while
|
|
8
|
-
* serializing instead of duplicating definitions.
|
|
9
|
-
*/
|
|
10
|
-
export function serializeSyncPlan(plan) {
|
|
11
|
-
const dataSourceIndex = new Map();
|
|
12
|
-
const bucketIndex = new Map();
|
|
13
|
-
const parameterIndex = new Map();
|
|
14
|
-
const expandingLookups = new Map();
|
|
3
|
+
function createTableProcessorSerializer() {
|
|
15
4
|
const addedTableValuedFunctions = new Map();
|
|
5
|
+
let usesRowMetadataSqlValue = false;
|
|
16
6
|
const replaceFunctionReferenceWithIndex = new MapSourceVisitor((value) => {
|
|
7
|
+
usesRowMetadataSqlValue ||= 'metadata' in value;
|
|
17
8
|
if ('function' in value) {
|
|
18
9
|
return { function: addedTableValuedFunctions.get(value.function), outputName: value.outputName };
|
|
19
10
|
}
|
|
@@ -34,7 +25,13 @@ export function serializeSyncPlan(plan) {
|
|
|
34
25
|
function serializeTableValued(source) {
|
|
35
26
|
return source.tableValuedFunctions.map((fn, i) => {
|
|
36
27
|
addedTableValuedFunctions.set(fn, i);
|
|
37
|
-
return
|
|
28
|
+
return {
|
|
29
|
+
functionName: fn.functionName,
|
|
30
|
+
functionInputs: fn.functionInputs.map(
|
|
31
|
+
// Since we don't support table-valued functions as inputs to other table-valued functions, this doesn't
|
|
32
|
+
// change expressions. It ensures we track row metadata use in any input, though.
|
|
33
|
+
(s) => serializeTableProcessorDataExpr(s))
|
|
34
|
+
};
|
|
38
35
|
});
|
|
39
36
|
}
|
|
40
37
|
function translateParameters(source) {
|
|
@@ -42,6 +39,56 @@ export function serializeSyncPlan(plan) {
|
|
|
42
39
|
return { expr: serializeTableProcessorDataExpr(key.expr) };
|
|
43
40
|
});
|
|
44
41
|
}
|
|
42
|
+
function serializeEventRowEvaluator(source) {
|
|
43
|
+
return {
|
|
44
|
+
hash: source.hashCode,
|
|
45
|
+
table: serializeTablePattern(source.sourceTable),
|
|
46
|
+
tableValuedFunctions: serializeTableValued(source),
|
|
47
|
+
filters: source.filters.map(serializeTableProcessorDataExpr),
|
|
48
|
+
partitionBy: translateParameters(source),
|
|
49
|
+
columns: source.columns.map((column) => {
|
|
50
|
+
if (column == 'star') {
|
|
51
|
+
return 'star';
|
|
52
|
+
}
|
|
53
|
+
return { expr: serializeTableProcessorDataExpr(column.expr), alias: column.alias };
|
|
54
|
+
})
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function serializeEventDefinition(event) {
|
|
58
|
+
return {
|
|
59
|
+
name: event.name,
|
|
60
|
+
sourceQueries: event.sourceQueries.map((query) => ({
|
|
61
|
+
sql: query.sql,
|
|
62
|
+
table: serializeTablePattern(query.sourceTable),
|
|
63
|
+
variants: query.variants.map(serializeEventRowEvaluator)
|
|
64
|
+
}))
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
get usesRowMetadataSqlValue() {
|
|
69
|
+
return usesRowMetadataSqlValue;
|
|
70
|
+
},
|
|
71
|
+
serializeTableProcessorDataExpr,
|
|
72
|
+
serializeTablePattern,
|
|
73
|
+
serializeTableValued,
|
|
74
|
+
translateParameters,
|
|
75
|
+
serializeEventDefinition
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Serializes a sync plan into a simple JSON object.
|
|
80
|
+
*
|
|
81
|
+
* While {@link SyncPlan}s are already serializable for the most part, it contains a graph of references from e.g.
|
|
82
|
+
* queriers to bucket creators. To represent this efficiently, we assign numbers to referenced elements while
|
|
83
|
+
* serializing instead of duplicating definitions.
|
|
84
|
+
*/
|
|
85
|
+
export function serializeSyncPlan(plan) {
|
|
86
|
+
const dataSourceIndex = new Map();
|
|
87
|
+
const bucketIndex = new Map();
|
|
88
|
+
const parameterIndex = new Map();
|
|
89
|
+
const expandingLookups = new Map();
|
|
90
|
+
const tableProcessorSerializer = createTableProcessorSerializer();
|
|
91
|
+
const { serializeTableProcessorDataExpr, serializeTablePattern, serializeTableValued, translateParameters } = tableProcessorSerializer;
|
|
45
92
|
function serializeDataSources() {
|
|
46
93
|
return plan.dataSources.map((source, i) => {
|
|
47
94
|
dataSourceIndex.set(source, i);
|
|
@@ -72,7 +119,7 @@ export function serializeSyncPlan(plan) {
|
|
|
72
119
|
tableValuedFunctions: serializeTableValued(source),
|
|
73
120
|
filters: source.filters.map(serializeTableProcessorDataExpr),
|
|
74
121
|
partitionBy: translateParameters(source),
|
|
75
|
-
output: source.outputs.map(
|
|
122
|
+
output: source.outputs.map(serializeTableProcessorDataExpr),
|
|
76
123
|
lookupScope: source.defaultLookupScope
|
|
77
124
|
};
|
|
78
125
|
});
|
|
@@ -124,8 +171,8 @@ export function serializeSyncPlan(plan) {
|
|
|
124
171
|
sourceInstantiation: source.sourceInstantiation.map(serializeParameterValue)
|
|
125
172
|
};
|
|
126
173
|
}
|
|
127
|
-
|
|
128
|
-
|
|
174
|
+
const events = plan.events.map(tableProcessorSerializer.serializeEventDefinition);
|
|
175
|
+
const serialized = {
|
|
129
176
|
dataSources: serializeDataSources(),
|
|
130
177
|
buckets: plan.buckets.map((bkt, index) => {
|
|
131
178
|
bucketIndex.set(bkt, index);
|
|
@@ -139,13 +186,24 @@ export function serializeSyncPlan(plan) {
|
|
|
139
186
|
streams: plan.streams.map((s) => ({
|
|
140
187
|
stream: s.stream,
|
|
141
188
|
queriers: s.queriers.map(serializeStreamQuerier)
|
|
142
|
-
}))
|
|
189
|
+
})),
|
|
190
|
+
version: tableProcessorSerializer.usesRowMetadataSqlValue ? 2 : 1
|
|
143
191
|
};
|
|
192
|
+
// Compiled events are intentionally additive to plan versions 1 and 2. The service also persists their raw SQL in
|
|
193
|
+
// the legacy eventDescriptors field, so older readers can ignore this field and retain equivalent event behavior.
|
|
194
|
+
if (events.length != 0) {
|
|
195
|
+
serialized.events = events;
|
|
196
|
+
}
|
|
197
|
+
return serialized;
|
|
144
198
|
}
|
|
145
199
|
export function deserializeSyncPlan(serialized) {
|
|
146
|
-
|
|
200
|
+
const { version } = serialized;
|
|
201
|
+
if (version < 1) {
|
|
147
202
|
throw new Error('Unknown sync plan version passed to deserializeSyncPlan()');
|
|
148
203
|
}
|
|
204
|
+
if (version > maxSupportedSyncPlanVersion) {
|
|
205
|
+
throw new Error(`Encountered a sync plan with version ${version}, the maximum supported version is ${maxSupportedSyncPlanVersion}. This can happen when the PowerSync service version is downgraded after deploying Sync Streams, consider upgrading or re-deploying.`);
|
|
206
|
+
}
|
|
149
207
|
function deserializeTablePattern(pattern) {
|
|
150
208
|
if (pattern.schema) {
|
|
151
209
|
return new TablePattern(`${pattern.connection}.${pattern.schema}`, pattern.table);
|
|
@@ -210,6 +268,36 @@ export function deserializeSyncPlan(serialized) {
|
|
|
210
268
|
defaultLookupScope: source.lookupScope
|
|
211
269
|
};
|
|
212
270
|
});
|
|
271
|
+
function deserializeEventRowEvaluator(source) {
|
|
272
|
+
const functions = (tableValuedFunctionsInScope = source.tableValuedFunctions);
|
|
273
|
+
return {
|
|
274
|
+
hashCode: source.hash,
|
|
275
|
+
sourceTable: deserializeTablePattern(source.table),
|
|
276
|
+
tableValuedFunctions: functions,
|
|
277
|
+
filters: source.filters.map(deserializeTableProcessorDataExpr),
|
|
278
|
+
parameters: deserializeParameters(source.partitionBy),
|
|
279
|
+
columns: source.columns.map((column) => {
|
|
280
|
+
if (column == 'star') {
|
|
281
|
+
return 'star';
|
|
282
|
+
}
|
|
283
|
+
return { expr: deserializeTableProcessorDataExpr(column.expr), alias: column.alias };
|
|
284
|
+
})
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
if (plan.events != null && !Array.isArray(plan.events)) {
|
|
288
|
+
throw new Error('Compiled sync plan events must be an array.');
|
|
289
|
+
}
|
|
290
|
+
const serializedEvents = plan.events ?? [];
|
|
291
|
+
const events = serializedEvents.map((event) => {
|
|
292
|
+
return {
|
|
293
|
+
name: event.name,
|
|
294
|
+
sourceQueries: event.sourceQueries.map((query) => ({
|
|
295
|
+
sql: query.sql,
|
|
296
|
+
sourceTable: deserializeTablePattern(query.table),
|
|
297
|
+
variants: query.variants.map(deserializeEventRowEvaluator)
|
|
298
|
+
}))
|
|
299
|
+
};
|
|
300
|
+
});
|
|
213
301
|
function deserializeParameterValue(stages, value) {
|
|
214
302
|
switch (value.type) {
|
|
215
303
|
case 'request':
|
|
@@ -268,7 +356,9 @@ export function deserializeSyncPlan(serialized) {
|
|
|
268
356
|
dataSources,
|
|
269
357
|
buckets,
|
|
270
358
|
parameterIndexes,
|
|
271
|
-
streams
|
|
359
|
+
streams,
|
|
360
|
+
events
|
|
272
361
|
};
|
|
273
362
|
}
|
|
363
|
+
export const maxSupportedSyncPlanVersion = 2;
|
|
274
364
|
//# sourceMappingURL=serialize.js.map
|