Postgres to BigQuery
Replicate Postgres tables into BigQuery, with CDC for deletes and clustered tables for inexpensive analysis.
The pipeline as it appears on the Pipeloom canvas. You can add transforms or triggers on the same canvas later.
What to know about Postgres into BigQuery
Choose xmin, CDC or a cursor
Xmin tracks inserts and updates with no cursor column but does not support regular views. CDC adds deletes and handles very large databases. Standard incremental needs a cursor column you choose.
Deleted rows need CDC
Only CDC carries deletes through to BigQuery. With xmin or a cursor column, a row deleted in Postgres remains in the BigQuery table, so reports built on it keep counting the row until you clear it.
JSON columns arrive as strings
Postgres json and jsonb are copied as strings, so they land as STRING in BigQuery. Use PARSE_JSON to get a JSON value and then JSON_VALUE to read keys.
The primary key drives clustering
BigQuery tables are clustered by _airbyte_extracted_at and the primary key. Tables with a primary key also support Append + Deduped, so they stay current without duplicate rows.
Example: query inside a jsonb column
SELECT
id,
JSON_VALUE(PARSE_JSON(payload), '$.customer.email') AS customer_email
FROM public.events
WHERE _airbyte_extracted_at >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY);Assumes a Postgres table events with a jsonb column payload, synced into a dataset named public. Names will differ in your setup.
Set it up
- Create the Postgres sourceAuthenticate with A read-only Postgres user (plus REPLICATION permission for CDC). You need: A read-only Postgres user that can read the tables you want to copy. For CDC only: logical replication turned on, a replication slot, and a publication with a replica identity on each table.
- Create the BigQuery destinationAuthenticate with A Google Cloud service account with the BigQuery User and BigQuery Data Editor roles, using a JSON key. You need: A Google Cloud project with BigQuery turned on, and a dataset to write to. Create it in the same location as the datasets you will join against. A service account with the BigQuery User and BigQuery Data Editor roles. For production loads, a Cloud Storage bucket for staging with an HMAC key, and the Storage Object Admin role for the service account.
- Connect them and pick streamsDraw the edge on the canvas and choose streams and sync modes. Postgres supports every sync mode; BigQuery supports every mode: full refresh overwrite, append and overwrite deduped, incremental append and incremental append deduped.
- Run, then scheduleThe first sync loads each stream in full. Then set the schedule.
What lands in BigQuery
- A table per stream with typed columns plus _airbyte_raw_id, _airbyte_generation_id, _airbyte_extracted_at and _airbyte_meta.
- Tables partitioned by day on _airbyte_extracted_at and clustered by that column and the primary key, so filtering on the partition column scans less data.
Type mapping
- object and array → JSON
- string → STRING
- integer → INT64
- number → NUMERIC
- timestamp with time zone → TIMESTAMP
- timestamp without time zone → DATETIME
- boolean → BOOL
When Postgres's schema changes
The destination tables are updated as the source changes. Namespaces map to BigQuery datasets, and invalid characters in names are replaced with underscores.
Streams you can sync
Includes Tables, Views, Materialized views and more. The full list is in the reference.
What it costs
A sync is metered at 1 credit per vCPU-minute, and a default sync uses 2.5 vCPUs. As an example, not a benchmark, a sync that takes 3 minutes uses 8 credits. Run 24 times a day, that is about 5,760 credits a month, more than Free's 1,500 but within Starter's 12,000 ($19 a month). Your own sync time decides the real figure, so try the calculator with your numbers.
Questions and fixes
Which replication method should I use?
Xmin is the simplest: it picks up inserts and updates without a cursor column, but it does not support regular views and is a poor fit under very heavy write traffic. Use CDC when you need deletes, when the database is 500 GB or more, or when a table has a primary key but no good cursor column. Standard incremental needs a cursor column you choose, such as updated_at.
Why do NaN and Infinity become null?
Infinity, -Infinity and NaN are not supported for double precision and numeric columns and are written as null.
Why does a sync fail with "Quota exceeded for concurrent script queries per project"?
That quota is shared with everything else in the project. Set Job Execution Project ID to a separate project so the connector's jobs count against that quota, or sync fewer streams at once. Data still lands in the dataset under Project ID.
Why does a load fail with "Fail to complete a load job in big query"?
BigQuery load jobs time out after 30 minutes of waiting, and two syncs loading into the same table at once are not supported. Make sure each table is written by one sync, and use an incremental mode to load less per sync.
Should I use the Cloud Storage bucket or standard inserts?
Use the bucket for production because it is faster for large volumes. Standard inserts need nothing to stage and suit small volumes and quick tests. Buckets using customer-managed encryption keys are not supported.
Sync Postgres to BigQuery today
Free forever, no card. Starter is $19 a month for three seats.