db_meta 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72b8b2a1d2345a78bad679153e910c7a092ff004495f03bc9f8fc14518080208
4
- data.tar.gz: 9a097fa844e9694e3282cc3ad6e5d05318e6e87bae9194e3a479f66f6c60df5b
3
+ metadata.gz: 2a8daf933344c242f2d8b50b218bddb5a4cf488a729316ad11c658af6c5cb782
4
+ data.tar.gz: b20f8d156bdfd1839d52781603a4abb83a099adfa415399acf53308cf887a566
5
5
  SHA512:
6
- metadata.gz: a1ca4509ecf3583ec46fd1c69ed0edcf17bbe4cceddf8675abf38e7818f00dcc0ecfd99793bf65b2d6ecff13387d27b256491d28cefbd115f2400a5429999ffc
7
- data.tar.gz: 6633e673c2deb5079879a21efd98569728ae8811b4525d8bfd75fff9e39d6f52811a68830eb55e09a26f439a6cde3b5227031ee4628a594bc8c86fa3cfa2547d
6
+ metadata.gz: cdddc2bfff37d407e381f9a642dd586c6ecf728f3db170f9db6e0ffc03d4c999633ee8c840d80fb4c2ced75910e399f8ea063825000c143bd4df87ea725eb863
7
+ data.tar.gz: 5ff480c88420a57634dc65bdd99dcf418a82bbd978457a42b8fd610c4c6d4896243f6b62fa312536b08efc4b21fc43ee4df90694b55c091a1d14c90d646e70d8
@@ -0,0 +1,67 @@
1
+ name: 01 - Test
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - "*"
7
+
8
+ pull_request:
9
+ branches:
10
+ - "*"
11
+
12
+ schedule:
13
+ - cron: 0 2 * * 3,6
14
+
15
+ # Allows you to run this workflow manually from the Actions tab
16
+ workflow_dispatch:
17
+
18
+ jobs:
19
+ build:
20
+ runs-on: ubuntu-latest
21
+ env:
22
+ CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
23
+ ORACLE_HOME: /opt/instantclient_23_7
24
+ LD_LIBRARY_PATH: /opt/instantclient_23_7
25
+ OCI_DIR: /opt/instantclient_23_7
26
+ NLS_LANG: AMERICAN_AMERICA.AL32UTF8
27
+
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ ruby: [ '4.0', '3.4', '3.3']
32
+
33
+ name: Ruby ${{ matrix.ruby }}
34
+ steps:
35
+ - uses: actions/checkout@v6
36
+
37
+ - name: Install Oracle instant client
38
+ run: |
39
+ mkdir -p /opt && cd /opt
40
+ curl https://download.oracle.com/otn_software/linux/instantclient/2370000/instantclient-basiclite-linux.x64-23.7.0.25.01.zip --output instantclient.zip
41
+ curl https://download.oracle.com/otn_software/linux/instantclient/2370000/instantclient-sdk-linux.x64-23.7.0.25.01.zip --output instantclient_sdk.zip
42
+ curl https://download.oracle.com/otn_software/linux/instantclient/2370000/instantclient-sqlplus-linux.x64-23.7.0.25.01.zip --output instantclient_sqlplus.zip
43
+ unzip -o instantclient.zip
44
+ unzip -o instantclient_sdk.zip
45
+ unzip -o instantclient_sqlplus.zip
46
+
47
+ - name: libaio.so.1 fix with symbolic link
48
+ run: |
49
+ TARGET="/lib/x86_64-linux-gnu/libaio.so.1"
50
+ SOURCE="/lib/x86_64-linux-gnu/libaio.so.1t64"
51
+
52
+ # Check if the symbolic link already exists
53
+ if [ ! -e "$TARGET" ]; then
54
+ sudo ln -s "$SOURCE" "$TARGET"
55
+ fi
56
+
57
+ - name: Set up Ruby
58
+ uses: ruby/setup-ruby@v1
59
+ with:
60
+ ruby-version: ${{ matrix.ruby }}
61
+ bundler: latest
62
+ bundler-cache: true
63
+ cache-version: 3
64
+
65
+ - name: Run default task
66
+ run: |
67
+ bundle exec rake
@@ -0,0 +1,57 @@
1
+ name: 02 - Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ jobs:
9
+
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ permissions:
13
+ id-token: write
14
+ contents: write
15
+ env:
16
+ ORACLE_HOME: /opt/instantclient_23_7
17
+ LD_LIBRARY_PATH: /opt/instantclient_23_7
18
+ OCI_DIR: /opt/instantclient_23_7
19
+ NLS_LANG: AMERICAN_AMERICA.AL32UTF8
20
+
21
+ steps:
22
+ - name: Checkout current code
23
+ uses: actions/checkout@v6
24
+
25
+ - name: Install Oracle instant client
26
+ run: |
27
+ mkdir -p /opt && cd /opt
28
+ curl https://download.oracle.com/otn_software/linux/instantclient/2370000/instantclient-basiclite-linux.x64-23.7.0.25.01.zip --output instantclient.zip
29
+ curl https://download.oracle.com/otn_software/linux/instantclient/2370000/instantclient-sdk-linux.x64-23.7.0.25.01.zip --output instantclient_sdk.zip
30
+ curl https://download.oracle.com/otn_software/linux/instantclient/2370000/instantclient-sqlplus-linux.x64-23.7.0.25.01.zip --output instantclient_sqlplus.zip
31
+ unzip -o instantclient.zip
32
+ unzip -o instantclient_sdk.zip
33
+ unzip -o instantclient_sqlplus.zip
34
+
35
+ - name: libaio.so.1 fix with symbolic link
36
+ run: |
37
+ TARGET="/lib/x86_64-linux-gnu/libaio.so.1"
38
+ SOURCE="/lib/x86_64-linux-gnu/libaio.so.1t64"
39
+
40
+ # Check if the symbolic link already exists
41
+ if [ ! -e "$TARGET" ]; then
42
+ sudo ln -s "$SOURCE" "$TARGET"
43
+ fi
44
+
45
+ - name: Set up Ruby
46
+ uses: ruby/setup-ruby@v1
47
+ with:
48
+ ruby-version: '4.0'
49
+ bundler: latest
50
+ bundler-cache: true
51
+ cache-version: 1
52
+
53
+ - name: Build gem
54
+ run: gem build *.gemspec
55
+
56
+ - name: Push to Rubygems
57
+ uses: rubygems/release-gem@v1
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.3.6
1
+ 4.0.3
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.3.6
1
+ ruby 4.0.3
data/CHANGELOG.md CHANGED
@@ -1,3 +1,36 @@
1
+ ## [0.14.0] - 2026-04-28
2
+
3
+ ### Changed
4
+ - Reuse a single OCI8 logical connection per worker thread for the duration of the fetch instead of acquiring/logging off on every type fetch. Drastically reduces session attach/detach traffic against the connection pool, which avoids the per-attach overhead (and the 23c Instant Client byte-leak symptom) on long extracts.
5
+ - Batch-load constraint metadata via two bulk queries against `USER_CONSTRAINTS` and `USER_CONS_COLUMNS` once before the parallel object fetch, and have `Constraint#fetch` read from an in-memory cache. Removes 2-3 round-trips per constraint.
6
+ - Constraints with Oracle-generated `SYS_*` names are now emitted without an explicit `CONSTRAINT <name>` clause, so DDL diffs across instances aren't dominated by name churn. User-given names are preserved.
7
+ - Redundant `SYS_*` NOT NULL CHECK constraints (whose condition is just `"COL" IS NOT NULL`) are filtered out — the column-level NOT NULL in the table DDL already covers them.
8
+ - Fixed `View#fetch` to reuse the acquired connection (previously called `Connection.instance.get` twice, leaking a logical connection per view).
9
+ - Fixed `View#extract` crash when a view has columns without comments (`column.comment.size` on nil).
10
+ - Fixed `Table#fetch` ensure logic (was `rescue` instead of `ensure`).
11
+ - Fixed `Column.all` typo (`loggoff` → cleanup no longer needed).
12
+
13
+ ### Added
14
+ - README troubleshooting section for macOS / Apple Silicon: short tip covering both the 23c OID lookup hang and the `libclntsh` ↔ OpenLDAP symbol-clash, with the shared `tnsnames.ora` + `NAMES.DIRECTORY_PATH=(TNSNAMES, EZCONNECT)` fix and links to the relevant ruby-oci8 / Oracle docs.
15
+ - Significantly expanded test coverage (49 → 125 examples; line coverage 58.94% → ~86%).
16
+ - Added Ruby 4.0 to the supported/tested matrix.
17
+
18
+ ### Removed
19
+ - Dropped Ruby 3.2 from the actively tested matrix (EOL 31 Mar 2026).
20
+
21
+ ## [0.13.1] - 2025-11-09
22
+
23
+ ### Added
24
+ - Trusted Publisher
25
+
26
+ ### Changed
27
+ - Updated development dependencies
28
+
29
+ ## [0.13.0] - 2024-12-25
30
+
31
+ ### Changed
32
+ - Support for Ruy 3.4
33
+
1
34
  ## [0.12.0] - 2024-06-15
