PostgreSQL & PGWire
QuestDB supports the Postgres Wire Protocol (PGWire) for data-in.
For querying and data-out, QuestDB is compatible with PostgreSQL queries.
This means that you can use your favorite PostgreSQL client or driver with QuestDB.
For information querying and data-out, see the Querying & SQL Overview
The PostgreSQL storage model is fundamentally different than that of QuestDB.
As a result, some features that exists for Postgres do not exist in QuestDB.
Ingest examples
- psql
- Python
- Java
- NodeJS
- Go
- Rust
Create the table:
psql -h localhost -p 8812 -U admin -d qdb \
-c "CREATE TABLE IF NOT EXISTS t1 (name STRING, value INT);"
Insert row:
psql -h localhost -p 8812 -U admin -d qdb -c "INSERT INTO t1 VALUES('a', 42)"
Query back:
psql -h localhost -p 8812 -U admin -d qdb -c "SELECT * FROM t1"
Note that you can also run psql
from Docker without installing the client
locally:
docker run -it --rm --network=host -e PGPASSWORD=quest \
postgres psql ....
This example uses the psychopg3 adapter.
To install the
client library, use pip
:
python3 -m pip install "psycopg[binary]"
import psycopg as pg
import time
# Connect to an existing QuestDB instance
conn_str = 'user=admin password=quest host=127.0.0.1 port=8812 dbname=qdb'
with pg.connect(conn_str, autocommit=True) as connection:
# Open a cursor to perform database operations
with connection.cursor() as cur:
# Execute a command: this creates a new table
cur.execute('''
CREATE TABLE IF NOT EXISTS test_pg (
ts TIMESTAMP,
name STRING,
value INT
) timestamp(ts);
''')
print('Table created.')
# Insert data into the table.
for x in range(10):
# Converting datetime into millisecond for QuestDB
timestamp = time.time_ns() // 1000
cur.execute('''
INSERT INTO test_pg
VALUES (%s, %s, %s);
''',
(timestamp, 'python example', x))
print('Rows inserted.')
#Query the database and obtain data as Python objects.
cur.execute('SELECT * FROM test_pg;')
records = cur.fetchall()
for row in records:
print(row)
# the connection is now closed
package com.myco;
import java.sql.*;
import java.util.Properties;
class App {
public static void main(String[] args) throws SQLException {
Properties properties = new Properties();
properties.setProperty("user", "admin");
properties.setProperty("password", "quest");
properties.setProperty("sslmode", "disable");
final Connection connection = DriverManager.getConnection(
"jdbc:postgresql://localhost:8812/qdb", properties);
connection.setAutoCommit(false);
final PreparedStatement statement = connection.prepareStatement(
"CREATE TABLE IF NOT EXISTS trades (" +
" ts TIMESTAMP, date DATE, name STRING, value INT" +
") timestamp(ts);");
statement.execute();
try (PreparedStatement preparedStatement = connection.prepareStatement(
"INSERT INTO TRADES VALUES (?, ?, ?, ?)")) {
preparedStatement.setTimestamp(
1,
new Timestamp(io.questdb.std.Os.currentTimeMicros()));
preparedStatement.setDate(2, new Date(System.currentTimeMillis()));
preparedStatement.setString(3, "abc");
preparedStatement.setInt(4, 123);
preparedStatement.execute();
}
System.out.println("Done");
connection.close();
}
}
This example uses the pg
package which
allows for quickly building queries using Postgres wire protocol. Details on the
use of this package can be found on the
node-postgres documentation.
This example uses naive Date.now() * 1000
inserts for Timestamp types in
microsecond resolution. For accurate microsecond timestamps, the
process.hrtime.bigint()
call can be used.
"use strict"
const { Client } = require("pg")
const start = async () => {
const client = new Client({
database: "qdb",
host: "127.0.0.1",
password: "quest",
port: 8812,
user: "admin",
})
await client.connect()
const createTable = await client.query(
"CREATE TABLE IF NOT EXISTS trades (" +
" ts TIMESTAMP, date DATE, name STRING, value INT" +
") timestamp(ts);",
)
console.log(createTable)
let now = new Date().toISOString()
const insertData = await client.query(
"INSERT INTO trades VALUES($1, $2, $3, $4);",
[now, now, "node pg example", 123],
)
await client.query("COMMIT")
console.log(insertData)
for (let rows = 0; rows < 10; rows++) {
// Providing a 'name' field allows for prepared statements / bind variables
now = new Date().toISOString()
const query = {
name: "insert-values",
text: "INSERT INTO trades VALUES($1, $2, $3, $4);",
values: [now, now, "node pg prep statement", rows],
}
await client.query(query)
}
await client.query("COMMIT")
const readAll = await client.query("SELECT * FROM trades")
console.log(readAll.rows)
await client.end()
}
start()
.then(() => console.log("Done"))
.catch(console.error)
This example uses the pgx driver and toolkit for PostgreSQL in Go. More details on the use of this toolkit can be found on the GitHub repository for pgx.
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/jackc/pgx/v4"
)
var conn *pgx.Conn
var err error
func main() {
ctx := context.Background()
conn, _ = pgx.Connect(ctx, "postgresql://admin:quest@localhost:8812/qdb")
defer conn.Close(ctx)
// text-based query
_, err := conn.Exec(ctx,
("CREATE TABLE IF NOT EXISTS trades (" +
" ts TIMESTAMP, date DATE, name STRING, value INT" +
") timestamp(ts);"))
if err != nil {
log.Fatalln(err)
}
// Prepared statement given the name 'ps1'
_, err = conn.Prepare(ctx, "ps1", "INSERT INTO trades VALUES($1,$2,$3,$4)")
if err != nil {
log.Fatalln(err)
}
// Insert all rows in a single commit
tx, err := conn.Begin(ctx)
if err != nil {
log.Fatalln(err)
}
for i := 0; i < 10; i++ {
// Execute 'ps1' statement with a string and the loop iterator value
_, err = conn.Exec(
ctx,
"ps1",
time.Now(),
time.Now().Round(time.Millisecond),
"go prepared statement",
i + 1)
if err != nil {
log.Fatalln(err)
}
}
// Commit the transaction
err = tx.Commit(ctx)
if err != nil {
log.Fatalln(err)
}
// Read all rows from table
rows, err := conn.Query(ctx, "SELECT * FROM trades")
fmt.Println("Reading from trades table:")
for rows.Next() {
var name string
var value int64
var ts time.Time
var date time.Time
err = rows.Scan(&ts, &date, &name, &value)
fmt.Println(ts, date, name, value)
}
err = conn.Close(ctx)
}
The following example shows how to use parameterized queries and prepared statements using the rust-postgres client.
use postgres::{Client, NoTls, Error};
use chrono::{Utc};
use std::time::SystemTime;
fn main() -> Result<(), Error> {
let mut client = Client::connect("postgresql://admin:quest@localhost:8812/qdb", NoTls)?;
// Basic query
client.batch_execute(
"CREATE TABLE IF NOT EXISTS trades ( \
ts TIMESTAMP, date DATE, name STRING, value INT \
) timestamp(ts);")?;
// Parameterized query
let name: &str = "rust example";
let val: i32 = 123;
let utc = Utc::now();
let sys_time = SystemTime::now();
client.execute(
"INSERT INTO trades VALUES($1,$2,$3,$4)",
&[&utc.naive_local(), &sys_time, &name, &val],
)?;
// Prepared statement
let mut txn = client.transaction()?;
let statement = txn.prepare("INSERT INTO trades VALUES ($1,$2,$3,$4)")?;
for value in 0..10 {
let utc = Utc::now();
let sys_time = SystemTime::now();
txn.execute(&statement, &[&utc.naive_local(), &sys_time, &name, &value])?;
}
txn.commit()?;
println!("import finished");
Ok(())
}
For query examples, see Query & SQL Overview.
Query examples
For full query details and examples, see the PostgreSQL section in the Query & SQL Overview.
Compatibility
List of supported features
- Querying (all types expect
BLOB
) - Prepared statements with bind parameters (check for specific libraries below)
INSERT
statements with bind parametersUPDATE
statements with bind parameters- DDL execution
- Batch inserts with
JDBC
- Plain authentication
List of supported connection properties
Name | Example | Description |
---|---|---|
database | qdb | Should be set to any value for example qdb , database name is ignored, QuestDB does not have database instance name |
user | admin | User name configured in pg.user or pg.readonly.user property in server.conf . Default value is admin |
password | quest | Password from pg.password or pg.readonly.password property in server.conf . Default value is quest |
options | -c statement_timeout=60000 | The only supported option is statement_timeout . It specifies maximum execution time in milliseconds for SELECT or UPDATE statement |
List of unsupported features
- SSL
- Remote file upload (
COPY
fromstdin
) DELETE
statementsBLOB
transfer
Recommended third party tools
The following list of third party tools includes drivers, clients or utility CLIs that our team has tested extensively. Picking an item from it will guarantee that your code will work with QuestDB.
We recognize that our community might value some features more than others. This is why we encourage you to open an issue on GitHub if you think we are missing something important for your workflow.
CLIs
PSQL 12
Support for SELECT
, INSERT
, UPDATE
, CREATE
, DROP
, TRUNCATE
.