This document describes how to connect to Spanner Omni using PGAdapter. You configure PGAdapter to establish secure connections. PGAdapter supports plain text, Transport Layer Security (TLS), and mutual TLS (mTLS) security modes. These modes protect your data during transmission by providing varying levels of encryption and authentication. Each security mode requires specific client configurations to ensure data integrity and confidentiality.
You can run PGAdapter as a standalone process or integrate it directly into your
application. For interactive management and manual query execution, connect to
your database using standard PostgreSQL tools like psql. For building
automated applications, use PostgreSQL-compatible drivers like the
following:
pgxfor Gopsycopg3for Pythonnode-postgresfor Node.js
For code samples using some of these drivers, see sample code in this document.
Security modes
Spanner Omni PGAdapter supports three security modes, which define how communication is encrypted and authenticated between PGAdapter and the database. To use these modes, configure the client options as described in the following table:
| Security mode | Description |
|---|---|
| Plain text | Communication is not encrypted. |
| TLS | Communication is encrypted using Transport Layer Security (TLS). This mode requires that you add the Spanner Omni CA certificate to the default Java truststore. |
| mTLS | Communication is encrypted using mutual TLS (mTLS). This mode requires you to provide both a client certificate and a client private key. |
Run as a standalone process
Run PGAdapter as a standalone process for non-Java applications and for standard
PostgreSQL tools, for example, psql, when you need manual database
interaction. This approach decouples the proxy from your application lifecycle,
which lets you manage and update it independently. To start PGAdapter as a
standalone process, use the following configuration methods based on your
selected security mode.
Use plain text mode
To start PGAdapter in plain text communication mode, run the following command:
java -jar pgadapter.jar \
-d DATABASE_ID \
-e ENDPOINT \
-r "isExperimentalHost=true;usePlainText=true"
Replace the following:
DATABASE_ID: the ID of your Spanner Omni database, for example,test-db.ENDPOINT: the endpoint of your Spanner Omni instance, for example,localhost:15000.
Use TLS mode
Before you can start PGAdapter in-process in TLS mode with your Java application, you must add your Spanner Omni CA certificate to the default Java truststore. To add an existing CA certificate to the Java truststore, run the following command:
sudo keytool -import -trustcacerts -file ~/.spanner/certs/ca.crt -alias spanner-ca -keystore $JAVA_HOME/lib/security/cacerts
To start PGAdapter in TLS mode, run the following command:
java -Djavax.net.ssl.trustStore=$JAVA_HOME/lib/security/cacerts \
-Djavax.net.ssl.trustStoreType=JKS \
-jar pgadapter.jar \
-d DATABASE_ID \
-e ENDPOINT \
-r "isExperimentalHost=true"
Use mTLS mode
Before you can start PGAdapter in mTLS mode, you must ensure that your client key is in the in PKCS#8 format. To convert an existing key in your keystore into a PKCS#8 format, run the following command:
openssl pkcs8 -topk8 -in ~/.spanner/certs/client.key -out ~/.spanner/certs/java-client.key -nocrypt
Alternatively, when you create your client certificate and key using the
Spanner Omni CLI, provide the --generate-pkcs8-key parameter to generate
the key in PKCS#8 format.
To start PGAdapter in mTLS mode, run the following command:
java -Djavax.net.ssl.trustStore=$JAVA_HOME/lib/security/cacerts \
-Djavax.net.ssl.trustStoreType=JKS \
-jar pgadapter.jar \
-d DATABASE_ID \
-e ENDPOINT \
-r "isExperimentalHost=true;clientCertificate=PATH_TO_CLIENT_CERT;clientKey=PATH_TO_CLIENT_KEY"
Replace the following:
PATH_TO_CLIENT_CERT: the path to your client certificate file.PATH_TO_CLIENT_KEY: the path to your client key file.
Connect with psql
After you establish a connection using one of the preceding methods, run psql
to manage your database and execute queries. To connect to psql, use the
following command:
psql -h PG_HOST -p PG_PORT -U USERNAME -d DATABASE_ID
Replace the following:
PG_HOST: the hostname or IP address of the machine where PGAdapter is running. If running locally, uselocalhost.PG_PORT: the port number where PGAdapter is running. If you haven't specified a custom port, PGAdapter uses port5432by default.USERNAME: your PostgreSQL username.
Run in-process with your application
You can also start PGAdapter in-process with your application. To establish
security, configure the OptionsMetadata object.
Use plain text mode
For plain text communication, such as local development or testing in secure environments where encryption overhead is unnecessary, use the following configuration:
OptionsMetadata.Builder builder =
OptionsMetadata.newBuilder()
.setEndpoint("ENDPOINT")
.setUsePlainText();
ProxyServer server = new ProxyServer(builder.build());
server.startServer();
server.awaitRunning();
Use TLS mode
For plain text communication for use in environments such as local development or testing, use the following configuration:
sudo keytool -import -trustcacerts -file /.spanner/certs/ca.crt -alias spanner-ca -keystore $JAVA_HOME/lib/security/cacerts
To establish a TLS connection in-process, use the following configuration:
OptionsMetadata.Builder builder =
OptionsMetadata.newBuilder()
.setEndpoint("ENDPOINT");
ProxyServer server = new ProxyServer(builder.build());
server.startServer();
server.awaitRunning();
Use mTLS mode
Before you can start PGAdapter in-process mode with your Java application, you must ensure that your client key is in the PKCS#8 format. To convert an existing key in your keystore into a PKCS#8 format, run the following command:
openssl pkcs8 -topk8 -in ~/.spanner/certs/client.key -out ~/.spanner/certs/java-client.key -nocrypt
Alternatively, when you create your client certificate and key using the
Spanner Omni CLI, provide the --generate-pkcs8-key parameter to generate
the key in PKCS#8 format.
To establish an mTLS connection in-process, use this configuration:
OptionsMetadata.Builder builder =
OptionsMetadata.newBuilder()
.setEndpoint("ENDPOINT")
.useClientCert("PATH_TO_CLIENT_CERT", "PATH_TO_CLIENT_KEY");
ProxyServer server = new ProxyServer(builder.build());
server.startServer();
server.awaitRunning();
Sample code
This section provides sample code for connecting to a Spanner Omni database using the following PostgreSQL-compatible drivers:
Replace the following placeholder in your connection strings:
- PASSWORD: the password for your PostgreSQL user.
JDBC
You can connect to PGAdapter using the PostgreSQL JDBC driver as if
you were connecting to a PostgreSQL database. To connect and query a
table in a
Spanner Omni database, use the following sample code:
String jdbcUrl =
"jdbc:postgresql://PG_HOST:PG_PORT/DATABASE_ID";
try (Connection connection = DriverManager.getConnection(jdbcUrl)) {
// Example: Query data
try (Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM Singers")) {
System.out.println("Query Results:");
while (resultSet.next()) {
long id = resultSet.getLong("id");
String name = resultSet.getString("name");
System.out.printf("ID: %d, Name: %s\n", id, name);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
Go (pgx)
You can connect to PGAdapter using pgx as if you were connecting to a
PostgreSQL database. Use the following sample code:
// Database connection string
connString := "postgres://USERNAME:PASSWORD@PG_HOST:PG_PORT/DATABASE_ID?sslmode=disable"
ctx := context.Background()
// Connect to PGAdapter
conn, err := pgx.Connect(ctx, connString)
if err != nil {
log.Fatalf("Connection error: %s", err.Error())
}
defer conn.Close(ctx)
// Query all rows from the Singers table
rows, err := conn.Query(ctx, "SELECT id, name FROM Singers")
if err != nil {
log.Fatalf("Query error: %s", err.Error())
}
defer rows.Close()
// Iterate over the result set
fmt.Println("Singers Table Data:")
for rows.Next() {
var id int
var name string
if err := rows.Scan(&id, &name); err != nil {
log.Fatalf("Scan error: %s", err.Error())
}
fmt.Printf("ID: %d, Name: %s\n", id, name)
}
Python (psycopg2 or psycopg3)
You can connect to PGAdapter using psycopg2 or psycopg3 as if you were
connecting to a PostgreSQL database. To connect and query a table in a
Spanner Omni database, use the following sample code:
# psycopg2
import psycopg2
connection = psycopg2.connect(database="DATABASE_ID",
host="PG_HOST",
port=PG_PORT)
cursor = connection.cursor()
cursor.execute('SELECT * FROM Singers')
for row in cursor:
print(row)
cursor.close()
connection.close()
# psycopg3
import psycopg
with psycopg.connect("host=PG_HOST port=PG_PORT dbname=DATABASE_ID sslmode=disable") as conn:
conn.autocommit = True
with conn.cursor() as cur:
cur.execute("SELECT * FROM Singers")
for row in cur:
print(row)
Node.js (node-postgres)
You can connect to PGAdapter using
node-postgres as if you were
connecting to a PostgreSQL database. To connect and query a table in a
Spanner Omni database, use the following sample code:
const { Client } = require('pg');
const client = new Client({
host: 'PG_HOST',
port: PG_PORT,
database: 'DATABASE_ID',
});
await client.connect();
const res = await client.query("SELECT * FROM Singers");
console.log(res.rows);
await client.end();