Skip to content

Commit f2e985f

Browse files
feat: enable override timestamp to NodaTime.Instant (#335)
* feat: enable override timestamp to NodaTime.Instant * feat: add option to override DateTime to NodaTime.Instant in Postgres * feat: add option to override DateTime to NodaTime.Instant in MySQL * fix: regenerate code * feat: add option to override either integer or string to NodaTime.Instant in SQLite * fix: move package references in .csproj to drivers
1 parent 3a45600 commit f2e985f

File tree

106 files changed

+2187
-702
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+2187
-702
lines changed

CodeGenerator/CodeGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ private void InitGenerators(GenerateRequest generateRequest)
8585
DbDriver = InstantiateDriver();
8686

8787
// initialize file generators
88-
CsprojGen = new(outputDirectory, projectName, namespaceName, Options);
88+
CsprojGen = new(DbDriver, outputDirectory, projectName, namespaceName);
8989
QueriesGen = new(DbDriver, namespaceName);
9090
ModelsGen = new(DbDriver, namespaceName);
9191
UtilsGen = new(DbDriver, namespaceName);
Lines changed: 12 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
11
using Google.Protobuf;
2+
using SqlcGenCsharp.Drivers;
23
using System;
4+
using System.Linq;
35
using File = Plugin.File;
46

57

68
namespace SqlcGenCsharp.Generators;
79

8-
internal class CsprojGen(string outputDirectory, string projectName, string namespaceName, Options options)
10+
internal class CsprojGen(DbDriver dbDriver, string outputDirectory, string projectName, string namespaceName)
911
{
10-
// TODO this logic needs to be moved to the Drivers project
11-
private const string DefaultDapperVersion = "2.1.66";
12-
private const string DefaultNpgsqlVersion = "8.0.6";
13-
private const string DefaultMysqlConnectorVersion = "2.4.0";
14-
private const string DefaultSqliteVersion = "9.0.0";
15-
private const string DefaultCsvHelperVersion = "33.0.1";
16-
private const string DefaultSystemTextJsonVersion = "9.0.6";
17-
1812
public File GenerateFile()
1913
{
2014
var csprojContents = GetFileContents();
@@ -27,7 +21,11 @@ public File GenerateFile()
2721

2822
private string GetFileContents()
2923
{
30-
var optionalNullableProperty = options.DotnetFramework.IsDotnetCore() ? Environment.NewLine + " <Nullable>enable</Nullable>" : "";
24+
var optionalNullableProperty = dbDriver.Options.DotnetFramework.IsDotnetCore() ? Environment.NewLine + " <Nullable>enable</Nullable>" : "";
25+
var referenceItems = dbDriver.GetPackageReferences()
26+
.Select(p => $""" <PackageReference Include="{p.Key}" Version="{p.Value}"/>""")
27+
.JoinByNewLine();
28+
3129
return $"""
3230
<!--{Consts.AutoGeneratedComment}-->
3331
<!--Run the following to add the project to the solution:
@@ -36,59 +34,16 @@ dotnet sln add {outputDirectory}/{projectName}.csproj
3634
<Project Sdk="Microsoft.NET.Sdk">
3735
3836
<PropertyGroup>
39-
<TargetFramework>{options.DotnetFramework.ToName()}</TargetFramework>
37+
<TargetFramework>{dbDriver.Options.DotnetFramework.ToName()}</TargetFramework>
4038
<RootNamespace>{namespaceName}</RootNamespace>
4139
<OutputType>Library</OutputType>{optionalNullableProperty}
4240
</PropertyGroup>
4341
44-
{GetPackageReferences()}
42+
<ItemGroup>
43+
{referenceItems}
44+
</ItemGroup>
4545
4646
</Project>
4747
""";
48-
49-
string GetPackageReferences()
50-
{
51-
var optionalDapperPackageReference = options.UseDapper
52-
? Environment.NewLine + $""" <PackageReference Include="Dapper" Version="{GetDapperVersion(options)}"/>"""
53-
: string.Empty;
54-
var optionalCsvHelper = options.DriverName is DriverName.MySqlConnector
55-
? Environment.NewLine + $""" <PackageReference Include="CsvHelper" Version="{DefaultCsvHelperVersion}"/>"""
56-
: string.Empty;
57-
var optionalSystemTextJson = IsSystemTextJsonNeeded()
58-
? Environment.NewLine + $""" <PackageReference Include="System.Text.Json" Version="{DefaultSystemTextJsonVersion}"/>"""
59-
: string.Empty;
60-
return $"""
61-
<ItemGroup>
62-
<PackageReference Include="{options.DriverName.ToName()}" Version="{GetDriverVersion(options)}"/>{optionalDapperPackageReference}{optionalCsvHelper}{optionalSystemTextJson}
63-
</ItemGroup>
64-
""";
65-
}
66-
}
67-
68-
private bool IsSystemTextJsonNeeded()
69-
{
70-
if (options.DotnetFramework.IsDotnetCore())
71-
return false;
72-
return options.DriverName is DriverName.MySqlConnector or DriverName.Npgsql;
73-
}
74-
75-
private static string GetDriverVersion(Options options)
76-
{
77-
if (string.IsNullOrEmpty(options.OverrideDriverVersion))
78-
return options.DriverName switch
79-
{
80-
DriverName.Npgsql => DefaultNpgsqlVersion,
81-
DriverName.MySqlConnector => DefaultMysqlConnectorVersion,
82-
DriverName.Sqlite => DefaultSqliteVersion,
83-
_ => throw new NotSupportedException($"unsupported driver: {options.DriverName}")
84-
};
85-
return options.OverrideDriverVersion;
86-
}
87-
88-
private static string GetDapperVersion(Options options)
89-
{
90-
return string.IsNullOrEmpty(options.OverrideDapperVersion)
91-
? DefaultDapperVersion
92-
: options.OverrideDapperVersion;
9348
}
9449
}

Drivers/ColumnMapping.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class ColumnMapping(
1616
Dictionary<string, DbTypeInfo> dbTypes,
1717
ReaderFn readerFn,
1818
ReaderFn? readerArrayFn = null,
19-
string? usingDirective = null,
19+
string[]? usingDirectives = null,
2020
WriterFn? writerFn = null,
2121
ConvertFunc? convertFunc = null,
2222
string? sqlMapper = null,
@@ -25,7 +25,7 @@ public class ColumnMapping(
2525
public Dictionary<string, DbTypeInfo> DbTypes { get; } = dbTypes;
2626
public ReaderFn ReaderFn { get; } = readerFn;
2727
public ReaderFn? ReaderArrayFn { get; } = readerArrayFn;
28-
public string? UsingDirective { get; } = usingDirective;
28+
public string[]? UsingDirectives { get; } = usingDirectives;
2929
public WriterFn? WriterFn { get; } = writerFn;
3030
public ConvertFunc? ConvertFunc { get; } = convertFunc;
3131
public string? SqlMapper { get; } = sqlMapper;

Drivers/DbDriver.cs

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ public record ConnectionGenCommands(string EstablishConnection, string Connectio
1111

1212
public abstract class DbDriver
1313
{
14+
protected const string DefaultDapperVersion = "2.1.66";
15+
protected const string DefaultSystemTextJsonVersion = "9.0.6";
16+
protected const string DefaultNodaTimeVersion = "3.2.0";
17+
1418
public Options Options { get; }
1519

1620
public string DefaultSchema { get; }
@@ -51,6 +55,7 @@ public abstract class DbDriver
5155
"NpgsqlCircle",
5256
"JsonElement",
5357
"NpgsqlCidr",
58+
"Instant"
5459
];
5560

5661
protected abstract Dictionary<string, ColumnMapping> ColumnMappings { get; }
@@ -68,6 +73,27 @@ public static string TransformQueryForSliceArgs(string originalSql, int sliceSiz
6873
throw new InvalidOperationException("Transaction is provided, but its connection is null.");
6974
""";
7075

76+
protected static readonly SqlMapperImplFunc DateTimeNodaInstantTypeHandler = _ => $$"""
77+
private class NodaInstantTypeHandler : SqlMapper.TypeHandler<Instant>
78+
{
79+
public override Instant Parse(object value)
80+
{
81+
if (value is DateTime dt)
82+
{
83+
if (dt.Kind != DateTimeKind.Utc)
84+
dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc);
85+
return dt.ToInstant();
86+
}
87+
throw new DataException($"Cannot convert {value?.GetType()} to Instant");
88+
}
89+
90+
public override void SetValue(IDbDataParameter parameter, Instant value)
91+
{
92+
parameter.Value = value;
93+
}
94+
}
95+
""";
96+
7197
protected DbDriver(
7298
Options options,
7399
Catalog catalog,
@@ -101,6 +127,21 @@ private static Dictionary<string, Dictionary<string, Table>> ConstructTablesLook
101127
);
102128
}
103129

130+
public virtual IDictionary<string, string> GetPackageReferences()
131+
{
132+
return new Dictionary<string, string> {
133+
{ "Dapper", Options.OverrideDapperVersion != string.Empty ? Options.OverrideDapperVersion : DefaultDapperVersion }
134+
}
135+
.MergeIf(new Dictionary<string, string>
136+
{
137+
{ "System.Text.Json", DefaultSystemTextJsonVersion }
138+
}, IsSystemTextJsonNeeded())
139+
.MergeIf(new Dictionary<string, string>
140+
{
141+
{ "NodaTime", DefaultNodaTimeVersion }
142+
}, TypeExistsInQueries("Instant"));
143+
}
144+
104145
public virtual ISet<string> GetUsingDirectivesForQueries()
105146
{
106147
return new HashSet<string>
@@ -118,17 +159,16 @@ public virtual ISet<string> GetUsingDirectivesForQueries()
118159
private ISet<string> GetUsingDirectivesForColumnMappings()
119160
{
120161
var usingDirectives = new HashSet<string>();
121-
foreach (var schemaTables in Tables.Values)
122-
foreach (var table in schemaTables.Values)
123-
foreach (var column in table.Columns)
124-
{
125-
var csharpType = GetCsharpTypeWithoutNullableSuffix(column, null);
126-
if (!ColumnMappings.ContainsKey(csharpType))
127-
continue;
162+
foreach (var query in Queries)
163+
foreach (var column in query.Columns)
164+
{
165+
var csharpType = GetCsharpTypeWithoutNullableSuffix(column, query);
166+
if (!ColumnMappings.ContainsKey(csharpType))
167+
continue;
128168

129-
var columnMapping = ColumnMappings[csharpType];
130-
usingDirectives.AddRangeExcludeNulls([columnMapping.UsingDirective]);
131-
}
169+
var columnMapping = ColumnMappings[csharpType];
170+
usingDirectives.AddRangeIf(columnMapping.UsingDirectives!, columnMapping.UsingDirectives is not null);
171+
}
132172
return usingDirectives;
133173
}
134174

@@ -223,6 +263,32 @@ public virtual string[] GetLastIdStatement(Query query)
223263
];
224264
}
225265

266+
public virtual string AddParametersToCommand(Query query)
267+
{
268+
return query.Params.Select(p =>
269+
{
270+
var commandVar = Variable.Command.AsVarName();
271+
var param = $"{Variable.Args.AsVarName()}.{p.Column.Name.ToPascalCase()}";
272+
var columnMapping = GetCsharpTypeWithoutNullableSuffix(p.Column, query);
273+
274+
if (p.Column.IsSqlcSlice)
275+
return $$"""
276+
for (int i = 0; i < {{param}}.Length; i++)
277+
{{commandVar}}.Parameters.AddWithValue($"@{{p.Column.Name}}Arg{i}", {{param}}[i]);
278+
""";
279+
280+
var writerFn = GetWriterFn(p.Column, query);
281+
var paramToWrite = writerFn is null ? param : writerFn(
282+
param,
283+
p.Column.Type.Name,
284+
IsColumnNotNull(p.Column, query),
285+
Options.UseDapper,
286+
Options.DotnetFramework.IsDotnetLegacy());
287+
var addParamToCommand = $"""{commandVar}.Parameters.AddWithValue("@{p.Column.Name}", {paramToWrite});""";
288+
return addParamToCommand;
289+
}).JoinByNewLine();
290+
}
291+
226292
public Column GetColumnFromParam(Parameter queryParam, Query query)
227293
{
228294
if (string.IsNullOrEmpty(queryParam.Column.Name))
@@ -285,17 +351,6 @@ public string AddNullableSuffixIfNeeded(string csharpType, bool notNull)
285351
return IsTypeNullable(csharpType) ? $"{csharpType}?" : csharpType;
286352
}
287353

288-
protected string? GetColumnDbTypeOverride(Column column)
289-
{
290-
var columnType = column.Type.Name.ToLower();
291-
foreach (var columnMapping in ColumnMappings.Values)
292-
{
293-
if (columnMapping.DbTypes.TryGetValue(columnType, out var dbTypeOverride))
294-
return dbTypeOverride.NpgsqlTypeOverride;
295-
}
296-
throw new NotSupportedException($"Column {column.Name} has unsupported column type: {column.Type.Name}");
297-
}
298-
299354
public bool IsTypeNullable(string csharpType)
300355
{
301356
if (NullableTypes.Contains(csharpType.Replace("?", ""))) return true;
@@ -365,4 +420,11 @@ public virtual string GetColumnReader(Column column, int ordinal, Query? query)
365420
}
366421
throw new NotSupportedException($"column {column.Name} has unsupported column type: {column.Type.Name} in {GetType().Name}");
367422
}
423+
424+
private bool IsSystemTextJsonNeeded()
425+
{
426+
if (Options.DotnetFramework.IsDotnetCore())
427+
return false;
428+
return TypeExistsInQueries("JsonElement");
429+
}
368430
}

Drivers/Generators/CommonGen.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,6 @@ public static string GetMethodParameterList(string argInterface, IEnumerable<Par
1313
: $"{argInterface} {Variable.Args.AsVarName()}")}";
1414
}
1515

16-
// TODO: extract AddWithValue statement generation to a method + possible override for Npgsql for type override
17-
public string AddParametersToCommand(Query query)
18-
{
19-
return query.Params.Select(p =>
20-
{
21-
var commandVar = Variable.Command.AsVarName();
22-
var param = $"{Variable.Args.AsVarName()}.{p.Column.Name.ToPascalCase()}";
23-
if (p.Column.IsSqlcSlice)
24-
return $$"""
25-
for (int i = 0; i < {{param}}.Length; i++)
26-
{{commandVar}}.Parameters.AddWithValue($"@{{p.Column.Name}}Arg{i}", {{param}}[i]);
27-
""";
28-
29-
var notNull = dbDriver.IsColumnNotNull(p.Column, query);
30-
var writerFn = dbDriver.GetWriterFn(p.Column, query);
31-
var paramToWrite = writerFn is null ? param : writerFn(param, p.Column.Type.Name, notNull, dbDriver.Options.UseDapper, dbDriver.Options.DotnetFramework.IsDotnetLegacy());
32-
var addParamToCommand = $"""{commandVar}.Parameters.AddWithValue("@{p.Column.Name}", {paramToWrite});""";
33-
return addParamToCommand;
34-
}).JoinByNewLine();
35-
}
36-
3716
public string ConstructDapperParamsDict(Query query)
3817
{
3918
if (!query.Params.Any()) return string.Empty;

Drivers/Generators/ExecDeclareGen.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ private string GetDriverNoTxBody(string sqlVar, Query query)
7070
{
7171
var (establishConnection, connectionOpen) = dbDriver.EstablishConnection(query);
7272
var createSqlCommand = dbDriver.CreateSqlCommand(sqlVar);
73-
var commandParameters = CommonGen.AddParametersToCommand(query);
73+
var commandParameters = dbDriver.AddParametersToCommand(query);
7474
return $$"""
7575
using ({{establishConnection}})
7676
{
@@ -89,7 +89,7 @@ private string GetDriverWithTxBody(string sqlVar, Query query)
8989
{
9090
var transactionProperty = Variable.Transaction.AsPropertyName();
9191
var commandVar = Variable.Command.AsVarName();
92-
var commandParameters = CommonGen.AddParametersToCommand(query);
92+
var commandParameters = dbDriver.AddParametersToCommand(query);
9393

9494
return $$"""
9595
{{dbDriver.TransactionConnectionNullExcetionThrow}}

Drivers/Generators/ExecLastIdDeclareGen.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private string GetDriverNoTxBody(string sqlVar, Query query)
6767
{
6868
var (establishConnection, connectionOpen) = dbDriver.EstablishConnection(query);
6969
var createSqlCommand = dbDriver.CreateSqlCommand(sqlVar);
70-
var commandParameters = CommonGen.AddParametersToCommand(query);
70+
var commandParameters = dbDriver.AddParametersToCommand(query);
7171
var returnLastId = ((IExecLastId)dbDriver).GetLastIdStatement(query).JoinByNewLine();
7272
return $$"""
7373
using ({{establishConnection}})
@@ -86,7 +86,7 @@ private string GetDriverWithTxBody(string sqlVar, Query query)
8686
{
8787
var transactionProperty = Variable.Transaction.AsPropertyName();
8888
var commandVar = Variable.Command.AsVarName();
89-
var commandParameters = CommonGen.AddParametersToCommand(query);
89+
var commandParameters = dbDriver.AddParametersToCommand(query);
9090
var returnLastId = ((IExecLastId)dbDriver).GetLastIdStatement(query).JoinByNewLine();
9191

9292
return $$"""

Drivers/Generators/ExecRowsDeclareGen.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ private string GetDriverNoTxBody(string sqlVar, Query query)
6969
{
7070
var (establishConnection, connectionOpen) = dbDriver.EstablishConnection(query);
7171
var createSqlCommand = dbDriver.CreateSqlCommand(sqlVar);
72-
var commandParameters = CommonGen.AddParametersToCommand(query);
72+
var commandParameters = dbDriver.AddParametersToCommand(query);
7373
return $$"""
7474
using ({{establishConnection}})
7575
{
@@ -87,7 +87,7 @@ private string GetDriverWithTxBody(string sqlVar, Query query)
8787
{
8888
var transactionProperty = Variable.Transaction.AsPropertyName();
8989
var commandVar = Variable.Command.AsVarName();
90-
var commandParameters = CommonGen.AddParametersToCommand(query);
90+
var commandParameters = dbDriver.AddParametersToCommand(query);
9191

9292
return $$"""
9393
{{dbDriver.TransactionConnectionNullExcetionThrow}}

Drivers/Generators/ManyDeclareGen.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private string GetDriverNoTxBody(string sqlVar, string returnInterface, Query qu
7979
{
8080
var (establishConnection, connectionOpen) = dbDriver.EstablishConnection(query);
8181
var createSqlCommand = dbDriver.CreateSqlCommand(sqlVar);
82-
var commandParameters = CommonGen.AddParametersToCommand(query);
82+
var commandParameters = dbDriver.AddParametersToCommand(query);
8383
var initDataReader = CommonGen.InitDataReader();
8484
var awaitReaderRow = CommonGen.AwaitReaderRow();
8585
var dataclassInit = CommonGen.InstantiateDataclass(query.Columns.ToArray(), returnInterface, query);
@@ -111,7 +111,7 @@ private string GetDriverWithTxBody(string sqlVar, string returnInterface, Query
111111
{
112112
var transactionProperty = Variable.Transaction.AsPropertyName();
113113
var commandVar = Variable.Command.AsVarName();
114-
var commandParameters = CommonGen.AddParametersToCommand(query);
114+
var commandParameters = dbDriver.AddParametersToCommand(query);
115115
var initDataReader = CommonGen.InitDataReader();
116116
var awaitReaderRow = CommonGen.AwaitReaderRow();
117117
var dataclassInit = CommonGen.InstantiateDataclass(query.Columns.ToArray(), returnInterface, query);

0 commit comments

Comments
 (0)