Skip to content

Database Branching

Database Branching

Applies to: HeliosDB Nano 4.31.1

Database branching in HeliosDB Nano works like Git branches for your data. Create isolated copies of your database for development, testing, or experimentation without duplicating storage.

One place the Git analogy breaks down: merge has no conflict detection. See Merging Branches before you plan a workflow around it.

Overview

Branches are lightweight, copy-on-write database snapshots that share unchanged data with their parent. This makes creating branches nearly instantaneous regardless of database size.

Creating Branches

From Current State

-- Create a branch from the current state of main
CREATE DATABASE BRANCH dev FROM main;
-- Or use AS OF NOW explicitly
CREATE DATABASE BRANCH dev FROM main AS OF NOW;

From a Point in Time

-- Branch from a specific timestamp
CREATE DATABASE BRANCH hotfix FROM main
AS OF TIMESTAMP '2025-01-15 00:00:00';
-- Branch from a specific transaction
CREATE DATABASE BRANCH feature FROM main
AS OF TRANSACTION 12345;

Switching Branches

SQL Syntax

USE BRANCH dev;

REPL Command

Terminal window
\use dev

Check Current Branch

Terminal window
\show branch

Listing Branches

REPL

Terminal window
\branches

SQL

SELECT * FROM pg_database_branches();

Output includes:

  • name - Branch name
  • parent - Parent branch name
  • created_at - Creation timestamp
  • commit_count - Number of commits
  • is_current - Whether this is the active branch

Merging Branches

MERGE DATABASE BRANCH does move rows — it is not a no-op. But it is last-writer-wins and performs no conflict detection: rows changed on both branches are overwritten by the merge, and no conflict is reported. Treat it as “apply the source branch’s rows onto the target”, not as a three-way merge.

Basic Merge

MERGE DATABASE BRANCH dev INTO main;

Options

OptionStatus
delete_branch_after = trueSupported — drops the source branch after a successful merge
conflict_resolution = 'fail' | 'target_wins' | 'branch_wins'Not implemented — the statement returns an error
-- Returns an error, by design:
MERGE DATABASE BRANCH dev INTO main
WITH (conflict_resolution = 'branch_wins');
MERGE BRANCH conflict_resolution is not implemented: merges are
last-writer-wins and conflicts are never detected. Remove the option to
merge with those semantics.

The option is parsed but the storage engine never detects a conflict or applies a strategy, so accepting it would be a silent no-op — you would ask for target_wins, get branch_wins, and be told the merge completed with zero conflicts. It now fails loudly instead of lying, and will stay that way until conflict detection exists.

Safe pattern for anything conflict-sensitive

Branch, validate, discard, then re-apply the validated SQL to main:

CREATE DATABASE BRANCH trial FROM main;
USE BRANCH trial;
-- experiment, inspect, verify
USE BRANCH main;
DROP DATABASE BRANCH trial;
-- then run the statements you actually validated against main

Dropping Branches

DROP DATABASE BRANCH dev;
-- With IF EXISTS to avoid errors
DROP DATABASE BRANCH IF EXISTS dev;

Use Cases

Development Environment

-- Create dev branch
CREATE DATABASE BRANCH dev FROM main;
USE BRANCH dev;
-- Make experimental changes
ALTER TABLE users ADD COLUMN preferences JSONB;
INSERT INTO users (name, preferences) VALUES ('Test', '{}');
-- If changes work, merge back (last-writer-wins: rows changed on both
-- branches are overwritten without a conflict report)
USE BRANCH main;
MERGE DATABASE BRANCH dev INTO main;

Feature Testing

-- Create branch for testing a feature
CREATE DATABASE BRANCH feature_x FROM main;
USE BRANCH feature_x;
-- Run integration tests with modified data
DELETE FROM users WHERE test_account = true;
-- Tests run here...
-- Discard when done (no merge needed)
USE BRANCH main;
DROP DATABASE BRANCH feature_x;

Point-in-Time Recovery

-- Create branch from before the incident
CREATE DATABASE BRANCH recovery FROM main
AS OF TIMESTAMP '2025-01-15 00:00:00';
-- Verify data is correct
USE BRANCH recovery;
SELECT COUNT(*) FROM orders;
-- If good, make it the new main
-- (requires careful planning in production)

How It Works

  1. Copy-on-Write: Branches share data pages with their parent. Only modified pages are copied.
  2. Snapshot Isolation: Each branch has its own transaction log and snapshot.
  3. Minimal Overhead: Creating a branch is O(1) regardless of data size.

Best Practices

  1. Name branches descriptively: feature_auth_refactor, bugfix_order_calc
  2. Keep branches short-lived: Merge or delete when done
  3. Test merges on a copy first: Create a test branch to verify merge results
  4. Use timestamps for recovery branches: Makes it clear what state you’re restoring

Limitations

  • No conflict detection on merge. MERGE DATABASE BRANCH is last-writer-wins; conflict_resolution= is not implemented and returns an error (see above).
  • Whole-database scope. A branch covers the whole database, not an individual table or schema.
  • Not a security boundary. Branching is storage-level copy-on-write isolation. It does not sandbox anything outside the database — an agent working on a branch can still make external side effects — and it is not an access-control mechanism.
  • Circular branch relationships are not allowed.
  • Branch names must be unique.