tablature 0.1.1 → 0.2.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: eaec79244b29bb6514402673f8cb56f281c9cf25fb73c9a4dab413520e96b652
4
- data.tar.gz: 54cfe803f7387135d1334dba6b4b16a0c7d1946adb6c88e092889fe881203511
3
+ metadata.gz: 37da9dc068662c878b97c2f7cf95274f9893d4a34940d9511db0933d98b3597c
4
+ data.tar.gz: 261087c9165cba3aa60d4999f398889725979fa0398a5775c2408c0e41a420bb
5
5
  SHA512:
6
- metadata.gz: f30ffd3c9b8b14b08447b222a2bc5202042ef37f2c530beda33ed651c47ea7095f536bec494eef5767c44ba7a9b9751578bd9e3ff497e53203ac9bba477fc8a2
7
- data.tar.gz: 2553bb52f8fecfa642cf2b8c1f3908bcabf01c3a9c6e5902076e7d447e6cf64c8bd13069902d95cc7e4478daff7a08b1d899128b74722687ad5a0dcf6e0962a0
6
+ metadata.gz: e66f6acf2ae0bbc061b92153b5f3ba798dd37f69302e2f6de5b6cf5363bdc3d8ce3bed6442017b6d85ef4aab52e96301056f72a16a393282396042c4d54334e1
7
+ data.tar.gz: 3855a566bc693bf97c7b20b2871703bb766fcd2553ef1e0043d5d18c47d7cc80eb60e779b0d2d9baf83aa2447379f9486de7104cb05945b2cc86adc9485fd537
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tablature (0.1.1)
4
+ tablature (0.2.0)
5
5
  activerecord (>= 5.0.0)
6
6
  railties (>= 5.0.0)
7
7
 
@@ -35,21 +35,21 @@ GEM
35
35
  arel (9.0.0)
36
36
  builder (3.2.3)
37
37
  coderay (1.1.2)
38
- concurrent-ruby (1.1.3)
38
+ concurrent-ruby (1.1.4)
39
39
  crass (1.0.4)
40
40
  database_cleaner (1.7.0)
41
41
  diff-lcs (1.3)
42
- erubi (1.7.1)
43
- i18n (1.1.1)
42
+ erubi (1.8.0)
43
+ i18n (1.5.1)
44
44
  concurrent-ruby (~> 1.0)
45
45
  loofah (2.2.3)
46
46
  crass (~> 1.0.2)
47
47
  nokogiri (>= 1.5.9)
48
48
  method_source (0.9.0)
49
- mini_portile2 (2.3.0)
49
+ mini_portile2 (2.4.0)
50
50
  minitest (5.11.3)
51
- nokogiri (1.8.5)
52
- mini_portile2 (~> 2.3.0)
51
+ nokogiri (1.10.0)
52
+ mini_portile2 (~> 2.4.0)
53
53
  pg (0.21.0)
54
54
  pry (0.12.2)
55
55
  coderay (~> 1.1.0)
@@ -103,4 +103,4 @@ DEPENDENCIES
103
103
  tablature!
104
104
 
105
105
  BUNDLED WITH
106
- 1.16.6
106
+ 1.17.2
data/README.md CHANGED
@@ -55,14 +55,69 @@ class CreateEvents < ActiveRecord::Migration[5.0]
55
55
  # Create partitions with the bounds of the partition.
56
56
  create_list_partition_of :events_by_list,
57
57
  name: 'events_list_y2018m12', values: (Date.parse('2018-12-01')..Date.parse('2018-12-31')).to_a
58
+
58
59
  end
59
60
 
60
61
  def down
61
- drop_table :events
62
+ drop_table :events_by_range
63
+ drop_table :events_by_list
62
64
  end
63
65
  end
