sequel-sequence 0.3.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
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
182
+ end
183
+ end
184
+ end
185
+ end
@@ -14,8 +14,11 @@ module Sequel
14
14
  check_options(options)
15
15
  if_exists = build_exists_condition(options[:if_exists])
16
16
  start_option = options[:start] || 1
17
+ num_label = options[:numeric_label] || 0
18
+ return if (current = lastval(name)) && (current >= start_option)
19
+
17
20
  sql = [create_sequence_table(stringify(name), if_exists)]
18
- sql << insert_into_sqlite_sequence(stringify(name), start_option)
21
+ sql << insert_into_sequence_table_init_values(stringify(name), start_option, num_label)
19
22
  run(sql.join("\n"))
20
23
  end
21
24
 
@@ -26,12 +29,12 @@ module Sequel
26
29
 
27
30
  def nextval(name)
28
31
  run(insert_into_sequence_table(stringify(name), 0))
29
- take_seq(name)
32
+ take_seq(stringify(name))
30
33
  end
31
34
 
32
35
  def nextval_with_label(name, num_label = 0)
33
36
  run(insert_into_sequence_table(stringify(name), num_label))
34
- take_seq(name)
37
+ take_seq(stringify(name))
35
38
  end
36
39
 
37
40
  def lastval(name)
@@ -45,14 +48,10 @@ module Sequel
45
48
  if current.nil?
46
49
  create_sequence(stringify(name), { start: value })
47
50
  elsif value < current
48
- # sql = [delete_from_sqlite_sequence(name)]
49
- # sql << drop_sequence_table(name)
50
- # sql << insert_into_sqlite_sequence(name, value)
51
- # run(sql.join("\n"))
52
51
  log_info DANGER_OPT_ID
53
52
  value = current
54
53
  else
55
- run(insert_into_sqlite_sequence(stringify(name), value))
54
+ run(update_sqlite_sequence(stringify(name), value))
56
55
  end
57
56
  value
58
57
  end
@@ -85,25 +84,20 @@ module Sequel
85
84
  )
86
85
  end
87
86
 
88
- def insert_into_sequence_table(name, num_label)
89
- "INSERT INTO #{name} (fiction) VALUES (#{num_label});"
87
+ def insert_into_sequence_table_init_values(name, start_id, num_label)
88
+ "INSERT INTO #{name} (id, fiction) VALUES (#{start_id}, #{num_label});"
90
89
  end
91
90
 
92
- def insert_into_sqlite_sequence(name, value)
93
- current = take_seq(name)
94
- if current.nil?
95
- "INSERT INTO sqlite_sequence (name, seq) VALUES ('#{name}', #{value});"
96
- else
97
- %(
98
- UPDATE sqlite_sequence
99
- SET seq = #{[current, value].max}
100
- WHERE name = '#{name}';
101
- )
102
- end
91
+ def insert_into_sequence_table(name, num_label)
92
+ "INSERT INTO #{name} (fiction) VALUES (#{num_label});"
103
93
  end
104
94
 
105
- def delete_from_sqlite_sequence(name)
106
- "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
+ )
107
101
  end
108
102
 
109
103
  def drop_sequence_table(name, if_exists = nil)
@@ -119,7 +113,7 @@ module Sequel
119
113
  CREATE TRIGGER IF NOT EXISTS #{table}_#{sequence} AFTER INSERT
120
114
  ON #{table}
121
115
  BEGIN
122
- INSERT INTO #{sequence}(fiction) VALUES (0);
116
+ INSERT INTO #{sequence} (fiction) VALUES (0);
123
117
  UPDATE #{table}
124
118
  SET #{column} = (SELECT MAX(seq) FROM sqlite_sequence WHERE name = '#{sequence}')
125
119
  WHERE rowid = NEW.rowid;
@@ -14,24 +14,24 @@ module Sequel
14
14
  end
15
15
 
16
16
  def custom_sequence?(_sequence_name)
17
- raise Sequel::MethodNotAllowed, Sequel::MethodNotAllowed::METHOD_NOT_ALLOWED
17
+ raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
18
18
  end
19
19
 
20
20
  def check_sequences
21
- raise Sequel::MethodNotAllowed, Sequel::MethodNotAllowed::METHOD_NOT_ALLOWED
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::MethodNotAllowed::METHOD_NOT_ALLOWED
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::MethodNotAllowed::METHOD_NOT_ALLOWED
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::MethodNotAllowed::METHOD_NOT_ALLOWED
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::MethodNotAllowed::METHOD_NOT_ALLOWED
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::MethodNotAllowed::METHOD_NOT_ALLOWED
49
+ raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
50
50
  end
