-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #401 from mk3008/397-experimental-generate-queries…
…-from-table-definition-classes 397 generate queries from table definition classes
- Loading branch information
Showing
34 changed files
with
1,455 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,5 @@ | ||
using Carbunql.Building; | ||
using Carbunql.Clauses; | ||
using Carbunql.Postgres.Linq; | ||
|
||
namespace Carbunql.Postgres; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using Carbunql.Analysis.Parser; | ||
using Carbunql.Clauses; | ||
using System.Diagnostics.CodeAnalysis; | ||
|
||
namespace Carbunql.Annotations; | ||
|
||
/// <summary> | ||
/// Factory class for creating auto number definitions. | ||
/// </summary> | ||
public static class AutoNumberDefinitionFactory | ||
{ | ||
/// <summary> | ||
/// Tries to create an auto number definition for the specified type <typeparamref name="T"/>. | ||
/// </summary> | ||
/// <typeparam name="T">The type of class for which to create the auto number definition.</typeparam> | ||
/// <param name="autoNumberDefinition">When this method returns, contains the auto number definition if successful, or the default value if unsuccessful.</param> | ||
/// <returns>True if the auto number definition is successfully created; otherwise, false.</returns> | ||
public static bool TryCreate<T>([MaybeNullWhen(false)] out ValueBase autoNumberDefinition) | ||
{ | ||
return TryCreate(typeof(T), out autoNumberDefinition); | ||
} | ||
|
||
/// <summary> | ||
/// Tries to create an auto number definition for the specified type. | ||
/// </summary> | ||
/// <param name="type">The type of class for which to create the auto number definition.</param> | ||
/// <param name="autoNumberDefinition">When this method returns, contains the auto number definition if successful, or the default value if unsuccessful.</param> | ||
/// <returns>True if the auto number definition is successfully created; otherwise, false.</returns> | ||
public static bool TryCreate(Type type, [MaybeNullWhen(false)] out ValueBase autoNumberDefinition) | ||
{ | ||
autoNumberDefinition = default; | ||
|
||
var atr = (TableAttribute?)Attribute.GetCustomAttribute(type, typeof(TableAttribute)); | ||
var def = string.Empty; | ||
|
||
if (atr != null) | ||
{ | ||
if (atr.HasAutoNumber == false) return false; | ||
def = atr.AutoNumberDefinition; | ||
} | ||
|
||
if (string.IsNullOrEmpty(def)) | ||
{ | ||
def = DbmsConfiguration.GetDefaultAutoNumberDefinitionLogic(); | ||
} | ||
|
||
if (string.IsNullOrEmpty(def)) | ||
{ | ||
return false; | ||
} | ||
|
||
autoNumberDefinition = ValueParser.Parse(def); | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
namespace Carbunql.Annotations; | ||
|
||
/// <summary> | ||
/// Attribute used to define metadata for a column in a database table. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)] | ||
public class ColumnAttribute : Attribute | ||
{ | ||
/// <summary> | ||
/// Initializes a new instance of the ColumnAttribute class. | ||
/// </summary> | ||
public ColumnAttribute() | ||
{ | ||
} | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the ColumnAttribute class with the specified column type. | ||
/// </summary> | ||
/// <param name="columnType">The type of the column.</param> | ||
public ColumnAttribute(string columnType) | ||
{ | ||
ColumnType = columnType; | ||
} | ||
|
||
/// <summary> | ||
/// Gets or sets the name of the column. | ||
/// </summary> | ||
public string ColumnName { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// Gets or sets the type of the column. | ||
/// </summary> | ||
public string ColumnType { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// Gets or sets the type of the related column, if any. | ||
/// </summary> | ||
public string RelationColumnType { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether the column is auto-incremented. | ||
/// </summary> | ||
public bool IsAutoNumber { get; set; } = false; | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether the column allows null values. | ||
/// </summary> | ||
public bool IsNullable { get; set; } = false; | ||
|
||
/// <summary> | ||
/// Gets or sets the definition for the auto-incremented column. | ||
/// </summary> | ||
public string AutoNumberDefinition { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// Gets or sets the default value for the column. | ||
/// </summary> | ||
public string DefaultValue { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// Gets or sets the comment associated with the column. | ||
/// </summary> | ||
public string Comment { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// Gets or sets the command to retrieve a timestamp value. | ||
/// </summary> | ||
public string TimestampCommand { get; set; } = string.Empty; | ||
|
||
/// <summary> | ||
/// Gets or sets the special type of the column, if any. | ||
/// </summary> | ||
public SpecialColumn SpecialColumn { get; set; } = SpecialColumn.None; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
using Carbunql.Analysis.Parser; | ||
using Carbunql.Definitions; | ||
using System.Reflection; | ||
|
||
namespace Carbunql.Annotations; | ||
|
||
/// <summary> | ||
/// Factory class for creating column definitions. | ||
/// </summary> | ||
public static class ColumnDefinitionFactory | ||
{ | ||
/// <summary> | ||
/// Creates column definitions for the specified type <typeparamref name="T"/>. | ||
/// </summary> | ||
/// <typeparam name="T">The type of class for which to create the column definitions.</typeparam> | ||
/// <param name="t">The table associated with the column definitions.</param> | ||
/// <returns>A list of generated column definitions.</returns> | ||
public static List<ColumnDefinition> Create<T>(ITable t) | ||
{ | ||
var lst = new List<ColumnDefinition>(); | ||
lst.AddRange(CreateFromLiteralProperties<T>(t)); | ||
lst.AddRange(CreateFromRelationPropeties<T>(t)); | ||
return lst; | ||
} | ||
|
||
private static List<ColumnDefinition> CreateFromLiteralProperties<T>(ITable t) | ||
{ | ||
// Exclude properties with invalid attributes. | ||
// Exclude properties with non-literal types. | ||
var props = PropertySelector.SelectLiteralProperties<T>() | ||
.Select(prop => CreateFromLiteralProperty(typeof(T), t, prop)); | ||
|
||
return props.ToList(); | ||
} | ||
|
||
internal static ColumnDefinition CreateFromLiteralProperty(Type type, ITable t, PropertyInfo prop) | ||
{ | ||
var attribute = (ColumnAttribute?)Attribute.GetCustomAttribute(prop, typeof(ColumnAttribute)); | ||
|
||
if (attribute == null) | ||
{ | ||
return Create(type, t, prop, string.Empty, string.Empty, string.Empty, null, null, string.Empty); | ||
|
||
} | ||
else | ||
{ | ||
return Create(type, t, prop, attribute.ColumnName, attribute.ColumnType, attribute.RelationColumnType, attribute.IsAutoNumber, attribute.IsNullable, attribute.DefaultValue); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Generates database column definitions from properties of related types. | ||
/// </summary> | ||
/// <typeparam name="T">The type to generate column definitions for.</typeparam> | ||
/// <param name="t">The table associated with the column definitions.</param> | ||
/// <returns>A list of generated column definitions.</returns> | ||
public static List<ColumnDefinition> CreateFromRelationPropeties<T>(ITable t) | ||
{ | ||
var parentprops = PropertySelector.SelectParentProperties<T>() | ||
.SelectMany(prop => CreateAsParentProperties(t, prop)); | ||
|
||
return parentprops.ToList(); | ||
} | ||
|
||
internal static List<ColumnDefinition> CreateAsParentProperties(ITable t, PropertyInfo prop) | ||
{ | ||
var parentType = prop.PropertyType; | ||
var parentclause = TableDefinitionClauseFactory.CreateTableDefinitionClause(parentType); | ||
var parentPkeyMaps = PrimaryKeyConstraintFactory.Create(parentType).PrimaryKeyMaps; | ||
if (!parentPkeyMaps.Any()) throw new InvalidProgramException(); | ||
|
||
var attributes = (IEnumerable<ParentRelationColumnAttribute>)Attribute.GetCustomAttributes(prop, typeof(ParentRelationColumnAttribute)); | ||
|
||
return parentPkeyMaps.Select(map => Create(parentType, t, map)).ToList(); | ||
} | ||
|
||
private static ColumnDefinition Create(Type parentType, ITable t, PrimaryKeyMap map) | ||
{ | ||
var prop = parentType.GetProperty(map.PropertyName)!; | ||
var parentColumn = CreateFromLiteralProperty(parentType, t, prop); | ||
return new ColumnDefinition(t, map.ColumnName, parentColumn.RelationColumnType); | ||
} | ||
|
||
private static ColumnDefinition Create<T>(ITable t, PropertyInfo prop, string columnName, string columnType, string relationColumnType, bool? isAutonumber, bool? isNullable, string defaultValue) | ||
{ | ||
return Create(typeof(T), t, prop, columnName, columnType, relationColumnType, isAutonumber, isNullable, defaultValue); | ||
} | ||
|
||
private static ColumnDefinition Create(Type type, ITable t, PropertyInfo prop, string columnName, string columnType, string relationColumnType, bool? isAutonumber, bool? isNullable, string defaultValue) | ||
{ | ||
if (string.IsNullOrEmpty(columnName)) | ||
{ | ||
columnName = DbmsConfiguration.ConvertToDefaultColumnNameLogic(prop.Name); | ||
} | ||
|
||
if (isAutonumber == null) | ||
{ | ||
isAutonumber = prop.IsAutoNumber(); | ||
} | ||
|
||
if (string.IsNullOrEmpty(relationColumnType)) | ||
{ | ||
relationColumnType = DbmsConfiguration.ToDbType(prop.PropertyType); | ||
} | ||
|
||
if (string.IsNullOrEmpty(columnType)) | ||
{ | ||
if (isAutonumber.Value) | ||
{ | ||
columnType = DbmsConfiguration.ToIdentityDbType(prop.PropertyType); | ||
} | ||
else | ||
{ | ||
columnType = DbmsConfiguration.ToDbType(prop.PropertyType); | ||
} | ||
} | ||
|
||
if (isNullable == null) | ||
{ | ||
isNullable = prop.IsDbNullable(); | ||
} | ||
|
||
var def = new ColumnDefinition(t, columnName, ValueParser.Parse(columnType)) | ||
{ | ||
RelationColumnType = ValueParser.Parse(relationColumnType), | ||
IsAutoNumber = isAutonumber.Value, | ||
IsNullable = isNullable.Value, | ||
}; | ||
|
||
if (def.IsAutoNumber && AutoNumberDefinitionFactory.TryCreate(type, out var autoNumberDefinition)) | ||
{ | ||
def.AutoNumberDefinition = autoNumberDefinition; | ||
} | ||
|
||
if (!string.IsNullOrEmpty(defaultValue)) | ||
{ | ||
def.DefaultValue = ValueParser.Parse(defaultValue); | ||
} | ||
|
||
return def; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
using Carbunql.Analysis.Parser; | ||
using Carbunql.Clauses; | ||
using Carbunql.Definitions; | ||
|
||
namespace Carbunql.Annotations; | ||
|
||
/// <summary> | ||
/// Factory class for generating index queries. | ||
/// </summary> | ||
public static class CreateIndexQueryFactory | ||
{ | ||
/// <summary> | ||
/// Generates index queries based on the properties of the specified type from the given table information. | ||
/// </summary> | ||
/// <typeparam name="T">Type of the object for which indexes are created.</typeparam> | ||
/// <param name="t">Table information for creating indexes.</param> | ||
/// <returns>List of generated index queries.</returns> | ||
public static List<CreateIndexQuery> Creates<T>(ITable t) | ||
{ | ||
var queries = new List<CreateIndexQuery>(); | ||
|
||
var props = PropertySelector.SelectParentProperties<T>(); | ||
|
||
foreach (var prop in props) | ||
{ | ||
var clause = new IndexOnClause(t); | ||
|
||
ColumnDefinitionFactory.CreateAsParentProperties(t, prop) | ||
.Select(x => x.ColumnName).ToList() | ||
.ForEach(x => clause.Add(SortableItemParser.Parse(x))); | ||
|
||
var query = new CreateIndexQuery(clause) | ||
{ | ||
IndexName = DbmsConfiguration.GetDefaultIndexNameLogic(prop.Name) | ||
}; | ||
|
||
queries.Add(query); | ||
} | ||
|
||
return queries; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
namespace Carbunql.Annotations; | ||
|
||
/// <summary> | ||
/// Factory class for creating "CREATE TABLE" queries. | ||
/// </summary> | ||
public static class CreateTableQueryFactory | ||
{ | ||
/// <summary> | ||
/// Creates a "CREATE TABLE" query for the specified type <typeparamref name="T"/>. | ||
/// </summary> | ||
/// <typeparam name="T">The type of class for which to create the table query.</typeparam> | ||
/// <returns>The created "CREATE TABLE" query.</returns> | ||
public static CreateTableQuery Create<T>() | ||
{ | ||
var clause = TableDefinitionClauseFactory.Create<T>(); | ||
return new CreateTableQuery(clause); | ||
} | ||
} |
Oops, something went wrong.