activerecord-tidb-adapter 0.1.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ee75804c5fe17ede37170f3856799e9ecd695aeb485ff1d41884e2cf92f3f8c
4
- data.tar.gz: c59c71505842a93e360841d1607b8ce10eb5cbaa4cec4e0fef960cec7b619f18
3
+ metadata.gz: 7e086031695fe37ee336da6d8722b947018501e8e3ce90051d00b83d7aebc742
4
+ data.tar.gz: af2a51ca724563cc076f5bf0ef018eb0bd8bb15a756947c316e374f30b100396
5
5
  SHA512:
6
- metadata.gz: bb073e2194896f6327234862b90c1511a9ffcf63ca4bdda8073fd24b321bbc3227f920cd3a2fb11aacab2cd2ff281b1f4526eae3f1cf6bb93f4fe75da08ca15b
7
- data.tar.gz: 310d87aac24163da5443928c41240cab11d6bdf1acabe9ec46cbf2e7af62b55c74054f46369b8e9eefda05d805af8a5cb2057afa1f0e8be75a369fd39771c2cd
6
+ metadata.gz: f8e0c432e30009832966b1a70d3a2842e9b64c9fa417c3744952c0d1feb6d64a956308496dcab19ba36ac2c9da7e48a0fa94d69ad14fa4a5d2849781b6fa8bcd
7
+ data.tar.gz: 844d1511bb454844e4d179b7c519dbcac9398a9e4df6ad3df85f8396d1f7b687ef3f13a77330f1126c4b6479fb8fe1f472c5feae8b3002f386e964626d005120
data/.gitignore CHANGED
@@ -6,3 +6,8 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /sqlnet.log
10
+ /test/config.yml
11
+ /test/db/
12
+ /test/fixtures/*.sqlite*
13
+ /debug.log
data/Gemfile CHANGED
@@ -8,7 +8,16 @@ gemspec
8
8
  gem 'rake', '~> 13.0'
9
9
 
10
10
  gem 'minitest', '~> 5.0'
11
+ gem 'minitest-excludes', '~> 2.0'
11
12
 
12
13
  gem 'pry'
13
14
 
14
15
  gem 'rubocop', '~> 1.18'
16
+
17
+ gem 'rails', git: 'https://github.com/tidb-incubator/rails.git', branch: '6-1-stable'
18
+
19
+ gem 'byebug', '~> 11.1'
20
+
21
+ gem 'sqlite3', '~> 1.4'
22
+
23
+ gem 'pg', '~> 1.2'
data/Gemfile.lock CHANGED
@@ -1,57 +1,174 @@
1
- PATH
2
- remote: .
3
- specs:
4
- activerecord-tidb-adapter (0.1.1)
5
- activerecord (~> 6.1)
6
- mysql2
7
-
8
- GEM
9
- remote: https://rubygems.org/
1
+ GIT
2
+ remote: https://github.com/tidb-incubator/rails.git
3
+ revision: e2a3d3fb2ee24c709b9f32221fbcb66cfda5acfc
4
+ branch: 6-1-stable
10
5
  specs:
6
+ actioncable (6.1.4)
7
+ actionpack (= 6.1.4)
8
+ activesupport (= 6.1.4)
9
+ nio4r (~> 2.0)
10
+ websocket-driver (>= 0.6.1)
11
+ actionmailbox (6.1.4)
12
+ actionpack (= 6.1.4)
13
+ activejob (= 6.1.4)
14
+ activerecord (= 6.1.4)
15
+ activestorage (= 6.1.4)
16
+ activesupport (= 6.1.4)
17
+ mail (>= 2.7.1)
18
+ actionmailer (6.1.4)
19
+ actionpack (= 6.1.4)
20
+ actionview (= 6.1.4)
21
+ activejob (= 6.1.4)
22
+ activesupport (= 6.1.4)
23
+ mail (~> 2.5, >= 2.5.4)
24
+ rails-dom-testing (~> 2.0)
25
+ actionpack (6.1.4)
26
+ actionview (= 6.1.4)
27
+ activesupport (= 6.1.4)
28
+ rack (~> 2.0, >= 2.0.9)
29
+ rack-test (>= 0.6.3)
30
+ rails-dom-testing (~> 2.0)
31
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
32
+ actiontext (6.1.4)
33
+ actionpack (= 6.1.4)
34
+ activerecord (= 6.1.4)
35
+ activestorage (= 6.1.4)
36
+ activesupport (= 6.1.4)
37
+ nokogiri (>= 1.8.5)
38
+ actionview (6.1.4)
39
+ activesupport (= 6.1.4)
40
+ builder (~> 3.1)
41
+ erubi (~> 1.4)
42
+ rails-dom-testing (~> 2.0)
43
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
44
+ activejob (6.1.4)
45
+ activesupport (= 6.1.4)
46
+ globalid (>= 0.3.6)
11
47
  activemodel (6.1.4)
12
48
  activesupport (= 6.1.4)
13
49
  activerecord (6.1.4)
14
50
  activemodel (= 6.1.4)
15
51
  activesupport (= 6.1.4)
52
+ activestorage (6.1.4)
53
+ actionpack (= 6.1.4)
54
+ activejob (= 6.1.4)
55
+ activerecord (= 6.1.4)
56
+ activesupport (= 6.1.4)
57
+ marcel (~> 1.0.0)
58
+ mini_mime (>= 1.1.0)
16
59
  activesupport (6.1.4)
17
60
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
61
  i18n (>= 1.6, < 2)
19
62
  minitest (>= 5.1)
20
63
  tzinfo (~> 2.0)
21
64
  zeitwerk (~> 2.3)
65
+ rails (6.1.4)
66
+ actioncable (= 6.1.4)
67
+ actionmailbox (= 6.1.4)
68
+ actionmailer (= 6.1.4)
69
+ actionpack (= 6.1.4)
70
+ actiontext (= 6.1.4)
71
+ actionview (= 6.1.4)
72
+ activejob (= 6.1.4)
73
+ activemodel (= 6.1.4)
74
+ activerecord (= 6.1.4)
75
+ activestorage (= 6.1.4)
76
+ activesupport (= 6.1.4)
77
+ bundler (>= 1.15.0)
78
+ railties (= 6.1.4)
79
+ sprockets-rails (>= 2.0.0)
80
+ railties (6.1.4)
81
+ actionpack (= 6.1.4)
82
+ activesupport (= 6.1.4)
83
+ method_source
84
+ rake (>= 0.13)
85
+ thor (~> 1.0)
86
+
87
+ PATH
88
+ remote: .
89
+ specs:
90
+ activerecord-tidb-adapter (0.5.0)
91
+ activerecord (~> 6.1)
92
+ mysql2
93
+
94
+ GEM
95
+ remote: https://rubygems.org/
96
+ specs:
22
97
  ast (2.4.2)
98
+ builder (3.2.4)
99
+ byebug (11.1.3)
23
100
  coderay (1.1.3)
24
101
  concurrent-ruby (1.1.9)
102
+ crass (1.0.6)
103
+ erubi (1.10.0)
104
+ globalid (0.5.2)
105
+ activesupport (>= 5.0)
25
106
  i18n (1.8.10)
26
107
  concurrent-ruby (~> 1.0)
108
+ loofah (2.12.0)
109
+ crass (~> 1.0.2)
110
+ nokogiri (>= 1.5.9)
111
+ mail (2.7.1)
112
+ mini_mime (>= 0.1.1)
113
+ marcel (1.0.1)
27
114
  method_source (1.0.0)
115
+ mini_mime (1.1.0)
28
116
  minitest (5.14.4)
117
+ minitest-excludes (2.0.1)
118
+ minitest (~> 5.0)
29
119
  mysql2 (0.5.3)
120
+ nio4r (2.5.8)
121
+ nokogiri (1.12.3-x86_64-darwin)
122
+ racc (~> 1.4)
123
+ nokogiri (1.12.3-x86_64-linux)
124
+ racc (~> 1.4)
30
125
  parallel (1.20.1)
31
126
  parser (3.0.2.0)
32
127
  ast (~> 2.4.1)
128
+ pg (1.2.3)
33
129
  pry (0.14.1)
34
130
  coderay (~> 1.1)
35
131
  method_source (~> 1.0)
132
+ racc (1.5.2)
133
+ rack (2.2.3)
134
+ rack-test (1.1.0)
135
+ rack (>= 1.0, < 3)
136
+ rails-dom-testing (2.0.3)
137
+ activesupport (>= 4.2.0)
138
+ nokogiri (>= 1.6)
139
+ rails-html-sanitizer (1.3.0)
140
+ loofah (~> 2.3)
36
141
  rainbow (3.0.0)
37
142
  rake (13.0.6)
38
143
  regexp_parser (2.1.1)
39
144
  rexml (3.2.5)
40
- rubocop (1.18.4)
145
+ rubocop (1.19.0)
41
146
  parallel (~> 1.10)
42
147
  parser (>= 3.0.0.0)
43
148
  rainbow (>= 2.2.2, < 4.0)
44
149
  regexp_parser (>= 1.8, < 3.0)
45
150
  rexml
46
- rubocop-ast (>= 1.8.0, < 2.0)
151
+ rubocop-ast (>= 1.9.1, < 2.0)
47
152
  ruby-progressbar (~> 1.7)
48
153
  unicode-display_width (>= 1.4.0, < 3.0)
49
- rubocop-ast (1.8.0)
154
+ rubocop-ast (1.10.0)
50
155
  parser (>= 3.0.1.1)
51
156
  ruby-progressbar (1.11.0)
157
+ sprockets (4.0.2)
158
+ concurrent-ruby (~> 1.0)
159
+ rack (> 1, < 3)
160
+ sprockets-rails (3.2.2)
161
+ actionpack (>= 4.0)
162
+ activesupport (>= 4.0)
163
+ sprockets (>= 3.0.0)
164
+ sqlite3 (1.4.2)
165
+ thor (1.1.0)
52
166
  tzinfo (2.0.4)
53
167
  concurrent-ruby (~> 1.0)
54
168
  unicode-display_width (2.0.0)
169
+ websocket-driver (0.7.5)
170
+ websocket-extensions (>= 0.1.0)
171
+ websocket-extensions (0.1.5)
55
172
  zeitwerk (2.4.2)
56
173
 
57
174
  PLATFORMS
@@ -60,10 +177,15 @@ PLATFORMS
60
177
 
61
178
  DEPENDENCIES
62
179
  activerecord-tidb-adapter!
180
+ byebug (~> 11.1)
63
181
  minitest (~> 5.0)
182
+ minitest-excludes (~> 2.0)
183
+ pg (~> 1.2)
64
184
  pry
185
+ rails!
65
186
  rake (~> 13.0)
66
187
  rubocop (~> 1.18)
188
+ sqlite3 (~> 1.4)
67
189
 
68
190
  BUNDLED WITH
69
191
  2.2.25
data/README.md CHANGED
@@ -21,7 +21,7 @@ Or install it yourself as:
21
21
 
22
22
  ## Usage
23
23
 
24
- database.yml
24
+ config/database.yml
25
25
 
26
26
  ```yml
27
27
  default: &default
@@ -44,16 +44,119 @@ development:
44
44
 
45
45
  * demo repo with rails 6.1.4: https://github.com/hooopo/activerecord-tidb-adapter-demo
46
46
 
47
+ ## TiDB features
48
+
49
+ **[Sequence](https://docs.pingcap.com/tidb/stable/sql-statement-create-sequence)**
50
+
51
+ Sequence as primary key
52
+
53
+ ```ruby
54
+ class TestSeq < ActiveRecord::Migration[6.1]
55
+ def up
56
+ # more options: increment, min_value, cycle, cache
57
+ create_sequence :orders_seq, start: 1024
58
+ create_table :orders, id: false do |t|
59
+ t.primary_key :id, default: -> { "nextval(orders_seq)" }
60
+ t.string :name
61
+ end
62
+ end
63
+
64
+ def down
65
+ drop_table :orders
66
+ drop_sequence :orders_seq
67
+ end
68
+ end
69
+ ```
70
+
71
+ Generated DDL
72
+ ```sql
73
+ mysql> show create table orders_seq\G;
74
+ *************************** 1. row ***************************
75
+ Sequence: orders_seq
76
+ Create Sequence: CREATE SEQUENCE `orders_seq` start with 1024 minvalue 1 maxvalue 9223372036854775806 increment by 1 nocache nocycle ENGINE=InnoDB
77
+
78
+ mysql> show create table orders\G;
79
+ *************************** 1. row ***************************
80
+ Table: orders
81
+ Create Table: CREATE TABLE `orders` (
82
+ `id` bigint(20) NOT NULL DEFAULT nextval(`activerecord_tidb_adapter_demo_development`.`orders_seq`),
83
+ `name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
84
+ PRIMARY KEY (`id`) /*T![clustered_index] CLUSTERED */
85
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
86
+
87
+ ```
88
+
89
+ This gem also adds a few helpers to interact with `SEQUENCE`
90
+
91
+ ```ruby
92
+ # Advance sequence and return new value
93
+ ActiveRecord::Base.nextval("numbers")
94
+
95
+ # Return value most recently obtained with nextval for specified sequence.
96
+ ActiveRecord::Base.lastval("numbers")
97
+
98
+ # Set sequence's current value
99
+ ActiveRecord::Base.setval("numbers", 1234)
100
+ ```
101
+
102
+
103
+ ## Setting up local TiDB server
104
+
105
+ Install [tiup](https://github.com/pingcap/tiup)
106
+
107
+ ```shell
108
+ $ curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
109
+ ```
110
+ Starting TiDB playground
111
+
112
+ ```shell
113
+ $ tiup playground nightly
114
+ ```
115
+
47
116
  ## Development
48
117
 
49
118
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
50
119
 
51
120
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
52
121
 
122
+ ## Testing
123
+
124
+ install gems
125
+
126
+ ```
127
+ bundle install
128
+ ```
129
+
130
+ start tidb server
131
+
132
+ ```
133
+ tiup playground nightly
134
+ ```
135
+
136
+ create database for testing
137
+
138
+ ```
139
+ MYSQL_USER=root MYSQL_HOST=127.0.0.1 MYSQL_PORT=4000 tidb=1 ARCONN=tidb bundle exec rake db:tidb:rebuild
140
+
141
+ ```
142
+
143
+ run tidb adapter tests and activerecord buildin tests
144
+
145
+ ```
146
+ MYSQL_USER=root MYSQL_HOST=127.0.0.1 MYSQL_PORT=4000 tidb=1 ARCONN=tidb bundle exec rake test:tidb
147
+
148
+ ```
149
+
150
+ run **ONLY** tidb adapter tests using `ONLY_TEST_TIDB` env:
151
+
152
+ ```
153
+ ONLY_TEST_TIDB=1 MYSQL_USER=root MYSQL_HOST=127.0.0.1 MYSQL_PORT=4000 tidb=1 ARCONN=tidb bundle exec rake test:tidb
154
+ ```
155
+
53
156
  ## Contributing
54
157
 
55
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/activerecord-tidb-adapter.
158
+ Bug reports and pull requests are welcome on GitHub at https://github.com/pingcap/activerecord-tidb-adapter.
56
159
 
57
160
  ## License
58
161
 
59
- Apache 2.0
162
+ Apache 2.0
data/Rakefile CHANGED
@@ -2,11 +2,56 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rake/testtask'
5
+ require 'pry'
5
6
 
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << 'test'
8
- t.libs << 'lib'
9
- t.test_files = FileList['test/**/*_test.rb']
7
+ require_relative 'test/support/paths_tidb'
8
+ require_relative 'test/support/rake_helpers'
9
+ require_relative 'test/support/config'
10
+
11
+ task test: ['test:tidb']
12
+ task default: [:test]
13
+
14
+ namespace :test do
15
+ Rake::TestTask.new('tidb') do |t|
16
+ t.libs = ARTest::TiDB.test_load_paths
17
+ t.test_files = test_files
18
+ t.warning = !ENV['WARNING'].nil?
19
+ t.verbose = false
20
+ end
21
+
22
+ task 'tidb:env' do
23
+ ENV['ARCONN'] = 'tidb'
24
+ ENV['tidb'] = '1'
25
+ end
26
+ end
27
+
28
+ task 'test:tidb' => 'test:tidb:env'
29
+
30
+ namespace :db do
31
+ namespace :tidb do
32
+ connection_arguments = lambda do |connection_name|
33
+ config = ARTest.config['connections']['tidb'][connection_name]
34
+ ["--user=#{config['username']}", "--password=#{config['password']}", "--port=#{config['port']}",
35
+ ("--host=#{config['host']}" if config['host'])].join(' ')
36
+ end
37
+
38
+ desc 'Build the TiDB test databases'
39
+ task :build do
40
+ config = ARTest.config['connections']['tidb']
41
+ `mysql #{connection_arguments['arunit']} -e "create DATABASE #{config['arunit']['database']} DEFAULT CHARACTER SET utf8mb4 COLLATE #{config['arunit']['collation']}"`
42
+ `mysql #{connection_arguments['arunit2']} -e "create DATABASE #{config['arunit2']['database']} DEFAULT CHARACTER SET utf8mb4 COLLATE #{config['arunit2']['collation']}"`
43
+ end
44
+
45
+ desc 'Drop the TiDB test databases'
46
+ task :drop do
47
+ config = ARTest.config['connections']['tidb']
48
+ `mysqladmin #{connection_arguments['arunit']} -f drop #{config['arunit']['database']}`
49
+ `mysqladmin #{connection_arguments['arunit2']} -f drop #{config['arunit2']['database']}`
50
+ end
51
+
52
+ desc 'Rebuild the TiDB test databases'
53
+ task rebuild: %i[drop build]
54
+ end
10
55
  end
