Skip to content

feat(elt-pipelines): Proposal postgresql sources pipeline#396

Open
bashanlam wants to merge 4 commits into
mainfrom
ingest-fase-proposal-db
Open

feat(elt-pipelines): Proposal postgresql sources pipeline#396
bashanlam wants to merge 4 commits into
mainfrom
ingest-fase-proposal-db

Conversation

@bashanlam

@bashanlam bashanlam commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

ref #340

Description
This PR implements the ingestion elt-pipelines to move proposal application data into the lakehouse. It establishes the end-to-end data flow by extracting data from PostgreSQL sources and loading it into the Iceberg destination.

Scope of Work

  • Build PostgreSQL DB extraction pipeline: Implemented extraction logic for PostgreSQL database sources using the elt framework.
  • Configure Iceberg destination loading: Configured the target pipeline to securely and efficiently stream data into the Apache Iceberg lakehouse destination.

Summary by CodeRabbit

  • New Features
    • Added a PostgreSQL data extraction pipeline supporting configurable database connections.
    • Extract data from multiple specified tables in a single pipeline run.
    • Stream large tables in manageable batches for improved processing efficiency.
    • Automatically preserve table schemas and convert PostgreSQL types for downstream use.
    • Handle empty tables and inconsistent or complex column values safely.

Implement the elt-pipeline proposal to extract data from the
PostgreSQL database source and load it into the Iceberg destination.
@bashanlam
bashanlam requested a review from martyngigg July 16, 2026 16:41
@bashanlam
bashanlam requested a review from a team as a code owner July 16, 2026 16:41
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3fe4771e-ea75-477d-b9ca-af1c6a5a7925

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Changes

Adds environment-configured PostgreSQL extraction, schema mapping to PyArrow, chunked table streaming, and pipeline resources for each configured table.

Postgres extraction pipeline

Layer / File(s) Summary
Postgres configuration contract
elt-pipelines/fase/ingest/fase/proposal/proposal.py
Defines PostgreSQL settings, parses comma-separated target tables, and builds the connection URI.
Postgres-to-Arrow extraction
elt-pipelines/fase/ingest/fase/utils/postgres.py
Inspects PostgreSQL schemas, maps types to PyArrow, streams chunks, normalises values, and preserves schemas for empty tables.
Proposal resource wiring
elt-pipelines/fase/ingest/fase/proposal/proposal.py
Initialises the extractor and yields replace-mode resources with table-specific callables and disabled watermarking.

Suggested reviewers: martyngigg

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: adding a PostgreSQL-backed proposal ingestion pipeline.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
elt-pipelines/fase/ingest/fase/utils/postgres.py (2)

18-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Format multiple statements on separate lines.

Putting multiple statements on a single line violates PEP 8 guidelines and reduces readability.

