csharp
using System;
using System.Collections.Generic;
using System.Linq;
public class StageConfig
{
public string Name { get; set; }
public bool Replace { get; set; }
public bool IfNotExists { get; set; }
public string StorageIntegration { get; set; }
public string Url { get; set; }
public string Credentials { get; set; }
public FileFormatOptions FileFormat { get; set; }
public Dictionary<string, string> CopyOptions { get; set; }
public string Comment { get; set; }
}
public class FileFormatOptions
{
public string FormatName { get; set; }
public string Type { get; set; }
public Dictionary<string, string> FormatOptions { get; set; } = new Dictionary<string, string>();
}
public class StageSqlGenerator
{
public static string GenerateCreateStageSql(StageConfig config)
{
var replaceModifier = config.Replace ? "OR REPLACE " : "";
var ifNotExistsModifier = config.IfNotExists ? "IF NOT EXISTS " : "";
var clauses = new List<string>();
AddStorageIntegration(config, clauses);
AddUrl(config, clauses);
AddCredentials(config, clauses);
AddFileFormat(config, clauses);
AddCopyOptions(config, clauses);
AddComment(config, clauses);
return FormatFinalSql(config, replaceModifier, ifNotExistsModifier, clauses);
}
private static void AddStorageIntegration(StageConfig config, List<string> clauses)
{
if (!string.IsNullOrEmpty(config.StorageIntegration))
{
clauses.Add($"STORAGE_INTEGRATION = {config.StorageIntegration}");
}
}
private static void AddUrl(StageConfig config, List<string> clauses)
{
if (!string.IsNullOrEmpty(config.Url))
{
clauses.Add($"URL = '{EscapeSqlString(config.Url)}'");
}
}
private static void AddCredentials(StageConfig config, List<string> clauses)
{
if (!string.IsNullOrEmpty(config.Credentials))
{
clauses.Add($"CREDENTIALS = ( {config.Credentials} )");
}
}
private static void AddFileFormat(StageConfig config, List<string> clauses)
{
if (config.FileFormat == null) return;
var ff = config.FileFormat;
if (!string.IsNullOrEmpty(ff.FormatName))
{
clauses.Add($"FILE_FORMAT = ( FORMAT_NAME = '{EscapeSqlString(ff.FormatName)}' )");
}
else if (!string.IsNullOrEmpty(ff.Type))
{
var typeClause = $"TYPE = {ff.Type}";
var options = ff.FormatOptions.Select(kv => $"{kv.Key} = {kv.Value}");
clauses.Add(options.Any()
? $"FILE_FORMAT = ( {typeClause}, {string.Join(", ", options)} )"
: $"FILE_FORMAT = ( {typeClause} )");
}
}
private static void AddCopyOptions(StageConfig config, List<string> clauses)
{
if (config.CopyOptions?.Any() != true) return;
var copyOptions = config.CopyOptions
.Select(kv => $"{kv.Key} = {kv.Value}");
clauses.Add($"COPY_OPTIONS = ( {string.Join(", ", copyOptions)} )");
}
private static void AddComment(StageConfig config, List<string> clauses)
{
if (!string.IsNullOrEmpty(config.Comment))
{
clauses.Add($"COMMENT = '{EscapeSqlString(config.Comment)}'");
}
}
private static string FormatFinalSql(StageConfig config, string replaceModifier,
string ifNotExistsModifier, List<string> clauses)
{
return $"CREATE {replaceModifier}STAGE {ifNotExistsModifier}{config.Name}" +
(clauses.Any()
? $"\n {string.Join("\n ", clauses)};"
: ";");
}
private static string EscapeSqlString(string input)
{
return input?.Replace("'", "''") ?? string.Empty;
}
}
// 使用示例
public class Program
{
public static void Main()
{
var config = new StageConfig
{
Name = "my_s3_stage",
Replace = true,
Url = "s3://mybucket/path/",
Credentials = "AWS_KEY_ID='AKIAXXX' AWS_SECRET_KEY='XXX'",
FileFormat = new FileFormatOptions
{
Type = "CSV",
FormatOptions = new Dictionary<string, string>
{
{ "FIELD_DELIMITER", "','" },
{ "SKIP_HEADER", "1" }
}
},
CopyOptions = new Dictionary<string, string>
{
{ "ON_ERROR", "'CONTINUE'" }
},
Comment = "Production stage for customer data"
};
var sql = StageSqlGenerator.GenerateCreateStageSql(config);
Console.WriteLine(sql);
}
}
生成的SQL语句示例:
sql
CREATE OR REPLACE STAGE my_s3_stage
URL = 's3://mybucket/path/'
CREDENTIALS = ( AWS_KEY_ID='AKIAXXX' AWS_SECRET_KEY='XXX' )
FILE_FORMAT = ( TYPE = CSV, FIELD_DELIMITER = ',', SKIP_HEADER = 1 )
COPY_OPTIONS = ( ON_ERROR = 'CONTINUE' )
COMMENT = 'Production stage for customer data';
实现说明:
- 配置类结构 :
StageConfig
包含所有可能的阶段配置参数,FileFormatOptions
专门处理文件格式配置 - SQL生成逻辑 :
- 自动处理OR REPLACE和IF NOT EXISTS修饰符
- 按正确顺序拼接各个配置参数
- 自动处理单引号转义
- 支持嵌套的文件格式和复制选项配置
- 扩展性:采用模块化设计,每个配置部分有独立的处理方法,方便后续扩展
- 格式处理:自动格式化生成的SQL语句,保持Snowflake SQL规范风格
- 安全处理:对字符串值进行基本的SQL注入防护(单引号转义)