Cross-database materialisations for Postgres
Project description
pg-xmat
Cross-database materialisations for PostgreSQL.
pg-xmat lets you define jobs for transforming and streaming data between different database servers.
It is a good choice when more than one of the following are requirements:
- one-off/batch jobs
- moving data between database instances
- use-cases not suitable for FDWs
- programmatic use
Installation
pip install pg-xmat
Quick Start
- Define jobs (
pg_xmat_jobs.py):
import os
SOURCE_DB_URL = os.getenv("SOURCE_DB_URL")
TARGET_DB_URL = os.getenv("TARGET_DB_URL")
JOBS = {
"export:users": {
"source": {"database": SOURCE_DB_URL, "schema": "public"},
"target": {"database": TARGET_DB_URL, "schema": "staging"},
"tables": {
"users": {"where": {"active": True}},
"profiles": {
"where": {"user_id": [1, 2, 3, 4]}}
"select": { "inserted_at": "current_date" },
}
}
}
}
- Run the job:
pg-xmat "export:users"
# or run all jobs matching glob expression
pg-xmat "export:*"
How it works
- Schema Preparation: Drops and recreates target schema, then replicates table structures from source
- Query Building: Constructs filtered SELECT queries based on your
whereandselectconfigurations - Streaming Transfer: Uses PostgreSQL's
COPYcommand to stream data directly between databases - Constraint Replication: Copies indexes, foreign keys, and constraints using
pg_dump --section=post-data
The process is designed to be fast and maintain data integrity by leveraging PostgreSQL's native bulk operations rather than row-by-row processing.
CLI Usage
pg-xmat [job_pattern] [options]
Arguments:
job_pattern Glob pattern to match job names (e.g., "export:*", "mat:users")
Options:
-c, --config FILE Path to configuration file (default: pg_xmat_jobs.py)
-v, --verbose Enable verbose logging
-h, --help Show help message
Job API
Schema
JOBS = {
"job_name": {
"source": {
"database": "postgresql://...", # Source database URL
"schema": "schema_name" # Source schema name
},
"target": {
"database": "postgresql://...", # Target database URL
"schema": "schema_name" # Target schema name
},
"tables": {
"table_name": {
"where": {...}, # Optional: Filter conditions
"select": {...} # Optional: Column transformations
}
}
}
}
Where Filters
Filter data during transfer using various condition types:
"where": {
# Exact match
"status": "active",
# IN clause (list/tuple)
"user_id": [1, 2, 3, 4],
"category": ("A", "B", "C"),
# Range queries (dict)
"created_at": {"gte": "2023-01-01", "lte": "2023-12-31"},
"price": {"gte": 100},
"score": {"lte": 90},
# Boolean values
"is_active": True,
"is_deleted": False,
}
Select Transformations
Transform columns during transfer using SQL expressions:
"select": {
"created_at": "DATE({column_name})", # Extract date part
"full_name": "CONCAT(first_name, ' ', last_name)", # Concatenate columns
"shifted_date": "{column_name} + INTERVAL '30 days'", # Date arithmetic
"normalized_email": "LOWER(TRIM({column_name}))" # Text normalization
}
The {column_name} placeholder is automatically replaced with the properly quoted column name.
Wildcard Tables
Process all tables in a schema:
"tables": {
"*": {
"where": {"tenant_id": 123} # Applied to all tables that have this column
}
}
Python API
Basic Usage
from pg_xmat import run_job, run_jobs
# Run a single job
job_config = {
"source": {"database": "postgresql://...", "schema": "public"},
"target": {"database": "postgresql://...", "schema": "staging"},
"tables": {"users": {"where": {"active": True}}}
}
run_job(job_config, verbose=True)
# Run multiple jobs with pattern matching
jobs_config = {"export:users": job_config, "export:orders": {...}}
run_jobs("export:*", jobs_config, verbose=True)
Examples
Data Migration
MIGRATION_JOB = {
"source": {"database": PROD_DB_URL, "schema": "public"},
"target": {"database": STAGING_DB_URL, "schema": "public"},
"tables": {
"users": {"where": {"created_at": {"gte": "2023-01-01"}}},
"orders": {"where": {"status": ["completed", "shipped"]}},
"products": {"where": {"active": True}}
}
}
Data Anonymization
ANONYMIZE_JOB = {
"source": {"database": PROD_DB_URL, "schema": "public"},
"target": {"database": TEST_DB_URL, "schema": "public"},
"tables": {
"users": {
"select": {
# Replace real emails with user1@example.com, user2@example.com, etc.
"email": "'user' || id || '@example.com'",
# Generate fake phone numbers like +15550000001, +15550000002, etc.
"phone": "'+1555' || LPAD(id::text, 7, '0')",
# Replace real names with "Test User 1", "Test User 2", etc.
"name": "'Test User ' || id"
}
}
}
}
Time-shifted Data
SHIFT_JOB = {
"source": {"database": PROD_DB_URL, "schema": "events"},
"target": {"database": TEST_DB_URL, "schema": "events"},
"tables": {
"*": {
"select": {
"created_at": "{column_name} + (CURRENT_DATE - DATE '2023-06-01')",
"updated_at": "{column_name} + (CURRENT_DATE - DATE '2023-06-01')"
}
}
}
}
Multi-tenant Data Extraction
TENANT_EXPORT = {
"source": {"database": MAIN_DB_URL, "schema": "public"},
"target": {"database": TENANT_DB_URL, "schema": "tenant_123"},
"tables": {
"users": {"where": {"tenant_id": 123}},
"orders": {"where": {"tenant_id": 123, "status": ["active", "pending"]}},
"analytics": {"where": {"tenant_id": 123, "date": {"gte": "2023-01-01"}}}
}
}
Requirements
- Python 3.7+
- PostgreSQL client tools (
psql,pg_dump) - Network access between source and target databases
Security
- Database passwords are automatically redacted in log output
- SQL identifiers are properly quoted to prevent injection
- Environment variables recommended for sensitive connection strings
License
MIT License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pg_xmat-0.1.1.tar.gz.
File metadata
- Download URL: pg_xmat-0.1.1.tar.gz
- Upload date:
- Size: 9.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9a29b6aa1dcbb6a7a0f6a41cb713cea8698cd245a653fa4665c312836c8a296
|
|
| MD5 |
abebe61569b439e8e41a247a6aecb696
|
|
| BLAKE2b-256 |
5b83b5e460c557c16b82ca7aa0fb37255ff554717b460615ec1093c287f4cb6c
|
File details
Details for the file pg_xmat-0.1.1-py3-none-any.whl.
File metadata
- Download URL: pg_xmat-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f803694d31f4c6acb7b339094a5bb8efa4f7e026658712d5e9a1b3660cff3ac
|
|
| MD5 |
8275f9b55cd3f4f34c676b197b537622
|
|
| BLAKE2b-256 |
df24f6df8c56bdf9a7f5b92682c6d67ab3fd845b065ba238e08a6c168548bf6a
|