High-performance, stream-oriented toolkit for exporting PostgreSQL data (via pgx v5) into various text-based formats like CSV, HTML, or logs.
- High Performance: Minimal allocations through buffer reuse and "fast path" optimizations for common types.
- Universal Output: Stream data to any target using the
RowWriterinterface (compatible withencoding/csv). - Robust Type Support: Handles complex PostgreSQL types including
pgtype(UUID, Numeric, Intervals, Network addresses) andzeronull. - Internationalized: Configurable decimal separators (e.g., for European/Russian Excel), localized date/time formats, and custom boolean labels.
- Memory Efficient: Processes billions of rows in a streaming fashion without loading the entire result set into memory.
| Layer | Component | Responsibility |
|---|---|---|
| High Level | Write |
Simple one-liner for standard exports to any RowWriter. |
| Middle Level | Config |
Formatting logic (nulls, dates, decimals, truncation). |
| Core Level | Handler |
Row iteration, buffer management, and low-level scanning. |
go get github.com/mdigger/pgfmtThe easiest way to export rows using default settings:
w := csv.NewWriter(os.Stdout)
err := pgfmt.Write(w, rows)
w.Flush()Handle specific regional requirements like semicolon delimiters, comma decimals, and UTF-8 BOM:
cfg := pgfmt.Config{
DecimalSeparator: ",",
DateOnlyFormat: "02.01.2006",
}
// Write BOM for Excel auto-detection
os.Stdout.Write([]byte{0xEF, 0xBB, 0xBF})
w := csv.NewWriter(os.Stdout)
w.Comma = ';'
streamer := pgfmt.NewRowStreamer(cfg.Format, nil)
err := streamer(w, rows)
w.Flush()Use the core Handler to transform rows into any custom format (e.g., printing to console):
handler := pgfmt.NewHandler(func(values []any, dst []string) {
for i, v := range values {
dst[i] = fmt.Sprint(v)
}
}, nil)
err := handler(rows, func(row []string) error {
fmt.Printf("Data: %v\n", row)
return nil
})The library can track unrecognized types during export. This is useful for debugging and fine-tuning your configuration:
cfg := &pgfmt.Config{
Logger: slog.Default(),
}
// After export:
unknownTypes := cfg.GetUnknownTypes()-
String Truncation:
Configuses an$O(1)$ byte-length check before falling back to rune counting for truncation. -
Lock-Free Tracking: Type monitoring uses
atomic.Pointerandsync.Mapfor zero contention during parallel exports. -
Buffer Reuse:
Handlerallocates row buffers once per stream, significantly reducing GC pressure.