sequel-sequence 0.3.0 → 0.4.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: c7012e03aa326861bef294edcf042850f662f9122664da409aa0dd764d9375d9
4
- data.tar.gz: b309b36a931be17ee7cb628c21083ef8543bb60139a249aa9fd1d42378321297
3
+ metadata.gz: ba4270613514ab8a88b24fa9034314333feba102dff7719a8e238ca1c84e67d1
4
+ data.tar.gz: 950c6494e0c9f55f6465beee78209649c784508c9c9c0dab1b9817576cbf3a7c
5
5
  SHA512:
6
- metadata.gz: 7c13a9a6e000990aaee2bdcca4d3d7519a7164c3217d4164f35e40e43deb76f652215169396647568cb03cf111d2a103f2f0f5d5f45e174b8107588e4be1808f
7
- data.tar.gz: e9483ab69ea26d780164a4e4683d75c2bd86d8c5d92983b506645c5544a3ebc277f25f12a463b1f959f675f0e780eaf3b22a82b9560e247f16f2a895518d0e53
6
+ metadata.gz: ed1505fd942d5247ea8ad64b47e1caa9cc420826ace83e4f8c075db257090576772219aa9f48007a0c9d84793f127ec82604a310688717b18ece0ceec8824d66
7
+ data.tar.gz: c781c917e87029eec161ae656bb331552e6e0f531ceb8c6f3df8b836686f9f1bc7a4f277f81c15a9c515f2861624ed73f978a7be7b7db35d29a32c09aef277f5
@@ -34,16 +34,27 @@ jobs:
34
34
  --health-timeout 5s
35
35
  --health-retries 5
36
36
  mysql:
37
- image: mariadb:11.1
37
+ image: mysql:latest
38
38
  env:
39
- MARIADB_ROOT_PASSWORD: root
39
+ MYSQL_ROOT_PASSWORD: rootroot
40
40
  ports:
41
41
  - 3306:3306
42
42
  options: >-
43
- --health-cmd="healthcheck.sh --connect --innodb_initialized"
43
+ --health-cmd="mysqladmin ping"
44
44
  --health-interval 10s
45
45
  --health-timeout 5s
46
46
  --health-retries 3
47
+ # mariadb:
48
+ # image: mariadb:11.1
49
+ # env:
50
+ # MARIADB_ROOT_PASSWORD: root
51
+ # ports:
52
+ # - 3306:3306
53
+ # options: >-
54
+ # --health-cmd="healthcheck.sh --connect --innodb_initialized"
55
+ # --health-interval 10s
56
+ # --health-timeout 5s
57
+ # --health-retries 3
47
58
 
48
59
  env:
49
60
  SEQUEL: ${{ matrix.sequel }}
@@ -54,9 +65,10 @@ jobs:
54
65
  - name: Install db dependencies and check connections
55
66
  run: |
56
67
  DEBIAN_FRONTEND="noninteractive" sudo apt-get install -yqq mysql-client libmysqlclient-dev postgresql-client libpq-dev
57
- mysql --host 127.0.0.1 --port ${{ job.services.mysql.ports[3306] }} -uroot -proot -e "SHOW GRANTS FOR 'root'@'localhost'"
68
+ mysql --host 127.0.0.1 --port ${{ job.services.mysql.ports[3306] }} -uroot -prootroot -e "SHOW GRANTS FOR 'root'@'localhost'"
58
69
  env PGPASSWORD=postgres psql -h localhost -p ${{ job.services.postgres.ports[5432] }} -U postgres -l
59
70
  sqlite3 --version
71
+ # mysql --host 127.0.0.1 --port ${{ job.services.mariadb.ports[3306] }} -uroot -proot -e "SHOW GRANTS FOR 'root'@'localhost'"
60
72
 
61
73
  - name: Set up Ruby
62
74
  uses: ruby/setup-ruby@v1
@@ -66,7 +78,11 @@ jobs:
66
78
 
