public (string sql, IReadOnlyList <string> parameters) Create(GeneratorOptions options) { var builder = new StringBuilder("INSERT INTO "); AddQualifiedTableName(builder); builder.Append("("); var parameters = new List <string>(); var columns = new List <string>(); for (var i = 0; i < Table.Columns.Count; i++) { var column = Table.Columns[i]; var par = column.Name; columns.Add(par); if (column.AutogeneratePrimaryKey) { continue; } builder.Append(par); parameters.Add(column.CSharpStyleNameLower); if (i < Table.Columns.Count - 1) { builder.Append(", "); } } builder.AppendLine(")").Append("VALUES ("); for (var i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; builder.Append('@').Append(parameter); if (i < parameters.Count - 1) { builder.Append(", "); } } builder.AppendLine(")"); builder.Append("RETURNING "); for (var i = 0; i < columns.Count; i++) { builder.Append(columns[i]); if (i < columns.Count - 1) { builder.Append(", "); } } builder.Append(';'); return(builder.ToString(), parameters); }
public IReadOnlyList <GeneratedSql> GetSql(IReadOnlyList <Table> tables, GeneratorOptions options) { var builder = new StringBuilder(); var result = new List <GeneratedSql>(); foreach (var table in tables) { builder.Clear(); var dependencies = new List <Table>(); var schema = table.Name.Schema; builder.Append($"CREATE TABLE {schema}.{table.Name.Table}").Append(" (").AppendLine(); for (var c = 0; c < table.Columns.Count; c++) { var column = table.Columns[c]; builder.AddTab(options).Append(column.Name.ToPostgresStyle()) .Append(' '); if (column.AutogeneratePrimaryKey && column.DataType.Type == typeof(int)) { builder.Append("SERIAL"); } else { builder.Append(column.DataType.PostgresType.ToUpperInvariant()); } if (column.IsPrimaryKey) { builder.Append(" PRIMARY KEY"); } if (column.AutogeneratePrimaryKey) { if (column.DataType.Type == typeof(Guid)) { builder.Append(' ') .Append("DEFAULT uuid_generate_v4()"); } else if (column.DataType.Type != typeof(int)) { throw new NotSupportedException($"Generating default primary key values is unsupported for data type {column.DataType.Type.Name} in table: {column}."); } } // primary key is implicitly not null if (!column.IsNullable && !column.IsPrimaryKey) { builder.Append(" NOT NULL"); } if (column.IsForeignKey) { builder.Append(" REFERENCES ") .Append($"{column.ForeignKey.Schema.ToLowerInvariant()}.{column.ForeignKey.Table.ToLowerInvariant()}(").Append(column.ForeignKey.Column.ToPostgresStyle()) .Append(')'); // TODO: add dependency } if (column.HasDefault) { builder.Append(" DEFAULT ") .Append(column.GetDefaultValueExpression()); } if (column.IsUnique && !column.IsPrimaryKey) { builder.Append(" UNIQUE"); } if (c < table.Columns.Count - 1) { builder.Append(','); } builder.Append("\r\n"); } builder.Append(')').Append(';'); result.Add(new GeneratedSql(table, builder.ToString(), dependencies)); } return(result); }
private static IReadOnlyList <Table> Parse(string input, GeneratorOptions options) { var expectsTable = true; var nextMustOpenTable = false; var careAboutSplitter = false; var precedingToken = default(IElephantToken); var tables = new List <Table>(); var columns = new List <ColumnPlaceholder>(); var currentTable = default(TablePlaceholder); var currentColumn = default(ColumnPlaceholder); foreach (var token in Tokenizer.Tokenize(input)) { if (nextMustOpenTable) { if (token == OpenTableToken.Value) { nextMustOpenTable = false; } else if (token != NewLineToken.Value) { throw new InvalidOperationException($"Expected open table token '{{' after table name: {currentTable.Name}."); } continue; } if (precedingToken == ForeignKeyToken.Value) { if (currentColumn == null) { throw new InvalidOperationException("Protected keyword 'fk' used outside column context."); } if (token is ForeignKeyDetailsToken foreignTable) { currentColumn.ForeignKey = foreignTable; } else { throw new InvalidOperationException($"No table-column of the format 'table.column' provided for foreign key. Instead found: {token}."); } precedingToken = token; continue; } if (precedingToken == DefaultToken.Value) { if (currentColumn == null) { throw new InvalidOperationException("Protected keyword 'df' used outside column context."); } if (token is ValueToken valueToken) { currentColumn.DefaultValue = valueToken; } else { throw new InvalidOperationException($"No default value of the format '[ some_value ]' provided for default. Instead found: {token}."); } precedingToken = token; continue; } switch (token) { case NewLineToken _: case CommaToken _: if (!careAboutSplitter || currentTable == null) { continue; } columns.Add(currentColumn); if (currentColumn.Name == null || currentColumn.DataType == null) { throw new InvalidOperationException("Column did not contain at least a name and a data type."); } currentColumn = null; careAboutSplitter = false; break; case NameToken name: if (expectsTable) { currentTable = new TablePlaceholder(name.Name); expectsTable = false; nextMustOpenTable = true; break; } if (currentColumn == null) { currentColumn = new ColumnPlaceholder { Name = name.Name }; careAboutSplitter = true; } break; case ColumnTableReferenceToken columnTableReferenceToken: if (expectsTable) { currentTable = new TablePlaceholder(columnTableReferenceToken.Column, columnTableReferenceToken.Table); expectsTable = false; nextMustOpenTable = true; } else { throw new InvalidOperationException($"Table-column name {columnTableReferenceToken} found outside the context of a foreign key in table: '{currentTable.Name}'."); } break; case DataTypeToken dataTypeToken: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected data type declaration outside column: {dataTypeToken.Type.Name}."); } currentColumn.DataType = dataTypeToken; break; case PrimaryKeyToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected primary key 'pk' declaration outside column in table: '{currentTable.Name}'."); } currentColumn.IsPrimaryKey = true; break; case ForeignKeyToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected foreign key 'fk' declaration outside column in table: '{currentTable.Name}'."); } if (currentColumn.IsPrimaryKey) { throw new InvalidOperationException($"The column '{currentColumn.Name}' is already a primary key, it cannot also be a foreign key in table: '{currentTable.Name}'."); } break; case NullToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected not null 'nn' declaration outside column in table: '{currentTable.Name}'."); } if (currentColumn.IsPrimaryKey) { throw new InvalidOperationException($"The column '{currentColumn.Name}' is a primary key, it cannot be nullable."); } currentColumn.IsNullable = true; break; case ValueToken innerValueToken: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected value declaration '{innerValueToken.Value}' outside column."); } if (currentColumn.DataType.Type == typeof(string) && int.TryParse(innerValueToken.Value, out var val)) { currentColumn.MaxLength = val; } else { throw new InvalidOperationException($"Unexpected value declaration '{innerValueToken.Value}' for non string column and not following default in table: '{currentTable.Name}'."); } break; case UniqueToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected unique 'uq' declaration outside column in table: '{currentTable.Name}'."); } currentColumn.IsUnique = true; break; case AutoGenerateToken _: if (currentColumn == null) { throw new InvalidOperationException($"Unexpected auto generate 'ag' declaration outside column in table: '{currentTable.Name}'."); } if (!currentColumn.IsPrimaryKey) { throw new InvalidOperationException($"The column '{currentColumn.Name}' is not a primary key, it cannot be autogenerated."); } currentColumn.AutogeneratePrimaryKey = true; break; case CloseTableToken _: if (currentTable == null) { throw new InvalidOperationException("Found end of table marker '}' outside of a table."); } if (currentColumn != null) { columns.Add(currentColumn); currentColumn = null; } var qualifiedName = new SchemaQualifiedName(currentTable.Schema ?? options.DefaultSchema ?? "public", currentTable.Name); var actualColumns = columns.Select(x => new Column(x.Name, x.DataType, x.DefaultValue, x.IsPrimaryKey, x.AutogeneratePrimaryKey, x.IsNullable, x.ForeignKey, x.MaxLength, x.IsUnique)); tables.Add(new Table(qualifiedName, actualColumns)); currentTable = null; columns.Clear(); expectsTable = true; break; } precedingToken = token; } return(tables); }
public static IReadOnlyList <GeneratedResult> Generate(IReadOnlyList <string> inputs, GeneratorOptions options) { var tables = new List <Table>(); foreach (var input in inputs) { var templateTables = Parse(input, options); tables.AddRange(templateTables); } var sql = SqlGenerator.GetSql(tables, options); var classes = ClassGenerator.GetClasses(sql, options); var repos = RepositoryClassGenerator.GetRepositories(classes, options); var result = new List <GeneratedResult>(); for (int i = 0; i < sql.Count; i++) { result.Add(new GeneratedResult(tables[i].CSharpStyleName, sql[i].Sql, classes[i].Class, repos[i].Repository)); } return(result); }
public IReadOnlyList <GeneratedRepository> GetRepositories(IReadOnlyList <GeneratedClass> classes, GeneratorOptions options) { var builder = new StringBuilder(); var result = new List <GeneratedRepository>(); var connectionUsing = "using (var connection = new NpgsqlConnection(" + (options.UseUnderscore ? "_connectionString" : "connectionString") + "))"; var namespaceRequired = options.ClassNamespace != options.RepositoryNamespace; foreach (var generatedClass in classes) { var name = GetQualifiedClassName(generatedClass); foreach (var requiredNamespace in RequiredNamespaces) { builder.Append("using ").Append(requiredNamespace).AppendLine(";"); } if (namespaceRequired) { builder.Append("using ").Append(options.ClassNamespace).AppendLine(";"); } builder.AppendLine(); builder.Append("namespace ").AppendLine(options.RepositoryNamespace).AppendLine("{"); // Now inside the namespace { builder.AddTab(options).Append("public class ").Append(generatedClass.Name).Append(options.RepositorySuffix).AppendLine(); builder.AddTab(options).AppendLine("{"); // Now inside the class { AddConstructorAndField(generatedClass, options, builder); builder.AppendLine(); // ReSharper disable UnusedVariable using (var getAll = new GeneratedMethod($"IEnumerable<{name}>", GetAll, string.Empty, builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.GetSelectAll(); AddWithAlignedIndentation(statement, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { builder.AddTab(options, 5).AppendLine("return Enumerate(reader);"); } } } builder.AppendLine(); var pkCol = generatedClass.Table.GetPrimaryKey(); var pkParamName = pkCol.CSharpStyleNameLower; var argByPk = pkCol.DataType.CSharpType + " " + pkParamName; using (var get = new GeneratedMethod(name, Get, argByPk, builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.GetSelectByPk(); AddWithAlignedIndentation(statement, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(pkParamName) .Append("\", ").Append(pkParamName).AppendLine(");").AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { using (var mvNext = new GeneratedUsing("while (await reader.ReadAsync())", builder, options, 5)) { builder.AddTab(options, 6).AppendLine("return GetCurrent(reader);"); } } builder.AppendLine(); builder.AddTab(options, 4).AppendLine("return null;"); } } builder.AppendLine(); using (var del = new GeneratedMethod("bool", Delete, argByPk, builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.GetDeleteByPk(); AddWithAlignedIndentation(statement, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(pkParamName) .Append("\", ").Append(pkParamName).AppendLine(");"); builder.AppendLine().AddTab(options, 4).AppendLine("var result = await command.ExecuteNonQueryAsync();"); builder.AppendLine().AddTab(options, 4).AppendLine("return result > 0;"); } } builder.AppendLine(); var paramName = ColumnPlaceholder.GetCSharpName(generatedClass.Name, true); using (var create = new GeneratedMethod(name, Create, $"{name} {paramName}", builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.Create(options); AddWithAlignedIndentation(statement.sql, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); foreach (var param in statement.parameters) { builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(param) .Append("\", ").Append($"{paramName}.{ColumnPlaceholder.GetCSharpName(param)}").AppendLine(");"); } builder.AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { using (var mvNext = new GeneratedUsing("while (await reader.ReadAsync())", builder, options, 5)) { builder.AddTab(options, 6).AppendLine("return GetCurrent(reader);"); } } builder.AppendLine(); builder.AddTab(options, 4).AppendLine("return null;"); } } builder.AppendLine(); using (var update = new GeneratedMethod(name, Update, $"{name} {paramName}", builder, options)) { using (var conn = new GeneratedUsing(connectionUsing, builder, options)) { builder.AddTab(options, 4) .Append("var command = new NpgsqlCommand(@\""); var statement = generatedClass.Sql.Update(options); AddWithAlignedIndentation(statement.sql, 4, options, builder); builder.AppendLine("\", connection);").AppendLine(); foreach (var param in statement.parameters) { builder.AddTab(options, 4).Append("command.Parameters.AddWithValue(\"") .Append(param) .Append("\", ").Append($"{paramName}.{ColumnPlaceholder.GetCSharpName(param)}").AppendLine(");"); } builder.AppendLine(); using (var reader = new GeneratedUsing("using (var reader = await command.ExecuteReaderAsync())", builder, options, 4)) { using (var mvNext = new GeneratedUsing("while (await reader.ReadAsync())", builder, options, 5)) { builder.AddTab(options, 6).AppendLine("return GetCurrent(reader);"); } } builder.AppendLine(); builder.AddTab(options, 4).AppendLine("return null;"); } } builder.AppendLine(); using (var enumer = new GeneratedMethod($"IEnumerable<{name}>", "Enumerate", "DbDataReader reader", builder, options, "private", false, true)) { using (var reader = new GeneratedUsing("while (reader.Read())", builder, options)) { builder.AddTab(options, 4).AppendLine("yield return GetCurrent(reader);"); } } builder.AppendLine(); using (var enumer = new GeneratedMethod(name, "GetCurrent", "DbDataReader reader", builder, options, "private", false, true)) { builder.AddTab(options, 3).Append("return new ").Append(name).AppendLine() .AddTab(options, 3).AppendLine("{"); for (var i = 0; i < generatedClass.Table.Columns.Count; i++) { var col = generatedClass.Table.Columns[i]; var colName = ColumnPlaceholder.GetCSharpName(col.Name); builder.AddTab(options, 4).Append(colName).Append(" = ") .Append(GetReaderQueryForColumn(col, i)); if (i < generatedClass.Table.Columns.Count - 1) { builder.AppendLine(","); } else { builder.AppendLine(); } } builder.AddTab(options, 3).AppendLine("};"); } } // ReSharper restore UnusedVariable builder.AddTab(options).AppendLine("}"); } builder.Append('}'); var repoCode = builder.ToString(); result.Add(new GeneratedRepository(generatedClass.Table, repoCode, options.RepositoryNamespace)); builder.Clear(); } return(result); }