Skip to content

Commit 4a6f0dc

Browse files
added :execrows query annotation support (#83)
1 parent 335fddd commit 4a6f0dc

15 files changed

+205
-130
lines changed

CodeGenerator/CodeGenerator.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ private MemberDeclarationSyntax AddMethodDeclaration(Query query)
239239
":many" => DbDriver.ManyDeclare(queryTextConstant, argInterface, returnInterface, query),
240240
":execlastid" => ((IExecLastId)DbDriver).ExecLastIdDeclare(queryTextConstant, argInterface, query),
241241
":copyfrom" => ((ICopyFrom)DbDriver).CopyFromDeclare(queryTextConstant, argInterface, query),
242-
_ => throw new InvalidDataException()
242+
":execrows" => ((IExecRows)DbDriver).ExecRowsDeclare(queryTextConstant, argInterface, query),
243+
_ => throw new NotImplementedException($"{query.Cmd} is not implemented")
243244
};
244245

245246
string GetInterfaceName(ClassMember classMemberType)

Drivers/ICopyFrom.cs

-9
This file was deleted.

Drivers/IExecLastId.cs

-9
This file was deleted.

Drivers/MySqlConnectorDriver.cs

+1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ IEnumerable<string> ExecuteScalarAndReturnCreated()
145145
}
146146
}
147147

