tablature 0.0.1 → 0.1.0
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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +81 -0
- data/.rubocop.yml +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +24 -17
- data/README.md +53 -3
- data/Rakefile +5 -0
- data/bin/console +4 -8
- data/lib/tablature/adapters/postgres/connection.rb +56 -0
- data/lib/tablature/adapters/postgres/errors.rb +41 -0
- data/lib/tablature/adapters/postgres/handlers/base.rb +43 -0
- data/lib/tablature/adapters/postgres/handlers/list.rb +66 -0
- data/lib/tablature/adapters/postgres/handlers/range.rb +68 -0
- data/lib/tablature/adapters/postgres/partitioned_tables.rb +60 -0
- data/lib/tablature/adapters/postgres/quoting.rb +16 -0
- data/lib/tablature/adapters/postgres/uuid.rb +12 -0
- data/lib/tablature/adapters/postgres.rb +113 -2
- data/lib/tablature/command_recorder.rb +4 -0
- data/lib/tablature/configuration.rb +4 -5
- data/lib/tablature/model.rb +4 -0
- data/lib/tablature/partitioned_table.rb +33 -0
- data/lib/tablature/schema_dumper.rb +22 -0
- data/lib/tablature/statements.rb +57 -0
- data/lib/tablature/version.rb +1 -1
- data/lib/tablature.rb +17 -2
- data/tablature.gemspec +3 -2
- metadata +44 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 78e1704fd9d861cc494afaea95d8b823cd9067ff7100215fee67a80fd3a6ed8c
|
|
4
|
+
data.tar.gz: d4cbb87e57da6eca06be82239cd9f2ed57e0ddfedc64313f7d065b72b63d12e4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ccaf49c24f56fd9dedc98c852fb7c0e79b90b80cb56822257c8ae3068d23afc5b0e116b3f3f09491c2f0f0a17d2d474ace5038e3907747b3a8fce193eb7c36b7
|
|
7
|
+
data.tar.gz: af0ac14e53c058c9c69700e9f8ce8e8195935ec4fa2719212243fec0ed6a93827f10807dfccc65c83dff0145241b389dc3db3a5b28a5938242bf2bd377e58345
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# TODO: Simplify this
|
|
2
|
+
version: 2
|
|
3
|
+
jobs:
|
|
4
|
+
postgres_10:
|
|
5
|
+
working_directory: ~/tablature-rb
|
|
6
|
+
docker:
|
|
7
|
+
- image: circleci/ruby:2.4
|
|
8
|
+
environment:
|
|
9
|
+
DATABASE_URL: "postgres://postgres@localhost:5432/dummy_test"
|
|
10
|
+
- image: circleci/postgres:10-alpine
|
|
11
|
+
environment:
|
|
12
|
+
POSTGRES_USER: postgres
|
|
13
|
+
POSTGRES_DB: dummy_test
|
|
14
|
+
POSTGRES_PASSWORD: ""
|
|
15
|
+
steps:
|
|
16
|
+
- checkout
|
|
17
|
+
|
|
18
|
+
- type: cache-restore
|
|
19
|
+
name: Restore bundle cache
|
|
20
|
+
key: tablature-{{ checksum "tablature.gemspec" }}-{{ checksum "Gemfile" }}
|
|
21
|
+
|
|
22
|
+
- run:
|
|
23
|
+
name: Install dependencies
|
|
24
|
+
command: bundle install --path vendor/bundle
|
|
25
|
+
|
|
26
|
+
- type: cache-save
|
|
27
|
+
name: Store bundle cache
|
|
28
|
+
key: tablature-{{ checksum "tablature.gemspec" }}-{{ checksum "Gemfile" }}
|
|
29
|
+
paths:
|
|
30
|
+
- vendor/bundle
|
|
31
|
+
|
|
32
|
+
- run:
|
|
33
|
+
name: Wait for Postgres
|
|
34
|
+
command: dockerize -wait tcp://localhost:5432 -timeout 1m
|
|
35
|
+
|
|
36
|
+
- run:
|
|
37
|
+
name: Run tests
|
|
38
|
+
command: bundle exec rspec --tag ~postgres_11
|
|
39
|
+
|
|
40
|
+
postgres_11:
|
|
41
|
+
working_directory: ~/tablature-rb
|
|
42
|
+
docker:
|
|
43
|
+
- image: circleci/ruby:2.4
|
|
44
|
+
environment:
|
|
45
|
+
DATABASE_URL: "postgres://postgres@localhost:5432/dummy_test"
|
|
46
|
+
- image: circleci/postgres:11-alpine
|
|
47
|
+
environment:
|
|
48
|
+
POSTGRES_USER: postgres
|
|
49
|
+
POSTGRES_DB: dummy_test
|
|
50
|
+
POSTGRES_PASSWORD: ""
|
|
51
|
+
steps:
|
|
52
|
+
- checkout
|
|
53
|
+
|
|
54
|
+
- type: cache-restore
|
|
55
|
+
name: Restore bundle cache
|
|
56
|
+
key: tablature-{{ checksum "tablature.gemspec" }}-{{ checksum "Gemfile" }}
|
|
57
|
+
|
|
58
|
+
- run:
|
|
59
|
+
name: Install dependencies
|
|
60
|
+
command: bundle install --path vendor/bundle
|
|
61
|
+
|
|
62
|
+
- type: cache-save
|
|
63
|
+
name: Store bundle cache
|
|
64
|
+
key: tablature-{{ checksum "tablature.gemspec" }}-{{ checksum "Gemfile" }}
|
|
65
|
+
paths:
|
|
66
|
+
- vendor/bundle
|
|
67
|
+
|
|
68
|
+
- run:
|
|
69
|
+
name: Wait for Postgres
|
|
70
|
+
command: dockerize -wait tcp://localhost:5432 -timeout 1m
|
|
71
|
+
|
|
72
|
+
- run:
|
|
73
|
+
name: Run tests
|
|
74
|
+
command: bundle exec rspec
|
|
75
|
+
|
|
76
|
+
workflows:
|
|
77
|
+
version: 2
|
|
78
|
+
tablature:
|
|
79
|
+
jobs:
|
|
80
|
+
- postgres_10
|
|
81
|
+
- postgres_11
|
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,46 +1,48 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
tablature (0.0
|
|
4
|
+
tablature (0.1.0)
|
|
5
5
|
activerecord (>= 5.0.0)
|
|
6
6
|
railties (>= 5.0.0)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
9
9
|
remote: https://rubygems.org/
|
|
10
10
|
specs:
|
|
11
|
-
actionpack (5.2.
|
|
12
|
-
actionview (= 5.2.
|
|
13
|
-
activesupport (= 5.2.
|
|
11
|
+
actionpack (5.2.2)
|
|
12
|
+
actionview (= 5.2.2)
|
|
13
|
+
activesupport (= 5.2.2)
|
|
14
14
|
rack (~> 2.0)
|
|
15
15
|
rack-test (>= 0.6.3)
|
|
16
16
|
rails-dom-testing (~> 2.0)
|
|
17
17
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
18
|
-
actionview (5.2.
|
|
19
|
-
activesupport (= 5.2.
|
|
18
|
+
actionview (5.2.2)
|
|
19
|
+
activesupport (= 5.2.2)
|
|
20
20
|
builder (~> 3.1)
|
|
21
21
|
erubi (~> 1.4)
|
|
22
22
|
rails-dom-testing (~> 2.0)
|
|
23
23
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
24
|
-
activemodel (5.2.
|
|
25
|
-
activesupport (= 5.2.
|
|
26
|
-
activerecord (5.2.
|
|
27
|
-
activemodel (= 5.2.
|
|
28
|
-
activesupport (= 5.2.
|
|
24
|
+
activemodel (5.2.2)
|
|
25
|
+
activesupport (= 5.2.2)
|
|
26
|
+
activerecord (5.2.2)
|
|
27
|
+
activemodel (= 5.2.2)
|
|
28
|
+
activesupport (= 5.2.2)
|
|
29
29
|
arel (>= 9.0)
|
|
30
|
-
activesupport (5.2.
|
|
30
|
+
activesupport (5.2.2)
|
|
31
31
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
32
32
|
i18n (>= 0.7, < 2)
|
|
33
33
|
minitest (~> 5.1)
|
|
34
34
|
tzinfo (~> 1.1)
|
|
35
35
|
arel (9.0.0)
|
|
36
36
|
builder (3.2.3)
|
|
37
|
+
coderay (1.1.2)
|
|
37
38
|
concurrent-ruby (1.1.3)
|
|
38
39
|
crass (1.0.4)
|
|
40
|
+
database_cleaner (1.7.0)
|
|
39
41
|
diff-lcs (1.3)
|
|
40
42
|
erubi (1.7.1)
|
|
41
43
|
i18n (1.1.1)
|
|
42
44
|
concurrent-ruby (~> 1.0)
|
|
43
|
-
loofah (2.2.
|
|
45
|
+
loofah (2.2.3)
|
|
44
46
|
crass (~> 1.0.2)
|
|
45
47
|
nokogiri (>= 1.5.9)
|
|
46
48
|
method_source (0.9.0)
|
|
@@ -49,6 +51,9 @@ GEM
|
|
|
49
51
|
nokogiri (1.8.5)
|
|
50
52
|
mini_portile2 (~> 2.3.0)
|
|
51
53
|
pg (0.21.0)
|
|
54
|
+
pry (0.12.2)
|
|
55
|
+
coderay (~> 1.1.0)
|
|
56
|
+
method_source (~> 0.9.0)
|
|
52
57
|
rack (2.0.6)
|
|
53
58
|
rack-test (1.1.0)
|
|
54
59
|
rack (>= 1.0, < 3)
|
|
@@ -57,9 +62,9 @@ GEM
|
|
|
57
62
|
nokogiri (>= 1.6)
|
|
58
63
|
rails-html-sanitizer (1.0.4)
|
|
59
64
|
loofah (~> 2.2, >= 2.2.2)
|
|
60
|
-
railties (5.2.
|
|
61
|
-
actionpack (= 5.2.
|
|
62
|
-
activesupport (= 5.2.
|
|
65
|
+
railties (5.2.2)
|
|
66
|
+
actionpack (= 5.2.2)
|
|
67
|
+
activesupport (= 5.2.2)
|
|
63
68
|
method_source
|
|
64
69
|
rake (>= 0.8.7)
|
|
65
70
|
thor (>= 0.19.0, < 2.0)
|
|
@@ -79,7 +84,7 @@ GEM
|
|
|
79
84
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
80
85
|
rspec-support (~> 3.7.0)
|
|
81
86
|
rspec-support (3.7.1)
|
|
82
|
-
thor (0.20.
|
|
87
|
+
thor (0.20.3)
|
|
83
88
|
thread_safe (0.3.6)
|
|
84
89
|
tzinfo (1.2.5)
|
|
85
90
|
thread_safe (~> 0.1)
|
|
@@ -89,7 +94,9 @@ PLATFORMS
|
|
|
89
94
|
|
|
90
95
|
DEPENDENCIES
|
|
91
96
|
bundler (~> 1.16)
|
|
97
|
+
database_cleaner
|
|
92
98
|
pg (~> 0.19)
|
|
99
|
+
pry
|
|
93
100
|
rake (~> 10.0)
|
|
94
101
|
rspec (~> 3.0)
|
|
95
102
|
rspec-instafail
|
data/README.md
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
# Tablature
|
|
2
2
|
|
|
3
|
+
Tablature is a library built on top of ActiveRecord to simplify management of partitioned tables in Rails applications.
|
|
4
|
+
It ships with Postgres support and can easily supports other databases through adapters.
|
|
5
|
+
|
|
3
6
|
## Installation
|
|
4
7
|
|
|
8
|
+
##### Requirements
|
|
9
|
+
|
|
10
|
+
Tablature requires Rails 5+ and Postgres 10+.
|
|
11
|
+
|
|
12
|
+
##### Installation
|
|
13
|
+
|
|
5
14
|
Add this line to your application's Gemfile:
|
|
6
15
|
|
|
7
16
|
```ruby
|
|
8
|
-
gem 'tablature'
|
|
17
|
+
gem 'tablature'
|
|
9
18
|
```
|
|
10
19
|
|
|
11
20
|
And then execute:
|
|
@@ -18,15 +27,56 @@ Or install it yourself as:
|
|
|
18
27
|
|
|
19
28
|
## Usage
|
|
20
29
|
|
|
30
|
+
### Partitioning a table
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
class CreateEvents < ActiveRecord::Migration[5.0]
|
|
34
|
+
def up
|
|
35
|
+
# Create the events table as a partitioned table using range as partitioning method
|
|
36
|
+
# and `event_date` as partition key.
|
|
37
|
+
create_range_partition :events_by_range, partition_key: 'event_date' do |t|
|
|
38
|
+
t.string :event_type, null: false
|
|
39
|
+
t.integer :value, null: false
|
|
40
|
+
t.date :event_date, null: false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Create partitions with the bounds of the partition.
|
|
44
|
+
create_range_partition_of :events_by_range,
|
|
45
|
+
name: 'events_range_y2018m12', range_start: '2018-12-01', range_end: '2019-01-01'
|
|
46
|
+
|
|
47
|
+
# Create the events table as a partitioned table using list as partitioning method
|
|
48
|
+
# and `event_date` as partition key.
|
|
49
|
+
create_list_partition :events_by_list, partition_key: 'event_date' do |t|
|
|
50
|
+
t.string :event_type, null: false
|
|
51
|
+
t.integer :value, null: false
|
|
52
|
+
t.date :event_date, null: false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Create partitions with the bounds of the partition.
|
|
56
|
+
create_list_partition_of :events_by_list,
|
|
57
|
+
name: 'events_list_y2018m12', values: (Date.parse('2018-12-01')..Date.parse('2018-12-31')).to_a
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def down
|
|
61
|
+
drop_table :events
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
```
|
|
65
|
+
|
|
21
66
|
## Development
|
|
22
67
|
|
|
23
|
-
After checking out the repo, run `bin/setup` to install dependencies.
|
|
68
|
+
After checking out the repo, run `bin/setup` to install dependencies.
|
|
69
|
+
Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
24
70
|
|
|
25
71
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
26
72
|
|
|
73
|
+
## Acknowledgements
|
|
74
|
+
Tablature's structure is heavily inspired by [Scenic](https://github.com/scenic-views/scenic) and [F(x)](http://github.com/teoljungberg/fx).
|
|
75
|
+
Tablature's features are heavily inspired by [PgParty](https://github.com/rkrage/pg_party).
|
|
76
|
+
|
|
27
77
|
## Contributing
|
|
28
78
|
|
|
29
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
79
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/aliou/tablature.
|
|
30
80
|
|
|
31
81
|
## License
|
|
32
82
|
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
|
@@ -3,12 +3,8 @@
|
|
|
3
3
|
require 'bundler/setup'
|
|
4
4
|
require 'tablature'
|
|
5
5
|
|
|
6
|
-
#
|
|
7
|
-
|
|
6
|
+
# Require the dummy test app in the console.
|
|
7
|
+
require File.expand_path('spec/dummy/config/environment')
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# Pry.start
|
|
12
|
-
|
|
13
|
-
require 'irb'
|
|
14
|
-
IRB.start(__FILE__)
|
|
9
|
+
require 'pry'
|
|
10
|
+
Pry.start
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Tablature
|
|
2
|
+
module Adapters
|
|
3
|
+
class Postgres
|
|
4
|
+
# Decorates an ActiveRecord connection with methods that help determine
|
|
5
|
+
# the connections capabilities.
|
|
6
|
+
#
|
|
7
|
+
# Every attempt is made to use the versions of these methods defined by
|
|
8
|
+
# Rails where they are available and public before falling back to our own
|
|
9
|
+
# implementations for older Rails versions.
|
|
10
|
+
#
|
|
11
|
+
# @api private
|
|
12
|
+
class Connection < SimpleDelegator
|
|
13
|
+
# True if the connection supports range partitions.
|
|
14
|
+
#
|
|
15
|
+
# @return [Boolean]
|
|
16
|
+
def supports_range_partitions?
|
|
17
|
+
postgresql_version >= 100_000
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# True if the connection supports list partitions.
|
|
21
|
+
#
|
|
22
|
+
# @return [Boolean]
|
|
23
|
+
def supports_list_partitions?
|
|
24
|
+
postgresql_version >= 100_000
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# True if the connection supports hash partitions.
|
|
28
|
+
#
|
|
29
|
+
# @return [Boolean]
|
|
30
|
+
def supports_hash_partitions?
|
|
31
|
+
postgresql_version >= 110_000
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# An integer representing the version of Postgres we're connected to.
|
|
35
|
+
#
|
|
36
|
+
# +postgresql_version+ is public in Rails 5, but protected in earlier
|
|
37
|
+
# versions.
|
|
38
|
+
#
|
|
39
|
+
# @return [Integer]
|
|
40
|
+
def postgresql_version
|
|
41
|
+
if undecorated_connection.respond_to?(:postgresql_version)
|
|
42
|
+
super
|
|
43
|
+
else
|
|
44
|
+
undecorated_connection.send(:postgresql_version)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def undecorated_connection
|
|
51
|
+
__getobj__
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Tablature
|
|
2
|
+
module Adapters
|
|
3
|
+
class Postgres
|
|
4
|
+
# Raised when a list partition operation is attempted on a database
|
|
5
|
+
# version that does not support list partitions.
|
|
6
|
+
#
|
|
7
|
+
# List partitions are supported on Postgres 10 or newer.
|
|
8
|
+
class ListPartitionsNotSupportedError < StandardError
|
|
9
|
+
def initialize
|
|
10
|
+
super('List partitions require Postgres 10 or newer')
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Raised when trying to create a list partition without specifying the values of the partition
|
|
15
|
+
# key.
|
|
16
|
+
class MissingListPartitionValuesError < StandardError
|
|
17
|
+
def initialize
|
|
18
|
+
super('Missing values for of list partition')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Raised when a range partition operation is attempted on a database
|
|
23
|
+
# version that does not support range partitions.
|
|
24
|
+
#
|
|
25
|
+
# Range partitions are supported on Postgres 10 or newer.
|
|
26
|
+
class RangePartitionsNotSupportedError < StandardError
|
|
27
|
+
def initialize
|
|
28
|
+
super('Range partitions require Postgres 10 or newer')
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Raised when trying to create a range partition without specifying the range of the partition
|
|
33
|
+
# key.
|
|
34
|
+
class MissingRangePartitionBoundsError < StandardError
|
|
35
|
+
def initialize
|
|
36
|
+
super('Missing bounds for of range partition')
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'tablature/adapters/postgres/quoting'
|
|
2
|
+
require 'tablature/adapters/postgres/uuid'
|
|
3
|
+
|
|
4
|
+
module Tablature
|
|
5
|
+
module Adapters
|
|
6
|
+
class Postgres
|
|
7
|
+
# @api private
|
|
8
|
+
module Handlers
|
|
9
|
+
# @api private
|
|
10
|
+
class Base
|
|
11
|
+
include Postgres::Quoting
|
|
12
|
+
include Postgres::UUID
|
|
13
|
+
|
|
14
|
+
protected
|
|
15
|
+
|
|
16
|
+
def create_partition(table_name, id_options, table_options, &block)
|
|
17
|
+
create_table(table_name, table_options) do |td|
|
|
18
|
+
# TODO: Handle the id things here (depending on the postgres version)
|
|
19
|
+
if id_options[:type] == :uuid
|
|
20
|
+
td.column(
|
|
21
|
+
id_options[:column_name], id_options[:type], null: false, default: uuid_function
|
|
22
|
+
)
|
|
23
|
+
elsif id_options[:type]
|
|
24
|
+
td.column(id_options[:column_name], id_options[:type], null: false)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
yield(td) if block.present?
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def extract_primary_key!(options)
|
|
32
|
+
type = options.fetch(:id, :bigserial)
|
|
33
|
+
column_name = options.fetch(:primary_key, :id)
|
|
34
|
+
|
|
35
|
+
raise ArgumentError, 'composite primary key not supported' if column_name.is_a?(Array)
|
|
36
|
+
|
|
37
|
+
{ type: type, column_name: column_name }
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require_relative 'base'
|
|
2
|
+
|
|
3
|
+
module Tablature
|
|
4
|
+
module Adapters
|
|
5
|
+
class Postgres
|
|
6
|
+
module Handlers
|
|
7
|
+
# @api private
|
|
8
|
+
class List < Base
|
|
9
|
+
def initialize(connection)
|
|
10
|
+
@connection = connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create_list_partition(table_name, options, &block)
|
|
14
|
+
raise_unless_list_partition_supported
|
|
15
|
+
|
|
16
|
+
# Postgres 10 does not handle indexes and therefore primary keys.
|
|
17
|
+
# Therefore we manually create an `id` column.
|
|
18
|
+
# TODO: Either make the library Postgres 11 only, or two code paths between Postgres 10
|
|
19
|
+
# and Postgres 11.
|
|
20
|
+
modified_options = options.except(:id, :primary_key, :partition_key)
|
|
21
|
+
id_options = extract_primary_key!(options.slice(:id, :primary_key))
|
|
22
|
+
partition_key = options.fetch(:partition_key)
|
|
23
|
+
|
|
24
|
+
modified_options[:id] = false
|
|
25
|
+
modified_options[:options] = "PARTITION BY LIST (#{quote_partition_key(partition_key)})"
|
|
26
|
+
|
|
27
|
+
create_partition(table_name, id_options, modified_options, &block)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def create_list_partition_of(parent_table, options)
|
|
31
|
+
values = options.fetch(:values, [])
|
|
32
|
+
raise MissingListPartitionValuesError if values.blank?
|
|
33
|
+
|
|
34
|
+
name = options.fetch(:name, partition_name(parent_table, values))
|
|
35
|
+
# TODO: Call `create_table` here instead of running the query.
|
|
36
|
+
# TODO: Pass the options to `create_table` to allow further configuration of the table,
|
|
37
|
+
# e.g. sub-partitioning the table.
|
|
38
|
+
query = <<~SQL.strip
|
|
39
|
+
CREATE TABLE #{quote_table_name(name)} PARTITION OF #{quote_table_name(parent_table)}
|
|
40
|
+
FOR VALUES IN (#{quote_collection(values)})
|
|
41
|
+
SQL
|
|
42
|
+
|
|
43
|
+
execute(query)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
attr_reader :connection
|
|
49
|
+
|
|
50
|
+
delegate :execute, :quote, :quote_column_name, :quote_table_name, :create_table,
|
|
51
|
+
to: :connection
|
|
52
|
+
|
|
53
|
+
def raise_unless_list_partition_supported
|
|
54
|
+
raise ListPartitionsNotSupportedError unless connection.supports_list_partitions?
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# TODO: Better ?
|
|
58
|
+
def partition_name(parent_table, values)
|
|
59
|
+
key = values.inspect
|
|
60
|
+
"#{parent_table}_#{Digest::MD5.hexdigest(key)[0..6]}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require_relative 'base'
|
|
2
|
+
|
|
3
|
+
module Tablature
|
|
4
|
+
module Adapters
|
|
5
|
+
class Postgres
|
|
6
|
+
module Handlers
|
|
7
|
+
# @api private
|
|
8
|
+
class Range < Base
|
|
9
|
+
def initialize(connection)
|
|
10
|
+
@connection = connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create_range_partition(table_name, options, &block)
|
|
14
|
+
raise_unless_range_partition_supported
|
|
15
|
+
|
|
16
|
+
# Postgres 10 does not handle indexes and therefore primary keys.
|
|
17
|
+
# Therefore we manually create an `id` column.
|
|
18
|
+
# TODO: Either make the library Postgres 11 only, or two code paths between Postgres 10
|
|
19
|
+
# and Postgres 11.
|
|
20
|
+
modified_options = options.except(:id, :primary_key, :partition_key)
|
|
21
|
+
id_options = extract_primary_key!(options.slice(:id, :primary_key))
|
|
22
|
+
partition_key = options.fetch(:partition_key)
|
|
23
|
+
|
|
24
|
+
modified_options[:id] = false
|
|
25
|
+
modified_options[:options] =
|
|
26
|
+
"PARTITION BY RANGE (#{quote_partition_key(partition_key)})"
|
|
27
|
+
|
|
28
|
+
create_partition(table_name, id_options, modified_options, &block)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def create_range_partition_of(parent_table, options)
|
|
32
|
+
range_start = options.fetch(:range_start, nil)
|
|
33
|
+
range_end = options.fetch(:range_end, nil)
|
|
34
|
+
|
|
35
|
+
raise MissingRangePartitionBoundsError if range_start.nil? || range_end.nil?
|
|
36
|
+
|
|
37
|
+
name = options.fetch(:name, partition_name(parent_table, range_start, range_end))
|
|
38
|
+
# TODO: Call `create_table` here instead of running the query.
|
|
39
|
+
# TODO: Pass the options to `create_table` to allow further configuration of the table,
|
|
40
|
+
# e.g. sub-partitioning the table.
|
|
41
|
+
query = <<~SQL.strip
|
|
42
|
+
CREATE TABLE #{quote_table_name(name)} PARTITION OF #{quote_table_name(parent_table)}
|
|
43
|
+
FOR VALUES FROM (#{quote(range_start)}) TO (#{quote(range_end)});
|
|
44
|
+
SQL
|
|
45
|
+
|
|
46
|
+
execute(query)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
attr_reader :connection
|
|
52
|
+
|
|
53
|
+
delegate :execute, :quote, :quote_column_name, :quote_table_name, :create_table,
|
|
54
|
+
to: :connection
|
|
55
|
+
|
|
56
|
+
def raise_unless_range_partition_supported
|
|
57
|
+
raise RangePartitionsNotSupportedError unless connection.supports_range_partitions?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def partition_name(parent_table, range_start, range_end)
|
|
61
|
+
key = [range_start, range_end].join(', ')
|
|
62
|
+
"#{parent_table}_#{Digest::MD5.hexdigest(key)[0..6]}"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Tablature
|
|
2
|
+
module Adapters
|
|
3
|
+
class Postgres
|
|
4
|
+
# Fetches the defined partitioned tables from the postgres connection.
|
|
5
|
+
# @api private
|
|
6
|
+
class PartitionedTables
|
|
7
|
+
def initialize(connection)
|
|
8
|
+
@connection = connection
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# All of the partitioned table that this connection has defined.
|
|
12
|
+
#
|
|
13
|
+
# @return [Array<Tablature::PartitionedTable>]
|
|
14
|
+
def all
|
|
15
|
+
partitions.group_by { |row| row['table_name'] }.map(&method(:to_tablature_table))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :connection
|
|
21
|
+
|
|
22
|
+
def partitions
|
|
23
|
+
connection.execute(<<-SQL)
|
|
24
|
+
SELECT
|
|
25
|
+
c.oid,
|
|
26
|
+
c.relname AS table_name,
|
|
27
|
+
p.partstrat AS type,
|
|
28
|
+
(i.inhrelid::REGCLASS)::TEXT AS partition_name
|
|
29
|
+
FROM pg_class c
|
|
30
|
+
INNER JOIN pg_partitioned_table p ON c.oid = p.partrelid
|
|
31
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
32
|
+
FULL OUTER JOIN pg_catalog.pg_inherits i ON c.oid = i.inhparent
|
|
33
|
+
WHERE
|
|
34
|
+
p.partstrat IN ('l', 'r', 'h')
|
|
35
|
+
AND c.relname NOT IN (SELECT extname FROM pg_extension)
|
|
36
|
+
AND n.nspname = ANY (current_schemas(false))
|
|
37
|
+
ORDER BY c.oid
|
|
38
|
+
SQL
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
METHOD_MAP = {
|
|
42
|
+
'l' => :list,
|
|
43
|
+
'r' => :range,
|
|
44
|
+
'h' => :hash
|
|
45
|
+
}.freeze
|
|
46
|
+
private_constant :METHOD_MAP
|
|
47
|
+
|
|
48
|
+
def to_tablature_table(table_name, rows)
|
|
49
|
+
result = rows.first
|
|
50
|
+
partioning_method = METHOD_MAP.fetch(result['type'])
|
|
51
|
+
partitions = rows.map { |row| row['partition_name'] }.compact
|
|
52
|
+
|
|
53
|
+
Tablature::PartitionedTable.new(
|
|
54
|
+
name: table_name, partioning_method: partioning_method, partitions: partitions
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Tablature
|
|
2
|
+
module Adapters
|
|
3
|
+
class Postgres
|
|
4
|
+
# @api private
|
|
5
|
+
module Quoting
|
|
6
|
+
def quote_partition_key(key)
|
|
7
|
+
key.to_s.split('::').map(&method(:quote_column_name)).join('::')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def quote_collection(values)
|
|
11
|
+
Array.wrap(values).map(&method(:quote)).join(',')
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
|
2
|
+
|
|
3
|
+
require_relative 'postgres/connection'
|
|
4
|
+
require_relative 'postgres/errors'
|
|
5
|
+
require_relative 'postgres/handlers/list'
|
|
6
|
+
require_relative 'postgres/handlers/range'
|
|
7
|
+
require_relative 'postgres/partitioned_tables'
|
|
8
|
+
|
|
1
9
|
module Tablature
|
|
2
10
|
# Tablature database adapters.
|
|
3
11
|
#
|
|
@@ -7,7 +15,7 @@ module Tablature
|
|
|
7
15
|
module Adapters
|
|
8
16
|
# An adapter for managing Postgres views.
|
|
9
17
|
#
|
|
10
|
-
# These methods are used
|
|
18
|
+
# These methods are used internally by Tablature and are not intended for direct
|
|
11
19
|
# use. Methods that alter database schema are intended to be called via
|
|
12
20
|
# {Statements}.
|
|
13
21
|
#
|
|
@@ -21,7 +29,7 @@ module Tablature
|
|
|
21
29
|
# would explicitly set it.
|
|
22
30
|
#
|
|
23
31
|
# @param [#connection] connectable An object that returns the connection
|
|
24
|
-
# for Tablature to use. Defaults to
|
|
32
|
+
# for Tablature to use. Defaults to +ActiveRecord::Base+.
|
|
25
33
|
#
|
|
26
34
|
# @example
|
|
27
35
|
# Tablature.configure do |config|
|
|
@@ -30,6 +38,109 @@ module Tablature
|
|
|
30
38
|
def initialize(connectable = ActiveRecord::Base)
|
|
31
39
|
@connectable = connectable
|
|
32
40
|
end
|
|
41
|
+
|
|
42
|
+
# @!method create_list_partition(table_name, options, &block)
|
|
43
|
+
# Creates a partitioned table using the list partition method.
|
|
44
|
+
#
|
|
45
|
+
# This is called in a migration via {Statements#create_list_partition}.
|
|
46
|
+
#
|
|
47
|
+
# @param [String, Symbol] table_name The name of the table to partition.
|
|
48
|
+
# @param [Hash] options The options to create the partition. Keys besides +:partition_key+
|
|
49
|
+
# will be passed to +create_table+.
|
|
50
|
+
# @option options [String, Symbol] :partition_key The partition key.
|
|
51
|
+
# @yield [td] A TableDefinition object. This allows creating the table columns the same way
|
|
52
|
+
# as Rails's +create_table+ does.
|
|
53
|
+
#
|
|
54
|
+
# @example
|
|
55
|
+
# create_list_partition :events, partition_key: :id
|
|
56
|
+
#
|
|
57
|
+
# @example
|
|
58
|
+
# create_list_partition :events, partition_key: :date do |t|
|
|
59
|
+
# t.date :date, null: false
|
|
60
|
+
# end
|
|
61
|
+
delegate :create_list_partition, to: :list_handler
|
|
62
|
+
|
|
63
|
+
# @!method create_list_partition_of(parent_table_name, options)
|
|
64
|
+
# Creates a partition of a parent by specifying the key values appearing in the partition.
|
|
65
|
+
#
|
|
66
|
+
# @param parent_table_name [String, Symbol] The name of the parent table.
|
|
67
|
+
# @param [Hash] options The options to create the partition.
|
|
68
|
+
# @option options [String, Symbol] :values The values appearing in the partition.
|
|
69
|
+
# @option options [String, Symbol] :name The name of the partition. If it is not given, this
|
|
70
|
+
# will be randomly generated.
|
|
71
|
+
#
|
|
72
|
+
# @example
|
|
73
|
+
# # With a table :events partitioned using the list method on the partition key `date`:
|
|
74
|
+
# create_list_partition_of :events, name: "events_2018-W49", values: [
|
|
75
|
+
# "2018-12-03", "2018-12-04", "2018-12-05", "2018-12-06", "2018-12-07", "2018-12-08", "2018-12-09"
|
|
76
|
+
# ]
|
|
77
|
+
delegate :create_list_partition_of, to: :list_handler
|
|
78
|
+
|
|
79
|
+
# @!method create_range_partition(table_name, options, &block)
|
|
80
|
+
# Creates a partitioned table using the range partition method.
|
|
81
|
+
#
|
|
82
|
+
# This is called in a migration via {Statements#create_range_partition}.
|
|
83
|
+
#
|
|
84
|
+
# @param [String, Symbol] table_name The name of the table to partition.
|
|
85
|
+
# @param [Hash] options The options to create the partition. Keys besides +:partition_key+
|
|
86
|
+
# will be passed to +create_table+.
|
|
87
|
+
# @option options [String, Symbol] :partition_key The partition key.
|
|
88
|
+
# @yield [td] A TableDefinition object. This allows creating the table columns the same way
|
|
89
|
+
# as Rails's +create_table+ does.
|
|
90
|
+
#
|
|
91
|
+
# @example
|
|
92
|
+
# create_range_partition :events, partition_key: :id
|
|
93
|
+
#
|
|
94
|
+
# @example
|
|
95
|
+
# create_range_partition :events, partition_key: :date do |t|
|
|
96
|
+
# t.date :date, null: false
|
|
97
|
+
# end
|
|
98
|
+
delegate :create_range_partition, to: :range_handler
|
|
99
|
+
|
|
100
|
+
# @!method create_range_partition_of(parent_table_name, options)
|
|
101
|
+
# Creates a partition of a parent by specifying the bound of the values appearing in the
|
|
102
|
+
# partition.
|
|
103
|
+
#
|
|
104
|
+
# @param parent_table_name [String, Symbol] The name of the parent table.
|
|
105
|
+
# @param [Hash] options The options to create the partition.
|
|
106
|
+
# @option options [String, Symbol] :range_start The start of the range of values appearing in
|
|
107
|
+
# the partition.
|
|
108
|
+
# @option options [String, Symbol] :range_end The end of the range of values appearing in
|
|
109
|
+
# the partition.
|
|
110
|
+
# @option options [String, Symbol] :name The name of the partition. If it is not given, this
|
|
111
|
+
# will be randomly generated.
|
|
112
|
+
#
|
|
113
|
+
# @example
|
|
114
|
+
# # With a table :events partitioned using the range method on the partition key `date`:
|
|
115
|
+
# create_range_partition_of :events, name: "events_2018-W49", range_start: '2018-12-03',
|
|
116
|
+
# range_end: '2018-12-10'
|
|
117
|
+
delegate :create_range_partition_of, to: :range_handler
|
|
118
|
+
|
|
119
|
+
# Returns an array of partitioned tables in the database.
|
|
120
|
+
#
|
|
121
|
+
# This collection of tables is used by the [Tablature::SchemaDumper] to populate the schema.rb
|
|
122
|
+
# file.
|
|
123
|
+
#
|
|
124
|
+
# @return [Array<Tablature::PartitionedTable]
|
|
125
|
+
def partitioned_tables
|
|
126
|
+
PartitionedTables.new(connection).all
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
attr_reader :connectable
|
|
132
|
+
|
|
133
|
+
def connection
|
|
134
|
+
Connection.new(connectable.connection)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def list_handler
|
|
138
|
+
@list_handler ||= Handlers::List.new(connection)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def range_handler
|
|
142
|
+
@range_handler ||= Handlers::Range.new(connection)
|
|
143
|
+
end
|
|
33
144
|
end
|
|
34
145
|
end
|
|
35
146
|
end
|
|
@@ -26,11 +26,10 @@ module Tablature
|
|
|
26
26
|
# Modify Tablature's current configuration
|
|
27
27
|
#
|
|
28
28
|
# @yieldparam [Tablature::Configuration] config current Tablature config
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
# ```
|
|
29
|
+
# @example
|
|
30
|
+
# Tablature.configure do |config|
|
|
31
|
+
# config.database = Tablature::Adapters::Postgres.new
|
|
32
|
+
# end
|
|
34
33
|
def self.configure
|
|
35
34
|
yield configuration
|
|
36
35
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Tablature
|
|
2
|
+
# The in-memory representation of a partitioned table definition.
|
|
3
|
+
#
|
|
4
|
+
# **This object is used internally by adapters and the schema dumper and is
|
|
5
|
+
# not intended to be used by application code. It is documented here for
|
|
6
|
+
# use by adapter gems.**
|
|
7
|
+
#
|
|
8
|
+
# @api extension
|
|
9
|
+
class PartitionedTable
|
|
10
|
+
# The name of the partitioned table
|
|
11
|
+
# @return [String]
|
|
12
|
+
attr_reader :name
|
|
13
|
+
|
|
14
|
+
# The partitioning method of the table
|
|
15
|
+
# @return [Symbol]
|
|
16
|
+
attr_reader :partioning_method
|
|
17
|
+
|
|
18
|
+
# The partitions of the table.
|
|
19
|
+
# @return [Array]
|
|
20
|
+
attr_reader :partitions
|
|
21
|
+
|
|
22
|
+
# Returns a new instance of PartitionTable.
|
|
23
|
+
#
|
|
24
|
+
# @param name [String] The name of the view.
|
|
25
|
+
# @param partioning_method [:symbol] One of :range, :list or :hash
|
|
26
|
+
# @param partitions [Array] The partitions of the table.
|
|
27
|
+
def initialize(name:, partioning_method:, partitions: [])
|
|
28
|
+
@name = name
|
|
29
|
+
@partioning_method = partioning_method
|
|
30
|
+
@partitions = partitions
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Tablature
|
|
2
|
+
# @api private
|
|
3
|
+
module SchemaDumper
|
|
4
|
+
def tables(stream)
|
|
5
|
+
# Add partitions to the list of ignored tables.
|
|
6
|
+
ActiveRecord::SchemaDumper.ignore_tables =
|
|
7
|
+
(ActiveRecord::SchemaDumper.ignore_tables || []) + partitions
|
|
8
|
+
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def dumpable_partitioned_tables
|
|
15
|
+
Tablature.database.partitioned_tables
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def partitions
|
|
19
|
+
dumpable_partitioned_tables.flat_map(&:partitions)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Tablature
|
|
2
|
+
# Methods that are made available in migrations.
|
|
3
|
+
module Statements
|
|
4
|
+
# Creates a partitioned table using the list partition method.
|
|
5
|
+
#
|
|
6
|
+
# @param name [String, Symbol] The name of the partition.
|
|
7
|
+
# @param options [Hash] The options to create the partition.
|
|
8
|
+
# @yield [td] A TableDefinition object. This allows creating the table columns the same way
|
|
9
|
+
# as Rails's +create_table+ does.
|
|
10
|
+
# @see Tablature::Adapters::Postgres#create_list_partition
|
|
11
|
+
def create_list_partition(name, options, &block)
|
|
12
|
+
raise ArgumentError, 'partition_key must be defined' if options[:partition_key].nil?
|
|
13
|
+
|
|
14
|
+
Tablature.database.create_list_partition(name, options, &block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Creates a partition of a parent by specifying the key values appearing in the partition.
|
|
18
|
+
#
|
|
19
|
+
# @param parent_table_name [String, Symbol] The name of the parent table.
|
|
20
|
+
# @param [Hash] options The options to create the partition.
|
|
21
|
+
#
|
|
22
|
+
# @see Tablature::Adapters::Postgres#create_list_partition_of
|
|
23
|
+
def create_list_partition_of(parent_table_name, options)
|
|
24
|
+
raise ArgumentError, 'values must be defined' if options[:values].nil?
|
|
25
|
+
|
|
26
|
+
Tablature.database.create_list_partition_of(parent_table_name, options)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Creates a partitioned table using the range partition method.
|
|
30
|
+
#
|
|
31
|
+
# @param name [String, Symbol] The name of the partition.
|
|
32
|
+
# @param options [Hash] The options to create the partition.
|
|
33
|
+
# @yield [td] A TableDefinition object. This allows creating the table columns the same way
|
|
34
|
+
# as Rails's +create_table+ does.
|
|
35
|
+
#
|
|
36
|
+
# @see Tablature::Adapters::Postgres#create_range_partition
|
|
37
|
+
def create_range_partition(name, options, &block)
|
|
38
|
+
raise ArgumentError, 'partition_key must be defined' if options[:partition_key].nil?
|
|
39
|
+
|
|
40
|
+
Tablature.database.create_range_partition(name, options, &block)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Creates a partition of a parent by specifying the key values appearing in the partition.
|
|
44
|
+
#
|
|
45
|
+
# @param parent_table_name [String, Symbol] The name of the parent table.
|
|
46
|
+
# @param [Hash] options The options to create the partition.
|
|
47
|
+
#
|
|
48
|
+
# @see Tablature::Adapters::Postgres#create_range_partition_of
|
|
49
|
+
def create_range_partition_of(parent_table, options)
|
|
50
|
+
if options[:range_start].nil? || options[:range_end].nil?
|
|
51
|
+
raise ArgumentError, 'range_start and range_end must be defined'
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
Tablature.database.create_range_partition_of(parent_table, options)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
data/lib/tablature/version.rb
CHANGED
data/lib/tablature.rb
CHANGED
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
require 'tablature/adapters/postgres'
|
|
2
|
+
require 'tablature/command_recorder'
|
|
2
3
|
require 'tablature/configuration'
|
|
4
|
+
require 'tablature/model'
|
|
5
|
+
require 'tablature/partitioned_table'
|
|
3
6
|
require 'tablature/railtie'
|
|
7
|
+
require 'tablature/schema_dumper'
|
|
8
|
+
require 'tablature/statements'
|
|
4
9
|
require 'tablature/version'
|
|
5
10
|
|
|
6
11
|
require 'active_record'
|
|
7
12
|
|
|
8
|
-
# Tablature adds methods to `ActiveRecord::Migration` to create and manage
|
|
9
|
-
#
|
|
13
|
+
# Tablature adds methods to `ActiveRecord::Migration` to create and manage partitioned
|
|
14
|
+
# tables in Rails applications.
|
|
10
15
|
module Tablature
|
|
16
|
+
# Hooks Tablature into Rails.
|
|
17
|
+
#
|
|
18
|
+
# Enables tablature migration methods.
|
|
11
19
|
def self.load
|
|
20
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.include Tablature::Statements
|
|
21
|
+
ActiveRecord::Migration::CommandRecorder.include Tablature::CommandRecorder
|
|
22
|
+
ActiveRecord::SchemaDumper.prepend Tablature::SchemaDumper
|
|
23
|
+
ActiveRecord::Base.prepend Tablature::Model
|
|
12
24
|
end
|
|
13
25
|
|
|
26
|
+
# The current database adapter used by Tablature.
|
|
27
|
+
#
|
|
28
|
+
# This defaults to {Adapters::Postgres} by can be overriden via {Configuration}.
|
|
14
29
|
def self.database
|
|
15
30
|
configuration.database
|
|
16
31
|
end
|
data/tablature.gemspec
CHANGED
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
|
|
11
11
|
spec.summary = 'Rails + Postgres Partitions'
|
|
12
12
|
spec.description = 'Rails + Postgres Partitions'
|
|
13
|
-
spec.homepage =
|
|
13
|
+
spec.homepage = 'https://aliou.me'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
15
|
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
@@ -21,10 +21,11 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.require_paths = ['lib']
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
|
24
|
+
spec.add_development_dependency 'database_cleaner'
|
|
25
|
+
spec.add_development_dependency 'pg', '~> 0.19'
|
|
24
26
|
spec.add_development_dependency 'rake', '~> 10.0'
|
|
25
27
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
26
28
|
spec.add_development_dependency 'rspec-instafail'
|
|
27
|
-
spec.add_development_dependency 'pg', '~> 0.19'
|
|
28
29
|
|
|
29
30
|
spec.add_dependency 'activerecord', '>= 5.0.0'
|
|
30
31
|
spec.add_dependency 'railties', '>= 5.0.0'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tablature
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Aliou Diallo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-12-
|
|
11
|
+
date: 2018-12-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -24,6 +24,34 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '1.16'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: database_cleaner
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: pg
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.19'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.19'
|
|
27
55
|
- !ruby/object:Gem::Dependency
|
|
28
56
|
name: rake
|
|
29
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,20 +94,6 @@ dependencies:
|
|
|
66
94
|
- - ">="
|
|
67
95
|
- !ruby/object:Gem::Version
|
|
68
96
|
version: '0'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: pg
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0.19'
|
|
76
|
-
type: :development
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0.19'
|
|
83
97
|
- !ruby/object:Gem::Dependency
|
|
84
98
|
name: activerecord
|
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -115,6 +129,7 @@ executables: []
|
|
|
115
129
|
extensions: []
|
|
116
130
|
extra_rdoc_files: []
|
|
117
131
|
files:
|
|
132
|
+
- ".circleci/config.yml"
|
|
118
133
|
- ".gitignore"
|
|
119
134
|
- ".rspec"
|
|
120
135
|
- ".rubocop.yml"
|
|
@@ -128,8 +143,21 @@ files:
|
|
|
128
143
|
- bin/setup
|
|
129
144
|
- lib/tablature.rb
|
|
130
145
|
- lib/tablature/adapters/postgres.rb
|
|
146
|
+
- lib/tablature/adapters/postgres/connection.rb
|
|
147
|
+
- lib/tablature/adapters/postgres/errors.rb
|
|
148
|
+
- lib/tablature/adapters/postgres/handlers/base.rb
|
|
149
|
+
- lib/tablature/adapters/postgres/handlers/list.rb
|
|
150
|
+
- lib/tablature/adapters/postgres/handlers/range.rb
|
|
151
|
+
- lib/tablature/adapters/postgres/partitioned_tables.rb
|
|
152
|
+
- lib/tablature/adapters/postgres/quoting.rb
|
|
153
|
+
- lib/tablature/adapters/postgres/uuid.rb
|
|
154
|
+
- lib/tablature/command_recorder.rb
|
|
131
155
|
- lib/tablature/configuration.rb
|
|
156
|
+
- lib/tablature/model.rb
|
|
157
|
+
- lib/tablature/partitioned_table.rb
|
|
132
158
|
- lib/tablature/railtie.rb
|
|
159
|
+
- lib/tablature/schema_dumper.rb
|
|
160
|
+
- lib/tablature/statements.rb
|
|
133
161
|
- lib/tablature/version.rb
|
|
134
162
|
- tablature.gemspec
|
|
135
163
|
homepage: https://aliou.me
|