PostgreSQL
The PostgreSQL connector (CONNECTOR_TYPE=postgres) translates GQL queries into
SQL and executes them against PostgreSQL. It requires a
mapping file that maps graph patterns to
relational tables.
1. Start PostgreSQL
docker network create memgql-net
docker run -d --rm \
--name postgres-dev \
--network memgql-net \
-p 5432:5432 \
--env POSTGRES_PASSWORD=postgres \
postgres:182. Seed data
docker exec -i postgres-dev psql -U postgres << 'SQL'
CREATE TABLE IF NOT EXISTS persons (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT
);
CREATE TABLE IF NOT EXISTS companies (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS knows (
from_id INT REFERENCES persons(id),
to_id INT REFERENCES persons(id)
);
CREATE TABLE IF NOT EXISTS works_at (
person_id INT REFERENCES persons(id),
company_id INT REFERENCES companies(id)
);
INSERT INTO persons (id, name, age) VALUES (1, 'Alice', 30), (2, 'Bob', 25);
INSERT INTO companies (id, name) VALUES (1, 'Acme Corp');
INSERT INTO knows (from_id, to_id) VALUES (1, 2);
INSERT INTO works_at (person_id, company_id) VALUES (1, 1);
SQL3. Start MemGQL
docker run --rm \
--name memgql \
--network memgql-net \
--stop-timeout 2 \
-p 7688:7688 \
--env CONNECTOR_TYPE=postgres \
--env POSTGRES_URL="host=postgres-dev user=postgres password=postgres dbname=postgres" \
--env MAPPING_FILE=/data/mapping.json \
--env BOLT_LISTEN_ADDR=0.0.0.0:7688 \
-v ./mapping.json:/data/mapping.json \
memgraph/memgql:latest4. Connect
mgconsole --port 76885. Query
MATCH (p:Person) RETURN p.name, p.age;MATCH (p:Person)-[:WORKS_AT]->(c:Company) RETURN p.name, c.name;MATCH (a:Person)-[:KNOWS]->(b:Person) RETURN a.name, b.name;For environment variables, see Reference.
TLS connections
For a cloud-hosted PostgreSQL, the connection mode goes in the connection
string as libpq’s sslmode=, and the connector declares what a URI cannot
express — which certificates to trust:
{
"name": "pg",
"type": "postgres",
"connection": {
"uri": "postgresql://user:pass@db.example.com:5432/app?sslmode=require",
"sslRootCert": "/etc/ssl/certs/customer-ca.pem",
"trustServerCertificate": false
}
}| Field | Description |
|---|---|
sslmode= (in uri) | libpq semantics, including the default: disable never attempts TLS, prefer uses it when it works, require makes it mandatory. |
sslRootCert | Path to a PEM bundle of CA certificates to trust instead of the system roots. Omit it to use the system trust store. |
trustServerCertificate | Encrypt without verifying the server’s identity. |
TLS is implemented with rustls, so there is no OpenSSL or other C library to install.
trustServerCertificate protects against passive eavesdropping but not
against an active man-in-the-middle, and it logs a warning on every use. It
exists because managed instances are routinely fronted by a certificate that
doesn’t match the hostname you dial; prefer sslRootCert where you can. The two
are mutually exclusive — a CA bundle plus “trust anything” is a contradiction
and is rejected.
Two more guardrails:
- Declaring TLS settings alongside
sslmode=disableis refused rather than silently connecting in plaintext. - Under
sslmode=prefer, a failed handshake retries in plaintext and warns — so enabling TLS support doesn’t strand a connector pointed at a server whose certificate you have no reason to trust. A connector that declaredsslRootCertortrustServerCertificatenever falls back; it named a trust anchor, so the failure is an error. Underrequireit is always an error.
These fields round-trip through EXPORT SCHEMA, so a dumped and reloaded
catalog reconnects with the same trust settings.
JSONB columns
A JSONB column can be mapped as typed properties at fixed paths, or passed
through as a document whose undeclared keys stay queryable:
"attributes": [
{ "name": "voltage", "column": "props", "path": "electrical.voltage", "type": "Double" },
{ "name": "props", "type": "Json" }
]MATCH (c:Component) WHERE c.voltage > 100 RETURN c.sku, c.props.rohs;Both forms push the extraction down to PostgreSQL, and a declared type makes
the comparison numeric instead of lexicographic. See
Schema File → JSON / JSONB columns.
Supported GQL features
| Feature | Postgres |
|---|---|
MATCH / WHERE / RETURN | ✓ |
Pattern-level WHERE (MATCH (n WHERE …)) | ✓ |
Multiple MATCH clauses in one query | ✓ |
OPTIONAL MATCH | ✓ |
WITH clause (chain query steps) | ✓ |
WITH DISTINCT / WITH … ORDER BY … LIMIT N | ✓ |
Multiple chained WITH steps | ✓ |
Pass a whole node through WITH n | ✓ |
Typed edge (a)-[r:R]->(b) | ✓ |
Untyped edge ()-[]->(b) | ✗ |
UNION / UNION ALL / UNION DISTINCT | ✓ |
Quantified path (){m,n} (bounded) | ✓ |
Whole-node RETURN n / whole-rel RETURN r | ✓ |
Map projections RETURN n {.a, .b} | ✓ |
IN list membership WHERE x IN [...] | ✓ |
STARTS WITH / ENDS WITH / CONTAINS | ✓ |
collect() aggregate | ✓ |
count, sum, avg, min, max | ✓ |
COUNT(DISTINCT …) | ✓ |
Arithmetic + - * / % | ✓ |
CASE WHEN … THEN … ELSE … END | ✓ |
COALESCE, NULLIF | ✓ |
INSERT (a {…}) RETURN a.x | ✓ |
DELETE | ✓ |
SET / REMOVE (property update / delete) | ✗ |
DETACH DELETE | ✗ |
INTERSECT / EXCEPT | ✗ |
Quantified path (){m,} (unbounded) | ✗ |
Shortest-path (ALL SHORTEST / SHORTEST k) | ✗ |
Known limitations
- Unbounded variable-length paths (
()-[*]->()) are rejected. Bound the depth ({1,5}) or run the query against a Cypher backend. FOR x IN [...](UNWIND-style) is rejected with an actionable error. Cypher backends (Memgraph, Neo4j) handle this syntax natively.- Path variables on variable-length patterns:
MATCH p = (a){1,3}(b) RETURN pis not supported. Drop thep =binding (or query a Cypher backend) andRETURNthe individual nodes / edges instead.