1
+ import 'crud.dart' ;
1
2
import 'schema_logic.dart' ;
2
3
3
4
/// The schema used by the database.
@@ -26,8 +27,27 @@ class Schema {
26
27
}
27
28
}
28
29
30
+ /// Options to include old values in [CrudEntry] for update statements.
31
+ ///
32
+ /// This options are enabled by passing it to a non-local [Table] constructor.
33
+ final class IncludeOldOptions {
34
+ /// A filter of column names for which updates should be tracked.
35
+ ///
36
+ /// When set to a non-null value, olumns not included in this list will not
37
+ /// appear in [CrudEntry.oldData] . By default, all columns are included.
38
+ final List <String >? columnFilter;
39
+
40
+ /// Whether to only include old values when they were changed by an update,
41
+ /// instead of always including all old values.
42
+ final bool onlyWhenChanged;
43
+
44
+ const IncludeOldOptions ({this .columnFilter, this .onlyWhenChanged = false });
45
+ }
46
+
29
47
/// A single table in the schema.
30
48
class Table {
49
+ static const _maxNumberOfColumns = 1999 ;
50
+
31
51
/// The synced table name, matching sync rules.
32
52
final String name;
33
53
@@ -37,20 +57,34 @@ class Table {
37
57
/// List of indexes.
38
58
final List <Index > indexes;
39
59
40
- /// Whether the table only exists only.
60
+ /// Whether to add a hidden `_metadata` column that will be enabled for
61
+ /// updates to attach custom information about writes that will be reported
62
+ /// through [CrudEntry.metadata] .
63
+ final bool includeMetadata;
64
+
65
+ /// Whether to track old values of columns for [CrudEntry.oldData] .
66
+ ///
67
+ /// See [IncludeOldOptions] for details.
68
+ final IncludeOldOptions ? includeOld;
69
+
70
+ /// Whether the table only exists locally.
41
71
final bool localOnly;
42
72
43
73
/// Whether this is an insert-only table.
44
74
final bool insertOnly;
45
75
76
+ /// Whether an `UPDATE` statement that doesn't change any values should be
77
+ /// ignored when creating CRUD entries.
78
+ final bool ignoreEmptyUpdate;
79
+
46
80
/// Override the name for the view
47
81
final String ? _viewNameOverride;
48
82
49
83
/// powersync-sqlite-core limits the number of columns
50
84
/// per table to 1999, due to internal SQLite limits.
51
85
///
52
86
/// In earlier versions this was limited to 63.
53
- final int maxNumberOfColumns = 1999 ;
87
+ final int maxNumberOfColumns = _maxNumberOfColumns ;
54
88
55
89
/// Internal use only.
56
90
///
@@ -66,9 +100,16 @@ class Table {
66
100
/// Create a synced table.
67
101
///
68
102
/// Local changes are recorded, and remote changes are synced to the local table.
69
- const Table (this .name, this .columns,
70
- {this .indexes = const [], String ? viewName, this .localOnly = false })
71
- : insertOnly = false ,
103
+ const Table (
104
+ this .name,
105
+ this .columns, {
106
+ this .indexes = const [],
107
+ String ? viewName,
108
+ this .localOnly = false ,
109
+ this .ignoreEmptyUpdate = false ,
110
+ this .includeMetadata = false ,
111
+ this .includeOld,
112
+ }) : insertOnly = false ,
72
113
_viewNameOverride = viewName;
73
114
74
115
/// Create a table that only exists locally.
@@ -78,6 +119,9 @@ class Table {
78
119
{this .indexes = const [], String ? viewName})
79
120
: localOnly = true ,
80
121
insertOnly = false ,
122
+ includeMetadata = false ,
123
+ includeOld = null ,
124
+ ignoreEmptyUpdate = false ,
81
125
_viewNameOverride = viewName;
82
126
83
127
/// Create a table that only supports inserts.
@@ -88,8 +132,14 @@ class Table {
88
132
///
89
133
/// SELECT queries on the table will always return 0 rows.
90
134
///
91
- const Table .insertOnly (this .name, this .columns, {String ? viewName})
92
- : localOnly = false ,
135
+ const Table .insertOnly (
136
+ this .name,
137
+ this .columns, {
138
+ String ? viewName,
139
+ this .ignoreEmptyUpdate = false ,
140
+ this .includeMetadata = false ,
141
+ this .includeOld,
142
+ }) : localOnly = false ,
93
143
insertOnly = true ,
94
144
indexes = const [],
95
145
_viewNameOverride = viewName;
@@ -106,9 +156,9 @@ class Table {
106
156
107
157
/// Check that there are no issues in the table definition.
108
158
void validate () {
109
- if (columns.length > maxNumberOfColumns ) {
159
+ if (columns.length > _maxNumberOfColumns ) {
110
160
throw AssertionError (
111
- "Table $name has more than $maxNumberOfColumns columns, which is not supported" );
161
+ "Table $name has more than $_maxNumberOfColumns columns, which is not supported" );
112
162
}
113
163
114
164
if (invalidSqliteCharacters.hasMatch (name)) {
@@ -121,6 +171,14 @@ class Table {
121
171
"Invalid characters in view name: $_viewNameOverride " );
122
172
}
123
173
174
+ if (includeMetadata && localOnly) {
175
+ throw AssertionError ("Local-only tables can't track metadata" );
176
+ }
177
+
178
+ if (includeOld != null && localOnly) {
179
+ throw AssertionError ("Local-only tables can't track old values" );
180
+ }
181
+
124
182
Set <String > columnNames = {"id" };
125
183
for (var column in columns) {
126
184
if (column.name == 'id' ) {
@@ -168,7 +226,13 @@ class Table {
168
226
'local_only' : localOnly,
169
227
'insert_only' : insertOnly,
170
228
'columns' : columns,
171
- 'indexes' : indexes.map ((e) => e.toJson (this )).toList (growable: false )
229
+ 'indexes' : indexes.map ((e) => e.toJson (this )).toList (growable: false ),
230
+ 'ignore_empty_update' : ignoreEmptyUpdate,
231
+ 'include_metadata' : includeMetadata,
232
+ if (includeOld case final includeOld? ) ...{
233
+ 'include_old' : includeOld.columnFilter ?? true ,
234
+ 'include_old_only_when_changed' : includeOld.onlyWhenChanged,
235
+ },
172
236
};
173
237
}
174
238
0 commit comments