51
51
 
52
52
  def nextval(_name)
53
- raise Sequel::MethodNotAllowed, Sequel::MethodNotAllowed::METHOD_NOT_ALLOWED
53
+ raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
54
54
  end
55
55
 
56
- # for connection.adapter_name = "PostgreSQL"
56
+ # for Postgres
57
57
  def currval(_name)
58
- raise Sequel::MethodNotAllowed, Sequel::MethodNotAllowed::METHOD_NOT_ALLOWED
58
+ raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
59
59
  end
60
60
 
61
- # for connection.adapter_name = "Mysql2"
61
+ # for MariaDB
62
62
  alias lastval currval
63
63
 
64
64
  def setval(_name, _value)
65
- raise Sequel::MethodNotAllowed, Sequel::MethodNotAllowed::METHOD_NOT_ALLOWED
65
+ raise Sequel::MethodNotAllowed, Sequel::Database::METHOD_NOT_ALLOWED
66
66
  end
67
67
 
68
68
  def build_exists_condition(option)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'database/server/mysql'
4
+ require_relative 'database/server/mariadb'
5
+
6
+ module Sequel
7
+ class Database
8
+ class << self
9
+ attr_reader :dbms
10
+ end
11
+
12
+ old_connect = singleton_method(:connect)
13
+
14
+ define_singleton_method(:connect) do |*args|
15
+ db = old_connect.call(*args)
16
+ if db.adapter_scheme == :mysql2
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})"
24
+ Sequel::Mysql2::Database.include(@dbms)
25
+ end
26
+ db
27
+ end
28
+ end
29
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sequel
4
4
  module Sequence
5
- VERSION = '0.3.0'
5
+ VERSION = '0.4.1'
6
6
  end
7
7
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  require 'sequel/database'
4
4
  require 'sequel/adapters/postgres'
5
- # require 'sequel/adapters/mysql'
6
5
  require 'sequel/adapters/mysql2'
7
6
  require 'sequel/adapters/sqlite'
8
7
  require 'sequel/error'
8
+ require 'sequel/sequence/database_ext_connection'
9
9
 
10
10
  module Sequel
11
11
  module Sequence
@@ -13,8 +13,6 @@ module Sequel
13
13
 
14
14
  module Database
15
15
  require 'sequel/sequence/database/postgresql'
16
- # require "sequel/sequence/database/mysql"
17
- require 'sequel/sequence/database/mysql2'
18
16
  require 'sequel/sequence/database/sqlite'
19
17
  end
20
18
  end
