Parallel Oracle-to-Parquet exporter CLI using oracledb Thin mode (no Oracle Client required)
Project description
oracle-pq-exporter
A fast, parallel Oracle-to-Parquet exporter CLI — built to replace SQL*Plus spool for bulk data extraction.
한국어 문서는 소스 배포판의
README.ko.md를 참고하세요. (Korean documentation is available inREADME.ko.md, shipped in the source distribution.)
oracle-pq-exporter connects to Oracle using oracledb Thin mode (no Oracle Client installation
required), splits a table across worker processes with ORA_HASH(ROWID, N), streams fetch batches
through Polars/PyArrow into snappy-compressed Parquet files, and merges the parts back into a single
file. It was battle-tested in a production Oracle → BigQuery migration.
Features
- No Oracle Client required — pure-Python
oracledbThin mode (UTF-8 by default, safe for CJK data) - Parallel extraction — rows are split across processes via
ORA_HASH(ROWID, N), then part files are merged with a streaming (memory-bounded) merge - NUMBER precision preserved — columns that exceed Float64's ~15.95 significant digits are fetched as strings so values survive exactly (see Precision handling)
- Automatic retry on transient network errors — reconnect-level blips are retried (default: 3 attempts, 5s apart)
- Graceful shutdown — SIGINT/SIGTERM finishes the current batch, closes writers, and exits cleanly
- Two extraction modes — by partition (
PARTITION (...)clause) or by date range (BETWEEN) - Sensible output — schema-only empty Parquet for 0-row results, per-worker logs, fetch/convert/write timing breakdown
Installation
pip install oracle-pq-exporter
Requires Python 3.10+. No Oracle Client, no extra system packages.
Connection configuration
Credentials are resolved in the following order (no password on the command line, so nothing leaks into your shell history):
- CLI arguments:
--user,--dsn - Environment variables:
OPQ_USER,OPQ_PASSWORD,OPQ_DSN - Config file:
--config path/to/config.toml, or the default~/.config/opq-export/config.toml - If no password is found anywhere, you are prompted interactively (
getpass)
Example ~/.config/opq-export/config.toml:
[oracle]
user = "myuser"
dsn = "host:1521/SERVICE"
# Putting the password here is discouraged — prefer OPQ_PASSWORD or the interactive prompt.
[export]
batch_size = 50000
num_workers = 2
merge = "Y"
output_dir = "./output"
log_dir = "./logs"
max_retries = 3
retry_delay = 5
Usage
# Partition-based extraction
opq-export partition OWNER TABLE_NAME PARTITION_NAME -w 8 -m Y
# Whole-table extraction (no partition)
opq-export partition OWNER TABLE_NAME
# Date-range extraction
opq-export date OWNER TABLE_NAME TARGET_COL START_DATE END_DATE -w 4 -m N
# Example: credentials via environment variables
export OPQ_USER=myuser OPQ_PASSWORD=secret OPQ_DSN=host:1521/SERVICE
opq-export date SALES T_ORDER ORDER_DATE 20240101 20241231 -w 8
Main options:
| Option | Description | Default |
|---|---|---|
-w, --workers |
Number of parallel workers (8–16 recommended) | 2 |
-m, --merge |
Merge part files after extraction (Y/N) | Y |
--batch-size |
Fetch batch size | 50000 |
--output-dir |
Parquet output directory | ./output |
--log-dir |
Log directory | ./logs |
--max-retries / --retry-delay |
Network retry count / delay (seconds) | 3 / 5 |
Output file name: {OWNER}_{TABLE}[_{PARTITION}]_{yyyymmdd}.parquet
(with -m N, per-worker ..._partNN.parquet files are kept instead).
NUMBER precision handling
Fetching Oracle NUMBER the default way materializes values as Float64, which silently corrupts
anything beyond ~15.95 significant digits. For example, a NUMBER(19,4) value of
813224649805.2459 comes back as 813224649805.2458. The loss happens at fetch time, so no
downstream layer (Polars, Arrow, Parquet) can recover it.
This tool fetches the following NUMBER columns as strings and stores them as strings in Parquet, preserving values exactly (logic validated against production data):
scale > 9(many decimal places)precision > 15(beyond Float64 significant digits)scale < 0(negative scale)
Other NUMBER columns map to Decimal, and DATE/TIMESTAMP columns map to
Datetime(us, Asia/Seoul).
Python API
from oracle_parquet_exporter import DbConfig, ExportJob, ExportSettings, export_data
db = DbConfig(user="myuser", password="secret", dsn="host:1521/SERVICE")
job = ExportJob(mode="partition", owner="SALES", table_name="T_ORDER", partition_nm="P202401")
export_data(job, db, ExportSettings(num_workers=8, merge="Y"))
Notes
- Same-table workers share nothing and connect independently — size
-wto what your DB host and network can absorb.
License
MIT
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 oracle_pq_exporter-0.1.1.tar.gz.
File metadata
- Download URL: oracle_pq_exporter-0.1.1.tar.gz
- Upload date:
- Size: 21.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
19f48dd27fe7c311fa29bc090da82761bc7096799b130c0dc09fabba1f6220b3
|
|
| MD5 |
342f3b0bbc61b1f34920755f69aff8e8
|
|
| BLAKE2b-256 |
21adf1bbaf9f662331da9b0d2fbef9eaeb26d5b5d9914fac52620e0a71521169
|
File details
Details for the file oracle_pq_exporter-0.1.1-py3-none-any.whl.
File metadata
- Download URL: oracle_pq_exporter-0.1.1-py3-none-any.whl
- Upload date:
- Size: 21.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7678066168aa34e16aeaab3638aef66a19eb093d3e5a2d683215d25f6b7720f2
|
|
| MD5 |
6fdc261c09c396facd08d3405e1599d3
|
|
| BLAKE2b-256 |
e4eccfe6999c3976115080e6f7e645d483dcc8889ea9dac418468edf2250fa59
|