Dolt SQL Server MariaDB Client Support

INTEGRATION
5 min read

Imagine connecting to your database with the same MariaDB client you've always used, but suddenly having access to Git-style version control. Every query you run against a database where you can branch, merge, diff, and rollback changes with the same confidence you have when working with source code.

Dolt is a SQL database with Git-style versioning. Dolt ships with a MySQL-compatible server that you can start on a repository using dolt sql-server. Once started, you can then connect to the running server using standard MySQL clients.

MariaDB is an open-source relational database that started as a fork of MySQL. It maintains wire protocol compatibility with MySQL while adding its own enhancements and features. Because of this compatibility, MariaDB clients work with MySQL-compatible databases like Dolt. We've even explored integration patterns like MariaDB to Dolt replication for change data capture scenarios.

So, we've had basic compatibility with MariaDB clients for a while, but we're excited to announce official support for MariaDB clients, now tested through our continuous integration (CI) pipeline. This expansion builds on our existing MySQL client support, which we first introduced in our 2020 blog post on MySQL Client Tests.

Why Official MariaDB Support?

While Dolt aims for broad MySQL compatibility, MariaDB clients often have their own unique behaviors and specific connectors. Rather than hoping compatibility would "just work," we've integrated dedicated tests for these clients into our CI. This means that every pull request to Dolt now verifies compatibility with Python, Node.js, Java, C, C++, Perl, Ruby, Elixir, Swift, Go, and R MariaDB connectors, preventing regressions. You can find more information about our tests for each client in our documentation.

Multi-stage, Multi-stage, Multi-stage!!

Our client integration tests use a multi-stage Docker build to create test environments for each language and connector combination. This design isolates language-specific dependencies while allowing parallel builds across different environments.

The architecture centers around four key stages: building Dolt itself, preparing language-specific client binaries, assembling the final runtime image, and orchestrating the tests. Consider how we handle Python MariaDB clients:

FROM python:3.14-slim-bookworm AS python_clients_build
RUN apt-get update && apt-get install -y binutils libmariadb-dev gcc && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir mysql-connector-python==8.0.33 PyMySQL==1.0.2 sqlalchemy==1.4.46 mariadb pyinstaller
COPY dolt/integration-tests/mysql-client-tests/python/ /build/python/
WORKDIR /build/python/
RUN pyinstaller --onefile mariadb-connector-test.py

This stage installs the MariaDB development headers, the MariaDB Python connector itself, and packages everything into a single executable, which will eventually be copied into our minimal runtime (final) image. You can see the complete multi-stage build configuration in our Dockerfile. This continues with our other stages, i.e., our C and C++ stages:

FROM debian:bookworm-slim AS c_clients_build
RUN apt-get update && apt-get install -y default-libmysqlclient-dev libmariadb-dev unixodbc-dev odbcinst wget gcc make && rm -rf /var/lib/apt/lists/*
RUN wget --progress=dot:giga https://dlm.mariadb.com/4465891/Connectors/odbc/connector-odbc-3.2.7/mariadb-connector-odbc_3.2.7-1+maria~bookworm_amd64.deb \
    && dpkg -i mariadb-connector-odbc_3.2.7-1+maria~bookworm_amd64.deb || apt-get install -y -f

Here we continue installing our MySQL and MariaDB-specific connectors including the ODBC driver. We've based most of our required clients on MariaDB's official list of connectors.

Performance Improvements?

Docker caches each stage independently, so changing a Python test doesn't force a rebuild of the Java or C++ environments. Different language stages build in parallel on multi-core systems which counts on GitHub Actions too. Of course, there's is a tradeoff ... higher storage usage when building. This put me in a pickle for a bit, but after some tinkering I found running a couple of background rm calls on unused packages cleared up enough space to keep things going. There's also the addition of a global cache with our new workflow, so those dependency downloads per language are saved, and that includes our Dolt build.

      - name: Free disk space
        run: |
          NAME="DISK-CLEANUP"
          echo "[${NAME}] Starting background cleanup..."
          [ -d "$AGENT_TOOLSDIRECTORY" ] && sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
          [ -d /usr/share/dotnet ] && sudo rm -rf /usr/share/dotnet &
          [ -d /usr/local/lib/android ] && sudo rm -rf /usr/local/lib/android &
          [ -d /opt/ghc ] && sudo rm -rf /opt/ghc &
          [ -d /usr/local/share/boost ] && sudo rm -rf /usr/local/share/boost &

# ...

      - name: Cache Docker layers
        uses: actions/cache@v3
        with:
          path: /mnt/.buildx-cache
          key: ${{ runner.os }}-docker-mysql-client-integrations
          restore-keys: |
            ${{ runner.os }}-docker

Generally, removing these preinstalled applications is faster than building, so we let them run in the background and continue our steps right away. Alternatively, if loading from cache, our final image is small enough to fit in the default free space.

Aside from performance, I think build stages also help with separation of concerns for each language implementation. The downside is you do need to be aware of the dynamic links per executable. Luckily, ldd is pretty useful for viewing these object dependencies. I use Docker's --target option to build a specific stage and check the executable's shared libraries after building.

docker build --target <stage_name> -t <image_name>:<tag> .

BATS Testing Framework Integration

The testing process at runtime uses Bash Automated Testing System (BATS), a framework we've relied on extensively for testing Dolt's command-line interface. BATS allows us to define executable test cases that ensure each client can connect. A test follows a consistent pattern: start a Dolt SQL server with a randomized port and database name, execute a language-specific test script using a MariaDB client, verify standard SQL operations work correctly, and test Dolt-specific version control functions like dolt_add() and dolt_commit().

Since building now happens within the image only, all test structures have been reduced to one interface/one-liner:

@test "python mariadb connector" {
  /build/bin/python/mariadb-connector-test $USER $PORT $REPO_NAME
}

@test "swift perfect-mariadb client" {
  /build/bin/swift/mariadb-swift-test $USER $PORT $REPO_NAME
}

You can view the complete test suite in our BATS test file.

Conclusion

This should demonstrate our commitment to making Dolt work with the tools developers already use. Whether you're connecting from Python with the MariaDB connector, building a Node.js application, or integrating Dolt into existing Java infrastructure, you now have the confidence that these connections are tested with every change we make.

Ready to try Dolt with your MariaDB client? Download Dolt and connect using your existing MariaDB connector. If you encounter any issues or have suggestions for other clients you'd like to see officially supported, join us on Discord or submit a GitHub issue. We're always eager to expand Dolt's compatibility with the tools you use every day.

SHARE

JOIN THE DATA EVOLUTION

Get started with Dolt

Or join our mailing list to get product updates.