Dedication Maintain responsibility and reverence, continuously crafting with artisanal spirit.
Readability Code and names must express their intent clearly and unambiguously so they can be understood by reading rather than debugging.
Cleanliness Embrace the concepts from “Refactoring” and “Clean Code”, pursuing clean and elegant code.
Consistency Maintain complete consistency in code style, naming, and usage patterns.
Simplicity Minimalist code, expressing the most correct meaning with the least code. Highly reusable, with no duplicate code or configuration. Delete unused code promptly.
Abstraction Clear hierarchy division and reasonable concept extraction. Keep methods, classes, packages, and modules at the same abstraction level.
Excellence Reject randomness, ensuring every line of code, every letter, and every space has its existential value.
Code Submission Guidelines
Ensure all steps in the build process complete successfully, including: Apache license header check, Checkstyle check, compilation, unit tests, etc. Build process command: ./mvnw clean install -B -T1C -Pcheck.
Unify code style through Spotless, execute ./mvnw spotless:apply -Pcheck to format code.
Ensure coverage is not lower than the master branch, except for simple getter /setter methods, unit tests need full coverage.
Keep each commit small, complete, and independently verifiable. Split a change into multiple commits when it contains independent objectives.
If you use IDEA, you can import src/resources/idea/code-style.xml to maintain code style consistency.
If you use IDEA, you can import src/resources/idea/inspections.xml to detect potential code issues.
Coding Standards
No line breaks are needed if each line of code does not exceed 200 characters.
There should be no meaningless blank lines. Please extract private methods instead of using blank line spacing for overly long method bodies or logically closed code segments.
Naming conventions:
Class and method names should avoid abbreviations, some variable names can use abbreviations.
Variable name arguments abbreviated as args;
Variable name parameters abbreviated as params;
Variable name environment abbreviated as env;
Variable name properties abbreviated as props;
Variable name configuration abbreviated as config.
Proper noun abbreviations of three characters or less use uppercase, abbreviations over three characters use camelCase.
Examples of class and method name abbreviations with three characters or less: SQL92Lexer, XMLTransfer, MySQLAdminExecutorCreator;
Examples of class and method name abbreviations over three characters: JdbcUrlAppender, YamlAgentConfigurationSwapper;
Variables should use lowercase camelCase: mysqlAuthenticationMethod, sqlStatement, mysqlConfig.
Local variables meeting the following conditions should be named according to these rules:
Except for directly returning method parameters, return variables should be named result;
Use each to name loop variables in loops;
Use entry instead of each in maps;
Captured exception names should be named ex;
When capturing exceptions and doing nothing, the exception name should be named ignored.
Method parameter names are forbidden from using result, each, entry.
Utility class names should be named xxUtils.
Configuration files use Spinal Case naming (a special Snake Case that uses - to separate words).
Extract code that requires explanatory comments into small methods, and use method names to express the intent.
In equals and == conditional expressions, constants on the left, variables on the right; in conditional expressions like greater than or less than, variables on the left, constants on the right.
Avoid using this modifier except for assignment statements where constructor parameters have the same name as global variables.
Local variables must not be declared as final, including ordinary local variables, for loop variables, enhanced for loop variables and try-with-resources resources.
Lambda parameters should not be marked as final.
Declare every class final unless it is an abstract class intended for inheritance.
Extract nested loops into separate methods.
The order of member variable definitions and parameter passing should remain consistent across all classes and methods.
Use guard clauses for invalid inputs, missing states and exceptional conditions so that the normal execution path uses positive conditions and minimal nesting.
Access control for classes and methods should be minimal.
Private methods used by a method should immediately follow that method. If there are multiple private methods, they should be written in the same order as they appear in the original method.
Method parameters and return values must not be null by default.
Allow null only when an existing API, SPI or framework contract explicitly uses it to represent absence, and document its meaning with @Nullable or JAVADOC.
Method parameters must not use Optional.
Use Lombok for boilerplate constructors, getters, setters and log variables only when the generated signature, visibility and behavior match the manual implementation.
Keep a manual implementation when it contains validation, business logic, documentation, compatibility or framework semantics.
When the expected number of elements is known before creating a mutable collection, set a sufficient initial capacity with a capacity argument or a constructor that accepts an existing collection.
Use a ternary operator when each if/else branch contains only a return statement or assigns the same variable; otherwise, use if/else.
Use @HighFrequencyInvocation to mark high-frequency production code whose performance behavior requires focused review.
Code is high-frequency in any of the following cases:
It runs repeatedly for every SQL request.
It runs repeatedly for every Pipeline data unit, including a record, event, packet or batch. Code that continuously processes those data units in an internal loop remains high-frequency even if its method is invoked only once or its executor is started only once.
Annotate a class, method or constructor at the smallest accurate scope that covers the high-frequency behavior.
On a class, the rules apply to the implementations of all methods and constructors in that class.
On a method or constructor, the rules apply to that implementation and the same-class private methods it calls.
Set canBeCached = true only when the annotated target is a cacheable resource intended for reuse.
Within the high-frequency scope, do not perform expensive operations that can be precomputed, cached, reused or moved out of the high-frequency path. Retain an expensive operation only when its result depends on the current SQL request or Pipeline data and it cannot be moved without changing correctness or lifecycle. Expensive operations include repeated I/O, blocking waits, reflection, parsing, serialization, full scans, and creation of large objects or many objects.
Within the high-frequency scope:
Do not use the Java Stream API;
Do not concatenate strings with +;
Do not call LinkedList#get(int).
Comments & Logging standards:
Logs and comments must be in English.
Comments can only contain JAVADOC, TODO and FIXME.
Public classes and methods must have JAVADOC. JAVADOC for user-facing APIs and SPIs needs to be clear and comprehensive. Other classes, methods, and methods overriding parent classes do not need JAVADOC.
Constructor JAVADOC must not be added by default. It is allowed only when it documents non-obvious behavior, compatibility constraints, side effects, or public API semantics not expressed by the class contract.
Unit Testing Standards
Test code and production code need to follow the same coding standards.
Unit tests need to follow the AIR (Automatic, Independent, Repeatable) design philosophy.
Automatic: Unit tests should be fully automated, not interactive. Manual inspection of output results is forbidden, use of System.out, log, etc. is not allowed, assertions must be used for verification.
Independent: Forbid mutual calls between unit test cases, forbid dependency on execution order. Each unit test can run independently.
Repeatable: Unit tests cannot be affected by the external environment and can be executed repeatedly.
Unit tests need to follow the BCDE (Border, Correct, Design, Error) design principles.
Border testing: Get expected results through boundary inputs such as loop boundaries, special values, data order, etc.
Correctness testing: Get expected results through correct inputs.
Reasonable design: Combined with production code design, design high-quality unit tests.
Error tolerance testing: Get expected results through incorrect inputs such as illegal data, exception flows, etc.
Unit tests must exercise behavior through public APIs only. Reflection-based invocation of private members is forbidden. If tests must access fields via reflection, use Plugins.getMemberAccessor() and limit reflection to Field access only.
Tests that modify static state must restore the original state after each test.
Obtain SPI implementations through the project loader by default. If the class under test implements TypedSPI or DatabaseTypedSPI, instantiate it through TypedSPILoader or DatabaseTypedSPILoader, not with new.
Every unit-test class must directly test a corresponding production class and be named <ProductionClassName>Test, using the exact simple name of the production class. This class-name rule is mandatory and is independent of scenario-focused test-method naming.
When a production method is covered by only one test case, name that test method assert<MethodName> without extra suffixes, and prefer isolating one public production method per dedicated test method; when practical, keep test method ordering aligned with the corresponding production methods.
For parameterized tests, provide display names via parameters and use "{0}" as the display-name template.
Keep test names concise and scenario-focused; avoid ReturnsXXX and wording that restates the expected result instead of naming the scenario.
Assertions must directly express the tested contract. Use not or containsString only when the contract requires inequality or substring matching; do not use them when an exact value or a more specific matcher is available.
Default to direct Mockito mocks. Use a private helper only for repeated local setup and a standalone fixture only for a stable external or packaged test boundary. Give fixtures the narrowest practical visibility, keep them in the nearest owning test package or module, and do not create cross-module test APIs for convenience. Delete or inline thin mock wrappers.
Data assertion standards should follow:
Boolean type assertions should use assertTrue and assertFalse;
Null value assertions should use assertNull and assertNotNull;
Non-boolean, non-null value equality assertions must use assertThat(actual, is(expected));
Type assertions must use assertThat(actual, isA(ExpectedType.class));
Reference identity assertions must use assertThat(actual, sameInstance(expected));
Reference non-identity assertions must use assertThat(actual, not(sameInstance(expected)));
The actual values in test cases should be named actual XXX, and expected values should be named expected XXX.
Using mock should follow the following specifications:
Mock databases, caches, registries, network calls, time, and other heavy external dependencies instead of connecting to external environments.
Mock objects with more than two levels of nesting when they are unrelated to the behavior under test; do not construct deep unrelated object graphs.
Prefer AutoMockExtension and its static or construction mocking support. Use direct mockStatic or mockConstruction only when the extension cannot apply and the reason is recorded; scope it with try-with-resources. When a class is listed in @StaticMockSettings, do not call mockStatic or mockConstruction for it; stub it through when(...).
Do not mix Mockito matchers with raw arguments in one invocation.
When verifying only one call, there’s no need to use times(1) parameter, the single-parameter method of verify is sufficient.
Do not stub methods or verify interactions that do not affect the behavior or result being tested. Omit stubbing when Mockito’s default return value is sufficient.
For deep chained interactions, use Mockito’s RETURNS_DEEP_STUBS instead of layering intermediate mocks.
Test data should use standardized prefixes (e.g., foo_/bar_) to clearly identify their test purpose
Use PropertiesBuilder simplify Properties building.
SQL Parsing Standards
Maintenance Standards
The G4 grammar files and SQLVisitor implementation classes involved in the SQL parsing module need to be marked with differential code according to the following database relationships. When database A does not provide corresponding database drivers and protocols, but directly uses database B’s drivers and protocols, database A can be considered a branch database of database B.
Usually branch databases will directly use the SQL parsing logic of the trunk database, but to adapt to the unique syntax of branch databases, some branch databases will copy from the trunk database and maintain their own SQL parsing logic. At this time, for the unique syntax of branch databases, comments need to be used for marking, and other parts need to be consistent with the implementation of the trunk database;
Trunk Database
Branch Database
MySQL
MariaDB, Doris
PostgreSQL
-
openGauss
-
Oracle
-
SQLServer
-
ClickHouse
-
Hive
-
Presto
-
SQL92
-
Differential code marking syntax, replace {DatabaseType} with the database type uppercase name when adding, for example: DORIS.
Add syntax: // {DatabaseType} ADDED BEGIN and // {DatabaseType} ADDED END;
Modify syntax: // {DatabaseType} CHANGED BEGIN and // {DatabaseType} CHANGED END.
G4 Standards
Lexical parsing specifications
Each rule on one line, no blank lines needed between rules.
Rule names use uppercase letters. If the name consists of multiple words, use underscore separation. DataType and Symbol rule names end with underscore. Rules with the same name as ANTLR built-in variables or keywords add underscore at the end for distinction.
Rules not exposed externally use fragment, fragment defined rules need to be declared after the rules they serve.
Common rule definitions are placed in Keyword.g4, each database can have its own specific rule definitions. For example: MySQLKeyword.g4.
Syntax parsing specifications
Leave a blank line after each rule, blank lines do not need indentation.
No space before the rule name, space after colon before starting to write the rule, semicolon on a separate line and maintain the same indentation as the previous line.
If a rule has more than 5 branches, each branch should be on a separate line.
Rule naming uses Java variable camelCase form.
Define an independent grammar file for each SQL statement type, file name consists of database name + statement type name + Statement. For example: MySQLDQLStatement.g4.
GitHub Action Standards
Workflow file names end with .yml.
Workflow file names consist of lowercase letters of trigger method-execution operation. For example: nightly-check.yml. pull_request triggered tasks omit the trigger method, for example: check.yml.
Trigger methods include: pull_request (no prefix), nightly, schedule.
The name attribute naming in Workflow files should be consistent with the file name, words separated by - with spaces on both sides of the separator, and the first letter of each word capitalized. For example: Nightly - Check.
The name attribute under Step should describe the function of the step, with the first letter of each word capitalized and prepositions in lowercase. For example: Build Project with Maven.
The job attribute naming in Workflow must be unique within the Workflow.
When using matrix, you must add job parallelism limit of 20. For example: max-parallel: 20.
Must set timeout for jobs, maximum not exceeding 1 hour. For example: timeout-minutes: 10.