使用C#配置信息类的属性生成Snowflake CREATE STAGE语句

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';

实现说明:

  1. 配置类结构StageConfig包含所有可能的阶段配置参数,FileFormatOptions专门处理文件格式配置
  2. SQL生成逻辑
    • 自动处理OR REPLACE和IF NOT EXISTS修饰符
    • 按正确顺序拼接各个配置参数
    • 自动处理单引号转义
    • 支持嵌套的文件格式和复制选项配置
  3. 扩展性:采用模块化设计,每个配置部分有独立的处理方法,方便后续扩展
  4. 格式处理:自动格式化生成的SQL语句,保持Snowflake SQL规范风格
  5. 安全处理:对字符串值进行基本的SQL注入防护(单引号转义)
相关推荐
zl218786544825 分钟前
Playwright同步、异步、并行、串行执行效率比较
开发语言·python·测试工具
Tony Bai1 小时前
【Go开发者的数据库设计之道】05 落地篇:Go 语言四种数据访问方案深度对比
开发语言·数据库·后端·golang
gopyer1 小时前
180课时吃透Go语言游戏后端开发3:Go语言中其他常用的数据类型
开发语言·游戏·golang·游戏后端开发
come112341 小时前
Go vs. PHP:核心优势劣势对比
开发语言·golang·php
eqwaak01 小时前
Flask实战指南:从基础到高阶的完整开发流程
开发语言·后端·python·学习·flask
默|笙2 小时前
【c++】红黑树的部分实现
开发语言·c++
轩情吖2 小时前
Qt常用控件之QSpinBox
开发语言·c++·qt·控件·桌面级开发·qspinbox·微调框
掘根2 小时前
【Qt】输入类控件2——SpinBox,DateEdit,TimeEdit,Dial,Slider
开发语言·qt
wshzrf2 小时前
【Java系列课程·Java学前须知】第3课 JDK,JVM,JRE的区别和优缺
java·开发语言·jvm
铅笔侠_小龙虾2 小时前
JVM 深入研究 -- 详解class 文件
java·开发语言·jvm