Chat2DB Database SPI Plugins Implementation Contract: A Complete Developer Guide

Chat2DB database SPI plugins must implement the IPlugin interface as a single entry point, expose database-specific implementations via IDbMetaData, IDbManager, and ISqlBuilder, and register via META-INF/services/ai.chat2db.spi.IPlugin to be discovered by the Spring-based runtime.

The Chat2DB project uses a Service Provider Interface (SPI) architecture to support multiple database dialects without hard-coding database logic into the core server. Understanding this implementation contract is essential for developers extending Chat2DB with custom database support or maintaining existing plugins.

Core SPI Architecture

The chat2db-community-spi module defines the contracts that every database plugin must obey. The architecture separates concerns into distinct interfaces: configuration, metadata discovery, connection management, and SQL generation.

The Primary Entry Point: IPlugin

Every plugin must provide exactly one class implementing ai.chat2db.spi.IPlugin, located at chat2db-community-server/chat2db-community-spi/src/main/java/ai/chat2db/spi/IPlugin.java. This class serves as the primary entry point discovered by the runtime.

The contract mandates:

  • Naming convention: The entry class must be named <Database>Plugin (e.g., MysqlPlugin, PostgresqlPlugin)
  • Registration: Must be listed in META-INF/services/ai.chat2db.spi.IPlugin

The IPlugin interface requires implementation of these core methods:

  • getDBConfig() – Returns default connection configuration (driver class, URL template, etc.)
  • getDbMetaData() – Provides the metadata provider (defaults to DefaultMetaService if not overridden)
  • getDbManager() – Returns the DDL and connection manager (implementation class must be named <Database>DBManager)
  • getSqlBuilder() – Returns the dialect-specific SQL builder
  • getValueProcessor() – Handles value type conversions
  • getSQLIdentifierProcessor() – Manages identifier quoting and escaping
  • getCommandExecutor() – Executes commands specific to the database
  • getKeyOperations() – Provides key operation handlers

Metadata Provider Interface

The IDbMetaData interface, defined in chat2db-community-spi/src/main/java/ai/chat2db/spi/IDbMetaData.java, supplies catalog information. Implementations must provide methods for enumerating database objects:

  • databases() – List available databases
  • schemas() – List schemas within a database
  • tables() – List tables with filtering support
  • columns() – Describe table columns
  • indexes() – List table indexes
  • functions() and triggers() – List routines and triggers

The interface also includes helpers for identifier handling and result-set type mapping.

Database Manager and SQL Builder

The IDbManager implementation (naming convention: <Database>DBManager) handles JDBC connections and executes DDL/DML. Critically, contract section 4 mandates that this class must not contain SQL construction logic; it delegates to an ISqlBuilder.

The ISqlBuilder interface provides the unified entry point for constructing SQL strings. Methods must follow the naming pattern build<Verb><Object> (e.g., buildCreateTable, buildDropIndex). The builder supports semantic groups including:

  • identifier() and literal() – Escaping and formatting
  • dql(), dml(), ddl(), dcl(), tcl() – SQL command categories
  • metadata() – Information schema queries
  • routine() – Stored procedure/function DDL
  • export() – Export-specific SQL generation
  • unsafe() – Raw SQL passthrough

Discovery and Registration

The runtime discovers plugins using Spring's ServiceLoader mechanism. Each plugin must include a registration file:


META-INF/services/ai.chat2db.spi.IPlugin

This file contains the fully-qualified name of the plugin's entry class (e.g., ai.chat2db.plugin.mysql.MysqlPlugin). Only one entry class is permitted per plugin JAR.

Implementation Requirements and Constraints

Security and Resource Management

The contract enforces strict security patterns for database interaction:

  • PreparedStatement usage: All SQL execution involving user input must use PreparedStatement. Direct use of Statement#createStatement().execute(...) is prohibited (contract sections 8-71 to 8-74)
  • Resource handling: JDBC objects must be managed via try-with-resources. Implementations must never close connections owned by the caller (contract sections 10-87 to 10-90)

Error Handling Semantics

Plugin methods must throw exceptions for failures and return empty collections where appropriate. Using null to signal unsupported operations, failures, or empty results violates the contract (sections 9-79 to 9-84).

Constants and Resources

All raw SQL fragments, error codes, and command names must reside in the .../constant package within each plugin module. Hard-coding literals in implementation classes violates contract sections 5-35 to 5-42. Resource files live under src/main/resources (contract section 7).