@@ -26,12 +24,6 @@ Sequel::Database.include(
26
24
  Sequel::Postgres::Database.include(
27
25
  Sequel::Sequence::Database::PostgreSQL
28
26
  )
29
- Sequel::Mysql2::Database.include(
30
- Sequel::Sequence::Database::Mysql2
31
- )
32
- # Sequel::Mysql::Database.include(
33
- # Sequel::Sequence::Database::Mysql
34
- # )
35
27
  Sequel::SQLite::Database.include(
36
28
  Sequel::Sequence::Database::SQLite
37
29
  )
@@ -7,10 +7,11 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Sequel::Sequence::VERSION
8
8
  spec.licenses = ['MIT']
9
9
  spec.summary = \
10
- "Add support for PostgreSQL's and MySQL's SEQUENCE on Sequel migrations."
10
+ 'Adds SEQUENCE support to Sequel for migrations to PostgreSQL, MariaDB, MySQL and SQLite.'
11
11
  spec.description = <<-DES
12
12
  This gem provides a single interface for SEQUENCE functionality
13
- in Postgresql and Mysql databases within the Sequel ORM.
13
+ in Postgresql and MariaDB DBMS within the Sequel ORM.
14
+ It also models the Sequences to meet the needs of SQLite and MySQL users.
14
15
  DES
15
16
  spec.authors = ['Nikolai Bocharov']
16
17
  spec.email = ['it.architect@yahoo.com']
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ MariaDB = Sequel.connect(
6
+ adapter: 'mysql2',
7
+ user: ENV['TEST_MARIA_USERNAME'] || 'root',
8
+ password: ENV['TEST_MARIA_PASSWORD'] || 'root',
9
+ host: ENV['TEST_MARIA_HOST'] || '127.0.0.1',
10
+ port: ENV['TEST_MARIA_PORT'] || 3306,
11
+ database: ENV['TEST_MARIA_DATABASE'] || 'test'
12
+ )
13
+
14
+ module MariadbTestHelper
15
+ def recreate_table
16
+ MariaDB.run 'DROP SEQUENCE IF EXISTS position'
17
+ MariaDB.run 'DROP TABLE IF EXISTS wares'
18
+ MariaDB.run 'DROP SEQUENCE IF EXISTS a'
19
+ MariaDB.run 'DROP SEQUENCE IF EXISTS b'
20
+ MariaDB.run 'DROP SEQUENCE IF EXISTS c'
21
+ sql = 'CREATE TABLE wares (id INT AUTO_INCREMENT, slug VARCHAR(255), quantity INT DEFAULT(0), PRIMARY KEY(id));'
22
+ MariaDB.run sql
23
+ end
24
+
25
+ def with_migration(&block)
26
+ migration_class = Sequel::Migration
27
+
28
+ Sequel::Model.db = MariaDB
29
+
30
+ Class.new(migration_class, &block).new(MariaDB)
31
+ end
32
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ MockDB = Sequel.connect('mock://postgresql')
@@ -5,20 +5,22 @@ require 'test_helper'
5
5
  MysqlDB = Sequel.connect(
6
6
  adapter: 'mysql2',
7
7
  user: ENV['TEST_MYSQL_USERNAME'] || 'root',
8
- password: ENV['TEST_MYSQL_PASSWORD'] || 'root',
9
- host: ENV['TEST_MYSQL_HOST'] || '127.0.0.1',
10
- port: ENV['TEST_MYSQL_PORT'] || 3306,
8
+ password: ENV['TEST_MYSQL_PASSWORD'] || 'rootroot',
9
+ host: ENV['TEST_MYSQL_HOST'] || '0.0.0.0',
10
+ port: ENV['TEST_MYSQL_PORT'] || 3360,
11
11
  database: ENV['TEST_MYSQL_DATABASE'] || 'test'
12
12
  )
13
13
 
14
14
  module MysqlTestHelper
15
15
  def recreate_table
16
- MysqlDB.run 'DROP SEQUENCE IF EXISTS position'
17
- MysqlDB.run 'DROP TABLE IF EXISTS wares'
18
- MysqlDB.run 'DROP SEQUENCE IF EXISTS a'
19
- MysqlDB.run 'DROP SEQUENCE IF EXISTS b'
20
- MysqlDB.run 'DROP SEQUENCE IF EXISTS c'
21
- sql = 'CREATE TABLE wares (id INT AUTO_INCREMENT, slug VARCHAR(255), quantity INT DEFAULT(0), PRIMARY KEY(id));'
16
+ MysqlDB.drop_table :creators, if_exists: true
17
+ MysqlDB.drop_sequence :position_id, if_exists: true
18
+ MysqlDB.drop_sequence :position
19
+ MysqlDB.drop_table :stuffs, if_exists: true
20
+ MysqlDB.drop_sequence 'a'
21
+ MysqlDB.drop_sequence 'b'
22
+ MysqlDB.drop_sequence 'c'
23
+ sql = 'CREATE TABLE stuffs (id INT AUTO_INCREMENT PRIMARY KEY, slug VARCHAR(255), quantity INT DEFAULT(0));'
22
24
  MysqlDB.run sql
23
25
  end
24
26
 
@@ -29,4 +31,9 @@ module MysqlTestHelper
29
31
 
30
32
  Class.new(migration_class, &block).new(MysqlDB)
31
33
  end
34
+
35
+ def sequence_table_exists?(name)
36
+ table_list = MysqlDB.fetch('SHOW TABLES;').all.map { |_key, value| value }
37
+ table_list.include?(name)
38
+ end
32
39
  end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mariadb_test_helper'
4
+
5
+ class MariadbSequenceTest < Minitest::Test
6
+ include MariadbTestHelper
7
+
8
+ setup do
9
+ recreate_table
10
+ end
11
+
12
+ test 'adds sequence with default values' do
13
+ with_migration do
14
+ def up
15
+ # create_sequence :position, {start: 1, increment: 1} - default values
16
+ create_sequence :position
17
+ end
18
+ end.up
19
+
20
+ assert_equal 1, MariaDB.nextval(:position)
21
+ assert_equal 2, MariaDB.nextval(:position)
22
+ end
23
+
24
+ test 'adds sequence reader within model and its inherited class' do
25
+ with_migration do
26
+ def up
27
+ create_sequence :position
28
+ end
29
+ end.up
30
+
31
+ class Ware < Sequel::Model; end
32
+
33
+ assert_equal 1, Ware.db.nextval('position')
34
+ assert_equal 2, Ware.db.nextval('position')
35
+
36
+ class InheritedWare < Ware; end
37
+
38
+ assert_equal 3, InheritedWare.db.nextval(:position)
39
+ assert_equal 4, InheritedWare.db.nextval(:position)
40
+ end
41
+
42
+ test 'adds sequence starting at 100' do
43
+ with_migration do
44
+ def up
45
+ create_sequence :position, start: 100
46
+ end
47
+ end.up
48
+
49
+ assert_equal 100, MariaDB.nextval(:position)
50
+ assert_equal 101, MariaDB.nextval(:position)
51
+ end
52
+
53
+ test 'adds sequence incremented by 2' do
54
+ with_migration do
55
+ def up
56
+ create_sequence :position, increment: 2
57
+ end
58
+ end.up
59
+
60
+ assert_equal 1, MariaDB.nextval(:position)
61
+ assert_equal 3, MariaDB.nextval(:position)
62
+ end
63
+
64
+ test 'adds sequence incremented by 2 (using :step alias)' do
65
+ with_migration do
66
+ def up
67
+ create_sequence :position, step: 2
68
+ end
69
+ end.up
70
+
71
+ assert_equal 1, MariaDB.nextval(:position)
72
+ assert_equal 3, MariaDB.nextval(:position)
73
+ end
74
+
75
+ test "returns current/last sequence value, which doesn't increase by itself" do
76
+ with_migration do
77
+ def up
78
+ create_sequence :position
79
+ end
80
+ end.up
81
+
82
+ MariaDB.nextval(:position)
83
+
84
+ assert_equal 1, MariaDB.currval(:position)
85
+ assert_equal 1, MariaDB.lastval(:position)
86
+ assert_equal 1, MariaDB.currval(:position)
87
+ assert_equal 1, MariaDB.lastval(:position)
88
+ end
89
+
90
+ test 'sets sequence value' do
91
+ with_migration do
92
+ def up
93
+ create_sequence :position
94
+ end
95
+ end.up
96
+
97
+ MariaDB.nextval(:position)
98
+ assert_equal MariaDB.currval(:position), 1
99
+
100
+ MariaDB.setval(:position, 101)
101
+ # in MariaDB, 'lastval' only works after 'nextval' rather than 'setval'
102
+ assert_equal 1, MariaDB.lastval(:position)
103
+
104
+ MariaDB.nextval(:position)
105
+ # now the value is correct
106
+ assert_equal 102, MariaDB.lastval(:position)
107
+ end
108
+
109
+ test 'drops sequence and check_sequences' do
110
+ with_migration do
111
+ def up
112
+ create_sequence :position
113
+ end
114
+ end.up
115
+
116
+ sequence = MariaDB.check_sequences.find_all do |seq|
117
+ seq[:Tables_in_test] == 'position'
118
+ end
119
+
120
+ assert_equal 1, sequence.size
121
+
122
+ with_migration do
123
+ def down
124
+ drop_sequence :position
125
+ end
126
+ end.down
127
+
128
+ sequence = MariaDB.check_sequences.find do |seq|
129
+ seq[:Tables_in_test] == 'position'
130
+ end
131
+
132
+ assert_nil sequence
133
+ end
134
+
135
+ test 'orders sequences' do
136
+ list = MariaDB.check_sequences.map { |s| s[:Tables_in_test] }
137
+ assert !list.include?('a')
138
+ assert !list.include?('b')
139
+ assert !list.include?('c')
140
+
141
+ with_migration do
142
+ def up
143
+ drop_table :things, if_exists: true
144
+ create_sequence :c
145
+ create_sequence :a
146
+ create_sequence :b
147
+ end
148
+ end.up
149
+
150
+ list = MariaDB.check_sequences.map { |s| s[:Tables_in_test] }
151
+ assert list.include?('a')
152
+ assert list.include?('b')
153
+ assert list.include?('c')
154
+ end
155
+
156
+ test 'creates table that references sequence' do
157
+ with_migration do
158
+ def up
159
+ drop_table :builders, if_exists: true
160
+ create_sequence :position_id, if_exists: false, start: 1
161
+ create_table :builders do
162
+ primary_key :id
163
+ String :name, text: true
164
+ Bignum :position, null: false
165
+ end
166
+ set_column_default_nextval :builders, :position, :position_id
167
+ end
168
+ end.up
169
+
170
+ class Builder < Sequel::Model; end
171
+
172
+ builder1 = Builder.create(name: 'Builder 1')
173
+ pos1 = MariaDB.currval(:position_id)
174
+ assert_equal pos1, builder1.reload.position
175
+
176
+ builder2 = Builder.create(name: 'Builder 2')
177
+ pos2 = MariaDB.currval(:position_id)
178
+ assert_equal pos2, builder2.reload.position
179
+
180
+ assert_equal pos2 - pos1, 1
181
+ end
182
+ end