PostgreSQL Database — Skill
Use this skill when working with PostgreSQL database in Delphi projects via FireDAC.
When to Use
- When configuring FireDAC connection with PostgreSQL
- When creating tables, sequences, functions, triggers and views
- When implementing Repositories with FireDAC + PostgreSQL
- When working with advanced types (JSONB, Arrays, UUID, ENUM)
- When implementing UPSERT, CTEs, Full-Text Search or Window Functions
- When planning schema migrations (versioned scripts)
PostgreSQL Versions
| Version | Relevant News |
|---|---|
| 12 | Generated Columns, CTE inlining, Partitioning improvements |
| 13 | Incremental sorting, Parallel vacuum, Deduplication in B-tree |
| 14 | Multirange types, SEARCH/CYCLE in recursive CTEs |
| 15 | MERGE statement, JSON logging, UNIQUE NULL NOT DISTINCT |
| 16 | Logical replication from standby, ANY_VALUE(), ICU default collations |
| 17 | RETURNING OLD/NEW no MERGE, JSON_TABLE, Identity columns improvements |
Recommendation: Use PostgreSQL 14+ for new projects. Enjoy
MERGE, JSONB and partitioning.
FireDAC Connection with PostgreSQL
Minimum Configuration
unit MeuApp.Infra.Database.PostgreSQL.Connection;
interface
uses
FireDAC.Comp.Client,
FireDAC.Phys.PG, // Driver PostgreSQL
FireDAC.Phys.PGDef, // Defaults do PostgreSQL
FireDAC.Stan.Def,
FireDAC.Stan.Pool,
FireDAC.DApt;
type
/// <summary>
/// Factory de connection PostgreSQL via FireDAC.
/// </summary>
TPostgreSQLConnectionFactory = class
public
/// <summary>
/// Cria e configura uma connection PostgreSQL.
/// </summary>
/// <param name="AServer">Address do servidor</param>
/// <param name="ADatabase">Nome do banco de data</param>
/// <param name="AUserName">User (default: postgres)</param>
/// <param name="APassword">Senha do banco</param>
/// <param name="APort">Porta (default: 5432)</param>
/// <returns>Connection FireDAC configurada e aberta</returns>
class function CreateConnection(
const AServer: string;
const ADatabase: string;
const AUserName: string = 'postgres';
const APassword: string = '';
APort: Integer = 5432
): TFDConnection;
/// <summary>
/// Cria connection via connection string completa.
/// </summary>
class function CreateFromConnectionString(
const AConnectionString: string
): TFDConnection;
end;
implementation
uses
System.SysUtils;
class function TPostgreSQLConnectionFactory.CreateConnection(
const AServer, ADatabase, AUserName, APassword: string;
APort: Integer): TFDConnection;
begin
if ADatabase.Trim.IsEmpty then
raise EArgumentException.Create('ADatabase não pode ser vazio');
Result := TFDConnection.Create(nil);
try
Result.DriverName := 'PG';
Result.Params.Values['Server'] := AServer;
Result.Params.Values['Port'] := APort.ToString;
Result.Params.Database := ADatabase;
Result.Params.UserName := AUserName;
Result.Params.Password := APassword;
{ Configurações recomendadas }
Result.Params.Values['CharacterSet'] := 'UTF8';
{ Opções do driver FireDAC }
Result.FormatOptions.StrsTrim2Len := True;
Result.FetchOptions.Mode := fmAll;
Result.ResourceOptions.AutoReconnect := True;
Result.TxOptions.Isolation := xiReadCommitted;
{ Schema padrão — 'public' por default, alterar se necessário }
// Result.Params.Values['MetaDefSchema'] := 'public';
Result.Connected := True;
except
Result.Free;
raise;
end;
end;
class function TPostgreSQLConnectionFactory.CreateFromConnectionString(
const AConnectionString: string): TFDConnection;
begin
Result := TFDConnection.Create(nil);
try
Result.ConnectionString := AConnectionString;
Result.Connected := True;
except
Result.Free;
raise;
end;
end;
FDPhysPGDriverLink — Configure Client Library
uses
FireDAC.Phys.PGWrapper,
FireDAC.Phys.PG;
var
LDriverLink: TFDPhysPGDriverLink;
begin
LDriverLink := TFDPhysPGDriverLink.Create(nil);
try
{ Apontar libpq.dll customizado (32/64-bit) }
LDriverLink.VendorLib := 'C:\PostgreSQL\bin\libpq.dll';
{ Windows: precisa também libintl-9.dll, libeay32.dll, ssleay32.dll no PATH }
finally
{ DriverLink vive por toda a aplicação — criar no DataModule }
end;
end;
Connection Pooling
{ Via FDManager }
FDManager.ConnectionDefs.AddConnectionDef;
with FDManager.ConnectionDefs.ConnectionDefByName('PG_POOL') do
begin
DriverID := 'PG';
Server := 'localhost';
Port := 5432;
Database := 'meubanco';
UserName := 'postgres';
Password := 'senha';
Params.Values['CharacterSet'] := 'UTF8';
Params.Values['Pooled'] := 'True';
Params.Values['POOL_MaximumItems'] := '50';
Params.Values['POOL_CleanupTimeout'] := '30000';
Params.Values['POOL_ExpireTimeout'] := '90000';
end;
SSL/TLS
{ Conexão segura com SSL }
Result.Params.Values['PGAdvanced'] := 'sslmode=require';
{ Para certificado de cliente: }
// Result.Params.Values['PGAdvanced'] :=
// 'sslmode=verify-full;sslcert=client-cert.pem;sslkey=client-key.pem;sslrootcert=ca.pem';
Data Types — PostgreSQL Mapping ↔ Delphi
| PostgreSQL | Delphi (FireDAC) | Note |
|---|---|---|
INTEGER / INT4 | ftInteger / AsInteger | 32-bit |
BIGINT / INT8 | ftLargeint / AsLargeInt | 64-bit |
SMALLINT / INT2 | ftSmallint / AsSmallInt | 16-bit |
SERIAL | ftAutoInc / AsInteger | 32-bit auto-increment |
BIGSERIAL | ftAutoInc / AsLargeInt | 64-bit auto-increment |
VARCHAR(N) | ftString / AsString | Limited text |
TEXT | ftMemo / AsString | Unlimited Text |
NUMERIC(P,S) | ftBCD / AsCurrency | Monetary values |
DOUBLE PRECISION | ftFloat / AsFloat | Ponto flutuante |
REAL / FLOAT4 | ftSingle / AsSingle | 32-bit float |
DATE | ftDate / AsDateTime | Date only |
TIME | ftTime / AsDateTime | Just in time |
TIMESTAMP | ftDateTime / AsDateTime | Date + Time (without timezone) |
TIMESTAMPTZ | ftDateTime / AsDateTime | Date + Time (with timezone) |
BOOLEAN | ftBoolean / AsBoolean | TRUE/FALSE native |
UUID | ftGuid / AsString | Use gen_random_uuid() (PG 13+) |
JSON | ftMemo / AsString | JSON text (validated) |
JSONB | ftMemo / AsString | Binary JSON (indexable) |
BYTEA | ftBlob / AsBytes | Binary data |
ARRAY | ftMemo / AsString | PostgreSQL Array as Text |
INET / CIDR | ftString / AsString | Network addresses |
Sequences and Auto-Increment
SERIAL / BIGSERIAL (Legacy)
-- Cria coluna auto-increment automaticamente + sequence
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- Equivale a criar uma SEQUENCE + DEFAULT nextval('customers_id_seq')
IDENTITY Columns (Modern — SQL Standard)
-- Preferir sobre SERIAL em novos projetos (PG 10+)
CREATE TABLE customers (
id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- GENERATED BY DEFAULT: permite override manual do ID
CREATE TABLE products (
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
Manual Sequences
CREATE SEQUENCE seq_order_number START WITH 1000 INCREMENT BY 1;
-- Usar no INSERT
INSERT INTO orders (order_number) VALUES (nextval('seq_order_number'));
RETURNING in Delphi
/// <summary>
/// Insere customer e obtém o id e created_at gerados pelo banco.
/// RETURNING funciona com Open (igual ao Firebird).
/// </summary>
procedure TPostgreSQLCustomerRepository.Insert(ACustomer: TCustomer);
var
LQuery: TFDQuery;
begin
LQuery := TFDQuery.Create(nil);
try
LQuery.Connection := FConnection;
LQuery.SQL.Text :=
'INSERT INTO customers (na