Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4b08701
fix: standardize assertions across end2end tests
SockworkOrange Aug 13, 2025
3f8bd97
fix: change mysql set data type to map to HashSet instead of array
SockworkOrange Aug 13, 2025
ad2cc89
fix: add test for invalid xml for postgres xml data type
SockworkOrange Aug 13, 2025
5531b1a
fix: split postgres authors and data types tables to different files
SockworkOrange Aug 14, 2025
969c2b3
fix: postgres legacy end2end tests
SockworkOrange Aug 14, 2025
51c8682
fix: split mysql authors and data types tables to different files
SockworkOrange Aug 14, 2025
3ac7709
fix: sql constant bad whitespace transformation
SockworkOrange Aug 14, 2025
ec8ce4c
fix: whitespace regex + more concise code
SockworkOrange Aug 14, 2025
8a3cb03
fix: re-generate code
SockworkOrange Aug 14, 2025
ce97f5d
fix: add missing mysql datetime tests
SockworkOrange Aug 14, 2025
b99b66c
fix: separate the rest of mysql data types by group
SockworkOrange Aug 14, 2025
e117147
fix: re-generate code
SockworkOrange Aug 14, 2025
5e17d12
fix: separate sqlite schema and query files by type and authors
SockworkOrange Aug 14, 2025
4c1d5c6
fix: extract common transaction exception throw to a constant
SockworkOrange Aug 15, 2025
a0b1a77
fix: more refactoring
SockworkOrange Aug 15, 2025
b1b9b91
fix: simplify Drivers method placement
SockworkOrange Aug 15, 2025
e5cb381
fix: missing assertions in postgres guid end2end tests
SockworkOrange Aug 15, 2025
5a6e777
fix: revert incorrect changes to Drivers
SockworkOrange Aug 15, 2025
7761b31
fix: refactor to support postgres enums
SockworkOrange Aug 16, 2025
60b5a63
fix: remove redundant using directives
SockworkOrange Aug 16, 2025
c246859
fix: enum logic for mysql
SockworkOrange Aug 16, 2025
b1cc012
fix: move enum logic to be behind an abstract class
SockworkOrange Aug 16, 2025
8ec132e
fix: postgres EnumToCsharpDataType
SockworkOrange Aug 16, 2025
de59496
fix: separate string data types to their own table
SockworkOrange Aug 19, 2025
c2b2126
feat: support full text search data types
SockworkOrange Aug 19, 2025
dddbdbf
fix: move test types config to constants
SockworkOrange Aug 22, 2025
3e5ac79
fix: more separate data types queries for Postgtres
SockworkOrange Aug 22, 2025
e74a768
fix: move authors schema to be first in config
SockworkOrange Aug 22, 2025
da0f1e8
fix: separate numeric data types queries for Postgtres
SockworkOrange Aug 22, 2025
5d5310c
fix: separate rest of postgres data types to own table
SockworkOrange Aug 22, 2025
917a5fd
feat: support array data types in batch inserts
SockworkOrange Aug 23, 2025
8d4aef2
fix: update Postgres data types doc
SockworkOrange Aug 23, 2025
d50fe93
fix: Update Postgrs doc with data type conversion example
SockworkOrange Aug 24, 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
4 changes: 2 additions & 2 deletions .github/workflows/legacy-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:

- name: Init PostgresSQL Schema
shell: powershell
run: psql -U $Env:POSTGRES_USER -f 'examples/config/postgresql/schema.sql'
run: psql -U $Env:POSTGRES_USER -f 'examples/config/postgresql/types/schema.sql' -f 'examples/config/postgresql/authors/schema.sql'
env:
PGSERVICE: ${{ steps.postgres.outputs.service-name }}
PGPASSWORD: ${{ env.POSTGRES_PASSWORD }}
Expand Down Expand Up @@ -101,7 +101,7 @@ jobs:
$env:Path += ";C:\Program Files\MySQL\MySQL Server 8.0\bin"
[Environment]::SetEnvironmentVariable("Path", $env:Path, "Machine")
mysql -u root -e "SET GLOBAL local_infile=1; CREATE DATABASE $Env:TESTS_DB;"
mysql -u root $Env:TESTS_DB --execute="source examples/config/mysql/schema.sql"
mysql -u root $Env:TESTS_DB --execute="source examples/config/mysql/types/schema.sql; source examples/config/mysql/authors/schema.sql;"

