Skip to content

Commit c37a525

Browse files
committed
Support including metadata for inserts
1 parent c1cf9dc commit c37a525

File tree

3 files changed

+95
-19
lines changed

3 files changed

+95
-19
lines changed

Diff for: crates/core/src/diff.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ fn powersync_diff_impl(
2424
diff_objects_with_options(data_old, data_new, ignore_removed)
2525
}
2626

27-
fn diff_objects(data_old: &str, data_new: &str) -> Result<String, SQLiteError> {
28-
diff_objects_with_options(data_old, data_new, false)
29-
}
30-
3127
/// Returns a JSON object containing entries from [data_new] that are not present in [data_old].
3228
///
3329
/// When [ignore_removed_columns] is set, columns that are present in [data_old] but not in
@@ -108,6 +104,10 @@ pub fn register(db: *mut sqlite::sqlite3) -> Result<(), ResultCode> {
108104
mod tests {
109105
use super::*;
110106

107+
fn diff_objects(data_old: &str, data_new: &str) -> Result<String, SQLiteError> {
108+
diff_objects_with_options(data_old, data_new, false)
109+
}
110+
111111
#[test]
112112
fn basic_diff_test() {
113113
assert_eq!(diff_objects("{}", "{}").unwrap(), "{}");

Diff for: crates/core/src/views.rs

+20-15
Original file line numberDiff line numberDiff line change
@@ -175,43 +175,48 @@ fn powersync_trigger_insert_sql_impl(
175175
let mut columns = ColumnNameAndTypeStatement::new(local_db, table)?;
176176
let json_fragment = json_object_fragment("NEW", &mut columns.names_iter())?;
177177

178+
let metadata_fragment = if table_info.flags.include_metadata() {
179+
", 'metadata', NEW._metadata"
180+
} else {
181+
""
182+
};
183+
178184
return if !local_only && !insert_only {
179185
let trigger = format!("\
180-
CREATE TRIGGER {:}
181-
INSTEAD OF INSERT ON {:}
186+
CREATE TRIGGER {trigger_name}
187+
INSTEAD OF INSERT ON {quoted_name}
182188
FOR EACH ROW
183189
BEGIN
184190
SELECT CASE
185191
WHEN (NEW.id IS NULL)
186192
THEN RAISE (FAIL, 'id is required')
187193
END;
188-
INSERT INTO {:}
189-
SELECT NEW.id, {:};
190-
INSERT INTO powersync_crud_(data) VALUES(json_object('op', 'PUT', 'type', {:}, 'id', NEW.id, 'data', json(powersync_diff('{{}}', {:}))));
191-
INSERT OR IGNORE INTO ps_updated_rows(row_type, row_id) VALUES({:}, NEW.id);
192-
INSERT OR REPLACE INTO ps_buckets(name, last_op, target_op) VALUES('$local', 0, {:});
193-
END", trigger_name, quoted_name, internal_name, json_fragment, type_string, json_fragment, type_string, MAX_OP_ID);
194+
INSERT INTO {internal_name}
195+
SELECT NEW.id, {json_fragment};
196+
INSERT INTO powersync_crud_(data) VALUES(json_object('op', 'PUT', 'type', {:}, 'id', NEW.id, 'data', json(powersync_diff('{{}}', {:})) {metadata_fragment}));
197+
INSERT OR IGNORE INTO ps_updated_rows(row_type, row_id) VALUES({type_string}, NEW.id);
198+
INSERT OR REPLACE INTO ps_buckets(name, last_op, target_op) VALUES('$local', 0, {MAX_OP_ID});
199+
END", type_string, json_fragment);
194200
Ok(trigger)
195201
} else if local_only {
196202
let trigger = format!(
197203
"\
198-
CREATE TRIGGER {:}
199-
INSTEAD OF INSERT ON {:}
204+
CREATE TRIGGER {trigger_name}
205+
INSTEAD OF INSERT ON {quoted_name}
200206
FOR EACH ROW
201207
BEGIN
202-
INSERT INTO {:} SELECT NEW.id, {:};
208+
INSERT INTO {internal_name} SELECT NEW.id, {json_fragment};
203209
END",
204-
trigger_name, quoted_name, internal_name, json_fragment
205210
);
206211
Ok(trigger)
207212
} else if insert_only {
208213
let trigger = format!("\
209-
CREATE TRIGGER {:}
210-
INSTEAD OF INSERT ON {:}
214+
CREATE TRIGGER {trigger_name}
215+
INSTEAD OF INSERT ON {quoted_name}
211216
FOR EACH ROW
212217
BEGIN
213218
INSERT INTO powersync_crud_(data) VALUES(json_object('op', 'PUT', 'type', {}, 'id', NEW.id, 'data', json(powersync_diff('{{}}', {:}))));
214-
END", trigger_name, quoted_name, type_string, json_fragment);
219+
END", type_string, json_fragment);
215220
Ok(trigger)
216221
} else {
217222
Err(SQLiteError::from(ResultCode::MISUSE))

Diff for: dart/test/crud_test.dart

+71
Original file line numberDiff line numberDiff line change
@@ -369,5 +369,76 @@ void main() {
369369
});
370370
});
371371
});
372+
373+
group('including metadata', () {
374+
void createTable([Map<String, Object?> options = const {}]) {
375+
final tableSchema = {
376+
'tables': [
377+
{
378+
'name': 'test',
379+
'columns': [
380+
{'name': 'name', 'type': 'text'},
381+
],
382+
...options,
383+
}
384+
]
385+
};
386+
387+
db.select('select powersync_init()');
388+
db.select(
389+
'select powersync_replace_schema(?)', [jsonEncode(tableSchema)]);
390+
}
391+
392+
test('is disabled by default', () {
393+
createTable();
394+
expect(
395+
() => db.execute(
396+
'INSERT INTO test (id, name, _metadata) VALUES (?, ?, ?)',
397+
['id', 'name', 'test insert'],
398+
),
399+
throwsA(isA<SqliteException>()),
400+
);
401+
});
402+
403+
test('can be disabled', () {
404+
createTable({'include_metadata': false});
405+
expect(
406+
() => db.execute(
407+
'INSERT INTO test (id, name, _metadata) VALUES (?, ?, ?)',
408+
['id', 'name', 'test insert'],
409+
),
410+
throwsA(isA<SqliteException>()),
411+
);
412+
});
413+
414+
test('supports insert statements', () {
415+
createTable({'include_metadata': true});
416+
db.execute(
417+
'INSERT INTO test (id, name, _metadata) VALUES (?, ?, ?)',
418+
['id', 'name', 'test insert'],
419+
);
420+
421+
final [row] = db.select('select data from ps_crud');
422+
final op = jsonDecode(row[0] as String);
423+
expect(op['data'], {'name': 'name'});
424+
expect(op['metadata'], 'test insert');
425+
});
426+
427+
test('supports update statements', () {
428+
createTable({'include_metadata': true});
429+
db.execute(
430+
'INSERT INTO test (id, name, _metadata) VALUES (?, ?, ?)',
431+
['id', 'name', 'test insert'],
432+
);
433+
db.execute('delete from ps_crud;');
434+
db.execute(
435+
'update test set name = name || ?, _metadata = ?', ['.', 'update']);
436+
437+
final [row] = db.select('select data from ps_crud');
438+
final op = jsonDecode(row[0] as String);
439+
expect(op['data'], {'name': 'name.'});
440+
expect(op['metadata'], 'update');
441+
});
442+
});
372443
});
373444
}

0 commit comments

Comments
 (0)