11
56
 
12
- task default: :test
57
+ task build_mysql_databases: 'db:mysql:build'
data/config.toml ADDED
@@ -0,0 +1 @@
1
+ new_collations_enabled_on_first_bootstrap = true
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Type
5
+ class << self
6
+ def adapter_name_from(_model)
7
+ :mysql
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,13 +4,14 @@ require 'active_record/connection_adapters'
4
4
  require 'active_record/connection_adapters/mysql2_adapter'
5
5
  require 'active_record/connection_adapters/tidb/setup'
6
6
  require_relative '../../version'
7
+ require_relative '../sequence'
7
8
 
8
9
  ActiveRecord::ConnectionAdapters::Tidb.initial_setup
9
10
 
10
11
  module ActiveRecord
11
- module ConnectionHandling #:nodoc:
12
+ module ConnectionHandling # :nodoc:
12
13
  # Establishes a connection to the database that's used by all Active Record objects.
13
- def tidb_connection(config) #:nodoc:
14
+ def tidb_connection(config) # :nodoc:
14
15
  config = config.symbolize_keys
15
16
  config[:flags] ||= 0
16
17
 
@@ -31,7 +32,118 @@ module ActiveRecord
31
32
 
32
33
  module ConnectionAdapters
33
34
  class TidbAdapter < Mysql2Adapter
