Connect to Any RDBMS
The mORMot framework provides direct access to external SQL databases through the mormot.db.sql. units. These databases can be used standalone or integrated with the ORM via virtual tables.
┌─────────────────────────────────────────────────────────────────┐
│ mormot.db.sql.pas │
│ TSqlDBConnectionProperties (abstract) │
│ TSqlDBConnection, TSqlDBStatement │
└─────────────────────────────────────────────────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Direct │ │ ODBC │ │ TDataSet │
│ Access │ │ Access │ │ Bridge │
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
▼ ▼ ▼
PostgreSQL Any ODBC FireDAC
Oracle (OCI) Driver UniDAC
SQLite3 NexusDB
| Provider | Unit | Direct Access |
|---|---|---|
| PostgreSQL | mormot.db.sql.postgres |
Yes (libpq) |
| Oracle | mormot.db.sql.oracle |
Yes (OCI) |
| SQLite3 | mormot.db.sql.sqlite3 |
Yes |
| ODBC | mormot.db.sql.odbc |
Yes |
| OleDB | mormot.db.sql.oledb |
Yes |
| Zeos/ZDBC | mormot.db.sql.zeos |
Yes (recommended) |
| FireDAC | mormot.db.rad.firedac |
Via TDataSet |
| UniDAC | mormot.db.rad.unidac |
Via TDataSet |
| NexusDB | mormot.db.rad.nexusdb |
Via TDataSet |
| Database | Direct | ODBC | OleDB | Zeos | TDataSet |
|---|---|---|---|---|---|
| PostgreSQL | ✓ | ✓ | - | ✓ | ✓ |
| Oracle | ✓ | ✓ | ✓ | ✓ | ✓ |
| MS SQL Server | - | ✓ | ✓ | ✓ | ✓ |
| MySQL/MariaDB | - | ✓ | - | ✓ | ✓ |
| SQLite3 | ✓ | ✓ | - | ✓ | ✓ |
| Firebird | - | ✓ | - | ✓ | ✓ |
| IBM DB2 | - | ✓ | ✓ | ✓ | ✓ |
| Informix | - | ✓ | - | - | ✓ |
uses
mormot.db.sql,
mormot.db.sql.postgres;
var
Props: TSqlDBPostgresConnectionProperties;
begin
// PostgreSQL direct
Props := TSqlDBPostgresConnectionProperties.Create(
'localhost:5432', 'mydb', 'user', 'password');
// Connection string alternative
Props := TSqlDBPostgresConnectionProperties.Create(
'host=localhost port=5432 dbname=mydb', '', 'user', 'password');
end;
Oracle (direct OCI):
uses
mormot.db.sql.oracle;
Props := TSqlDBOracleConnectionProperties.Create(
'myserver/orcl', '', 'scott', 'tiger');
// With TNS alias
Props := TSqlDBOracleConnectionProperties.Create(
'PROD_DB', '', 'user', 'password');
MS SQL Server (ODBC):
uses
mormot.db.sql.odbc;
Props := TSqlDBOdbcConnectionProperties.Create(
'Driver={SQL Server};Server=.\SQLEXPRESS;Database=mydb',
'', 'user', 'password');
// Windows authentication
Props := TSqlDBOdbcConnectionProperties.Create(
'Driver={SQL Server};Server=.\SQLEXPRESS;Database=mydb;Trusted_Connection=Yes',
'', '', '');
Zeos (recommended for cross-database):
uses
mormot.db.sql.zeos;
// PostgreSQL via Zeos
Props := TSqlDBZeosConnectionProperties.Create(
'zdbc:postgresql://localhost:5432/mydb', '', 'user', 'password');
// MySQL via Zeos
Props := TSqlDBZeosConnectionProperties.Create(
'zdbc:mysql://localhost:3306/mydb', '', 'user', 'password');
// Firebird via Zeos
Props := TSqlDBZeosConnectionProperties.Create(
'zdbc:firebird://localhost/C:\Data\mydb.fdb', '', 'SYSDBA', 'masterkey');
var
Props: TSqlDBConnectionProperties;
begin
// Create once, use throughout application
Props := TSqlDBPostgresConnectionProperties.Create(...);
try
// Props manages connection pool internally
UseDatabase(Props);
finally
Props.Free; // Release all connections
end;
end;
Important: Create TSqlDBConnectionProperties once per application. It manages connection pooling automatically.
The simplest and safest pattern:
var
Rows: ISqlDBRows;
begin
Rows := Props.Execute(
'SELECT * FROM Customer WHERE Country = ?', ['USA']);
while Rows.Step do
WriteLn(Rows['Name'], ' - ', Rows['City']);
// No try..finally needed - interface auto-releases
end;
For more control:
var
Conn: TSqlDBConnection;
Stmt: TSqlDBStatement;
begin
Conn := Props.ThreadSafeConnection; // Thread-safe connection
Stmt := Conn.NewStatementPrepared(
'SELECT * FROM Customer WHERE Country = ?', True); // True = cache
try
Stmt.Bind(1, 'USA');
Stmt.ExecutePrepared;
while Stmt.Step do
WriteLn(Stmt.ColumnUtf8(0));
finally
Stmt.Free;
end;
end;
For convenient (but slower) field access:
var
Row: Variant;
begin
with Props.Execute(
'SELECT * FROM Customer WHERE Country = ?',
['USA'], @Row) do
while Step do
WriteLn(Row.Name, ' - ', Row.City); // Late-binding!
end;
Export query results directly to JSON:
var
Json: RawUtf8;
begin
Json := Props.Execute(
'SELECT ID, Name, Email FROM Customer', []).FetchAllAsJson(True);
// Returns: [{"ID":1,"Name":"John","Email":"john@example.com"},...]
end;
| Type | Delphi Type | Description |
|---|---|---|
ftNull |
- | SQL NULL |
ftInt64 |
Int64 |
Any integer |
ftDouble |
Double |
Floating-point |
ftCurrency |
Currency |
Fixed 4 decimals |
ftDate |
TDateTime |
Date and time |
ftUtf8 |
RawUtf8 |
Unicode text |
ftBlob |
RawByteString |
Binary data |
// Type-safe access
Value := Stmt.ColumnInt(0); // Int64
Value := Stmt.ColumnDouble(1); // Double
Value := Stmt.ColumnCurrency(2); // Currency
Value := Stmt.ColumnUtf8(3); // RawUtf8
Value := Stmt.ColumnDateTime(4); // TDateTime
Value := Stmt.ColumnBlob(5); // RawByteString
// Null checking
if Stmt.ColumnNull(0) then
WriteLn('Value is NULL');
// Variant access (auto-conversion)
Value := Stmt.Column[0]; // Returns Variant
Value := Stmt['ColumnName']; // By name
// Use ? for positional parameters
Stmt := Conn.NewStatementPrepared(
'INSERT INTO Customer (Name, Email, Age) VALUES (?, ?, ?)', True);
Stmt.Bind(1, 'John Doe');
Stmt.Bind(2, 'john@example.com');
Stmt.Bind(3, 30);
Stmt.ExecutePrepared;
High-performance bulk operations:
// PostgreSQL, Oracle, and MSSQL support array binding
Stmt := Conn.NewStatementPrepared(
'INSERT INTO Log (Timestamp, Message) VALUES (?, ?)', True);
Stmt.BindArray(1, DateTimes); // TDateTimeDynArray
Stmt.BindArray(2, Messages); // TRawUtf8DynArray
Stmt.ExecutePrepared; // Single network roundtrip!
var
Data: RawByteString;
begin
Data := StringFromFile('image.png');
Stmt.Bind(1, ftBlob, Data); // Explicit type
Stmt.ExecutePrepared;
end;
var
Conn: TSqlDBConnection;
begin
Conn := Props.ThreadSafeConnection;
Conn.StartTransaction;
try
Conn.Execute('UPDATE Account SET Balance = Balance - 100 WHERE ID = 1');
Conn.Execute('UPDATE Account SET Balance = Balance + 100 WHERE ID = 2');
Conn.Commit;
except
Conn.Rollback;
raise;
end;
end;
// Most databases don't support true nested transactions
// mORMot uses SAVEPOINTs where available
Conn.StartTransaction; // Begin
try
Conn.StartTransaction; // SAVEPOINT (if supported)
try
// Operations
Conn.Commit; // RELEASE SAVEPOINT
except
Conn.Rollback; // ROLLBACK TO SAVEPOINT
raise;
end;
Conn.Commit; // COMMIT
except
Conn.Rollback; // ROLLBACK
end;
uses
mormot.orm.sql,
mormot.db.sql.postgres;
var
Props: TSqlDBPostgresConnectionProperties;
Model: TOrmModel;
Server: TRestServerDB;
begin
// Create connection properties
Props := TSqlDBPostgresConnectionProperties.Create(
'localhost:5432', 'mydb', 'user', 'password');
// Create model
Model := TOrmModel.Create([TOrmCustomer, TOrmOrder]);
// Map ORM classes to external database
OrmMapExternal(Model, TOrmCustomer, Props);
OrmMapExternal(Model, TOrmOrder, Props);
// Create server (SQLite3 for other tables)
Server := TRestServerDB.Create(Model, ':memory:');
// Now ORM operations go to PostgreSQL
Server.Orm.Add(Customer, True);
end;
// Custom table name
OrmMapExternal(Model, TOrmCustomer, Props, 'CUSTOMERS');
// Custom field mapping
OrmMapExternal(Model, TOrmCustomer, Props, 'CUSTOMERS')
.MapField('Name', 'CUSTOMER_NAME')
.MapField('Email', 'EMAIL_ADDRESS');
// mORMot can create tables in external database
Props.UseFastCreateMissingFields := True;
Server.CreateMissingTables; // Creates in PostgreSQL
var
PostgresProps, OracleProps: TSqlDBConnectionProperties;
begin
// Different databases for different tables
PostgresProps := TSqlDBPostgresConnectionProperties.Create(...);
OracleProps := TSqlDBOracleConnectionProperties.Create(...);
Model := TOrmModel.Create([
TOrmUser, // Internal SQLite3
TOrmProduct, // PostgreSQL
TOrmLegacy // Oracle
]);
OrmMapExternal(Model, TOrmProduct, PostgresProps);
OrmMapExternal(Model, TOrmLegacy, OracleProps);
Server := TRestServerDB.Create(Model, 'users.db3');
end;
Thanks to SQLite3 virtual tables, you can JOIN across databases:
// This works even though Product is in PostgreSQL and User is in SQLite3!
Server.Orm.ExecuteList([TOrmProduct, TOrmUser],
'SELECT Product.Name, User.Email ' +
'FROM Product, User ' +
'WHERE Product.OwnerID = User.ID');
// Direct libpq access (fastest)
uses mormot.db.sql.postgres;
Props := TSqlDBPostgresConnectionProperties.Create(
'localhost:5432', 'dbname', 'user', 'pass');
// Connection string options
Props := TSqlDBPostgresConnectionProperties.Create(
'host=localhost port=5432 dbname=mydb sslmode=require',
'', 'user', 'pass');
Tips:
TDocVariant// Direct OCI access (fastest for Oracle)
uses mormot.db.sql.oracle;
Props := TSqlDBOracleConnectionProperties.Create(
'server/service', '', 'user', 'pass');
// Array binding for bulk operations
Props.ArrayBindingEnabled := True;
Tips:
// ODBC (recommended)
uses mormot.db.sql.odbc;
Props := TSqlDBOdbcConnectionProperties.Create(
'Driver={ODBC Driver 17 for SQL Server};Server=.\SQLEXPRESS;Database=mydb',
'', 'user', 'pass');
// OleDB alternative
uses mormot.db.sql.oledb;
Props := TSqlDBOleDBConnectionProperties.Create(
'Provider=SQLNCLI11;Server=.\SQLEXPRESS;Database=mydb',
'', 'user', 'pass');
Tips:
Trusted_Connection=Yes// Zeos (recommended)
uses mormot.db.sql.zeos;
Props := TSqlDBZeosConnectionProperties.Create(
'zdbc:mysql://localhost:3306/mydb', '', 'user', 'pass');
// ODBC alternative
Props := TSqlDBOdbcConnectionProperties.Create(
'Driver={MySQL ODBC 8.0 Driver};Server=localhost;Database=mydb',
'', 'user', 'pass');
Tips:
SET NAMES utf8mb4)// Connection pool is automatic
Props := TSqlDBPostgresConnectionProperties.Create(...);
// Configure pool size
Props.ConnectionPoolMaxCount := 20; // Max connections
Props.ConnectionPoolTimeout := 60000; // Timeout in ms
// Enable statement caching (True = cache)
Stmt := Conn.NewStatementPrepared(SQL, True);
// Clear cache if needed
Conn.ClearStatementCache;
// Use ExecuteNoResult for multiple statements
Conn.ExecuteNoResult('DELETE FROM TempTable');
Conn.ExecuteNoResult('INSERT INTO TempTable SELECT * FROM Source');
// Or use batch interface
Batch := TRestBatch.Create(Server.Orm, TOrmCustomer);
try
for i := 1 to 10000 do
Batch.Add(CreateCustomer(i), True);
Server.Orm.BatchSend(Batch); // Single transaction
finally
Batch.Free;
end;
For VCL/FMX applications that need TDataSet:
uses
mormot.db.rad.ui.sql;
var
DataSet: TSqlDBDataSet;
begin
DataSet := TSqlDBDataSet.Create(Self);
DataSet.Connection := Props;
DataSet.SQL.Text := 'SELECT * FROM Customer';
DataSet.Open;
DBGrid1.DataSource.DataSet := DataSet;
end;
uses
mormot.db.rad.ui.sql;
var
Rows: ISqlDBRows;
begin
Rows := Props.Execute('SELECT * FROM Customer', []);
DBGrid1.DataSource.DataSet := ToDataSet(Self, Rows);
end;
| mORMot 1 | mORMot 2 |
|---|---|
SynDB.pas |
mormot.db.sql.pas |
SynDBOracle.pas |
mormot.db.sql.oracle.pas |
SynDBODBC.pas |
mormot.db.sql.odbc.pas |
SynOleDB.pas |
mormot.db.sql.oledb.pas |
SynDBZeos.pas |
mormot.db.sql.zeos.pas |
SynDBSQLite3.pas |
mormot.db.sql.sqlite3.pas |
SynDBDataset.pas |
mormot.db.rad.pas |
SynDBFireDAC.pas |
mormot.db.rad.firedac.pas |
mORMotDB.pas |
mormot.orm.sql.pas |
| mORMot 1 | mORMot 2 |
|---|---|
TSQLDBConnectionProperties |
TSqlDBConnectionProperties |
TSQLDBConnection |
TSqlDBConnection |
TSQLDBStatement |
TSqlDBStatement |
ISQLDBRows |
ISqlDBRows |
TSQLDBFieldType |
TSqlDBFieldType |
VirtualTableExternalRegister |
OrmMapExternal |
// mORMot 1
VirtualTableExternalRegister(Model, TSQLCustomer, Props);
// mORMot 2
OrmMapExternal(Model, TOrmCustomer, Props);
Next Chapter: External NoSQL Database Access (MongoDB)
| Previous | Index | Next |
|---|---|---|
| Chapter 7: Database Layer | Index | Chapter 9: External NoSQL Database Access |