sequel-sequence 0.4.0 → 0.4.1
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 +3 -0
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTING.md +1 -0
- data/README.md +6 -2
- data/Rakefile +26 -1
- data/lib/sequel/error.rb +6 -6
- data/lib/sequel/sequence/database/server/mariadb.rb +64 -56
- data/lib/sequel/sequence/database/server/mysql.rb +179 -172
- data/lib/sequel/sequence/database/sqlite.rb +7 -20
- data/lib/sequel/sequence/database.rb +10 -10
- data/lib/sequel/sequence/database_ext_connection.rb +7 -3
- data/lib/sequel/sequence/version.rb +1 -1
- data/test/mock_test_helper.rb +5 -0
- data/test/mysql_test_helper.rb +5 -0
- data/test/sequel/mock_sequence_test.rb +100 -0
- data/test/sequel/mysql_sequence_test.rb +16 -0
- data/test/sequel/sqlite_sequence_test.rb +9 -7
- data/test/sqlite_test_helper.rb +8 -1
- data/test/test_helper.rb +1 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 749ea113ce41f1f7f70f620d5aa70deac470a76491cdb36b4bd8e5c739487a07
|
4
|
+
data.tar.gz: 9628bc8f57ad5a1ead78d82eaa8348963876c93a6be40df0d8c9dabfab928f76
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0dbf107132dad0bcd23bcf5da23cb9fb121119dafb109865978a6bf26fe8465985875fd6d33b72422cd57e1d5b74b54fce065ba7a2fdf21b0d76f032b7c149e
|
7
|
+
data.tar.gz: bd1bbeaf56ddcd37b4bc86b6b83ad06e85212c7f09bb32db03004bfc9599dd240b141ea6c6130d64fe8b85471c3e6bb7cd8537714b7b6f34f744142b117ce606
|
data/.github/workflows/ci.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.1 - 2023-09-28
|
15
|
+
|
16
|
+
- [Added] Important notice to README.md.
|
17
|
+
- [Added] MySQL tests cover 100%.
|
18
|
+
- [Added] SQLite tests cover 100%.
|
19
|
+
- [Added] Mock connection to check for additional ORM exceptions.
|
20
|
+
|
14
21
|
## v0.4.0 - 2023-09-26
|
15
22
|
|
16
23
|
- [Fixed] Differences between MySQL and MariaDB.
|
data/CONTRIBUTING.md
CHANGED
@@ -235,4 +235,5 @@ $ bundle exec rake TEST=test/sequel/postgresql_sequence_test.rb
|
|
235
235
|
$ bundle exec rake TEST=test/sequel/mariadb_sequence_test.rb
|
236
236
|
$ bundle exec rake TEST=test/sequel/mysql_sequence_test.rb
|
237
237
|
$ bundle exec rake TEST=test/sequel/sqlite_sequence_test.rb
|
238
|
+
$ bundle exec rake TEST=test/sequel/mock_sequence_test.rb
|
238
239
|
```
|
data/README.md
CHANGED
@@ -48,7 +48,7 @@ Sequel.migration do
|
|
48
48
|
end
|
49
49
|
```
|
50
50
|
|
51
|
-
You can also specify the initial value
|
51
|
+
You can also specify the following optional parameters: `if_exists` – a condition of acceptability; `start` – an initial value; `increment` or `step` – step size to the next auto incrementing value:
|
52
52
|
|
53
53
|
```ruby
|
54
54
|
create_sequence :position, increment: 2
|
@@ -107,7 +107,7 @@ CREATE TABLE `name_of_your_sequence_table`
|
|
107
107
|
(id integer primary key autoincrement, fiction integer);
|
108
108
|
```
|
109
109
|
|
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.
|
110
|
+
You might utilize the last field `fiction` 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
111
|
```ruby
|
112
112
|
create_sequence :position, if_exists: false, start: 1000, numeric_label: 1
|
113
113
|
```
|
@@ -120,6 +120,10 @@ By default, `fiction` has a zero value.
|
|
120
120
|
|
121
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.
|
122
122
|
|
123
|
+
## Known issues you may encounter
|
124
|
+
|
125
|
+
This solution does not allow you to simultaneously work with MySQL and MariaDB databases from one application. If such a need arises, move the data processing functionality to different microservices.
|
126
|
+
|
123
127
|
## Maintainer
|
124
128
|
|
125
129
|
- [Nikolai Bocharov](https://github.com/oreol-group)
|
data/Rakefile
CHANGED
@@ -9,7 +9,8 @@ Rake::TestTask.new(:test) do |t|
|
|
9
9
|
t.libs << 'lib'
|
10
10
|
t.test_files = FileList['test/sequel/postgresql_sequence_test.rb',
|
11
11
|
'test/sequel/mariadb_sequence_test.rb',
|
12
|
-
'test/sequel/sqlite_sequence_test.rb'
|
12
|
+
'test/sequel/sqlite_sequence_test.rb',
|
13
|
+
'test/sequel/mock_sequence_test.rb']
|
13
14
|
end
|
14
15
|
|
15
16
|
Rake::TestTask.new(:mysql) do |t|
|
@@ -18,6 +19,30 @@ Rake::TestTask.new(:mysql) do |t|
|
|
18
19
|
t.test_files = FileList['test/sequel/mysql_sequence_test.rb']
|
19
20
|
end
|
20
21
|
|
22
|
+
Rake::TestTask.new(:postgresql) do |t|
|
23
|
+
t.libs << 'test'
|
24
|
+
t.libs << 'lib'
|
25
|
+
t.test_files = FileList['test/sequel/postgresql_sequence_test.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Rake::TestTask.new(:mariadb) do |t|
|
29
|
+
t.libs << 'test'
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.test_files = FileList['test/sequel/mariadb_sequence_test.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
Rake::TestTask.new(:sqlite) do |t|
|
35
|
+
t.libs << 'test'
|
36
|
+
t.libs << 'lib'
|
37
|
+
t.test_files = FileList['test/sequel/sqlite_sequence_test.rb']
|
38
|
+
end
|
39
|
+
|
40
|
+
Rake::TestTask.new(:mock) do |t|
|
41
|
+
t.libs << 'test'
|
42
|
+
t.libs << 'lib'
|
43
|
+
t.test_files = FileList['test/sequel/mock_sequence_test.rb']
|
44
|
+
end
|
45
|
+
|
21
46
|
RuboCop::RakeTask.new
|
22
47
|
|
23
48
|
task default: %i[test rubocop]
|
data/lib/sequel/error.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Sequel
|
4
|
-
|
5
|
-
|
4
|
+
(
|
5
|
+
# Error raised when attempting to utilize an invalid adapter to SEQUENCE interface.
|
6
|
+
MethodNotAllowed = Class.new(Error)
|
7
|
+
).name
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
super(message)
|
10
|
-
end
|
9
|
+
class Database
|
10
|
+
METHOD_NOT_ALLOWED = 'Method not allowed'
|
11
11
|
end
|
12
12
|
end
|
@@ -1,71 +1,79 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module Sequel
|
4
|
+
module Sequence
|
5
|
+
module Database
|
6
|
+
module Server
|
7
|
+
module Mariadb
|
8
|
+
def quote_column_name(name)
|
9
|
+
"`#{name.gsub('`', '``')}`"
|
10
|
+
end
|
7
11
|
|
8
|
-
|
9
|
-
|
10
|
-
|
12
|
+
def quote_sequence_name(name)
|
13
|
+
"`#{name.gsub(/[`"']/, '')}`"
|
14
|
+
end
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
def check_sequences
|
17
|
+
fetch("SHOW FULL TABLES WHERE Table_type = 'SEQUENCE';").all.to_a
|
18
|
+
end
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
def create_sequence(name, options = {})
|
21
|
+
increment = options[:increment] || options[:step]
|
22
|
+
if_exists = build_exists_condition(options[:if_exists])
|
23
|
+
name = quote_name(name.to_s)
|
20
24
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
sql = ["CREATE SEQUENCE #{if_exists || Sequel::Database::IF_NOT_EXISTS} #{name}"]
|
26
|
+
sql << "INCREMENT BY #{increment}" if increment
|
27
|
+
sql << "START WITH #{options[:start]}" if options[:start]
|
28
|
+
sql << ';'
|
25
29
|
|
26
|
-
|
27
|
-
|
30
|
+
run(sql.join("\n"))
|
31
|
+
end
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
33
|
+
def drop_sequence(name)
|
34
|
+
name = quote_name(name.to_s)
|
35
|
+
sql = "DROP SEQUENCE IF EXISTS #{name}"
|
36
|
+
run(sql)
|
37
|
+
end
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
def nextval(name)
|
40
|
+
name = quote(name.to_s)
|
41
|
+
out = nil
|
42
|
+
fetch("SELECT nextval(#{name});") do |row|
|
43
|
+
out = row["nextval(#{name})".to_sym]
|
44
|
+
end
|
45
|
+
out
|
46
|
+
end
|
43
47
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
def lastval(name)
|
49
|
+
name = quote(name.to_s)
|
50
|
+
out = nil
|
51
|
+
fetch("SELECT lastval(#{name});") do |row|
|
52
|
+
out = row["lastval(#{name})".to_sym]
|
53
|
+
end
|
54
|
+
out
|
55
|
+
end
|
52
56
|
|
53
|
-
|
57
|
+
alias currval lastval
|
54
58
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
59
|
+
def setval(name, value)
|
60
|
+
name = quote(name.to_s)
|
61
|
+
out = nil
|
62
|
+
fetch("SELECT setval(#{name}, #{value});") do |row|
|
63
|
+
out = row["setval(#{name}, #{value})".to_sym]
|
64
|
+
end
|
65
|
+
out
|
66
|
+
end
|
63
67
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
def set_column_default_nextval(table, column, sequence)
|
69
|
+
table = table.to_s
|
70
|
+
column = column.to_s
|
71
|
+
sequence = quote(sequence.to_s)
|
72
|
+
run "ALTER TABLE IF EXISTS #{table} " \
|
73
|
+
"ALTER COLUMN #{column} SET DEFAULT nextval(#{sequence});"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
70
78
|
end
|
71
79
|
end
|
@@ -1,178 +1,185 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
3
|
+
module Sequel
|
4
|
+
module Sequence
|
5
|
+
module Database
|
6
|
+
module Server
|
7
|
+
module Mysql
|
8
|
+
def check_sequences
|
9
|
+
fetch('SELECT * FROM mysql_sequence;').all.to_a
|
10
|
+
end
|
11
|
+
|
12
|
+
def custom_sequence?(sequence_name)
|
13
|
+
table_matcher do
|
14
|
+
out = nil
|
15
|
+
fetch(select_from_mysql_sequence_where(stringify(sequence_name))) do |row|
|
16
|
+
out = row[:name]
|
17
|
+
end
|
18
|
+
!out.nil?
|
19
|
+
end || false
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_sequence(name, options = {})
|
23
|
+
check_options(options)
|
24
|
+
if_exists = build_exists_condition(options[:if_exists])
|
25
|
+
start_option = options[:start] || 1
|
26
|
+
num_label = options[:numeric_label] || 0
|
27
|
+
return if (current = lastval(name)) && (current >= start_option)
|
28
|
+
|
29
|
+
run create_sequence_table(stringify(name), if_exists)
|
30
|
+
run insert_into_sequence_table_init_values(stringify(name), start_option, num_label)
|
31
|
+
run create_mysql_sequence
|
32
|
+
table_matcher { run delete_from_mysql_sequence(stringify(name)) }
|
33
|
+
run insert_into_mysql_sequence(stringify(name), start_option)
|
34
|
+
end
|
35
|
+
|
36
|
+
def drop_sequence(name, options = {})
|
37
|
+
if_exists = build_exists_condition(options[:if_exists])
|
38
|
+
run drop_sequence_table(stringify(name), if_exists)
|
39
|
+
table_matcher { run delete_from_mysql_sequence(stringify(name)) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def nextval(name)
|
43
|
+
run insert_into_sequence_table(stringify(name), 0)
|
44
|
+
table_matcher { run delete_from_mysql_sequence(stringify(name)) }
|
45
|
+
run insert_last_insert_id_into_mysql_sequence(stringify(name))
|
46
|
+
take_seq(stringify(name))
|
47
|
+
end
|
48
|
+
|
49
|
+
def nextval_with_label(name, num_label = 0)
|
50
|
+
run insert_into_sequence_table(stringify(name), num_label)
|
51
|
+
table_matcher { run delete_from_mysql_sequence(stringify(name)) }
|
52
|
+
run insert_last_insert_id_into_mysql_sequence(stringify(name))
|
53
|
+
take_seq(stringify(name))
|
54
|
+
end
|
55
|
+
|
56
|
+
def lastval(name)
|
57
|
+
take_seq(stringify(name))
|
58
|
+
end
|
59
|
+
|
60
|
+
alias currval lastval
|
61
|
+
|
62
|
+
def setval(name, value)
|
63
|
+
current = lastval(stringify(name))
|
64
|
+
if current.nil?
|
65
|
+
create_sequence(stringify(name), { start: value })
|
66
|
+
elsif value < current
|
67
|
+
log_info Sequel::Database::DANGER_OPT_ID
|
68
|
+
value = current
|
69
|
+
elsif value > current
|
70
|
+
run insert_into_sequence_table_init_values(stringify(name), value, 0)
|
71
|
+
table_matcher { run delete_from_mysql_sequence(stringify(name)) }
|
72
|
+
run insert_into_mysql_sequence(stringify(name), value)
|
73
|
+
end
|
74
|
+
value
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_column_default_nextval(table, column, sequence)
|
78
|
+
run create_sequenced_column(stringify(table),
|
79
|
+
stringify(column),
|
80
|
+
stringify(sequence))
|
81
|
+
run update_sequenced_column(stringify(table),
|
82
|
+
stringify(column),
|
83
|
+
stringify(sequence))
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def stringify(name)
|
89
|
+
@name ||= {}
|
90
|
+
@name.fetch(name, nil) || (@name[name] = name.to_s)
|
91
|
+
end
|
92
|
+
|
93
|
+
def select_from_mysql_sequence_where(name)
|
94
|
+
"SELECT * FROM mysql_sequence where name = '#{name}';"
|
95
|
+
end
|
96
|
+
|
97
|
+
def create_sequence_table(name, if_exists = nil)
|
98
|
+
%(
|
99
|
+
CREATE TABLE #{if_exists || Sequel::Database::IF_NOT_EXISTS} #{name}
|
100
|
+
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
101
|
+
fiction INT);
|
102
|
+
).strip
|
103
|
+
end
|
104
|
+
|
105
|
+
def insert_into_sequence_table_init_values(name, start_id, num_label)
|
106
|
+
"INSERT INTO #{name} (id, fiction) VALUES (#{start_id}, #{num_label});"
|
107
|
+
end
|
108
|
+
|
109
|
+
def create_mysql_sequence
|
110
|
+
%(
|
111
|
+
CREATE TABLE #{Sequel::Database::IF_NOT_EXISTS} mysql_sequence
|
112
|
+
(name VARCHAR(40), seq INT);
|
113
|
+
).strip
|
114
|
+
end
|
115
|
+
|
116
|
+
def select_max_seq(name)
|
117
|
+
"SELECT MAX(seq) AS id FROM mysql_sequence WHERE name = '#{name}';"
|
118
|
+
end
|
119
|
+
|
120
|
+
def take_seq(name)
|
121
|
+
table_matcher do
|
122
|
+
out = nil
|
123
|
+
fetch(select_max_seq(name)) do |row|
|
124
|
+
out = row[:id]
|
125
|
+
end
|
126
|
+
out
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def delete_from_mysql_sequence(name)
|
131
|
+
"DELETE QUICK IGNORE FROM mysql_sequence WHERE name = '#{name}';"
|
132
|
+
end
|
133
|
+
|
134
|
+
def insert_into_mysql_sequence(name, value)
|
135
|
+
"INSERT INTO mysql_sequence (name, seq) VALUES ('#{name}', #{value});"
|
136
|
+
end
|
137
|
+
|
138
|
+
def drop_sequence_table(name, if_exists = nil)
|
139
|
+
"DROP TABLE #{if_exists || Sequel::Database::IF_EXISTS} #{name};"
|
140
|
+
end
|
141
|
+
|
142
|
+
def insert_into_sequence_table(name, num_label)
|
143
|
+
"INSERT INTO #{name} (fiction) VALUES (#{num_label});"
|
144
|
+
end
|
145
|
+
|
146
|
+
def insert_last_insert_id_into_mysql_sequence(name)
|
147
|
+
"INSERT INTO mysql_sequence (name, seq) VALUES ('#{name}', LAST_INSERT_ID());"
|
148
|
+
end
|
149
|
+
|
150
|
+
def create_sequenced_column(table, _column, sequence)
|
151
|
+
%(
|
152
|
+
CREATE TRIGGER IF NOT EXISTS #{table}_#{sequence} BEFORE INSERT
|
153
|
+
ON #{table}
|
154
|
+
FOR EACH ROW BEGIN
|
155
|
+
DELETE QUICK IGNORE FROM mysql_sequence WHERE name = '#{sequence}';
|
156
|
+
INSERT INTO #{sequence} SET fiction = 0;
|
157
|
+
INSERT INTO mysql_sequence SET name = '#{sequence}', seq = LAST_INSERT_ID();
|
158
|
+
|
159
|
+
END;
|
160
|
+
).strip
|
161
|
+
end
|
162
|
+
|
163
|
+
def update_sequenced_column(table, column, sequence)
|
164
|
+
%(
|
165
|
+
CREATE TRIGGER IF NOT EXISTS #{table}_#{column} BEFORE INSERT
|
166
|
+
ON #{table}
|
167
|
+
FOR EACH ROW FOLLOWS #{table}_#{sequence}
|
168
|
+
SET NEW.#{column} = ( SELECT MAX(seq) FROM mysql_sequence WHERE name = '#{sequence}' );
|
169
|
+
).strip
|
170
|
+
end
|
171
|
+
|
172
|
+
def table_matcher(&block)
|
173
|
+
block.call
|
174
|
+
rescue Sequel::DatabaseError => e
|
175
|
+
return if e.message =~ /\AMysql2::Error: Table(.)*doesn't exist\z/
|
176
|
+
|
177
|
+
# :nocov:
|
178
|
+
raise e
|
179
|
+
# :nocov:
|
180
|
+
end
|
181
|
+
end
|
124
182
|
end
|
125
|
-
out
|
126
183
|
end
|
127
184
|
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
185
|
end
|
@@ -48,14 +48,10 @@ module Sequel
|
|
48
48
|
if current.nil?
|
49
49
|
create_sequence(stringify(name), { start: value })
|
50
50
|
elsif value < current
|
51
|
-
# sql = [delete_from_sqlite_sequence(name)]
|
52
|
-
# sql << drop_sequence_table(name)
|
53
|
-
# sql << insert_into_sqlite_sequence(name, value)
|
54
|
-
# run(sql.join("\n"))
|
55
51
|
log_info DANGER_OPT_ID
|
56
52
|
value = current
|
57
53
|
else
|
58
|
-
run(
|
54
|
+
run(update_sqlite_sequence(stringify(name), value))
|
59
55
|
end
|
60
56
|
value
|
61
57
|
end
|
@@ -96,21 +92,12 @@ module Sequel
|
|
96
92
|
"INSERT INTO #{name} (fiction) VALUES (#{num_label});"
|
97
93
|
end
|
98
94
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
UPDATE sqlite_sequence
|
106
|
-
SET seq = #{[current, value].max}
|
107
|
-
WHERE name = '#{name}';
|
108
|
-
)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def delete_from_sqlite_sequence(name)
|
113
|
-
"DELETE FROM sqlite_sequence WHERE name = '#{name}';"
|
95
|
+
def update_sqlite_sequence(name, value)
|
96
|
+
%(
|
97
|
+
UPDATE sqlite_sequence
|
98
|
+
SET seq = #{value}
|
99
|
+
WHERE name = '#{name}';
|
100
|
+
)
|
114
101
|
end
|
115
102
|
|
116
103
|
def drop_sequence_table(name, if_exists = nil)
|
@@ -14,24 +14,24 @@ module Sequel
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def custom_sequence?(_sequence_name)
|
17
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
17
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
18
18
|
end
|
19
19
|
|
20
20
|
def check_sequences
|
21
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
21
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
22
22
|
end
|
23
23
|
|
24
24
|
def create_sequence(_name, _options = {})
|
25
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
25
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
26
26
|
end
|
27
27
|
|
28
28
|
def drop_sequence(_name, _options = {})
|
29
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
29
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
30
30
|
end
|
31
31
|
|
32
32
|
def quote_name(name)
|
33
33
|
unless respond_to?(:quote_column_name, false)
|
34
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
34
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
35
35
|
end
|
36
36
|
|
37
37
|
name.to_s.split('.', 2).map { |part| quote_column_name(part) }.join('.')
|
@@ -39,30 +39,30 @@ module Sequel
|
|
39
39
|
|
40
40
|
def quote(name)
|
41
41
|
unless respond_to?(:quote_sequence_name, false)
|
42
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
42
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
43
43
|
end
|
44
44
|
|
45
45
|
name.to_s.split('.', 2).map { |part| quote_sequence_name(part) }.join('.')
|
46
46
|
end
|
47
47
|
|
48
48
|
def nextval_with_label(_name, _num_label = 0)
|
49
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
49
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
50
50
|
end
|
51
51
|
|
52
52
|
def nextval(_name)
|
53
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
53
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
54
54
|
end
|
55
55
|
|
56
56
|
# for Postgres
|
57
57
|
def currval(_name)
|
58
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
58
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
59
59
|
end
|
60
60
|
|
61
61
|
# for MariaDB
|
62
62
|
alias lastval currval
|
63
63
|
|
64
64
|
def setval(_name, _value)
|
65
|
-
raise Sequel::MethodNotAllowed, Sequel::
|
65
|
+
raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
|
66
66
|
end
|
67
67
|
|
68
68
|
def build_exists_condition(option)
|
@@ -14,9 +14,13 @@ module Sequel
|
|
14
14
|
define_singleton_method(:connect) do |*args|
|
15
15
|
db = old_connect.call(*args)
|
16
16
|
if db.adapter_scheme == :mysql2
|
17
|
-
@dbms = db.mariadb?
|
18
|
-
|
19
|
-
|
17
|
+
@dbms = if db.mariadb?
|
18
|
+
Sequel::Sequence::Database::Server::Mariadb
|
19
|
+
else
|
20
|
+
Sequel::Sequence::Database::Server::Mysql
|
21
|
+
end
|
22
|
+
db.log_info "Sequel::Database.connect (mariadb? = #{db.mariadb?.inspect})"
|
23
|
+
db.log_info "Sequel::Database.connect (server_version = #{db.server_version.inspect})"
|
20
24
|
Sequel::Mysql2::Database.include(@dbms)
|
21
25
|
end
|
22
26
|
db
|
data/test/mysql_test_helper.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mock_test_helper'
|
4
|
+
|
5
|
+
class MockSequenceTest < Minitest::Test
|
6
|
+
test 'checks check_options with params' do
|
7
|
+
mocked_method = Minitest::Mock.new
|
8
|
+
some_instance = MockDB
|
9
|
+
|
10
|
+
mocked_method.expect :call, true, [Sequel::Database::DANGER_OPT_INCREMENT]
|
11
|
+
some_instance.stub :log_info, mocked_method do
|
12
|
+
some_instance.check_options({ increment: 2 })
|
13
|
+
end
|
14
|
+
mocked_method.verify
|
15
|
+
|
16
|
+
mocked_method.expect :call, true, [Sequel::Database::DANGER_OPT_INCREMENT]
|
17
|
+
some_instance.stub :log_info, mocked_method do
|
18
|
+
some_instance.check_options({ step: 2 })
|
19
|
+
end
|
20
|
+
mocked_method.verify
|
21
|
+
end
|
22
|
+
|
23
|
+
test 'checks custom_sequence?' do
|
24
|
+
assert_raises Sequel::MethodNotAllowed do
|
25
|
+
MockDB.custom_sequence?(:position)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
test 'checks check_sequences' do
|
30
|
+
assert_raises Sequel::MethodNotAllowed do
|
31
|
+
MockDB.check_sequences
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
test 'checks create_sequence' do
|
36
|
+
assert_raises Sequel::MethodNotAllowed do
|
37
|
+
MockDB.create_sequence(:position)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
test 'checks drop_sequence' do
|
42
|
+
assert_raises Sequel::MethodNotAllowed do
|
43
|
+
MockDB.drop_sequence(:position)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
test 'checks quote_name' do
|
48
|
+
assert_raises Sequel::MethodNotAllowed do
|
49
|
+
MockDB.quote_name(:position)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
test 'checks quote' do
|
54
|
+
assert_raises Sequel::MethodNotAllowed do
|
55
|
+
MockDB.quote(:position)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
test 'checks nextval_with_label' do
|
60
|
+
assert_raises Sequel::MethodNotAllowed do
|
61
|
+
MockDB.nextval_with_label(:position, 100)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
test 'checks nextval' do
|
66
|
+
assert_raises Sequel::MethodNotAllowed do
|
67
|
+
MockDB.nextval(:position)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
test 'checks currval' do
|
72
|
+
assert_raises Sequel::MethodNotAllowed do
|
73
|
+
MockDB.currval(:position)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
test 'checks lastval' do
|
78
|
+
assert_raises Sequel::MethodNotAllowed do
|
79
|
+
MockDB.lastval(:position)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
test 'checks setval' do
|
84
|
+
assert_raises Sequel::MethodNotAllowed do
|
85
|
+
MockDB.setval(:position, 100)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
test 'checks build_exists_condition for a true condition' do
|
90
|
+
assert_equal Sequel::Database::IF_EXISTS, MockDB.build_exists_condition(true)
|
91
|
+
end
|
92
|
+
|
93
|
+
test 'checks build_exists_condition for a false condition' do
|
94
|
+
assert_equal Sequel::Database::IF_NOT_EXISTS, MockDB.build_exists_condition(false)
|
95
|
+
end
|
96
|
+
|
97
|
+
test 'checks build_exists_condition for a non boolean condition' do
|
98
|
+
assert_nil MockDB.build_exists_condition('')
|
99
|
+
end
|
100
|
+
end
|
@@ -352,4 +352,20 @@ class MysqlSequenceTest < Minitest::Test
|
|
352
352
|
|
353
353
|
assert_equal pos4 - pos2, 2
|
354
354
|
end
|
355
|
+
|
356
|
+
test "catches a Mysql2::Error: «Table mysql_sequence doesn't exist»" do
|
357
|
+
assert !sequence_table_exists?('position')
|
358
|
+
|
359
|
+
MysqlDB.drop_table :mysql_sequence, if_exists: true
|
360
|
+
|
361
|
+
last_number = MysqlDB.lastval(:position)
|
362
|
+
assert_nil last_number
|
363
|
+
end
|
364
|
+
|
365
|
+
test 'creates a Sequence by calling DB.setval(position, 1) if it still does not exist' do
|
366
|
+
assert !sequence_table_exists?('position')
|
367
|
+
|
368
|
+
MysqlDB.setval(:position, 100)
|
369
|
+
assert_equal 100, MysqlDB.currval(:position)
|
370
|
+
end
|
355
371
|
end
|
@@ -166,9 +166,7 @@ class SqliteSequenceTest < Minitest::Test
|
|
166
166
|
end
|
167
167
|
end.down
|
168
168
|
|
169
|
-
sequence = SQLiteDB.check_sequences.
|
170
|
-
seq[:name] == 'position'
|
171
|
-
end
|
169
|
+
sequence = (list = SQLiteDB.check_sequences).empty? ? nil : list
|
172
170
|
|
173
171
|
assert_nil sequence
|
174
172
|
end
|
@@ -192,9 +190,7 @@ class SqliteSequenceTest < Minitest::Test
|
|
192
190
|
end
|
193
191
|
end.down
|
194
192
|
|
195
|
-
sequence = SQLiteDB.check_sequences.
|
196
|
-
seq[:name] == 'position'
|
197
|
-
end
|
193
|
+
sequence = (list = SQLiteDB.check_sequences).empty? ? nil : list
|
198
194
|
|
199
195
|
assert_nil sequence
|
200
196
|
end
|
@@ -295,7 +291,6 @@ class SqliteSequenceTest < Minitest::Test
|
|
295
291
|
test 'creates table that references sequence' do
|
296
292
|
with_migration do
|
297
293
|
def up
|
298
|
-
drop_table :apprentices, if_exists: true
|
299
294
|
create_sequence :position_id, if_exists: false, start: 1
|
300
295
|
create_table :apprentices do
|
301
296
|
primary_key :id
|
@@ -326,4 +321,11 @@ class SqliteSequenceTest < Minitest::Test
|
|
326
321
|
|
327
322
|
assert_equal pos4 - pos2, 2
|
328
323
|
end
|
324
|
+
|
325
|
+
test 'creates a Sequence by calling DB.setval(position, 1) if it still does not exist' do
|
326
|
+
assert !sequence_table_exists?('position')
|
327
|
+
|
328
|
+
SQLiteDB.setval(:position, 100)
|
329
|
+
assert_equal 100, SQLiteDB.currval(:position)
|
330
|
+
end
|
329
331
|
end
|
data/test/sqlite_test_helper.rb
CHANGED
@@ -10,9 +10,10 @@ SQLiteDB = Sequel.connect(
|
|
10
10
|
|
11
11
|
module SqliteTestHelper
|
12
12
|
def recreate_table
|
13
|
+
SQLiteDB.drop_table :apprentices, if_exists: true
|
13
14
|
SQLiteDB.drop_sequence :position
|
14
15
|
SQLiteDB.drop_sequence :position_id, if_exists: true
|
15
|
-
SQLiteDB.
|
16
|
+
SQLiteDB.drop_table :objects, if_exists: true
|
16
17
|
SQLiteDB.drop_sequence 'a'
|
17
18
|
SQLiteDB.drop_sequence 'b'
|
18
19
|
SQLiteDB.drop_sequence 'c'
|
@@ -27,4 +28,10 @@ module SqliteTestHelper
|
|
27
28
|
|
28
29
|
Class.new(migration_class, &block).new(SQLiteDB)
|
29
30
|
end
|
31
|
+
|
32
|
+
def sequence_table_exists?(name)
|
33
|
+
sql = "SELECT name FROM sqlite_schema WHERE type ='table' AND name NOT LIKE 'sqlite_%';"
|
34
|
+
table_list = SQLiteDB.fetch(sql).all.map { |_key, value| value }
|
35
|
+
table_list.include?(name)
|
36
|
+
end
|
30
37
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel-sequence
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nikolai Bocharov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-09-
|
11
|
+
date: 2023-09-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -194,9 +194,11 @@ files:
|
|
194
194
|
- lib/sequel/sequence/version.rb
|
195
195
|
- sequel-sequence.gemspec
|
196
196
|
- test/mariadb_test_helper.rb
|
197
|
+
- test/mock_test_helper.rb
|
197
198
|
- test/mysql_test_helper.rb
|
198
199
|
- test/postgresql_test_helper.rb
|
199
200
|
- test/sequel/mariadb_sequence_test.rb
|
201
|
+
- test/sequel/mock_sequence_test.rb
|
200
202
|
- test/sequel/mysql_sequence_test.rb
|
201
203
|
- test/sequel/postgresql_sequence_test.rb
|
202
204
|
- test/sequel/sqlite_sequence_test.rb
|