2
35
 
3
36
  ### Changed
data/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/db_meta.svg)](https://badge.fury.io/rb/db_meta)
2
- [![Maintainability](https://api.codeclimate.com/v1/badges/ffd17b164da909c5c172/maintainability)](https://codeclimate.com/github/thomis/db_meta/maintainability)
3
- [![Test Coverage](https://api.codeclimate.com/v1/badges/ffd17b164da909c5c172/test_coverage)](https://codeclimate.com/github/thomis/db_meta/test_coverage)
4
- [![ci](https://github.com/thomis/db_meta/actions/workflows/ci.yml/badge.svg)](https://github.com/thomis/db_meta/actions/workflows/ci.yml)
2
+ [![01 - Test](https://github.com/thomis/db_meta/actions/workflows/01_test.yml/badge.svg)](https://github.com/thomis/db_meta/actions/workflows/01_test.yml)
3
+ [![02 - Release](https://github.com/thomis/db_meta/actions/workflows/02_release.yml/badge.svg)](https://github.com/thomis/db_meta/actions/workflows/02_release.yml)
5
4
 
6
- # Welcome to db_meta
7
- Database meta and core data extraction.
5
+ # db_meta
8
6
 
9
- ## Is it production ready?
7
+ Extract Oracle schema metadata and core data as SQL DDL files.
10
8
 
11
- Well, I would not say, but I am using it already for my database development work where the gem covers my needs. Be careful and check details. Please create an issue when you think that someting is wrong or missing.
9
+ `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.
10
+
11
+ ## Status
12
+
13
+ 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.
12
14
 
13
15
  ## Installation
14
16
  via Gemfile
@@ -31,6 +33,13 @@ meta.fetch
31
33
  meta.extract
32
34
  ```
33
35
 
36
+ ## Output conventions
37
+
38
+ A few decisions worth knowing about, especially if you compare extracts across instances:
39
+
40
+ - **Auto-generated `SYS_*` constraint names are stripped from the output.** Oracle invents names like `SYS_C0012345` for unnamed constraints, and those names differ between instances — making schema diffs noisy. Constraints with a `SYS_*` name are emitted without an explicit `CONSTRAINT <name>` clause; on import, Oracle just generates a fresh name. User-given constraint names are preserved as-is.
41
+ - **Redundant `NOT NULL` CHECK constraints are omitted.** Oracle exposes column-level `NOT NULL` both as a column attribute and as a `SYS_*` 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.
42
+
34
43
  ## Supported Databases
35
44
  - Oracle
36
45
 
@@ -50,9 +59,14 @@ meta.extract
50
59
 
51
60
  Currently supported and tested ruby versions are:
52
61
 
53
- - 3.3
54
- - 3.2
55
- - 3.1
62
+ - 4.0 (EOL 31 Mar 2029)
63
+ - 3.4 (EOL 31 Mar 2028)
64
+ - 3.3 (EOL 31 Mar 2027)
65
+
66
+ Ruby versions not tested anymore:
67
+
68
+ - 3.2 (EOL 31 Mar 2026)
69
+ - 3.1 (EOL 31 Mar 2025)
56
70
  - 3.0 (EOL 31 Mar 2024)
57
71
  - 2.7 (EOL 31 Mar 2023)
58
72
  - 2.6 (EOL 31 Mar 2022)
@@ -60,8 +74,61 @@ Currently supported and tested ruby versions are:
60
74
  ## Planned Features
61
75
  - Storage and tablespace clause
62
76
 
77
+ ## Troubleshooting (macOS / Apple Silicon)
78
+
79
+ 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`.
80
+
81
+ 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:
82
+
83
+ - [ruby-oci8 #32 — OCI8 hangs when switching to LDAP](https://github.com/kubo/ruby-oci8/issues/32)
84
+ - [ruby-oci8 #41 — Assertion failure using LDAP](https://github.com/kubo/ruby-oci8/issues/41)
85
+ - [Oracle Instant Client FAQ](https://www.oracle.com/database/technologies/instant-client/faqs.html)
86
+
87
+ ## Publishing
88
+
89
+ This project uses [Trusted Publishing](https://guides.rubygems.org/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.
90
+
91
+ With Trusted Publishing configured, gem releases are automatically published to RubyGems when the release workflow runs, providing a more secure and streamlined publishing process.
92
+
63
93
  ## Contributing
64
- ...
94
+
95
+ We welcome contributions to db_meta! Here's how you can help:
96
+
97
+ 1. **Fork the repository** - Create your own fork of the code
98
+ 2. **Create a feature branch** - Make your changes in a new git branch:
99
+ ```
100
+ git checkout -b my-new-feature
101
+ ```
102
+ 3. **Make your changes** - Write your code and tests
103
+ 4. **Run the tests** - Ensure all tests pass:
104
+ ```
105
+ bundle exec rake
106
+ ```
107
+ 5. **Commit your changes** - Write clear and meaningful commit messages:
108
+ ```
109
+ git commit -am 'Add some feature'
110
+ ```
111
+ 6. **Push to your branch** - Push your changes to GitHub:
112
+ ```
113
+ git push origin my-new-feature
114
+ ```
115
+ 7. **Create a Pull Request** - Open a PR from your fork to the main repository
116
+
117
+ ### Guidelines
118
+
119
+ - Write tests for any new functionality
120
+ - Follow the existing code style and conventions
121
+ - Update documentation as needed
122
+ - Keep commits focused and atomic
123
+ - Write clear commit messages
124
+
125
+ ### Reporting Issues
126
+
127
+ Found a bug or have a feature request? Please open an issue on GitHub with:
128
+ - A clear title and description
129
+ - Steps to reproduce (for bugs)
130
+ - Expected vs actual behavior
131
+ - Ruby version and environment details
65
132
 
66
133
  ## License
67
134
  db_meta is released under [Apache License, Version 2.0](https://opensource.org/licenses/Apache-2.0)
data/db_meta.gemspec CHANGED
@@ -18,11 +18,12 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_development_dependency "bundler", "~> 2.3"
21
+ spec.add_development_dependency "bundler", ">= 2.3"
22
22
  spec.add_development_dependency "rake", "~> 13.1"
23
23
  spec.add_development_dependency "rspec", "~> 3.13"
24
24
  spec.add_development_dependency "standard", "~> 1.34"
25
25
  spec.add_development_dependency "simplecov", "~> 0.21"
26
26
 
27
27
  spec.add_dependency "ruby-oci8", "~> 2.2"
28
+ spec.add_dependency "logger", "~> 1.6"
28
29
  end
@@ -20,7 +20,7 @@ module DbMeta
20
20
  @include = args[:include]
21
21
 
22
22
  @objects = []
23
- @invalid_objects = Hash.new([])
23
+ @invalid_objects = Hash.new { |h, key| h[key] = [] }
24
24
 
25
25
  @base_folder = args[:base_folder] || File.expand_path(File.join(Dir.pwd, "/#{@username}@#{@instance}"))
26
26
 
@@ -5,6 +5,8 @@ module DbMeta
5
5
  class Connection
6
6
  include Singleton
7
7
 
8
+ THREAD_KEY = :db_meta_oracle_connection
9
+
8
10
  attr_accessor :username, :password, :database_instance
9
11
  attr_reader :pool
10
12
  attr_reader :worker
@@ -23,11 +25,22 @@ module DbMeta
23
25
  Log.info("Connected to #{@username}@#{@database_instance}")
24
26
  end
25
27
 
26
- # create and return logical connection. It creates physical connection as needed.
27
- ::OCI8.new(@username, @password, @pool)
28
+ # one logical connection per thread - reused across all fetches in that thread
29
+ Thread.current[THREAD_KEY] ||= ::OCI8.new(@username, @password, @pool)
30
+ end
31
+
32
+ def release_thread_connection
33
+ connection = Thread.current[THREAD_KEY]
34
+ return unless connection
35
+ connection.logoff
36
+ rescue
37
+ # connection may already be closed
38
+ ensure
39
+ Thread.current[THREAD_KEY] = nil
28
40
  end
29
41
 
30
42
  def disconnect
43
+ release_thread_connection
31
44
  return unless @pool
32
45
  @pool.destroy
33
46
  Log.info("Logged off from #{@username}@#{@database_instance}")
@@ -26,6 +26,9 @@ module DbMeta
26
26
  end
27
27
 
28
28
  def fetch(args = {})
29
+ # bulk-load metadata that would otherwise drive N round-trips per object
30
+ Constraint.preload
31
+
29
32
  # fetch details in parallel
30
33
  # number of threads = physical connections / 2 to prevent application locking
31
34
  worker = (1..Connection.instance.worker / 2).map {
@@ -35,6 +38,8 @@ module DbMeta
35
38
  object.fetch
36
39
  end
37
40
  rescue ThreadError
41
+ ensure
42
+ Connection.instance.release_thread_connection
38
43
  end
39
44
  }
40
45
  worker.map(&:join) # wait until all are done
@@ -215,8 +220,6 @@ module DbMeta
215
220
  Log.info("Objects: #{items.size}, Object Types: #{types.uniq.size}")
216
221
 
217
222
  objects
218
- ensure
219
- connection&.logoff # closes logical connection
220
223
  end
221
224
  end
222
225
  end
@@ -36,8 +36,6 @@ module DbMeta
36
36
  cursor.close
37
37
 
38
38
  columns
39
- rescue
40
- connection.loggoff
41
39
  end
42
40
 
43
41
  private
@@ -9,8 +9,6 @@ module DbMeta
9
9
  while (row = cursor.fetch)
10
10
  @text = row[0]
11
11
  end
12
- ensure
13
- connection.logoff
14
12
  end
15
13
  end
16
14
  end
@@ -5,6 +5,72 @@ module DbMeta
5
5
 
6
6
  attr_reader :constraint_type, :table_name, :search_condition, :referential_constraint, :delete_rule, :columns
7
7
 
8
+ @@cache = {}
9
+ @@cache_mutex = Mutex.new
10
+
11
+ def self.preload(args = {})
12
+ connection_class = args[:connection_class] || Connection
13
+ connection = connection_class.instance.get
14
+
15
+ meta = {}
16
+ cursor = connection.exec(
17
+ "select constraint_name, constraint_type, table_name, search_condition, r_constraint_name, delete_rule " \
18
+ "from user_constraints"
19
+ )
20
+ cursor.fetch_hash do |row|
21
+ name = row["CONSTRAINT_NAME"]
22
+ type = translate_constraint_type(row["CONSTRAINT_TYPE"])
23
+ search_condition = row["SEARCH_CONDITION"]
24
+
25
+ # Skip Oracle-generated NOT NULL CHECK constraints. They are already
26
+ # represented by the column's NOT NULL clause in the table DDL, so
27
+ # emitting them again is redundant noise that breaks schema diffs.
28
+ next if redundant_not_null?(name, type, search_condition)
29
+
30
+ meta[name] = {
31
+ constraint_type: type,
32
+ table_name: row["TABLE_NAME"],
33
+ search_condition: search_condition,
34
+ r_constraint_name: row["R_CONSTRAINT_NAME"],
35
+ delete_rule: row["DELETE_RULE"],
36
+ columns: []
37
+ }
38
+ end
39
+ cursor.close
40
+
41
+ cursor = connection.exec(
42
+ "select constraint_name, column_name, position " \
43
+ "from user_cons_columns order by constraint_name, position"
44
+ )
45
+ cursor.fetch_hash do |row|
46
+ entry = meta[row["CONSTRAINT_NAME"]]
47
+ next unless entry
48
+ entry[:columns] << row["COLUMN_NAME"]
49
+ end
50
+ cursor.close
51
+
52
+ @@cache_mutex.synchronize { @@cache = meta }
53
+ end
54
+
55
+ def self.system_generated?(name)
56
+ name.to_s.start_with?("SYS_")
57
+ end
58
+
59
+ def self.redundant_not_null?(name, type, search_condition)
60
+ return false unless system_generated?(name) && type == "CHECK"
61
+ return false if search_condition.nil?
62
+ # Match patterns like: "COL_NAME" IS NOT NULL or COL_NAME IS NOT NULL
63
+ !!search_condition.match?(/\A\s*"?[A-Z0-9_$#]+"?\s+IS\s+NOT\s+NULL\s*\z/i)
64
+ end
65
+
66
+ def self.cache
67
+ @@cache
68
+ end
69
+
70
+ def self.reset_cache
71
+ @@cache_mutex.synchronize { @@cache = {} }
72
+ end
73
+
8
74
  def initialize(args = {})
9
75
  super
10
76
 
@@ -13,37 +79,29 @@ module DbMeta
13
79
  end
14
80
 
15
81
  def fetch(args = {})
16
- connection = Connection.instance.get
17
- cursor = connection.exec("select * from user_constraints where constraint_name = '#{@name}'")
18
- cursor.fetch_hash do |item|
19
- @constraint_type = translate_constraint_type(item["CONSTRAINT_TYPE"])
20
- @extract_type = :merged if @constraint_type == "FOREIGN KEY"
21
- @table_name = item["TABLE_NAME"]
22
- @search_condition = item["SEARCH_CONDITION"]
23
- @delete_rule = item["DELETE_RULE"]
24
-
25
- if @constraint_type == "FOREIGN KEY"
26
- constraint = Constraint.new("OBJECT_TYPE" => "CONSTRAINT", "OBJECT_NAME" => item["R_CONSTRAINT_NAME"])
27
- constraint.fetch
28
- @referential_constraint = constraint
29
- end
30
- end
31
- cursor.close
82
+ entry = @@cache[@name]
83
+ return unless entry
32
84
 
33
- # get affected columns
34
- cursor = connection.exec("select * from user_cons_columns where constraint_name = '#{@name}' order by position")
35
- cursor.fetch_hash do |item|
36
- @columns << item["COLUMN_NAME"]
85
+ @constraint_type = entry[:constraint_type]
86
+ @table_name = entry[:table_name]
87
+ @search_condition = entry[:search_condition]
88
+ @delete_rule = entry[:delete_rule]
89
+ @columns = entry[:columns].dup
90
+ @extract_type = :merged if @constraint_type == "FOREIGN KEY"
91
+
92
+ if @constraint_type == "FOREIGN KEY" && entry[:r_constraint_name]
93
+ @referential_constraint = Constraint.new(
94
+ "OBJECT_TYPE" => "CONSTRAINT",
95
+ "OBJECT_NAME" => entry[:r_constraint_name]
96
+ )
97
+ @referential_constraint.fetch
37
98
  end
38
- cursor.close
39
- ensure
40
- connection.logoff
41
99
  end
42
100
 
43
101
  def extract(args = {})
44
102
  buffer = []
45
103
  buffer << "ALTER TABLE #{@table_name} ADD ("
46
- buffer << " CONSTRAINT #{@name}"
104
+ buffer << " CONSTRAINT #{@name}" unless Constraint.system_generated?(@name)
47
105
 
48
106
  case @constraint_type
49
107
  when "CHECK"
@@ -69,9 +127,7 @@ module DbMeta
69
127
  ["PRIMARY KEY", "FOREIGN KEY", "UNIQUE", "CHECK"].index(type)
70
128
  end
71
129
 
72
- private
73
-
74
- def translate_constraint_type(type)
130
+ def self.translate_constraint_type(type)
75
131
  case type
76
132
  when "P"
77
133
  "PRIMARY KEY"
@@ -15,8 +15,6 @@ module DbMeta
15
15
  @host = row[2].to_s
16
16
  end
17
17
  cursor.close
18
- ensure
19
- connection.logoff
20
18
  end
21
19
 
22
20
  def extract(args = {})
@@ -14,8 +14,6 @@ module DbMeta
14
14
  @source << row[0].to_s
15
15
  end
16
16
  cursor.close
17
- ensure
18
- connection.logoff
19
17
  end
20
18
 
21
19
  def extract(args = {})
@@ -38,12 +38,10 @@ module DbMeta
38
38
  @columns[idx] = row[0] # replace sys_... entry
39
39
  end
40
40
  cursor.close
41
- ensure
42
- connection.logoff
43
41
  end
44
42
 
45
43
  def extract(args = {})
46
- "CREATE#{(@uniqueness == "UNIQUE") ? " UNIQUE" : nil} INDEX #{@name} ON #{@table_name}(#{@columns.join(", ")});"
44
+ "CREATE#{" UNIQUE" if @uniqueness == "UNIQUE"} INDEX #{@name} ON #{@table_name}(#{@columns.join(", ")});"
47
45
  end
48
46
  end
49
47
  end
@@ -33,8 +33,6 @@ module DbMeta
33
33
  @comment = item["COMMENTS"]
34
34
  end
35
35
  cursor.close
36
- ensure
37
- connection.logoff
38
36
  end
39
37
 
40
38
  def extract(args = {})
@@ -6,22 +6,21 @@ module DbMeta
6
6
  attr_reader :header, :body
7
7
 
8
8
  def fetch
9
+ connection = Connection.instance.get
10
+
9
11
  @header = ""
10
- cursor = Connection.instance.get.exec("select text from user_source where type = 'PACKAGE' and name = '#{@name}' order by line")
12
+ cursor = connection.exec("select text from user_source where type = 'PACKAGE' and name = '#{@name}' order by line")
11
13
  while (row = cursor.fetch)
12
14
  @header << row[0].to_s
13
15
  end
14
16
  cursor.close
15
17
 
16
18
  @body = ""
17
- connection = Connection.instance.get
18
19
  cursor = connection.exec("select text from user_source where type = 'PACKAGE BODY' and name = '#{@name}' order by line")
19
20
  while (row = cursor.fetch)
20
21
  @body << row[0].to_s
21
22
  end
22
23
  cursor.close
23
- ensure
24
- connection.logoff
25
24
  end
26
25
 
27
26
  def extract(args = {})
@@ -14,8 +14,6 @@ module DbMeta
14
14
  @source << row[0].to_s
15
15
  end
16
16
  cursor.close
17
- ensure
18
- connection.logoff
19
17
  end
20
18
 
21
19
  def extract(args = {})
@@ -23,8 +23,6 @@ module DbMeta
23
23
  @compatible = row["COMPATIBLE"]
24
24
  end
25
25
  cursor.close
26
- ensure
27
- connection.logoff
28
26
  end
29
27
 
30
28
  def extract(args = {})
@@ -19,8 +19,6 @@ module DbMeta
19
19
  @last_number = row[6].to_i
20
20
  end
21
21
  cursor.close
22
- ensure
23
- connection.logoff
24
22
  end
25
23
 
26
24
  def extract(args = {})
@@ -21,8 +21,6 @@ module DbMeta
21
21
  @db_link = row[2].to_s
22
22
  end
23
23
  cursor.close
24
- ensure
25
- connection.logoff
26
24
  end
27
25
 
28
26
  def extract(args = {})
@@ -36,8 +36,6 @@ module DbMeta
36
36
  @duration = row[3].to_s
37
37
  end
38
38
  cursor.close
39
- rescue
40
- connection.logoff
41
39
  end
42
40
 
43
41
  def extract(args = {})
@@ -54,7 +52,7 @@ module DbMeta
54
52
  if @iot_type == "IOT"
55
53
  constraint = @constraints.find { |c| c.constraint_type == "PRIMARY KEY" }
56
54
  buffer[-1] += ","
57
- buffer << " CONSTRAINT #{constraint.name}"
55
+ buffer << " CONSTRAINT #{constraint.name}" unless Constraint.system_generated?(constraint.name)
58
56
  buffer << " PRIMARY KEY (#{constraint.columns.join(", ")})"
59
57
  buffer << " ENABLE VALIDATE"
60
58
  end
@@ -44,8 +44,6 @@ module DbMeta
44
44
  buffer << nil
45
45
 
46
46
  buffer.join("\n")
47
- ensure
48
- connection.logoff
49
47
  end
50
48
 
51
49
  def ddl_drop
@@ -27,8 +27,6 @@ module DbMeta
27
27
  parse_trigger_type
28
28
 
29
29
  cursor.close
30
- ensure
31
- connection.logoff
32
30
  end
33
31
 
34
32
  def extract(args = {})
@@ -19,8 +19,6 @@ module DbMeta
19
19
  @body << row[0].to_s
20
20
  end
21
21
  cursor.close
22
- ensure
23
- connection.logoff
24
22
  end
25
23
 
26
24
  def extract(args = {})
@@ -15,13 +15,11 @@ module DbMeta
15
15
 
16
16
  @source = ""
17
17
  connection = Connection.instance.get
18
- cursor = Connection.instance.get.exec("select text from user_views where view_name = '#{@name}'")
18
+ cursor = connection.exec("select text from user_views where view_name = '#{@name}'")
19
19
  while (row = cursor.fetch)
20
20
  @source << row[0].to_s
21
21
  end
22
22
  cursor.close
23
- ensure
24
- connection.logoff
25
23
  end
26
24
 
27
25
  def extract(args = {})
@@ -47,7 +45,7 @@ module DbMeta
47
45
 
48
46
  # view column comments
49
47
  @columns.each do |column|
50
- next if column.comment.size == 0
48
+ next if column.comment.nil? || column.comment.size == 0
51
49
  buffer << "COMMENT ON COLUMN #{@name}.#{column.name} IS '#{column.comment.gsub("'", "''")}';"
52
50
  end
53
51
 
@@ -1,3 +1,3 @@
1
1
  module DbMeta
2
- VERSION = "0.12.0"
2
+ VERSION = "0.14.0"
3
3
  end
metadata CHANGED
@@ -1,27 +1,26 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: db_meta
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thomi
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-11-22 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: bundler
15
14
  requirement: !ruby/object:Gem::Requirement
16
15
  requirements:
17
- - - "~>"
16
+ - - ">="
18
17
  - !ruby/object:Gem::Version
19
18
  version: '2.3'
20
19
  type: :development
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
- - - "~>"
23
+ - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '2.3'
27
26
  - !ruby/object:Gem::Dependency
@@ -94,6 +93,20 @@ dependencies:
94
93
  - - "~>"
95
94
  - !ruby/object:Gem::Version
96
95
  version: '2.2'
96
+ - !ruby/object:Gem::Dependency
97
+ name: logger
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.6'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.6'
97
110
  description: Database meta and core data extraction.
98
111
  email:
99
112
  - thomas.steiner@ikey.ch
@@ -102,8 +115,8 @@ extensions: []
102
115
  extra_rdoc_files: []
103
116
  files:
104
117
  - ".github/dependabot.yml"
105
- - ".github/workflows/cd.yml"
106
- - ".github/workflows/ci.yml"
118
+ - ".github/workflows/01_test.yml"
119
+ - ".github/workflows/02_release.yml"
107
120
  - ".gitignore"
108
121
  - ".rspec"
109
122
  - ".ruby-version"
@@ -158,7 +171,6 @@ homepage: https://github.com/thomis/db_meta
158
171
  licenses:
159
172
  - Apache-2.0
160
173
  metadata: {}
161
- post_install_message:
162
174
  rdoc_options: []
163
175
  require_paths:
164
176
  - lib
@@ -173,8 +185,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
185
  - !ruby/object:Gem::Version
174
186
  version: '0'
175
187
  requirements: []
176
- rubygems_version: 3.5.22
177
- signing_key:
188
+ rubygems_version: 4.0.6
178
189
  specification_version: 4
179
190
  summary: Database meta and core data extraction
180
191
  test_files: []
@@ -1,46 +0,0 @@
1
- name: cd
2
-
3
- on:
4
- workflow_dispatch:
5
-
6
- jobs:
7
-
8
- build:
9
- runs-on: ubuntu-latest
10
- env:
11
- ORACLE_HOME: /opt/instantclient_21_12
12
- LD_LIBRARY_PATH: /opt/instantclient_21_12
13
- OCI_DIR: /opt/instantclient_21_12
14
- NLS_LANG: AMERICAN_AMERICA.AL32UTF8
15
-
16
- steps:
17
- - name: Checkout current code
18
- uses: actions/checkout@v4
19
-
20
- - name: Install Oracle instant client
21
- run: |
22
- mkdir -p /opt && cd /opt
23
- curl https://download.oracle.com/otn_software/linux/instantclient/2112000/instantclient-basic-linux.x64-21.12.0.0.0dbru.zip --output instantclient.zip
24
- curl https://download.oracle.com/otn_software/linux/instantclient/2112000/instantclient-sdk-linux.x64-21.12.0.0.0dbru.zip --output instantclient_sdk.zip
25
- unzip instantclient.zip
26
- unzip instantclient_sdk.zip
27
-
28
- - name: Set up Ruby
29
- uses: ruby/setup-ruby@v1
30
- with:
31
- ruby-version: '3.3'
32
- bundler: latest
33
- bundler-cache: true
34
- cache-version: 1
35
-
36
- - name: Push to Rubygems
37
- env:
38
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39
- run: |
40
- git config --global user.email "thomas.steiner@ikey.ch"
41
- git config --global user.name "thomis"
42
- mkdir ~/.gem
43
- echo -e "---\n:rubygems_api_key: ${{ secrets.RUBYGEMS_API_KEY }}" > ~/.gem/credentials
44
- chmod 600 ~/.gem/credentials
45
- bundle exec rake release
46
- rm ~/.gem/credentials
@@ -1,65 +0,0 @@
1
- name: ci
2
-
3
- on:
4
- push:
5
- branches:
6
- - "*"
7
-
8
- pull_request:
9
- branches:
10
- - "*"
11
-
12
- schedule:
13
- - cron: 0 2 * * 3,6
14
-
15
- # Allows you to run this workflow manually from the Actions tab
16
- workflow_dispatch:
17
-
18
- jobs:
19
- build:
20
- runs-on: ubuntu-latest
21
- env:
22
- CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
23
- ORACLE_HOME: /opt/instantclient_21_12
24
- LD_LIBRARY_PATH: /opt/instantclient_21_12
25
- OCI_DIR: /opt/instantclient_21_12
26
- NLS_LANG: AMERICAN_AMERICA.AL32UTF8
27
-
28
- strategy:
29
- fail-fast: false
30
- matrix:
31
- ruby: [ '3.3', '3.2', '3.1', '3.0']
32
-
33
- name: Ruby ${{ matrix.ruby }}
34
- steps:
35
- - uses: actions/checkout@v4
36
-
37
- - name: Install Oracle instant client
38
- run: |
39
- mkdir -p /opt && cd /opt
40
- curl https://download.oracle.com/otn_software/linux/instantclient/2112000/instantclient-basic-linux.x64-21.12.0.0.0dbru.zip --output instantclient.zip
41
- curl https://download.oracle.com/otn_software/linux/instantclient/2112000/instantclient-sdk-linux.x64-21.12.0.0.0dbru.zip --output instantclient_sdk.zip
42
- unzip instantclient.zip
43
- unzip instantclient_sdk.zip
44
-
45
- - name: Set up Ruby
46
- uses: ruby/setup-ruby@v1
47
- with:
48
- ruby-version: ${{ matrix.ruby }}
49
- bundler: latest
50
- bundler-cache: true
51
- cache-version: 3
52
-
53
- - name: Code Climate setup test reporter
54
- run: |
55
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
56
- chmod +x ./cc-test-reporter
57
- ./cc-test-reporter before-build
58
-
59
- - name: Run default task
60
- run: |
61
- bundle exec rake
62
-
63
- - name: Code Climate publish test coverage
64
- run: |
65
- ./cc-test-reporter after-build