Skip to content

SPOC-489: Add bidirectional large object migration between native and lolor storage#34

Open
danolivo wants to merge 1 commit intomainfrom
spoc-489
Open

SPOC-489: Add bidirectional large object migration between native and lolor storage#34
danolivo wants to merge 1 commit intomainfrom
spoc-489

Conversation

@danolivo
Copy link
Copy Markdown
Contributor

@danolivo danolivo commented Apr 2, 2026

Adds lolor.migrate_from_native() and lolor.migrate_to_native() SQL functions
that move large objects between PostgreSQL's native pg_catalog storage and
lolor's replication-compatible tables, preserving OIDs, owners, ACLs, and data.

Design decisions

  • Native → lolor migration is manual: the user calls migrate_from_native() explicitly after CREATE EXTENSION lolor. This requires superuser and should be done during a maintenance window.
  • Lolor → native migration is automatic: DROP EXTENSION lolor triggers migrate_to_native() via the event trigger, ensuring no large objects are lost on extension removal. If migration fails (e.g. OID conflict), the DROP is rejected so the user can resolve the issue and retry.
  • Migration is safe only in master-replica configurations for now. In multi-master setups, migrated OIDs lack node-encoding and may conflict with lolor-generated OIDs on other nodes.

Implementation details

  • Forward migration uses bulk INSERT for performance; reverse migration uses the native LO API (lo_create_orig/lo_lseek64_orig/lowrite_orig) since direct INSERT into pg_catalog.pg_largeobject is not allowed from SQL.
  • LOBLKSIZE is derived at runtime from current_setting('block_size') to support non-default block sizes.
  • Uses lo_lseek64_orig with bigint offsets to handle large objects exceeding 2 GB.
  • The event trigger guards migrate_to_native() with a pg_proc existence check for backward compatibility with lolor versions < 1.2.3.
  • Row count verification after each bulk copy detects silent data loss.
  • Materializes the OID list before lo_unlink_orig to avoid scanning the catalog while modifying it.
  • Emits NOTICE during installation if active streaming replicas are detected.

Tests

  • Forward migration: two native LOs migrated, data integrity verified through lolor API.
  • Reverse migration via DROP EXTENSION: all objects (including ones created directly in lolor) restored to native storage with data verification.
  • Manual migrate_to_native() without dropping the extension.
  • OID conflict detection for both migrate_from_native and migrate_to_native.
  • DROP EXTENSION rejected on OID conflict, user resolves and retries successfully.
  • Edge cases: empty native storage, empty lolor storage, extension upgrade path 1.2.2 → 1.2.3.
  • TAP test for standby promotion with lolor installed.

…rage

Add migrate_from_native() and migrate_to_native() SQL functions that move
large objects between PostgreSQL's native pg_catalog storage and lolor's
replication-compatible tables, preserving OIDs, owners, ACLs, and data.

The DROP EXTENSION event trigger now calls migrate_to_native() automatically
before removing the extension, preventing data loss that previously occurred
when lolor tables were dropped with the schema.

Key details:
- Forward migration uses bulk INSERT for performance
- Reverse migration uses the native LO API (lo_create_orig/lowrite_orig)
  since direct INSERT into pg_catalog.pg_largeobject is not allowed
- LOBLKSIZE derived at runtime from block_size GUC for non-default builds
- Uses lo_lseek64_orig for large objects exceeding 2 GB
- Both functions require superuser and verify lolor is enabled
- Event trigger guards migrate_to_native() with pg_proc existence check
  for backward compatibility with versions < 1.2.3
- Emits NOTICE during installation if streaming replicas are detected
- Migration is safe only in master-replica configurations for now

Add regression tests for forward migration, reverse migration via DROP
EXTENSION, manual migrate_to_native, empty-database edge cases, and the
1.2.2 to 1.2.3 upgrade path.
@danolivo danolivo self-assigned this Apr 2, 2026
@danolivo danolivo added the enhancement New feature or request label Apr 2, 2026
@danolivo danolivo requested review from mason-sharp and rasifr April 2, 2026 09:45
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 2, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bbe300ab-6db0-43d0-9867-5e19c43e7d5a

📥 Commits

Reviewing files that changed from the base of the PR and between cff946a and dceb007.

⛔ Files ignored due to path filters (1)
  • expected/lolor.out is excluded by !**/*.out
📒 Files selected for processing (8)
  • Makefile
  • README.md
  • docs/index.md
  • lolor--1.2.2--1.2.3.sql
  • lolor.control
  • sql/lolor.sql
  • src/lolor.c
  • t/006_promote_standby.pl

📝 Walkthrough

Walkthrough

This pull request adds version 1.2.3 of the lolor PostgreSQL extension with bidirectional large object migration support. Two new SQL functions enable migrating native PostgreSQL large objects to lolor storage and back, with automatic migration triggered during extension drop. Documentation, tests, and build configuration are updated to reflect the new feature.

Changes

Cohort / File(s) Summary
Build Configuration
Makefile, lolor.control
Updated extension version to 1.2.3 and added new SQL migration script to the build data file list.
Migration Implementation
lolor--1.2.2--1.2.3.sql
New extension upgrade script defining lolor.migrate_from_native() and lolor.migrate_to_native() functions for bidirectional large object migration with OID/ownership/ACL preservation and transactional safety checks.
Documentation
README.md, docs/index.md
Added "Migrating large objects" sections documenting the bidirectional workflow, safety requirements for master-replica setups, and updated limitations regarding multi-master configurations.
Testing
sql/lolor.sql, t/006_promote_standby.pl
Expanded SQL test suite with migration validation, OID conflict scenarios, and data integrity checks; added new Perl test for standby promotion with large object verification.
Extension Drop Handler
src/lolor.c
Enhanced lolor_on_drop_extension() to automatically invoke migrate_to_native() during extension drop, with error handling for migration failures.

Poem

🐰 A rabbit hops with glee, for objects migrate so free!
From native stores to lolor's keep, then back again—no secrets steep.
With OIDs preserved and ACLs intact, our bidirectional path's a fact!
pip pip

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main feature: adding bidirectional large object migration between native PostgreSQL and lolor storage, which is the primary focus of all changeset modifications.
Description check ✅ Passed The description is detailed and directly related to the changeset, explaining design decisions, implementation details, and testing approach for the bidirectional migration feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch spoc-489

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Apr 2, 2026

Up to standards ✅

🟢 Issues 26 high

Results:
26 new issues

Category Results
Compatibility 26 high (25 false positives)

View in Codacy

🟢 Metrics 0 duplication

Metric Results
Duplication 0

View in Codacy

TIP This summary will be updated as you push new changes. Give us feedback

END IF;

-- Migrate each object using the native LO API (_orig functions).
-- We cannot INSERT directly into pg_catalog.pg_largeobject from SQL,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Superusers can do INSERT directly into pg_catalog.pg_largeobject. That restriction applies only for non-superusers. Since both functions already require superuser, the loop can be replaced with bulk INSERT ... SELECT, the same approach migrate_from_native() uses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants