SQL Formatter 处理 sqlc 宏的格式化问题解决方案

SQL Formatter 处理 sqlc 宏的格式化问题解决方案

痛点:SQLC 宏在格式化中的困境

作为现代SQL开发工作流的重要工具,sqlc(SQL Compiler)通过代码生成技术将SQL查询转换为类型安全的编程语言代码。然而,当开发者尝试使用SQL Formatter对包含sqlc宏的SQL文件进行格式化时,经常会遇到以下问题:

  • 语法解析错误:sqlc特有的宏语法(如 @sqlc.arg@sqlc.narg)被误认为非法标识符
  • 格式化中断:宏语句破坏整体代码结构,导致格式化结果支离破碎
  • 参数替换失效:宏相关的参数占位符无法正确识别和处理

SQL Formatter 参数处理机制解析

核心架构概述

SQL Formatter采用分层架构处理SQL格式化:

mermaid

参数处理核心类

// Params.ts 核心参数处理逻辑
export default class Params {
  private params: ParamItems | string[] | undefined;
  private index: number;

  public get({ key, text }: { key?: string; text: string }): string {
    if (!this.params) return text;
    if (key) return (this.params as ParamItems)[key];
    return (this.params as string[])[this.index++];
  }
}

sqlc 宏格式化解决方案

方案一:使用自定义参数类型配置

针对sqlc常见的 :name@name 等参数语法,通过 paramTypes 配置进行支持:

const formattedSQL = format(sqlText, {
  language: 'postgresql',
  paramTypes: {
    named: [':', '@'], // 支持 :param 和 @param 语法
    quoted: [':', '@'] // 支持 :"param" 和 @"param" 语法
  },
  params: {
    user_id: '123',
    status: "'active'"
  }
});

方案二:处理 sqlc 特殊宏语法

对于 @sqlc.arg@sqlc.narg 等特殊宏,需要自定义正则表达式匹配:

const formattedSQL = format(sqlText, {
  language: 'postgresql',
  paramTypes: {
    custom: [{
      regex: String.raw`@sqlc\.(arg|narg)\(['"]([\w_]+)['"]\)`,
      key: (text) => {
        const match = text.match(/@sqlc\.(arg|narg)\(['"]([\w_]+)['"]\)/);
        return match ? match[2] : text;
      }
    }]
  },
  params: {
    user_id: '123',
    status: "'active'"
  }
});

方案三:完整示例代码

-- 原始包含sqlc宏的SQL
SELECT 
  users.id,
  users.name,
  users.email
FROM users
WHERE 
  users.id = @sqlc.arg('user_id')
  AND users.status = @sqlc.narg('status')
  AND users.created_at > :start_date;
// 格式化配置
const config = {
  language: 'postgresql',
  tabWidth: 2,
  keywordCase: 'upper',
  paramTypes: {
    named: [':', '@'],
    custom: [{
      regex: String.raw`@sqlc\.(arg|narg)\(['"]([\w_]+)['"]\)`,
      key: (text) => text.replace(/@sqlc\.(arg|narg)\(['"]([\w_+)['"]\)/, '$2')
    }]
  },
  params: {
    user_id: '123',
    status: "'active'",
    start_date: "'2023-01-01'"
  }
};

const result = format(sqlText, config);

不同SQL方言的适配策略

PostgreSQL 方言适配

// PostgreSQL + sqlc 配置
const pgConfig = {
  language: 'postgresql',
  paramTypes: {
    numbered: ['$'],    // 支持 $1, $2
    named: [':', '@'],  // 支持 :name, @name
    custom: [{
      regex: String.raw`@sqlc\.[a-z]+\([^)]+\)`,
      key: extractSqlcParamName
    }]
  }
};

MySQL 方言适配

// MySQL + sqlc 配置
const mysqlConfig = {
  language: 'mysql',
  paramTypes: {
    positional: true,   // 支持 ?
    named: [':', '@'],  // 支持 :name, @name
    custom: [{
      regex: String.raw`@sqlc\.[a-z]+\([^)]+\)`,
      key: extractSqlcParamName
    }]
  }
};

高级配置技巧

参数提取工具函数

function extractSqlcParamName(text) {
  const patterns = [
    /@sqlc\.arg\(['"]([^'"]+)['"]\)/,
    /@sqlc\.narg\(['"]([^'"]+)['"]\)/,
    /@sqlc\.(slice|where)\(['"]([^'"]+)['"]\)/
  ];
  
  for (const pattern of patterns) {
    const match = text.match(pattern);
    if (match) return match[1] || match[2];
  }
  return text;
}

批量处理配置

const sqlcAwareConfig = {
  language: 'postgresql',
  tabWidth: 2,
  useTabs: false,
  keywordCase: 'upper',
  linesBetweenQueries: 2,
  paramTypes: {
    named: [':', '@', '$'],
    quoted: [':', '@', '$'],
    custom: [
      {
        regex: String.raw`@sqlc\.[a-z]+\([^)]+\)`,
        key: extractSqlcParamName
      },
      {
        regex: String.raw`\{\{[\w_]+\}\}`, // 支持模板语法
        key: text => text.slice(2, -2)
      }
    ]
  }
};

常见问题排查表

问题现象原因分析解决方案
宏语法被拆散词法分析器无法识别完整宏使用自定义正则匹配完整宏
参数未替换参数名提取不正确完善key提取函数
格式化错误方言不支持特定语法检查方言兼容性
性能下降复杂正则表达式优化正则模式,避免回溯

最佳实践总结

  1. 明确方言选择:根据实际使用的数据库选择正确的language配置
  2. 渐进式配置:从简单配置开始,逐步添加复杂的参数类型支持
  3. 正则表达式优化:使用非贪婪匹配和具体模式避免性能问题
  4. 测试验证:对各类sqlc宏进行充分测试,确保格式化效果
  5. 版本兼容:注意SQL Formatter版本更新可能带来的配置变化

通过合理的paramTypes配置和自定义参数处理,SQL Formatter能够完美处理包含sqlc宏的SQL代码,实现既保持代码可读性又不破坏宏功能的格式化效果。这种方案特别适合在CI/CD流水线中集成,确保团队代码风格的一致性。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值