SQLFluff 方言贡献指南:从理解词法与语法体系到提交一个方言修复
SQLFluff 方言贡献指南从理解词法与语法体系到提交一个方言修复【免费下载链接】sqlfluffA modular SQL linter and auto-formatter with support for multiple dialects and templated code.项目地址: https://gitcode.com/GitHub_Trending/sq/sqlfluffSQLFluff 以模块化的方式内置了 25 种 SQL 方言的解析能力而这一切都建立在方言 继承 ANSI 覆盖/新增语法段的架构之上。本文以 dialect.rst 为骨架结合 src/sqlfluff/dialects 下的真实源码与测试体系完整讲解 SQLFluff 的词法Lexer、语法Parser、Segment/Grammar 抽象、关键词管理、参考语法查找方法并逐例拆解 4 个真实的方言修复案例与配套测试流程。读完本文你将能够独立定位一个解析失败的方言问题写出正确的语法定义并提交一份带测试用例的合格 PR。为什么说方言贡献是用户进入 SQLFluff 的最佳入口SQLFluff 的维护者全部是业余时间贡献的志愿者而用户往往比维护者更了解自己所用的 SQL 方言并且拥有真实的数据库实例来验证语法是否有效。因此官方将贡献方言改动视为用户改进 SQLFluff 的最佳方式之一——能自己修掉阻塞自己的解析问题通常是解锁使用 SQLFluff 最快的一条路。好消息是SQLFluff 的 lexer 与 parser 被设计得高度模块化无需深厚的 Python 功底或对 SQLFluff 内部机制的深入了解即可上手扩展。仓库在 src/sqlfluff/dialects/AGENTS.md 中给出了一整套面向方言开发的指引而 GitHub 上健壮的 CI 流水线会在维护者介入之前先帮你验证改动是否正确、是否会影响其他用户。SQLFluff 如何解析 SQLLexer 与 Parser 的两阶段流水线在讨论怎么改语法之前必须先理解 SQLFluff 读取一条 SQL 的两步流程详见架构文档 architecture.rstLexing词法分析把 SQL 文本切分成符号与逻辑分组例如把SELECT * FROM table WHERE col1 12345切成SELECT、*、FROM、table、WHERE、col1、、12345而不是S、E、L……单字符序列。Parsing语法分析按方言文件里定义的语法把这些符号组装成带类型的解析树。方言文件dialect_name.py就是语法定义的载体。以最核心的 dialect_ansi.py 为例SelectClauseSegment的真实定义L1889-L1904如下class SelectClauseSegment(BaseSegment): A group of elements in a select target statement. type select_clause match_grammar: Matchable Sequence( SELECT, Ref(SelectClauseModifierSegment, optionalTrue), Indent, Delimited( Ref(SelectClauseElementSegment), allow_trailingTrue, ), Dedent, terminators[Ref(SelectClauseTerminatorGrammar)], parse_modeParseMode.GREEDY_ONCE_STARTED, )这段语法表达了select_clause以SELECT开头随后是一个允许尾随逗号的逗号分隔的SelectClauseElementSegment列表并在遇到终结符时结束。其中两个关键参数值得展开terminators终结符告诉 parser 该段落在哪里结束。这里的SelectClauseTerminatorGrammar会在遇到FROM、WHERE、ORDER BY等下一个子句起始处停下。解析过程本身是尝试各种 Segment/语法组合的密集计算因此明确段落的终止位置能让匹配高效收敛。parse_modeParseMode.GREEDY_ONCE_STARTED贪婪解析模式一旦匹配到段落开头就贪婪地吞下直到终结符为止的全部内容。这样任何意料之外的语法都会以unparsable区块的形式浮出表面而不是让整个匹配静默失败——这正是问题诊断时能看到Found unparsable section提示的机制来源。与之形成对比的是短小无歧义的段落比如JoinOnConditionSegmentL1976-L1985就不需要终结符和贪婪模式class JoinOnConditionSegment(BaseSegment): The ON condition within a JOIN clause. type join_on_condition match_grammar: Matchable Sequence( ON, Conditional(ImplicitIndent, indented_on_contentsTrue), OptionallyBracketed(Ref(ExpressionSegment)), Conditional(Dedent, indented_on_contentsTrue), )版本提示与本文档写作时代相比当前源码中JoinOnConditionSegment已改用Conditional(ImplicitIndent, indented_on_contentsTrue)来表达缩进控制这是项目在 src/sqlfluff/dialects/dialect_ansi.py 中持续演进的结果——贡献时以当前主干源码为准。不难注意到一个 Segment 可以引用另一个 SegmentRef(...)。这是把复杂 SQL 表达式拆解为可独立管理与处理的组件的关键手段。Segment 与 Grammar理解两种抽象Segment语法段一段定义了type的语法单元。这个type在后续的 lint 规则或解析树中可以被引用。Segment 既可以通过工厂函数如TypedParser、SegmentGenerator等创建也可以通过定义类来创建。Grammar语法一段可以在 Segment 中复用的语法。它本质上是一段语法的别名避免在多个地方重复书写同一套定义。Grammar 的另一个巨大优势是其他方言可以只覆盖某个 Segment 的一小块语法而不必为了微调而整段重写。例如 ANSI 方言定义了dialect_ansi.py#L466NotOperatorGrammar StringParser(NOT, KeywordSegment, typekeyword)而 MySQL 方言则覆盖为dialect_mysql.py#L300-L303NotOperatorGrammar OneOf( StringParser(NOT, KeywordSegment, typekeyword), StringParser(!, CodeSegment, typenot_operator), ),这使 MySQL 能在所有原本使用NotOperatorGrammar的位置同时接受!前提是各处都引用NotOperatorGrammar而非硬编码NOT。对比复制粘贴并长期维护两份近乎相同的代码的做法这种覆盖机制让方言定制变得极其轻量。语法原语一览创建 SQL 语法时有如下常用原语对应 sqlfluffrs/dialects 之外的 Python 语法模块 的实现Grammar用途示例KEYWORD匹配一个裸 SQL 关键字SELECTSequence()关键字或 Segment 的已知有序序列Sequence(SELECT, Ref(SelectClauseElementSegment), FROM...)AnyNumberOf()从一组候选中选择可重复任意次SELECT, AnyNumberOf(Ref(WildcardExpressionSegment), Ref(ColumnReferenceSegment)...)...OneOf()比AnyNumberOf更严格只能从集合中选一个OneOf(INNER,OUTER,FULL), JOINDelimited()用于列表默认是逗号分隔SELECT, Delimited(SelectClauseElementSegment), FROM...Bracketed()用于括号包裹的内容如函数参数Ref(FunctionNameSegment), Bracketed(Ref(FunctionContentsGrammar)这些原语中optionalTrue是最高频的附加参数用于进一步刻画语句构成。DeleteStatementSegmentANSI 定义见 dialect_ansi.py#L3790就是典型例子match_grammar Sequence( DELETE, Ref(FromClauseSegment), Ref(WhereClauseSegment, optionalTrue), )WHERE子句被声明为可选——尽管无数人因为缺少WHERE的 DELETE 而后悔但 SQL 语法确实允许这样写。方言体系一切从 ANSI 继承无论哪种 SQL 方言SELECT.. FROM... WHERE这类基础语句都大同小异差别集中在各厂商为实现自身需求而扩展的语法上。SQLFluff 因此允许创建相互独立的dialects。所有方言文件都位于 src/sqlfluff/dialects而每个方言最终都从dialect_ansi.py继承。在 SQLFluff 中一个方言本质上就是一个从 ANSI 方言复制过来然后增删/覆盖解析段的文件。如果某个方言的SELECT/FROM/WHERE与 ANSI 完全一致、只有ORDER BY不同那么只需覆盖ORDER BY子句方言文件会非常小而对差异巨大的方言如 T-SQL需要覆盖的部分则多得多。从源码看方言的注册与覆盖依赖 src/sqlfluff/core/dialects/base.py 提供的几个核心方法copy_as(name)L132复制当前方言并改名是继承的入口add(**kwargs)L168直接向方言注册新 Segmentreplace(**kwargs)L185覆盖已存在的语法元素要求元素已注册否则抛ValueErrorinsert_lexer_matchers(lexer_patch, before)L370向已有 lexer 结构插入新规则sets(label)L96访问方言的关键词等集合随子方言复制。src/sqlfluff/dialects/AGENTS.md 中的继承层级示意如下可作阅读源码的索引ANSI (base) ← 所有方言均从此继承 ├── T-SQL (Microsoft SQL Server) ├── PostgreSQL │ └── Redshift (extends PostgreSQL) ├── MySQL │ └── MariaDB (extends MySQL) ├── BigQuery ├── Snowflake └── ... (20 种方言)Lexing何时需要动词法在语法解析之前SQL 必须先被lexed——切分为符号与逻辑分组。比如行内注释在 dialect_ansi.py#L101-L105 中是这样定义的RegexLexer( inline_comment, r(--|#)[^\n]*, CommentSegment, segment_kwargs{trim_start: (--, #)}, ),即从--或#开始直到换行的内容整体作为一个注释块处理因此无需在语法层再定义其内部结构甚至直接给它指定了解析段名CommentSegment。Lexing 按顺序进行从 SQL 开头读起取最长匹配吞掉归档为符号留给后续解析再对剩余文本重复该过程。因此SELECT * FROM table WHERE col1 12345不会被拆成S、E、L……而是SELECT、*、FROM、table等。对于简单的语法新增通常不需要碰 lexer 定义大多数常见情况已被覆盖但遇到更复杂的场景或 lexing 报错时就需要在这里补充规则。一个经典例子是 BigQuery 的参数化变量variable_nameANSI lexer 不认识。与其分别定义和变量名两个部分不如让 lexer 把整个表达式作为一个符号整体解析bigquery_dialect.insert_lexer_matchers( [ RegexLexer(atsign_literal, r[a-zA-Z_][\w]*, CodeSegment), ], beforeequals, )注意beforeequals它指定了该符号在 lexer 匹配顺序中的优先级。如果还定义了独立的at_sign规则用于匹配单独出现的那么应当让atsign_literal优先被尝试匹配失败再回退到at_sign。Keywords关键词的 RESERVED 与 UNRESERVED大多数方言都有自己的关键词文件dialect_name_keywords.py部分方言直接继承 ANSI 关键词后增删。关键词被划分为RESERVED保留与UNRESERVED非保留两类RESERVED 关键词受到额外限制不能作为标识符使用。如果在语法中使用了某个关键词如SELECT它就必须出现在某个 Keywords 列表中否则会看到类似这样的报错RuntimeError: Grammar refers to NanKeywordSegment which was not found in the redshift dialect该例来自 redshift 方言未把NAN注册为关键词的场景。此外如果修改了 ANSI 主方言并新增了关键词需要评估其他方言是否也应继承该语法——通常是的除非那些方言显式覆盖了它。从哪查找你的数据库的权威语法在动手写 Segment 之前最好的做法是先找到该方言语法的权威来源source of truth再映射为 SQLFluff 的 Segment/Grammar。临时拼凑语法虽能救急但对照权威来源才能穷举出该方言接受的全部语句形态。优先查看数据库引擎源码中的 parser 规范。许多数据库引擎用 Flex/Bison 之类的解析器生成器编写其 parser 规范是一条语句究竟如何被解析的终极答案——你可能会惊讶于某些文档盲区里引擎居然能解析的语法。再对照方言的官方参考文档获取语句语法的高层概览、限制与设计意图。若引擎闭源则往往只能依赖参考文档但它始终不如引擎内部真正用于代码生成的 bison 语法准确。同样重要的是把放进测试 fixture 的查询先交给真实引擎验证可解析性。测试语句不必是有效的可以引用不存在的表名但必须是可解析的。SQLFluff 不应该要求解析引擎本身会拒绝的语句——过度匹配的语法会在别处制造解析问题。各方言的语法资源速查ANSI SQLANSI 标准本身是收费的含语法的 Part 2 最有用。网上存在一些相关资源讨论标准各部分并链接旧版/草案版本的站点、可浏览的 BNF 语法视图、较早的 SQL:1999 标准副本以及可用于验证查询能否被 ANSI 标准解析的 SQL-2016 校验器。PostgreSQL参考最新版官方文档与 bison 语法src/backend/parser/gram.y及 Flex scannersrc/backend/parser/scan.l。验证可解析性的最直接方式是把语句粘贴进psql若得到ERROR: syntax error说明不可解析不要放入主测试 fixture若是其他错误如列名不存在说明解析成功最好让 SQLFluff 也能解析。另有pgsql-parser这类包装官方源码与 bison 语法的 CLI 工具可直接查看 PG 眼中的精确解析树。MySQL参考官方参考文档与 bison 语法sql/sql_yacc.yy。把语句粘贴进mysql出现ERROR 1064 (42000): You have an error in your SQL syntax即代表解析错误。实战案例四个真实方言修复以下四个案例来自 SQLFluff 的真实 issue 与 PR覆盖了读懂现有语法、小步修改、对照参考语法从零实现、追查深层语法链四种典型场景。开发环境搭建与 Git 流程不在本文范围参见 CONTRIBUTING.md 与 git.rst项目采用标准的 Fork PR 工作流。示例 1为 PostgreSQL 的CREATE FUNCTION增加SETOF返回类型issue #1520 报告 SQLFluff 无法解析如下语句CREATE OR REPLACE FUNCTION public.postgres_setof_test() RETURNS SETOF text报错为Found unparsable section: CREATE OR REPLACE FUNCTION crw_public.po...问题出在 postgres 方言的CreateFunctionStatementSegment。修复前的语法允许返回TABLE(...)或单个DatatypeSegment却没有SETOFmatch_grammar Sequence( CREATE, Sequence(OR, REPLACE, optionalTrue), Ref(TemporaryGrammar, optionalTrue), FUNCTION, Sequence(IF, NOT, EXISTS, optionalTrue), Ref(FunctionNameSegment), Ref(FunctionParameterListGrammar), Sequence( # Optional function return type RETURNS, OneOf( Sequence( TABLE, Bracketed( Delimited( OneOf( Ref(DatatypeSegment), Sequence( Ref(ParameterNameSegment), Ref(DatatypeSegment) ), ), delimiterRef(CommaSegment), ) ), optionalTrue, ), Ref(DatatypeSegment), ), optionalTrue, ), Ref(FunctionDefinitionGrammar), )修复方式是把SETOF datatype作为OneOf的另一个返回选项加入。这条修复最终并入主干当前 dialect_postgres.py#L1446-L1492 中CreateFunctionStatementSegment的RETURNS分支即为修复后的形态OneOf中包含TABLE、Sequence(SETOF, Ref(DatatypeSegment))与Ref(DatatypeSegment)三个选项。修改后上述语句即可正常解析并配以相应测试用例。示例 2让select 1 from group不再被FROM终结符误伤issue #1537 报告select 1 from group无法解析 parsing violations L: 1 | P: 10 | PRS | Line 1, Position 10: Found unparsable section: from L: 1 | P: 14 | PRS | Line 1, Position 14: Found unparsable section: groupsqlfluff parse给出的解析树显示from_clause下的FromClauseSegment预期落空。查看其定义发现终结符是这样的FromClauseTerminatorGrammar OneOf( WHERE, LIMIT, GROUP, ORDER, HAVING, QUALIFY, WINDOW, Ref(SetOperatorSegment), Ref(WithNoSchemaBindingClauseSegment), ),parser 一看到GROUP就认为FROM子句结束了——这过于激进。修复方式是让终结符只在完整词组出现时生效将GROUP与ORDER分别替换为Sequence(GROUP, BY)与Sequence(ORDER, BY)。当前 dialect_ansi.py#L542-L555 中的FromClauseTerminatorGrammar正是修复后的形态后续还增加了FETCH、OFFSET等终结符可对照阅读FromClauseTerminatorGrammar OneOf( WHERE, Ref(LimitClauseSegment), Sequence(GROUP, BY), Sequence(ORDER, BY), HAVING, QUALIFY, WINDOW, Ref(SetOperatorSegment), Ref(WithNoSchemaBindingClauseSegment), Ref(WithDataClauseSegment), FETCH, OFFSET, ),示例 3对照 ANSI BNF 与 PostgreSQL bison 语法从零实现CREATE CAST/DROP CASTPR #4744 为 ANSI 与 PostgreSQL 方言从零贡献了CREATE CAST/DROP CAST语句。贡献新语句的第一步是判断它是否属于 ANSI 标准如果是应当先在 ANSI 方言中加入一个厂商中立的通用版本供其他方言继承。虽然每个数据库引擎实际都会偏离 ANSI 标准但把合理通用的 Segment 放进 ANSI 方言通常对大多数方言都是合适的。本例中ANSI 标准确实定义了CREATE CAST其 BNF 形如user-defined cast definition :: CREATE CAST left paren source data type AS target data type right paren WITH cast function [ AS ASSIGNMENT ]据此在 dialect_ansi.py 中构建厂商中立的CreateCastStatementSegment当前实现见 L3884-L3915class CreateCastStatementSegment(BaseSegment): A CREATE CAST statement. type create_cast_statement match_grammar: Matchable Sequence( CREATE, CAST, Bracketed( Ref(DatatypeSegment), AS, Ref(DatatypeSegment), ), WITH, Ref.keyword(SPECIFIC, optionalTrue), OneOf( ROUTINE, FUNCTION, PROCEDURE, Sequence( OneOf(INSTANCE, STATIC, CONSTRUCTOR, optionalTrue), METHOD, ), ), Ref(FunctionNameSegment), Ref(FunctionParameterListGrammar, optionalTrue), Sequence(FOR, Ref(ObjectReferenceSegment), optionalTrue), Sequence(AS, ASSIGNMENT, optionalTrue), )写作时有几条经验值得沉淀复用已有 Segment数据类型、函数名、函数参数列表都已存在直接Ref即可既简化新语法又便于日后在其他方言中集中修改这些区域。识别共享结构当参考语法中的某个符号被多个其他符号/语句复用时就是应该抽成共享 Segment/Grammar 的强信号——这会为解析树增加结构方便 lint 规则分析。对照参考语法走查先按高层文档起草 Segment再拿 bison 语法系统性地逐条核对并检查有无遗漏。接着看 PostgreSQL。对照其文档发现与 ANSI 的显著差异只能指定FUNCTIONROUTINE、PROCEDURE会被拒绝不支持SPECIFIC更重要的是 PG 有非标准扩展如WITHOUT FUNCTION与AS IMPLICIT。这些差异最终体现在 dialect_postgres.py#L1264-L1294 的CreateCastStatementSegment覆盖实现中class CreateCastStatementSegment(ansi.CreateCastStatementSegment): A CREATE CAST statement. match_grammar: Matchable Sequence( CREATE, CAST, Bracketed( Ref(DatatypeSegment), AS, Ref(DatatypeSegment), ), OneOf( Sequence( WITH, FUNCTION, Ref(FunctionNameSegment), Ref(FunctionParameterListGrammar, optionalTrue), ), Sequence(WITHOUT, FUNCTION), Sequence(WITH, INOUT), ), OneOf( Sequence(AS, ASSIGNMENT, optionalTrue), Sequence(AS, IMPLICIT, optionalTrue), optionalTrue, ), )阅读 bison 语法的技巧bison 语法冗长但可快速定位在符号后加:搜索如CreateCastStmt:直达其定义从最高层开始找PostgreSQL 所有语句都以Stmt结尾逐层下钻如看到function_with_argtypes就搜function_with_argtypes:弄清其含义。bison 语法值得花几分钟细读文档里没有的关键词替身拼写可能真实存在且经过测试验证——PG 文档是人工维护而非自动生成的文档与实际可解析语法之间存在缝隙。留意 bison 的递归与 SQLFluff 的差异bison 语法高度递归它没有AnyOf、Delimited、Bracketed这类高层结构而 SQLFluff 对递归扩展并不友好。括号内表达式这种递归是合理且常见的但大量琐碎递归应当改写成 SQLFluff 的高层概念。例如 bison 里一个括号包裹的逗号分隔列表func_args/func_args_list相互递归在 SQLFluff 中直接用BracketedDelimited表达即可。示例 4用参考语法修复 PostgreSQL 数组切片issue #4336 报告 PostgreSQL 数组切片解析不正确可简化为测试用例SELECT a[2:23];从 bison 语法逐层下钻SelectStmt:→select_no_parens→simple_select→target_list→target_el发现核心是a_expr整个语法中广泛代表表达式的符号SQLFluff 以ExpressionSegment实现继续target_el→a_expr→c_expr→columnref得到关键规则ColId indirection再深入indirection/indirection_el终于找到数组访问器indirection_el: snip | [ a_expr ] | [ opt_slice_bound : opt_slice_bound ] opt_slice_bound: a_expr | /*EMPTY*/由此得到三条观察存在一串 indirection 元素数组下标可以是简单表达式最关键的是每个切片边界都是可选的且出现时是表达式。接着按同样自顶向下的方式在 SQLFluff 语法中导航postgres.SelectStatementSegment→ 基本是 ANSI 副本 →ansi.SelectStatementSegment记住Ref总是优先取方言自身语法再回退继承语法→postgres.SelectClauseSegment.match_grammar→ansi.SelectClauseElementSegment→ansi.BaseExpressionElementGrammar→ansi.ExpressionSegment→Expression_A_Grammar→Expression_C_Grammar→Expression_D_Grammar→ 序列末尾的postgres.AccessorGrammar→postgres.ArrayAccessorSegment——它正是 bison 中indirection_el的对应物。修复前的ArrayAccessorSegment当时的实现暴露出两个问题只接受数字字面量不接受表达式这正是 issue 的根因且一旦出现SliceSegment即:前后必须有数字字面量导致[:]PG 完全可以解析的合法 SQL无法通过。对照 bison 语法重写后当前 dialect_postgres.py#L1017-L1048 的实现已经同时支持单个元素访问[n]、切片[n:m]、[:m]、[n:]、[:]且下标可为QualifiedNumericLiteralSegment、NumericLiteralSegment或ExpressionSegmentclass ArrayAccessorSegment(ansi.ArrayAccessorSegment): Overwrites Array Accessor in ANSI to allow n many consecutive brackets. match_grammar Bracketed( OneOf( # These three are for a single element access: [n] Ref(QualifiedNumericLiteralSegment), Ref(NumericLiteralSegment), Ref(ExpressionSegment), # This is for slice access: [n:m], [:m], [n:], and [:] Sequence( OneOf( Ref(QualifiedNumericLiteralSegment), Ref(NumericLiteralSegment), Ref(ExpressionSegment), optionalTrue, ), Ref(SliceSegment), OneOf( Ref(QualifiedNumericLiteralSegment), Ref(NumericLiteralSegment), Ref(ExpressionSegment), optionalTrue, ), ), ), bracket_typesquare, )测试你的改动不止修好这么简单改完语法、确认原问题被修复之后还差两步才算完成跑测试套件确认没有破坏其他东西以及新增测试用例供他人日后回归。添加测试用例添加用例很简单在 test/fixtures/dialects 下对应方言目录中添加一个 SQL 文件可扩展已有用例文件也可新建。建议加入 issue 中的原始 SQL若官方语法文档带示例段落如 Snowflake 文档每个语法定义末尾都有示例一并拷入用例文件用参考语法穷举各种刁钻组合——不必可运行只要能被正确解析成正确结构、且能通过数据库引擎的解析阶段即可。参考文档的示例往往偏简单无法覆盖全部真实可能尽量构造一条用到尽可能多语法分支的语句务必先用真实引擎验证测试 SQL 可解析粘贴进控制台运行或用与引擎同源码的 CLI 解析工具如 pgsql-parser。报错没关系如列名无效只要不是语法解析错误即可。YML fixture 文件除 SQL 文件外还有对应的自动生成 YAML记录了 SQL 的解析结果。把 YAML 纳入源码的意义在于当有人重定义了语法、导致解析树变化时SQL 文本不变而 YAML 会变——通过审查随 PR 提交的 YAML 差异可以确认解析变化是符合预期的。除新增用例外不应出现无关 YAML 文件变化这也是很好的自查手段。新增或修改 fixture SQL 后用如下命令重新生成 YAMLtest/generate_parse_fixture_yml.py 是生成器的实现入口tox -e generate-fixture-yml也可以只生成某个方言、或只处理新增/变更文件速度更快tox -e generate-fixture-yml -- --dialect postgres tox -e generate-fixture-yml -- --new-only生成后执行git status查看差异。务必核对测试输出或 YAML 中的解析后结构检查每个查询元素类型是否正确——典型 bug 包括独立关键字如INTERVAL被解析成函数名、本应是date_part的元素被解析成identifier。通常无需手写断言但开发者有责任人工核查自动生成的 YAML 结构不能因为没有报解析错误就默认一切正常。运行测试套件完整tox会跑所有 Python 版本、带/不带 dbt 的全套测试非常耗时应留给 CI。本地只需运行有足够信心的部分。测试单个 fixturedialects_test参数化地自动收集 test/fixtures/dialects 下所有文件。例如修改了dialects/hive/select_interval.sqltox -e py310 -- -s test/dialects/dialects_test.py -k hive-select_interval.sql-s让 pytest 打印解析后结构便于快速检查各元素类型与生成的 fixture YAML 内容一致。已激活项目虚拟环境时可直接调用 pytest 提速pytest -s test/dialects/dialects_test.py -k hive-select_interval.sql运行全部方言测试tox -e py310 -- test/dialects/dialects_test.py只跑某个方言tox -e py310 -- test/dialects/dialects_test.py -k ansi若方言改动是为了修复某条规则误报可只跑该规则测试例如 LT01tox -e py310 -- -k LT01 test提交前的最终检查格式与 lint 通常依赖 pre-commit 钩子即可配置见 CONTRIBUTING.md提交 PR 前建议跑一遍单 Python 版本、不带 dbt 的全量测试约 10 分钟tox -e py311若还要覆盖与 lint可运行tox -e generate-fixture-yml,cov-init,py311,cov-report,linting耗时更长。注意覆盖率测试本地运行需多个版本含 Windows 与 dbt本地可能出现缺失覆盖的报告属正常现象其余交给 CI。注意首次贡献者需要维护者先触发一次测试首个 PR 合并后才自动运行代码风格上项目使用ruff与black由 pre-commit 自动执行也可手动运行多数简单错误用black格式化即可修正个别问题需ruff查看后手动处理。提交你的变更项目采用标准 GitHub 工作流fork 仓库 → 本地 clone → 修改 → push 到自己的 fork → 向原仓库开 PRGit 操作细节见 git.rst。PR 打开后 CI 会在 5-10 分钟内跑完全部绿灯后维护者会尽快接手。小、易理解、测试全绿的 PR 更容易被快速合并。如有疑问可以在仓库中开 issue或加入社区 Slack 向维护者/社区快速提问入口见 jointhecommunity.rst。总结一次方言贡献的完整闭环是——先用真实引擎/参考语法确认目标语句应可解析再在 src/sqlfluff/dialects 中找到对应的 Segment/Grammar 做最小修改优先复用与继承、善用OneOf/Sequence/Delimited/Bracketed与optionalTrue接着把 SQL 加入 test/fixtures/dialects 的对应目录、用tox -e generate-fixture-yml生成 YAML 并人工核对解析树结构最后运行针对性测试并提交 PR。这条路径不仅能让 SQLFluff 更快支持你日常使用的语法也是深入理解解析器设计的最佳实践课堂。【免费下载链接】sqlfluffA modular SQL linter and auto-formatter with support for multiple dialects and templated code.项目地址: https://gitcode.com/GitHub_Trending/sq/sqlfluff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →