Skip to content

Commit 11655a0

Browse files
committed
Merge branch 'raw_tables_skilldevs' into my-example
2 parents a1c3ec6 + bf9f1e9 commit 11655a0

17 files changed

+693
-129
lines changed

packages/powersync_core/lib/src/database/native/native_powersync_database.dart

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ class PowerSyncDatabaseImpl
4444
@override
4545
SqliteDatabase database;
4646

47+
@override
48+
bool manualSchemaManagement;
49+
4750
@override
4851
@protected
4952
late Future<void> isInitialized;
@@ -76,15 +79,21 @@ class PowerSyncDatabaseImpl
7679
required String path,
7780
int maxReaders = SqliteDatabase.defaultMaxReaders,
7881
Logger? logger,
82+
bool manualSchemaManagement = false,
7983
@Deprecated("Use [PowerSyncDatabase.withFactory] instead.")
8084
// ignore: deprecated_member_use_from_same_package
8185
SqliteConnectionSetup? sqliteSetup}) {
8286
// ignore: deprecated_member_use_from_same_package
8387
DefaultSqliteOpenFactory factory =
8488
// ignore: deprecated_member_use_from_same_package
8589
PowerSyncOpenFactory(path: path, sqliteSetup: sqliteSetup);
86-
return PowerSyncDatabaseImpl.withFactory(factory,
87-
schema: schema, maxReaders: maxReaders, logger: logger);
90+
return PowerSyncDatabaseImpl.withFactory(
91+
factory,
92+
schema: schema,
93+
maxReaders: maxReaders,
94+
logger: logger,
95+
manualSchemaManagement: manualSchemaManagement,
96+
);
8897
}
8998

9099
/// Open a [PowerSyncDatabase] with a [PowerSyncOpenFactory].
@@ -96,22 +105,32 @@ class PowerSyncDatabaseImpl
96105
///
97106
/// [logger] defaults to [autoLogger], which logs to the console in debug builds.
98107
factory PowerSyncDatabaseImpl.withFactory(
99-
DefaultSqliteOpenFactory openFactory,
100-
{required Schema schema,
101-
int maxReaders = SqliteDatabase.defaultMaxReaders,
102-
Logger? logger}) {
108+
DefaultSqliteOpenFactory openFactory, {
109+
required Schema schema,
110+
int maxReaders = SqliteDatabase.defaultMaxReaders,
111+
Logger? logger,
112+
bool manualSchemaManagement = false,
113+
}) {
103114
final db = SqliteDatabase.withFactory(openFactory, maxReaders: maxReaders);
104115
return PowerSyncDatabaseImpl.withDatabase(
105-
schema: schema, database: db, logger: logger);
116+
schema: schema,
117+
database: db,
118+
logger: logger,
119+
manualSchemaManagement: manualSchemaManagement,
120+
);
106121
}
107122

