ridgepole-ext-tidb 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ba5b2121e9d9b95fadbaacebb618d89855c9de0a55240ec56fb9bb992c678e95
4
+ data.tar.gz: f208857ea6b30710b2b09f20ce21358ce050d6d6f2d10704b895f90acc3ced42
5
+ SHA512:
6
+ metadata.gz: ac287e753371f6056334b07869dc97dc1af6cbc4560241d16e0c09f35108c75b2f945bf04d43621f8545a025bf8da8ebacfc17faaec888dce956acd6bf69d338
7
+ data.tar.gz: f3feca6ac36a33aa4a1a08b621515614997b187a6f90834b9e2d886169a607cf36f8298a9812ade8201ec78f797ab62dc38e4a59b468bbdde4168b3f458de03c
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - Initial implementation of TiDB AUTO_RANDOM support for Ridgepole
12
+ - Schema dumper extension for AUTO_RANDOM columns
13
+ - Connection adapter extensions for TiDB compatibility
14
+ - Comprehensive test suite
15
+
16
+ ## [0.1.0] - 2025-09-05
17
+
18
+ ### Added
19
+ - Initial release
20
+ - TiDB AUTO_RANDOM column support
21
+ - Ridgepole integration
data/CLAUDE.md ADDED
@@ -0,0 +1,63 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ ridgepole-ext-tidb is a Ruby gem that extends Ridgepole to support TiDB's AUTO_RANDOM column attribute for distributed ID generation. It integrates with the trilogy adapter and extends ActiveRecord's schema management.
8
+
9
+ ## Development Commands
10
+
11
+ ### Setup
12
+ ```bash
13
+ bundle install
14
+ ```
15
+
16
+ ### Testing
17
+ ```bash
18
+ # Basic tests without TiDB
19
+ SKIP_TIDB_TESTS=1 bundle exec rspec
20
+
21
+ # Full integration tests (requires Docker)
22
+ docker-compose up -d
23
+ bundle exec rspec
24
+
25
+ # Docker-based testing
26
+ docker-compose run --rm test
27
+
28
+ # Run single test file
29
+ bundle exec rspec spec/ridgepole/ext/tidb_spec.rb
30
+ ```
31
+
32
+ ### Code Quality
33
+ ```bash
34
+ bundle exec rubocop
35
+ bundle exec rake # runs spec + rubocop
36
+ ```
37
+
38
+ ### TiDB Environment
39
+ ```bash
40
+ docker-compose up -d tidb
41
+ docker-compose exec tidb mysql -u root -h 127.0.0.1 -P 4000 -e "CREATE DATABASE IF NOT EXISTS ridgepole_test"
42
+ ```
43
+
44
+ ## Architecture
45
+
46
+ ### Extension Pattern
47
+ The gem uses module prepending/including to extend existing ActiveRecord functionality:
48
+ - `setup!` method loads trilogy adapter and injects modules
49
+ - `SchemaDumper` module prepended to ActiveRecord::SchemaDumper
50
+ - `TrilogyAdapter` module included in AbstractMysqlAdapter
51
+
52
+ ### Core Components
53
+ - **Main Extension** (`lib/ridgepole/ext/tidb.rb`): Entry point and setup
54
+ - **Schema Dumper** (`lib/ridgepole/ext/tidb/schema_dumper.rb`): Detects and dumps AUTO_RANDOM attributes by querying INFORMATION_SCHEMA.COLUMNS
55
+ - **Connection Adapters** (`lib/ridgepole/ext/tidb/connection_adapters.rb`): Adds TiDB detection and AUTO_RANDOM column support
56
+
57
+ ### TiDB Integration
58
+ - Detects TiDB by querying `@@tidb_version` or version strings
59
+ - AUTO_RANDOM columns identified via INFORMATION_SCHEMA queries
60
+ - Modifies column creation to add AUTO_RANDOM attribute (requires BIGINT PRIMARY KEY)
61
+
62
+ ### Test Configuration
63
+ Uses trilogy adapter connecting to TiDB on port 4000. Supports mock TiDB behavior for development and automatic test cleanup.
data/Dockerfile ADDED
@@ -0,0 +1,19 @@
1
+ FROM ruby:3.1
2
+
3
+ WORKDIR /app
4
+
5
+ # Install system dependencies for mysql2
6
+ RUN apt-get update -qq && \
7
+ apt-get install -y build-essential default-mysql-client default-libmysqlclient-dev \
8
+ pkg-config libssl-dev zlib1g-dev && \
9
+ rm -rf /var/lib/apt/lists/*
10
+
11
+ # Install dependencies
12
+ COPY Gemfile Gemfile.lock ridgepole-ext-tidb.gemspec ./
13
+ COPY lib/ridgepole/ext/tidb/version.rb lib/ridgepole/ext/tidb/
14
+
15
+ RUN bundle install
16
+
17
+ COPY . .
18
+
19
+ CMD ["bundle", "exec", "rspec"]
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Forgxisto.inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # Ridgepole::Ext::Tidb
2
+
3
+ ![Tests](https://github.com/forgxisto/ridgepole-ext-tidb/actions/workflows/test.yml/badge.svg)
4
+ ![Ruby Version](https://img.shields.io/badge/ruby-3.1%2B-red)
5
+ ![TiDB Compatibility](https://img.shields.io/badge/TiDB-AUTO__RANDOM-blue)
6
+
7
+ TiDBの`AUTO_RANDOM`カラム属性をサポートするRidgepole拡張機能です。この拡張により、TiDBの分散IDジェネレーション機能をSchemafile管理に統合できます。
8
+
9
+ ## 主な機能
10
+
11
+ - **AUTO_RANDOM検出**: TiDBのAUTO_RANDOMカラムを自動検出
12
+ - **スキーマダンプ対応**: AUTO_RANDOM属性をRidgefileに正確に出力
13
+ - **冪等性保証**: スキーマ適用の際の差分を正確に計算
14
+ - **trilogy アダプター対応**: mysql2の代わりにtrilogyを使用
15
+ - **Ruby 3.4+ 対応**: 最新のRubyバージョンに完全対応
16
+
17
+ ## インストール
18
+
19
+ Gemfileに以下を追加してください:
20
+
21
+ ```ruby
22
+ gem 'ridgepole-ext-tidb'
23
+ ```
24
+
25
+ そして以下を実行:
26
+
27
+ ```bash
28
+ $ bundle install
29
+ ```
30
+
31
+ または直接インストール:
32
+
33
+ ```bash
34
+ $ gem install ridgepole-ext-tidb
35
+ ```
36
+
37
+ ## 使用方法
38
+
39
+ ### 基本設定
40
+
41
+ ```ruby
42
+ require 'ridgepole'
43
+ require 'ridgepole/ext/tidb'
44
+
45
+ # ActiveRecord読み込み後にTiDB拡張をセットアップ
46
+ Ridgepole::Ext::Tidb.setup!
47
+
48
+ # Ridgepoleクライアントを設定
49
+ client = Ridgepole::Client.new({
50
+ adapter: 'trilogy', # trilogy アダプターを使用
51
+ host: 'localhost',
52
+ port: 4000,
53
+ username: 'root',
54
+ password: '',
55
+ database: 'your_database'
56
+ })
57
+ ```
58
+
59
+ ### Schemafileでの使用
60
+
61
+ ```ruby
62
+ # Schemafile
63
+ create_table "users", id: { type: :bigint, auto_random: true } do |t|
64
+ t.string :name, null: false
65
+ t.string :email, null: false
66
+ t.timestamps
67
+ end
68
+
69
+ create_table "posts", force: :cascade do |t|
70
+ t.bigint :id, auto_random: true, primary_key: true
71
+ t.bigint :user_id, null: false
72
+ t.string :title, null: false
73
+ t.text :content
74
+ t.timestamps
75
+ end
76
+ ```
77
+
78
+ ### CLI使用例
79
+
80
+ 環境変数またはdatabase.ymlファイルを用意してから実行:
81
+
82
+ ```bash
83
+ # 環境変数で設定
84
+ export TIDB_HOST=localhost
85
+ export TIDB_PORT=4000
86
+ export TIDB_USER=root
87
+ export TIDB_PASSWORD=""
88
+ export TIDB_DATABASE=your_database
89
+
90
+ # スキーマを適用
91
+ $ bundle exec ridgepole -c config/database.yml -E development -f Schemafile --apply
92
+
93
+ # ドライランで確認
94
+ $ bundle exec ridgepole -c config/database.yml -E development -f Schemafile --apply --dry-run
95
+
96
+ # スキーマをダンプ
97
+ $ bundle exec ridgepole -c config/database.yml -E development --export -o Schemafile
98
+ ```
99
+
100
+ ## データベース設定
101
+
102
+ ### database.yml例
103
+
104
+ プロジェクトルートに `config/database.yml` を作成:
105
+
106
+ ```yaml
107
+ development:
108
+ adapter: trilogy
109
+ host: <%= ENV['TIDB_HOST'] || 'localhost' %>
110
+ port: <%= ENV['TIDB_PORT'] || 4000 %>
111
+ username: <%= ENV['TIDB_USER'] || 'root' %>
112
+ password: <%= ENV['TIDB_PASSWORD'] || '' %>
113
+ database: <%= ENV['TIDB_DATABASE'] || 'your_app_development' %>
114
+ encoding: utf8mb4
115
+ collation: utf8mb4_unicode_ci
116
+
117
+ test:
118
+ adapter: trilogy
119
+ host: <%= ENV['TIDB_HOST'] || 'localhost' %>
120
+ port: <%= ENV['TIDB_PORT'] || 4000 %>
121
+ username: <%= ENV['TIDB_USER'] || 'root' %>
122
+ password: <%= ENV['TIDB_PASSWORD'] || '' %>
123
+ database: <%= ENV['TIDB_DATABASE'] || 'your_app_test' %>
124
+ encoding: utf8mb4
125
+ collation: utf8mb4_unicode_ci
126
+ ```
127
+
128
+ ## TiDB AUTO_RANDOMについて
129
+
130
+ TiDBの`AUTO_RANDOM`は、分散環境での重複しないID生成機能です:
131
+
132
+ - **ホットスポット回避**: 順番IDによるホットスポットを防ぐ
133
+ - **高性能**: 挿入パフォーマンスが向上
134
+ - **スケーラビリティ**: 水平スケーリングに適している
135
+
136
+ ### AUTO_RANDOMの仕組み
137
+
138
+ ```sql
139
+ -- TiDBでのAUTO_RANDOMテーブル例
140
+ CREATE TABLE users (
141
+ id BIGINT PRIMARY KEY AUTO_RANDOM,
142
+ name VARCHAR(100) NOT NULL
143
+ );
144
+ ```
145
+
146
+ このテーブルにデータを挿入すると、IDは自動的にランダムな値が割り当てられます。
147
+
148
+ ## 開発
149
+
150
+ ### 前提条件
151
+
152
+ - Ruby 3.1 以上
153
+ - TiDB 4.0 以上 (テスト用)
154
+ - Docker (テスト環境用)
155
+
156
+ ### セットアップ
157
+
158
+ ```bash
159
+ $ git clone https://github.com/forgxisto/ridgepole-ext-tidb.git
160
+ $ cd ridgepole-ext-tidb
161
+ $ bundle install
162
+ ```
163
+
164
+ ### テスト実行
165
+
166
+ ```bash
167
+ # 基本機能テスト(TiDBなしでも実行可能)
168
+ $ SKIP_TIDB_TESTS=1 bundle exec rspec
169
+
170
+ # TiDB統合テスト(Dockerが必要)
171
+ $ docker compose up -d
172
+ $ bundle exec rspec
173
+
174
+ # Docker環境でのテスト
175
+ $ docker compose run --rm test
176
+ ```
177
+
178
+ ### TiDBテスト環境
179
+
180
+ ```bash
181
+ # TiDBサービス起動
182
+ $ docker compose up -d tidb
183
+
184
+ # テスト用データベース作成
185
+ $ docker compose exec tidb mysql -u root -h 127.0.0.1 -P 4000 -e "CREATE DATABASE IF NOT EXISTS ridgepole_test"
186
+
187
+ # テスト実行
188
+ $ bundle exec rspec
189
+ ```
190
+
191
+ ## Contributing
192
+
193
+ 1. このリポジトリをフォーク
194
+ 2. フィーチャーブランチを作成 (`git checkout -b my-new-feature`)
195
+ 3. 変更をコミット (`git commit -am 'Add some feature'`)
196
+ 4. ブランチにプッシュ (`git push origin my-new-feature`)
197
+ 5. プルリクエストを作成
198
+
199
+ ## ライセンス
200
+
201
+ このgemは[MIT License](LICENSE.txt)の下でオープンソースとして利用可能です。
202
+
203
+ ## 参考リンク
204
+
205
+ - [Ridgepole](https://github.com/ridgepole/ridgepole) - スキーマ管理ツール
206
+ - [TiDB](https://github.com/pingcap/tidb) - 分散SQLデータベース
207
+ - [TiDB AUTO_RANDOM](https://docs.pingcap.com/tidb/stable/auto-random) - AUTO_RANDOMドキュメント
208
+ - [trilogy](https://github.com/trilogy-libraries/trilogy) - MySQLクライアントライブラリ
209
+
210
+ ## サポート
211
+
212
+ 問題や質問がある場合は、[Issues](https://github.com/forgxisto/ridgepole-ext-tidb/issues)にて報告してください。
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,36 @@
1
+ version: '3.8'
2
+
3
+ services:
4
+ tidb:
5
+ image: pingcap/tidb:latest
6
+ container_name: ridgepole-tidb-test
7
+ ports:
8
+ - "14000:4000"
9
+ - "14080:10080"
10
+ environment:
11
+ - MYSQL_ROOT_PASSWORD=
12
+ command:
13
+ - --store=mocktikv
14
+ - --host=0.0.0.0
15
+ - --advertise-address=0.0.0.0
16
+ healthcheck:
17
+ test: ["CMD", "sh", "-c", "nc -z 127.0.0.1 4000 || exit 1"]
18
+ interval: 10s
19
+ timeout: 5s
20
+ retries: 5
21
+ start_period: 30s
22
+
23
+ test:
24
+ build: .
25
+ depends_on:
26
+ - tidb
27
+ environment:
28
+ - TIDB_HOST=tidb
29
+ - TIDB_PORT=4000
30
+ - TIDB_USER=root
31
+ - TIDB_PASSWORD=
32
+ - TIDB_DATABASE=ridgepole_test
33
+ working_dir: /app
34
+ volumes:
35
+ - .:/app
36
+ command: bundle exec rspec
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ridgepole
4
+ module Ext
5
+ module Tidb
6
+ module ConnectionAdapters
7
+ module TrilogyAdapter
8
+ def self.included(base)
9
+ base.extend(ClassMethods)
10
+ end
11
+
12
+ module ClassMethods
13
+ # Check if this is a TiDB connection (class method)
14
+ def tidb?
15
+ @tidb ||= detect_tidb_connection
16
+ end
17
+
18
+ private
19
+
20
+ def detect_tidb_connection
21
+ # Try TiDB-specific query first
22
+ begin
23
+ result = select_value('SELECT @@tidb_version')
24
+ return true if result && !result.empty?
25
+ rescue StandardError
26
+ # Not a TiDB or query failed, continue
27
+ end
28
+
29
+ # Try version string detection
30
+ begin
31
+ result = select_value('SELECT VERSION()')
32
+ return true if result&.include?('TiDB')
33
+ rescue StandardError
34
+ # Version query failed, continue
35
+ end
36
+
37
+ # Try variables detection
38
+ begin
39
+ result = select_value("SHOW VARIABLES LIKE 'version_comment'")
40
+ return true if result&.include?('TiDB')
41
+ rescue StandardError
42
+ # Variables query failed
43
+ end
44
+
45
+ false
46
+ rescue StandardError => e
47
+ # Log error if logger available
48
+ warn "TiDB detection failed: #{e.message}" if defined?(Rails) && Rails.logger
49
+ false
50
+ end
51
+ end
52
+
53
+ # Check if this is a TiDB connection (instance method)
54
+ def tidb?
55
+ @tidb ||= self.class.tidb?
56
+ end
57
+
58
+ # Override create_table to support auto_random option in column definitions
59
+ def create_table(table_name, id: :default, primary_key: nil, force: nil, **options, &block)
60
+ # Extract id column options to check for auto_random
61
+ if id.is_a?(Hash) && id[:auto_random] && tidb?
62
+ # Convert id options for TiDB AUTO_RANDOM
63
+ id = convert_id_options_for_tidb(id)
64
+ end
65
+
66
+ super(table_name, id: id, primary_key: primary_key, force: force, **options, &block)
67
+ end
68
+
69
+ # Override add_column to support auto_random option
70
+ def add_column(table_name, column_name, type, **options)
71
+ auto_random = options.delete(:auto_random)
72
+
73
+ if auto_random && tidb?
74
+ # Validate AUTO_RANDOM constraints
75
+ validate_auto_random_constraints!(table_name, column_name, type, options)
76
+
77
+ # Call super first to create the basic column
78
+ super(table_name, column_name, type, **options)
79
+
80
+ # Then modify it to add AUTO_RANDOM
81
+ modify_column_for_auto_random(table_name, column_name, type, options)
82
+ else
83
+ super(table_name, column_name, type, **options)
84
+ end
85
+ end
86
+
87
+ # Check if a column has AUTO_RANDOM attribute
88
+ def auto_random_column?(table_name, column_name)
89
+ return false unless tidb?
90
+
91
+ sql = <<~SQL
92
+ SELECT EXTRA
93
+ FROM INFORMATION_SCHEMA.COLUMNS
94
+ WHERE TABLE_SCHEMA = DATABASE()
95
+ AND TABLE_NAME = #{quote(table_name.to_s)}
96
+ AND COLUMN_NAME = #{quote(column_name.to_s)}
97
+ SQL
98
+
99
+ result = select_value(sql)
100
+ return false unless result
101
+
102
+ result.downcase.include?('auto_random')
103
+ rescue StandardError => e
104
+ warn "Failed to check AUTO_RANDOM attribute: #{e.message}" if defined?(Rails) && Rails.logger
105
+ false
106
+ end
107
+
108
+ private
109
+
110
+ def convert_id_options_for_tidb(id_options)
111
+ # Ensure proper type for AUTO_RANDOM
112
+ id_options = id_options.dup
113
+ id_options[:type] ||= :bigint
114
+
115
+ # AUTO_RANDOM requires bigint type
116
+ unless %i[bigint integer].include?(id_options[:type])
117
+ raise Ridgepole::Ext::Tidb::AutoRandomConstraintError,
118
+ "AUTO_RANDOM requires :bigint or :integer type, got #{id_options[:type]}"
119
+ end
120
+
121
+ id_options
122
+ end
123
+
124
+ def modify_column_for_auto_random(table_name, column_name, type, _options)
125
+ # Build the SQL type string
126
+ sql_type = case type
127
+ when :bigint then 'BIGINT'
128
+ when :integer then 'INT'
129
+ else
130
+ raise Ridgepole::Ext::Tidb::UnsupportedFeatureError,
131
+ "Unsupported type for AUTO_RANDOM: #{type}"
132
+ end
133
+
134
+ # Add AUTO_RANDOM and PRIMARY KEY
135
+ alter_sql = "ALTER TABLE #{quote_table_name(table_name)} " \
136
+ "MODIFY COLUMN #{quote_column_name(column_name)} " \
137
+ "#{sql_type} AUTO_RANDOM PRIMARY KEY"
138
+
139
+ begin
140
+ execute(alter_sql)
141
+ rescue StandardError => e
142
+ # Re-raise with more context
143
+ raise Ridgepole::Ext::Tidb::Error,
144
+ "Failed to add AUTO_RANDOM attribute to #{table_name}.#{column_name}: #{e.message}"
145
+ end
146
+ end
147
+
148
+ def validate_auto_random_constraints!(table_name, _column_name, type, options)
149
+ # AUTO_RANDOM must be on a bigint or int column
150
+ unless %i[bigint integer].include?(type)
151
+ raise Ridgepole::Ext::Tidb::AutoRandomConstraintError,
152
+ "AUTO_RANDOM requires :bigint or :integer column type, got #{type}"
153
+ end
154
+
155
+ # AUTO_RANDOM must be primary key
156
+ if options[:primary_key] == false
157
+ raise Ridgepole::Ext::Tidb::AutoRandomConstraintError,
158
+ 'AUTO_RANDOM column must be a primary key'
159
+ end
160
+
161
+ # Check if table already has a primary key (only if table exists)
162
+ return unless table_exists?(table_name)
163
+
164
+ begin
165
+ existing_pk = primary_keys(table_name)
166
+ unless existing_pk.empty?
167
+ raise Ridgepole::Ext::Tidb::AutoRandomConstraintError,
168
+ "Cannot add AUTO_RANDOM column to table with existing primary key: #{existing_pk.join(', ')}"
169
+ end
170
+ rescue NoMethodError
171
+ # primary_keys method doesn't exist - skip check
172
+ rescue StandardError => e
173
+ # Only log database-related errors, re-raise validation errors
174
+ raise if e.is_a?(Ridgepole::Ext::Tidb::AutoRandomConstraintError)
175
+
176
+ warn "Warning: Could not verify primary key constraints: #{e.message}" if defined?(Rails) && Rails.logger
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ridgepole
4
+ module Ext
5
+ module Tidb
6
+ module SchemaDumper
7
+ # Override prepare_column_options to include auto_random
8
+ def prepare_column_options(column)
9
+ spec = super(column)
10
+
11
+ # Add auto_random if this column has the attribute
12
+ spec[:auto_random] = true if tidb_connection? && auto_random_column?(current_table_name, column.name)
13
+
14
+ spec
15
+ end
16
+
17
+ private
18
+
19
+ def tidb_connection?
20
+ connection.respond_to?(:tidb?) && connection.tidb?
21
+ rescue StandardError
22
+ false
23
+ end
24
+
25
+ def current_table_name
26
+ # Get the current table name being processed
27
+ # In ActiveRecord schema dumper context, @table holds the current table name
28
+ return @table.to_s if instance_variable_defined?(:@table) && @table
29
+
30
+ # Fallback: try to extract from the column if it has table information
31
+ nil
32
+ end
33
+
34
+ def auto_random_column?(table_name, column_name)
35
+ return false unless table_name && column_name
36
+ return false unless tidb_connection?
37
+
38
+ # Use the connection's auto_random_column? method if available
39
+ return connection.auto_random_column?(table_name, column_name) if connection.respond_to?(:auto_random_column?)
40
+
41
+ # Fallback: direct query
42
+ check_auto_random_attribute(table_name, column_name)
43
+ end
44
+
45
+ def check_auto_random_attribute(table_name, column_name)
46
+ sql = <<~SQL
47
+ SELECT EXTRA
48
+ FROM INFORMATION_SCHEMA.COLUMNS
49
+ WHERE TABLE_SCHEMA = DATABASE()
50
+ AND TABLE_NAME = #{connection.quote(table_name.to_s)}
51
+ AND COLUMN_NAME = #{connection.quote(column_name.to_s)}
52
+ SQL
53
+
54
+ result = connection.select_value(sql)
55
+ return false unless result
56
+
57
+ result.downcase.include?('auto_random')
58
+ rescue StandardError => e
59
+ # Log warning if possible
60
+ if defined?(Rails) && Rails.logger
61
+ warn "Failed to check AUTO_RANDOM attribute for #{table_name}.#{column_name}: #{e.message}"
62
+ end
63
+ false
64
+ end
65
+
66
+ # Override table method to track current table name
67
+ def table(table_name, stream)
68
+ @table = table_name
69
+ super(table_name, stream)
70
+ ensure
71
+ @table = nil
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ridgepole
4
+ module Ext
5
+ module Tidb
6
+ VERSION = '0.1.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'tidb/version'
4
+
5
+ module Ridgepole
6
+ module Ext
7
+ module Tidb
8
+ class Error < StandardError; end
9
+ class TidbConnectionError < Error; end
10
+ class AutoRandomConstraintError < Error; end
11
+ class UnsupportedFeatureError < Error; end
12
+
13
+ # Initialize the TiDB extension for Ridgepole
14
+ def self.setup!
15
+ # Ensure trilogy adapter is loaded
16
+ load_trilogy_adapter
17
+
18
+ # Load the extension modules when setup is called
19
+ require_relative 'tidb/schema_dumper'
20
+ require_relative 'tidb/connection_adapters'
21
+
22
+ # Only extend if ActiveRecord is already loaded
23
+ if defined?(::ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter)
24
+ ::ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.include(
25
+ Ridgepole::Ext::Tidb::ConnectionAdapters::TrilogyAdapter
26
+ )
27
+ end
28
+
29
+ return unless defined?(::ActiveRecord::SchemaDumper)
30
+
31
+ ::ActiveRecord::SchemaDumper.prepend(
32
+ Ridgepole::Ext::Tidb::SchemaDumper
33
+ )
34
+ end
35
+
36
+ def self.load_trilogy_adapter
37
+ require 'trilogy'
38
+ require 'activerecord-trilogy-adapter'
39
+ require 'active_record/connection_adapters/trilogy_adapter'
40
+ rescue LoadError => e
41
+ warn "Warning: Failed to load trilogy adapter: #{e.message}"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/ridgepole/ext/tidb'
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/ridgepole/ext/tidb/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'ridgepole-ext-tidb'
7
+ spec.version = Ridgepole::Ext::Tidb::VERSION
8
+ spec.authors = ['ikad']
9
+ spec.email = ['info@forgxisto.com']
10
+
11
+ spec.summary = 'TiDB AUTO_RANDOM support extension for Ridgepole'
12
+ spec.description = "Extends Ridgepole to support TiDB's AUTO_RANDOM column attribute for seamless schema management"
13
+ spec.homepage = 'https://github.com/forgxisto/ridgepole-ext-tidb'
14
+ spec.license = 'MIT'
15
+ spec.required_ruby_version = '>= 3.1.0'
16
+
17
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
18
+ spec.metadata['homepage_uri'] = spec.homepage
19
+ spec.metadata['source_code_uri'] = 'https://github.com/forgxisto/ridgepole-ext-tidb'
20
+ spec.metadata['changelog_uri'] = 'https://github.com/forgxisto/ridgepole-ext-tidb/blob/main/CHANGELOG.md'
21
+
22
+ # Specify which files should be added to the gem when it is released.
23
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (File.expand_path(f) == __FILE__) ||
27
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile]) ||
28
+ f.end_with?('.gem')
29
+ end
30
+ end
31
+ spec.bindir = 'exe'
32
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
33
+ spec.require_paths = ['lib']
34
+
35
+ # Dependencies
36
+ spec.add_dependency 'ridgepole'
37
+
38
+ # Development dependencies
39
+ spec.add_development_dependency 'activerecord-trilogy-adapter'
40
+ spec.add_development_dependency 'rake'
41
+ spec.add_development_dependency 'rspec'
42
+ spec.add_development_dependency 'rubocop'
43
+ spec.add_development_dependency 'trilogy'
44
+
45
+ # Ruby 3.4+ compatibility
46
+ spec.add_development_dependency 'benchmark'
47
+ spec.add_development_dependency 'bigdecimal'
48
+ spec.add_development_dependency 'logger'
49
+ spec.add_development_dependency 'mutex_m'
50
+ end
metadata ADDED
@@ -0,0 +1,199 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ridgepole-ext-tidb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - ikad
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 2025-09-06 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ridgepole
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: activerecord-trilogy-adapter
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rubocop
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: trilogy
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: benchmark
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: bigdecimal
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: logger
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: mutex_m
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ description: Extends Ridgepole to support TiDB's AUTO_RANDOM column attribute for
153
+ seamless schema management
154
+ email:
155
+ - info@forgxisto.com
156
+ executables: []
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".rspec"
161
+ - CHANGELOG.md
162
+ - CLAUDE.md
163
+ - Dockerfile
164
+ - LICENSE.txt
165
+ - README.md
166
+ - Rakefile
167
+ - docker-compose.yml
168
+ - lib/ridgepole-ext-tidb.rb
169
+ - lib/ridgepole/ext/tidb.rb
170
+ - lib/ridgepole/ext/tidb/connection_adapters.rb
171
+ - lib/ridgepole/ext/tidb/schema_dumper.rb
172
+ - lib/ridgepole/ext/tidb/version.rb
173
+ - ridgepole-ext-tidb.gemspec
174
+ homepage: https://github.com/forgxisto/ridgepole-ext-tidb
175
+ licenses:
176
+ - MIT
177
+ metadata:
178
+ allowed_push_host: https://rubygems.org
179
+ homepage_uri: https://github.com/forgxisto/ridgepole-ext-tidb
180
+ source_code_uri: https://github.com/forgxisto/ridgepole-ext-tidb
181
+ changelog_uri: https://github.com/forgxisto/ridgepole-ext-tidb/blob/main/CHANGELOG.md
182
+ rdoc_options: []
183
+ require_paths:
184
+ - lib
185
+ required_ruby_version: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: 3.1.0
190
+ required_rubygems_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ requirements: []
196
+ rubygems_version: 3.6.2
197
+ specification_version: 4
198
+ summary: TiDB AUTO_RANDOM support extension for Ridgepole
199
+ test_files: []