- name: Run Tests
shell: powershell
Expand Down
100 changes: 18 additions & 82 deletions CodeGenerator/CodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,13 @@
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Enum = Plugin.Enum;

namespace SqlcGenCsharp;

public class CodeGenerator
{
private readonly HashSet<string> _excludedSchemas =
[
"pg_catalog",
"information_schema"
];

private Options? _options;
private Dictionary<string, Dictionary<string, Table>>? _tables;
private Dictionary<string, Dictionary<string, Enum>>? _enums;
private Catalog? _catalog;
private List<Query>? _queries;
private DbDriver? _dbDriver;
private QueriesGen? _queriesGen;
Expand All @@ -37,16 +29,10 @@ private Options Options
set => _options = value;
}

private Dictionary<string, Dictionary<string, Table>> Tables
private Catalog Catalog
{
get => _tables!;
set => _tables = value;
}

private Dictionary<string, Dictionary<string, Enum>> Enums
{
get => _enums!;
set => _enums = value;
get => _catalog!;
set => _catalog = value;
}

private List<Query> Queries
Expand Down Expand Up @@ -89,79 +75,29 @@ private void InitGenerators(GenerateRequest generateRequest)
{
var outputDirectory = generateRequest.Settings.Codegen.Out;
var projectName = new DirectoryInfo(outputDirectory).Name;
Options = new Options(generateRequest);
Options = new(generateRequest);
if (Options.DebugRequest)
return;

Queries = generateRequest.Queries.ToList();
Tables = ConstructTablesLookup(generateRequest.Catalog);
Enums = ConstructEnumsLookup(generateRequest.Catalog);

var namespaceName = Options.NamespaceName == string.Empty ? projectName : Options.NamespaceName;
DbDriver = InstantiateDriver(generateRequest.Catalog.DefaultSchema);
Catalog = generateRequest.Catalog;
DbDriver = InstantiateDriver();

// initialize file generators
CsprojGen = new CsprojGen(outputDirectory, projectName, namespaceName, Options);
QueriesGen = new QueriesGen(DbDriver, namespaceName);
ModelsGen = new ModelsGen(DbDriver, namespaceName);
UtilsGen = new UtilsGen(DbDriver, namespaceName);
}

private Dictionary<string, Dictionary<string, Table>> ConstructTablesLookup(Catalog catalog)
{
return catalog.Schemas
.Where(s => !_excludedSchemas.Contains(s.Name))
.ToDictionary(
s => s.Name == catalog.DefaultSchema ? string.Empty : s.Name,
s => s.Tables.ToDictionary(t => t.Rel.Name, t => t));
}

/// <summary>
/// Enums in the request exist only in the default schema (in mysql), this remaps enums to their original schema.
/// </summary>
/// <param name="catalog"></param>
/// <returns></returns>
private Dictionary<string, Dictionary<string, Enum>> ConstructEnumsLookup(Catalog catalog)
{
var defaultSchemaCatalog = catalog.Schemas.First(s => s.Name == catalog.DefaultSchema);
var schemaEnumTuples = defaultSchemaCatalog.Enums
.Select(e => new
{
EnumItem = e,
Schema = FindEnumSchema(e)
});
var schemaToEnums = schemaEnumTuples
.GroupBy(x => x.Schema)
.ToDictionary(
group => group.Key,
group => group.ToDictionary(
x => x.EnumItem.Name,
x => x.EnumItem)
);
return schemaToEnums;
}

