Skip to content

Coding and testing guidelines for MySQL contributors

Henrik Ingo edited this page Nov 4, 2026 · 4 revisions

Contributing to MySQL | mysql-server CONTRIBUTING.md


When you contribute to MySQL Server, include code that other developers can maintain and tests that demonstrate the intended behavior. This guide explains how to follow the coding style, choose the right tests, and prepare a change for review. It covers both bug fixes and new functionality. For a bug-fix contribution, first file or identify the corresponding report at bugs.mysql.com and reference it in the pull request. A bug report is required before a bug-fix contribution can be accepted. See Contributing to MySQL for the current reporting and submission procedure, accounts, and the Oracle Contribution Agreement.

If a crash or other failure may be security-sensitive, submit a private report at bugs.mysql.com and keep reproduction details and further discussion in that report. Do not post a potentially sensitive reproducer in a public issue or pull request. For additional reporting options, see Oracle's vulnerability reporting guidance.

For a feature or a significant design change, discuss the proposal early and follow the community's design and implementation process.

Keep the change focused

Start with a clear description of the problem and the behavior you want to deliver. For a bug fix, identify the cause and explain how the change corrects it. For a feature, describe its intended use and the requirements it satisfies.

Keep the implementation, its tests, and any necessary documentation together. Avoid unrelated refactoring or formatting changes: they make the patch harder to review and can obscure the fix. If a refactoring is needed, explain its relationship to the change.

Before adding a helper or abstraction, look for an existing one in the affected module. Follow established interfaces for memory allocation, diagnostics, locking, and resource cleanup. A change that works in one execution path must also respect the surrounding code's ownership and lifetime rules. Inspect related callers and implementations for the same assumption or defect. Choose relevant boundary cases and positive controls that show previously working behavior still works, such as non-NULL values alongside NULL cases or other implementations of a collation operation. For optimizer changes, correct results through a fallback do not establish that an existing supported optimization remains available; check that property separately when relevant.

Follow the coding style

The MySQL Coding Guidelines use the Google C++ Style Guide for new code, with MySQL member prefixes: m_ for nonstatic members and s_ for static members.

Older code retains MySQL naming conventions, including class names such as Query_arena and functions in snake_case. Follow the conventions of the module you are modifying; avoid renaming existing interfaces merely to change their style. Use /** ... */ for Doxygen comments and @ for Doxygen commands.

Avoid adding the legacy st_ struct names and uppercase typedef aliases in C++. Public headers intended for C callers must remain compatible with C. Use the repository's .clang-format configuration and the formatter version specified by the branch you are working on. Apply the repository’s formatting rules to the C++ lines changed by your contribution. Avoid reformatting unrelated code.

If LLVM’s git-clang-format helper is installed, you can use it to format changed lines. Otherwise, use your editor’s formatting support or clang-format on selected line ranges. Use the formatter version required by the branch and inspect the diff for unrelated changes.

Review the diff after formatting and remove unrelated formatting changes. Formatting tools do not check naming, ownership, interfaces, or the clarity of the implementation.

Write code that explains its intent

Choose names that describe the concepts in the affected module. Keep functions cohesive, and make resource ownership and error handling explicit. Check error paths as carefully as the successful path, including partial initialization and cleanup after failure.

Comments should explain assumptions, invariants, or reasons that are difficult to infer from the code. Document what callers need to know: what the function accepts, what it returns, and who owns any returned resource. Keep comments and documentation consistent with the implementation.

For interfaces that accept user input, validate the input before using it. Consider boundary values, size calculations, privileges, and resource limits where they affect the change.

Choose tests that demonstrate the behavior

MySQL has tests at several levels. Choose coverage according to what your change affects.

What you need to verify Test approach
SQL results, errors, server behavior, or interaction between server components MySQL Test Run (MTR) tests under mysql-test/
A C++ function, algorithm, or class that can be exercised directly Google Test unit tests, commonly under unittest/gunit/
A performance improvement A reproducible benchmark, with functional regression coverage where applicable