67
79
  - name: Create MySQL database
68
80
  run: |
69
- mysql -e 'create database test; use test; create table if not exists wares(id int auto_increment, primary key(id)); create table if not exists builders(id int auto_increment, primary key(id));' --host 127.0.0.1 --port ${{ job.services.mysql.ports[3306] }} -uroot -proot
81
+ mysql -e 'create database test;' --host 127.0.0.1 --port ${{ job.services.mysql.ports[3306] }} -uroot -prootroot
82
+
83
+ # - name: Create MariaDB database
84
+ # run: |
85
+ # mysql -e 'create database test; use test; create table if not exists wares(id int auto_increment, primary key(id)); create table if not exists builders(id int auto_increment, primary key(id));' --host 127.0.0.1 --port ${{ job.services.mariadb.ports[3306] }} -uroot -proot
70
86
 
71
87
  - name: Create PostgreSQL database
72
88
  env:
@@ -78,19 +94,36 @@ jobs:
78
94
  run: |
79
95
  mkdir ./db && touch ./db/test.sqlite3 && sqlite3 ./db/test.sqlite3
80
96
 
81
- - name: Run Tests
82
- run: bundle exec rake test
97
+ # - name: Run MariaDB Tests
98
+ # run: bundle exec rake TEST=test/sequel/mariadb_sequence_test.rb
99
+ # env:
100
+ # TEST_MARIA_DATABASE: test
101
+ # TEST_MARIA_HOST: 127.0.0.1
102
+ # TEST_MARIA_PORT: ${{ job.services.mariadb.ports[3306] }}
103
+ # TEST_MARIA_USERNAME: root
104
+ # TEST_MARIA_PASSWORD: root
105
+
106
+ - name: Run PostgreSQL Tests
107
+ run: bundle exec rake TEST=test/sequel/postgresql_sequence_test.rb
83
108
  env:
84
109
  TEST_POSTGRES_DATABASE: test
85
110
  TEST_POSTGRES_HOST: localhost
86
111
  TEST_POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }}
87
112
  TEST_POSTGRES_USERNAME: postgres
88
113
  TEST_POSTGRES_PASSWORD: postgres
114
+
115
+ - name: Run MySQL Tests
116
+ run: bundle exec rake TEST=test/sequel/mysql_sequence_test.rb
117
+ env:
89
118
  TEST_MYSQL_DATABASE: test
90
119
  TEST_MYSQL_HOST: 127.0.0.1
91
120
  TEST_MYSQL_PORT: ${{ job.services.mysql.ports[3306] }}
92
121
  TEST_MYSQL_USERNAME: root
93
- TEST_MYSQL_PASSWORD: root
122
+ TEST_MYSQL_PASSWORD: rootroot
123
+
124
+ - name: Run SQLite Tests
125
+ run: bundle exec rake TEST=test/sequel/sqlite_sequence_test.rb
126
+ env:
94
127
  TEST_SQLITE_DATABASE: "db/test.sqlite3"
95
128
 
96
129
  - name: Run Rubocop
data/.rubocop.yml CHANGED
@@ -35,3 +35,6 @@ Style/Documentation:
35
35
  Lint/ConstantDefinitionInBlock:
36
36
  Exclude:
37
37
  - test/**/*
38
+
39
+ Metrics/ModuleLength:
40
+ Max: 150
data/CHANGELOG.md CHANGED
@@ -11,6 +11,13 @@ Prefix your message with one of the following:
11
11
  - [Security] in case of vulnerabilities.
12
12
  -->
13
13
 
14
+ ## v0.4.0 - 2023-09-26
15
+
16
+ - [Fixed] Differences between MySQL and MariaDB.
17
+ - [Added] Gem API support for MySQL databases.
18
+ - [Changed] README.md, CONTRIBUTING.md and .gemspec description.
19
+ - [Fixed] Some API support for SQLite databases.
20
+
14
21
  ## v0.3.0 - 2023-09-21
