Skip to content

Commit

Permalink
Merge branch 'upgrade-6017643d64f52796e56e451a2275efc5ad088e4f'
Browse files Browse the repository at this point in the history
  • Loading branch information
davidmartos96 committed Aug 24, 2023
2 parents 8d251b0 + e4b80a5 commit 6c76b6c
Show file tree
Hide file tree
Showing 36 changed files with 758 additions and 404 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ For development updates make sure to check out the official [ElectricSQL Discord

---

Non official Dart client implementation for [Electric](https://electric-sql.com/) based on commit `cd77e4a67f5367e1376cab752bd16177801d20b5` of the [electric git repository](https://github.com/electric-sql/electric)
Non official Dart client implementation for [Electric](https://electric-sql.com/) based on commit `6017643d64f52796e56e451a2275efc5ad088e4f` of the [electric git repository](https://github.com/electric-sql/electric)

- Client based on the typescript client from the `clients/typescript` subfolder.

Expand Down
4 changes: 3 additions & 1 deletion packages/electricsql/lib/src/auth/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ Future<String> authToken({String? iss, String? key}) async {
final mockKey = key ?? 'integration-tests-signing-key-example';

final int nowInSecs = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final iat = nowInSecs - 1; // Remove 1 second

// Subtract 1 second to account for clock precision when validating the token
final iat = nowInSecs - 1;

final jwt = JWT(
{
Expand Down
6 changes: 4 additions & 2 deletions packages/electricsql/lib/src/migrators/bundle.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ class BundleMigrator implements Migrator {

if (migration.version != version) {
throw Exception(
'Migrations cannot be altered once applied: expecting $version at index $i.',
'Local migrations $version does not match server version ${migration.version}. '
'This is an unrecoverable error. Please clear your local storage and try again. '
'Check documentation (https://electric-sql.com/docs/reference/limitations) to learn more.',
);
}
}
Expand Down Expand Up @@ -141,7 +143,7 @@ class BundleMigrator implements Migrator {

await adapter.runInTransaction([
...statements,
Statement(applied, [version, DateTime.now().millisecondsSinceEpoch])
Statement(applied, [version, DateTime.now().millisecondsSinceEpoch]),
]);
}

Expand Down
4 changes: 2 additions & 2 deletions packages/electricsql/lib/src/migrators/schema.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ final kBaseMigrations = [
//"-- Somewhere to track migrations\n",
'CREATE TABLE IF NOT EXISTS $migrationsTable (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n version TEXT NOT NULL UNIQUE,\n applied_at TEXT NOT NULL\n);',
//"-- Initialisation of the metadata table\n",
"INSERT INTO $metaTable (key, value) VALUES ('compensations', 0), ('lastAckdRowId','0'), ('lastSentRowId', '0'), ('lsn', ''), ('clientId', ''), ('subscriptions', '');",
"INSERT INTO $metaTable (key, value) VALUES ('compensations', 1), ('lastAckdRowId','0'), ('lastSentRowId', '0'), ('lsn', ''), ('clientId', ''), ('subscriptions', '');",
//"-- These are toggles for turning the triggers on and off\n",
'DROP TABLE IF EXISTS $triggersTable;',
'CREATE TABLE $triggersTable (tablename TEXT PRIMARY KEY, flag INTEGER);',
//"-- Somewhere to keep dependency tracking information\n",
'CREATE TABLE $shadowTable (\n namespace TEXT NOT NULL,\n tablename TEXT NOT NULL,\n primaryKey TEXT NOT NULL,\n tags TEXT NOT NULL,\n PRIMARY KEY (namespace, tablename, primaryKey));',
],
version: '0',
)
),
];
52 changes: 27 additions & 25 deletions packages/electricsql/lib/src/migrators/triggers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,43 +132,50 @@ List<Statement> generateOplogTriggers(
List<Statement> generateCompensationTriggers(
TableFullName tableFullName,
Table table,
Tables tables,
) {
final tableName = table.tableName;
final namespace = table.namespace;
final foreignKeys = table.foreignKeys;

List<Statement> makeTriggers(ForeignKey foreignKey) {
final childKey = foreignKey.childKey;
final fkTable = tables[foreignKey.table];
if (fkTable == null) {
throw Exception('Table ${foreignKey.table} for foreign key not found.');
}
final joinedFkPKs = joinColsForJSON(fkTable.primary, null);
final joinedFkCols = joinColsForJSON(fkTable.columns, null);

const fkTableNamespace =
'main'; // currently, Electric always uses the 'main' namespace
final fkTableName = foreignKey.table;
final fkTablePK =
foreignKey.parentKey; // primary key of the table pointed at by the FK.
final joinedFkPKs = joinColsForJSON([fkTablePK], null);

return <String>[
'DROP TRIGGER IF EXISTS compensation_insert_${namespace}_${tableName}_${childKey}_into_oplog;',
'''
-- Triggers for foreign key compensations
DROP TRIGGER IF EXISTS compensation_insert_${namespace}_${tableName}_${childKey}_into_oplog;''',
// The compensation trigger inserts a row in `_electric_oplog` if the row pointed at by the FK exists
// The way how this works is that the values for the row are passed to the nested SELECT
// which will return those values for every record that matches the query
// which can be at most once since we filter on the foreign key which is also the primary key and thus is unique.
'''
CREATE TRIGGER compensation_insert_${namespace}_${tableName}_${childKey}_into_oplog
AFTER INSERT ON $tableFullName
WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == '${fkTable.namespace}.${fkTable.tableName}') AND
WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == '$fkTableNamespace.$fkTableName') AND
1 == (SELECT value from _electric_meta WHERE key == 'compensations')
BEGIN
INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)
SELECT '${fkTable.namespace}', '${fkTable.tableName}', 'UPDATE', json_object($joinedFkPKs), json_object($joinedFkCols), NULL, NULL
FROM ${fkTable.namespace}.${fkTable.tableName} WHERE ${foreignKey.parentKey} = new.${foreignKey.childKey};
SELECT '$fkTableNamespace', '$fkTableName', 'UPDATE', json_object($joinedFkPKs), json_object($joinedFkPKs), NULL, NULL
FROM $fkTableNamespace.$fkTableName WHERE ${foreignKey.parentKey} = new.${foreignKey.childKey};
END;
''',
'DROP TRIGGER IF EXISTS compensation_update_${namespace}_${tableName}_${foreignKey.childKey}_into_oplog;',
'''
CREATE TRIGGER compensation_update_${namespace}_${tableName}_${foreignKey.childKey}_into_oplog
AFTER UPDATE ON $namespace.$tableName
WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == '${fkTable.namespace}.${fkTable.tableName}') AND
WHEN 1 == (SELECT flag from _electric_trigger_settings WHERE tablename == '$fkTableNamespace.$fkTableName') AND
1 == (SELECT value from _electric_meta WHERE key == 'compensations')
BEGIN
INSERT INTO _electric_oplog (namespace, tablename, optype, primaryKey, newRow, oldRow, timestamp)
SELECT '${fkTable.namespace}', '${fkTable.tableName}', 'UPDATE', json_object($joinedFkPKs), json_object($joinedFkCols), NULL, NULL
FROM ${fkTable.namespace}.${fkTable.tableName} WHERE ${foreignKey.parentKey} = new.${foreignKey.childKey};
SELECT '$fkTableNamespace', '$fkTableName', 'UPDATE', json_object($joinedFkPKs), json_object($joinedFkPKs), NULL, NULL
FROM $fkTableNamespace.$fkTableName WHERE ${foreignKey.parentKey} = new.${foreignKey.childKey};
END;
''',
].map(Statement.new).toList();
Expand All @@ -185,16 +192,11 @@ List<Statement> generateCompensationTriggers(
/// @returns An array of SQLite statements that add the necessary oplog and compensation triggers.
List<Statement> generateTableTriggers(
TableFullName tableFullName,
Tables tables,
Table table,
) {
final table = tables[tableFullName];
if (table == null) {
throw Exception(
'Could not generate triggers for $tableFullName. Table not found.',
);
}
final oplogTriggers = generateOplogTriggers(tableFullName, table);
final fkTriggers = generateCompensationTriggers(tableFullName, table, tables);
final fkTriggers =
generateCompensationTriggers(tableFullName, table); //, tables)
return [...oplogTriggers, ...fkTriggers];
}

Expand All @@ -203,8 +205,8 @@ List<Statement> generateTableTriggers(
/// @returns An array of SQLite statements that add the necessary oplog and compensation triggers for all tables.
List<Statement> generateTriggers(Tables tables) {
final List<Statement> tableTriggers = [];
tables.forEach((tableFullName, _table) {
final triggers = generateTableTriggers(tableFullName, tables);
tables.forEach((tableFullName, table) {
final triggers = generateTableTriggers(tableFullName, table); //, tables)
tableTriggers.addAll(triggers);
});

Expand All @@ -213,7 +215,7 @@ List<Statement> generateTriggers(Tables tables) {
Statement(
'CREATE TABLE _electric_trigger_settings(tablename TEXT PRIMARY KEY, flag INTEGER);',
),
...tableTriggers
...tableTriggers,
];

return stmts;
Expand Down
21 changes: 21 additions & 0 deletions packages/electricsql/lib/src/proto/satellite.pb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -893,13 +893,19 @@ class SatRelationColumn extends $pb.GeneratedMessage {
? ''
: 'primaryKey',
protoName: 'primaryKey')
..aOB(
4,
const $core.bool.fromEnvironment('protobuf.omit_field_names')
? ''
: 'isNullable')
..hasRequiredFields = false;

SatRelationColumn._() : super();
factory SatRelationColumn({
$core.String? name,
$core.String? type,
$core.bool? primaryKey,
$core.bool? isNullable,
}) {
final _result = create();
if (name != null) {
Expand All @@ -911,6 +917,9 @@ class SatRelationColumn extends $pb.GeneratedMessage {
if (primaryKey != null) {
_result.primaryKey = primaryKey;
}
if (isNullable != null) {
_result.isNullable = isNullable;
}
return _result;
}
factory SatRelationColumn.fromBuffer($core.List<$core.int> i,
Expand Down Expand Up @@ -975,6 +984,18 @@ class SatRelationColumn extends $pb.GeneratedMessage {
$core.bool hasPrimaryKey() => $_has(2);
@$pb.TagNumber(3)
void clearPrimaryKey() => clearField(3);

@$pb.TagNumber(4)
$core.bool get isNullable => $_getBF(3);
@$pb.TagNumber(4)
set isNullable($core.bool v) {
$_setBool(3, v);
}

@$pb.TagNumber(4)
$core.bool hasIsNullable() => $_has(3);
@$pb.TagNumber(4)
void clearIsNullable() => clearField(4);
}

class SatRelation extends $pb.GeneratedMessage {
Expand Down
2 changes: 1 addition & 1 deletion packages/electricsql/lib/src/proto/satellite.pbenum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class SatInStartReplicationResp_ReplicationError_Code extends $pb.ProtobufEnum {
];

static final $core
.Map<$core.int, SatInStartReplicationResp_ReplicationError_Code>
.Map<$core.int, SatInStartReplicationResp_ReplicationError_Code>
_byValue = $pb.ProtobufEnum.initByValue(values);
static SatInStartReplicationResp_ReplicationError_Code? valueOf(
$core.int value) =>
Expand Down
3 changes: 2 additions & 1 deletion packages/electricsql/lib/src/proto/satellite.pbjson.dart
Original file line number Diff line number Diff line change
Expand Up @@ -283,12 +283,13 @@ const SatRelationColumn$json = const {
const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
const {'1': 'type', '3': 2, '4': 1, '5': 9, '10': 'type'},
const {'1': 'primaryKey', '3': 3, '4': 1, '5': 8, '10': 'primaryKey'},
const {'1': 'is_nullable', '3': 4, '4': 1, '5': 8, '10': 'isNullable'},
],
};

/// Descriptor for `SatRelationColumn`. Decode as a `google.protobuf.DescriptorProto`.
final $typed_data.Uint8List satRelationColumnDescriptor = $convert.base64Decode(
'ChFTYXRSZWxhdGlvbkNvbHVtbhISCgRuYW1lGAEgASgJUgRuYW1lEhIKBHR5cGUYAiABKAlSBHR5cGUSHgoKcHJpbWFyeUtleRgDIAEoCFIKcHJpbWFyeUtleQ==');
'ChFTYXRSZWxhdGlvbkNvbHVtbhISCgRuYW1lGAEgASgJUgRuYW1lEhIKBHR5cGUYAiABKAlSBHR5cGUSHgoKcHJpbWFyeUtleRgDIAEoCFIKcHJpbWFyeUtleRIfCgtpc19udWxsYWJsZRgEIAEoCFIKaXNOdWxsYWJsZQ==');
@$core.Deprecated('Use satRelationDescriptor instead')
const SatRelation$json = const {
'1': 'SatRelation',
Expand Down
Loading

0 comments on commit 6c76b6c

Please sign in to comment.