Skip to content

Commit 35b2766

Browse files
support more data types and add relevant docs
1 parent 2a719b0 commit 35b2766

38 files changed

+1172
-525
lines changed

CodeGenerator/Generators/CsprojGen.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace SqlcGenCsharp.Generators;
77

88
internal class CsprojGen(string outputDirectory, string projectName, string namespaceName, Options options)
99
{
10-
private const string DefaultDapperVersion = "2.1.35";
10+
private const string DefaultDapperVersion = "2.1.66";
1111
private const string DefaultNpgsqlVersion = "8.0.6";
1212
private const string DefaultMysqlConnectorVersion = "2.4.0";
1313
private const string DefaultSqliteVersion = "9.0.0";

CodeGenerator/Generators/UtilsGen.cs

+19-2
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,31 @@ public static string TransformQueryForSqliteBatch(string originalSql, int cntRec
7878

7979
var optionalNullToNStringConverter = dbDriver.Options.DriverName is DriverName.MySqlConnector
8080
? $$"""
81-
public class NullToNStringConverter : DefaultTypeConverter
81+
public class NullToStringConverter : DefaultTypeConverter
8282
{
8383
public override {{dbDriver.AddNullableSuffixIfNeeded("string", true)}} ConvertToString(
84-
{{dbDriver.AddNullableSuffixIfNeeded("object", true)}} value, IWriterRow row, MemberMapData memberMapData)
84+
{{dbDriver.AddNullableSuffixIfNeeded("object", false)}} value, IWriterRow row, MemberMapData memberMapData)
8585
{
8686
return value == null ? @"\N" : base.ConvertToString(value, row, memberMapData);
8787
}
8888
}
89+
90+
public class BoolToBitConverter : DefaultTypeConverter
91+
{
92+
public override {{dbDriver.AddNullableSuffixIfNeeded("string", true)}} ConvertToString(
93+
{{dbDriver.AddNullableSuffixIfNeeded("object", false)}} value, IWriterRow row, MemberMapData memberMapData)
94+
{
95+
switch (value)
96+
{
97+
case null:
98+
return @"\N";
99+
case bool b:
100+
return b ? "1" : "0";
101+
default:
102+
return base.ConvertToString(value, row, memberMapData);
103+
}
104+
}
105+
}
89106
"""
90107
: string.Empty;
91108

Drivers/DbDriver.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public abstract class DbDriver
1717

1818
private HashSet<string> NullableTypesInDotnetCore { get; } = ["string", "object"];
1919

20-
private HashSet<string> NullableTypes { get; } = ["bool", "short", "int", "long", "float", "double", "decimal", "DateTime"];
20+
protected HashSet<string> NullableTypes { get; } = ["bool", "byte", "short", "int", "long", "float", "double", "decimal", "DateTime"];
2121

2222
protected abstract List<ColumnMapping> ColumnMappings { get; }
2323

Drivers/MySqlConnectorDriver.cs

+20-12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ public partial class MySqlConnectorDriver(Options options, Dictionary<string, Ta
1919
{
2020
{ "bigint", null }
2121
}, ordinal => $"reader.GetInt64({ordinal})"),
22+
new("byte",
23+
new Dictionary<string, string?>
24+
{
25+
{ "bit", null }
26+
}, ordinal => $"reader.GetFieldValue<byte>({ordinal})"),
2227
new("byte[]",
2328
new Dictionary<string, string?>
2429
{
@@ -65,10 +70,7 @@ public partial class MySqlConnectorDriver(Options options, Dictionary<string, Ta
6570
new("bool",
6671
new Dictionary<string, string?>
6772
{
68-
{ "bit", null },
6973
{ "tinyint", null },
70-
{ "bool", null },
71-
{ "boolean", null }
7274
}, ordinal => $"reader.GetBoolean({ordinal})"),
7375
new("double",
7476
new Dictionary<string, string?>
@@ -170,24 +172,30 @@ public string GetCopyFromImpl(Query query, string queryTextConstant)
170172
var csvWriterVar = Variable.CsvWriter.AsVarName();
171173
var loaderVar = Variable.Loader.AsVarName();
172174
var connectionVar = Variable.Connection.AsVarName();
175+
var nullConverterFn = Variable.NullConverterFn.AsVarName();
173176

174177
var loaderColumns = query.Params.Select(p => $"\"{p.Column.Name}\"").JoinByComma();
175178
var (establishConnection, connectionOpen) = EstablishConnection(query);
176179
return $$"""
177180
const string supportedDateTimeFormat = "yyyy-MM-dd H:mm:ss";
178181
var {{Variable.Config.AsVarName()}} = new CsvConfiguration(CultureInfo.CurrentCulture) { Delimiter = "{{csvDelimiter}}" };
182+
var {{nullConverterFn}} = new Utils.NullToStringConverter();
179183
using (var {{Variable.Writer.AsVarName()}} = new StreamWriter("{{tempCsvFilename}}", false, new UTF8Encoding(false)))
180184
using (var {{csvWriterVar}} = new CsvWriter({{Variable.Writer.AsVarName()}}, {{Variable.Config.AsVarName()}}))
181185
{
182-
var options = new TypeConverterOptions { Formats = new[] { supportedDateTimeFormat } };
183-
{{csvWriterVar}}.Context.TypeConverterOptionsCache.AddOptions<DateTime>(options);
184-
{{csvWriterVar}}.Context.TypeConverterOptionsCache.AddOptions<DateTime?>(options);
185-
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<bool?>(new Utils.NullToNStringConverter());
186-
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<short?>(new Utils.NullToNStringConverter());
187-
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<int?>(new Utils.NullToNStringConverter());
188-
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<long?>(new Utils.NullToNStringConverter());
189-
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<DateTime?>(new Utils.NullToNStringConverter());
190-
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<string>(new Utils.NullToNStringConverter());
186+
var {{Variable.Options}} = new TypeConverterOptions { Formats = new[] { supportedDateTimeFormat } };
187+
{{csvWriterVar}}.Context.TypeConverterOptionsCache.AddOptions<DateTime>({{Variable.Options}});
188+
{{csvWriterVar}}.Context.TypeConverterOptionsCache.AddOptions<DateTime?>({{Variable.Options}});
189+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<bool?>(new Utils.BoolToBitConverter());
190+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<byte?>({{nullConverterFn}});
191+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<short?>({{nullConverterFn}});
192+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<int?>({{nullConverterFn}});
193+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<long?>({{nullConverterFn}});
194+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<float?>({{nullConverterFn}});
195+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<decimal?>({{nullConverterFn}});
196+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<double?>({{nullConverterFn}});
197+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<DateTime?>({{nullConverterFn}});
198+
{{csvWriterVar}}.Context.TypeConverterCache.AddConverter<string>({{nullConverterFn}});
191199
await {{csvWriterVar}}.WriteRecordsAsync({{Variable.Args.AsVarName()}});
192200
}
193201

Drivers/Variable.cs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ namespace SqlcGenCsharp.Drivers;
22

33
public enum Variable
44
{
5+
Options,
56
Config,
67
ConnectionString,
78
Connection,
@@ -11,6 +12,7 @@ public enum Variable
1112
Writer,
1213
Loader,
1314
CsvWriter,
15+
NullConverterFn,
1416

1517
Args,
1618
QueryParams,

docs/04_Postgres.md

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# PostgresSQL
2+
## :execlastid - Implementation
3+
Implemented via a `RETURNING` clause, allowing the `INSERT` command to return the newly created id, which can be of any
4+
data type that can have a unique constraint.
5+
6+
## :copyfrom - Implementation
7+
Implemented via the `COPY FROM` command which can load binary data directly from `stdin`.
8+
9+
## Data Types
10+
Since in batch insert the data is not validated by the SQL itself but written in a binary format,
11+
we consider support for the different data types separately for batch inserts and everything else.
12+
13+
| DB Type | Supported? | Supported in Batch? |
14+
|----------------------------|------------|---------------------|
15+
| boolean |||
16+
| smallint |||
17+
| integer |||
18+
| bigint |||
19+
| real |||
20+
| decimal, numeric |||
21+
| double precision |||
22+
| date |||
23+
| timestamp |||
24+
| char |||
25+
| varchar, character varying |||
26+
| text |||
27+
| bytea |||

docs/05_MySql.md

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# MySQL
2+
3+
## :execlastid - Implementation
4+
The implementation differs if we're using `Dapper` or not.
5+
6+
### Driver - MySqlConnector
7+
The driver provides a `LastInsertedId` property to get the latest inserted id in the DB.
8+
When accessing the property, it automatically performs the below query:
9+
10+
```sql
11+
SELECT LAST_INSERT_ID();
12+
```
13+
14+
That will work only when the id column is defined as `serial` or `bigserial`, and the generated method will always return
15+
a `long` value.
16+
17+
### Dapper
18+
Since the `LastInsertedId` is DB specific and hence not available in Dapper, the `LAST_INSERT_ID` query is simply
19+
appended to the original query like this:
20+
21+
```sql
22+
INSERT INTO tab1 (field1, field2) VALUES ('a', 1);
23+
SELECT LAST_INSERT_ID();
24+
```
25+
26+
The generated method will return `int` & `long` for `serial` & `bigserial` respectively.
27+
28+
## :copyfrom - Implementation
29+
Implemented via the `LOAD DATA` command which can load data from a `CSV` file to a table.
30+
Requires us to first save the input batch as a CSV, and then load it via the driver.
31+
32+
## Data Types
33+
Since in batch insert the data is not validated by the SQL itself but written and read from a CSV,
34+
we consider support for the different data types separately for batch inserts and everything else.
35+
36+
| DB Type | Supported? | Supported in Batch? |
37+
|----------------------|------------|---------------------|
38+
| bool, boolean |||
39+
| bit |||
40+
| smallint |||
41+
| mediumint |||
42+
| integer, int |||
43+
| bigint |||
44+
| real |||
45+
| numeric |||
46+
| decimal |||
47+
| double precision |||
48+
| year |||
49+
| date |||
50+
| timestamp |||
51+
| char |||
52+
| nchar, national char |||
53+
| varchar |||
54+
| tinytext |||
55+
| mediumtext |||
56+
| text |||
57+
| longtext |||
58+
| binary |||
59+
| varbinary |||
60+
| tinyblob |||
61+
| blob |||
62+
| mediumblob |||
63+
| longblob |||

docs/06_Sqlite.md

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# SQLite3
2+
3+
## :execlastid - Implementation
4+
Implemented via a `RETURNING` clause, allowing the `INSERT` command to return the newly created id, which can be of any
5+
data type that can have a unique constraint.
6+
7+
```sql
8+
INSERT INTO tab1 (field1, field2) VALUES ('a', 1) RETURNING id_field;
9+
```
10+
11+
## :copyfrom - Implementation
12+
Implemented via a multi `VALUES` clause, like this:
13+
14+
```sql
15+
INSERT INTO tab1 (field1, field2) VALUES
16+
('a', 1),
17+
('b', 2),
18+
('c', 3);
19+
```
20+
21+
## Data Types
22+
23+
| DB Type | Supported? |
24+
|---------|------------|
25+
| integer ||
26+
| real ||
27+
| text ||
28+
| blob ||
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)