sequel-sequence 0.4.0 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ci.gemfile +25 -0
- data/.github/workflows/ci.yml +4 -2
- data/CHANGELOG.md +18 -0
- data/CONTRIBUTING.md +16 -5
- data/README.md +19 -4
- data/Rakefile +26 -1
- data/lib/sequel/error.rb +6 -6
- data/lib/sequel/sequence/database/postgresql.rb +17 -7
- data/lib/sequel/sequence/database/server/mariadb.rb +89 -56
- data/lib/sequel/sequence/database/server/mysql.rb +179 -172
- data/lib/sequel/sequence/database/sqlite.rb +16 -24
- 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/lib/sequel/sequence.rb +46 -14
- data/sequel-sequence.gemspec +13 -13
- data/test/mariadb_test_helper.rb +2 -0
- data/test/mock_test_helper.rb +5 -0
- data/test/mysql_test_helper.rb +5 -0
- data/test/postgresql_test_helper.rb +3 -1
- data/test/sequel/mariadb_sequence_test.rb +67 -16
- data/test/sequel/mock_sequence_test.rb +100 -0
- data/test/sequel/mysql_sequence_test.rb +58 -16
- data/test/sequel/postgresql_sequence_test.rb +58 -6
- data/test/sequel/sqlite_sequence_test.rb +51 -11
- data/test/sqlite_test_helper.rb +8 -1
- data/test/test_helper.rb +1 -0
- metadata +8 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a027c577de1ee27127880328c29208456a26635c9ea18b1c5eb3c1cb0dbbd5fc
|
4
|
+
data.tar.gz: 71764d6ec16bf3425ba3d3bfb4ac47c04dd2a6f7001342c0ae35ebec63af293d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ce8091d3c644545cecff05c0f85a6252bdd8de015710770a861bfea81793fc50c5b96d19ce304fc2158cd40fc8bfde72f88309e0dc6dea468e2a977a533f980
|
7
|
+
data.tar.gz: a80404a67e51b77a1dd6ac40fe8ebbd93329b170e25855024f325e965f76ec9ad723ea2698fc4f699edad9cd7eb8555c5f6b28d870eb6c2f0355c44686904b19
|
data/.ci.gemfile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
gem 'bundler', '>= 2.2.4'
|
6
|
+
gem 'minitest-utils', '~> 0.4.6'
|
7
|
+
gem 'pry-byebug', '~> 3.10.1'
|
8
|
+
gem 'rake', '~> 13.0.2'
|
9
|
+
gem 'rubocop', '~> 1.56.3'
|
10
|
+
gem 'sequel', '>= 5.28', '<6.0'
|
11
|
+
gem 'simplecov', '~> 0.22.0'
|
12
|
+
|
13
|
+
# MRI/Rubinius Adapter Dependencies
|
14
|
+
platform :ruby do
|
15
|
+
gem 'mysql2', '~> 0.5.3'
|
16
|
+
gem 'pg', '~> 1.5.4'
|
17
|
+
gem 'sqlite3', '~> 1.6.0'
|
18
|
+
end
|
19
|
+
|
20
|
+
# JRuby Adapter Dependencies
|
21
|
+
platform :jruby do
|
22
|
+
gem 'jdbc-mysql', '~> 8.0.17'
|
23
|
+
gem 'jdbc-postgres', '~> 42.2.14'
|
24
|
+
gem 'jdbc-sqlite3', '~> 3.42'
|
25
|
+
end
|
data/.github/workflows/ci.yml
CHANGED
@@ -16,7 +16,6 @@ jobs:
|
|
16
16
|
os: ['ubuntu-latest']
|
17
17
|
sequel: ['~>5.28']
|
18
18
|
ruby: ['3.2', '3.1', '3.0', '2.7']
|
19
|
-
gemfile: ['Gemfile']
|
20
19
|
runs-on: ${{ matrix.os }}
|
21
20
|
name: Tests with Ruby ${{ matrix.ruby }}
|
22
21
|
|
@@ -58,7 +57,7 @@ jobs:
|
|
58
57
|
|
59
58
|
env:
|
60
59
|
SEQUEL: ${{ matrix.sequel }}
|
61
|
-
BUNDLE_GEMFILE:
|
60
|
+
BUNDLE_GEMFILE: .ci.gemfile
|
62
61
|
steps:
|
63
62
|
- uses: actions/checkout@v4
|
64
63
|
|
@@ -126,5 +125,8 @@ jobs:
|
|
126
125
|
env:
|
127
126
|
TEST_SQLITE_DATABASE: "db/test.sqlite3"
|
128
127
|
|
128
|
+
- name: Run Mock Tests
|
129
|
+
run: bundle exec rake TEST=test/sequel/mock_sequence_test.rb
|
130
|
+
|
129
131
|
- name: Run Rubocop
|
130
132
|
run: bundle exec rake rubocop
|
data/CHANGELOG.md
CHANGED
@@ -11,6 +11,24 @@ Prefix your message with one of the following:
|
|
11
11
|
- [Security] in case of vulnerabilities.
|
12
12
|
-->
|
13
13
|
|
14
|
+
## v0.4.2 - 2023-10-03
|
15
|
+
|
16
|
+
- [Added] Additions into README.md.
|
17
|
+
- [Added] Exclusion of dependence on the Postgresql constraint for "PG::The object is not in the required state P: ERROR: currval of sequence "name_of_sequence" is not yet defined in this session".
|
18
|
+
- [Added] `custom_sequence?` method for MariaDB and SQLite.
|
19
|
+
- [Fixed] Dependencies on gems by moving them from .gemspec to Gemfile
|
20
|
+
- [Fixed] `currval` for initial state of sequence in Postgresql
|
21
|
+
- [Fixed] `lastval` for initial state of sequence in MariaDB
|
22
|
+
- [Changed] The default action `setval` for MariaDB to invoke `setval` if necessary
|
23
|
+
- [Changed] The class initialization algorithm in `sequence.rb` depending on the gems included in the user project.
|
24
|
+
|
25
|
+
## v0.4.1 - 2023-09-28
|
26
|
+
|
27
|
+
- [Added] Important notice to README.md.
|
28
|
+
- [Added] MySQL tests cover 100%.
|
29
|
+
- [Added] SQLite tests cover 100%.
|
30
|
+
- [Added] Mock connection to check for additional ORM exceptions.
|
31
|
+
|
14
32
|
## v0.4.0 - 2023-09-26
|
15
33
|
|
16
34
|
- [Fixed] Differences between MySQL and MariaDB.
|
data/CONTRIBUTING.md
CHANGED
@@ -33,13 +33,14 @@ your contribution is according to how this project works.
|
|
33
33
|
|
34
34
|
1. [Fork](https://help.github.com/forking/) sequel-sequence
|
35
35
|
2. Create a topic branch - `git checkout -b my_branch`
|
36
|
-
3.
|
37
|
-
4.
|
36
|
+
3. Unlock gem dependencies in `sequel-sequence.gemspec`
|
37
|
+
4. Make your changes using [descriptive commit messages](#commit-messages)
|
38
|
+
5. Update CHANGELOG.md describing your changes by adding an entry to the
|
38
39
|
"Unreleased" section. If this section is not available, create one right
|
39
40
|
before the last version.
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
6. Push to your branch - `git push origin my_branch`
|
42
|
+
7. [Create a pull request](https://docs.github.com/articles/creating-a-pull-request)
|
43
|
+
8. That's it!
|
43
44
|
|
44
45
|
## Styleguides
|
45
46
|
|
@@ -235,4 +236,14 @@ $ bundle exec rake TEST=test/sequel/postgresql_sequence_test.rb
|
|
235
236
|
$ bundle exec rake TEST=test/sequel/mariadb_sequence_test.rb
|
236
237
|
$ bundle exec rake TEST=test/sequel/mysql_sequence_test.rb
|
237
238
|
$ bundle exec rake TEST=test/sequel/sqlite_sequence_test.rb
|
239
|
+
$ bundle exec rake TEST=test/sequel/mock_sequence_test.rb
|
240
|
+
```
|
241
|
+
|
242
|
+
Short command:
|
243
|
+
```bash
|
244
|
+
$ bundle exec rake postgresql
|
245
|
+
$ bundle exec rake mariadb
|
246
|
+
$ bundle exec rake mysql
|
247
|
+
$ bundle exec rake sqlite
|
248
|
+
$ bundle exec rake mock
|
238
249
|
```
|
data/README.md
CHANGED
@@ -16,7 +16,7 @@ gem install sequel-sequence
|
|
16
16
|
Or add the following line to your project's Gemfile:
|
17
17
|
|
18
18
|
```ruby
|
19
|
-
gem
|
19
|
+
gem 'sequel-sequence'
|
20
20
|
```
|
21
21
|
|
22
22
|
## Usage with PostgreSQL and MariaDB
|
@@ -24,6 +24,8 @@ gem "sequel-sequence"
|
|
24
24
|
To create and delete a `SEQUENCE`, simply use the `create_sequence` and `drop_sequence` methods.
|
25
25
|
|
26
26
|
```ruby
|
27
|
+
require: 'sequel-sequence'
|
28
|
+
|
27
29
|
Sequel.migration do
|
28
30
|
up do
|
29
31
|
create_sequence :position, if_exists: false
|
@@ -37,6 +39,8 @@ end
|
|
37
39
|
|
38
40
|
It would also be correct to write:
|
39
41
|
```ruby
|
42
|
+
require: 'sequel-sequence'
|
43
|
+
|
40
44
|
Sequel.migration do
|
41
45
|
up do
|
42
46
|
create_sequence :position
|
@@ -48,7 +52,7 @@ Sequel.migration do
|
|
48
52
|
end
|
49
53
|
```
|
50
54
|
|
51
|
-
You can also specify the initial value
|
55
|
+
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
56
|
|
53
57
|
```ruby
|
54
58
|
create_sequence :position, increment: 2
|
@@ -95,7 +99,7 @@ DB.currval("position")
|
|
95
99
|
DB.lastval("position")
|
96
100
|
# Both options are acceptable in PostgreSQL and MySQL.
|
97
101
|
|
98
|
-
# Set sequence
|
102
|
+
# Set a new sequence value. It must be greater than lastval or currval. Only PostgreSQL allows setting a lower value.
|
99
103
|
DB.setval("position", 1234)
|
100
104
|
```
|
101
105
|
|
@@ -107,7 +111,7 @@ CREATE TABLE `name_of_your_sequence_table`
|
|
107
111
|
(id integer primary key autoincrement, fiction integer);
|
108
112
|
```
|
109
113
|
|
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.
|
114
|
+
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
115
|
```ruby
|
112
116
|
create_sequence :position, if_exists: false, start: 1000, numeric_label: 1
|
113
117
|
```
|
@@ -120,6 +124,17 @@ By default, `fiction` has a zero value.
|
|
120
124
|
|
121
125
|
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
126
|
|
127
|
+
## Known issues you may encounter
|
128
|
+
|
129
|
+
- 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.
|
130
|
+
- When you start with a new database in SQLite, you'll receive an error message - "`SQLite3::SQLException: no such table: sqlite_sequence`". `sqlite_sequence` table is not created, until you define at least one autoincrement and primary key column in your schema.
|
131
|
+
|
132
|
+
## Additional handy functions
|
133
|
+
|
134
|
+
To discover a database information about `SEQUENCE`s you could take advantage of `check_sequences` and `custom_sequence?` methods.
|
135
|
+
- `custom_sequence?(:sequence_name)` allows you to instantly find out the availability of the called `SEQUENCE`.
|
136
|
+
- `check_sequences` provides complete information about known `SEQUENCE`s in the datebase. The output data depends on RDBMS.
|
137
|
+
|
123
138
|
## Maintainer
|
124
139
|
|
125
140
|
- [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
|
@@ -65,12 +65,21 @@ module Sequel
|
|
65
65
|
|
66
66
|
# for Postgres
|
67
67
|
def currval(name)
|
68
|
-
|
68
|
+
quoted_name = quote(name.to_s)
|
69
69
|
out = nil
|
70
|
-
fetch("SELECT currval(#{
|
70
|
+
fetch("SELECT currval(#{quoted_name})") do |row|
|
71
71
|
out = row[:currval]
|
72
72
|
end
|
73
73
|
out
|
74
|
+
rescue Sequel::DatabaseError => e
|
75
|
+
# We exclude dependence on the postgresql constraint.
|
76
|
+
if e.message =~ /\APG::ObjectNotInPrerequisiteState:(.)*is not yet defined in this session\n\z/
|
77
|
+
return nextval(name)
|
78
|
+
end
|
79
|
+
|
80
|
+
# :nocov:
|
81
|
+
raise e
|
82
|
+
# :nocov:
|
74
83
|
end
|
75
84
|
|
76
85
|
# for MariaDB
|
@@ -86,11 +95,12 @@ module Sequel
|
|
86
95
|
end
|
87
96
|
|
88
97
|
def set_column_default_nextval(table, column, sequence)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
98
|
+
sql = %(
|
99
|
+
ALTER TABLE IF EXISTS #{table}
|
100
|
+
ALTER COLUMN #{quote_name(column.to_s)}
|
101
|
+
SET DEFAULT nextval(#{quote(sequence.to_s)}::regclass)
|
102
|
+
).strip
|
103
|
+
run sql
|
94
104
|
end
|
95
105
|
end
|
96
106
|
end
|
@@ -1,71 +1,104 @@
|
|
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 custom_sequence?(sequence_name)
|
17
|
+
db = name_of_current_database
|
18
|
+
return false if db.empty?
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
name = quote_name(name.to_s)
|
20
|
+
sql = "SHOW FULL TABLES WHERE Table_type = 'SEQUENCE' and Tables_in_#{db} = '#{sequence_name}';"
|
21
|
+
fetch(sql).all.size.positive?
|
22
|
+
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
sql << ';'
|
24
|
+
def check_sequences
|
25
|
+
fetch("SHOW FULL TABLES WHERE Table_type = 'SEQUENCE';").all.to_a
|
26
|
+
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
+
def create_sequence(name, options = {})
|
29
|
+
increment = options[:increment] || options[:step]
|
30
|
+
if_exists = build_exists_condition(options[:if_exists])
|
31
|
+
name = quote_name(name.to_s)
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
33
|
+
sql = ["CREATE SEQUENCE #{if_exists || Sequel::Database::IF_NOT_EXISTS} #{name}"]
|
34
|
+
sql << "INCREMENT BY #{increment}" if increment
|
35
|
+
sql << "START WITH #{options[:start]}" if options[:start]
|
36
|
+
sql << ';'
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
out = nil
|
38
|
-
fetch("SELECT nextval(#{name});") do |row|
|
39
|
-
out = row["nextval(#{name})".to_sym]
|
40
|
-
end
|
41
|
-
out
|
42
|
-
end
|
38
|
+
run(sql.join("\n"))
|
39
|
+
end
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
50
|
-
out
|
51
|
-
end
|
41
|
+
def drop_sequence(name)
|
42
|
+
name = quote_name(name.to_s)
|
43
|
+
sql = "DROP SEQUENCE IF EXISTS #{name}"
|
44
|
+
run(sql)
|
45
|
+
end
|
52
46
|
|
53
|
-
|
47
|
+
def nextval(name)
|
48
|
+
name = quote(name.to_s)
|
49
|
+
out = nil
|
50
|
+
fetch("SELECT nextval(#{name});") do |row|
|
51
|
+
out = row["nextval(#{name})".to_sym]
|
52
|
+
end
|
53
|
+
out
|
54
|
+
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
56
|
+
def lastval(name)
|
57
|
+
quoted_name = quote(name.to_s)
|
58
|
+
out = nil
|
59
|
+
fetch("SELECT lastval(#{quoted_name});") do |row|
|
60
|
+
out = row["lastval(#{quoted_name})".to_sym]
|
61
|
+
end
|
62
|
+
return nextval(name) if out.nil?
|
63
|
+
|
64
|
+
out
|
65
|
+
end
|
66
|
+
|
67
|
+
alias currval lastval
|
63
68
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
69
|
+
def setval(name, value)
|
70
|
+
current = lastval(name)
|
71
|
+
if value <= current
|
72
|
+
log_info Sequel::Database::DANGER_OPT_ID if value < current
|
73
|
+
value = current
|
74
|
+
else
|
75
|
+
quoted_name = quote(name.to_s)
|
76
|
+
value -= 1
|
77
|
+
out = nil
|
78
|
+
fetch("SELECT setval(#{quoted_name}, #{value});") do |row|
|
79
|
+
out = row["setval(#{quoted_name}, #{value})".to_sym]
|
80
|
+
end
|
81
|
+
value = nextval(name)
|
82
|
+
end
|
83
|
+
value
|
84
|
+
end
|
85
|
+
|
86
|
+
def set_column_default_nextval(table, column, sequence)
|
87
|
+
sql = %(
|
88
|
+
ALTER TABLE IF EXISTS #{quote(table.to_s)}
|
89
|
+
ALTER COLUMN #{quote_name(column.to_s)}
|
90
|
+
SET DEFAULT nextval(#{quote(sequence.to_s)})
|
91
|
+
).strip
|
92
|
+
run sql
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def name_of_current_database
|
98
|
+
fetch('SELECT DATABASE() AS db;').first[:db]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
70
103
|
end
|
71
104
|
end
|