15
22
 
16
23
  - [Added] A parametrized 'IF EXISTS' condition into the drop_sequence.
data/CONTRIBUTING.md CHANGED
@@ -67,14 +67,21 @@ your contribution is according to how this project works.
67
67
  - This project uses [Rubocop](https://rubocop.org) to enforce code style. Before
68
68
  submitting your changes, make sure your tests are passing and code conforms to
69
69
  the expected style by running `rake`.
70
+ ```bash
71
+ $ bundle exec rake rubocop
72
+ ```
70
73
  - Do not change the library version. This will be done by the maintainer
71
74
  whenever a new version is about to be released.
72
75
 
73
- ### Ruby tests
76
+ ## Ruby tests
77
+
78
+ Key points in preparing RDBMS and using tests.
79
+
80
+ ### Preparing a PostgreSQL database
74
81
 
75
82
  - Make sure you have a test PostgreSQL database:
76
83
  ```bash
77
- sudo psql -U USER_NAME -d test
84
+ $ sudo psql -U USER_NAME -d test
78
85
  test=# \dt
79
86
  List of relations
80
87
  Schema | Name | Type | Owner
@@ -84,28 +91,31 @@ test=# \dt
84
91
  ```
85
92
  and role `postgres`
86
93
  ```bash
87
- psql -d test -c 'SELECT rolname FROM pg_roles;'
94
+ $ psql -d test -c 'SELECT rolname FROM pg_roles;'
88
95
  rolname
89
96
  ---------------------------
90
97
  postgres
91
98
  ```
92
99
  - If none of them exist, create role
93
100
  ```bash
94
- psql -d postgres -c "create role postgres superuser createdb login password 'postgres';"
101
+ $ psql -d postgres -c "create role postgres superuser createdb login password 'postgres';"
95
102
  ```
96
103
  and database with a couple of tables:
97
104
 
98
105
  ```bash
99
- sudo psql -U postgres -d postgres
106
+ $ sudo psql -U postgres -d postgres
100
107
  postgres=# CREATE DATABASE test;
101
108
  postgres=# \c test
102
109
  test=# CREATE TABLE IF NOT EXISTS things ();
103
110
  test=# CREATE TABLE IF NOT EXISTS masters ();
104
111
  test=# \q
105
112
  ```
106
- - Make sure you have a test MySQL database:
113
+
114
+ ### Preparing a MariaDB database
115
+
116
+ - Make sure you have a test MariaDB database:
107
117
  ```bash
108
- mysql
118
+ $ mysql
109
119
  MariaDB [(none)]> show databases;
110
120
  MariaDB [(none)]> USE test;
111
121
  MariaDB [test]> SHOW TABLES;
@@ -123,19 +133,106 @@ MariaDB [(none)]> USE test;
123
133
  MariaDB [test]> CREATE TABLE IF NOT EXISTS wares(id int auto_increment, primary key(id));
124
134
  MariaDB [test]> CREATE TABLE IF NOT EXISTS builders(id int auto_increment, primary key(id));
125
135
  ```
136
+
137
+ ### Preparing a MySQL database
138
+
139
+ The optimal way to share Mysql and MariaDB on the same computer is to utilize docker containers.
140
+
141
+ - Check the local availability of a Mysql container:
142
+ ```bash
143
+ $ docker image ls
144
+ REPOSITORY TAG IMAGE ID CREATED SIZE
145
+ mysql latest 3503aa5f0751 2 days ago 599MB
146
+ ```
147
+ - If there is no distribution package download the docker container with Mysql to the local computer:
148
+ ```bash
149
+ $ docker run -p 3360:3306 --name test_mysql -e MYSQL_ROOT_PASSWORD=rootroot -d mysql:latest
150
+ ```
151
+ - Show running containers:
152
+ ```bash
153
+ $ docker container ls
154
+ CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
155
+ a0d3476699f4 mysql:latest "docker-entrypoint.s…" 17 minutes ago Up 11 seconds 33060/tcp, 0.0.0.0:3360->3306/tcp test_mysql
156
+ ```
157
+ - Launch the MySQL container if it is still not running:
158
+ ```bash
159
+ $ docker start test_mysql
160
+ ```
161
+
162
+ To create a new database for tests, you could run the MySQL client in the terminal as follows:
163
+
164
+ - Check the IP address of the running MySQL server:
165
+ ```bash
166
+ $ docker inspect test_mysql
167
+ ...
168
+ "IPAddress": "172.17.0.2",
169
+ ...
170
+ ```
171
+ - Run the MySQL client:
172
+ ```bash
173
+ $ docker run -e MYSQL_ROOT_PASSWORD=rootroot -it mysql /bin/bash
174
+ bash-4.4#
175
+ ```
176
+ - Launch the MySQL shell:
177
+ ```bash
178
+ bash-4.4# mysql -h 172.17.0.2 -u root -p
179
+ Enter password:
180
+ Welcome to the MySQL monitor. Commands end with ; or \g.
181
+ Your MySQL connection id is 8
182
+ Server version: 8.1.0 MySQL Community Server - GPL
183
+
184
+ Copyright (c) 2000, 2023, Oracle and/or its affiliates.
185
+
186
+ Oracle is a registered trademark of Oracle Corporation and/or its
187
+ affiliates. Other names may be trademarks of their respective
188
+ owners.
189
+
190
+ Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
191
+
192
+ mysql>
193
+ ```
194
+ - Make sure that the test database is available:
195
+ ```bash
196
+ mysql> show schemas;
197
+ +--------------------+
198
+ | Database |
199
+ +--------------------+
200
+ | information_schema |
201
+ | mysql |
202
+ | performance_schema |
203
+ | sys |
204
+ | test |
205
+ +--------------------+
206
+ ```
207
+ - If the test danabase doesn't exists, create it:
208
+ ```bash
209
+ mysql> CREATE DATABASE test;
210
+ ```
211
+ - Create a couple of tables:
212
+ ```bash
213
+ mysql> USE test;
214
+ mysql> CREATE TABLE IF NOT EXISTS stuffs(id int auto_increment, primary key(id));
215
+ mysql> CREATE TABLE IF NOT EXISTS creators(id int auto_increment, primary key(id));
216
+ ```
217
+
218
+ ### Preparing a SQLite database
219
+
126
220
  - Add a test SQLite database:
127
221
  ```bash
128
- mkdir db && touch db/test.sqlite3
222
+ $ mkdir db && touch db/test.sqlite3
129
223
  ```
130
224
  - Add a couple of tables in the SQLite database:
131
225
  ```bash
132
- sqlite3 db/test.sqlite3
226
+ $ sqlite3 db/test.sqlite3
133
227
  sqlite> create table objects(id integer primary key autoincrement);
134
228
  sqlite> create table apprentices(id integer primary key autoincrement);
135
229
  ```
136
- - Run the tests separately:
230
+
231
+ ### Running tests:
232
+
137
233
  ```bash
138
- bundle exec rake TEST=test/sequel/postgresql_sequence_test.rb
139
- bundle exec rake TEST=test/sequel/mysql_sequence_test.rb
140
- bundle exec rake TEST=test/sequel/sqlite_sequence_test.rb
234
+ $ bundle exec rake TEST=test/sequel/postgresql_sequence_test.rb
235
+ $ bundle exec rake TEST=test/sequel/mariadb_sequence_test.rb
236
+ $ bundle exec rake TEST=test/sequel/mysql_sequence_test.rb
237
+ $ bundle exec rake TEST=test/sequel/sqlite_sequence_test.rb
141
238
  ```
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![Downloads total](https://img.shields.io/gem/dt/sequel-sequence.svg)](https://rubygems.org/profiles/it_architect)
6
6
  [![Code Climate](https://codeclimate.com/github/Oreol-Group/sequel-sequence.svg)](https://codeclimate.com/github/Oreol-Group/sequel-sequence)
7
7
 
8
- Adds a useful interface and support for PostgreSQL and MySQL `SEQUENCE` on Sequel migrations. Gem includes functionality to cover the needs of SQLite users as well.
8
+ Adds a useful interface for PostgreSQL and MariaDB `SEQUENCE` on Sequel migrations. This Gem includes functionality to meet the needs of MySQL and SQLite users as well.
9
9
 
10
10
  ## Installation
11
11
 
@@ -21,7 +21,7 @@ gem "sequel-sequence"
21
21
 
22
22
  ## Usage with PostgreSQL and MariaDB
23
23
 
24
- To create a `SEQUENCE`, just use the method `create_sequence`.
24
+ To create and delete a `SEQUENCE`, simply use the `create_sequence` and `drop_sequence` methods.
25
25
 
26
26
  ```ruby
27
27
  Sequel.migration do
@@ -29,6 +29,19 @@ Sequel.migration do
29
29
  create_sequence :position, if_exists: false
30
30
  end
31
31
 
32
+ down do
33
+ drop_sequence :position, if_exists: true
34
+ end
35
+ end
36
+ ```
37
+
38
+ It would also be correct to write:
39
+ ```ruby
40
+ Sequel.migration do
41
+ up do
42
+ create_sequence :position
43
+ end
44
+
32
45
  down do
33
46
  drop_sequence :position
34
47
  end
@@ -39,16 +52,19 @@ You can also specify the initial value and increment:
39
52
 
40
53
  ```ruby
41
54
  create_sequence :position, increment: 2
55
+ create_sequence :position, step: 2
42
56
  create_sequence :position, start: 100
43
57
  create_sequence :position, if_exists: false
44
58
  ```
45
59
 
60
+ The `increment` and `step` parameters have the same meaning. By default their values are 1. The default value of `start` is 1 as well.
61
+
46
62
  To define a column that has a sequence as its default value, use something like the following:
47
63
 
48
64
  ```ruby
49
65
  Sequel.migration do
50
66
  change do
51
- create_sequence :position_id, if_exists: false, start: 1
67
+ create_sequence :position_id, if_exists: false, start: 1000
52
68
 
53
69
  create_table(:things) do
54
70
  primary_key :id
@@ -83,23 +99,26 @@ DB.lastval("position")
83
99
  DB.setval("position", 1234)
84
100
  ```
85
101
 
86
- ## Usage with SQLite
102
+ ## Usage with SQLite and MySQL
87
103
 
88
- In SQLite, the sequence functionality is implemented by registering tables in the database with a primary key of `id` and an additional integer field `fiction`.
104
+ The sequence functionality for SQLite or MySQL databases is implemented by registering tables in the database with a primary key of `id` and an additional integer field `fiction`.
89
105
  ```sql
90
106
  CREATE TABLE `name_of_your_sequence_table`
91
107
  (id integer primary key autoincrement, fiction integer);
92
108
  ```
93
109
 
94
- You might utilize the last one as a numeric label to collect statistics on the operation of the end-to-end counter `"name_of_your_sequence_table".id` within the application.
95
-
110
+ You might utilize the last field as a numeric label to collect statistics on the operation of the end-to-end counter `"name_of_your_sequence_table".id` within the application.
111
+ ```ruby
112
+ create_sequence :position, if_exists: false, start: 1000, numeric_label: 1
113
+ ```
114
+ and
96
115
  ```ruby
97
- DB.nextval_with_label("position", 1)
116
+ DB.nextval_with_label(:position, 1)
98
117
  ```
99
118
 
100
119
  By default, `fiction` has a zero value.
101
120
 
102
- Otherwise, the operation of this gem for SQLite is identical to the ways of using Sequence in more advanced databases.
121
+ Otherwise, the operation of this gem for SQLite and MySQL is similar to the ways of using Sequence in more advanced RDBMS. There is only one difference here, you won't be able to change the increment value from 1 to another using the `increment` or `step` parameter.
103
122
 
104
123
  ## Maintainer
105
124
 
data/Rakefile CHANGED
@@ -7,7 +7,15 @@ require 'rubocop/rake_task'
7
7
  Rake::TestTask.new(:test) do |t|
8
8
  t.libs << 'test'
9
9
  t.libs << 'lib'
10
- t.test_files = FileList['test/**/*_test.rb']
10
+ t.test_files = FileList['test/sequel/postgresql_sequence_test.rb',
11
+ 'test/sequel/mariadb_sequence_test.rb',
12
+ 'test/sequel/sqlite_sequence_test.rb']
13
+ end
14
+
15
+ Rake::TestTask.new(:mysql) do |t|
16
+ t.libs << 'test'
17
+ t.libs << 'lib'
18
+ t.test_files = FileList['test/sequel/mysql_sequence_test.rb']
11
19
  end
12
20
 
13
21
  RuboCop::RakeTask.new
@@ -2,6 +2,7 @@
2
2
 
3
3
  # https://sequel.jeremyevans.net/rdoc/files/doc/sql_rdoc.html
4
4
  # https://github.com/jeremyevans/sequel/blob/master/lib/sequel/database/connecting.rb
5
+ # See information about disable_insert_returning in https://github.com/jeremyevans/sequel/blob/master/doc/release_notes/4.9.0.txt
5
6
  module Sequel
6
7
  module Sequence
7
8
  module Database
@@ -62,7 +63,7 @@ module Sequel
62
63
  out
63
64
  end
64
65
 
65
- # for db.database_type = :postgres
66
+ # for Postgres
66
67
  def currval(name)
67
68
  name = quote(name.to_s)
68
69
  out = nil
@@ -72,7 +73,7 @@ module Sequel
72
73
  out
73
74
  end
74
75
 
75
- # for db.database_type = :mysql2
76
+ # for MariaDB
76
77
  alias lastval currval
77
78
 
78
79
  def setval(name, value)
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mariadb
4
+ def quote_column_name(name)
5
+ "`#{name.gsub('`', '``')}`"
6
+ end
7
+
8
+ def quote_sequence_name(name)
9
+ "`#{name.gsub(/[`"']/, '')}`"
10
+ end
11
+
12
+ def check_sequences
13
+ fetch("SHOW FULL TABLES WHERE Table_type = 'SEQUENCE';").all.to_a
14
+ end
15
+
16
+ def create_sequence(name, options = {})
17
+ increment = options[:increment] || options[:step]
18
+ if_exists = build_exists_condition(options[:if_exists])
19
+ name = quote_name(name.to_s)
20
+
21
+ sql = ["CREATE SEQUENCE #{if_exists || Sequel::Database::IF_NOT_EXISTS} #{name}"]
22
+ sql << "INCREMENT BY #{increment}" if increment
23
+ sql << "START WITH #{options[:start]}" if options[:start]
24
+ sql << ';'
25
+
26
+ run(sql.join("\n"))
27
+ end
28
+
29
+ def drop_sequence(name)
30
+ name = quote_name(name.to_s)
31
+ sql = "DROP SEQUENCE IF EXISTS #{name}"
32
+ run(sql)
33
+ end
34
+
35
+ def nextval(name)
36
+ name = quote(name.to_s)
37
+ out = nil
38
+ fetch("SELECT nextval(#{name});") do |row|
39
+ out = row["nextval(#{name})".to_sym]
40
+ end
41
+ out
42
+ end
43
+
44
+ def lastval(name)
45
+ name = quote(name.to_s)
46
+ out = nil
47
+ fetch("SELECT lastval(#{name});") do |row|
48
+ out = row["lastval(#{name})".to_sym]
49
+ end
50
+ out
51
+ end
52
+
53
+ alias currval lastval
54
+
55
+ def setval(name, value)
56
+ name = quote(name.to_s)
57
+ out = nil
58
+ fetch("SELECT setval(#{name}, #{value});") do |row|
59
+ out = row["setval(#{name}, #{value})".to_sym]
60
+ end
61
+ out
62
+ end
63
+
64
+ def set_column_default_nextval(table, column, sequence)
65
+ table = table.to_s
66
+ column = column.to_s
67
+ sequence = quote(sequence.to_s)
68
+ run "ALTER TABLE IF EXISTS #{table} " \
69
+ "ALTER COLUMN #{column} SET DEFAULT nextval(#{sequence});"
70
+ end
71
+ end
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mysql
4
+ def check_sequences
5
+ fetch('SELECT * FROM mysql_sequence;').all.to_a
6
+ end
7
+
8
+ def custom_sequence?(sequence_name)
9
+ out = nil
10
+ begin
11
+ fetch(select_from_mysql_sequence_where(stringify(sequence_name))) do |row|
12
+ out = row[:name]
13
+ end
14
+ rescue Sequel::DatabaseError
15
+ return false
16
+ end
17
+
18
+ !out.nil?
19
+ end
20
+
21
+ def create_sequence(name, options = {})
22
+ check_options(options)
23
+ if_exists = build_exists_condition(options[:if_exists])
24
+ start_option = options[:start] || 1
25
+ num_label = options[:numeric_label] || 0
26
+ return if (current = lastval(name)) && (current >= start_option)
27
+
28
+ run create_sequence_table(stringify(name), if_exists)
29
+ run insert_into_sequence_table_init_values(stringify(name), start_option, num_label)
30
+ run create_mysql_sequence
31
+ table_matcher { run delete_from_mysql_sequence(stringify(name)) }
32
+ run insert_into_mysql_sequence(stringify(name), start_option)
33
+ end
34
+
35
+ def drop_sequence(name, options = {})
36
+ if_exists = build_exists_condition(options[:if_exists])
37
+ run drop_sequence_table(stringify(name), if_exists)
38
+ table_matcher { run delete_from_mysql_sequence(stringify(name)) }
39
+ end
40
+
41
+ def nextval(name)
42
+ run insert_into_sequence_table(stringify(name), 0)
43
+ table_matcher { run delete_from_mysql_sequence(stringify(name)) }
44
+ run insert_last_insert_id_into_mysql_sequence(stringify(name))
45
+ take_seq(stringify(name))
46
+ end
47
+
48
+ def nextval_with_label(name, num_label = 0)
49
+ run insert_into_sequence_table(stringify(name), num_label)
50
+ table_matcher { run delete_from_mysql_sequence(stringify(name)) }
51
+ run insert_last_insert_id_into_mysql_sequence(stringify(name))
52
+ take_seq(stringify(name))
53
+ end
54
+
55
+ def lastval(name)
56
+ take_seq(stringify(name))
57
+ end
58
+
59
+ alias currval lastval
60
+
61
+ def setval(name, value)
62
+ current = lastval(stringify(name))
63
+ if current.nil?
64
+ create_sequence(stringify(name), { start: value })
65
+ elsif value < current
66
+ log_info Sequel::Database::DANGER_OPT_ID
67
+ value = current
68
+ elsif value > current
69
+ run insert_into_sequence_table_init_values(stringify(name), value, 0)
70
+ table_matcher { run delete_from_mysql_sequence(stringify(name)) }
71
+ run insert_into_mysql_sequence(stringify(name), value)
72
+ end
73
+ value
74
+ end
75
+
76
+ def set_column_default_nextval(table, column, sequence)
77
+ run create_sequenced_column(stringify(table),
78
+ stringify(column),
79
+ stringify(sequence))
80
+ run update_sequenced_column(stringify(table),
81
+ stringify(column),
82
+ stringify(sequence))
83
+ end
84
+
85
+ private
86
+
87
+ def stringify(name)
88
+ @name ||= {}
89
+ @name.fetch(name, nil) || (@name[name] = name.to_s)
90
+ end
91
+
92
+ def select_from_mysql_sequence_where(name)
93
+ "SELECT * FROM mysql_sequence where name = '#{name}';"
94
+ end
95
+
96
+ def create_sequence_table(name, if_exists = nil)
97
+ %(
98
+ CREATE TABLE #{if_exists || Sequel::Database::IF_NOT_EXISTS} #{name}
99
+ (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
100
+ fiction INT);
101
+ ).strip
102
+ end
103
+
104
+ def insert_into_sequence_table_init_values(name, start_id, num_label)
105
+ "INSERT INTO #{name} (id, fiction) VALUES (#{start_id}, #{num_label});"
106
+ end
107
+
108
+ def create_mysql_sequence
109
+ %(
110
+ CREATE TABLE #{Sequel::Database::IF_NOT_EXISTS} mysql_sequence
111
+ (name VARCHAR(40), seq INT);
112
+ ).strip
113
+ end
114
+
115
+ def select_max_seq(name)
116
+ "SELECT MAX(seq) AS id FROM mysql_sequence WHERE name = '#{name}';"
117
+ end
118
+
119
+ def take_seq(name)
120
+ table_matcher do
121
+ out = nil
122
+ fetch(select_max_seq(name)) do |row|
123
+ out = row[:id]
124
+ end
125
+ out
126
+ end
127
+ end
128
+
129
+ def delete_from_mysql_sequence(name)
130
+ "DELETE QUICK IGNORE FROM mysql_sequence WHERE name = '#{name}';"
131
+ end
132
+
133
+ def insert_into_mysql_sequence(name, value)
134
+ "INSERT INTO mysql_sequence (name, seq) VALUES ('#{name}', #{value});"
135
+ end
136
+
137
+ def drop_sequence_table(name, if_exists = nil)
138
+ "DROP TABLE #{if_exists || Sequel::Database::IF_EXISTS} #{name};"
139
+ end
140
+
141
+ def insert_into_sequence_table(name, num_label)
142
+ "INSERT INTO #{name} (fiction) VALUES (#{num_label});"
143
+ end
144
+
145
+ def insert_last_insert_id_into_mysql_sequence(name)
146
+ "INSERT INTO mysql_sequence (name, seq) VALUES ('#{name}', LAST_INSERT_ID());"
147
+ end
148
+
149
+ def create_sequenced_column(table, _column, sequence)
150
+ %(
151
+ CREATE TRIGGER IF NOT EXISTS #{table}_#{sequence} BEFORE INSERT
152
+ ON #{table}
153
+ FOR EACH ROW BEGIN
154
+ DELETE QUICK IGNORE FROM mysql_sequence WHERE name = '#{sequence}';
155
+ INSERT INTO #{sequence} SET fiction = 0;
156
+ INSERT INTO mysql_sequence SET name = '#{sequence}', seq = LAST_INSERT_ID();
157
+
158
+ END;
159
+ ).strip
160
+ end
161
+
162
+ def update_sequenced_column(table, column, sequence)
163
+ %(
164
+ CREATE TRIGGER IF NOT EXISTS #{table}_#{column} BEFORE INSERT
165
+ ON #{table}
166
+ FOR EACH ROW FOLLOWS #{table}_#{sequence}
167
+ SET NEW.#{column} = ( SELECT MAX(seq) FROM mysql_sequence WHERE name = '#{sequence}' );
168
+ ).strip
169
+ end
170
+
171
+ def table_matcher(&block)
172
+ block.call
173
+ rescue Sequel::DatabaseError => e
174
+ return if e.message =~ /\AMysql2::Error: Table(.)*doesn't exist\z/
175
+
176
+ raise e
177
+ end
178
+ end