MySQL Test Run is commonly called MTR. Its driver, mysql-test-run.pl, starts test servers and runs test scripts through `mysqltest. See The MySQL Test Framework for the complete reference.

A unit test can exercise an internal corner case precisely, while an MTR test can verify that the behavior is correct through SQL. Add both when they cover different failure modes. Add new cases to an existing test file for the affected functionality whenever possible. For example, a decimal-type regression would normally belong in an existing decimal test. Create a new test file when no existing file is a suitable home for the coverage.

Make regression tests detect the original problem

For a bug fix, reduce the reproduction to the smallest case that still reaches the cause of the failure. Preserve the properties that trigger it, such as NULL values, a particular index, transaction state, or a sequence of statements.

Check that the test fails with the implementation change absent and passes with it present. Inspect the failure: a syntax error, an unavailable feature, or an unrelated result difference does not demonstrate the original bug. If the public baseline cannot reproduce the reported failure, explain that in the pull request and describe what the test verifies.

For a fix with logically distinct changes, identify which test case protects each part and check that it reaches the intended execution path. A test may fail without the patch even if it covers only one part of the fix. Check that each logically distinct change has a test that would fail if that change were missing. When that coverage is unclear, selectively reverting one change or adding a focused unit test can help demonstrate it; mutation testing is not required for every contribution.

Assert the intended result or error whenever possible. For a crash fix, also check the result of the operation if it has one. For a feature, cover the normal case and the boundary or invalid-input cases that matter to its requirements.

Keep a small set of purpose-labelled cases that exercise distinct behavior. Briefly explain what each variant adds. Avoid repeated Boolean rewrites or identical query executions unless the repetition itself triggers the bug. For prepared statements, vary parameter values or alternate empty and nonempty results when that exercises state changes relevant to the fix.

Write an MTR test

An MTR test normally has two files:

mysql-test/t/example.test
mysql-test/r/example.result

Component suites use the corresponding directories under mysql-test/suite/<suite>/. Choose a descriptive lowercase test name and look at nearby tests for the suite's conventions. The .test file contains SQL and mysqltest commands. The .result file contains the expected output, which the test runner compares with the actual output.

The file organization guidelines explain test placement and shared include files.

MTR checklist

  • Add related cases to an existing test file and update its result file.
  • Use the current test database whenever possible.
  • Rely on the test environment's default settings; set and restore values only when the case requires a change.
  • Keep identifiers short and simple, and format SQL within 80 columns where possible.
  • Start each bug-regression section with the bug number and bug title, then explain what its cases check.
  • Run ANALYZE TABLE after inserting data when using EXPLAIN or depending on a particular query plan.
  • Keep cases focused, remove test objects, and restore settings you changed.
  • Run each modified test with both optimizers (see the example below). Keep result checks active for both and limit necessary skips to optimizer-specific plan checks.

A small example

The following example checks that COUNT(DISTINCT ...) counts distinct nonnull values. It illustrates setup, result checking, and cleanup; a regression test for a real fix should use the statements that trigger that bug.

Add a section to the existing mysql-test/t/count_distinct.test and update mysql-test/r/count_distinct.result. Insert it where the required default settings are in effect and its table names do not conflict with surrounding sections. Replace the placeholder bug number and title below with the actual report when adding a regression case:

# Bug#<number> <bug title>
# Verify that COUNT(DISTINCT) ignores NULL and duplicate values.

CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1), (1), (2), (NULL);

SELECT COUNT(DISTINCT a) AS n FROM t1;

DROP TABLE t1;

The query should return 2. That expectation comes from the behavior being tested and should be checked before accepting any recorded output.

From the mysql-test directory of a configured and built tree, record the result and then run the test normally:

./mysql-test-run.pl --record main.count_distinct
./mysql-test-run.pl main.count_distinct
./mysql-test-run.pl --hypergraph main.count_distinct

For a test in a component suite, use its qualified name, such as innodb.example. If you built MySQL in a separate directory, run MTR from <build-directory>/mysql-test. See the branch's build instructions to set up the build first.

Review the diff in the existing .result file and include it with the modified .test file in your contribution. Recording captures what the server did; it does not prove that the output is correct. Inspect every result change and confirm that it follows from the intended behavior. Review every result file changed by the test update.

The test quick start provides more details about creating and recording tests.

Keep tests readable

Use spaces and newline line endings. Write SQL keywords in uppercase and object names in lowercase, unless lettercase is the behavior under test. Use lowercase mysqltest commands. Use # for comments; lines starting with -- can be test commands.

Keep SQL statements on one line when they fit within 80 characters. For longer queries, put clauses on separate lines and align the SELECT, FROM, WHERE, GROUP BY, HAVING, WINDOW, and ORDER BY keywords. Keep each clause on one line where possible; wrap long expressions with consistent indentation. For example:

SELECT a, SUM(b) AS total
FROM t1
WHERE b > 0
GROUP BY a
HAVING SUM(b) > 10
ORDER BY a;

Keep table, column, variable, and prepared-statement names short and simple. Names such as t1, a, and stmt are usually sufficient for a small test.

Use more descriptive names only when they help the reader distinguish roles.

Describe what the cases check. Explain unusual setup and why it is necessary. Use --echo when a label would make the recorded output easier to understand.

See the test formatting guidelines.

Make tests independent and repeatable

Create tables in the current test database whenever possible. Create a separate database only when the behavior being tested requires it. Create the objects and data the test needs, and remove them at the end. Do not depend on another test having run first.

Assume the test environment's default settings for the optimizer switch, SQL mode, character set, and collation. There is no need to set these explicitly for an ordinary test. Set a nondefault value only when it is necessary to exercise the behavior under test, and explain that dependency. When adding a case to an existing file, check that earlier sections restore any settings they change.

Restore changed global settings to their original values, and restore session settings before unrelated test sections. Clean up connections and files that the test creates.

Keep the data set small enough for routine test runs. Use feature checks such as the appropriate include/have_*.inc file when a test requires a particular engine or build capability. A skip should represent an unavailable prerequisite; it should not hide a failure in supported behavior.

If the order of returned rows matters, specify ORDER BY. If order is irrelevant, --sorted_result can normalize output without changing the query. Avoid dependencies on wall-clock time, random data, machine paths, or exact timing. For concurrent tests, use synchronization facilities to establish the required ordering instead of relying on fixed sleeps. See Thread Synchronization in Test Cases and Other Tips for Writing Test Cases.

Normalize only output that is unrelated to the behavior being checked. Broad replacements or disabled result logging can make a test pass while hiding the regression it was intended to detect.

Check errors explicitly

Place an expected error immediately before the statement that should fail. Use a symbolic error name where available:

CREATE TABLE t1 (a INT PRIMARY KEY);
INSERT INTO t1 VALUES (1);

--error ER_DUP_ENTRY
INSERT INTO t1 VALUES (1);

SELECT a FROM t1;
DROP TABLE t1;

This checks both rejection of the duplicate and the table's state afterward. See Checking for Expected Errors for the command's syntax and behavior.

Test query plans deliberately

For optimizer changes, decide whether you need to check query results, plan structure, estimates, or a combination. Prefer a targeted check of the property that would regress. Avoid requiring an entire plan to remain unchanged when only one part matters.

Keep query-result checks enabled for both optimizers. If an access path is unavailable under one optimizer, selectively skip only the corresponding plan assertion. Do not skip the whole regression section merely because its plan differs. Explain any optimizer-specific skips and any hints needed to exercise the intended path.

Always run ANALYZE TABLE after inserting data if the test uses EXPLAIN or depends on a specific query plan being chosen. Analyze each populated table involved before running the statements that check the plan. If you insert more data later, analyze the affected tables again before the next plan check. Cost estimates and execution times can vary. Where the branch provides include/elide_costs.inc, use its replacements to remove irrelevant values from EXPLAIN FORMAT=TREE or EXPLAIN ANALYZE output. For example:

--source include/elide_costs.inc

CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (1), (2), (3);
ANALYZE TABLE t1;

--replace_regex $elide_costs_and_time
EXPLAIN FORMAT=TREE SELECT a FROM t1 WHERE a = 1;

DROP TABLE t1;

Keep row estimates visible when they are the property under test. If the reproduction requires particular statistics beyond ANALYZE TABLE, establish those explicitly as well. Check that the case remains stable across runs.

A timing comparison belongs in a benchmark. Describe the data, configuration, baseline, and repeated measurements so that reviewers can assess the result.

Write and run C++ unit tests

If related unit tests already share a test class with suitable setup and cleanup, add your test to that class. Test the observable contract: inputs, outputs, state changes, and error handling. Include boundary values relevant to the implementation. Avoid assertions that merely duplicate its internal steps.

When adding a file, follow the module's naming conventions and register it in the appropriate CMake test target. A file added to the source tree is not necessarily built or run automatically. MySQL may combine several test files into one executable, so check which target contains your test.

Build that target and run the relevant tests. Google Test supports listing and filtering tests; substitute the executable and fixture names from your build:

path/to/test_executable --gtest_list_tests
path/to/test_executable --gtest_filter='FixtureName.*'

Run the surrounding tests after the focused case passes. The MySQL Google Test documentation describes the test framework and where to find examples.

Prepare the contribution for review

Build and test the final version of the patch. A debug build is useful for catching assertions. Also validate a release build when the bug or change depends on behavior that differs between debug and release configurations. Use sanitizer builds when investigating memory errors or undefined behavior.

Run each modified MTR test with both the traditional optimizer and the hypergraph optimizer. Run MTR normally for the traditional optimizer and add --hypergraph for the hypergraph optimizer, as shown above. Check that the build supports hypergraph and that both runs execute the intended cases. A skipped test does not establish coverage. Report unavailable coverage explicitly.

Start with the regression test, then run related tests in the affected area. This does not require running the entire suite twice. Rerun affected checks after review changes. If a broader run fails, investigate the failure and report its relationship to the patch.

In the pull request, provide:

  • The problem, expected behavior, and public issue or bug link.
  • A concise explanation of the implementation and significant design choices.
  • The tests added or changed, including how they detect the original failure.
  • The build configuration, commands run, and their results.
  • Any relevant coverage limits or remaining failures.

Before submitting, review the diff for unintended changes, confirm that new tests are registered and executed, and check that the recorded results match the intended behavior. Include documentation changes when the patch changes an interface or behavior that users need to understand.

If you are unsure where a test belongs or which coverage is appropriate, ask in the pull request or MySQL Community Discussions. Include a small reproduction and explain what you want to verify so that maintainers can give concrete guidance.

Clone this wiki locally