private string FindEnumSchema(Enum e)
{
foreach (var schemaTables in Tables)
{
foreach (var table in schemaTables.Value)
{
var isEnumColumn = table.Value.Columns.Any(c => c.Type.Name == e.Name);
if (isEnumColumn)
return schemaTables.Key;
}
}
throw new InvalidDataException($"No enum {e.Name} schema found.");
CsprojGen = new(outputDirectory, projectName, namespaceName, Options);
QueriesGen = new(DbDriver, namespaceName);
ModelsGen = new(DbDriver, namespaceName);
UtilsGen = new(DbDriver, namespaceName);
}

private DbDriver InstantiateDriver(string defaultSchema)
private DbDriver InstantiateDriver()
{
return Options.DriverName switch
{
DriverName.MySqlConnector => new MySqlConnectorDriver(Options, defaultSchema, Tables, Enums, Queries),
DriverName.Npgsql => new NpgsqlDriver(Options, defaultSchema, Tables, Enums, Queries),
DriverName.Sqlite => new SqliteDriver(Options, defaultSchema, Tables, Enums, Queries),
DriverName.MySqlConnector => new MySqlConnectorDriver(Options, Catalog, Queries),
DriverName.Npgsql => new NpgsqlDriver(Options, Catalog, Queries),
DriverName.Sqlite => new SqliteDriver(Options, Catalog, Queries),
_ => throw new ArgumentException($"unknown driver: {Options.DriverName}")
};
}
Expand All @@ -182,7 +118,7 @@ public Task<GenerateResponse> Generate(GenerateRequest generateRequest)
var files = GetFileQueries()
.Select(fq => QueriesGen.GenerateFile(fq.Value, fq.Key))
.AddRangeExcludeNulls([
ModelsGen.GenerateFile(Tables, Enums),
ModelsGen.GenerateFile(DbDriver.Tables, DbDriver.Enums),
UtilsGen.GenerateFile()
])
.AddRangeIf([CsprojGen.GenerateFile()], Options.GenerateCsproj);
Expand Down Expand Up @@ -218,12 +154,12 @@ private static Plugin.File RequestToJsonFile(GenerateRequest request)
{
var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithIndentation());
request.PluginOptions = GetOptionsWithoutDebugRequest(request);
return new Plugin.File { Name = "request.json", Contents = ByteString.CopyFromUtf8(formatter.Format(request)) };
return new() { Name = "request.json", Contents = ByteString.CopyFromUtf8(formatter.Format(request)) };
}

private static Plugin.File RequestToProtobufFile(GenerateRequest request)
{
request.PluginOptions = GetOptionsWithoutDebugRequest(request);
return new Plugin.File { Name = "request.message", Contents = request.ToByteString() };
return new() { Name = "request.message", Contents = request.ToByteString() };
}
}
17 changes: 15 additions & 2 deletions CodeGenerator/Generators/EnumsGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace SqlcGenCsharp.Generators;

internal class EnumsGen(DbDriver dbDriver)

Check warning on line 9 in CodeGenerator/Generators/EnumsGen.cs

View workflow job for this annotation

GitHub Actions / Build (WASM)

Parameter 'dbDriver' is unread.

Check warning on line 9 in CodeGenerator/Generators/EnumsGen.cs

View workflow job for this annotation

GitHub Actions / Codegen Tests

Parameter 'dbDriver' is unread.
{
public MemberDeclarationSyntax[] Generate(string name, IList<string> possibleValues)
{
Expand All @@ -31,15 +31,28 @@
.Select(v => $"[\"{v}\"] = {name}.{v.ToPascalCase()}")
.JoinByComma()}}
};

private static readonly Dictionary<{{name}}, string> EnumToString = new Dictionary<{{name}}, string>()
{
[{{name}}.Invalid] = string.Empty,
{{possibleValues
.Select(v => $"[{name}.{v.ToPascalCase()}] = \"{v}\"")
.JoinByComma()}}
};

