Skip to content

Commit fb175d7

Browse files
committed
Internalise statement value binding
1 parent 5401452 commit fb175d7

File tree

7 files changed

+183
-92
lines changed

7 files changed

+183
-92
lines changed

Libsql.Client.Tests/PositionalArgumentTests.cs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class PositionalArgumentTests
77
[Fact]
88
public async Task SingleParameter()
99
{
10-
var rs = await _db.Query("SELECT ?", 1);
10+
var rs = await _db.Query("SELECT ?", (Integer)1);
1111
var row = rs.Rows.First();
1212
var value = row.First();
1313
var integer = Assert.IsType<Integer>(value);
@@ -18,8 +18,9 @@ public async Task SingleParameter()
1818
[Fact]
1919
public async Task MultipleParameters()
2020
{
21-
var rs = await _db.Query("SELECT ?, ?, ?", 1.0, "2", 3);
21+
var rs = await _db.Query("SELECT ?, ?, ?", (Real)1.0, (Text)"2", (Integer)3);
2222
var row = rs.Rows.First();
23+
Console.WriteLine(rs.Rows);
2324
var integer = Assert.IsType<Integer>(row.Skip(2).First());
2425

2526
Assert.Equal(3, integer.Value);
@@ -28,7 +29,7 @@ public async Task MultipleParameters()
2829
[Fact]
2930
public async Task BindIntParameter()
3031
{
31-
var rs = await _db.Query("SELECT ?", 1);
32+
var rs = await _db.Query("SELECT ?", (Integer)1);
3233
var row = rs.Rows.First();
3334
var value = row.First();
3435
var integer = Assert.IsType<Integer>(value);
@@ -39,7 +40,7 @@ public async Task BindIntParameter()
3940
[Fact]
4041
public async Task BindRealParameter()
4142
{
42-
var rs = await _db.Query("SELECT ?", 1.0);
43+
var rs = await _db.Query("SELECT ?", (Real)1.0);
4344
var row = rs.Rows.First();
4445
var value = row.First();
4546
var real = Assert.IsType<Real>(value);
@@ -50,7 +51,7 @@ public async Task BindRealParameter()
5051
[Fact]
5152
public async Task BindStringParameter()
5253
{
53-
var rs = await _db.Query("SELECT ?", "hello");
54+
var rs = await _db.Query("SELECT ?", (Text)"hello");
5455
var row = rs.Rows.First();
5556
var value = row.First();
5657
var text = Assert.IsType<Text>(value);
@@ -60,6 +61,15 @@ public async Task BindStringParameter()
6061

6162
[Fact]
6263
public async Task BindSingleNullParameter()
64+
{
65+
var rs = await _db.Query("SELECT ?", Value.Null);
66+
var row = rs.Rows.First();
67+
var value = row.First();
68+
Assert.IsType<Null>(value);
69+
}
70+
71+
[Fact]
72+
public async Task BindSingleNullParameter_Implicit()
6373
{
6474
var rs = await _db.Query("SELECT ?", null);
6575
var row = rs.Rows.First();
@@ -70,7 +80,7 @@ public async Task BindSingleNullParameter()
7080
[Fact]
7181
public async Task BindMultipleParametersWithANull()
7282
{
73-
var rs = await _db.Query("SELECT ?, ?, ?", 1, null, 3);
83+
var rs = await _db.Query("SELECT ?, ?, ?", (Integer)1, Value.Null, (Integer)3);
7484
var row = rs.Rows.First();
7585
var value = row.Skip(1).First();
7686
Assert.IsType<Null>(value);
@@ -79,7 +89,7 @@ public async Task BindMultipleParametersWithANull()
7989
[Fact]
8090
public async Task BindBlobParameter()
8191
{
82-
var rs = await _db.Query("SELECT ?", new byte[] { 1, 2, 3 });
92+
var rs = await _db.Query("SELECT ?", (Blob)new byte[] { 1, 2, 3 });
8393
var row = rs.Rows.First();
8494
var value = row.First();
8595
var blob = Assert.IsType<Blob>(value);

Libsql.Client/DatabaseWrapper.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -141,21 +141,21 @@ public Task<IResultSet> Query(string sql)
141141
{
142142
return Task.Run(() =>
143143
{
144-
using (var statement = new StatementWrapper(this, _connection, sql))
144+
using (var statement = new StatementBuilder(this, _connection, sql))
145145
{
146-
return QueryStatement(statement);
146+
return QueryStatement(statement.Build());
147147
}
148148
});
149149
}
150150

151-
public Task<IResultSet> Query(string sql, params object[] args)
151+
public Task<IResultSet> Query(string sql, params Value[] args)
152152
{
153153
return Task.Run(() => {
154-
using (var statement = new StatementWrapper(this, _connection, sql))
154+
using (var statement = new StatementBuilder(this, _connection, sql))
155155
{
156-
statement.BindAll(args);
156+
statement.Bind(args);
157157

158-
return QueryStatement(statement);
158+
return QueryStatement(statement.Build());
159159
}
160160
});
161161
}
@@ -164,21 +164,21 @@ public Task<ulong> Execute(string sql)
164164
{
165165
return Task.Run(() =>
166166
{
167-
using (var statement = new StatementWrapper(this, _connection, sql))
167+
using (var statement = new StatementBuilder(this, _connection, sql))
168168
{
169-
return ExecuteStatement(statement);
169+
return ExecuteStatement(statement.Build());
170170
};
171171
});
172172
}
173173

174-
public Task<ulong> Execute(string sql, params object[] args)
174+
public Task<ulong> Execute(string sql, params Value[] args)
175175
{
176176
return Task.Run(() => {
177-
using (var statement = new StatementWrapper(this, _connection, sql))
177+
using (var statement = new StatementBuilder(this, _connection, sql))
178178
{
179-
statement.BindAll(args);
179+
statement.Bind(args);
180180

181-
return ExecuteStatement(statement);
181+
return ExecuteStatement(statement.Build());
182182
}
183183
});
184184
}
@@ -193,17 +193,17 @@ public Task<ulong> Execute(IStatement statement)
193193
return statement.Execute();
194194
}
195195

196-
internal Task<IResultSet> Query(StatementWrapper statement)
196+
internal Task<IResultSet> Query(StatementBuilder statement)
197197
{
198198
return Task.Run(() => {
199-
return QueryStatement(statement);
199+
return QueryStatement(statement.Build());
200200
});
201201
}
202202

203-
internal Task<ulong> Execute(StatementWrapper statement)
203+
internal Task<ulong> Execute(StatementBuilder statement)
204204
{
205205
return Task.Run(() => {
206-
return ExecuteStatement(statement);
206+
return ExecuteStatement(statement.Build());
207207
});
208208
}
209209

@@ -261,7 +261,7 @@ public Task Sync()
261261

262262
public Task<IStatement> Prepare(string sql)
263263
{
264-
return Task.Run<IStatement>(() => new StatementWrapper(this, _connection, sql));
264+
return Task.Run<IStatement>(() => new StatementBuilder(this, _connection, sql));
265265
}
266266

267267
private void ReleaseUnmanagedResources()

Libsql.Client/IDatabaseClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public interface IDatabaseClient
2222
/// <param name="args">The parameters to use in the query.</param>
2323
/// <returns>The result set returned by the query.</returns>
2424
/// <exception cref="LibsqlException">Thrown when the query fails to execute.</exception>
25-
Task<IResultSet> Query(string sql, params object[] args);
25+
Task<IResultSet> Query(string sql, params Value[] args);
2626

2727
/// <summary>
2828
/// Executes the given prepared statement and returns the result set.
@@ -47,7 +47,7 @@ public interface IDatabaseClient
4747
/// <param name="args">The parameters to use in the query.</param>
4848
/// <returns>The number of affected rows.</returns>
4949
/// <exception cref="LibsqlException">Thrown when the SQL fails to execute.</exception>
50-
Task<ulong> Execute(string sql, params object[] args);
50+
Task<ulong> Execute(string sql, params Value[] args);
5151

5252
/// <summary>
5353
/// Executes the given prepared statement.

Libsql.Client/Rows.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ private unsafe Value[] ParseRow(libsql_row_t row)
122122
_enumeratorData.ColumnTypes[i] == ValueType.Real ? row.GetReal(i) :
123123
_enumeratorData.ColumnTypes[i] == ValueType.Text ? row.GetText(i) :
124124
_enumeratorData.ColumnTypes[i] == ValueType.Blob ? row.GetBlob(i) :
125-
_enumeratorData.ColumnTypes[i] == ValueType.Null ? (Value)new Null() :
125+
_enumeratorData.ColumnTypes[i] == ValueType.Null ? (Value)Value.Null :
126126
throw new ArgumentOutOfRangeException($"Non-exhaustive check. Could not find a case to match value of {_enumeratorData.ColumnTypes[i]}");
127127

128128
parsedRow[i] = value;

Libsql.Client/StatementBuilder.cs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.Diagnostics;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Libsql.Client
10+
{
11+
internal class StatementBuilder : IStatement
12+
{
13+
int IStatement.BoundValuesCount => _values.Count;
14+
private StatementWrapper _stmt;
15+
private readonly libsql_connection_t _connection;
16+
private readonly string _sql;
17+
private readonly DatabaseWrapper _database;
18+
private IList<Value> _values = new List<Value>(100);
19+
20+
public unsafe StatementBuilder(DatabaseWrapper database, libsql_connection_t connection, string sql)
21+
{
22+
_connection = connection;
23+
_sql = sql;
24+
_database = database;
25+
_stmt = new StatementWrapper(connection, sql);
26+
27+
Debug.Assert(_database != null);
28+
}
29+
30+
public StatementWrapper Build()
31+
{
32+
foreach (var value in _values)
33+
{
34+
switch (value)
35+
{
36+
case Integer i:
37+
_stmt.BindInt(i);
38+
break;
39+
case Real d:
40+
_stmt.BindFloat(d);
41+
break;
42+
case Text s:
43+
_stmt.BindString(s);
44+
break;
45+
case Blob b:
46+
_stmt.BindBlob(b);
47+
break;
48+
case Null _:
49+
case null:
50+
_stmt.BindNull();
51+
break;
52+
default:
53+
throw new ArgumentException($"Unsupported argument type: {value.GetType()}");
54+
}
55+
}
56+
57+
return _stmt;
58+
}
59+
60+
public unsafe void Bind(Value value)
61+
{
62+
_values.Add(value);
63+
}
64+
65+
public unsafe void Bind(params Value[] values)
66+
{
67+
if (values is null) return;
68+
69+
foreach (var value in values) {
70+
_values.Add(value);
71+
}
72+
}
73+
74+
public void Bind(Integer integer)
75+
{
76+
_values.Add(integer);
77+
}
78+
79+
public void Bind(Real real)
80+
{
81+
_values.Add(real);
82+
}
83+
84+
public void Bind(Text text)
85+
{
86+
_values.Add(text);
87+
}
88+
89+
public void Bind(Blob blob)
90+
{
91+
_values.Add(blob);
92+
}
93+
94+
public void BindNull()
95+
{
96+
_values.Add(Value.Null);
97+
}
98+
99+
public Task<ulong> Execute()
100+
{
101+
return _database.Execute(this);
102+
}
103+
104+
public Task<IResultSet> Query()
105+
{
106+
return _database.Query(this);
107+
}
108+
109+
public void Reset()
110+
{
111+
Bindings.libsql_free_stmt(_stmt.Stmt);
112+
113+
_stmt = new StatementWrapper(_connection, _sql);
114+
_values = new List<Value>(100);
115+
}
116+
117+
118+
private void ReleaseUnmanagedResources()
119+
{
120+
Bindings.libsql_free_stmt(_stmt.Stmt);
121+
}
122+
123+
public void Dispose()
124+
{
125+
ReleaseUnmanagedResources();
126+
GC.SuppressFinalize(this);
127+
}
128+
129+
~StatementBuilder()
130+
{
131+
ReleaseUnmanagedResources();
132+
}
133+
}
134+
}

0 commit comments

Comments
 (0)