Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d35a8c7
Differentiate Query and Execute query types
tvandinther Aug 31, 2024
dc21606
Refactor to add statement interface
tvandinther Aug 31, 2024
9c07064
Implement Real binding
tvandinther Aug 31, 2024
8c8405d
Implement Text binding
tvandinther Aug 31, 2024
602537c
Implement Blob binding
tvandinther Aug 31, 2024
c15febc
Implement Null binding
tvandinther Aug 31, 2024
ac1d18f
Update README.md
tvandinther Aug 31, 2024
578dd5a
Remove writelines
tvandinther Aug 31, 2024
be88102
Add BoundValuesCount test
tvandinther Aug 31, 2024
669a37e
Fix prepared statement disposable bug
tvandinther Aug 31, 2024
a8f090f
Update README to address IDisposable on prepared statements
tvandinther Aug 31, 2024
2d38915
Remove console.write
tvandinther Aug 31, 2024
33537fd
Add behaviour-finding tests and update README to reflect findings
tvandinther Aug 31, 2024
931b4e6
Implement statement reset
tvandinther Aug 31, 2024
1595d32
Add index argument to bind methods and add xmldoc
tvandinther Sep 5, 2024
2fb7e32
Implement parameter count
tvandinther Sep 5, 2024
8be0390
WIP
tvandinther Aug 30, 2025
59ee312
Remove column names
tvandinther Aug 31, 2025
9f078ae
Add devbox in favour of nix shell
tvandinther Aug 31, 2025
ee9d84c
Update libsql to 0.9.21
tvandinther Aug 31, 2025
79c534b
update script to allow for more options
tvandinther Aug 31, 2025
8900ade
Add just
tvandinther Aug 31, 2025
2b3c305
add workspace extension recommendations
tvandinther Aug 31, 2025
848afd0
Remove devbox from CI
tvandinther Aug 31, 2025
65c2a80
Update README to include checkmark on prepared statements
tvandinther Aug 31, 2025
f318d3d
Update Demo to use new syntax
tvandinther Aug 31, 2025
1d5e778
Simplify script
tvandinther Aug 31, 2025
d9f767c
WIP
tvandinther Aug 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-dependencies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Generate bindings
if: |
steps.cache-rust-bindings.outputs.cache-hit != 'true'
run: ./generate-bindings.sh
run: ./generate-bindings.sh --remote https://github.com/tvandinther/libsql --commit add-missing-statement-functions
shell: bash

- name: Upload bindings
Expand Down
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"nefrob.vscode-just-syntax",
"ms-dotnettools.csharp",
"rust-lang.rust-analyzer"
]
}
8 changes: 4 additions & 4 deletions Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@


// Execute SQL statements directly
var rs = await dbClient.Execute("CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `height` REAL, `data` BLOB)");
var rs = await dbClient.Query("CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT NOT NULL, `height` REAL, `data` BLOB)");


// Read the results by using the IResultSet interface
var rs1 = await dbClient.Execute("INSERT INTO `users` (`name`, `height`, `data`) VALUES ('John Doe', 182.6, X'a4c7b8e21d3f50a6b9d2e8f7c1349a0b5c6d7e218349b6d012c71e8f9a093fed'), ('Jane Doe', 0.5, X'00')");
var rs1 = await dbClient.Query("INSERT INTO `users` (`name`, `height`, `data`) VALUES ('John Doe', 182.6, X'a4c7b8e21d3f50a6b9d2e8f7c1349a0b5c6d7e218349b6d012c71e8f9a093fed'), ('Jane Doe', 0.5, X'00')");
Console.WriteLine($"Inserted {rs1.RowsAffected} rows");
Console.WriteLine($"Last inserted id: {rs1.LastInsertRowId}");
var rs2 = await dbClient.Execute("SELECT `id`, `name`, `height`, `data` FROM `users`");
var rs2 = await dbClient.Query("SELECT `id`, `name`, `height`, `data` FROM `users`");
PrintTable(rs2);


