PostgreSQL Protocol Quick Start Guide
PostgreSQL Protocol Quick Start Guide
For developers integrating HeliosDB Nano with PostgreSQL clients
5-Minute Quick Start
1. Start the Server
cargo run --example postgres_server_extended2. Connect with psql
psql -h 127.0.0.1 -p 5432 -U postgres3. Run Queries
-- Simple queriesSELECT * FROM users;INSERT INTO users VALUES (4, 'David', 'david@example.com');
-- TransactionsBEGIN;UPDATE users SET email = 'newemail@example.com' WHERE id = 1;COMMIT;4. Test Prepared Statements (Python)
import psycopg2
conn = psycopg2.connect(host='127.0.0.1', port=5432, user='postgres')cur = conn.cursor()
# Prepared statement (automatic)cur.execute("SELECT * FROM users WHERE id = %s", (1,))print(cur.fetchone()) # (1, 'Alice', 'alice@example.com')Programmatic Usage
Starting a Server
use heliosdb_nano::{EmbeddedDatabase, protocol::postgres::PgServerBuilder};use std::sync::Arc;
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { // Create database let db = Arc::new(EmbeddedDatabase::new_in_memory()?);
// Setup schema db.execute("CREATE TABLE users (id INT, name TEXT)")?; db.execute("INSERT INTO users VALUES (1, 'Alice')")?;
// Start PostgreSQL server let server = PgServerBuilder::new() .address("127.0.0.1:5432".parse()?) .build(db);
server.serve().await?; Ok(())}Custom Configuration
use heliosdb_nano::protocol::postgres::{PgServerBuilder, AuthMethod};
let server = PgServerBuilder::new() .address("0.0.0.0:5432".parse()?) .auth_method(AuthMethod::CleartextPassword) // Or Trust, Md5, ScramSha256 .max_connections(50) .build(db);Client Examples
Python (psycopg2)
import psycopg2
# Connectconn = psycopg2.connect( host='127.0.0.1', port=5432, user='postgres')
# Simple querycur = conn.cursor()cur.execute("SELECT * FROM users")for row in cur.fetchall(): print(row)
# Prepared statement (automatic with placeholders)cur.execute("SELECT * FROM users WHERE id = %s", (1,))print(cur.fetchone())
# Transactionconn.autocommit = Falsecur.execute("INSERT INTO users VALUES (%s, %s)", (2, 'Bob'))conn.commit()
conn.close()Node.js (pg)
const { Client } = require('pg');
const client = new Client({ host: '127.0.0.1', port: 5432, user: 'postgres',});
await client.connect();
// Simple queryconst res = await client.query('SELECT * FROM users');console.log(res.rows);
// Prepared statementconst res2 = await client.query( 'SELECT * FROM users WHERE id = $1', [1]);console.log(res2.rows[0]);
// Transactionawait client.query('BEGIN');await client.query('INSERT INTO users VALUES ($1, $2)', [2, 'Bob']);await client.query('COMMIT');
await client.end();Java (JDBC)
import java.sql.*;
Connection conn = DriverManager.getConnection( "jdbc:postgresql://127.0.0.1:5432/postgres", "postgres", "");
// Simple queryStatement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery("SELECT * FROM users");while (rs.next()) { System.out.println(rs.getInt("id") + ": " + rs.getString("name"));}
// Prepared statementPreparedStatement pstmt = conn.prepareStatement( "SELECT * FROM users WHERE id = ?");pstmt.setInt(1, 1);ResultSet rs2 = pstmt.executeQuery();while (rs2.next()) { System.out.println(rs2.getString("name"));}
// Transactionconn.setAutoCommit(false);pstmt = conn.prepareStatement("INSERT INTO users VALUES (?, ?)");pstmt.setInt(1, 2);pstmt.setString(2, "Bob");pstmt.executeUpdate();conn.commit();
conn.close();Go (lib/pq)
package main
import ( "database/sql" _ "github.com/lib/pq")
func main() { db, _ := sql.Open("postgres", "host=127.0.0.1 port=5432 user=postgres sslmode=disable") defer db.Close()
// Simple query rows, _ := db.Query("SELECT * FROM users") defer rows.Close() for rows.Next() { var id int var name string rows.Scan(&id, &name) println(id, name) }
// Prepared statement stmt, _ := db.Prepare("SELECT * FROM users WHERE id = $1") defer stmt.Close() rows2, _ := stmt.Query(1) defer rows2.Close()
// Transaction tx, _ := db.Begin() tx.Exec("INSERT INTO users VALUES ($1, $2)", 2, "Bob") tx.Commit()}Supported Features
✅ Fully Supported
- Simple query protocol (SELECT, INSERT, UPDATE, DELETE)
- Extended query protocol (prepared statements)
- Transactions (BEGIN, COMMIT, ROLLBACK)
- Parameter binding ($1, $2, etc.)
- All common data types (INT, TEXT, FLOAT, BOOLEAN, JSON, etc.)
- Multiple concurrent connections
- Authentication (Trust, CleartextPassword)
⚠️ Partial Support
- MD5 authentication (basic implementation)
- SCRAM-SHA-256 (needs full SASL flow)
- pg_catalog system tables (minimal emulation)
❌ Not Yet Supported
- SSL/TLS encryption (coming in v2.1)
- COPY protocol (coming in v2.1)
- Binary result format (coming in v2.2)
- Listen/Notify (coming in v3.0)
- Cursors (coming in v3.0)
Common Use Cases
Case 1: Embedded Database with Network Access
Scenario: SQLite-style embedded database that also accepts network connections.
// Create embedded databaselet db = Arc::new(EmbeddedDatabase::new("./mydata.helio")?);
// Access locallydb.execute("CREATE TABLE logs (message TEXT)")?;
// Also expose via PostgreSQL protocoltokio::spawn(async move { let server = PgServerBuilder::new() .address("127.0.0.1:5432".parse()?) .build(db.clone()); server.serve().await});
// Local and remote access to same databaseCase 2: Testing PostgreSQL Applications
Scenario: Run PostgreSQL-compatible tests without Docker.
#[tokio::test]async fn test_my_app() { let db = Arc::new(EmbeddedDatabase::new_in_memory()?);
// Start server in background let db_clone = db.clone(); tokio::spawn(async move { let server = PgServerBuilder::new() .address("127.0.0.1:15432".parse().unwrap()) .build(db_clone); server.serve().await });
// Wait for server to start tokio::time::sleep(Duration::from_millis(100)).await;
// Connect with your application let conn = postgres::Config::new() .host("127.0.0.1") .port(15432) .user("postgres") .connect(postgres::NoTls)?;
// Run tests...}Case 3: Lightweight PostgreSQL Alternative
Scenario: Replace PostgreSQL for small deployments.
// Single binary, no installationlet db = Arc::new(EmbeddedDatabase::new("./data")?);
let server = PgServerBuilder::new() .address("0.0.0.0:5432".parse()?) .auth_method(AuthMethod::CleartextPassword) .max_connections(20) .build(db);
// Compatible with existing PostgreSQL tools// psql, pgAdmin, DBeaver, etc.server.serve().await?;Troubleshooting
Connection Refused
psql: could not connect to server: Connection refusedFix: Ensure server is running and listening on correct port.
# Check server logsRUST_LOG=debug cargo run --example postgres_server_extended
# Verify portlsof -i :5432netstat -an | grep 5432Authentication Failed
psql: FATAL: password authentication failed for user "postgres"Fix: Check authentication method.
// For testing, use Trust auth (no password).auth_method(AuthMethod::Trust)
// For production, use CleartextPassword or ScramSha256.auth_method(AuthMethod::CleartextPassword)Prepared Statement Not Found
ERROR: prepared statement "xyz" does not existFix: Ensure statement is prepared before use.
# Prepare firstcur.execute("PREPARE my_stmt AS SELECT * FROM users WHERE id = $1")
# Then executecur.execute("EXECUTE my_stmt(1)")
# Or use parameterized queries (automatic preparation)cur.execute("SELECT * FROM users WHERE id = %s", (1,))Type Mismatch
ERROR: Type conversion error: cannot convert STRING to INT4Fix: Ensure parameter types match column types.
# Wrongcur.execute("INSERT INTO users (id) VALUES (%s)", ("1",)) # String
# Correctcur.execute("INSERT INTO users (id) VALUES (%s)", (1,)) # IntegerPerformance Tips
1. Use Prepared Statements
Slow (re-parses every time):
for i in range(1000): cur.execute(f"SELECT * FROM users WHERE id = {i}")Fast (prepare once, reuse):
for i in range(1000): cur.execute("SELECT * FROM users WHERE id = %s", (i,))2. Batch Operations
Slow (1000 round-trips):
for user in users: cur.execute("INSERT INTO users VALUES (%s, %s)", (user.id, user.name))Fast (1 round-trip):
cur.executemany( "INSERT INTO users VALUES (%s, %s)", [(u.id, u.name) for u in users])3. Use Transactions
Slow (auto-commit each statement):
cur.execute("INSERT INTO users VALUES (1, 'Alice')")cur.execute("INSERT INTO users VALUES (2, 'Bob')")cur.execute("INSERT INTO users VALUES (3, 'Charlie')")Fast (batch in transaction):
conn.autocommit = Falsecur.execute("INSERT INTO users VALUES (1, 'Alice')")cur.execute("INSERT INTO users VALUES (2, 'Bob')")cur.execute("INSERT INTO users VALUES (3, 'Charlie')")conn.commit()4. Connection Pooling
Slow (new connection each request):
def handle_request(): conn = psycopg2.connect(...) # ... use connection ... conn.close()Fast (reuse connections):
from psycopg2 import pool
db_pool = pool.SimpleConnectionPool(1, 20, host='127.0.0.1', ...)
def handle_request(): conn = db_pool.getconn() # ... use connection ... db_pool.putconn(conn)Security Best Practices
1. Use Strong Authentication
// Development.auth_method(AuthMethod::Trust)
// Production.auth_method(AuthMethod::ScramSha256)2. Bind to Localhost Only
// Development (accessible from network).address("0.0.0.0:5432".parse()?)
// Production (local only).address("127.0.0.1:5432".parse()?)3. Limit Connections
.max_connections(50) // Prevent resource exhaustion4. Use SSH Tunnel for Remote Access
# On remote machinessh -L 5432:localhost:5432 remote-host
# Then connect to localhostpsql -h 127.0.0.1 -p 5432 -U postgres5. Always Use Prepared Statements
# Vulnerable to SQL injectioncur.execute(f"SELECT * FROM users WHERE name = '{user_input}'")
# Safe (parameter binding)cur.execute("SELECT * FROM users WHERE name = %s", (user_input,))Advanced Configuration
Custom Authentication
use heliosdb_nano::protocol::postgres::{AuthManager, AuthMethod};
// Create custom auth managerlet mut auth = AuthManager::new(AuthMethod::CleartextPassword);auth.add_user("alice".to_string(), "secret123".to_string());auth.add_user("bob".to_string(), "password456".to_string());
// Use custom authlet server = PgServerBuilder::new() .address("127.0.0.1:5432".parse()?) .auth_manager(auth) .build(db);Statement Cache Tuning
The prepared statement cache is per-connection with default limits:
- Max statements: 100
- Max portals: 50
These are hardcoded currently but can be made configurable:
// In your connection handler (future enhancement)let prepared_statements = PreparedStatementManager::with_capacity(1000, 500);Logging Configuration
# Set log level via environmentRUST_LOG=heliosdb_nano=debug cargo run
# Fine-grained controlRUST_LOG=heliosdb_nano::protocol::postgres=trace cargo run
# Production (errors only)RUST_LOG=error cargo runMigration from PostgreSQL
Step 1: Export Data
pg_dump -h postgres-host -U user dbname > dump.sqlStep 2: Clean Dump (Remove Unsupported Features)
# Remove unsupported PostgreSQL-specific syntaxsed -i '/CREATE EXTENSION/d' dump.sqlsed -i '/GRANT/d' dump.sql # RBAC not yet supportedsed -i '/ALTER DEFAULT/d' dump.sqlStep 3: Import to HeliosDB Nano
# Start HeliosDB Nano servercargo run --example postgres_server_extended
# Import dumppsql -h 127.0.0.1 -p 5432 -U postgres -f dump.sqlCompatibility Notes
Supported PostgreSQL Features:
- CREATE TABLE, DROP TABLE
- INSERT, SELECT, UPDATE, DELETE
- BEGIN, COMMIT, ROLLBACK
- Indexes (limited)
- JSON/JSONB
- Basic data types
Unsupported (Use Alternatives):
- Extensions → Native HeliosDB features
- Triggers → Application logic
- Views → Materialized views (coming)
- SERIAL → Manual ID generation
- Foreign keys → Application-level validation
Next Steps
- Try the example:
cargo run --example postgres_server_extended - Read the docs:
/docs/implementation/POSTGRES_PROTOCOL_IMPLEMENTATION_SUMMARY.md - Run tests:
cargo test postgres_extended_protocol_tests - Report issues: GitHub Issues
Happy coding! 🚀