sequel-sequence 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10902f0e1d1afb59a3461ea44138203c9ddf3f0455b49ffe811c7b23aadc8ede
4
- data.tar.gz: 6e928d7c8827d2cc67c32d4fe28156490b99b187cbf6cbc19f546c2706e55087
3
+ metadata.gz: c7012e03aa326861bef294edcf042850f662f9122664da409aa0dd764d9375d9
4
+ data.tar.gz: b309b36a931be17ee7cb628c21083ef8543bb60139a249aa9fd1d42378321297
5
5
  SHA512:
6
- metadata.gz: 545bb293f276fd0f92441941fa9efc7dc432fd1b1a5131bb10a7465aba833db72f2ca2e29abc3f6cc427b958df3bc47354abf1d9d420efa77d1d610de85e2166
7
- data.tar.gz: 20273fe64a4e90fd2f8345ea8b08d96819bb4c52ff26684b2e67d49674c0360fc0926a66c565041b6a16f8e49d348133cca80cc629c4c14614a1116eb3e6dc60
6
+ metadata.gz: 7c13a9a6e000990aaee2bdcca4d3d7519a7164c3217d4164f35e40e43deb76f652215169396647568cb03cf111d2a103f2f0f5d5f45e174b8107588e4be1808f
7
+ data.tar.gz: e9483ab69ea26d780164a4e4683d75c2bd86d8c5d92983b506645c5544a3ebc277f25f12a463b1f959f675f0e780eaf3b22a82b9560e247f16f2a895518d0e53
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: "🐛 Bug Report"
3
+ about: Report a reproducible bug or regression.
4
+ title: 'Bug: '
5
+ labels: 'Status: Unconfirmed'
6
+
7
+ ---
8
+
9
+ <!--
10
+ - Please provide a clear and concise description of what the bug is.
11
+ - If possible, add an example reproducing your issue.
12
+ - Please test using the latest version of ar-sequence
13
+ to make sure your issue has not already been fixed.
14
+ -->
15
+
16
+ ## Description
17
+
18
+ [Add bug description here]
19
+
20
+ ## How to reproduce
21
+
22
+ [Add steps on how to reproduce this issue]
23
+
24
+ ## What do you expect
25
+
26
+ [Describe what do you expect to happen]
27
+
28
+ ## What happened instead
29
+
30
+ [Describe the actual results]
31
+
32
+ ## Software:
33
+
34
+ - Gem version: [Add gem version here]
35
+ - Ruby version: [Add version here]
36
+
37
+ ## Full backtrace
38
+
39
+ ```text
40
+ [Paste full backtrace here]
41
+ ```
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: "💡 Feature request"
3
+ about: Have an idea that may be useful? Make a suggestion!
4
+ title: 'Feature Request: '
5
+ labels: 'Feature request'
6
+
7
+ ---
8
+
9
+ ## Description
10
+
11
+ _A clear and concise description of what the problem is._
12
+
13
+ ## Describe the solution
14
+
15
+ _A clear and concise description of what you want to happen._
16
+
17
+ ## Alternatives you considered
18
+
19
+ _A clear and concise description of any alternative solutions or features you've considered._
20
+
21
+ ## Additional context
22
+
23
+ _Add any other context, screenshots, links, etc about the feature request here._
@@ -0,0 +1,38 @@
1
+ <!--
2
+ If you're making a doc PR or something tiny where the below is irrelevant,
3
+ delete this template and use a short description, but in your description aim to
4
+ include both what the change is, and why it is being made, with enough context
5
+ for anyone to understand.
6
+ -->
7
+
8
+ <details>
9
+ <summary>PR Checklist</summary>
10
+
11
+ ### PR Structure
12
+
13
+ - [ ] This PR has reasonably narrow scope (if not, break it down into smaller
14
+ PRs).
15
+ - [ ] This PR avoids mixing refactoring changes with feature changes (split into
16
+ two PRs otherwise).
17
+ - [ ] This PR's title starts is concise and descriptive.
18
+
19
+ ### Thoroughness
20
+
21
+ - [ ] This PR adds tests for the most critical parts of the new functionality or
22
+ fixes.
23
+ - [ ] I've updated any docs, `.md` files, etc… affected by this change.
24
+
25
+ </details>
26
+
27
+ ### What
28
+
29
+ [TODO: Short statement about what is changing.]
30
+
31
+ ### Why
32
+
33
+ [TODO: Why this change is being made. Include any context required to understand
34
+ the why.]
35
+
36
+ ### Known limitations
37
+
38
+ [TODO or N/A]
@@ -0,0 +1,14 @@
1
+ # Documentation:
2
+ # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates
3
+
4
+ version: 2
5
+ updates:
6
+ - package-ecosystem: "github-actions"
7
+ directory: "/"
8
+ schedule:
9
+ interval: "monthly"
10
+
11
+ - package-ecosystem: "bundler"
12
+ directory: "/"
13
+ schedule:
14
+ interval: "monthly"
@@ -0,0 +1,97 @@
1
+ name: CI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs: {}
6
+ push:
7
+ branches: [ master ]
8
+ pull_request:
9
+ branches: ['**']
10
+
11
+ jobs:
12
+ tests:
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ os: ['ubuntu-latest']
17
+ sequel: ['~>5.28']
18
+ ruby: ['3.2', '3.1', '3.0', '2.7']
19
+ gemfile: ['Gemfile']
20
+ runs-on: ${{ matrix.os }}
21
+ name: Tests with Ruby ${{ matrix.ruby }}
22
+
23
+ services:
24
+ postgres:
25
+ image: postgres:13
26
+ env:
27
+ POSTGRES_USER: postgres
28
+ POSTGRES_PASSWORD: postgres
29
+ ports:
30
+ - 5432:5432
31
+ options: >-
32
+ --health-cmd pg_isready
33
+ --health-interval 10s
34
+ --health-timeout 5s
35
+ --health-retries 5
36
+ mysql:
37
+ image: mariadb:11.1
38
+ env:
39
+ MARIADB_ROOT_PASSWORD: root
40
+ ports:
41
+ - 3306:3306
42
+ options: >-
43
+ --health-cmd="healthcheck.sh --connect --innodb_initialized"
44
+ --health-interval 10s
45
+ --health-timeout 5s
46
+ --health-retries 3
47
+
48
+ env:
49
+ SEQUEL: ${{ matrix.sequel }}
50
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
51
+ steps:
52
+ - uses: actions/checkout@v4
53
+
54
+ - name: Install db dependencies and check connections
55
+ run: |
56
+ 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 -proot -e "SHOW GRANTS FOR 'root'@'localhost'"
58
+ env PGPASSWORD=postgres psql -h localhost -p ${{ job.services.postgres.ports[5432] }} -U postgres -l
59
+ sqlite3 --version
60
+
61
+ - name: Set up Ruby
62
+ uses: ruby/setup-ruby@v1
63
+ with:
64
+ ruby-version: ${{ matrix.ruby }}
65
+ bundler-cache: true
66
+
67
+ - name: Create MySQL database
68
+ run: |
69
+ 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.mysql.ports[3306] }} -uroot -proot
70
+
71
+ - name: Create PostgreSQL database
72
+ env:
73
+ PGPASSWORD: postgres
74
+ run: |
75
+ psql -c 'create database "test";' -U postgres -h localhost -p ${{ job.services.postgres.ports[5432] }}
76
+
77
+ - name: Create SQLite database
78
+ run: |
79
+ mkdir ./db && touch ./db/test.sqlite3 && sqlite3 ./db/test.sqlite3
80
+
81
+ - name: Run Tests
82
+ run: bundle exec rake test
83
+ env:
84
+ TEST_POSTGRES_DATABASE: test
85
+ TEST_POSTGRES_HOST: localhost
86
+ TEST_POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }}
87
+ TEST_POSTGRES_USERNAME: postgres
88
+ TEST_POSTGRES_PASSWORD: postgres
89
+ TEST_MYSQL_DATABASE: test
90
+ TEST_MYSQL_HOST: 127.0.0.1
91
+ TEST_MYSQL_PORT: ${{ job.services.mysql.ports[3306] }}
92
+ TEST_MYSQL_USERNAME: root
93
+ TEST_MYSQL_PASSWORD: root
94
+ TEST_SQLITE_DATABASE: "db/test.sqlite3"
95
+
96
+ - name: Run Rubocop
97
+ run: bundle exec rake rubocop
data/.gitignore CHANGED
@@ -1,11 +1,17 @@
1
- .bundle
2
-
3
1
  ## MAC OS
4
2
  .DS_Store
5
3
 
6
4
  ## PROJECT::GENERAL
7
5
  coverage
6
+ rdoc
8
7
  doc
9
8
  pkg
10
9
  tmp
10
+ log
11
+ db
12
+
13
+ ## PROJECT::SPECIFIC
14
+ .bundle
15
+ .byebug_history
11
16
  Gemfile.lock
17
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,37 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.7
3
+ NewCops: enable
4
+ SuggestExtensions:
5
+ rubocop-rake: false
6
+
7
+ Naming/FileName:
8
+ Enabled: false
9
+
10
+ Metrics/MethodLength:
11
+ Enabled: false
12
+
13
+ Metrics/ClassLength:
14
+ Exclude:
15
+ - test/**/*
16
+
17
+ Metrics/AbcSize:
18
+ Enabled: false
19
+
20
+ Metrics/CyclomaticComplexity:
21
+ Enabled: false
22
+
23
+ Metrics/PerceivedComplexity:
24
+ Enabled: false
25
+
26
+ Gemspec/RequireMFA:
27
+ Enabled: false
28
+
29
+ Gemspec/DevelopmentDependencies:
30
+ EnforcedStyle: gemspec
31
+
32
+ Style/Documentation:
33
+ Enabled: false
34
+
35
+ Lint/ConstantDefinitionInBlock:
36
+ Exclude:
37
+ - test/**/*
data/CHANGELOG.md CHANGED
@@ -11,6 +11,20 @@ Prefix your message with one of the following:
11
11
  - [Security] in case of vulnerabilities.