35
+ include ActiveRecord::Sequence::Adapter
34
36
  ADAPTER_NAME = 'Tidb'
37
+
38
+ def supports_savepoints?
39
+ false
40
+ end
41
+
42
+ def supports_foreign_keys?
43
+ false
44
+ end
45
+
46
+ def supports_bulk_alter?
47
+ false
48
+ end
49
+
50
+ def supports_advisory_locks?
51
+ false
52
+ end
53
+
54
+ def supports_optimizer_hints?
55
+ true
56
+ end
57
+
58
+ def supports_json?
59
+ true
60
+ end
61
+
62
+ def supports_index_sort_order?
63
+ # TODO: check TiDB version
64
+ true
65
+ end
66
+
67
+ def supports_expression_index?
68
+ true
69
+ end
70
+
71
+ def supports_common_table_expressions?
72
+ tidb_version >= '5.1.0'
73
+ end
74
+
75
+ def transaction_isolation_levels
76
+ {
77
+ read_committed: 'READ COMMITTED',
78
+ repeatable_read: 'REPEATABLE READ'
79
+ }
80
+ end
81
+
82
+ def initialize(connection, logger, conn_params, config)
83
+ super(connection, logger, conn_params, config)
84
+
85
+ tidb_version_string = query_value('select version()')
86
+ @tidb_version = tidb_version_string[/TiDB-v([0-9.]+)/, 1]
87
+ end
88
+
89
+ def tidb_version_string
90
+ @tidb_version
91
+ end
92
+
93
+ def tidb_version
94
+ Version.new(tidb_version_string)
95
+ end
96
+
97
+ def self.database_exists?(config)
98
+ !ActiveRecord::Base.tidb_connection(config).nil?
99
+ rescue ActiveRecord::NoDatabaseError
100
+ false
101
+ end
102
+
103
+ def insert(arel, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
104
+ sql, binds = to_sql_and_binds(arel, binds)
105
+ value = exec_insert(sql, name, binds, pk, sequence_name)
106
+ return id_value if id_value.present?
107
+
108
+ model = arel.ast.relation.instance_variable_get(:@klass)
109
+ pk_def = schema_cache.columns_hash(model.table_name)[pk]
110
+ if pk_def&.default_function && pk_def.default_function =~ /nextval/
111
+ query_value("SELECT #{pk_def.default_function.sub('nextval', 'lastval')}")
112
+ elsif model.sequence_name
113
+ ActiveRecord::Base.lastval(model.sequence_name)
114
+ else
115
+ last_inserted_id(value)
116
+ end
117
+ end
118
+ alias create insert
119
+
120
+ def new_column_from_field(_table_name, field)
121
+ type_metadata = fetch_type_metadata(field[:Type], field[:Extra])
122
+ default = field[:Default]
123
+ default_function = nil
124
+
125
+ if type_metadata.type == :datetime && /\ACURRENT_TIMESTAMP(?:\([0-6]?\))?\z/i.match?(default)
126
+ default_function = default
127
+ default = nil
128
+ elsif type_metadata.extra == 'DEFAULT_GENERATED'
129
+ default = +"(#{default})" unless default.start_with?('(')
130
+ default_function = default
131
+ default = nil
132
+ elsif default.to_s =~ /nextval/i
133
+ default_function = default
134
+ default = nil
135
+ end
136
+
137
+ MySQL::Column.new(
138
+ field[:Field],
139
+ default,
140
+ type_metadata,
141
+ field[:Null] == 'YES',
142
+ default_function,
143
+ collation: field[:Collation],
144
+ comment: field[:Comment].presence
145
+ )
146
+ end
35
147
  end
36
148
  end
37
149
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/all'
4
+ require 'active_record'
5
+ require 'active_record/connection_adapters/mysql/schema_dumper'
6
+ require 'active_record/migration/command_recorder'
7
+ require 'active_record/schema_dumper'
8
+
9
+ module ActiveRecord
10
+ module Sequence
11
+ require 'active_record/sequence/command_recorder'
12
+ require 'active_record/sequence/adapter'
13
+ require 'active_record/sequence/schema_dumper'
14
+ require 'active_record/sequence/model_methods'
15
+ end
16
+ end
17
+
18
+ ActiveRecord::Migration::CommandRecorder.include(ActiveRecord::Sequence::CommandRecorder)
19
+
20
+ ActiveRecord::SchemaDumper.prepend(ActiveRecord::Sequence::SchemaDumper)
21
+ ActiveRecord::Base.extend(ActiveRecord::Sequence::ModelMethods)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Sequence
5
+ module Adapter
6
+ def check_sequences
7
+ select_all(
8
+ 'SELECT * FROM information_schema.sequences ORDER BY sequence_name'
9
+ ).to_a
10
+ end
11
+
12
+ def create_sequence(name, options = {})
13
+ increment = options[:increment] || options[:step]
14
+ name = quote_column_name(name)
15
+
16
+ sql = ["CREATE SEQUENCE IF NOT EXISTS #{name}"]
17
+ sql << "INCREMENT BY #{increment}" if increment
18
+ sql << "START WITH #{options[:start]}" if options[:start]
19
+ sql << if options[:cache]
20
+ "CACHE #{options[:cache]}"
21
+ else
22
+ 'NOCACHE'
23
+ end
24
+
25
+ sql << if options[:cycle]
26
+ 'CYCLE'
27
+ else
28
+ 'NOCYCLE'
29
+ end
30
+
31
+ sql << "MIN_VALUE #{options[:min_value]}" if options[:min_value]
32
+
33
+ sql << "COMMENT #{quote(options[:comment].to_s)}" if options[:comment]
34
+
35
+ execute(sql.join("\n"))
36
+ end
37
+
38
+ def drop_sequence(name)
39
+ name = quote_column_name(name)
40
+ sql = "DROP SEQUENCE IF EXISTS #{name}"
41
+ execute(sql)
42
+ end
43
+
44
+ def recreate_sequence(name, options = {})
45
+ drop_sequence(name)
46
+ create_sequence(name, options)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Sequence
5
+ module CommandRecorder
6
+ def create_sequence(name, options = {})
7
+ record(__method__, [name, options])
8
+ end
9
+
10
+ def drop_sequence(name)
11
+ record(__method__, [name])
12
+ end
13
+
14
+ def invert_create_sequence(args)
15
+ name, = args
16
+ [:drop_sequence, [name]]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Sequence
5
+ module ModelMethods
6
+ def nextval(name)
7
+ name = connection.quote_column_name(name)
8
+ connection.query_value("SELECT nextval(#{name})")
9
+ end
10
+
11
+ def lastval(name)
12
+ name = connection.quote_column_name(name)
13
+ connection.query_value("SELECT lastval(#{name})")
14
+ end
15
+
16
+ def setval(name, value)
17
+ name = connection.quote_column_name(name)
18
+ connection.query_value("SELECT setval(#{name}, #{value})")
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module Sequence
5
+ module SchemaDumper
6
+ def header(stream)
7
+ super
8
+ sequences(stream)
9
+ end
10
+
11
+ def sequences(stream)
12
+ sequences = @connection.check_sequences
13
+ return if sequences.empty?
14
+
15
+ sequences.each do |seq|
16
+ start_value = seq['START']
17
+ increment = seq['INCREMENT']
18
+ cache = seq['CACHE']
19
+ cache_value = seq['CACHE_VALUE']
20
+ min_value = seq['MIN_VALUE']
21
+ cycle = seq['CYCLE']
22
+ comment = seq['COMMENT']
23
+
24
+ options = []
25
+
26
+ options << "start: #{start_value}" if start_value && Integer(start_value) != 1
27
+
28
+ options << "increment: #{increment}" if increment && Integer(increment) != 1
29
+
30
+ options << "cache: #{cache_value}" if cache_value && Integer(cache_value) != 0
31
+
32
+ options << "min_value: #{min_value}" if min_value
33
+
34
+ options << 'cycle: true' if cycle.to_i != 0
35
+
36
+ options << "comment: #{comment.inspect}" if comment.present?
37
+
38
+ statement = [
39
+ 'create_sequence',
40
+ seq['SEQUENCE_NAME'].inspect
41
+ ].join(' ')
42
+
43
+ if options.any?
44
+ statement << (options.any? ? ", #{options.join(', ')}" : '')
45
+ end
46
+
47
+ stream.puts " #{statement}"
48
+ end
49
+
50
+ stream.puts
51
+ end
52
+ end
53
+ end
54
+ end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
- TIDB_ADAPTER_VERSION = '0.1.1'
4
+ TIDB_ADAPTER_VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-tidb-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hooopo Wang
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-06 00:00:00.000000000 Z
11
+ date: 2021-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -45,7 +45,6 @@ executables: []
45
45
  extensions: []
46
46
  extra_rdoc_files: []
47
47
  files:
48
- - ".github/workflows/main.yml"
49
48
  - ".gitignore"
50
49
  - Gemfile
51
50
  - Gemfile.lock
@@ -55,9 +54,16 @@ files:
55
54
  - activerecord-tidb-adapter.gemspec
56
55
  - bin/console
57
56
  - bin/setup
57
+ - config.toml
58
58
  - lib/active_record/connection_adapters/tidb/database_tasks.rb
59
59
  - lib/active_record/connection_adapters/tidb/setup.rb
60
+ - lib/active_record/connection_adapters/tidb/type.rb
60
61
  - lib/active_record/connection_adapters/tidb_adapter.rb
62
+ - lib/active_record/sequence.rb
63
+ - lib/active_record/sequence/adapter.rb
64
+ - lib/active_record/sequence/command_recorder.rb
65
+ - lib/active_record/sequence/model_methods.rb
66
+ - lib/active_record/sequence/schema_dumper.rb
61
67
  - lib/activerecord-tidb-adapter.rb
62
68
  - lib/version.rb
63
69
  homepage: https://github.com/pingcap/activerecord-tidb-adapter
@@ -1,16 +0,0 @@
1
- name: Ruby
2
-
3
- on: [push,pull_request]
4
-
5
- jobs:
6
- build:
7
- runs-on: ubuntu-latest
8
- steps:
9
- - uses: actions/checkout@v2
10
- - name: Set up Ruby
11
- uses: ruby/setup-ruby@v1
12
- with:
13
- ruby-version: 2.7.0
14
- bundler-cache: true
15
- - name: Run the default task
16
- run: bundle exec rake