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 +4 -4
- data/.github/workflows/ci.yml +41 -8
- data/.rubocop.yml +3 -0
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTING.md +110 -13
- data/README.md +28 -9
- data/Rakefile +9 -1
- data/lib/sequel/sequence/database/postgresql.rb +3 -2
- data/lib/sequel/sequence/database/server/mariadb.rb +71 -0
- data/lib/sequel/sequence/database/server/mysql.rb +178 -0
- data/lib/sequel/sequence/database/sqlite.rb +11 -4
- data/lib/sequel/sequence/database.rb +2 -2
- data/lib/sequel/sequence/database_ext_connection.rb +25 -0
- data/lib/sequel/sequence/version.rb +1 -1
- data/lib/sequel/sequence.rb +1 -9
- data/sequel-sequence.gemspec +3 -2
- data/test/mariadb_test_helper.rb +32 -0
- data/test/mysql_test_helper.rb +11 -9
- data/test/sequel/mariadb_sequence_test.rb +182 -0
- data/test/sequel/mysql_sequence_test.rb +214 -41
- data/test/sequel/sqlite_sequence_test.rb +77 -5
- data/test/sqlite_test_helper.rb +2 -1
- metadata +11 -6
- data/lib/sequel/sequence/database/mysql.rb +0 -10
- data/lib/sequel/sequence/database/mysql2.rb +0 -81
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba4270613514ab8a88b24fa9034314333feba102dff7719a8e238ca1c84e67d1
|
4
|
+
data.tar.gz: 950c6494e0c9f55f6465beee78209649c784508c9c9c0dab1b9817576cbf3a7c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed1505fd942d5247ea8ad64b47e1caa9cc420826ace83e4f8c075db257090576772219aa9f48007a0c9d84793f127ec82604a310688717b18ece0ceec8824d66
|
7
|
+
data.tar.gz: c781c917e87029eec161ae656bb331552e6e0f531ceb8c6f3df8b836686f9f1bc7a4f277f81c15a9c515f2861624ed73f978a7be7b7db35d29a32c09aef277f5
|
data/.github/workflows/ci.yml
CHANGED
@@ -34,16 +34,27 @@ jobs:
|
|
34
34
|
--health-timeout 5s
|
35
35
|
--health-retries 5
|
36
36
|
mysql:
|
37
|
-
image:
|
37
|
+
image: mysql:latest
|
38
38
|
env:
|
39
|
-
|
39
|
+
MYSQL_ROOT_PASSWORD: rootroot
|
40
40
|
ports:
|
41
41
|
- 3306:3306
|
42
42
|
options: >-
|
43
|
-
--health-cmd="
|
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 -
|
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;
|
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
|
-
|
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:
|
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
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
|
-
|
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
|
-
|
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
|
-
|
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/
|
140
|
-
bundle exec rake TEST=test/sequel/
|
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
|
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`,
|
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:
|
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
|
-
|
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
|
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(
|
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
|
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
|
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
|
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
|
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
|