Understande Activerecord connector and build your own - First part
Active Record is one of the most powerful ORMs in the Ruby ecosystem. It has been around for a long time, and for good reason: it makes it easy to work with data in a natural, expressive way from Ruby code. For many applications, it remains an excellent choice for managing records without the complexity of writing SQL everywhere.
That said, Active Record has an important limitation. Its connector ecosystem is not as broad or as well maintained as one might expect. Outside of the core support for SQLite, PostgreSQL, and MySQL, finding a connector that is both reliable and actively maintained can be difficult.
In this article series, I want to look at how an Active Record connector is structured, what role it plays inside the ORM, and how it fits into the overall design. Then, we will build a simple connector step by step to better understand how it works under the hood.
Architecture
The implementation of Active Record adapters can be found in the Rails source code under activerecord/lib/active_record/connection_adapters/. This directory contains the adapter infrastructure shared by all database backends, as well as the built-in adapters for MySQL, PostgreSQL, and SQLite3.
When implementing a new adapter, this directory is the primary reference, as it defines the interfaces and behaviors expected by Active Record. Its overall structure with the relevant elements is illustrated in the following diagram.
AbstractAdapter
AbstractAdapter is the base class for every Active Record adapter. The Rails source code includes implementations for adapters such as MySQL, PostgreSQL, and SQLite3, which can serve as references when building a new connector.
This class is the core of the adapter. It is responsible for initializing and managing the database connection, exposing the API expected by Active Record, and coordinating the different adapter modules.
The @raw_connection instance variable stores the native connection object returned by the underlying database driver. The reconnect method is responsible for establishing or re-establishing this connection when needed.
Most of the adapter’s functionality is split into specialized modules that are mixed into AbstractAdapter.
Quoting
The Quoting module defines how SQL identifiers and values are quoted to produce valid SQL and prevent SQL injection.
In most adapters, only quote_column_name (and sometimes quote_table_name) needs to be implemented. The remaining quoting methods are derived from these base implementations.
DatabaseStatements
This module implements the execution of SQL statements. It contains the logic that transforms Active Record (and Arel) queries into SQL sent to the database.
The most important methods to implement are:
perform_query, which sends the SQL statement to the database.cast_result, which converts the native database result into anActiveRecord::Result.
Other methods, such as affected_rows or explain, expose database-specific capabilities.
SchemaStatements
SchemaStatements contains everything related to schema inspection and modification.
It defines how Active Record retrieves metadata such as tables, views, columns, indexes, foreign keys, and constraints. It also implements schema modification operations such as renaming tables or columns, changing column definitions, or updating comments.
This module is also responsible for mapping database-specific column types to Active Record types.
Column
Column represents the metadata associated with a database column, including its name, type, default value, nullability, and other attributes.
Most adapters provide their own Column subclass to expose additional database-specific information. Instances are typically created by new_column_from_field, which is implemented in SchemaStatements.
StatementPool
StatementPool manages the cache of prepared statements used by the adapter.
Its purpose is to reuse prepared statements whenever possible to improve performance while ensuring they are properly released when no longer needed. In most adapters, very little customization is required beyond implementing dealloc.
SchemaCreation
SchemaCreation generates database-specific SQL from Active Record’s schema definition objects.
It is primarily used by migrations and schema dumping to translate abstract operations—such as creating tables, adding columns, or defining indexes—into SQL compatible with the target database.