Skip to content

Commit e8de1f2

Browse files
authored
Merge pull request #29 from powersync-ja/feat/optional-parameters
[feat] Optional parameters
2 parents 6146f59 + 48ca53e commit e8de1f2

6 files changed

Lines changed: 49 additions & 31 deletions

File tree

PowerSync/PowerSync.Common/Client/PowerSyncDatabase.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ public interface IPowerSyncDatabase : IEventStream<PowerSyncDBEvent>
5858

5959
public Task<CrudTransaction?> GetNextCrudTransaction();
6060

61-
Task<NonQueryResult> Execute(string query, object[]? parameters = null);
61+
Task<NonQueryResult> Execute(string query, object?[]? parameters = null);
6262

63-
Task<T[]> GetAll<T>(string sql, object[]? parameters = null);
63+
Task<T[]> GetAll<T>(string sql, object?[]? parameters = null);
6464

65-
Task<T?> GetOptional<T>(string sql, object[]? parameters = null);
65+
Task<T?> GetOptional<T>(string sql, object?[]? parameters = null);
6666

67-
Task<T> Get<T>(string sql, object[]? parameters = null);
67+
Task<T> Get<T>(string sql, object?[]? parameters = null);
6868

6969
Task<T> ReadLock<T>(Func<ILockContext, Task<T>> fn, DBLockOptions? options = null);
7070

@@ -549,24 +549,24 @@ public async Task<string> GetClientId()
549549
return await BucketStorageAdapter.GetClientId();
550550
}
551551

552-
public async Task<NonQueryResult> Execute(string query, object[]? parameters = null)
552+
public async Task<NonQueryResult> Execute(string query, object?[]? parameters = null)
553553
{
554554
await WaitForReady();
555555
return await Database.Execute(query, parameters);
556556
}
557557

558-
public async Task<T[]> GetAll<T>(string query, object[]? parameters = null)
558+
public async Task<T[]> GetAll<T>(string query, object?[]? parameters = null)
559559
{
560560
await WaitForReady();
561561
return await Database.GetAll<T>(query, parameters);
562562
}
563563