108123
/// Open a PowerSyncDatabase on an existing [SqliteDatabase].
109124
///
110125
/// Migrations are run on the database when this constructor is called.
111126
///
112127
/// [logger] defaults to [autoLogger], which logs to the console in debug builds.s
113-
PowerSyncDatabaseImpl.withDatabase(
114-
{required this.schema, required this.database, Logger? logger}) {
128+
PowerSyncDatabaseImpl.withDatabase({
129+
required this.schema,
130+
required this.database,
131+
Logger? logger,
132+
this.manualSchemaManagement = false,
133+
}) {
115134
this.logger = logger ?? autoLogger;
116135
isInitialized = baseInit();
117136
}
@@ -120,7 +139,7 @@ class PowerSyncDatabaseImpl
120139
@internal
121140
Future<void> connectInternal({
122141
required PowerSyncBackendConnector connector,
123-
required SyncOptions options,
142+
required ResolvedSyncOptions options,
124143
required AbortController abort,
125144
required Zone asyncWorkZone,
126145
}) async {
@@ -135,7 +154,6 @@ class PowerSyncDatabaseImpl
135154
SendPort? initPort;
136155
final hasInitPort = Completer<void>();
137156
final receivedIsolateExit = Completer<void>();
138-
final resolved = ResolvedSyncOptions(options);
139157

140158
Future<void> waitForShutdown() async {
141159
// Only complete the abortion signal after the isolate shuts down. This
@@ -183,7 +201,7 @@ class PowerSyncDatabaseImpl
183201
final port = initPort = data[1] as SendPort;
184202
hasInitPort.complete();
185203
var crudStream = database
186-
.onChange(['ps_crud'], throttle: resolved.crudThrottleTime);
204+
.onChange(['ps_crud'], throttle: options.crudThrottleTime);
187205
crudUpdateSubscription = crudStream.listen((event) {
188206
port.send(['update']);
189207
});
@@ -245,9 +263,10 @@ class PowerSyncDatabaseImpl
245263
_PowerSyncDatabaseIsolateArgs(
246264
receiveMessages.sendPort,
247265
dbRef,
248-
resolved,
266+
options,
249267
crudMutex.shared,
250268
syncMutex.shared,
269+
schema,
251270
),
252271
debugName: 'Sync ${database.openFactory.path}',
253272
onError: receiveUnhandledErrors.sendPort,
@@ -291,13 +310,15 @@ class _PowerSyncDatabaseIsolateArgs {
291310
final ResolvedSyncOptions options;
292311
final SerializedMutex crudMutex;
293312
final SerializedMutex syncMutex;
313+
final Schema schema;
294314

295315
_PowerSyncDatabaseIsolateArgs(
296316
this.sPort,
297317
this.dbRef,
298318
this.options,
299319
this.crudMutex,
300320
this.syncMutex,
321+
this.schema,
301322
);
302323
}
303324

@@ -393,6 +414,7 @@ Future<void> _syncIsolate(_PowerSyncDatabaseIsolateArgs args) async {
393414
final storage = BucketStorage(connection);
394415
final sync = StreamingSyncImplementation(
395416
adapter: storage,
417+
schema: args.schema,
396418
connector: InternalConnector(
397419
getCredentialsCached: getCredentialsCached,
398420
prefetchCredentials: prefetchCredentials,

packages/powersync_core/lib/src/database/powersync_database.dart

Lines changed: 41 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,23 @@ abstract class PowerSyncDatabase
3232
/// A maximum of [maxReaders] concurrent read transactions are allowed.
3333
///
3434
/// [logger] defaults to [autoLogger], which logs to the console in debug builds.
35-
factory PowerSyncDatabase(
36-
{required Schema schema,
37-
required String path,
38-
Logger? logger,
39-
@Deprecated("Use [PowerSyncDatabase.withFactory] instead.")
40-
// ignore: deprecated_member_use_from_same_package
41-
SqliteConnectionSetup? sqliteSetup}) {
35+
factory PowerSyncDatabase({
36+
required Schema schema,
37+
required String path,
38+
Logger? logger,
39+
bool manualSchemaManagement = false,
40+
@Deprecated("Use [PowerSyncDatabase.withFactory] instead.")
41+
// ignore: deprecated_member_use_from_same_package
42+
SqliteConnectionSetup? sqliteSetup,
43+
}) {
4244
return PowerSyncDatabaseImpl(
43-
schema: schema,
44-
path: path,
45-
logger: logger,
46-
// ignore: deprecated_member_use_from_same_package
47-
sqliteSetup: sqliteSetup);
45+
schema: schema,
46+
path: path,
47+
manualSchemaManagement: manualSchemaManagement,
48+
logger: logger,
49+
// ignore: deprecated_member_use_from_same_package
50+
sqliteSetup: sqliteSetup,
51+
);
4852
}
4953

5054
/// Open a [PowerSyncDatabase] with a [PowerSyncOpenFactory].
@@ -55,24 +59,38 @@ abstract class PowerSyncDatabase
5559
/// Subclass [PowerSyncOpenFactory] to add custom logic to this process.
5660
///
5761
/// [logger] defaults to [autoLogger], which logs to the console in debug builds.
58-
factory PowerSyncDatabase.withFactory(DefaultSqliteOpenFactory openFactory,
59-
{required Schema schema,
60-
int maxReaders = SqliteDatabase.defaultMaxReaders,
61-
Logger? logger}) {
62-
return PowerSyncDatabaseImpl.withFactory(openFactory,
63-
schema: schema, maxReaders: maxReaders, logger: logger);
62+
factory PowerSyncDatabase.withFactory(
63+
DefaultSqliteOpenFactory openFactory, {
64+
required Schema schema,
65+
int maxReaders = SqliteDatabase.defaultMaxReaders,
66+
bool manualSchemaManagement = false,
67+
Logger? logger,
68+
}) {
69+
return PowerSyncDatabaseImpl.withFactory(
70+
openFactory,
71+
schema: schema,
72+
maxReaders: maxReaders,
73+
manualSchemaManagement: manualSchemaManagement,
74+
logger: logger,
75+
);
6476
}
6577

6678
/// Open a PowerSyncDatabase on an existing [SqliteDatabase].
6779
///
6880
/// Migrations are run on the database when this constructor is called.
6981
///
7082
/// [logger] defaults to [autoLogger], which logs to the console in debug builds.
71-
factory PowerSyncDatabase.withDatabase(
72-
{required Schema schema,
73-
required SqliteDatabase database,
74-
Logger? loggers}) {
83+
factory PowerSyncDatabase.withDatabase({
84+
required Schema schema,
85+
required SqliteDatabase database,
86+
bool manualSchemaManagement = false,
87+
Logger? logger,
88+
}) {
7589
return PowerSyncDatabaseImpl.withDatabase(
76-
schema: schema, database: database, logger: loggers);
90+
schema: schema,
91+
database: database,
92+
manualSchemaManagement: manualSchemaManagement,
93+
logger: logger,
94+
);
7795
}
7896
}

packages/powersync_core/lib/src/database/powersync_database_impl_stub.dart

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class PowerSyncDatabaseImpl
3232
@override
3333
SqliteDatabase get database => throw UnimplementedError();
3434

35+
@override
36+
bool get manualSchemaManagement => throw UnimplementedError();
37+
3538
@override
3639
Future<void> get isInitialized => throw UnimplementedError();
3740

@@ -53,6 +56,7 @@ class PowerSyncDatabaseImpl
5356
{required Schema schema,
5457
required String path,
5558
int maxReaders = SqliteDatabase.defaultMaxReaders,
59+
bool manualSchemaManagement = false,
5660
Logger? logger,
5761
@Deprecated("Use [PowerSyncDatabase.withFactory] instead.")
5862
// ignore: deprecated_member_use_from_same_package
@@ -72,6 +76,7 @@ class PowerSyncDatabaseImpl
7276
DefaultSqliteOpenFactory openFactory, {
7377
required Schema schema,
7478
int maxReaders = SqliteDatabase.defaultMaxReaders,
79+
bool manualSchemaManagement = false,
7580
Logger? logger,
7681
}) {
7782
throw UnimplementedError();
@@ -82,10 +87,12 @@ class PowerSyncDatabaseImpl
8287
/// Migrations are run on the database when this constructor is called.
8388
///
8489
/// [logger] defaults to [autoLogger], which logs to the console in debug builds.s
85-
factory PowerSyncDatabaseImpl.withDatabase(
86-
{required Schema schema,
87-
required SqliteDatabase database,
88-
Logger? logger}) {
90+
factory PowerSyncDatabaseImpl.withDatabase({
91+
required Schema schema,
92+
required SqliteDatabase database,
93+
bool manualSchemaManagement = false,
94+
Logger? logger,
95+
}) {
8996
throw UnimplementedError();
9097
}
9198

@@ -115,7 +122,7 @@ class PowerSyncDatabaseImpl
115122
required PowerSyncBackendConnector connector,
116123
required AbortController abort,
117124
required Zone asyncWorkZone,
118-
required SyncOptions options,
125+
required ResolvedSyncOptions options,
119126
}) {
120127
throw UnimplementedError();
121128
}

packages/powersync_core/lib/src/database/powersync_db_mixin.dart

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
3838
/// Use [attachedLogger] to propagate logs to [Logger.root] for custom logging.
3939
Logger get logger;
4040

41+
bool get manualSchemaManagement;
42+
43+
bool _manualSchemaManagementCompleted = false;
44+
4145
@Deprecated("This field is unused, pass params to connect() instead")
4246
Map<String, dynamic>? clientParams;
4347

@@ -110,10 +114,36 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
110114
statusStream = statusStreamController.stream;
111115
updates = powerSyncUpdateNotifications(database.updates);
112116

117+
_manualSchemaManagementCompleted = false;
118+
113119
await database.initialize();
114120
await _checkVersion();
115121
await database.execute('SELECT powersync_init()');
116-
await updateSchema(schema);
122+
123+
if (!manualSchemaManagement) {
124+
// Create the internal db schema
125+
await updateSchema(schema);
126+
await _afterSchemaReady();
127+
}
128+
}
129+
130+
Future<void> markSchemaAsReady() async {
131+
await isInitialized;
132+
_manualSchemaManagementCompleted = true;
133+
134+
await _afterSchemaReady();
135+
}
136+
137+
void _assertSchemaIsReady() {
138+
if (!manualSchemaManagement || _manualSchemaManagementCompleted) {
139+
return;
140+
}
141+
142+
throw AssertionError(
143+
'In manual schema management mode, you need to mark the powersync database as ready');
144+
}
145+
146+
Future<void> _afterSchemaReady() async {
117147
await _updateHasSynced();
118148
}
119149

@@ -289,11 +319,14 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
289319
// the lock for the connection.
290320
await initialize();
291321

292-
final resolvedOptions = SyncOptions(
293-
crudThrottleTime: options?.crudThrottleTime ?? crudThrottleTime,
322+
_assertSchemaIsReady();
323+
324+
final resolvedOptions = ResolvedSyncOptions.resolve(
325+
options,
326+
crudThrottleTime: crudThrottleTime,
294327
// ignore: deprecated_member_use_from_same_package
295-
retryDelay: options?.retryDelay ?? retryDelay,
296-
params: options?.params ?? params,
328+
retryDelay: retryDelay,
329+
params: params,
297330
);
298331

299332
// ignore: deprecated_member_use_from_same_package
@@ -362,7 +395,7 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
362395
@internal
363396
Future<void> connectInternal({
364397
required PowerSyncBackendConnector connector,
365-
required SyncOptions options,
398+
required ResolvedSyncOptions options,
366399
required AbortController abort,
367400
required Zone asyncWorkZone,
368401
});
@@ -451,13 +484,15 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
451484
/// Get an unique id for this client.
452485
/// This id is only reset when the database is deleted.
453486
Future<String> getClientId() async {
487+
_assertSchemaIsReady(); // TODO(skilldevs): Needed?
454488
final row = await get('SELECT powersync_client_id() as client_id');
455489
return row['client_id'] as String;
456490
}
457491

458492
/// Get upload queue size estimate and count.
459493
Future<UploadQueueStats> getUploadQueueStats(
460494
{bool includeSize = false}) async {
495+
_assertSchemaIsReady();
461496
if (includeSize) {
462497
final row = await getOptional(
463498
'SELECT SUM(cast(data as blob) + 20) as size, count(*) as count FROM ps_crud');
@@ -485,6 +520,7 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
485520
/// data by transaction. One batch may contain data from multiple transactions,
486521
/// and a single transaction may be split over multiple batches.
487522
Future<CrudBatch?> getCrudBatch({int limit = 100}) async {
523+
_assertSchemaIsReady();
488524
final rows = await getAll(
489525
'SELECT id, tx_id, data FROM ps_crud ORDER BY id ASC LIMIT ?',
490526
[limit + 1]);
@@ -531,6 +567,7 @@ mixin PowerSyncDatabaseMixin implements SqliteConnection {
531567
/// Unlike [getCrudBatch], this only returns data from a single transaction at a time.
532568
/// All data for the transaction is loaded into memory.
533569
Future<CrudTransaction?> getNextCrudTransaction() async {
570+
_assertSchemaIsReady();
534571
return await readTransaction((tx) async {
535572
final first = await tx.getOptional(
536573
'SELECT id, tx_id, data FROM ps_crud ORDER BY id ASC LIMIT 1');

0 commit comments

Comments
 (0)