-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathIDBAdapter.cs
More file actions
168 lines (140 loc) · 5 KB
/
IDBAdapter.cs
File metadata and controls
168 lines (140 loc) · 5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
namespace PowerSync.Common.DB;
using System.Collections.Generic;
using System.Threading.Tasks;
using PowerSync.Common.Utils;
public class NonQueryResult
{
// Represents the auto-generated row id if applicable.
public long? InsertId { get; set; }
// Number of affected rows.
public int RowsAffected { get; set; }
}
public class QueryResult
{
public class QueryRows
{
// Raw array with all dataset.
public List<Dictionary<string, object>> Array { get; set; } = [];
// The length of the dataset.
public int Length => Array.Count;
}
public QueryRows Rows { get; set; } = new QueryRows();
}
public interface IDBGetUtils
{
// Execute a read-only query and return results.
Task<T[]> GetAll<T>(string sql, object?[]? parameters = null);
Task<dynamic[]> GetAll(string sql, object?[]? parameters = null);
// Execute a read-only query and return the first result, or null if the ResultSet is empty.
Task<T?> GetOptional<T>(string sql, object?[]? parameters = null);
Task<dynamic?> GetOptional(string sql, object?[]? parameters = null);
// Execute a read-only query and return the first result, error if the ResultSet is empty.
Task<T> Get<T>(string sql, object?[]? parameters = null);
Task<dynamic> Get(string sql, object?[]? parameters = null);
}
public interface ILockContext : IDBGetUtils
{
// Execute a single write statement.
Task<NonQueryResult> Execute(string query, object?[]? parameters = null);
// Execute a batch of write statements.
Task<NonQueryResult> ExecuteBatch(string query, object?[][]? parameters = null);
}
public interface ITransaction : ILockContext
{
// Commit multiple changes to the local DB using the Transaction context.
Task Commit();
// Roll back multiple attempted changes using the Transaction context.
Task Rollback();
}
public enum RowUpdateType
{
SQLITE_INSERT = 18,
SQLITE_DELETE = 9,
SQLITE_UPDATE = 23
}
public class TableUpdateOperation(RowUpdateType OpType, long RowId)
{
public RowUpdateType OpType { get; set; } = OpType;
public long RowId { get; set; } = RowId;
}
public interface INotification
{
}
public class UpdateNotification(string table, RowUpdateType OpType, long RowId) : TableUpdateOperation(OpType, RowId), INotification
{
public string Table { get; set; } = table;
}
public class BatchedUpdateNotification : INotification
{
public UpdateNotification[] RawUpdates { get; set; } = [];
public string[] Tables { get; set; } = [];
public Dictionary<string, TableUpdateOperation[]> GroupedUpdates { get; set; } = [];
}
public class DBAdapterEvent
{
public INotification? TablesUpdated { get; set; }
}
public class DBLockOptions
{
// Optional timeout in milliseconds.
public int? TimeoutMs { get; set; }
}
public class DBAdapterUtils
{
public static string[] ExtractTableUpdates(INotification update)
{
return update switch
{
BatchedUpdateNotification batchedUpdate => batchedUpdate.Tables,
UpdateNotification singleUpdate => [singleUpdate.Table],
_ => throw new ArgumentException("Invalid update type", nameof(update))
};
}
}
public interface IDBAdapter : IEventStream<DBAdapterEvent>, ILockContext
{
/// <summary>
/// Closes the adapter.
/// </summary>
new Task Close();
/// <summary>
/// The name of the adapter.
/// </summary>
string Name { get; }
/// <summary>
/// Executes a read lock with the given function.
/// </summary>
Task<T> ReadLock<T>(Func<ILockContext, Task<T>> fn, DBLockOptions? options = null);
/// <summary>
/// Executes a read transaction with the given function.
/// </summary>
Task<T> ReadTransaction<T>(Func<ITransaction, Task<T>> fn, DBLockOptions? options = null);
/// <summary>
/// Executes a write lock with the given function.
/// </summary>
Task WriteLock(Func<ILockContext, Task> fn, DBLockOptions? options = null);
/// <summary>
/// Executes a write lock with the given function.
/// </summary>
///
/// <remarks>
/// This is an overload of <see cref="WriteLock"/> that allows the function to return a result.
/// </remarks>
Task<T> WriteLock<T>(Func<ILockContext, Task<T>> fn, DBLockOptions? options = null);
/// <summary>
/// Executes a write transaction with the given function.
/// </summary>
Task WriteTransaction(Func<ITransaction, Task> fn, DBLockOptions? options = null);
/// <summary>
/// Executes a write transaction with the given function.
/// </summary>
///
/// <remarks>
/// This is an overload of <see cref="WriteTransaction"/> that allows the function to return a result.
/// </remarks>
Task<T> WriteTransaction<T>(Func<ITransaction, Task<T>> fn, DBLockOptions? options = null);
/// <summary>
/// This method refreshes the schema information across all connections. This is for advanced use cases, and should generally not be needed.
/// </summary>
Task RefreshSchema();
}