Skip to content

Commit 449c09a

Browse files
use schema for internal names
1 parent 444eadc commit 449c09a

File tree

4 files changed

+53
-42
lines changed

4 files changed

+53
-42
lines changed

packages/common/src/client/AbstractPowerSyncDatabase.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
252252
this._isReadyPromise = this.initialize();
253253

254254
this.triggerManager = new TriggerManagerImpl({
255-
db: this
255+
db: this,
256+
schema: this.schema
256257
});
257258
}
258259

packages/common/src/client/triggers/TriggerManager.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ export interface BaseTriggerDiffRecord {
1818

1919
export interface TriggerDiffUpdateRecord extends BaseTriggerDiffRecord {
2020
operation: DiffTriggerOperation.UPDATE;
21-
change: string;
21+
value: string;
22+
previous_value?: string;
2223
}
2324

2425
export interface TriggerDiffInsertRecord extends BaseTriggerDiffRecord {
2526
operation: DiffTriggerOperation.INSERT;
26-
change: string;
27+
value: string;
2728
}
2829

2930
export interface TriggerDiffDeleteRecord extends BaseTriggerDiffRecord {
@@ -41,7 +42,8 @@ export interface TriggerCreationHooks {
4142

4243
export interface CreateDiffTriggerOptions {
4344
/**
44-
* Source table to trigger and track changes from.
45+
* Source table/view to trigger and track changes from.
46+
* This should be present in the PowerSync database's schema.
4547
*/
4648
source: string;
4749

@@ -102,7 +104,8 @@ export interface TriggerDiffHandlerContext {
102104

103105
export interface TrackDiffOptions {
104106
/**
105-
* A source SQLite table to track changes for.
107+
* A source SQLite table/view to track changes for.
108+
* This should be present in the PowerSync database's schema.
106109
*/
107110
source: string;
108111
/**
@@ -138,13 +141,15 @@ export interface TriggerManager {
138141
* CREATE TEMP TABLE ${destination} (
139142
* id TEXT,
140143
* operation TEXT,
141-
* change TEXT,
142144
* timestamp TEXT
145+
* value TEXT,
146+
* previous_value TEXT
143147
* );
144148
* ```
145-
* The `change` column contains the JSON representation of the change. This column is NULL for
149+
* The `value` column contains the JSON representation of the change. This column is NULL for
146150
* {@link DiffTriggerOperation#DELETE} operations.
147-
* For {@link DiffTriggerOperation#UPDATE} operations the `change` column is a JSON object containing both the `new` and `old` values.
151+
* For {@link DiffTriggerOperation#UPDATE} operations the `previous_value` column contains the previous value of the changed row
152+
* in a JSON format.
148153
*
149154
* @returns A callback to remove the trigger and drop the destination table.
150155
*/

packages/common/src/client/triggers/TriggerManagerImpl.ts

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { LockContext } from '../../db/DBAdapter.js';
2+
import { Schema } from '../../db/schema/Schema.js';
23
import { type AbstractPowerSyncDatabase } from '../AbstractPowerSyncDatabase.js';
34
import {
45
CreateDiffTriggerOptions,
@@ -10,10 +11,20 @@ import {
1011

1112
export type TriggerManagerImplOptions = {
1213
db: AbstractPowerSyncDatabase;
14+
schema: Schema;
1315
};
1416

1517
export class TriggerManagerImpl implements TriggerManager {
16-
constructor(protected options: TriggerManagerImplOptions) {}
18+
protected schema: Schema;
19+
20+
constructor(protected options: TriggerManagerImplOptions) {
21+
this.schema = options.schema;
22+
options.db.registerListener({
23+
schemaChanged: (schema) => {
24+
this.schema = schema;
25+
}
26+
});
27+
}
1728

1829
protected get db() {
1930
return this.options.db;
@@ -47,6 +58,17 @@ export class TriggerManagerImpl implements TriggerManager {
4758
throw new Error('At least one column must be specified for the trigger.');
4859
}
4960

61+
/**
62+
* Allow specifying the View name as the source.
63+
* We can lookup the internal table name from the schema.
64+
*/
65+
const sourceDefinition = this.schema.tables.find((table) => table.viewName == source);
66+
if (!sourceDefinition) {
67+
throw new Error(`Source table or view "${source}" not found in the schema.`);
68+
}
69+
70+
const internalSource = sourceDefinition.internalName;
71+
5072
/**
5173
* When is a tuple of the query and the parameters.
5274
*/
@@ -79,8 +101,9 @@ export class TriggerManagerImpl implements TriggerManager {
79101
CREATE TEMP TABLE ${destination} (
80102
id TEXT,
81103
operation TEXT,
82-
change TEXT,
83-
timestamp TEXT
104+
timestamp TEXT,
105+
value TEXT,
106+
previous_value TEXT
84107
);
85108
`);
86109

@@ -89,15 +112,15 @@ export class TriggerManagerImpl implements TriggerManager {
89112
triggerIds.push(insertTriggerId);
90113

91114
await tx.execute(/* sql */ `
92-
CREATE TEMP TRIGGER ${insertTriggerId} AFTER INSERT ON ${source} ${whenCondition} BEGIN
115+
CREATE TEMP TRIGGER ${insertTriggerId} AFTER INSERT ON ${internalSource} ${whenCondition} BEGIN
93116
INSERT INTO
94-
${destination} (id, operation, change, timestamp)
117+
${destination} (id, operation, timestamp, value)
95118
VALUES
96119
(
97120
NEW.id,
98121
'INSERT',
99-
${jsonFragment('NEW')},
100-
strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')
122+
strftime ('%Y-%m-%dT%H:%M:%fZ', 'now'),
123+
${jsonFragment('NEW')}
101124
);
102125
103126
END;
@@ -110,21 +133,16 @@ export class TriggerManagerImpl implements TriggerManager {
110133

111134
await tx.execute(/* sql */ `
112135
CREATE TEMP TRIGGER ${updateTriggerId} AFTER
113-
UPDATE ON ${source} ${whenCondition} BEGIN
136+
UPDATE ON ${internalSource} ${whenCondition} BEGIN
114137
INSERT INTO
115-
${destination} (id, operation, change, timestamp)
138+
${destination} (id, operation, timestamp, value, previous_value)
116139
VALUES
117140
(
118141
NEW.id,
119142
'UPDATE',
120-
--- Reports both the new and old values in JSON format
121-
json_object (
122-
'new',
123-
${jsonFragment('NEW')},
124-
'old',
125-
${jsonFragment('OLD')}
126-
),
127-
strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')
143+
strftime ('%Y-%m-%dT%H:%M:%fZ', 'now'),
144+
${jsonFragment('NEW')},
145+
${jsonFragment('OLD')}
128146
);
129147
130148
END;
@@ -137,7 +155,7 @@ export class TriggerManagerImpl implements TriggerManager {
137155

138156
// Create delete trigger for basic JSON
139157
await tx.execute(/* sql */ `
140-
CREATE TEMP TRIGGER ${deleteTriggerId} AFTER DELETE ON ${source} ${whenCondition} BEGIN
158+
CREATE TEMP TRIGGER ${deleteTriggerId} AFTER DELETE ON ${internalSource} ${whenCondition} BEGIN
141159
INSERT INTO
142160
${destination} (id, operation, timestamp)
143161
VALUES

packages/node/tests/trigger.test.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('Triggers', () => {
1212
const tempTable = 'temp_remote_lists';
1313

1414
await database.triggers.createDiffTrigger({
15-
source: 'ps_data__lists',
15+
source: 'lists',
1616
destination: tempTable,
1717
columns: ['name'],
1818
operations: [DiffTriggerOperation.INSERT, DiffTriggerOperation.UPDATE]
@@ -88,8 +88,7 @@ describe('Triggers', () => {
8888
* Watch the todos table for changes. Only track the diff for rows belonging to the first list.
8989
*/
9090
await database.triggers.trackTableDiff({
91-
// TODO investigate it this can be improved
92-
source: 'ps_data__todos',
91+
source: 'todos',
9392
columns: ['list_id'],
9493
// TODO, should/could we expose columns without json. Maybe with a temp view
9594
filter: `json_extract(NEW.data, '$.list_id') = '${firstList.id}'`,
@@ -174,17 +173,6 @@ describe('Triggers', () => {
174173

175174
const todos: Database['todos'][] = [];
176175

177-
// const createTodo = async () =>
178-
// database.execute(
179-
// /* sql */ `
180-
// INSERT INTO
181-
// todos (id, content, list_id)
182-
// VALUES
183-
// (uuid (), 'todo', ?)
184-
// `,
185-
// [firstList.id]
186-
// );
187-
188176
const createTodo = async () => {
189177
// Create todos for both lists
190178
await database.writeLock(async (tx) => {
@@ -204,8 +192,7 @@ describe('Triggers', () => {
204192
// Configure the trigger to watch for changes.
205193
// The onChange handler is guaranteed to see any change after the state above.
206194
database.triggers.trackTableDiff({
207-
// TODO investigate it this can be improved
208-
source: 'ps_data__todos',
195+
source: 'todos',
209196
columns: ['list_id'],
210197
// TODO, should/could we expose columns without json. Maybe with a temp view
211198
filter: `json_extract(NEW.data, '$.list_id') = '${firstList.id}'`,

0 commit comments

Comments
 (0)