564-
public async Task<T?> GetOptional<T>(string query, object[]? parameters = null)
564+
public async Task<T?> GetOptional<T>(string query, object?[]? parameters = null)
565565
{
566566
await WaitForReady();
567567
return await Database.GetOptional<T>(query, parameters);
568568
}
569-
public async Task<T> Get<T>(string query, object[]? parameters = null)
569+
public async Task<T> Get<T>(string query, object?[]? parameters = null)
570570
{
571571
await WaitForReady();
572572
return await Database.Get<T>(query, parameters);
@@ -614,7 +614,7 @@ public async Task<T> WriteTransaction<T>(Func<ITransaction, Task<T>> fn, DBLockO
614614
/// Use <see cref="SQLWatchOptions.ThrottleMs"/> to specify the minimum interval between queries.
615615
/// Source tables are automatically detected using <c>EXPLAIN QUERY PLAN</c>.
616616
/// </summary>
617-
public Task Watch<T>(string query, object[]? parameters, WatchHandler<T> handler, SQLWatchOptions? options = null)
617+
public Task Watch<T>(string query, object?[]? parameters, WatchHandler<T> handler, SQLWatchOptions? options = null)
618618
{
619619
var tcs = new TaskCompletionSource<bool>();
620620
Task.Run(async () =>
@@ -658,7 +658,7 @@ public Task Watch<T>(string query, object[]? parameters, WatchHandler<T> handler
658658

659659
private record ExplainedResult(string opcode, int p2, int p3);
660660
private record TableSelectResult(string tbl_name);
661-
public async Task<string[]> ResolveTables(string sql, object[]? parameters = null, SQLWatchOptions? options = null)
661+
public async Task<string[]> ResolveTables(string sql, object?[]? parameters = null, SQLWatchOptions? options = null)
662662
{
663663
List<string> resolvedTables = options?.Tables != null ? [.. options.Tables] : [];
664664

PowerSync/PowerSync.Common/DB/IDBAdapter.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,19 @@ public class QueryRows
3030
public interface IDBGetUtils
3131
{
3232
// Execute a read-only query and return results.
33-
Task<T[]> GetAll<T>(string sql, object[]? parameters = null);
33+
Task<T[]> GetAll<T>(string sql, object?[]? parameters = null);
3434

3535
// Execute a read-only query and return the first result, or null if the ResultSet is empty.
36-
Task<T?> GetOptional<T>(string sql, object[]? parameters = null);
36+
Task<T?> GetOptional<T>(string sql, object?[]? parameters = null);
3737

3838
// Execute a read-only query and return the first result, error if the ResultSet is empty.
39-
Task<T> Get<T>(string sql, object[]? parameters = null);
39+
Task<T> Get<T>(string sql, object?[]? parameters = null);
4040
}
4141

4242
public interface ILockContext : IDBGetUtils
4343
{
4444
// Execute a single write statement.
45-
Task<NonQueryResult> Execute(string query, object[]? parameters = null);
45+
Task<NonQueryResult> Execute(string query, object?[]? parameters = null);
4646
}
4747

4848
public interface ITransaction : ILockContext
@@ -117,7 +117,7 @@ public interface IDBAdapter : IEventStream<DBAdapterEvent>, ILockContext
117117
/// <summary>
118118
/// Execute a batch of write statements.
119119
/// </summary>
120-
Task<QueryResult> ExecuteBatch(string query, object[][]? parameters = null);
120+
Task<QueryResult> ExecuteBatch(string query, object?[][]? parameters = null);
121121

122122
/// <summary>
123123
/// The name of the adapter.

PowerSync/PowerSync.Common/MDSQLite/MDSQLiteAdapter.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,29 +144,29 @@ protected virtual void LoadExtension(SqliteConnection db)
144144
readConnection?.Close();
145145
}
146146

147-
public async Task<NonQueryResult> Execute(string query, object[]? parameters = null)
147+
public async Task<NonQueryResult> Execute(string query, object?[]? parameters = null)
148148
{
149149
return await WriteLock((ctx) => ctx.Execute(query, parameters));
150150
}
151151

152-
public Task<QueryResult> ExecuteBatch(string query, object[][]? parameters = null)
152+
public Task<QueryResult> ExecuteBatch(string query, object?[][]? parameters = null)
153153
{
154154
// https://learn.microsoft.com/en-gb/dotnet/standard/data/sqlite/batching
155155
throw new NotImplementedException();
156156
}
157157

158-
public async Task<T> Get<T>(string sql, object[]? parameters = null)
158+
public async Task<T> Get<T>(string sql, object?[]? parameters = null)
159159
{
160160
return await ReadLock((ctx) => ctx.Get<T>(sql, parameters));
161161
;
162162
}
163163

164-
public async Task<T[]> GetAll<T>(string sql, object[]? parameters = null)
164+
public async Task<T[]> GetAll<T>(string sql, object?[]? parameters = null)
165165
{
166166
return await ReadLock((ctx) => ctx.GetAll<T>(sql, parameters));
167167
}
168168

169-
public async Task<T?> GetOptional<T>(string sql, object[]? parameters = null)
169+
public async Task<T?> GetOptional<T>(string sql, object?[]? parameters = null)
170170
{
171171
return await ReadLock((ctx) => ctx.GetOptional<T>(sql, parameters));
172172
}
@@ -302,22 +302,22 @@ public async Task Rollback()
302302
await connection.Execute("ROLLBACK");
303303
}
304304

305-
public Task<NonQueryResult> Execute(string query, object[]? parameters = null)
305+
public Task<NonQueryResult> Execute(string query, object?[]? parameters = null)
306306
{
307307
return connection.Execute(query, parameters);
308308
}
309309

310-
public Task<T> Get<T>(string sql, object[]? parameters = null)
310+
public Task<T> Get<T>(string sql, object?[]? parameters = null)
311311
{
312312
return connection.Get<T>(sql, parameters);
313313
}
314314

315-
public Task<T[]> GetAll<T>(string sql, object[]? parameters = null)
315+
public Task<T[]> GetAll<T>(string sql, object?[]? parameters = null)
316316
{
317317
return connection.GetAll<T>(sql, parameters);
318318
}
319319

320-
public Task<T?> GetOptional<T>(string sql, object[]? parameters = null)
320+
public Task<T?> GetOptional<T>(string sql, object?[]? parameters = null)
321321
{
322322
return connection.GetOptional<T>(sql, parameters);
323323
}

PowerSync/PowerSync.Common/MDSQLite/MDSQLiteConnection.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public void FlushUpdates()
7272
Emit(new DBAdapterEvent { TablesUpdated = batchedUpdate });
7373
}
7474

75-
private static void PrepareCommand(SqliteCommand command, string query, object[]? parameters)
75+
private static void PrepareCommand(SqliteCommand command, string query, object?[]? parameters)
7676
{
7777
if (parameters == null || parameters.Length == 0)
7878
{
@@ -113,7 +113,7 @@ private static void PrepareCommand(SqliteCommand command, string query, object[]
113113
}
114114
}
115115

116-
public async Task<NonQueryResult> Execute(string query, object[]? parameters = null)
116+
public async Task<NonQueryResult> Execute(string query, object?[]? parameters = null)
117117
{
118118
using var command = Db.CreateCommand();
119119
PrepareCommand(command, query, parameters);
@@ -127,7 +127,7 @@ public async Task<NonQueryResult> Execute(string query, object[]? parameters = n
127127
};
128128
}
129129

130-
public async Task<QueryResult> ExecuteQuery(string query, object[]? parameters = null)
130+
public async Task<QueryResult> ExecuteQuery(string query, object?[]? parameters = null)
131131
{
132132
var result = new QueryResult();
133133
using var command = Db.CreateCommand();
@@ -151,7 +151,7 @@ public async Task<QueryResult> ExecuteQuery(string query, object[]? parameters =
151151
return result;
152152
}
153153

154-
public async Task<T[]> GetAll<T>(string sql, object[]? parameters = null)
154+
public async Task<T[]> GetAll<T>(string sql, object?[]? parameters = null)
155155
{
156156
var result = await ExecuteQuery(sql, parameters);
157157

@@ -177,7 +177,7 @@ public async Task<T[]> GetAll<T>(string sql, object[]? parameters = null)
177177
return [.. items];
178178
}
179179

180-
public async Task<T?> GetOptional<T>(string sql, object[]? parameters = null)
180+
public async Task<T?> GetOptional<T>(string sql, object?[]? parameters = null)
181181
{
182182
var result = await ExecuteQuery(sql, parameters);
183183

@@ -198,7 +198,7 @@ public async Task<T[]> GetAll<T>(string sql, object[]? parameters = null)
198198
return JsonConvert.DeserializeObject<T>(json);
199199
}
200200

201-
public async Task<T> Get<T>(string sql, object[]? parameters = null)
201+
public async Task<T> Get<T>(string sql, object?[]? parameters = null)
202202
{
203203
return await GetOptional<T>(sql, parameters) ?? throw new InvalidOperationException("Result set is empty");
204204
}

Tests/PowerSync/PowerSync.Common.Tests/Client/PowerSyncDatabaseTests.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,25 @@ await db.Execute(
7070
Assert.Equal(age.ToString(), row.make);
7171
}
7272

73+
[Fact]
74+
public async Task QueryWithNullParams()
75+
{
76+
var id = Guid.NewGuid().ToString();
77+
var name = "Test user";
78+
79+
await db.Execute(
80+
"INSERT INTO assets(id, description, make) VALUES(?, ?, ?)",
81+
[id, name, null]
82+
);
83+
84+
var result = await db.GetAll<AssetResult>("SELECT id, description, make FROM assets WHERE id = ? AND make IS NULL", [id]);
85+
86+
Assert.Single(result);
87+
var row = result.First();
88+
Assert.Equal(name, row.description);
89+
Assert.Equal(null, row.make);
90+
}
91+
7392
[Fact]
7493
public async Task FailedInsertTest()
7594
{

demos/CommandLine/Demo.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ static async Task Main()
102102

103103
await db.Connect(connector, new PowerSync.Common.Client.Sync.Stream.PowerSyncConnectionOptions
104104
{
105-
ClientImplementation = PowerSync.Common.Client.Sync.Stream.SyncClientImplementation.RUST,
106105
AppMetadata = new Dictionary<string, string>
107106
{
108107
{ "app_version", GetAppVersion() },

0 commit comments

Comments
 (0)