Documentation menu

Docs / Clients

PostgreSQL wire

Connect with psql, psycopg, node-postgres or pgjdbc. The wire protocol is PostgreSQL's; the SQL is not.

Available View Markdown
Works
Simple and extended protocols, prepared statements, portals with a row limit, pipelining, SCRAM-SHA-256 and SCRAM-SHA-256-PLUS, TLS.
Differs
Ordinary columns are described as text in prepared statements. The user name is ignored. There are no date or time types.
Missing
COPY, cancel requests, named (server-side) cursors, per-user authorization, \dt and other catalog-backed psql commands.
Operational impact
A connection activates the database it names. An idle connection is closed after 300 s.
Evidence
psql 16.2 and 17, psycopg 3.3.6, node-postgres 8.23.1 and pgjdbc 42.7.14, on source builds. node-postgres and pgjdbc were run without TLS.

Any node serves any database. Use the database name as dbname, any user name, and the database token as the password. Authentication is SCRAM-SHA-256; cleartext passwords are not offered. With TLS configured, a plaintext connection is refused with 28000.

On a loopback development node, drop the TLS options from each example.

#psql

Shell
PGPASSWORD=$TOKEN psql "host=n1.example.net port=7151 dbname=app user=app \
  sslmode=verify-full sslrootcert=ca/ca.crt channel_binding=require" \
  -c "CREATE TABLE items (id INTEGER PRIMARY KEY, name TEXT NOT NULL)" \
  -c "INSERT INTO items VALUES (1, 'first')" -c "SELECT * FROM items"

\dt, \copy and FETCH_COUNT are not supported. Use SHOW TABLES.

#psycopg 3

Python
import os, psycopg

with psycopg.connect(host="n2.example.net", port=7151, dbname="app", user="app",
                     password=os.environ["GAUNTLET_DATABASE_TOKEN"], sslmode="verify-full",
                     sslrootcert="ca/ca.crt") as conn:
    print(conn.execute("SELECT name FROM items WHERE id = %s", (1,)).fetchone())

#node-postgres

JavaScript
import fs from "node:fs";
import pg from "pg";

const client = new pg.Client({
  host: "n3.example.net", port: 7151, database: "app", user: "app",
  password: process.env.GAUNTLET_DATABASE_TOKEN,
  ssl: { ca: fs.readFileSync("ca/ca.crt", "utf8") },
});
await client.connect();
const { rows } = await client.query("SELECT name FROM items WHERE id = $1", [1]);
console.log(rows);
await client.end();
  • node-postgres sends true and false as untyped text, so they are stored as the text 'true' and 'false'.
  • A Buffer parameter is stored as a BLOB.
  • Unverified: the project's node-postgres suite runs on loopback without TLS. The ssl option above has no recorded test.

#JDBC (pgjdbc)

Java
String url = "jdbc:postgresql://n1.example.net:7151/app"
           + "?sslmode=verify-full&sslrootcert=ca/ca.crt";
Properties props = new Properties();
props.setProperty("user", "app");
props.setProperty("password", System.getenv("GAUNTLET_DATABASE_TOKEN"));
try (Connection conn = DriverManager.getConnection(url, props);
     PreparedStatement st = conn.prepareStatement("SELECT name FROM items WHERE id = ?")) {
    st.setLong(1, 1);
    try (ResultSet rs = st.executeQuery()) { while (rs.next()) System.out.println(rs.getString(1)); }
}
  • pgjdbc's default settings work, including binary transfer.
  • setQueryTimeout is not enforced: cancel requests are ignored.
  • Unverified: as with node-postgres, the JDBC suite runs without TLS.

#Driver behaviour

  • Column types in prepared statements are text for ordinary columns, because SQLite affinity does not fix a type. Stable expressions keep their type: CAST(x AS INTEGER) is int8, count(*) is int8. Cast when your driver needs a number.
  • Results are text unless the driver asks for binary. Binary works for int8, float8, text, bytea, numeric, bool, int2, int4, oid, float4, name, char and varchar.
  • numeric is not arbitrary precision. A numeric parameter becomes an INTEGER if it has no fraction and fits in 64 bits, otherwise the nearest REAL.
  • SET works only when it keeps the server's value: client_encoding UTF8, DateStyle ISO, TimeZone UTC, extra_float_digits 1 to 3. application_name can be set.
  • Per connection: 256 prepared statements, 64 portals, 4,096 parameters, 1 MiB of SQL per statement.

#ORMs

No ORM is certified. Most ORMs start by reading PostgreSQL catalogs that Gauntlet does not have, so expect their migrations and introspection to fail. ORM support through the libSQL protocol is a v1 roadmap item.

Search runs in your browser over an index of these pages. No query leaves this site.