SPI Boundary Rules

The chat2db-community-spi and chat2db-community-tools modules must remain database-agnostic. No concrete database-type branching (e.g., if (type == MYSQL)) is permitted inside the SPI layer; such logic belongs exclusively in concrete plugin implementations (contract sections 11-94 to 11-98).

Optional Extensions

Plugins may optionally implement:

  • ISqlSyntaxPlugin – Exposed via IPlugin#getSqlSyntaxPlugin() for SQL parsing and completion support (must be obtained through the primary plugin, not via separate ServiceLoader)
  • IAccountManager – For database-specific user account management
  • IRoutineManager – For stored procedure and function management

Code Example: Minimal MySQL Plugin

Below is a complete skeleton implementing the Chat2DB SPI contract for MySQL:

// src/main/java/ai/chat2db/plugin/mysql/MysqlPlugin.java
package ai.chat2db.plugin.mysql;

import ai.chat2db.community.domain.api.config.DBConfig;
import ai.chat2db.spi.*;

public class MysqlPlugin implements IPlugin, ISqlSyntaxPlugin {
    
    @Override
    public DBConfig getDBConfig() {
        return new DBConfig()
                .setDbType("MYSQL")
                .setDriverClassName("com.mysql.cj.jdbc.Driver")
                .setUrlTemplate("jdbc:mysql://{host}:{port}/{database}");
    }

    @Override
    public IDbManager getDbManager() {
        return new MysqlDBManager();
    }

    @Override
    public ISqlBuilder getSqlBuilder() {
        return new MysqlSqlBuilder();
    }

    @Override
    public ISqlSyntaxPlugin getSqlSyntaxPlugin() {
        return this;
    }
}
// src/main/java/ai/chat2db/plugin/mysql/MysqlDBManager.java
package ai.chat2db.plugin.mysql;

import ai.chat2db.spi.IDbManager;
import java.sql.Connection;
import java.sql.PreparedStatement;

public class MysqlDBManager implements IDbManager {
    
    @Override
    public void execute(Connection conn, String sql, Object... args) throws Exception {
        try (PreparedStatement ps = conn.prepareStatement(sql)) {
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
            ps.execute();
        }
    }
}

Contract reference documentation is maintained in java-plugin-contracts.md at the repository root, with full interface definitions available in the chat2db-community-spi module source tree.

Summary

  • Single entry point: Implement IPlugin in a class named <Database>Plugin and register it in META-INF/services/ai.chat2db.spi.IPlugin
  • Core interfaces: Provide implementations for IDbMetaData (catalog discovery), IDbManager (connection/execution), and ISqlBuilder (SQL generation)
  • Security first: Use PreparedStatement for all parameterized queries and try-with-resources for resource management
  • Naming conventions: Follow the <Database>DBManager pattern for managers and build<Verb><Object> for SQL builder methods
  • No null returns: Throw exceptions for errors, return empty collections for missing data
  • SPI purity: Keep database-agnostic logic in SPI modules; database-specific branching belongs only in plugin implementations

Frequently Asked Questions

What files must I create to add a new database plugin to Chat2DB?

You must create a class implementing ai.chat2db.spi.IPlugin named <YourDatabase>Plugin, plus implementations of IDbMetaData, IDbManager (named <YourDatabase>DBManager), and ISqlBuilder. Additionally, create the file META-INF/services/ai.chat2db.spi.IPlugin in your resources directory containing the fully-qualified name of your plugin class.

Can I use Statement instead of PreparedStatement in my plugin implementation?

No. The Chat2DB SPI contract explicitly prohibits using Statement#createStatement().execute() for SQL containing user input. You must use PreparedStatement with parameterized queries to prevent SQL injection vulnerabilities (contract sections 8-71 to 8-74).

Where should I put raw SQL strings in my Chat2DB plugin?

All raw SQL fragments, error codes, and command names must be defined as constants in the .../constant package within your plugin module, not hard-coded in implementation classes. Resources such as property files should reside under src/main/resources (contract sections 5-35 to 5-42 and section 7).

How does Chat2DB discover and load my database plugin?

Chat2DB uses Spring's ServiceLoader mechanism to discover plugins at runtime. The loader reads the META-INF/services/ai.chat2db.spi.IPlugin file from your plugin JAR, instantiates the class listed there, and registers it with the application context. Only one IPlugin implementation is permitted per plugin archive.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →