Skip to content

feat: Support PostgreSQL export for export-metadata#19698

Open
JWuCines wants to merge 2 commits into
apache:masterfrom
JWuCines:feature/support_postgres_export_metadata
Open

feat: Support PostgreSQL export for export-metadata#19698
JWuCines wants to merge 2 commits into
apache:masterfrom
JWuCines:feature/support_postgres_export_metadata

Conversation

@JWuCines

Copy link
Copy Markdown
Contributor

Description

The export-metadata tool currently only supports exporting from Derby metadata stores. This PR adds support for exporting from PostgreSQL by implementing a generic JDBC-based exportTable in SQLMetadataConnector, which PostgreSQL (and any future connector) inherits automatically.

Added generic JDBC export in SQLMetadataConnector

Implemented exportTableWithJdbc, a protected method that exports any table to CSV using standard JDBC. Binary/BLOB columns (including PostgreSQL BYTEA) are hex-encoded, booleans are written as true/false strings, and string values containing commas, quotes, \n, or \r are properly CSV-escaped per RFC 4180. The base exportTable delegates to this method, while DerbyConnector continues to override it with Derby's native SYSCS_EXPORT_TABLE.

Updated ExportMetadata for PostgreSQL compatibility

  • Table names are no longer unconditionally uppercased; uppercasing is now applied only for Derby (detected via jdbc:derby URI prefix), since PostgreSQL uses lowercase table names.
  • Updated the @Command description to mention PostgreSQL support.

Added unit tests

Four new tests in SQLMetadataConnectorTest exercise the generic JDBC export path via TestDerbyConnector.exportTableGeneric():

  • testExportTable — verifies hex-encoded BLOBs and true/false boolean strings
  • testExportTableWithSpecialCharacters — verifies CSV quoting/escaping for commas, double quotes, and plain values
  • testExportTableWithNullValues — verifies NULL columns produce empty CSV fields
  • testExportTablePreservesAllColumns — verifies all columns (including nullable trailing columns like used_status_last_updated) are exported

Updated documentation

  • export-metadata.md — removed the Derby-only limitation, added a "PostgreSQL" section under "Running the tool" with the required -Ddruid.extensions.loadList and -Ddruid.metadata.storage.type flags, clarified _raw.csv description
  • metadata-migration.md — updated intro and export tool reference to include PostgreSQL
  • deep-storage-migration.md — updated export tool reference, added note about no running processes needed when migrating from PostgreSQL

Release note

The export-metadata tool now supports exporting from PostgreSQL metadata stores in addition to Derby. When exporting from PostgreSQL, pass -Ddruid.extensions.loadList='["postgresql-metadata-storage"]' -Ddruid.metadata.storage.type=postgresql on the command line along with the appropriate --connectURI.


Key changed/added classes in this PR
  • SQLMetadataConnector — added exportTable and exportTableWithJdbc for generic JDBC CSV export
  • ExportMetadata — added isDerby() helper, conditional table name casing
  • TestDerbyConnector — added exportTableGeneric() to test the generic JDBC path
  • SQLMetadataConnectorTest — added 4 export tests

This PR has:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.

@JWuCines
JWuCines force-pushed the feature/support_postgres_export_metadata branch from b69c7a6 to baa141e Compare July 16, 2026 13:53

@FrankChen021 FrankChen021 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity Findings
P0 0
P1 2
P2 1
P3 0
Total 3

Reviewed 7 of 7 changed files.

Found three export-migration risks: buffered PostgreSQL reads, dropped current-schema segment columns, and lost CSV escaping during rewrites.


This is an automated review by Codex GPT-5.6-Sol

{
retryWithHandle(
(HandleCallback<Void>) handle -> {
try (Statement stmt = handle.getConnection().createStatement();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Stream PostgreSQL results instead of buffering the table

PostgreSQL JDBC buffers the complete ResultSet by default. This plain Statement runs on an auto-commit connection and never sets a fetch size; PostgreSQL cursor-based fetching requires autoCommit=false and a positive fetch size. Exporting a production segments table can therefore exhaust heap before rows are written. Run the query in a transaction and apply getStreamingFetchSize().

final String exportTableName = isDerby() ? StringUtils.toUpperCase(tableName) : tableName;
dbConnector.exportTable(
StringUtils.toUpperCase(tableName),
exportTableName,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Preserve current segment columns in the importable CSV

The generic raw export preserves all columns, but run() immediately passes the file through rewriteSegmentsExport, which emits only columns 0 through 8. Current segment tables also contain required used_status_last_updated and additional fingerprint/upgrade columns. A PostgreSQL migration therefore loses those values, and the documented COPY into a freshly created current table fails because used_status_last_updated is NOT NULL without a default. The new test covers only the raw file; the final CSV and import commands need to preserve the current schema.

} else {
final String val = rs.getString(i);
if (val != null) {
if (val.contains(",") || val.contains("\"") || val.contains("\n") || val.contains("\r")) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve CSV escaping through the rewrite stage

Although the raw writer correctly quotes commas and double quotes, each ExportMetadata rewrite parses those fields and concatenates them into the final CSV without re-escaping. Legal datasource and metadata identifiers may contain these characters, so the final importable file gains extra columns or malformed quoting even though the raw file is valid. Use a CSV writer or shared escaping routine in the rewrite stage and add an end-to-end test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants