db_meta
Extract Oracle schema metadata and core data as SQL DDL files.
db_meta connects to an Oracle schema and writes out DDL for every object (tables, views, indexes, constraints, packages, sequences, synonyms, grants, …), plus optional INSERT scripts for reference/lookup data. The output is a folder of .sql files organized by object type — suitable for checking into version control, diffing across environments, or seeding a fresh schema.
Status
Used in day-to-day database development by the author. It covers the most common Oracle object types but is not exhaustive — exotic features (advanced storage clauses, partitioning details, etc.) may be missing or simplified. Spot-check the output before relying on it for a migration, and please open an issue if you hit something that's wrong or missing.
Installation
via Gemfile
gem 'db_meta'
via command prompt
gem install
Example
require 'rubygems'
require 'db_meta'
= DbMeta::DbMeta.new(username: 'a_username', password: 'a_password', instance: 'an_instance')
.fetch
.extract
Output conventions
A few decisions worth knowing about, especially if you compare extracts across instances:
- Auto-generated
SYS_*constraint names are stripped from the output. Oracle invents names likeSYS_C0012345for unnamed constraints, and those names differ between instances — making schema diffs noisy. Constraints with aSYS_*name are emitted without an explicitCONSTRAINT <name>clause; on import, Oracle just generates a fresh name. User-given constraint names are preserved as-is. - Redundant
NOT NULLCHECK constraints are omitted. Oracle exposes column-levelNOT NULLboth as a column attribute and as aSYS_*CHECK constraint with a body of"COL" IS NOT NULL. The column-level form is already in the table DDL, so the duplicate CHECK is filtered out. - Materialized views reuse the
INTERVALexpression as the schedule start. Oracle'sUSER_REFRESH.NEXT_DATEadvances on every refresh cycle, so emitting it would make the DDL diverge between instances. The default output therefore writesSTART WITH <interval> NEXT <interval>— e.g.START WITH TRUNC(SYSDATE+1) NEXT TRUNC(SYSDATE+1). This keeps the first refresh aligned with the schedule cadence (a midnight-daily MV will first refresh at the next midnight, regardless of when the script runs), whereSTART WITH SYSDATEwould have triggered an off-cadence refresh at install time. Note that Oracle does not preserve the originally specified start date in its data dictionary — once an MV has refreshed at least once, the original is gone — so there's no way to round-trip an explicit user-given start. Passpreserve_mview_schedule: truetoextractto keep the liveTO_DATE(...)form when the exact next-refresh moment matters.
Tip for source DDL: where time-of-day matters (e.g. "00:05 daily"), anchor the INTERVAL expression — TRUNC(SYSDATE+1) + 5/1440 rather than SYSDATE + 1. Anchored expressions encode the time-of-day in the formula itself, so extracts round-trip cleanly; self-referential expressions like SYSDATE + 1 don't carry that intent and Oracle has no record of when "tomorrow at 00:05" was originally meant.
- Sequences start from their
MINVALUE, not from the liveLAST_NUMBER. Oracle'sLAST_NUMBERadvances every time someone callsNEXTVAL, so emitting it would make extracts diverge between any two instances of the same schema. The default output therefore starts each sequence at its baseline (typically 1), so a fresh schema build is reproducible and diffs stay clean. If you're seeding a new schema alongside imported data and need sequences to continue past existing PK values, passpreserve_sequence_position: truetoextract:
.extract(preserve_sequence_position: true)
Supported Databases
- Oracle
Supported Oracle object types
- Table (including Trigger, Constraint, Index)
- View and Materialized Views
- Grant
- Function, Procedures, Packages
- Type
- Synonym
- Database Link
- Queue
- Function based Indexes
- more to come...
Supported Ruby Versions
Currently supported and tested ruby versions are:
- 4.0 (EOL 31 Mar 2029)
- 3.4 (EOL 31 Mar 2028)
- 3.3 (EOL 31 Mar 2027)
Ruby versions not tested anymore:
- 3.2 (EOL 31 Mar 2026)
- 3.1 (EOL 31 Mar 2025)
- 3.0 (EOL 31 Mar 2024)
- 2.7 (EOL 31 Mar 2023)
- 2.6 (EOL 31 Mar 2022)
Planned Features
- Storage and tablespace clause
Troubleshooting (macOS / Apple Silicon)
If OCI8.new hangs for ~10s, prints "byte leak" gibberish, or crashes with ldap_first_entry: Assertion …, the issue is almost always that the Instant Client is trying to use LDAP/OID for database name resolution. The fix is the same in both cases: tell Oracle to use a local tnsnames.ora instead of LDAP. Create ~/opt/oracle/admin/tnsnames.ora with your DB alias and ~/opt/oracle/admin/sqlnet.ora containing NAMES.DIRECTORY_PATH=(TNSNAMES, EZCONNECT), then export TNS_ADMIN=$HOME/opt/oracle/admin.
For background and the libclntsh ↔ OpenLDAP symbol-clash variant (caused by Oracle's bundled LDAP client and Homebrew's OpenLDAP both loading into the same Ruby process), see:
- ruby-oci8 #32 — OCI8 hangs when switching to LDAP
- ruby-oci8 #41 — Assertion failure using LDAP
- Oracle Instant Client FAQ
Publishing
This project uses Trusted Publishing to securely publish gems to RubyGems.org. Trusted Publishing eliminates the need for long-lived API tokens by using OpenID Connect (OIDC) to establish a trusted relationship between GitHub Actions and RubyGems.org.
With Trusted Publishing configured, gem releases are automatically published to RubyGems when the release workflow runs, providing a more secure and streamlined publishing process.
Contributing
We welcome contributions to db_meta! Here's how you can help:
- Fork the repository - Create your own fork of the code
- Create a feature branch - Make your changes in a new git branch:
git checkout -b my-new-feature - Make your changes - Write your code and tests
- Run the tests - Ensure all tests pass:
bundle exec rake - Commit your changes - Write clear and meaningful commit messages:
git commit -am 'Add some feature' - Push to your branch - Push your changes to GitHub:
git push origin my-new-feature - Create a Pull Request - Open a PR from your fork to the main repository
Guidelines
- Write tests for any new functionality
- Follow the existing code style and conventions
- Update documentation as needed
- Keep commits focused and atomic
- Write clear commit messages
Reporting Issues
Found a bug or have a feature request? Please open an issue on GitHub with:
- A clear title and description
- Steps to reproduce (for bugs)
- Expected vs actual behavior
- Ruby version and environment details
License
db_meta is released under Apache License, Version 2.0