// Using positional arguments
var searchString = "hn";
var rs3 = await dbClient.Execute("SELECT `id`, `name`, `height`, `data` FROM `users` WHERE `name` LIKE concat('%', ?, '%')", searchString);
var rs3 = await dbClient.Query("SELECT `id`, `name`, `height`, `data` FROM `users` WHERE `name` LIKE concat('%', ?, '%')", searchString);
PrintTable(rs3);


Expand Down
2 changes: 1 addition & 1 deletion Libsql.Client.Tests/EmbeddedReplicaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public async Task CanConnectAndQueryReplicaDatabase()
{
await DatabaseClient.Sync();

var rs = await DatabaseClient.Execute("SELECT COUNT(*) FROM albums");
var rs = await DatabaseClient.Query("SELECT COUNT(*) FROM albums");

var count = rs.Rows.First().First();
var value = Assert.IsType<Integer>(count);
Expand Down
64 changes: 64 additions & 0 deletions Libsql.Client.Tests/ExecuteTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
namespace Libsql.Client.Tests;

public class ExecuteTests
{
private readonly IDatabaseClient _db = DatabaseClient.Create().Result;

[Fact]
public async Task CreateTable_NoRowsAffected()
{
var rowsAffected = await _db.Execute("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

Assert.Equal(0ul, rowsAffected);
}



[Fact]
public async Task RowsAffected_ReturnsOne_WhenRepeatedInserts()
{
await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

for (int i = 0; i < 10; i++)
{
var rowsAffected = await _db.Execute("INSERT INTO `test` DEFAULT VALUES");
Assert.Equal(1ul, rowsAffected);
}
}

[Fact]
public async Task RowsAffected_ReturnsExpectedValue()
{
await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

var rs = await _db.Query("INSERT INTO `test` DEFAULT VALUES");

Assert.Equal(1ul, rs.RowsAffected);
}

[Fact]
public async Task RowsAffected_ReturnsExectedValue_WhenMultipleUpdates()
{
await _db.Execute("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `value` INTEGER)");

for (int i = 0; i < 10; i++)
{
var rs = await _db.Execute("INSERT INTO `test` DEFAULT VALUES");
}

var rs2 = await _db.Query("UPDATE `test` SET `value` = 1");

Assert.Equal(10ul, rs2.RowsAffected);
}

[Fact]
public async Task Throws_WhenRowsReturned()
{
async Task action()
{
await _db.Execute("SELECT 1");
}

await Assert.ThrowsAsync<LibsqlException>(action);
}
}
16 changes: 8 additions & 8 deletions Libsql.Client.Tests/PositionalArgumentTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class PositionalArgumentTests : IDisposable
[Fact]
public async Task SingleParameter()
{
var rs = await _db.Execute("SELECT ?", 1);
var rs = await _db.Query("SELECT ?", 1);
var row = rs.Rows.First();
var value = row.First();
var integer = Assert.IsType<Integer>(value);
Expand All @@ -18,7 +18,7 @@ public async Task SingleParameter()
[Fact]
public async Task MultipleParameters()
{
var rs = await _db.Execute("SELECT ?, ?, ?", 1.0, "2", 3);
var rs = await _db.Query("SELECT ?, ?, ?", 1.0, "2", 3);
var row = rs.Rows.First();
var integer = Assert.IsType<Integer>(row.Skip(2).First());

Expand All @@ -28,7 +28,7 @@ public async Task MultipleParameters()
[Fact]
public async Task BindIntParameter()
{
var rs = await _db.Execute("SELECT ?", 1);
var rs = await _db.Query("SELECT ?", 1);
var row = rs.Rows.First();
var value = row.First();
var integer = Assert.IsType<Integer>(value);
Expand All @@ -39,7 +39,7 @@ public async Task BindIntParameter()
[Fact]
public async Task BindRealParameter()
{
var rs = await _db.Execute("SELECT ?", 1.0);
var rs = await _db.Query("SELECT ?", 1.0);
var row = rs.Rows.First();
var value = row.First();
var real = Assert.IsType<Real>(value);
Expand All @@ -50,7 +50,7 @@ public async Task BindRealParameter()
[Fact]
public async Task BindStringParameter()
{
var rs = await _db.Execute("SELECT ?", "hello");
var rs = await _db.Query("SELECT ?", "hello");
var row = rs.Rows.First();
var value = row.First();
var text = Assert.IsType<Text>(value);
Expand All @@ -61,7 +61,7 @@ public async Task BindStringParameter()
[Fact]
public async Task BindSingleNullParameter()
{
var rs = await _db.Execute("SELECT ?", null);
var rs = await _db.Query("SELECT ?", null);
var row = rs.Rows.First();
var value = row.First();
Assert.IsType<Null>(value);
Expand All @@ -70,7 +70,7 @@ public async Task BindSingleNullParameter()
[Fact]
public async Task BindMultipleParametersWithANull()
{
var rs = await _db.Execute("SELECT ?, ?, ?", 1, null, 3);
var rs = await _db.Query("SELECT ?, ?, ?", 1, null, 3);
var row = rs.Rows.First();
var value = row.Skip(1).First();
Assert.IsType<Null>(value);
Expand All @@ -79,7 +79,7 @@ public async Task BindMultipleParametersWithANull()
[Fact]
public async Task BindBlobParameter()
{
var rs = await _db.Execute("SELECT ?", new byte[] { 1, 2, 3 });
var rs = await _db.Query("SELECT ?", new byte[] { 1, 2, 3 });
var row = rs.Rows.First();
var value = row.First();
var blob = Assert.IsType<Blob>(value);
Expand Down
2 changes: 1 addition & 1 deletion Libsql.Client.Tests/RemoteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public RemoteTests(DatabaseContainer fixture)
[SkippableFact]
public async Task CanConnectAndQueryRemoteDatabase()
{
var rs = await DatabaseClient.Execute("SELECT COUNT(*) FROM tracks");
var rs = await DatabaseClient.Query("SELECT COUNT(*) FROM tracks");

var count = rs.Rows.First().First();
var value = Assert.IsType<Integer>(count);
Expand Down
36 changes: 26 additions & 10 deletions Libsql.Client.Tests/ResultSetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public class ResultSetTests : IDisposable
[Fact]
public async Task Columns_EmptyEnumerable_WhenNonQuery()
{
var rs = await _db.Execute("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");
var rs = await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");
var columns = rs.Columns;

Assert.Empty(columns);
Expand All @@ -16,7 +16,7 @@ public async Task Columns_EmptyEnumerable_WhenNonQuery()
[Fact]
public async Task Columns_HasLength_WhenNotEmpty()
{
var rs = await _db.Execute("SELECT 1, 2, 3");
var rs = await _db.Query("SELECT 1, 2, 3");
var columns = rs.Columns;

Assert.Equal(3, columns.Count());
Expand All @@ -25,7 +25,7 @@ public async Task Columns_HasLength_WhenNotEmpty()
[Fact]
public async Task Columns_NamesAreMarshalled()
{
var rs = await _db.Execute("SELECT 1 AS [column_one]");
var rs = await _db.Query("SELECT 1 AS [column_one]");
var columns = rs.Columns;

Assert.Single(columns);
Expand All @@ -35,20 +35,28 @@ public async Task Columns_NamesAreMarshalled()
[Fact]
public async Task Columns_NamesAreMarshalled_WhenSeveral()
{
var rs = await _db.Execute("SELECT 1 AS [column_one], 2 AS [column_two]");
var rs = await _db.Query("SELECT 1 AS [column_one], 2 AS [column_two]");
var columns = rs.Columns;

Assert.Equal(2, columns.Count());
Assert.Equal("column_one", columns.First());
Assert.Equal("column_two", columns.Last());
}

[Fact]
public async Task LastInsertRowId_Zero_WhenNonQuery()
{
var rs = await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

Assert.Equal(0, rs.LastInsertRowId);
}

[Fact]
public async Task LastInsertRowId_ReturnsExpectedValue()
{
await _db.Execute("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

var rs = await _db.Execute("INSERT INTO `test` DEFAULT VALUES");
var rs = await _db.Query("INSERT INTO `test` DEFAULT VALUES");

Assert.Equal(1, rs.LastInsertRowId);
}
Expand All @@ -61,23 +69,31 @@ public async Task LastInsertRowId_ReturnsExpectedValue_WhenMultipleInserts()
IResultSet rs;
for (int i = 0; i < 10; i++)
{
rs = await _db.Execute("INSERT INTO `test` DEFAULT VALUES");
rs = await _db.Query("INSERT INTO `test` DEFAULT VALUES");
Assert.Equal(i + 1, rs.LastInsertRowId);
}
}

[Fact]
public async Task Changes_ReturnsExpectedValue()
public async Task RowsAffected_Zero_WhenNonQuery()
{
var rs = await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

Assert.Equal(0ul, rs.RowsAffected);
}

[Fact]
public async Task RowsAffected_ReturnsExpectedValue()
{
await _db.Execute("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

var rs = await _db.Execute("INSERT INTO `test` DEFAULT VALUES");
var rs = await _db.Query("INSERT INTO `test` DEFAULT VALUES");

Assert.Equal(1ul, rs.RowsAffected);
}

[Fact]
public async Task Changes_ReturnsExectedValue_WhenMultipleUpdates()
public async Task RowsAffected_ReturnsExectedValue_WhenMultipleUpdates()
{
await _db.Execute("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `value` INTEGER)");

Expand All @@ -86,7 +102,7 @@ public async Task Changes_ReturnsExectedValue_WhenMultipleUpdates()
var rs = await _db.Execute("INSERT INTO `test` DEFAULT VALUES");
}

var rs2 = await _db.Execute("UPDATE `test` SET `value` = 1");
var rs2 = await _db.Query("UPDATE `test` SET `value` = 1");

Assert.Equal(10ul, rs2.RowsAffected);
}
Expand Down
48 changes: 45 additions & 3 deletions Libsql.Client.Tests/RowsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,53 @@
public class RowsTests : IDisposable
{
private readonly IDatabaseClient _db = DatabaseClient.Create().Result;

[Fact]
public async Task IteratedRows_Throws_WhenCreate()
{
async Task<IEnumerable<IEnumerable<Value>>> Action() {
var rs = await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT)");

return rs.Rows.ToList();
}

await Assert.ThrowsAsync<LibsqlException>(Action);
}

[Fact]
public async Task IteratedRows_Throws_WhenCreateIfNotExists()
{
var rs = await _db.Query("CREATE TABLE IF NOT EXISTS `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT)");

Assert.Empty(rs.Rows);
}

[Fact]
public async Task Rows_Empty_WhenInsert()
{
await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT)");

var rs = await _db.Query("INSERT INTO `test` (`name`) VALUES ('libsql')");

Assert.Empty(rs.Rows);
}

[Fact]
public async Task Rows_Empty_WhenUpdate()
{
await _db.Query("CREATE TABLE `test` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `name` TEXT)");
var rs = await _db.Query("INSERT INTO `test` (`name`) VALUES ('libsql')");
Assert.Equal(1ul, rs.RowsAffected);

var rs2 = await _db.Query("UPDATE `test` SET `name` = 'libsql2' WHERE id = 1");

Assert.Empty(rs2.Rows);
}

[Fact]
public async Task Rows_WhenEmpty()
{
var rs = await _db.Execute("SELECT 1 WHERE 1 = 0");
var rs = await _db.Query("SELECT 1 WHERE 1 = 0");
var rows = rs.Rows;

Assert.Empty(rows);
Expand All @@ -16,7 +58,7 @@ public async Task Rows_WhenEmpty()
[Fact]
public async Task Rows_CanIterateTwice()
{
var rs = await _db.Execute("SELECT 1");
var rs = await _db.Query("SELECT 1");

var firstArray = rs.Rows.ToArray();
var secondArray = rs.Rows.ToArray();
Expand All @@ -27,7 +69,7 @@ public async Task Rows_CanIterateTwice()
[Fact]
public async Task Rows_CanPartiallyIterateTwice()
{
var rs = await _db.Execute("SELECT 1 UNION SELECT 2 UNION SELECT 3");
var rs = await _db.Query("SELECT 1 UNION SELECT 2 UNION SELECT 3");

var firstArray = rs.Rows.Take(2).ToArray();
var secondArray = rs.Rows.ToArray();
Expand Down
Loading
Loading