64
66
  ```
65
67
 
68
+ ### Having a partition back a model
69
+
70
+
71
+ In your migration:
72
+ ```ruby
73
+ # db/migrate/create_events.rb
74
+ class CreateEvents < ActiveRecord::Migration
75
+ def change
76
+ # You can use blocks when the partition key are SQL expression instead of
77
+ # being only a field.
78
+ create_range_partition :events, partition_key: -> { '(timestamp::DATE)' } do |t|
79
+ t.string :event_type, null: false
80
+ t.integer :value, null: false
81
+ t.datetime :timestamp, null: false
82
+ t.timestamps
83
+ end
84
+
85
+ create_range_partition_of :events,
86
+ name: 'events_y2018m12', range_start: '2018-12-01', range_end: '2019-01-01'
87
+
88
+ create_range_partition_of :events,
89
+ name: 'events_y2019m01', range_start: '2019-01-01', range_end: '2019-02-01'
90
+ end
91
+ end
92
+ ```
93
+
94
+ In your model, calling one of `range_partition` or `list_partition` to inject
95
+ methods:
96
+ ```
97
+ # app/models/event.rb
98
+ class Event < ApplicationRecord
99
+ range_partition
100
+ end
101
+ ```
102
+
103
+ Finally, you can now list the partitions :
104
+ ```ruby
105
+ >> Event.partitions
106
+ # => ["events_y2018m12", "events_y2019m01"]
107
+ ```
108
+
109
+ You can also create new partitions directly from the model :
110
+ ```ruby
111
+ >> Event.create_range_partition(
112
+ name: 'events_y2019m02',
113
+ range_start: '2019-02-01'.to_date,
114
+ range_end: '2019-03-01'.to_date
115
+ )
116
+ # => ...
117
+ >> Event.partitions
118
+ # => ["events_y2018m12", "events_y2019m01", "events_y2019m02"]
119
+ ```
120
+
66
121
  ## Development
67
122
 
68
123
  After checking out the repo, run `bin/setup` to install dependencies.
@@ -25,7 +25,8 @@ module Tablature
25
25
  c.oid,
26
26
  c.relname AS table_name,
27
27
  p.partstrat AS type,
28
- (i.inhrelid::REGCLASS)::TEXT AS partition_name
28
+ (i.inhrelid::REGCLASS)::TEXT AS partition_name,
29
+ pg_get_partkeydef(c.oid) AS partition_key_definition
29
30
  FROM pg_class c
30
31
  INNER JOIN pg_partitioned_table p ON c.oid = p.partrelid
31
32
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
@@ -47,11 +48,17 @@ module Tablature
47
48
 
48
49
  def to_tablature_table(table_name, rows)
49
50
  result = rows.first
50
- partioning_method = METHOD_MAP.fetch(result['type'])
51
+ partitioning_method = METHOD_MAP.fetch(result['type'])
51
52
  partitions = rows.map { |row| row['partition_name'] }.compact.map(&method(:unquote))
53
+ # This is very fragile code. This makes the assumption that:
54
+ # - Postgres will always have a function `pg_get_partkeydef` that returns the partition
55
+ # method with the partition key
56
+ # - Postgres will never have a partition method with two words in its name.
57
+ _, partition_key = result['partition_key_definition'].split(' ', 2)
52
58
 
53
59
  Tablature::PartitionedTable.new(
54
- name: table_name, partioning_method: partioning_method, partitions: partitions
60
+ name: table_name, partitioning_method: partitioning_method,
61
+ partitions: partitions, partition_key: partition_key
55
62
  )
56
63
  end
57
64
 
@@ -4,7 +4,11 @@ module Tablature
4
4
  # @api private
5
5
  module Quoting
6
6
  def quote_partition_key(key)
7
- key.to_s.split('::').map(&method(:quote_column_name)).join('::')
7
+ if key.respond_to?(:call)
8
+ key.call.to_s
9
+ else
10
+ key.to_s.split('::').map(&method(:quote_column_name)).join('::')
11
+ end
8
12
  end
9
13
 
10
14
  def quote_collection(values)
@@ -47,7 +47,7 @@ module Tablature
47
47
  # @param [String, Symbol] table_name The name of the table to partition.
48
48
  # @param [Hash] options The options to create the partition. Keys besides +:partition_key+
49
49
  # will be passed to +create_table+.
50
- # @option options [String, Symbol] :partition_key The partition key.
50
+ # @option options [String, Symbol, #call] :partition_key The partition key.
51
51
  # @yield [td] A TableDefinition object. This allows creating the table columns the same way
52
52
  # as Rails's +create_table+ does.
53
53
  #
@@ -84,7 +84,6 @@ module Tablature
84
84
  # @param [String, Symbol] table_name The name of the table to partition.
85
85
  # @param [Hash] options The options to create the partition. Keys besides +:partition_key+
86
86
  # will be passed to +create_table+.
87
- # @option options [String, Symbol] :partition_key The partition key.
88
87
  # @yield [td] A TableDefinition object. This allows creating the table columns the same way
89
88
  # as Rails's +create_table+ does.
90
89
  #
@@ -0,0 +1,29 @@
1
+ require 'forwardable'
2
+
3
+ module Tablature
4
+ module Model
5
+ module PartitionMethods
6
+ extend Forwardable
7
+ def_delegators :tablature_partition, :partitions, :partition_key, :partitioning_method
8
+
9
+ def tablature_partition
10
+ partition = Tablature.database.partitioned_tables.find do |pt|
11
+ pt.name == partition_name.to_s
12
+ end
13
+ raise Tablature::MissingPartition if partition.nil?
14
+
15
+ partition
16
+ end
17
+
18
+ def partitioned?
19
+ begin
20
+ tablature_partition
21
+ rescue Tablature::MissingPartition
22
+ return false
23
+ end
24
+
25
+ true
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,4 +1,51 @@
1
+ require 'tablature/model/partition_methods'
2
+
1
3
  module Tablature
2
4
  module Model
5
+ module ListPartitionMethods
6
+ def create_list_partition(options)
7
+ Tablature.database.create_list_partition_of(tablature_partition.name, options)
8
+ end
9
+ end
10
+
11
+ module RangePartitionMethods
12
+ def create_range_partition(options)
13
+ Tablature.database.create_range_partition_of(tablature_partition.name, options)
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+ def list_partition(partition_name = table_name)
19
+ setup_partition(partition_name)
20
+ extend(PartitionMethods)
21
+ extend(ListPartitionMethods)
22
+ end
23
+
24
+ def range_partition(partition_name = table_name)
25
+ setup_partition(partition_name)
26
+ extend(PartitionMethods)
27
+ extend(RangePartitionMethods)
28
+ end
29
+
30
+ # @api private
31
+ def inspect
32
+ return super unless partitioned?
33
+
34
+ # Copied from the Rails source.
35
+ attr_list = attribute_types.map { |name, type| "#{name}: #{type.type}" } * ', '
36
+ "#{self}(#{attr_list})"
37
+ end
38
+
39
+ private
40
+
41
+ def setup_partition(partition_name)
42
+ class_attribute(:partition_name)
43
+ self.partition_name = partition_name
44
+ end
45
+ end
46
+
47
+ def self.included(klass)
48
+ klass.extend ClassMethods
49
+ end
3
50
  end
4
51
  end
@@ -13,21 +13,27 @@ module Tablature
13
13
 
14
14
  # The partitioning method of the table
15
15
  # @return [Symbol]
16
- attr_reader :partioning_method
16
+ attr_reader :partitioning_method
17
17
 
18
18
  # The partitions of the table.
19
19
  # @return [Array]
20
20
  attr_reader :partitions
21
21
 
22
+ # The partition key expression.
23
+ # @return [String]
24
+ attr_reader :partition_key
25
+
22
26
  # Returns a new instance of PartitionTable.
23
27
  #
24
28
  # @param name [String] The name of the view.
25
- # @param partioning_method [:symbol] One of :range, :list or :hash
29
+ # @param partitioning_method [:symbol] One of :range, :list or :hash
26
30
  # @param partitions [Array] The partitions of the table.
27
- def initialize(name:, partioning_method:, partitions: [])
31
+ # @param partition_key [String] The partition key expression.
32
+ def initialize(name:, partitioning_method:, partitions: [], partition_key:)
28
33
  @name = name
29
- @partioning_method = partioning_method
34
+ @partitioning_method = partitioning_method
30
35
  @partitions = partitions
36
+ @partition_key = partition_key
31
37
  end
32
38
  end
33
39
  end
@@ -1,3 +1,3 @@
1
1
  module Tablature
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/lib/tablature.rb CHANGED
@@ -20,7 +20,7 @@ module Tablature
20
20
  ActiveRecord::ConnectionAdapters::AbstractAdapter.include Tablature::Statements
21
21
  ActiveRecord::Migration::CommandRecorder.include Tablature::CommandRecorder
22
22
  ActiveRecord::SchemaDumper.prepend Tablature::SchemaDumper
23
- ActiveRecord::Base.prepend Tablature::Model
23
+ ActiveRecord::Base.include Tablature::Model
24
24
  end
25
25
 
26
26
  # The current database adapter used by Tablature.
@@ -29,4 +29,10 @@ module Tablature
29
29
  def self.database
30
30
  configuration.database
31
31
  end
32
+
33
+ class MissingPartition < StandardError
34
+ def initialize
35
+ super('Missing partition')
36
+ end
37
+ end
32
38
  end
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.1.1
4
+ version: 0.2.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-05 00:00:00.000000000 Z
11
+ date: 2019-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -154,6 +154,7 @@ files:
154
154
  - lib/tablature/command_recorder.rb
155
155
  - lib/tablature/configuration.rb
156
156
  - lib/tablature/model.rb
157
+ - lib/tablature/model/partition_methods.rb
157
158
  - lib/tablature/partitioned_table.rb
158
159
  - lib/tablature/railtie.rb
159
160
  - lib/tablature/schema_dumper.rb