🧹 Proposed fix
-        if 'int' in t: return 'bigint'
-        if 'bool' in t: return 'bool'
-        if 'json' in t or 'uuid' in t: return 'text' 
-        if 'float' in t or 'numeric' in t or 'double' in t: return 'double'
-        if 'timestamp' in t: return 'timestamp'
-        if 'date' in t: return 'date'
+        if 'int' in t:
+            return 'bigint'
+        if 'bool' in t:
+            return 'bool'
+        if 'json' in t or 'uuid' in t:
+            return 'text' 
+        if 'float' in t or 'numeric' in t or 'double' in t:
+            return 'double'
+        if 'timestamp' in t:
+            return 'timestamp'
+        if 'date' in t:
+            return 'date'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@elt-pipelines/fase/ingest/fase/utils/postgres.py` around lines 18 - 23,
Reformat the type-mapping conditionals in the visible utility function so each
if statement and its return statement appears on separate lines, preserving the
existing ordering and return values.

Source: Linters/SAST tools


39-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Format multiple statements on separate lines.

Placing the if/elif/else condition and assignment on the same line makes the code harder to read and violates PEP 8 formatting standards.

🧹 Proposed fix
-            if pq_type_str == 'bigint': pa_type = pa.int64()
-            elif pq_type_str == 'bool': pa_type = pa.bool_()
-            elif pq_type_str == 'double': pa_type = pa.float64()
-            elif pq_type_str == 'timestamp': pa_type = pa.timestamp('us')
-            elif pq_type_str == 'date': pa_type = pa.date32()
-            else: pa_type = pa.string()
+            if pq_type_str == 'bigint':
+                pa_type = pa.int64()
+            elif pq_type_str == 'bool':
+                pa_type = pa.bool_()
+            elif pq_type_str == 'double':
+                pa_type = pa.float64()
+            elif pq_type_str == 'timestamp':
+                pa_type = pa.timestamp('us')
+            elif pq_type_str == 'date':
+                pa_type = pa.date32()
+            else:
+                pa_type = pa.string()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@elt-pipelines/fase/ingest/fase/utils/postgres.py` around lines 39 - 44,
Reformat the type-mapping conditional in the visible PostgreSQL utility code so
each if/elif/else condition and its pa_type assignment appear on separate
properly indented lines, without changing the existing type mappings or fallback
behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@elt-pipelines/fase/ingest/fase/proposal/proposal.py`:
- Around line 31-37: Update the connection_uri method to URL-encode the stripped
username and password with urllib.parse.quote_plus before interpolating them
into the URI, while preserving the existing driver, host, port, and database
formatting.

In `@elt-pipelines/fase/ingest/fase/utils/postgres.py`:
- Around line 81-84: Update the empty-result branch around has_data so it yields
a zero-row PyArrow table using target_schema, rather than constructing dummy_row
and a one-row DataFrame of None values. Preserve the existing behavior for
sources containing data.

---

Nitpick comments:
In `@elt-pipelines/fase/ingest/fase/utils/postgres.py`:
- Around line 18-23: Reformat the type-mapping conditionals in the visible
utility function so each if statement and its return statement appears on
separate lines, preserving the existing ordering and return values.
- Around line 39-44: Reformat the type-mapping conditional in the visible
PostgreSQL utility code so each if/elif/else condition and its pa_type
assignment appear on separate properly indented lines, without changing the
existing type mappings or fallback behavior.
🪄 Autofix (Beta)

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf9112f6-fe76-4ea3-a46b-f6dde01e1c47

📥 Commits

Reviewing files that changed from the base of the PR and between 86989d4 and be2ad19.

📒 Files selected for processing (2)
  • elt-pipelines/fase/ingest/fase/proposal/proposal.py
  • elt-pipelines/fase/ingest/fase/utils/postgres.py

Comment thread elt-pipelines/fase/ingest/fase/proposal/proposal.py
Comment thread elt-pipelines/fase/ingest/fase/utils/postgres.py Outdated
@bashanlam bashanlam changed the title feat: extract Postgres source and load to Iceberg feat(elt-pipelines): Proposal PostgreSQL sources pipeline Jul 16, 2026
@bashanlam bashanlam changed the title feat(elt-pipelines): Proposal PostgreSQL sources pipeline feat(elt-pipelines): Proposal postgresql sources pipeline Jul 16, 2026
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Fixes Applied Successfully

Fixed 2 file(s) based on 2 unresolved review comments.

Files modified:

  • elt-pipelines/fase/ingest/fase/proposal/proposal.py
  • elt-pipelines/fase/ingest/fase/utils/postgres.py

Commit: 62abf0d9322a685b52826a077284ef6ed2aab4f2

The changes have been pushed to the ingest-fase-proposal-db branch.

Time taken: 2m 22s

coderabbitai Bot and others added 3 commits July 16, 2026 16:50
Fixed 2 file(s) based on 2 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

@WHTaylor WHTaylor left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A lot of this was supposed to be covered by the sqldatabase package. If that didn't work for this, it'd probably be ideal if we could fix it instead of reimplementing a lot of the same functionality.

Comment on lines +41 to +46
class Extract(BaseExtract):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Instantiate pipeline config and pass it directly to the generic utility extractor
self.postgres_config = PipelinePostgresConfig()
self.extractor = PostgresExtractor(self.postgres_config)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Extract classes are instantiated in such a way that doing this:

Suggested change
class Extract(BaseExtract):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Instantiate pipeline config and pass it directly to the generic utility extractor
self.postgres_config = PipelinePostgresConfig()
self.extractor = PostgresExtractor(self.postgres_config)
class Extract(BaseExtract):
config_cls = PipelinePostgresConfig
def __init__(self, config: PipelinePostgresConfig):
super().__init__(config)
self.extractor = PostgresExtractor(config)

Will set the config up correctly, and make it so the environment variables are prefixed with the job name (so they'd be PROPOSAL__HOST, PROPOSAL__PORT etc. at the moment).


The way it's set up at the moment isn't ideal because it doesn't type check to access fields on the config. For now it means we'd have to store self.target_tables = config.target_tables in the initializer, rather than accessing them through the config later

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants