sequel-sequence 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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