Stream-export filtered PostgreSQL rows to CSV and bulk-import via COPY
Project description
pg-export-import
Stream-export filtered PostgreSQL rows to CSV and bulk-import via COPY. Supports multi-table, FK-aware pipelines with idempotent re-runs.
Installation
pip install pg-export-import
Requirements
- Python 3.11+
- PostgreSQL (source and target can be on different servers)
Library usage
Single table
from pg_export_import import ConnectionConfig, export_and_import
src = ConnectionConfig(host="db1.example.com", dbname="prod", user="app", password="...")
tgt = ConnectionConfig(host="db2.example.com", dbname="staging", user="app", password="...")
result = export_and_import(
source_config=src,
target_config=tgt,
source_table="public.orders",
target_table="public.orders",
where_clause="created_at >= %s",
where_params=("2024-01-01",),
csv_path="/data/exports/orders.csv", # optional; auto-generated if omitted
delete_before_import=True, # optional; delete target rows before importing (default False)
# delete_where_clause="created_at >= %s", # optional; defaults to where_clause
# delete_where_params=("2024-01-01",), # optional; defaults to where_params
)
print(result.status, result.exported_count, result.imported_count, result.deleted_count)
result.status is one of "success", "export_failed", "delete_failed", or "import_failed".
Multi-table pipeline
from pg_export_import import ConnectionConfig, run_pipeline
src = ConnectionConfig(...)
tgt = ConnectionConfig(...)
tables = [
# parents before children (FK order)
{"source_table": "customers", "target_table": "customers", "where_clause": ""},
{"source_table": "orders", "target_table": "orders", "where_clause": "created_at >= %s", "where_params": ("2024-01-01",)},
{"source_table": "order_items", "target_table": "order_items", "where_clause": "created_at >= %s", "where_params": ("2024-01-01",)},
]
results = run_pipeline(
source_config=src,
target_config=tgt,
tables=tables,
csv_dir="/data/exports", # optional; defaults to tempdir
fetch_size=5000, # optional; rows per round-trip (default 5000)
delete_before_import=True, # optional; set False to append without deleting (default True)
)
for r in results:
print(r["table"], r["status"], r["exported"], r["imported"])
CSV file naming
Each pipeline run captures a single timestamp once at start. All tables in that run share the same timestamp:
{csv_dir}/{source_table}_{YYYYMMDD_HHMMSS}.csv
Dots in schema-qualified names become underscores:
| source_table | csv_dir | filename |
|---|---|---|
orders |
/data/exports |
orders_20250320_143022.csv |
public.orders |
/data/exports |
public_orders_20250320_143022.csv |
- The
csv_diris created automatically if it does not exist. - On success, CSV files are deleted.
- On failure, CSV files are preserved for debugging or re-import.
Table config schema
Each entry in the tables argument to run_pipeline supports:
| Key | Type | Required | Description |
|---|---|---|---|
source_table |
str | yes | Source table reference ("table" or "schema.table") |
target_table |
str | yes | Target table reference |
where_clause |
str | yes | SQL WHERE fragment (empty string = all rows) |
where_params |
tuple | dict | None | no | Bind values for where_clause placeholders |
delete_where_clause |
str | no | WHERE fragment for DELETE on target (defaults to where_clause) |
delete_where_params |
tuple | dict | None | no | Bind values for delete_where_clause |
fetch_size |
int | no | Per-table override for rows fetched per round-trip; takes precedence over run_pipeline's fetch_size argument |
delete_before_import |
bool | no | Per-table override; when False, skips DELETE for this table regardless of the pipeline-level argument |
Security notes
where_clauseis embedded as a raw SQL fragment — it must come from trusted application code, never from user input.- Runtime filter values must be passed via
where_params, not interpolated into the clause string. - All table and column identifiers are validated and safely quoted via
psycopg.sql.Identifier.
Project details
Release history Release notifications | RSS feed
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_export_import-0.1.0b3.tar.gz.
File metadata
- Download URL: pg_export_import-0.1.0b3.tar.gz
- Upload date:
- Size: 17.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
475f48b67fbe66fbfd65f4e91fdffd7fe6be2bae2910d6df68d14dc2f315b344
|
|
| MD5 |
d33fa182396e237f32d6ac729bf7cfba
|
|
| BLAKE2b-256 |
a23342407cf25d5d0afaae98e39caf7e6104f77bac165ce7d5f4674389a0c599
|
File details
Details for the file pg_export_import-0.1.0b3-py3-none-any.whl.
File metadata
- Download URL: pg_export_import-0.1.0b3-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d689d1a254c5942b34dcff49ab49c7e2265382a4ffdb481cd0137e66f9f2df4
|
|
| MD5 |
1ce4f71c6b9a17eb070f8e76a483caaf
|
|
| BLAKE2b-256 |
1489680b8592fcd1c01e9aaf798e6793570d09a3196599bb71d33e7921c29832
|