PostgreSQL
PostgreSQL
This skill covers PostgreSQL database operations using the PgQuery command-line tool for executing DDL scripts, managing schemas, and running queries against PostgreSQL databases.
Instructions
When helping users with PostgreSQL operations, follow these guidelines:
-
Always Use PgQuery Tool: Use
Y:/CSharpDLLs/PgQuery/PgQuery.exefor all PostgreSQL operations. Never usepsqlor other PostgreSQL clients directly. -
Configuration Files: PostgreSQL connection details are stored in JSON configuration files (typically in
R:/JsonParams/). Common configs:x3rocs_db.json- x3rocs database connectiondw_db.json- Data warehouse connection
-
Command Format: Use
--configfor database connection and either--filefor SQL files or--sqlfor inline SQL:Y:/CSharpDLLs/PgQuery/PgQuery.exe --config "<config-path>" --file "<sql-file-path>" -
DDL Execution Order: When executing multiple DDL scripts that have dependencies:
- Create new tables before tables that reference them (foreign keys)
- Drop tables in reverse order (dependent tables first)
- Handle permission errors by checking if shared functions already exist
-
Error Handling: Common PostgreSQL errors:
42501: must be owner of function- Shared function already exists, skip recreation42P01: relation does not exist- Table/view doesn't exist, check dependencies23503: foreign key violation- Referenced table missing or data constraint violated
-
Schema Organization: Follow the project's schema organization:
x3schema - X3 integration data (customers, contacts, etc.)rocsschema - ROCS application data (price discounts, etc.)publicschema - Shared utilities and functions
Examples
Example 1: Execute a DDL Script
User: Run the contact.sql DDL to create the new contact table
Claude: I'll execute the contact.sql DDL using PgQuery:
- Use the x3rocs database connection
- Execute the file from the Postgresql directory
- Handle any permission errors for shared functions
[Runs: Y:/CSharpDLLs/PgQuery/PgQuery.exe --config "R:/JsonParams/x3rocs_db.json" --file "c:/Users/matthew.heath/Git/RocsMiddleware/Postgresql/contact.sql"]
Example 2: Execute Multiple Dependent DDL Files
User: We need to update the customer and contact tables - contact has a foreign key to customer
Claude: I'll execute these in the correct order:
1. First run customer.sql (parent table)
2. Then run contact.sql (child table with foreign key)
3. Report any errors
[Executes customer.sql first, then contact.sql]
Example 3: Run Ad-Hoc Query
User: Check if the x3.contact table exists
Claude: I'll query the PostgreSQL information schema:
[Runs: Y:/CSharpDLLs/PgQuery/PgQuery.exe --config "R:/JsonParams/x3rocs_db.json" --sql "SELECT table_name FROM information_schema.tables WHERE table_schema = 'x3' AND table_name = 'contact'"]
Reference Implementation Details
PgQuery Tool
Location: Y:/CSharpDLLs/PgQuery/PgQuery.exe or C:/Users/matthew.heath/Git/PgQuery
Purpose: Command-line tool for executing PostgreSQL queries and DDL scripts with JSON configuration support
Command Syntax
# Execute SQL file
Y:/CSharpDLLs/PgQuery/PgQuery.exe --config "<config-file>" --file "<sql-file>"
# Execute inline SQL
Y:/CSharpDLLs/PgQuery/PgQuery.exe --config "<config-file>" --sql "<sql-statement>"
Parameters:
--config(required): Path to PostgreSQL connection config JSON file--file: Path to SQL file to execute--sql: Inline SQL statement to execute- Must specify either
--fileor--sql, not both
Configuration File Format
Location: R:/JsonParams/*.json
{
"host": "rivsprod01",
"port": "5432",
"database": "x3rocs",
"username": "jordan",
"password": "your-password"
}
Common DDL Patterns
Creating Tables with Foreign Keys
Pattern: Always create parent tables before child tables
-- Parent table (customer.sql)
DROP TABLE IF EXISTS x3.customer CASCADE;
CREATE TABLE x3.customer (
customer_code VARCHAR(30) PRIMARY KEY,
customer_name VARCHAR(50) NOT NULL,
-- ... other fields
);
-- Child table (contact.sql) - references parent
DROP TABLE IF EXISTS x3.contact CASCADE;
CREATE TABLE x3.contact (
customer_code VARCHAR(30) NOT NULL,
contact_code VARCHAR(15) NOT NULL,
-- ... other fields
PRIMARY KEY (customer_code, contact_code),
FOREIGN KEY (customer_code) REFERENCES x3.customer(customer_code) ON DELETE CASCADE
);
Execution Order:
- Execute
customer.sqlfirst - Execute
contact.sqlsecond
Hash-Based Change Detection
Pattern: Use MD5 hash triggers for detecting data changes
-- Hash column in table
CREATE TABLE x3.customer (
customer_code VARCHAR(30) PRIMARY KEY,
-- ... data fields
x3_hash VARCHAR(32), -- MD5 hash for change detection
updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
-- Hash calculation function
CREATE OR REPLACE FUNCTION x3.update_customer_hash()
RETURNS TRIGGER AS $$
BEGIN
NEW.x3_hash := md5(
COALESCE(NEW.customer_code, '') || '|' ||
COALESCE(NEW.customer_name, '') || '|' ||
-- ... concatenate all fields for hashing
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Trigger to auto-calculate hash
CREATE TRIGGER trg_update_customer_x3hash
BEFORE INSERT OR UPDATE ON x3.customer
FOR EACH ROW
EXECUTE FUNCTION x3.update_customer_hash();
Key Points:
- MD5 is sufficient for change detection (not security)
- 32 characters (VARCHAR(32)) for MD5 hex output
- Exclude timestamp and hash fields from hash calculation
- Use
||for string concatenation with pipe delimiter
Upsert Functions
Pattern: Stored procedures for INSERT ... ON CONFLICT DO UPDATE
CREATE OR REPLACE FUNCTION x3.upsert_contact(
p_customer_code VARCHAR(30),
p_contact_code VARCHAR(15),
p_title VARCHAR(20),
-- ... other parameters
)
RETURNS VOID AS $$
BEGIN
INSERT INTO x3.contact (
customer_code, contact_code, title, -- ...
) VALUES (
p_customer_code, p_contact_code, p_title, -- ...
)
ON CONFLICT (customer_code, contact_code)
DO UPDATE SET
title = EXCLUDED.title,
-- ... update all fields
END;
$$ LANGUAGE plpgsql;
Troubleshooting
Permission Error: "must be owner of function"
Cause: Shared function already exists and is owned by another user
Solution: Skip recreating the shared function or comment it out in the DDL script
-- Comment out if function already exists
-- CREATE OR REPLACE FUNCTION x3.update_updated_column()
-- RETURNS TRIGGER AS $$
-- BEGIN
-- NEW.updated = CURRENT_TIMESTAMP;
-- RETURN NEW;
-- END;
-- $$ LANGUAGE 'plpgsql';
Foreign Key Violation on Table Creation
Cause: Referenced table doesn't exist yet
Solution: Execute DDL files in dependency order (parent tables first)
Cascade Drop Warning
Cause: DROP TABLE ... CASCADE will drop dependent objects
Solution: This is expected behavior. The CASCADE keyword is intentional for clean rebuilds.
Best Practices
- Always Use Absolute Paths: PgQuery requires absolute paths for
--fileparameter - Test on Non-Production First: Execute DDL on test databases before production
- Use CASCADE on DROP:
DROP TABLE IF EXISTS x3.customer CASCADEensures clean drops - Hash for Change Detection: Use MD5 hashing to track when data changes between systems
- Composite Primary Keys: Use format
(customer_code, contact_code)for multi-column keys - Foreign Key Cascades: Use
ON DELETE CASCADEfor parent-child relationships - Schema Namespacing: Keep tables organized in schemas (
x3.,rocs., etc.) - Timestamp Triggers: Auto-update
updatedcolumn with BEFORE UPDATE triggers
More from lawless-m/claude-skills
vram-gpu-oom
GPU VRAM management patterns for sharing memory across services (Ollama, Whisper, ComfyUI). OOM retry logic, auto-unload on idle, and service signaling protocol.
10rust
Rust development patterns, project setup, CLI/TUI applications, error handling, and system integration
1browserbridge
Real-time browser debugging and interaction via WebSocket bridge server on localhost:3141
1web-frontend
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.
1