12
12
  -->
13
13
 
14
+ ## v0.3.0 - 2023-09-21
15
+
16
+ - [Added] A parametrized 'IF EXISTS' condition into the drop_sequence.
17
+ - [Added] A parametrized 'IF NOT EXISTS' condition into the create_sequence.
18
+ - [Added] Gem API support for SQLite databases.
19
+ - [Fixed] Tests for the Mysql database.
20
+
21
+ ## v0.2.0 - 2023-09-14
22
+
23
+ - [Added] CI features based on GitHub Actions.
24
+ - [Fixed] README.md
25
+ - [Changed] Unit tests.
26
+ - [Fixed] Sequel::Sequence::Database exceptions.
27
+
14
28
  ## v0.1.0 - 2023-09-10
15
29
 
16
30
  - Initial release.
data/CONTRIBUTING.md CHANGED
@@ -9,7 +9,7 @@ changes to this document in a pull request.
9
9
  ## Code of Conduct
10
10
 
11
11
  Everyone interacting in this project's codebases, issue trackers, chat rooms and
12
- mailing lists is expected to follow the [code of conduct](https://github.com/fnando/sequel-sequence/blob/main/CODE_OF_CONDUCT.md).
12
+ mailing lists is expected to follow the [code of conduct](https://github.com/oreol-group/sequel-sequence/blob/master/CODE_OF_CONDUCT.md).
13
13
 
14
14
  ## Reporting bugs
15
15
 
@@ -26,7 +26,7 @@ the behavior, and find related reports.
26
26
  ## Contributing with code
27
27
 
28
28
  Before making any radicals changes, please make sure you discuss your intention
29
- by [opening an issue on Github](https://github.com/fnando/sequel-sequence/issues).
29
+ by [opening an issue on Github](https://github.com/oreol-group/sequel-sequence/issues).
30
30
 
31
31
  When you're ready to make your pull request, follow checklist below to make sure
32
32
  your contribution is according to how this project works.
@@ -81,11 +81,22 @@ test=# \dt
81
81
  --------+---------+-------+-----------
82
82
  public | masters | table | USER_NAME
83
83
  public | things | table | USER_NAME
84
- (2 rows)
85
84
  ```
86
- - If it doesn't exists, create one with a couple of tables:
85
+ and role `postgres`
86
+ ```bash
87
+ psql -d test -c 'SELECT rolname FROM pg_roles;'
88
+ rolname
89
+ ---------------------------
90
+ postgres
91
+ ```
92
+ - If none of them exist, create role
93
+ ```bash
94
+ psql -d postgres -c "create role postgres superuser createdb login password 'postgres';"
95
+ ```
96
+ and database with a couple of tables:
97
+
87
98
  ```bash
88
- sudo psql -U USER_NAME -d postgres
99
+ sudo psql -U postgres -d postgres
89
100
  postgres=# CREATE DATABASE test;
90
101
  postgres=# \c test
91
102
  test=# CREATE TABLE IF NOT EXISTS things ();
@@ -104,7 +115,6 @@ MariaDB [test]> SHOW TABLES;
104
115
  | builders |
105
116
  | wares |
106
117
  +----------------------+
107
- 4 rows in set (0.001 sec)
108
118
  ```
109
119
  - If it doesn't exists, create one with a couple of tables:
110
120
  ```bash
@@ -113,8 +123,19 @@ MariaDB [(none)]> USE test;
113
123
  MariaDB [test]> CREATE TABLE IF NOT EXISTS wares(id int auto_increment, primary key(id));
114
124
  MariaDB [test]> CREATE TABLE IF NOT EXISTS builders(id int auto_increment, primary key(id));
115
125
  ```
126
+ - Add a test SQLite database:
127
+ ```bash
128
+ mkdir db && touch db/test.sqlite3
129
+ ```
130
+ - Add a couple of tables in the SQLite database:
131
+ ```bash
132
+ sqlite3 db/test.sqlite3
133
+ sqlite> create table objects(id integer primary key autoincrement);
134
+ sqlite> create table apprentices(id integer primary key autoincrement);
135
+ ```
116
136
  - Run the tests separately:
117
137
  ```bash
118
- bundle exec rake TEST=test/ar/postgresql_sequence_test.rb
119
- bundle exec rake TEST=test/ar/mysql_sequence_test.rb
138
+ bundle exec rake TEST=test/sequel/postgresql_sequence_test.rb
139
+ bundle exec rake TEST=test/sequel/mysql_sequence_test.rb
140
+ bundle exec rake TEST=test/sequel/sqlite_sequence_test.rb
120
141
  ```
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # sequel-sequence
2
2
 
3
- [![Tests](https://github.com/oreol-group/sequel-sequence/workflows/Tests/badge.svg)](https://github.com/oreol-group/sequel-sequence)
3
+ [![CI](https://github.com/oreol-group/sequel-sequence/actions/workflows/ci.yml/badge.svg)](https://github.com/oreol-group/sequel-sequence)
4
4
  [![Gem](https://img.shields.io/gem/v/sequel-sequence.svg)](https://rubygems.org/gems/sequel-sequence)
5
- [![Downloads total](https://img.shields.io/gem/dt/sequel-sequence.svg)](https://rubygems.org/gems/sequel-sequence)
5
+ [![Downloads total](https://img.shields.io/gem/dt/sequel-sequence.svg)](https://rubygems.org/profiles/it_architect)
6
+ [![Code Climate](https://codeclimate.com/github/Oreol-Group/sequel-sequence.svg)](https://codeclimate.com/github/Oreol-Group/sequel-sequence)
6
7
 
7
- Adds a useful interface and support for PostgreSQL and MySQL `SEQUENCE` on Sequel migrations
8
+ Adds a useful interface and support for PostgreSQL and MySQL `SEQUENCE` on Sequel migrations. Gem includes functionality to cover the needs of SQLite users as well.
8
9
 
9
10
  ## Installation
10
11
 
@@ -18,14 +19,14 @@ Or add the following line to your project's Gemfile:
18
19
  gem "sequel-sequence"
19
20
  ```
20
21
 
21
- ## Usage
22
+ ## Usage with PostgreSQL and MariaDB
22
23
 
23
24
  To create a `SEQUENCE`, just use the method `create_sequence`.
24
25
 
25
26
  ```ruby
26
27
  Sequel.migration do
27
28
  up do
28
- create_sequence :position
29
+ create_sequence :position, if_exists: false
29
30
  end
30
31
 
31
32
  down do
@@ -39,22 +40,22 @@ You can also specify the initial value and increment:
39
40
  ```ruby
40
41
  create_sequence :position, increment: 2
41
42
  create_sequence :position, start: 100
43
+ create_sequence :position, if_exists: false
42
44
  ```
43
45
 
44
- To define a column that has a sequence as its default value, use something like
45
- the following:
46
+ To define a column that has a sequence as its default value, use something like the following:
46
47
 
47
48
  ```ruby
48
49
  Sequel.migration do
49
50
  change do
50
- create_sequence :position_id
51
+ create_sequence :position_id, if_exists: false, start: 1
51
52
 
52
53
  create_table(:things) do
53
54
  primary_key :id
54
55
  String :name, text: true
55
56
 
56
57
  # PostgreSQL uses bigint as the sequence's default type.
57
- Bignum :position, null: false
58
+ Bignum :position
58
59
 
59
60
  Time :created_at, null: false
60
61
  Time :updated_at, null: false
@@ -82,6 +83,24 @@ DB.lastval("position")
82
83
  DB.setval("position", 1234)
83
84
  ```
84
85
 
86
+ ## Usage with SQLite
87
+
88
+ In SQLite, the sequence functionality is implemented by registering tables in the database with a primary key of `id` and an additional integer field `fiction`.
89
+ ```sql
90
+ CREATE TABLE `name_of_your_sequence_table`
91
+ (id integer primary key autoincrement, fiction integer);
92
+ ```
93
+
94
+ You might utilize the last one 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.
95
+
96
+ ```ruby
97
+ DB.nextval_with_label("position", 1)
98
+ ```
99
+
100
+ By default, `fiction` has a zero value.
101
+
102
+ Otherwise, the operation of this gem for SQLite is identical to the ways of using Sequence in more advanced databases.
103
+
85
104
  ## Maintainer
86
105
 
87
106
  - [Nikolai Bocharov](https://github.com/oreol-group)
@@ -93,16 +112,16 @@ DB.setval("position", 1234)
93
112
  ## Contributing
94
113
 
95
114
  For more details about how to contribute, please read
96
- https://github.com/oreol-group/sequel-sequence/blob/main/CONTRIBUTING.md.
115
+ https://github.com/oreol-group/sequel-sequence/blob/master/CONTRIBUTING.md.
97
116
 
98
117
  ## License
99
118
 
100
119
  The gem is available as open source under the terms of the
101
120
  [MIT License](https://opensource.org/licenses/MIT). A copy of the license can be
102
- found at https://github.com/oreol-group/sequel-sequence/blob/main/LICENSE.md.
121
+ found at https://github.com/oreol-group/sequel-sequence/blob/master/LICENSE.md.
103
122
 
104
123
  ## Code of Conduct
105
124
 
106
125
  Everyone interacting in the sequel-sequence project's codebases, issue trackers,
107
126
  chat rooms and mailing lists is expected to follow the
108
- [code of conduct](https://github.com/oreol-group/sequel-sequence/blob/main/CODE_OF_CONDUCT.md).
127
+ [code of conduct](https://github.com/oreol-group/sequel-sequence/blob/master/CODE_OF_CONDUCT.md).
@@ -20,9 +20,10 @@ module Sequel
20
20
 
21
21
  def create_sequence(name, options = {})
22
22
  increment = options[:increment] || options[:step]
23
+ if_exists = build_exists_condition(options[:if_exists])
23
24
  name = quote_name(name.to_s)
24
25
 
25
- sql = ["CREATE SEQUENCE IF NOT EXISTS #{name}"]
26
+ sql = ["CREATE SEQUENCE #{if_exists} #{name}"]
26
27
  sql << "INCREMENT BY #{increment}" if increment
27
28
  sql << "START WITH #{options[:start]}" if options[:start]
28
29
  sql << ';'
@@ -32,7 +33,7 @@ module Sequel
32
33
 
33
34
  def drop_sequence(name)
34
35
  name = quote_name(name.to_s)
35
- sql = "DROP SEQUENCE #{name}"
36
+ sql = "DROP SEQUENCE IF EXISTS #{name}"
36
37
  run(sql)
37
38
  end
38
39
 
@@ -35,9 +35,10 @@ module Sequel
35
35
 
36
36
  def create_sequence(name, options = {})
37
37
  increment = options[:increment] || options[:step]
38
+ if_exists = build_exists_condition(options[:if_exists])
38
39
  name = quote_name(name.to_s)
39
40
 
40
- sql = ["CREATE SEQUENCE IF NOT EXISTS #{name}"]
41
+ sql = ["CREATE SEQUENCE #{if_exists} #{name}"]
41
42
  sql << "INCREMENT BY #{increment}" if increment
42
43
  sql << "START WITH #{options[:start]}" if options[:start]
43
44
  sql << ';'
@@ -48,7 +49,7 @@ module Sequel
48
49
 
49
50
  def drop_sequence(name)
50
51
  name = quote_name(name.to_s)
51
- sql = "DROP SEQUENCE #{name}"
52
+ sql = "DROP SEQUENCE IF EXISTS #{name}"
52
53
  run(sql)
53
54
  end
54
55
 
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ # https://sequel.jeremyevans.net/rdoc/files/doc/sql_rdoc.html
4
+ # https://github.com/jeremyevans/sequel/blob/master/lib/sequel/database/connecting.rb
5
+ module Sequel
6
+ module Sequence
7
+ module Database
8
+ module SQLite
9
+ def check_sequences
10
+ fetch('SELECT * FROM `sqlite_sequence`;').all.to_a
11
+ end
12
+
13
+ def create_sequence(name, options = {})
14
+ check_options(options)
15
+ if_exists = build_exists_condition(options[:if_exists])
16
+ start_option = options[:start] || 1
17
+ sql = [create_sequence_table(stringify(name), if_exists)]
18
+ sql << insert_into_sqlite_sequence(stringify(name), start_option)
19
+ run(sql.join("\n"))
20
+ end
21
+
22
+ def drop_sequence(name, options = {})
23
+ if_exists = build_exists_condition(options[:if_exists])
24
+ run(drop_sequence_table(stringify(name), if_exists))
25
+ end
26
+
27
+ def nextval(name)
28
+ run(insert_into_sequence_table(stringify(name), 0))
29
+ take_seq(name)
30
+ end
31
+
32
+ def nextval_with_label(name, num_label = 0)
33
+ run(insert_into_sequence_table(stringify(name), num_label))
34
+ take_seq(name)
35
+ end
36
+
37
+ def lastval(name)
38
+ take_seq(stringify(name))
39
+ end
40
+
41
+ alias currval lastval
42
+
43
+ def setval(name, value)
44
+ current = lastval(stringify(name))
45
+ if current.nil?
46
+ create_sequence(stringify(name), { start: value })
47
+ 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
+ log_info DANGER_OPT_ID
53
+ value = current
54
+ else
55
+ run(insert_into_sqlite_sequence(stringify(name), value))
56
+ end
57
+ value
58
+ end
59
+
60
+ def set_column_default_nextval(table, column, sequence)
61
+ run(create_sequenced_column(stringify(table),
62
+ stringify(column),
63
+ stringify(sequence)))
64
+ end
65
+
66
+ private
67
+
68
+ def stringify(name)
69
+ @name ||= {}
70
+ @name.fetch(name, nil) || (@name[name] = name.to_s)
71
+ end
72
+
73
+ def take_seq(name)
74
+ out = nil
75
+ fetch(select_max_seq(name)) do |row|
76
+ out = row[:id]
77
+ end
78
+ out
79
+ end
80
+
81
+ def create_sequence_table(name, if_exists = nil)
82
+ %(
83
+ CREATE TABLE #{if_exists || IF_NOT_EXISTS} #{name}
84
+ (id integer primary key autoincrement, fiction integer);
85
+ )
86
+ end
87
+
88
+ def insert_into_sequence_table(name, num_label)
89
+ "INSERT INTO #{name} (fiction) VALUES (#{num_label});"
90
+ end
91
+
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
103
+ end
104
+
105
+ def delete_from_sqlite_sequence(name)
106
+ "DELETE FROM sqlite_sequence WHERE name = '#{name}';"
107
+ end
108
+
109
+ def drop_sequence_table(name, if_exists = nil)
110
+ "DROP TABLE #{if_exists || IF_EXISTS} #{name};"
111
+ end
112
+
113
+ def select_max_seq(name)
114
+ "SELECT MAX(seq) AS id FROM sqlite_sequence WHERE name = '#{name}';"
115
+ end
116
+
117
+ def create_sequenced_column(table, column, sequence)
118
+ %(
119
+ CREATE TRIGGER IF NOT EXISTS #{table}_#{sequence} AFTER INSERT
120
+ ON #{table}
121
+ BEGIN
122
+ INSERT INTO #{sequence}(fiction) VALUES (0);
123
+ UPDATE #{table}
124
+ SET #{column} = (SELECT MAX(seq) FROM sqlite_sequence WHERE name = '#{sequence}')
125
+ WHERE rowid = NEW.rowid;
126
+ END;
127
+ )
128
+ end
129
+ end
130
+ end
131
+ end
132
+ end