PostgreSQL
Adding and configuring a PostgreSQL connection within Qualytics empowers the platform to build a symbolic link with your schema to perform operations like data discovery, visualization, reporting, syncing, profiling, scanning, anomaly surveillance, and more.
This documentation provides a step-by-step guide on how to add PostgreSQL as a source datastore in Qualytics. It covers the entire process, from initial connection setup to testing and finalizing the configuration. A PostgreSQL datastore can also be the place Qualytics writes to; see Use as an Enrichment Datastore.
By following these instructions, enterprises can ensure their PostgreSQL environment is properly connected with Qualytics, unlocking the platform's potential to help you proactively manage your full data quality lifecycle.
Let’s get started 🚀
PostgreSQL Setup Guide
Qualytics connects to PostgreSQL through the PostgreSQL JDBC driver. It reads the standard information schema to list the schemas the role can see, and uses standard JDBC metadata APIs for tables, columns, primary keys, and incremental fields.
Minimum PostgreSQL Permissions (Source Datastore)
| Permission | Purpose |
|---|---|
CONNECT ON DATABASE |
Allow the role to connect to the target database |
USAGE ON SCHEMA |
Access objects within the schema |
SELECT ON ALL TABLES IN SCHEMA |
Read data from all existing tables for profiling and scanning |
SELECT ON ALL SEQUENCES IN SCHEMA |
Read sequence metadata for incremental field detection |
Additional Permissions for an Enrichment Datastore
When PostgreSQL is used as an enrichment datastore, the following additional permissions are required so Qualytics can write its outputs, which are named from the linked source's prefix (for example _tpch_failed_checks):
| Permission | Purpose |
|---|---|
CREATE ON SCHEMA |
Create enrichment outputs |
INSERT ON ALL TABLES IN SCHEMA |
Write anomaly records, scan results, and check metrics |
UPDATE ON ALL TABLES IN SCHEMA |
Update enrichment records during rescans |
DELETE ON ALL TABLES IN SCHEMA |
Replace records when a run overwrites an output |
The database user that creates the enrichment tables owns them, so no additional grant is needed to change their structure during version migrations or to remove them during cleanup. PostgreSQL derives both from table ownership.
Example: Source Datastore Role (Read-Only)
Replace <database_name>, <schema_name>, and <password> with your actual values.
-- Create a dedicated read-only role
CREATE ROLE qualytics_read_role LOGIN PASSWORD '<password>';
-- Grant connection and schema access
GRANT CONNECT ON DATABASE <database_name> TO qualytics_read_role;
GRANT USAGE ON SCHEMA <schema_name> TO qualytics_read_role;
-- Grant read access to all existing and future tables
GRANT SELECT ON ALL TABLES IN SCHEMA <schema_name> TO qualytics_read_role;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA <schema_name> TO qualytics_read_role;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema_name> GRANT SELECT ON TABLES TO qualytics_read_role;
Example: Enrichment Database User (Read-Write)
-- Create a dedicated read-write role
CREATE ROLE qualytics_readwrite_role LOGIN PASSWORD '<password>';
-- Grant connection, schema access, and table creation
GRANT CONNECT ON DATABASE <database_name> TO qualytics_readwrite_role;
GRANT USAGE, CREATE ON SCHEMA <schema_name> TO qualytics_readwrite_role;
-- Grant full data manipulation on all existing and future tables
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA <schema_name> TO qualytics_readwrite_role;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema_name> GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO qualytics_readwrite_role;
Note
The information_schema, pg_catalog, pg_toast, and pg_internal schemas are left out of discovery, along with the temporary and statistics schemas PostgreSQL creates with a pg_ prefix. You do not need to restrict access to them manually.
Incremental Loading with Commit Timestamps
For optimal incremental load performance, it is recommended to enable track_commit_timestamp = on in your PostgreSQL configuration (postgresql.conf). This allows Qualytics to detect recently modified rows using transaction commit timestamps (pg_xact_commit_timestamp), reducing the amount of data read during incremental operations.
Fallback to a Full Load
If the source database does not track commit timestamps (track_commit_timestamp is off), an incremental load automatically falls back to a full load instead of failing. The operation completes normally, reads the full table, and reports a warning explaining that commit timestamp tracking is disabled on the source database. Because the fallback reads the full table, the operation does not record an incremental position for that load.
If Qualytics cannot verify the setting on the source database, the configured incremental behavior is kept unchanged.
To restore incremental loading:
- Set
track_commit_timestamp = oninpostgresql.confand restart the PostgreSQL server. The setting only takes effect after a restart. - Run a Sync operation on the datastore.
Note
PostgreSQL records commit timestamps only for transactions committed after the setting is enabled. Changes made while the setting was off are not tracked by commit timestamps; they are covered by the full loads that ran during that period.
Troubleshooting Common Errors
| Error | Likely Cause | Fix |
|---|---|---|
FATAL: password authentication failed |
Incorrect username or password | Verify the credentials and ensure the role exists with \du in psql |
FATAL: no pg_hba.conf entry for host |
The PostgreSQL server does not allow connections from the Qualytics host IP | Add the Qualytics IP to pg_hba.conf and reload the configuration |
permission denied for schema |
The role lacks USAGE on the target schema |
Run GRANT USAGE ON SCHEMA <schema_name> TO <role> |
permission denied for table |
The role lacks SELECT on one or more tables |
Run GRANT SELECT ON ALL TABLES IN SCHEMA <schema_name> TO <role> |
permission denied to create table |
The enrichment database user lacks CREATE on the schema |
Run GRANT CREATE ON SCHEMA <schema_name> TO <role> |
Detailed Troubleshooting Notes
Authentication Errors
The error FATAL: password authentication failed indicates that the credentials provided are incorrect or the role does not exist.
Common causes:
- Incorrect password: the password does not match the one set for the role.
- Role does not exist: the role name was misspelled or was never created.
- Authentication method mismatch: the
pg_hba.conffile requires a different authentication method (e.g.,md5vsscram-sha-256).
Note
PostgreSQL logs detailed authentication errors in the server log. Check pg_log or log_directory for the exact reason.
Permission Errors
The error permission denied for schema or permission denied for table means the role authenticated successfully but lacks the necessary grants.
Common causes:
- Missing
USAGEon schema: the role cannot access the schema even if table-level grants exist. - Missing
SELECTon tables: the role has schema access but cannot read specific tables. - Default privileges not set: new tables created after the initial grant are not automatically accessible. Use
ALTER DEFAULT PRIVILEGESto fix this.
Connection Errors
The error FATAL: no pg_hba.conf entry for host means the PostgreSQL server does not recognize the Qualytics host IP.
Common causes:
- IP not whitelisted: the Qualytics server IP is not listed in
pg_hba.conf. - Wrong database name: the
pg_hba.confentry restricts access to specific databases. - SSL required: the server requires SSL connections but the client is connecting without SSL.
Tip
Start by confirming credentials are valid (authentication errors), then verify schema/table permissions (permission errors), and finally check network connectivity (connection errors).
Add a Source Datastore
A source datastore is a storage location Qualytics connects to so it can profile, scan, and monitor data. Adding PostgreSQL as a source lets Qualytics query it through the PostgreSQL JDBC driver and run quality operations on the tables it discovers.
Before you start, review the Minimum PostgreSQL Permissions the connecting role needs.
Field reference
The Add Datastore page shows the sections below when PostgreSQL is selected. When reusing an existing connection, the Connection Properties and Secrets Management sections come already filled in and read-only: Qualytics has already validated those credentials, so you fill in only the Location and the General fields. To change a saved connection's credentials, edit the connection through the Manage Connections page; edits there apply to every datastore that reuses the connection.
Connection Properties
These fields define the PostgreSQL server Qualytics connects to and the role it connects with. They belong to the connection: when reusing an existing connection, they come already filled in and read-only.
| Field | Required | Type | Description |
|---|---|---|---|
| Connection Name | Text | A label for the saved connection (e.g., acme_postgres_reporting), so other datastores can reuse it later. |
|
| Host | Text | The hostname or address of the PostgreSQL server. | |
| Port | Number | The port the PostgreSQL instance listens on. Defaults to 5432. |
|
| User | Text | The PostgreSQL role Qualytics connects as. | |
| Password | Text | The password for that role. | |
| Max Parallelization | Number | Caps how many queries Qualytics runs at the same time against a datastore reached through this connection, from 1 to 100. Leave it empty for no explicit limit. The cap applies per datastore, so datastores sharing this connection each get their own allowance. Sits in the collapsed Advanced section of the form. See Max Parallelization. |
Secrets Management
This group is optional: use it only if you want Qualytics to pull credentials from a secrets manager instead of typing them into the form. Turn on HashiCorp Vault to show the fields below. Despite the label, any secrets manager that exposes a compatible REST API works, not only HashiCorp Vault; see Secrets Management. It also belongs to the connection: read-only when reusing an existing connection.
| Field | Required | Type | Description |
|---|---|---|---|
| Login URL | Text | The Vault endpoint Qualytics uses to authenticate (e.g., https://vault.example.com/v1/auth/approle/login). |
|
| Credentials Payload | Text | A JSON body containing the credentials Vault expects (e.g., {"role_id":"...","secret_id":"..."}). |
|
| Token JSONPath | Text | The JSONPath that extracts the client token from Vault's response. Defaults to $.auth.client_token. |
|
| Secret URL | Text | The Vault path where the secret is stored (e.g., https://vault.example.com/v1/secret/data/postgresql). |
|
| Token Header Name | Text | The HTTP header name used to send the token. Defaults to X-Vault-Token. |
|
| Data JSONPath | Text | The JSONPath that extracts the secret payload from Vault's response. Defaults to $.data. |
Location
Pick the database and the schema or schemas Qualytics should read from. You fill these in on both flows.
| Field | Required | Type | Description |
|---|---|---|---|
| Database | Option | The database to read from. Click the refresh icon to load the databases the role can connect to. | |
| Schema | Option | One or more schemas inside the selected database. Each schema you pick becomes its own Qualytics datastore. Defaults to public. |
One datastore per schema
Selecting more than one schema creates one source datastore per schema, named from the Name Template. See Multi-Schema Source Datastore Creation for details.
System schemas
The information_schema, pg_catalog, pg_toast, and pg_internal schemas are left out of discovery, along with the temporary and statistics schemas PostgreSQL creates with a pg_ prefix, so none of them appear in the list.
General
Common fields for every source datastore, shown below the Location section. You fill these in on both flows.
| Field | Required | Type | Description |
|---|---|---|---|
| Name Template | Text | Defines the naming pattern for each source datastore being created. Use {{schema}} as a placeholder that gets replaced with the actual schema name (e.g., pg_{{schema}} becomes pg_sales). Left empty, the datastore is named from the connection name and the schema. |
|
| Group | Option | Organizes your datastores under a shared group in the navigation tree. Select an existing group or create a new one with the Add New Group toggle. | |
| Teams | Option | Select one or more teams to associate with this source datastore. | |
| Initiate Sync | Checkbox | Automatically sync the datastore to detect containers and fields after creation. |
Below the form, an information banner lists the Public addresses and Private addresses your datastore connections originate from. Allow the ones that match your network setup through your security groups or firewall rules; see How Connections Work for where the addresses come from.
Steps
There are two ways to set up the connection: reuse a connection you already saved (Existing Connection) or create a new one from scratch (New Connection). The tabs below walk through each option; pick the one you want to follow. Each field is described in the Field reference above.
Step 1: Navigate to the Datastores page.
Step 2: Click the Add button at the top-right corner and choose Source .
Step 3: The Add Datastore page opens.
Step 4: Select New Connection next to the Search field.
Step 5: Select PostgreSQL from the connector grid. Use the search field to filter connectors by name.
Step 6: Fill in the Connection Properties: the Connection Name, Host, Port, User, and Password.
Step 7: Optionally, expand Secrets Management to retrieve credentials from a secrets manager.
Step 8: Fill in the Location fields (Database and Schema) and the General fields.
Step 9: Click Test connection. A success message confirms that the connection has been verified.
Info
The Finish and Next buttons stay disabled until the connection test passes on the current values. If the test fails, see Troubleshooting Common Errors.
Step 10: Click Finish to create the datastore.
Tip
To link a destination so Qualytics can write anomalies and metadata from the first operation, click Next instead of Finish. See Use as an Enrichment Datastore below.
Step 11: A success dialog confirms that your datastore has been added. Click Go to your datastore to open its page.
Step 1: Navigate to the Datastores page.
Step 2: Click the Add button at the top-right corner and choose Source .
Step 3: The Add Datastore page opens.
Step 4: Select Existing Connection next to the Search field.
Step 5: Select the saved PostgreSQL connection from the grid. Use the search field to filter connections by name. The Connection Properties and Secrets Management sections come already filled in and read-only.
Start a new connection from this one
To use the selected connection as a starting point for a brand-new connection instead, click the Duplicate as a new connection button on the selected connection. The form switches to New Connection mode with the connection's settings already filled in for you to adjust.
Step 6: Fill in the Location fields (Database and Schema) and the General fields. These are the only fields left to fill in.
Step 7: Click Test connection. A success message confirms that the connection has been verified.
Info
The Finish and Next buttons stay disabled until the connection test passes on the current values. If the test fails, see Troubleshooting Common Errors.
Step 8: Click Finish to create the datastore.
Tip
To link a destination so Qualytics can write anomalies and metadata from the first operation, click Next instead of Finish. See Use as an Enrichment Datastore below.
Step 9: A success dialog confirms that your datastore has been added. Click Go to your datastore to open its page.
Use as an Enrichment Datastore
An enrichment datastore is where Qualytics writes its findings: scan results, source record examples, remediation snapshots, materialized copies, and exports. Keep it on a dedicated schema or storage path, separate from the source data you govern. When a source datastore is linked to it, that source's writes land there. The destination can share the source's connection when those credentials have the required write access.
To use a PostgreSQL datastore as a destination, its connection user needs the write grants listed under Additional Permissions for an Enrichment Datastore. Then link it from the datastore that will write to it: open its Settings menu, click Enrichment , and either pick the PostgreSQL datastore under Use existing or create it there under Add new. The same choice is offered in the second step of the Add Datastore page. The flow is the same for every connector and is documented once, in Link an Enrichment Destination and Link Enrichment on Datastore Creation.
The rest of this section covers only what is specific to PostgreSQL: the fields you fill in when creating the destination on PostgreSQL from inside that dialog. For what Qualytics writes and how the outputs are named, see Enrichment Outputs.
Field reference
The Enrichment Destination step shows the sections below when PostgreSQL is selected. When reusing an existing connection, the Connection Properties and Secrets Management sections come already filled in and read-only.
Connection Properties
These fields define the PostgreSQL server Qualytics connects to and the role it connects with. They are the same fields as on the source datastore flow, repeated here so this section stands on its own.
| Field | Required | Type | Description |
|---|---|---|---|
| Connection Name | Text | A label for the saved connection (e.g., acme_postgres_enrichment), so other datastores can reuse it later. |
|
| Host | Text | The hostname or address of the PostgreSQL server. | |
| Port | Number | The port the PostgreSQL instance listens on. Defaults to 5432. |
|
| User | Text | The PostgreSQL role Qualytics connects as. | |
| Password | Text | The password for that role. | |
| Max Parallelization | Number | Caps how many queries Qualytics runs at the same time against a datastore reached through this connection, from 1 to 100. Leave it empty for no explicit limit. The cap applies per datastore, so datastores sharing this connection each get their own allowance. Sits in the collapsed Advanced section of the form. See Max Parallelization. |
Secrets Management
This group is optional: use it only if you want Qualytics to pull credentials from a secrets manager instead of typing them into the form. Turn on HashiCorp Vault to show the fields below. Despite the label, any secrets manager that exposes a compatible REST API works, not only HashiCorp Vault; see Secrets Management. It also belongs to the connection: read-only when reusing an existing connection.
| Field | Required | Type | Description |
|---|---|---|---|
| Login URL | Text | The Vault endpoint Qualytics uses to authenticate (e.g., https://vault.example.com/v1/auth/approle/login). |
|
| Credentials Payload | Text | A JSON body containing the credentials Vault expects (e.g., {"role_id":"...","secret_id":"..."}). |
|
| Token JSONPath | Text | The JSONPath that extracts the client token from Vault's response. Defaults to $.auth.client_token. |
|
| Secret URL | Text | The Vault path where the secret is stored (e.g., https://vault.example.com/v1/secret/data/postgresql). |
|
| Token Header Name | Text | The HTTP header name used to send the token. Defaults to X-Vault-Token. |
|
| Data JSONPath | Text | The JSONPath that extracts the secret payload from Vault's response. Defaults to $.data. |
Location
Where Qualytics writes the enrichment tables.
| Field | Required | Type | Description |
|---|---|---|---|
| Database | Option | The database Qualytics writes the enrichment tables into. Pick exactly one. | |
| Schema | Option | The schema inside that database where the enrichment tables are created. Make sure the connection user has write access to it. |
Warning
The account used by an enrichment datastore needs read and write access, while a source datastore needs only read access.
General
| Field | Required | Type | Description |
|---|---|---|---|
| Name | Text | The name of the new datastore. | |
| Teams | Option | Select one or more teams to associate with the datastore. |
Table prefix
Qualytics generates a Prefix from the source datastore's name for Scan, Remediation, and Materialize outputs. Each source sharing a destination needs a unique prefix. Export tables instead use the normalized datastore name. To change the prefix before creating the datastore, click Edit prefixes in the Prefix section of this step.
Write Behavior
Whether a Scan also writes a snapshot of the anomalous records, and whether Qualytics syncs and profiles the destination after each write.
| Field | Required | Type | Description |
|---|---|---|---|
| Remediation Strategy | Choice | Controls whether and how anomalous source records are written to the enrichment destination. None does not write them and is the default, Append adds the anomalous records after each scan, and Overwrite keeps only the records from the latest scan. | |
| Sync and profile the enrichment destination | Toggle | On by default. After this datastore writes to the destination, Qualytics syncs the destination and profiles the new outputs so they are ready to use without a manual step. |
Steps
Fill in these fields under Add new, either in the Enrichment Destination dialog of the datastore that will write here or in the second step of the Add Datastore page, then click Test connection and save. The steps are the same for every connector: see Link an Enrichment Destination or Link Enrichment on Datastore Creation.
API Payload Examples
This section provides detailed examples of API payloads to guide you through the process of creating and managing datastores using Qualytics API. Each example includes endpoint details, sample payloads, and instructions on how to replace placeholder values with actual data relevant to your setup.
Note
The database key must be present in the payload, but its value can be empty (""). When it is empty, the connection uses the postgres database. Omitting the key, or sending null, is rejected.
Creating a Source Datastore
This section provides sample payloads for creating a PostgreSQL datastore. Replace the placeholder values with actual data relevant to your setup.
Endpoint: /api/datastores (post)
{
"name": "your_datastore_name",
"teams": ["Public"],
"database": "postgresql_database",
"schema": "postgresql_schema",
"enrichment_only": false,
"trigger_sync": true,
"connection": {
"name": "your_connection_name",
"type": "postgresql",
"host": "postgresql_host",
"port": 5432,
"username": "postgresql_username",
"password": "postgresql_password"
}
}
# Step 1: Create a Connection
qualytics connections create \
--type postgresql \
--name "your_connection_name" \
--host ${DB_HOST} \
--port 5432 \
--username ${DB_USER} \
--password ${DB_PASSWORD}
# Step 2: Create a Source Datastore
qualytics datastores create \
--name "your_datastore_name" \
--connection-name "your_connection_name" \
--database your_database \
--schema public
Create an Enrichment Datastore
This section provides sample payloads for creating an enrichment datastore. Replace the placeholder values with actual data relevant to your setup.
Endpoint: /api/datastores (post)
{
"name": "your_datastore_name",
"teams": ["Public"],
"database": "postgresql_database",
"schema": "postgresql_schema",
"enrichment_only": true,
"connection": {
"name": "your_connection_name",
"type": "postgresql",
"host": "postgresql_host",
"port": 5432,
"username": "postgresql_username",
"password": "postgresql_password"
}
}
# Step 1: Create a Connection
qualytics connections create \
--type postgresql \
--name "your_connection_name" \
--host ${DB_HOST} \
--port 5432 \
--username ${DB_USER} \
--password ${DB_PASSWORD}
# Step 2: Create an Enrichment Datastore
qualytics datastores create \
--name "your_datastore_name" \
--connection-name "your_connection_name" \
--database your_database \
--schema your_enrichment_schema \
--enrichment-only
Link an Enrichment Destination
Use the provided endpoint to link a datastore to its enrichment destination:
Endpoint Details: /api/datastores/{datastore-id}/enrichment/{enrichment-id} (patch)