chore: initial git load of code space
This commit is contained in:
@@ -0,0 +1,440 @@
|
||||
using Strata.SqlTools.Breakdowns.SqlServer;
|
||||
using Strata.SqlTools.SqlBreakdown.Classes;
|
||||
using Strata.SqlTools.SqlBreakdown.Interfaces.QueryEngine;
|
||||
|
||||
namespace Strata.SqlTools.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extension methods providing a fluent API for building SQL queries with WITH clauses (CTEs).
|
||||
/// Enables method chaining for intuitive query construction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This extension class provides a fluent, chainable API for building QueryBreakdown objects.
|
||||
/// Instead of setting properties individually, you can use these methods to build queries in a
|
||||
/// more intuitive, method-chaining style.</para>
|
||||
///
|
||||
/// <para>Example - Traditional approach:</para>
|
||||
/// <code>
|
||||
/// var cteQuery = new QueryBreakdown();
|
||||
/// cteQuery.SelectClause.Clause = "id, name";
|
||||
/// cteQuery.FromClause.Clause = "users";
|
||||
/// cteQuery.WhereClause.Clause = "active = 1";
|
||||
/// var mainQuery = new QueryBreakdown();
|
||||
/// mainQuery.AddWithClause("active_users", cteQuery);
|
||||
/// mainQuery.SelectClause.Clause = "*";
|
||||
/// mainQuery.FromClause.Clause = "active_users";
|
||||
/// var sql = mainQuery.GetSql();
|
||||
/// </code>
|
||||
///
|
||||
/// <para>Example - Fluent approach (using these extensions):</para>
|
||||
/// <code>
|
||||
/// var sql = new QueryBreakdown()
|
||||
/// .WithCte("active_users", cte => cte
|
||||
/// .Select("id, name")
|
||||
/// .From("users")
|
||||
/// .Where("active = 1"))
|
||||
/// .Select("*")
|
||||
/// .From("active_users")
|
||||
/// .GetSql();
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
public static class QueryBreakdownExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Sets the SELECT clause and returns the query for method chaining.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to configure.</param>
|
||||
/// <param name="selectClause">The SELECT clause SQL text (e.g., "id, name, email").</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query is null.</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var query = new QueryBreakdown()
|
||||
/// .Select("id, name")
|
||||
/// .From("users");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown Select(this QueryBreakdown query, string selectClause)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
query.SelectClause.Clause = selectClause;
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the FROM clause and returns the query for method chaining.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to configure.</param>
|
||||
/// <param name="fromClause">The FROM clause SQL text (e.g., "users" or "users u JOIN orders o").</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query is null.</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var query = new QueryBreakdown()
|
||||
/// .Select("*")
|
||||
/// .From("users u JOIN orders o ON u.id = o.user_id");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown From(this QueryBreakdown query, string fromClause)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
query.FromClause.Clause = fromClause;
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the WHERE clause and returns the query for method chaining.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to configure.</param>
|
||||
/// <param name="whereClause">The WHERE clause SQL text (e.g., "active = 1 AND age > 18").</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query is null.</exception>
|
||||
/// <remarks>
|
||||
/// This method replaces any existing WHERE clause. To add conditions to an existing WHERE clause,
|
||||
/// use <see cref="AddWhere(QueryBreakdown, string)"/> instead.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var query = new QueryBreakdown()
|
||||
/// .Select("*")
|
||||
/// .From("users")
|
||||
/// .Where("active = 1");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown Where(this QueryBreakdown query, string whereClause)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
query.WhereClause.Clause = whereClause;
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a condition to the existing WHERE clause and returns the query for method chaining.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to configure.</param>
|
||||
/// <param name="whereCondition">The WHERE condition to append (e.g., "AND active = 1").</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query is null.</exception>
|
||||
/// <remarks>
|
||||
/// This method appends to the existing WHERE clause. If you want to replace the WHERE clause entirely,
|
||||
/// use <see cref="Where(QueryBreakdown, string)"/> instead.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var query = new QueryBreakdown()
|
||||
/// .Select("*")
|
||||
/// .From("users")
|
||||
/// .Where("active = 1")
|
||||
/// .AddWhere("AND age > 18");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown AddWhere(this QueryBreakdown query, string whereCondition)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
query.AddWhereClause(whereCondition);
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the GROUP BY clause and returns the query for method chaining.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to configure.</param>
|
||||
/// <param name="groupByClause">The GROUP BY clause SQL text (e.g., "department, year").</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query is null.</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var query = new QueryBreakdown()
|
||||
/// .Select("department, COUNT(*) as count")
|
||||
/// .From("employees")
|
||||
/// .GroupBy("department");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown GroupBy(this QueryBreakdown query, string groupByClause)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
query.GroupByClause.Clause = groupByClause;
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the HAVING clause and returns the query for method chaining.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to configure.</param>
|
||||
/// <param name="havingClause">The HAVING clause SQL text (e.g., "COUNT(*) > 5").</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query is null.</exception>
|
||||
/// <remarks>
|
||||
/// The HAVING clause filters groups after GROUP BY has been applied. Typically used with aggregate functions.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var query = new QueryBreakdown()
|
||||
/// .Select("department, COUNT(*) as count")
|
||||
/// .From("employees")
|
||||
/// .GroupBy("department")
|
||||
/// .Having("COUNT(*) > 5");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown Having(this QueryBreakdown query, string havingClause)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
query.HavingClause.Clause = havingClause;
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the ORDER BY clause and returns the query for method chaining.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to configure.</param>
|
||||
/// <param name="orderByClause">The ORDER BY clause SQL text (e.g., "name ASC, created_date DESC").</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query is null.</exception>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var query = new QueryBreakdown()
|
||||
/// .Select("*")
|
||||
/// .From("employees")
|
||||
/// .OrderBy("last_name ASC, first_name ASC");
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown OrderBy(this QueryBreakdown query, string orderByClause)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
query.OrderByClause.Clause = orderByClause;
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Common Table Expression (CTE) to this query with fluent configuration.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to add the CTE to.</param>
|
||||
/// <param name="tableName">The name of the CTE (used in the WITH clause).</param>
|
||||
/// <param name="configureAction">An action that configures the CTE query using fluent methods.</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query or configureAction is null.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when tableName is null, empty, or whitespace.</exception>
|
||||
/// <remarks>
|
||||
/// <para>This method creates a new QueryBreakdown for the CTE and allows you to configure it
|
||||
/// using the fluent API within a lambda expression.</para>
|
||||
///
|
||||
/// <para>Parameters defined in the CTE query are automatically merged into the parent query's
|
||||
/// parameter collection. If a parameter name conflict occurs, the parent query's parameter
|
||||
/// takes precedence.</para>
|
||||
///
|
||||
/// <para>For recursive CTEs, configure the IsRecursive flag and RecursiveQuery property on the
|
||||
/// CTE within the configureAction.</para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Simple CTE
|
||||
/// var sql = new QueryBreakdown()
|
||||
/// .WithCte("active_users", cte => cte
|
||||
/// .Select("id, name, email")
|
||||
/// .From("users")
|
||||
/// .Where("active = 1"))
|
||||
/// .Select("*")
|
||||
/// .From("active_users")
|
||||
/// .GetSql();
|
||||
/// </code>
|
||||
/// </example>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// // Multiple CTEs
|
||||
/// var sql = new QueryBreakdown()
|
||||
/// .WithCte("active_users", cte => cte
|
||||
/// .Select("id, name")
|
||||
/// .From("users")
|
||||
/// .Where("active = 1"))
|
||||
/// .WithCte("active_orders", cte => cte
|
||||
/// .Select("order_id, user_id, amount")
|
||||
/// .From("orders")
|
||||
/// .Where("status = 'completed'"))
|
||||
/// .Select("u.name, COUNT(o.order_id) as order_count")
|
||||
/// .From("active_users u")
|
||||
/// .From("LEFT JOIN active_orders o ON u.id = o.user_id")
|
||||
/// .GroupBy("u.id, u.name")
|
||||
/// .GetSql();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown WithCte(
|
||||
this QueryBreakdown query,
|
||||
string tableName,
|
||||
Action<QueryBreakdown> configureAction)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tableName))
|
||||
{
|
||||
throw new ArgumentException("CTE table name cannot be null, empty, or whitespace.", nameof(tableName));
|
||||
}
|
||||
|
||||
if (configureAction == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureAction), "Configuration action cannot be null.");
|
||||
}
|
||||
|
||||
// Create a new QueryBreakdown for the CTE
|
||||
var cteQuery = new QueryBreakdown();
|
||||
|
||||
// Configure the CTE using the provided action
|
||||
configureAction(cteQuery);
|
||||
|
||||
// Add the CTE to the main query
|
||||
query.AddWithClause(tableName, cteQuery);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Common Table Expression (CTE) defined by a column list.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to add the CTE to.</param>
|
||||
/// <param name="tableName">The name of the CTE.</param>
|
||||
/// <param name="columns">The list of column names for the CTE.</param>
|
||||
/// <param name="configureAction">An action that configures the CTE query.</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query, configureAction, or columns is null.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when tableName is null, empty, or whitespace, or when columns is empty.</exception>
|
||||
/// <remarks>
|
||||
/// <para>Allows explicit specification of CTE column names using the syntax:
|
||||
/// WITH cte_name (col1, col2, col3) AS (query)</para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var sql = new QueryBreakdown()
|
||||
/// .WithCte("active_users", new[] { "id", "name", "email" }, cte => cte
|
||||
/// .Select("user_id, user_name, user_email")
|
||||
/// .From("users")
|
||||
/// .Where("status = 'active'"))
|
||||
/// .Select("*")
|
||||
/// .From("active_users")
|
||||
/// .GetSql();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown WithCte(
|
||||
this QueryBreakdown query,
|
||||
string tableName,
|
||||
string[] columns,
|
||||
Action<QueryBreakdown> configureAction)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tableName))
|
||||
{
|
||||
throw new ArgumentException("CTE table name cannot be null, empty, or whitespace.", nameof(tableName));
|
||||
}
|
||||
|
||||
if (columns == null || columns.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Columns cannot be null or empty.", nameof(columns));
|
||||
}
|
||||
|
||||
if (configureAction == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureAction), "Configuration action cannot be null.");
|
||||
}
|
||||
|
||||
// Create a new QueryBreakdown for the CTE with column list
|
||||
var cteQuery = new QueryBreakdown();
|
||||
|
||||
// Configure the CTE using the provided action
|
||||
configureAction(cteQuery);
|
||||
|
||||
// Create a WithClause with column list
|
||||
var withClause = new WithClause(tableName, cteQuery)
|
||||
{
|
||||
ColumnList = new List<string>(columns)
|
||||
};
|
||||
|
||||
// Add the CTE to the main query
|
||||
query.AddWithClause(withClause);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Common Table Expression (CTE) defined with an IQueryBreakdown instance.
|
||||
/// </summary>
|
||||
/// <param name="query">The query to add the CTE to.</param>
|
||||
/// <param name="tableName">The name of the CTE.</param>
|
||||
/// <param name="cteQuery">The query that defines the CTE.</param>
|
||||
/// <returns>The same query object for method chaining.</returns>
|
||||
/// <exception cref="ArgumentNullException">Thrown when query or cteQuery is null.</exception>
|
||||
/// <exception cref="ArgumentException">Thrown when tableName is null, empty, or whitespace.</exception>
|
||||
/// <remarks>
|
||||
/// This is the base method used by the other WithCte overloads. Use when you have an
|
||||
/// already-configured query to add as a CTE.
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// var cteQuery = new QueryBreakdown()
|
||||
/// .Select("id, name")
|
||||
/// .From("users")
|
||||
/// .Where("active = 1");
|
||||
///
|
||||
/// var sql = new QueryBreakdown()
|
||||
/// .WithCte("active_users", cteQuery)
|
||||
/// .Select("*")
|
||||
/// .From("active_users")
|
||||
/// .GetSql();
|
||||
/// </code>
|
||||
/// </example>
|
||||
public static QueryBreakdown WithCte(
|
||||
this QueryBreakdown query,
|
||||
string tableName,
|
||||
IQueryBreakdown cteQuery)
|
||||
{
|
||||
if (query == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(query), "Query cannot be null.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(tableName))
|
||||
{
|
||||
throw new ArgumentException("CTE table name cannot be null, empty, or whitespace.", nameof(tableName));
|
||||
}
|
||||
|
||||
if (cteQuery == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cteQuery), "CTE query cannot be null.");
|
||||
}
|
||||
|
||||
query.AddWithClause(tableName, cteQuery);
|
||||
return query;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user