148+
148149
public override MemberDeclarationSyntax ManyDeclare(string queryTextConstant, string argInterface,
149150
string returnInterface, Query query)
150151
{

Drivers/NpgsqlDriver.cs

+84-6
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,23 @@
99

1010
namespace SqlcGenCsharp.Drivers;
1111

12-
public class NpgsqlDriver(DotnetFramework dotnetFramework) : DbDriver(dotnetFramework), ICopyFrom
12+
public class NpgsqlDriver(DotnetFramework dotnetFramework) : DbDriver(dotnetFramework), ICopyFrom, IExecRows
1313
{
1414
protected override List<(string, Func<int, string>, HashSet<string>)> GetColumnMapping()
1515
{
1616
return
1717
[
1818
("long", ordinal => $"reader.GetInt64({ordinal})", ["serial", "bigserial"]),
19-
("byte[]", ordinal => $"Utils.GetBytes(reader, {ordinal})",
20-
["binary", "bit", "bytea", "blob", "longblob", "mediumblob", "tinyblob", "varbinary"]),
19+
("byte[]", ordinal => $"Utils.GetBytes(reader, {ordinal})", [
20+
"binary",
21+
"bit",
22+
"bytea",
23+
"blob",
24+
"longblob",
25+
"mediumblob",
26+
"tinyblob",
27+
"varbinary"
28+
]),
2129
("string", ordinal => $"reader.GetString({ordinal})",
2230
[
2331
"char",
@@ -30,13 +38,26 @@ public class NpgsqlDriver(DotnetFramework dotnetFramework) : DbDriver(dotnetFram
3038
"time",
3139
"timestamp",
3240
"tinytext",
33-
"varchar"
41+
"varchar",
42+
"pg_catalog.varchar"
3443
]),
3544
("object", ordinal => $"reader.GetString({ordinal})", ["json"]),
36-
("int", ordinal => $"reader.GetInt32({ordinal})", ["int2", "int4", "int8"]),
45+
("int", ordinal => $"reader.GetInt32({ordinal})", [
46+
"integer",
47+
"int2",
48+
"int4",
49+
"int8",
50+
"pg_catalog.int2",
51+
"pg_catalog.int4",
52+
"pg_catalog.int8"
53+
]),
3754
("float", ordinal => $"reader.GetFloat({ordinal})", ["numeric", "float4", "float8"]),
3855
("decimal", ordinal => $"reader.GetDecimal({ordinal})", ["decimal"]),
39-
("bool", ordinal => $"reader.GetBoolean({ordinal})", ["bool", "boolean"])
56+
("bool", ordinal => $"reader.GetBoolean({ordinal})", [
57+
"bool",
58+
"boolean",
59+
"pg_catalog.bool"
60+
])
4061
];
4162
}
4263

@@ -163,4 +184,61 @@ string AddRowsToCopyCommand()
163184
""";
164185
}
165186
}
187+
188+
public MemberDeclarationSyntax ExecRowsDeclare(string queryTextConstant, string argInterface, Query query)
189+
{
190+
var parametersStr = CommonGen.GetParameterListAsString(argInterface, query.Params);
191+
var (establishConnection, connectionOpen) = EstablishConnection(query);
192+
var createSqlCommand = CreateSqlCommand(queryTextConstant);
193+
var commandParameters = CommonGen.GetCommandParameters(query.Params);
194+
var executeScalarAndReturnCreated = ExecuteScalarAndReturnCreated();
195+
var methodBody = DotnetFramework.LatestDotnetSupported()
196+
? GetWithUsingAsStatement()
197+
: GetWithUsingAsBlock();
198+
199+
return ParseMemberDeclaration($$"""
200+
public async Task<long> {{query.Name}}({{parametersStr}})
201+
{
202+
{{methodBody}}
203+
}
204+
""")!;
205+
206+
string GetWithUsingAsStatement()
207+
{
208+
return $$"""
209+
{
210+
await using {{establishConnection}};
211+
{{connectionOpen.AppendSemicolonUnlessEmpty()}}
212+
await using {{createSqlCommand}};
213+
{{commandParameters.JoinByNewLine()}}
214+
{{executeScalarAndReturnCreated.JoinByNewLine()}}
215+
}
216+
""";
217+
}
218+
219+
string GetWithUsingAsBlock()
220+
{
221+
return $$"""
222+
{
223+
using ({{establishConnection}})
224+
{
225+
{{connectionOpen.AppendSemicolonUnlessEmpty()}}
226+
using ({{createSqlCommand}})
227+
{
228+
{{commandParameters.JoinByNewLine()}}
229+
{{executeScalarAndReturnCreated.JoinByNewLine()}}
230+
}
231+
}
232+
}
233+
""";
234+
}
235+
236+
IEnumerable<string> ExecuteScalarAndReturnCreated()
237+
{
238+
return new[]
239+
{
240+
$"return await {Variable.Command.Name()}.ExecuteNonQueryAsync();",
241+
};
242+
}
243+
}
166244
}

Drivers/QueryAnnotations.cs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.CodeAnalysis.CSharp.Syntax;
2+
using Plugin;
3+
4+
namespace SqlcGenCsharp.Drivers;
5+
6+
public interface IExecLastId
7+
{
8+
MemberDeclarationSyntax ExecLastIdDeclare(string queryTextConstant, string argInterface, Query query);
9+
}
10+
11+
public interface ICopyFrom
12+
{
13+
MemberDeclarationSyntax CopyFromDeclare(string queryTextConstant, string argInterface, Query query);
14+
}
15+
16+
public interface IExecRows
17+
{
18+
MemberDeclarationSyntax ExecRowsDeclare(string queryTextConstant, string argInterface, Query query);
19+
}

EndToEndTests/DataGenerator.cs

+4
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ public static class DataGenerator
77

88
public const string DrSeussAuthor = "Dr. Seuss";
99
public const string DrSeussQuote = "You'll miss the best things if you keep your eyes shut";
10+
11+
public const string GenericAuthor = "Albert Einstein";
12+
public const string GenericQuote1 = "Quote that everyone always attribute to Einstein";
13+
public const string GenericQuote2 = "Only 2 things are infinite, the universe and human stupidity";
1014
}

EndToEndTests/ISqlDriverTester.cs

-19
This file was deleted.

EndToEndTests/MySqlConnectorTester.cs EndToEndTests/MySqlConnectorTests.cs

+5-12
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
namespace SqlcGenCsharpTests;
88

9-
public class MySqlConnectorTester : SqlDriverTester
9+
public class MySqlConnectorTests
1010
{
1111
private static string ConnectionStringEnv => "MYSQL_CONNECTION_STRING";
1212

13-
private QuerySql QuerySql { get; } =
14-
new(Environment.GetEnvironmentVariable(ConnectionStringEnv)!);
13+
private QuerySql QuerySql { get; } = new(Environment.GetEnvironmentVariable(ConnectionStringEnv)!);
1514

16-
protected override async Task<long> CreateFirstAuthorAndTest()
15+
[Test]
16+
public async Task TestBasicFlow()
1717
{
1818
var createAuthorReturnIdArgs = new QuerySql.CreateAuthorReturnIdArgs
1919
{
@@ -28,11 +28,7 @@ protected override async Task<long> CreateFirstAuthorAndTest()
2828
Name: DataGenerator.BojackAuthor,
2929
Bio: DataGenerator.BojackTheme
3030
});
31-
return insertedId;
32-
}
3331

34-
protected override async Task CreateSecondAuthorAndTest()
35-
{
3632
var createAuthorArgs = new QuerySql.CreateAuthorArgs
3733
{
3834
Name = DataGenerator.DrSeussAuthor,
@@ -51,13 +47,10 @@ protected override async Task CreateSecondAuthorAndTest()
5147
Bio: DataGenerator.DrSeussQuote
5248
});
5349
ClassicAssert.AreEqual(2, actualAuthors.Count);
54-
}
5550

56-
protected override async Task DeleteFirstAuthorAndTest(long idToDelete)
57-
{
5851
var deleteAuthorArgs = new QuerySql.DeleteAuthorArgs
5952
{
60-
Id = idToDelete
53+
Id = insertedId
6154
};
6255
await QuerySql.DeleteAuthor(deleteAuthorArgs);
6356
var authorRows = await QuerySql.ListAuthors();

EndToEndTests/NpgsqlTester.cs EndToEndTests/NogsqlTests.cs

+45-25
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@
77

88
namespace SqlcGenCsharpTests;
99

10-
public class NpgsqlTester : SqlDriverTester
10+
public class NogsqlTests
1111
{
1212
private static readonly Random Randomizer = new();
1313

1414
private static string ConnectionStringEnv => "POSTGRES_CONNECTION_STRING";
1515

16-
private QuerySql QuerySql { get; } =
17-
new(Environment.GetEnvironmentVariable(ConnectionStringEnv)!);
16+
private QuerySql QuerySql { get; } = new(Environment.GetEnvironmentVariable(ConnectionStringEnv)!);
1817

19-
protected override async Task<long> CreateFirstAuthorAndTest()
18+
[TearDown]
19+
public async Task EmptyTestsTable()
20+
{
21+
await QuerySql.TruncateAuthors();
22+
}
23+
24+
[Test]
25+
public async Task TestBasicFlow()
2026
{
2127
var bojackCreateAuthorArgs = new QuerySql.CreateAuthorArgs
2228
{
@@ -41,30 +47,15 @@ protected override async Task<long> CreateFirstAuthorAndTest()
4147
Name: DataGenerator.BojackAuthor,
4248
Bio: DataGenerator.BojackTheme
4349
});
44-
return bojackInsertedId;
45-
46-
long GetId(QuerySql.CreateAuthorRow? createdAuthorRow)
47-
{
48-
var type = typeof(QuerySql.CreateAuthorRow);
49-
var valueProperty = type.GetProperty("Value");
50-
var idProperty = type.GetProperty("Id");
51-
52-
if (valueProperty == null)
53-
return (long)(idProperty?.GetValue(createdAuthorRow) ?? throw new InvalidOperationException());
54-
var value = valueProperty.GetValue(createdAuthorRow);
55-
return (long)(idProperty?.GetValue(value) ?? throw new InvalidOperationException());
56-
}
57-
}
5850

59-
protected override async Task CreateSecondAuthorAndTest()
60-
{
6151
var createAuthorArgs = new QuerySql.CreateAuthorArgs
6252
{
6353
Name = DataGenerator.DrSeussAuthor,
6454
Bio = DataGenerator.DrSeussQuote
6555
};
6656
await QuerySql.CreateAuthor(createAuthorArgs);
6757
var authors = await QuerySql.ListAuthors();
58+
ClassicAssert.AreEqual(2, authors.Count);
6859
Assert.That(authors[0] is
6960
{
7061
Name: DataGenerator.BojackAuthor,
@@ -75,14 +66,10 @@ protected override async Task CreateSecondAuthorAndTest()
7566
Name: DataGenerator.DrSeussAuthor,
7667
Bio: DataGenerator.DrSeussQuote
7768
});
78-
ClassicAssert.AreEqual(2, authors.Count);
79-
}
8069

81-
protected override async Task DeleteFirstAuthorAndTest(long idToDelete)
82-
{
8370
var deleteAuthorArgs = new QuerySql.DeleteAuthorArgs
8471
{
85-
Id = idToDelete
72+
Id = bojackInsertedId
8673
};
8774
await QuerySql.DeleteAuthor(deleteAuthorArgs);
8875
var authorRows = await QuerySql.ListAuthors();
@@ -92,8 +79,41 @@ protected override async Task DeleteFirstAuthorAndTest(long idToDelete)
9279
Bio: DataGenerator.DrSeussQuote
9380
});
9481
ClassicAssert.AreEqual(1, authorRows.Count);
82+
return;
83+
84+
long GetId(QuerySql.CreateAuthorRow? createdAuthorRow)
85+
{
86+
var type = typeof(QuerySql.CreateAuthorRow);
87+
var valueProperty = type.GetProperty("Value");
88+
var idProperty = type.GetProperty("Id");
89+
90+
if (valueProperty == null)
91+
return (long)(idProperty?.GetValue(createdAuthorRow) ?? throw new InvalidOperationException());
92+
var value = valueProperty.GetValue(createdAuthorRow);
93+
return (long)(idProperty?.GetValue(value) ?? throw new InvalidOperationException());
94+
}
95+
}
96+
97+
[Test]
98+
public async Task TestExecRowsFlow()
99+
{
100+
var bojackCreateAuthorArgs = new QuerySql.CreateAuthorArgs
101+
{
102+
Name = DataGenerator.GenericAuthor,
103+
Bio = DataGenerator.GenericQuote1
104+
};
105+
await QuerySql.CreateAuthor(bojackCreateAuthorArgs);
106+
await QuerySql.CreateAuthor(bojackCreateAuthorArgs);
107+
108+
var updateAuthorsArgs = new QuerySql.UpdateAuthorsArgs
109+
{
110+
Bio = DataGenerator.GenericQuote2
111+
};
112+
var affectedRows = await QuerySql.UpdateAuthors(updateAuthorsArgs);
113+
ClassicAssert.AreEqual(2, affectedRows);
95114
}
96115

116+
[Test]
97117
public async Task TestCopyFlow()
98118
{
99119
const int batchSize = 100;

0 commit comments

Comments
 (0)