1
1
package com.powersync.db.schema
2
2
3
+ import com.powersync.db.crud.CrudEntry
3
4
import kotlinx.serialization.SerialName
4
5
import kotlinx.serialization.Serializable
6
+ import kotlinx.serialization.json.JsonElement
7
+ import kotlinx.serialization.json.JsonPrimitive
8
+ import kotlinx.serialization.json.buildJsonArray
5
9
6
10
private const val MAX_AMOUNT_OF_COLUMNS = 1999
7
11
8
12
/* *
9
13
* A single table in the schema.
10
14
*/
11
- public data class Table constructor (
15
+ public data class Table (
12
16
/* *
13
17
* The synced table name, matching sync rules.
14
18
*/
@@ -33,6 +37,22 @@ public data class Table constructor(
33
37
* Override the name for the view
34
38
*/
35
39
private val viewNameOverride : String? = null ,
40
+ /* *
41
+ * Whether to add a hidden `_metadata` column that will be enabled for updates to attach custom
42
+ * information about writes that will be reported through [CrudEntry.metadata].
43
+ */
44
+ val trackMetadata : Boolean = false ,
45
+ /* *
46
+ * When set to a non-null value, track old values of columns for [CrudEntry.oldData].
47
+ *
48
+ * See [TrackPreviousValuesOptions] for details.
49
+ */
50
+ val trackPreviousValues : TrackPreviousValuesOptions ? = null ,
51
+ /* *
52
+ * Whether an `UPDATE` statement that doesn't change any values should be ignored when creating
53
+ * CRUD entries.
54
+ */
55
+ val ignoreEmptyUpdates : Boolean = false ,
36
56
) {
37
57
init {
38
58
/* *
@@ -81,6 +101,9 @@ public data class Table constructor(
81
101
name : String ,
82
102
columns : List <Column >,
83
103
viewName : String? = null,
104
+ ignoreEmptyUpdates : Boolean = false,
105
+ trackMetadata : Boolean = false,
106
+ trackPreviousValues : TrackPreviousValuesOptions ? = null,
84
107
): Table =
85
108
Table (
86
109
name,
@@ -89,6 +112,9 @@ public data class Table constructor(
89
112
localOnly = false ,
90
113
insertOnly = true ,
91
114
viewNameOverride = viewName,
115
+ ignoreEmptyUpdates = ignoreEmptyUpdates,
116
+ trackMetadata = trackMetadata,
117
+ trackPreviousValues = trackPreviousValues,
92
118
)
93
119
}
94
120
@@ -135,6 +161,13 @@ public data class Table constructor(
135
161
throw AssertionError (" Invalid characters in view name: $viewNameOverride " )
136
162
}
137
163
164
+ check(! localOnly || ! trackMetadata) {
165
+ " Can't track metadata for local-only tables."
166
+ }
167
+ check(! localOnly || trackPreviousValues == null ) {
168
+ " Can't track old values for local-only tables."
169
+ }
170
+
138
171
val columnNames = mutableSetOf (" id" )
139
172
for (column in columns) {
140
173
when {
@@ -185,6 +218,26 @@ public data class Table constructor(
185
218
get() = viewNameOverride ? : name
186
219
}
187
220
221
+ /* *
222
+ * Options to include old values in [CrudEntry.oldData] for update statements.
223
+ *
224
+ * These options are enabled by passing them to a non-local [Table] constructor.
225
+ */
226
+ public data class TrackPreviousValuesOptions (
227
+ /* *
228
+ * A filter of column names for which updates should be tracked.
229
+ *
230
+ * When set to a non-null value, columns not included in this list will not appear in
231
+ * [CrudEntry.oldData]. By default, all columns are included.
232
+ */
233
+ val columnFilter : List <String >? = null ,
234
+ /* *
235
+ * Whether to only include old values when they were changed by an update, instead of always
236
+ * including all old values,
237
+ */
238
+ val onlyWhenChanged : Boolean = false ,
239
+ )
240
+
188
241
@Serializable
189
242
internal data class SerializableTable (
190
243
var name : String ,
@@ -196,16 +249,39 @@ internal data class SerializableTable(
196
249
val insertOnly : Boolean = false ,
197
250
@SerialName(" view_name" )
198
251
val viewName : String? = null ,
252
+ @SerialName(" ignore_empty_update" )
253
+ val ignoreEmptyUpdate : Boolean = false ,
254
+ @SerialName(" include_metadata" )
255
+ val includeMetadata : Boolean = false ,
256
+ @SerialName(" include_old" )
257
+ val includeOld : JsonElement = JsonPrimitive (false),
258
+ @SerialName(" include_old_only_when_changed" )
259
+ val includeOldOnlyWhenChanged : Boolean = false ,
199
260
)
200
261
201
262
internal fun Table.toSerializable (): SerializableTable =
202
263
with (this ) {
203
264
SerializableTable (
204
- name,
205
- columns.map { it.toSerializable() },
206
- indexes.map { it.toSerializable() },
207
- localOnly,
208
- insertOnly,
209
- viewName,
265
+ name = name,
266
+ columns = columns.map { it.toSerializable() },
267
+ indexes = indexes.map { it.toSerializable() },
268
+ localOnly = localOnly,
269
+ insertOnly = insertOnly,
270
+ viewName = viewName,
271
+ ignoreEmptyUpdate = ignoreEmptyUpdates,
272
+ includeMetadata = trackMetadata,
273
+ includeOld =
274
+ trackPreviousValues?.let {
275
+ if (it.columnFilter != null ) {
276
+ buildJsonArray {
277
+ for (column in it.columnFilter) {
278
+ add(JsonPrimitive (column))
279
+ }
280
+ }
281
+ } else {
282
+ JsonPrimitive (true )
283
+ }
284
+ } ? : JsonPrimitive (false ),
285
+ includeOldOnlyWhenChanged = trackPreviousValues?.onlyWhenChanged ? : false ,
210
286
)
211
287
}
0 commit comments