public static {{name}} To{{name}}(this string me)
{
return StringToEnum[me];
}

public static {{name}}[] To{{name}}Arr(this string me)
public static string Stringify(this {{name}} me)
{
return EnumToString[me];
}

public static HashSet<{{name}}> To{{name}}Set(this string me)
{
return me.Split(',').ToList().Select(v => StringToEnum[v]).ToArray();
return new HashSet<{{name}}>(me.Split(',').ToList().Select(v => StringToEnum[v]));
}
}
""")!;
Expand Down
10 changes: 6 additions & 4 deletions CodeGenerator/Generators/ModelsGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Plugin;
using SqlcGenCsharp.Drivers;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

Expand All @@ -17,7 +16,6 @@ internal class ModelsGen(DbDriver dbDriver, string namespaceName)

private DataClassesGen DataClassesGen { get; } = new(dbDriver);


private EnumsGen EnumsGen { get; } = new(dbDriver);

public File GenerateFile(
Expand Down Expand Up @@ -59,8 +57,12 @@ private MemberDeclarationSyntax[] GenerateEnums(Dictionary<string, Dictionary<st
{
return s.Value.SelectMany(e =>
{
var enumName = e.Value.Name.ToModelName(s.Key, dbDriver.DefaultSchema);
return EnumsGen.Generate(enumName, e.Value.Vals);
if (dbDriver is EnumDbDriver enumDbDriver)
{
var enumName = enumDbDriver.EnumToModelName(s.Key, e.Value);
return EnumsGen.Generate(enumName, e.Value.Vals);
}
return [];
});
}).ToArray();
}
Expand Down
15 changes: 11 additions & 4 deletions CodeGenerator/Generators/QueriesGen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;


namespace SqlcGenCsharp.Generators;

internal class QueriesGen(DbDriver dbDriver, string namespaceName)
internal partial class QueriesGen(DbDriver dbDriver, string namespaceName)
{
private static readonly string[] ResharperDisables =
[
Expand Down Expand Up @@ -132,16 +133,22 @@ private IEnumerable<MemberDeclarationSyntax> GetMembersForSingleQuery(Query quer

private MemberDeclarationSyntax? GetQueryTextConstant(Query query)
{
var transformQueryText = dbDriver.TransformQueryText(query);
if (transformQueryText == string.Empty)
var transformedQueryText = dbDriver.TransformQueryText(query);
if (transformedQueryText == string.Empty)
return null;

var singleLineQueryText = LongWhitespaceRegex().Replace(transformedQueryText, " ");
return ParseMemberDeclaration(
$"""
private const string {ClassMember.Sql.Name(query.Name)} = "{transformQueryText}";
private const string {ClassMember.Sql.Name(query.Name)} = "{singleLineQueryText}";
""")!
.AppendNewLine();
}


[GeneratedRegex(@"\s{1,}")]
private static partial Regex LongWhitespaceRegex();

private MemberDeclarationSyntax AddMethodDeclaration(Query query)
{
var queryTextConstant = ClassMember.Sql.Name(query.Name);
Expand Down
4 changes: 2 additions & 2 deletions CodegenTests/CodegenSchemaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ public void TestSchemaScopedEnum()
var expected = new HashSet<string>
{
"DummySchemaDummyTable",
"DummySchemaDummyTableDummyColumn",
"DummySchemaDummyTableDummyColumnExtensions"
"DummyTableDummyColumn",
"DummyTableDummyColumnExtensions"
};
var actual = GetMemberNames(modelsCode);
Assert.That(actual.IsSupersetOf(expected));
Expand Down
1 change: 0 additions & 1 deletion CodegenTests/CodegenTypeOverrideTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using Plugin;
using SqlcGenCsharp;
using System.Text;
using System.Xml;

namespace CodegenTests;

Expand Down
Loading
Loading