using System.Runtime.Serialization;
using System.Text;
using Strata.SqlTools.SqlBreakdown.Classes;
using Strata.SqlTools.SqlBreakdown.Interfaces;
namespace Strata.SqlTools.Breakdowns.Snowflake;
///
/// Snowflake SQL-specific collection for managing multiple QueryBreakdown objects.
///
///
/// This class extends SqlBreakdownCollection with Snowflake-specific functionality,
/// including support for Snowflake features like semi-structured data, stage references,
/// time travel, snowflake-specific parameters (:parameter and @parameter syntax),
/// and proper batch handling.
///
[Serializable]
public class QueryBreakdownCollection : SqlBreakdownCollection
{
private readonly List _queryBreakdowns;
///
/// Initializes a new instance of the class for Snowflake.
///
public QueryBreakdownCollection() : base()
{
_queryBreakdowns = new List();
}
///
/// Initializes a new instance of the class with initial query breakdowns.
///
/// The initial collection of query breakdowns.
public QueryBreakdownCollection(IEnumerable queryBreakdowns)
: base(queryBreakdowns?.Cast() ?? Enumerable.Empty())
{
_queryBreakdowns = new List(queryBreakdowns ?? Enumerable.Empty());
}
///
/// Validates that the backing list survived deserialization, since deserialization bypasses
/// the constructors that normally initialize it (SonarQube rule S5766).
///
/// The streaming context for the deserialization operation.
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
if (_queryBreakdowns is null)
{
throw new SerializationException("Deserialized QueryBreakdownCollection is missing its backing list.");
}
}
///
/// Gets the collection of QueryBreakdown objects.
///
public IReadOnlyList QueryBreakdowns => _queryBreakdowns.AsReadOnly();
///
/// Adds a QueryBreakdown to the collection.
///
/// The query breakdown to add.
/// Thrown when queryBreakdown is null.
public void Add(QueryBreakdown queryBreakdown)
{
if (queryBreakdown == null)
{
throw new ArgumentNullException(nameof(queryBreakdown));
}
_queryBreakdowns.Add(queryBreakdown);
base.Add(queryBreakdown);
}
///
/// Adds multiple QueryBreakdowns to the collection.
///
/// The query breakdowns to add.
/// Thrown when queryBreakdowns is null.
public void AddRange(IEnumerable queryBreakdowns)
{
if (queryBreakdowns == null)
{
throw new ArgumentNullException(nameof(queryBreakdowns));
}
foreach (var breakdown in queryBreakdowns)
{
Add(breakdown);
}
}
///
/// Removes a QueryBreakdown from the collection.
///
/// The query breakdown to remove.
/// True if removed; otherwise, false.
public bool Remove(QueryBreakdown queryBreakdown)
{
var removed = _queryBreakdowns.Remove(queryBreakdown);
if (removed)
{
base.Remove(queryBreakdown);
}
return removed;
}
///
/// Clears all query breakdowns from the collection.
///
public new void Clear()
{
_queryBreakdowns.Clear();
base.Clear();
}
///
/// Gets the Snowflake SQL batch representation with Snowflake-specific formatting.
///
///
/// Generates Snowflake SQL with proper statement separation and optional session setup.
/// Snowflake uses semicolons as statement separators instead of GO.
///
/// Whether to include setup and finish clauses.
/// Whether to include session context setup statements.
/// The formatted Snowflake SQL batch.
public string GetSnowflakeBatch(bool includeSetupFinish = true, bool includeSessionSetup = false)
{
var sb = new StringBuilder();
// Add session setup if requested
if (includeSessionSetup)
{
sb.AppendLine("-- Snowflake Session Setup");
sb.AppendLine("ALTER SESSION SET NULLABLE_AS_NULL = FALSE;");
sb.AppendLine("ALTER SESSION SET ERROR_ON_NONDETERMINISTIC_UPDATE = FALSE;");
sb.AppendLine();
}
// Add all queries with semicolon separators
if (_queryBreakdowns.Count > 0)
{
for (int i = 0; i < _queryBreakdowns.Count; i++)
{
var query = _queryBreakdowns[i];
var sql = query.GetSql(includeSetupFinish);
// Ensure proper termination
var trimmed = sql.TrimEnd();
sb.Append(trimmed);
if (!trimmed.EndsWith(';'))
{
sb.Append(";");
}
// Add spacing between statements
if (i < _queryBreakdowns.Count - 1)
{
sb.AppendLine();
sb.AppendLine();
}
}
}
return sb.ToString();
}
///
/// Filters queries that reference Snowflake stages (using @ or @~ syntax).
///
///
/// Stage references use the pattern @stage_name/ or @~/stage_name/.
/// This specifically matches stage references and avoids false positives from @parameter syntax.
///
/// Optional stage name to filter by. If null, returns all queries using any stage.
/// Query breakdowns that reference stages.
public IEnumerable WhereUseStageReference(string? stageName = null)
{
return _queryBreakdowns.Where(q =>
{
var sql = q.GetSql();
// Use regex to match stage references: @stage_name/ or @~/stage_name/
// This avoids false positives from @parameter syntax
var stagePattern = @"@[\w~]+/";
if (!System.Text.RegularExpressions.Regex.IsMatch(sql, stagePattern, System.Text.RegularExpressions.RegexOptions.None, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout))
{
return false;
}
if (stageName == null)
{
return true;
}
var specificPattern = stageName.Contains("~")
? $@"@~/{System.Text.RegularExpressions.Regex.Escape(stageName.TrimStart('@', '~', '/'))}/"
: $@"@{System.Text.RegularExpressions.Regex.Escape(stageName.TrimStart('@'))}/";
return System.Text.RegularExpressions.Regex.IsMatch(sql, specificPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase, Strata.SqlTools.SqlBreakdown.Utilities.RegexDefaults.MatchTimeout);
});
}
///
/// Filters queries that reference JSON/semi-structured data using Snowflake's JSON operators.
///
/// Query breakdowns that use JSON functions or colon notation.
public IEnumerable WhereUseSemiStructuredData()
{
return _queryBreakdowns.Where(q =>
{
var sql = q.GetSql().ToUpperInvariant();
// Check for JSON functions or colon notation used in semi-structured data
return sql.Contains("JSON_") ||
sql.Contains("OBJECT_") ||
sql.Contains("ARRAY_") ||
sql.Contains("FLATTEN(") ||
sql.Contains(":VALUE") ||
sql.Contains(":NAME") ||
sql.Contains(":TYPE");
});
}
///
/// Filters queries that use Snowflake-specific parameter syntax (:param or @param).
///
/// The parameter name (with or without : or @).
/// Query breakdowns using the specified Snowflake parameter.
public IEnumerable WhereUseSnowflakeParameter(string parameterName)
{
if (string.IsNullOrWhiteSpace(parameterName))
{
throw new ArgumentNullException(nameof(parameterName));
}
// Normalize parameter name (remove : or @)
var cleanName = parameterName.TrimStart(':', '@');
return _queryBreakdowns.Where(q =>
{
var sql = q.GetSql();
return sql.Contains($":{cleanName}", StringComparison.OrdinalIgnoreCase) ||
sql.Contains($"@{cleanName}", StringComparison.OrdinalIgnoreCase);
});
}
///
/// Filters queries that use Snowflake time travel features.
///
///
/// Detects use of BEFORE, AT, or MATCH_CONDITION clauses for time travel queries.
///
/// Query breakdowns using time travel syntax.
public IEnumerable WhereUseTimeTravelFeature()
{
return _queryBreakdowns.Where(q =>
{
var sql = q.GetSql().ToUpperInvariant();
return sql.Contains("BEFORE (") ||
sql.Contains("AT (") ||
sql.Contains("MATCH_CONDITION");
});
}
///
/// Filters queries that use Snowflake functions (PARSE_JSON, OBJECT_INSERT, ARRAY, etc.).
///
/// Query breakdowns using Snowflake-specific functions.
public IEnumerable WhereUseSnowflakeFunctions()
{
return _queryBreakdowns.Where(q =>
{
var sql = q.GetSql().ToUpperInvariant();
var snowflakeFunctions = new[]
{
"PARSE_JSON", "OBJECT_INSERT", "ARRAY_CONSTRUCT", "ARRAY_AGG",
"FLATTEN", "GET_PATH", "TRY_PARSE_JSON", "JSON_EXTRACT_PATH_TEXT",
"JSON_EXTRACT_PATH_WITH_DEFAULT", "HASHAGGREGATE", "LISTAGG",
"APPROX_COUNT_DISTINCT", "APPROX_PERCENTILE", "GREATEST", "LEAST",
"NULLIF", "ZEROIFNULL", "STRTOK", "SPLIT_PART", "PIVOT", "UNPIVOT"
};
return snowflakeFunctions.Any(func => sql.Contains(func));
});
}
///
/// Filters queries that reference temporary or dynamic tables.
///
/// Query breakdowns using temporary tables.
public IEnumerable WhereUseTemporaryTables()
{
return _queryBreakdowns.Where(q =>
{
var sql = q.GetSql().ToUpperInvariant();
return sql.Contains("TEMPORARY TABLE") ||
sql.Contains("TEMP TABLE") ||
sql.Contains("CREATE TEMP ") ||
sql.Contains("DYNAMIC TABLE");
});
}
///
/// Filters queries that reference external tables or stages.
///
/// Query breakdowns using external data sources.
public IEnumerable WhereUseExternalData()
{
return _queryBreakdowns.Where(q =>
{
var sql = q.GetSql().ToUpperInvariant();
return sql.Contains("EXTERNAL TABLE") ||
sql.Contains(" FROM @") ||
sql.Contains("COPY INTO @");
});
}
///
/// Filters queries by the SELECT clause content using Snowflake's format.
///
/// The text to find in the SELECT clause.
/// Filtered query breakdowns.
public IEnumerable WhereSelectContains(string selectContains)
{
if (string.IsNullOrWhiteSpace(selectContains))
{
throw new ArgumentNullException(nameof(selectContains));
}
return _queryBreakdowns.Where(q =>
q.SelectClause?.Clause?.Contains(selectContains, StringComparison.OrdinalIgnoreCase) ?? false);
}
///
/// Filters queries that reference specific tables or schemas.
///
/// The table name or schema pattern to find.
/// Filtered query breakdowns.
public IEnumerable WhereTableContains(string tableNameContains)
{
if (string.IsNullOrWhiteSpace(tableNameContains))
{
throw new ArgumentNullException(nameof(tableNameContains));
}
return _queryBreakdowns.Where(q =>
q.FromClause?.Clause?.Contains(tableNameContains, StringComparison.OrdinalIgnoreCase) ?? false);
}
///
/// Filters queries that have WHERE clauses.
///
/// Query breakdowns with WHERE clauses.
public IEnumerable WhereHaveWhereClause()
{
return _queryBreakdowns.Where(q =>
!string.IsNullOrWhiteSpace(q.WhereClause?.Clause));
}
///
/// Filters queries without WHERE clauses (potentially risky for full table scans).
///
/// Query breakdowns without WHERE clauses.
public IEnumerable WhereHaveNoWhereClause()
{
return _queryBreakdowns.Where(q =>
string.IsNullOrWhiteSpace(q.WhereClause?.Clause));
}
///
/// Filters queries that have GROUP BY clauses.
///
/// Query breakdowns with GROUP BY clauses.
public IEnumerable WhereHaveGroupByClause()
{
return _queryBreakdowns.Where(q =>
!string.IsNullOrWhiteSpace(q.GroupByClause?.Clause));
}
///
/// Filters queries that have ORDER BY clauses.
///
/// Query breakdowns with ORDER BY clauses.
public IEnumerable WhereHaveOrderByClause()
{
return _queryBreakdowns.Where(q =>
!string.IsNullOrWhiteSpace(q.OrderByClause?.Clause));
}
///
/// Gets a comprehensive analysis of all queries in the collection.
///
/// Analysis summary for each query.
public IEnumerable AnalyzeQueries()
{
return _queryBreakdowns.Select((q, index) => new SnowflakeQueryAnalysis
{
Index = index,
HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
UsesSemiStructuredData = UseSemiStructuredData(q),
UsesStageReference = UsesStageReference(q),
UsesTimeTravelFeature = UsesTimeTravelFeature(q),
UsesSnowflakeFunctions = UsesSnowflakeFunctions(q),
UsesExternalData = UsesExternalData(q),
ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
ParameterCount = q.ParameterList.Count()
});
}
///
/// Gets the combined Snowflake SQL from all breakdowns.
///
/// Whether to include setup and finish clauses.
/// The combined Snowflake SQL.
public string GetCombinedSql(bool includeSetupFinish = true)
{
return GetSnowflakeBatch(includeSetupFinish);
}
///
/// Helper method to check if a query uses semi-structured data.
///
private static bool UseSemiStructuredData(QueryBreakdown query)
{
var sql = query.GetSql().ToUpperInvariant();
return sql.Contains("JSON_") ||
sql.Contains("OBJECT_") ||
sql.Contains("ARRAY_") ||
sql.Contains("FLATTEN(");
}
///
/// Helper method to check if a query uses stage references.
///
private static bool UsesStageReference(QueryBreakdown query)
{
return query.GetSql().Contains("@") &&
(query.GetSql().Contains("FROM @") || query.GetSql().Contains(" @"));
}
///
/// Helper method to check if a query uses time travel features.
///
private static bool UsesTimeTravelFeature(QueryBreakdown query)
{
var sql = query.GetSql().ToUpperInvariant();
return sql.Contains("BEFORE (") ||
sql.Contains("AT (") ||
sql.Contains("MATCH_CONDITION");
}
///
/// Helper method to check if a query uses Snowflake-specific functions.
///
private static bool UsesSnowflakeFunctions(QueryBreakdown query)
{
var sql = query.GetSql().ToUpperInvariant();
var snowflakeFunctions = new[]
{
"PARSE_JSON", "OBJECT_INSERT", "ARRAY_CONSTRUCT", "FLATTEN",
"LISTAGG", "APPROX_COUNT_DISTINCT", "HASH", "ZEROIFNULL"
};
return snowflakeFunctions.Any(func => sql.Contains(func));
}
///
/// Helper method to check if a query uses external data.
///
private static bool UsesExternalData(QueryBreakdown query)
{
var sql = query.GetSql().ToUpperInvariant();
return sql.Contains("EXTERNAL TABLE") ||
sql.Contains(" FROM @") ||
sql.Contains("COPY INTO @");
}
///
/// Synchronizes parameter values across all queries in the collection.
/// Ensures that if a parameter with the same name exists in multiple queries, they all have the same value.
///
public void SynchronizeParameters()
{
// Get all unique parameter names across all queries
var allParameterNames = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var query in _queryBreakdowns)
{
foreach (var paramName in query.Parameters.Keys)
{
allParameterNames.Add(paramName);
}
}
// For each parameter, use the last query's value and sync to all queries that have it
foreach (var paramName in allParameterNames)
{
object? lastValue = null;
bool parameterFound = false;
// Find the last query that has this parameter and get its value
for (int i = _queryBreakdowns.Count - 1; i >= 0; i--)
{
if (_queryBreakdowns[i].Parameters.ContainsKey(paramName))
{
lastValue = _queryBreakdowns[i].Parameters[paramName];
parameterFound = true;
break;
}
}
// Synchronize the parameter value to all queries that have it
if (parameterFound)
{
foreach (var query in _queryBreakdowns.Where(q => q.Parameters.ContainsKey(paramName)))
{
query.Parameters[paramName] = lastValue!;
}
}
}
}
///
/// Adds a parameter with a specific value to all queries in the collection.
///
/// The name of the parameter (without the : or @ prefix).
/// The value to assign to the parameter. Can be null.
public void AddParameterToAll(string parameterName, object? value)
{
if (string.IsNullOrWhiteSpace(parameterName))
{
throw new ArgumentException("Parameter name cannot be null or empty.", nameof(parameterName));
}
foreach (var query in _queryBreakdowns)
{
query.Parameters[parameterName] = value!;
}
}
///
/// Gets all unique parameters from all queries in the collection as a combined dictionary.
///
/// A dictionary containing all unique parameters across all queries.
protected Dictionary GetCombinedParameterDictionary()
{
var combinedParams = new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var query in _queryBreakdowns)
{
// Add from ParameterList (parsed parameters)
foreach (var param in query.ParameterList)
{
combinedParams[param.Name] = param.Value;
}
// Add/override from Parameters dictionary (manually added parameters)
foreach (var param in query.Parameters)
{
combinedParams[param.Key] = param.Value;
}
}
return combinedParams;
}
///
/// Gets a formatted string representation of all unique parameters with Snowflake-specific syntax.
///
/// If true, includes Snowflake data types in the output format.
///
/// A formatted string such as ":paramName = value" or ":paramName = value -- VARIANT"
/// for each unique parameter.
///
public string GetParametersAsString(bool includeDataTypes = false)
{
var parameters = GetCombinedParameterDictionary();
if (parameters.Count == 0)
{
return string.Empty;
}
var sb = new StringBuilder();
var isFirst = true;
foreach (var param in parameters.OrderBy(p => p.Key, StringComparer.OrdinalIgnoreCase))
{
if (!isFirst)
{
sb.AppendLine(",");
}
sb.Append($":{param.Key} = {FormatParameterValue(param.Value)}");
if (includeDataTypes)
{
var dataType = GetSnowflakeDataType(param.Value);
sb.Append($" -- {dataType}");
}
isFirst = false;
}
return sb.ToString();
}
///
/// Gets a detailed usage report for all parameters across the queries in the collection.
///
/// An enumerable of ParameterUsageReport objects with usage statistics.
///
/// Gets a report of parameter usage across all queries.
///
/// Parameter usage information.
public IEnumerable GetParameterUsageReport()
{
// Collect all unique parameter names from both ParameterList and Parameters dictionary
var allParamNames = new HashSet(StringComparer.OrdinalIgnoreCase);
foreach (var query in _queryBreakdowns)
{
// Add from ParameterList (parsed parameters)
foreach (var param in query.ParameterList)
{
allParamNames.Add(param.Name);
}
// Add from Parameters dictionary (manually added parameters)
foreach (var paramName in query.Parameters.Keys)
{
allParamNames.Add(paramName);
}
}
foreach (var paramName in allParamNames)
{
var queriesUsing = 0;
object? lastValue = null;
foreach (var query in _queryBreakdowns)
{
// Check ParameterList first (parsed)
var param = query.ParameterList.FirstOrDefault(p => p.Name.Equals(paramName, StringComparison.OrdinalIgnoreCase));
if (param != null)
{
queriesUsing++;
lastValue = param.Value;
}
// Also check Parameters dictionary (manually added)
else if (query.Parameters.TryGetValue(paramName, out var dictValue))
{
queriesUsing++;
lastValue = dictValue;
}
}
yield return new ParameterUsageReport
{
ParameterName = paramName,
Value = lastValue,
UsedInQueryCount = queriesUsing,
TotalQueries = _queryBreakdowns.Count
};
}
}
///
/// Gets the total number of columns selected across all queries.
///
/// Total column count.
public int GetTotalSelectedColumns()
{
return _queryBreakdowns.Sum(q =>
!string.IsNullOrWhiteSpace(q.SelectClause?.Clause)
? q.SelectClause.Clause.Split(',').Length
: 0);
}
///
/// Gets all unique table names referenced across all queries.
///
///
/// This provides a quick overview of which tables are being queried.
/// Note: This is a best-effort extraction and may not capture all table references,
/// especially in complex subqueries or with aliasing.
///
/// List of unique table names.
public IEnumerable GetUniqueTableReferences()
{
var tables = new HashSet(StringComparer.OrdinalIgnoreCase);
var tableNames = _queryBreakdowns
.Where(q => !string.IsNullOrWhiteSpace(q.FromClause?.Clause))
.SelectMany(q => ExtractTableNames(q.FromClause!.Clause!));
foreach (var table in tableNames)
{
tables.Add(table);
}
return tables;
}
///
/// Gets a summary of all queries including their types and basic composition.
///
/// Summary information for each query.
public IEnumerable GetQuerySummaries()
{
var stageQueries = WhereUseStageReference().ToHashSet();
var semiStructuredQueries = WhereUseSemiStructuredData().ToHashSet();
return _queryBreakdowns.Select((q, index) => new SnowflakeQueryAnalysis
{
Index = index,
HasSelectClause = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause),
HasFromClause = !string.IsNullOrWhiteSpace(q.FromClause?.Clause),
HasWhereClause = !string.IsNullOrWhiteSpace(q.WhereClause?.Clause),
HasGroupByClause = !string.IsNullOrWhiteSpace(q.GroupByClause?.Clause),
HasOrderByClause = !string.IsNullOrWhiteSpace(q.OrderByClause?.Clause),
HasCTE = q.WithClauses.Count > 0,
UsesSemiStructuredData = semiStructuredQueries.Contains(q),
UsesStageReference = stageQueries.Contains(q),
UsesTimeTravelFeature = UsesTimeTravelFeature(q),
UsesSnowflakeFunctions = false,
UsesExternalData = false,
ColumnCount = !string.IsNullOrWhiteSpace(q.SelectClause?.Clause) ? q.SelectClause.Clause.Split(',').Length : 0,
ParameterCount = q.ParameterList.Count()
});
}
///
/// Helper method to extract table names from a FROM clause.
///
private static IEnumerable ExtractTableNames(string fromClause)
{
if (string.IsNullOrWhiteSpace(fromClause))
{
yield break;
}
var parts = fromClause.Split(',');
foreach (var part in parts)
{
var trimmed = part.Trim();
var tokens = trimmed.Split(new[] { " AS ", " " }, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length > 0)
{
var tableName = tokens[0].Trim();
if (!string.IsNullOrWhiteSpace(tableName))
{
yield return tableName;
}
}
}
}
///
/// Helper method to convert a .NET object to its corresponding Snowflake data type string.
///
private static string GetSnowflakeDataType(object? value)
{
return value switch
{
null => "VARIANT",
bool => "BOOLEAN",
byte or sbyte or short or ushort or int or uint or long or ulong => "NUMBER",
float or double or decimal => "NUMBER",
DateTime or DateTimeOffset => "TIMESTAMP_NTZ",
TimeSpan => "TIME",
string => value.ToString()!.Length > 255 ? "VARCHAR(MAX)" : "VARCHAR(255)",
byte[] => "BINARY",
_ => "VARIANT"
};
}
///
/// Helper method to format a parameter value for safe inclusion in Snowflake SQL statements.
///
private static string FormatParameterValue(object? value)
{
return value switch
{
null => "NULL",
bool b => b ? "TRUE" : "FALSE",
string s => $"'{s.Replace("'", "''")}'",
DateTime dt => $"'{dt:yyyy-MM-dd HH:mm:ss}'",
DateTimeOffset dto => $"'{dto:yyyy-MM-dd HH:mm:ss}'",
_ => value.ToString() ?? "NULL"
};
}
}
///
/// Analysis information about a Snowflake query.
///
public class SnowflakeQueryAnalysis
{
///
/// Gets or sets the index of the query in the collection.
///
public int Index { get; set; }
///
/// Gets or sets whether the query has a SELECT clause.
///
public bool HasSelectClause { get; set; }
///
/// Gets or sets whether the query has a FROM clause.
///
public bool HasFromClause { get; set; }
///
/// Gets or sets whether the query has a WHERE clause.
///
public bool HasWhereClause { get; set; }
///
/// Gets or sets whether the query has a GROUP BY clause.
///
public bool HasGroupByClause { get; set; }
///
/// Gets or sets whether the query has an ORDER BY clause.
///
public bool HasOrderByClause { get; set; }
///
/// Gets or sets whether the query has Common Table Expressions (CTEs).
///
public bool HasCTE { get; set; }
///
/// Gets or sets whether the query uses semi-structured data functions.
///
public bool UsesSemiStructuredData { get; set; }
///
/// Gets or sets whether the query references Snowflake stages.
///
public bool UsesStageReference { get; set; }
///
/// Gets or sets whether the query uses Snowflake time travel features.
///
public bool UsesTimeTravelFeature { get; set; }
///
/// Gets or sets whether the query uses Snowflake-specific functions.
///
public bool UsesSnowflakeFunctions { get; set; }
///
/// Gets or sets whether the query uses external data sources.
///
public bool UsesExternalData { get; set; }
///
/// Gets or sets the number of columns in the SELECT clause.
///
public int ColumnCount { get; set; }
///
/// Gets or sets the number of parameters used.
///
public int ParameterCount { get; set; }
///
/// Returns a string representation of the query analysis.
///
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"Snowflake Query #{Index}");
sb.AppendLine($" Basic Structure:");
sb.AppendLine($" SELECT: {(HasSelectClause ? "Yes" : "No")} ({ColumnCount} columns)");
sb.AppendLine($" FROM: {(HasFromClause ? "Yes" : "No")}");
sb.AppendLine($" WHERE: {(HasWhereClause ? "Yes" : "No")}");
sb.AppendLine($" GROUP BY: {(HasGroupByClause ? "Yes" : "No")}");
sb.AppendLine($" ORDER BY: {(HasOrderByClause ? "Yes" : "No")}");
sb.AppendLine($" Snowflake Features:");
sb.AppendLine($" Semi-Structured Data: {(UsesSemiStructuredData ? "Yes" : "No")}");
sb.AppendLine($" Stage Reference: {(UsesStageReference ? "Yes" : "No")}");
sb.AppendLine($" Time Travel: {(UsesTimeTravelFeature ? "Yes" : "No")}");
sb.AppendLine($" Snowflake Functions: {(UsesSnowflakeFunctions ? "Yes" : "No")}");
sb.AppendLine($" External Data: {(UsesExternalData ? "Yes" : "No")}");
sb.Append($" Parameters: {ParameterCount}");
return sb.ToString();
}
}
///
/// Reports parameter usage statistics across queries in a Snowflake collection.
///
public class ParameterUsageReport
{
///
/// Gets or sets the name of the parameter.
///
public string ParameterName { get; set; } = string.Empty;
///
/// Gets or sets the current value of the parameter.
///
public object? Value { get; set; }
///
/// Gets or sets the number of queries using this parameter.
///
public int UsedInQueryCount { get; set; }
///
/// Gets or sets the total number of queries in the collection.
///
public int TotalQueries { get; set; }
///
/// Gets a value indicating whether this parameter is used in all queries.
///
public bool IsUsedInAllQueries => UsedInQueryCount == TotalQueries && TotalQueries > 0;
///
/// Returns a string representation of the parameter usage report.
///
public override string ToString()
{
if (TotalQueries == 0)
{
return $"{ParameterName}: No queries";
}
var percentage = (UsedInQueryCount * 100.0) / TotalQueries;
var valueStr = Value switch
{
null => "NULL",
string s => $"'{s}'",
_ => Value.ToString() ?? "NULL"
};
return $"{ParameterName} = {valueStr} ({UsedInQueryCount}/{TotalQueries} queries - {percentage:F1}%)";
}
}