activerecord-turntable 2.5.0 → 3.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -0
- data/.travis.yml +3 -7
- data/CHANGELOG.md +4 -14
- data/Gemfile +3 -0
- data/Guardfile +12 -7
- data/README.md +11 -19
- data/Rakefile +14 -15
- data/activerecord-turntable.gemspec +24 -27
- data/gemfiles/rails5_0.gemfile +6 -0
- data/lib/active_record/turntable/active_record_ext/abstract_adapter.rb +24 -20
- data/lib/active_record/turntable/active_record_ext/activerecord_import_ext.rb +6 -16
- data/lib/active_record/turntable/active_record_ext/acts_as_archive_extension.rb +25 -16
- data/lib/active_record/turntable/active_record_ext/association.rb +33 -14
- data/lib/active_record/turntable/active_record_ext/association_preloader.rb +4 -24
- data/lib/active_record/turntable/active_record_ext/clever_load.rb +2 -2
- data/lib/active_record/turntable/active_record_ext/connection_handler_extension.rb +11 -15
- data/lib/active_record/turntable/active_record_ext/database_tasks.rb +9 -9
- data/lib/active_record/turntable/active_record_ext/fixtures.rb +11 -41
- data/lib/active_record/turntable/active_record_ext/locking_optimistic.rb +40 -147
- data/lib/active_record/turntable/active_record_ext/log_subscriber.rb +6 -37
- data/lib/active_record/turntable/active_record_ext/migration_proxy.rb +1 -1
- data/lib/active_record/turntable/active_record_ext/persistence.rb +54 -148
- data/lib/active_record/turntable/active_record_ext/relation.rb +17 -45
- data/lib/active_record/turntable/active_record_ext/schema_dumper.rb +80 -78
- data/lib/active_record/turntable/active_record_ext/sequencer.rb +6 -15
- data/lib/active_record/turntable/active_record_ext/transactions.rb +14 -9
- data/lib/active_record/turntable/active_record_ext.rb +15 -16
- data/lib/active_record/turntable/algorithm/modulo_algorithm.rb +1 -1
- data/lib/active_record/turntable/algorithm/range_algorithm.rb +9 -9
- data/lib/active_record/turntable/algorithm/range_bsearch_algorithm.rb +12 -12
- data/lib/active_record/turntable/base.rb +27 -14
- data/lib/active_record/turntable/cluster.rb +10 -13
- data/lib/active_record/turntable/cluster_helper_methods.rb +6 -6
- data/lib/active_record/turntable/config.rb +3 -3
- data/lib/active_record/turntable/connection_proxy/mixable.rb +1 -1
- data/lib/active_record/turntable/connection_proxy.rb +23 -22
- data/lib/active_record/turntable/helpers/test_helper.rb +4 -4
- data/lib/active_record/turntable/master_shard.rb +12 -7
- data/lib/active_record/turntable/migration.rb +41 -47
- data/lib/active_record/turntable/mixer/fader/calculate_shards_sum_result.rb +7 -7
- data/lib/active_record/turntable/mixer/fader/select_shards_merge_result.rb +12 -12
- data/lib/active_record/turntable/mixer.rb +121 -121
- data/lib/active_record/turntable/plugin.rb +1 -1
- data/lib/active_record/turntable/pool_proxy.rb +7 -19
- data/lib/active_record/turntable/query_cache.rb +41 -0
- data/lib/active_record/turntable/railtie.rb +7 -5
- data/lib/active_record/turntable/railties/databases.rake +19 -20
- data/lib/active_record/turntable/seq_shard.rb +15 -15
- data/lib/active_record/turntable/sequencer/api.rb +7 -7
- data/lib/active_record/turntable/sequencer/barrage.rb +6 -7
- data/lib/active_record/turntable/sequencer/mysql.rb +2 -2
- data/lib/active_record/turntable/sequencer.rb +15 -15
- data/lib/active_record/turntable/shard.rb +23 -20
- data/lib/active_record/turntable/sql_tree_patch.rb +59 -57
- data/lib/active_record/turntable/util.rb +1 -21
- data/lib/active_record/turntable/version.rb +1 -1
- data/lib/active_record/turntable.rb +14 -19
- data/lib/activerecord-turntable.rb +2 -2
- data/script/performance/algorithm +8 -9
- data/spec/active_record/turntable/active_record_ext/association_preloader_spec.rb +4 -4
- data/spec/active_record/turntable/active_record_ext/association_spec.rb +5 -14
- data/spec/active_record/turntable/active_record_ext/clever_load_spec.rb +6 -6
- data/spec/active_record/turntable/active_record_ext/fixture_set_spec.rb +4 -4
- data/spec/active_record/turntable/active_record_ext/locking_optimistic_spec.rb +2 -15
- data/spec/active_record/turntable/active_record_ext/migration_spec.rb +4 -4
- data/spec/active_record/turntable/active_record_ext/persistence_spec.rb +17 -15
- data/spec/active_record/turntable/active_record_ext/sequencer_spec.rb +1 -1
- data/spec/active_record/turntable/active_record_ext/test_fixtures_spec.rb +3 -3
- data/spec/active_record/turntable/algorithm/modulo_algorithm_spec.rb +2 -3
- data/spec/active_record/turntable/algorithm/range_algorithm_spec.rb +2 -3
- data/spec/active_record/turntable/algorithm/range_bsearch_algorithm_spec.rb +2 -3
- data/spec/active_record/turntable/algorithm_spec.rb +5 -5
- data/spec/active_record/turntable/base_spec.rb +1 -1
- data/spec/active_record/turntable/cluster_spec.rb +1 -1
- data/spec/active_record/turntable/config_spec.rb +1 -1
- data/spec/active_record/turntable/connection_proxy_spec.rb +16 -17
- data/spec/active_record/turntable/finder_spec.rb +1 -1
- data/spec/active_record/turntable/mixer/fader_spec.rb +1 -1
- data/spec/active_record/turntable/mixer_spec.rb +2 -4
- data/spec/active_record/turntable/query_cache_spec.rb +28 -0
- data/spec/active_record/turntable/sequencer/api_spec.rb +4 -4
- data/spec/active_record/turntable/sequencer/barrage_spec.rb +2 -2
- data/spec/active_record/turntable/sequencer/mysql_spec.rb +1 -1
- data/spec/active_record/turntable/shard_spec.rb +1 -1
- data/spec/active_record/turntable/sql_tree_patch_spec.rb +2 -2
- data/spec/active_record/turntable/transaction_spec.rb +2 -2
- data/spec/active_record/turntable_spec.rb +3 -3
- data/spec/migrations/002_create_user_statuses.rb +4 -4
- data/spec/migrations/003_create_cards.rb +3 -3
- data/spec/migrations/004_create_cards_users.rb +2 -2
- data/spec/models/user_status.rb +0 -1
- data/spec/spec_helper.rb +15 -14
- data/spec/support/turntable_helper.rb +4 -4
- metadata +98 -59
- data/gemfiles/rails4_0.gemfile +0 -7
- data/gemfiles/rails4_1.gemfile +0 -7
- data/gemfiles/rails4_2.gemfile +0 -7
- data/lib/active_record/turntable/rack/connection_management.rb +0 -18
- data/lib/active_record/turntable/rack/query_cache.rb +0 -40
- data/lib/active_record/turntable/rack.rb +0 -8
- data/spec/active_record/turntable/rack/query_cache_spec.rb +0 -19
@@ -2,19 +2,19 @@
|
|
2
2
|
#
|
3
3
|
# Sequencer via HTTP API
|
4
4
|
#
|
5
|
-
require
|
5
|
+
require "httpclient"
|
6
6
|
|
7
7
|
module ActiveRecord::Turntable
|
8
8
|
class Sequencer
|
9
9
|
class Api < Sequencer
|
10
|
-
API_ENDPOINT =
|
11
|
-
NEXT_VALUE_ENDPOINT =
|
10
|
+
API_ENDPOINT = "/sequences/".freeze
|
11
|
+
NEXT_VALUE_ENDPOINT = "/new".freeze
|
12
12
|
|
13
13
|
def initialize(klass, options = {})
|
14
14
|
@klass = klass
|
15
15
|
@options = options
|
16
|
-
@host = @options[
|
17
|
-
@port = @options[
|
16
|
+
@host = @options[:api_host]
|
17
|
+
@port = @options[:api_port]
|
18
18
|
@client = HTTPClient.new
|
19
19
|
end
|
20
20
|
|
@@ -22,14 +22,14 @@ module ActiveRecord::Turntable
|
|
22
22
|
res = @client.get_content("http://#{@host}:#{@port}#{API_ENDPOINT}#{sequence_name}#{NEXT_VALUE_ENDPOINT}")
|
23
23
|
new_id = res.to_i
|
24
24
|
raise SequenceNotFoundError if new_id.zero?
|
25
|
-
|
25
|
+
new_id
|
26
26
|
end
|
27
27
|
|
28
28
|
def current_sequence_value(sequence_name)
|
29
29
|
res = @client.get_content("http://#{@host}:#{@port}#{API_ENDPOINT}#{sequence_name}")
|
30
30
|
current_id = res.to_i
|
31
31
|
raise SequenceNotFoundError if current_id.zero?
|
32
|
-
|
32
|
+
current_id
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
@@ -4,25 +4,24 @@ module ActiveRecord::Turntable
|
|
4
4
|
@@unique_barrage_instance = {}
|
5
5
|
|
6
6
|
def initialize(klass, options = {})
|
7
|
-
require
|
7
|
+
require "barrage"
|
8
8
|
@klass = klass
|
9
9
|
@options = options["options"]
|
10
|
-
@barrage = get_barrage_instance
|
11
10
|
end
|
12
11
|
|
13
12
|
def next_sequence_value(sequence_name)
|
14
|
-
|
13
|
+
barrage.next
|
15
14
|
end
|
16
15
|
|
17
16
|
def current_sequence_value(sequence_name)
|
18
|
-
|
17
|
+
barrage.current
|
19
18
|
end
|
20
19
|
|
21
20
|
private
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
def barrage
|
23
|
+
@@unique_barrage_instance[@options] ||= ::Barrage.new(@options)
|
24
|
+
end
|
26
25
|
end
|
27
26
|
end
|
28
27
|
end
|
@@ -17,7 +17,7 @@ module ActiveRecord::Turntable
|
|
17
17
|
res = conn.execute("SELECT LAST_INSERT_ID()")
|
18
18
|
new_id = res.first.first.to_i
|
19
19
|
raise SequenceNotFoundError if new_id.zero?
|
20
|
-
|
20
|
+
new_id
|
21
21
|
end
|
22
22
|
|
23
23
|
def current_sequence_value(sequence_name)
|
@@ -25,7 +25,7 @@ module ActiveRecord::Turntable
|
|
25
25
|
conn.execute "UPDATE #{@klass.connection.quote_table_name(sequence_name)} SET id=LAST_INSERT_ID(id)"
|
26
26
|
res = conn.execute("SELECT LAST_INSERT_ID()")
|
27
27
|
current_id = res.first.first.to_i
|
28
|
-
|
28
|
+
current_id
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -14,9 +14,9 @@ module ActiveRecord::Turntable
|
|
14
14
|
end
|
15
15
|
|
16
16
|
@@sequence_types = {
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
17
|
+
api: Api,
|
18
|
+
mysql: Mysql,
|
19
|
+
barrage: Barrage,
|
20
20
|
}
|
21
21
|
|
22
22
|
@@sequences = {}
|
@@ -24,9 +24,9 @@ module ActiveRecord::Turntable
|
|
24
24
|
cattr_reader :sequences, :tables
|
25
25
|
|
26
26
|
def self.build(klass, sequence_name = nil, cluster_name = nil)
|
27
|
-
sequence_name ||= current_cluster_config_for(cluster_name || klass)[
|
28
|
-
seq_config = current_cluster_config_for(cluster_name || klass)[
|
29
|
-
seq_type = (seq_config[
|
27
|
+
sequence_name ||= current_cluster_config_for(cluster_name || klass)[:seq].keys.first
|
28
|
+
seq_config = current_cluster_config_for(cluster_name || klass)[:seq][sequence_name]
|
29
|
+
seq_type = (seq_config[:seq_type] ? seq_config[:seq_type].to_sym : :mysql)
|
30
30
|
@@tables[klass.table_name] ||= (@@sequences[sequence_name(klass.table_name, klass.primary_key)] ||= @@sequence_types[seq_type].new(klass, seq_config))
|
31
31
|
end
|
32
32
|
|
@@ -39,7 +39,7 @@ module ActiveRecord::Turntable
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def self.table_name(seq_name)
|
42
|
-
seq_name.split(
|
42
|
+
seq_name.split("_").first
|
43
43
|
end
|
44
44
|
|
45
45
|
def next_sequence_value
|
@@ -52,13 +52,13 @@ module ActiveRecord::Turntable
|
|
52
52
|
|
53
53
|
private
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
55
|
+
def self.current_cluster_config_for(klass_or_name)
|
56
|
+
cluster_name = if klass_or_name.is_a?(Symbol)
|
57
|
+
klass_or_name
|
58
|
+
else
|
59
|
+
klass_or_name.turntable_cluster_name.to_s
|
60
|
+
end
|
61
|
+
ActiveRecord::Base.turntable_config[:clusters][cluster_name]
|
62
|
+
end
|
63
63
|
end
|
64
64
|
end
|
@@ -1,16 +1,19 @@
|
|
1
1
|
module ActiveRecord::Turntable
|
2
2
|
class Shard
|
3
3
|
module Connections; end
|
4
|
+
def self.connection_classes
|
5
|
+
Connections.constants.map { |name| Connections.const_get(name) }
|
6
|
+
end
|
4
7
|
|
5
8
|
DEFAULT_CONFIG = {
|
6
|
-
"connection" => (defined?(Rails) ? Rails.env : "development")
|
9
|
+
"connection" => (defined?(Rails) ? Rails.env : "development"),
|
7
10
|
}.with_indifferent_access
|
8
11
|
|
9
12
|
attr_reader :name
|
10
13
|
|
11
14
|
def initialize(shard_spec)
|
12
15
|
@config = DEFAULT_CONFIG.merge(shard_spec)
|
13
|
-
@name = @config[
|
16
|
+
@name = @config[:connection]
|
14
17
|
ActiveRecord::Base.turntable_connections[name] = connection_pool
|
15
18
|
end
|
16
19
|
|
@@ -26,26 +29,26 @@ module ActiveRecord::Turntable
|
|
26
29
|
|
27
30
|
private
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
def connection_klass
|
33
|
+
@connection_klass ||= create_connection_class
|
34
|
+
end
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
def get_or_set_connection_class
|
37
|
+
if Connections.const_defined?(name.classify)
|
38
|
+
klass = Connections.const_get(name.classify)
|
39
|
+
else
|
40
|
+
klass = Class.new(ActiveRecord::Base)
|
41
|
+
Connections.const_set(name.classify, klass)
|
42
|
+
klass.abstract_class = true
|
43
|
+
end
|
44
|
+
klass
|
40
45
|
end
|
41
|
-
klass
|
42
|
-
end
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
def create_connection_class
|
48
|
+
klass = get_or_set_connection_class
|
49
|
+
klass.remove_connection
|
50
|
+
klass.establish_connection ActiveRecord::Base.connection_pool.spec.config[:shards][name].with_indifferent_access
|
51
|
+
klass
|
52
|
+
end
|
50
53
|
end
|
51
54
|
end
|
@@ -1,22 +1,22 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "sql_tree"
|
2
|
+
require "active_support/core_ext/kernel/reporting"
|
3
3
|
|
4
4
|
module SQLTree
|
5
5
|
class << self
|
6
6
|
attr_accessor :identifier_quote_field_char
|
7
7
|
end
|
8
|
-
self.identifier_quote_field_char =
|
8
|
+
self.identifier_quote_field_char = "`"
|
9
9
|
end
|
10
10
|
|
11
11
|
class SQLTree::Token
|
12
|
-
extended_keywords =
|
12
|
+
extended_keywords = %w(BINARY LIMIT OFFSET INDEX KEY USE FORCE IGNORE)
|
13
13
|
KEYWORDS.concat(extended_keywords)
|
14
14
|
|
15
15
|
extended_keywords.each do |kwd|
|
16
16
|
const_set(kwd, Class.new(SQLTree::Token::Keyword))
|
17
17
|
end
|
18
18
|
|
19
|
-
BINARY_ESCAPE = Class.new(SQLTree::Token).new(
|
19
|
+
BINARY_ESCAPE = Class.new(SQLTree::Token).new("x")
|
20
20
|
|
21
21
|
def possible_index_hint?
|
22
22
|
[SQLTree::Token::USE, SQLTree::Token::FORCE, SQLTree::Token::IGNORE].include?(self.class)
|
@@ -29,7 +29,7 @@ end
|
|
29
29
|
|
30
30
|
class SQLTree::Tokenizer
|
31
31
|
def tokenize_quoted_string(&block) # :yields: SQLTree::Token::String
|
32
|
-
string =
|
32
|
+
string = ""
|
33
33
|
until next_char.nil? || current_char == "'"
|
34
34
|
string << (current_char == "\\" ? instance_eval("%@\\#{next_char.gsub('@', '\@')}@") : current_char)
|
35
35
|
end
|
@@ -38,34 +38,33 @@ class SQLTree::Tokenizer
|
|
38
38
|
|
39
39
|
# @note Override to handle x'..' binary string
|
40
40
|
def each_token(&block) # :yields: SQLTree::Token
|
41
|
-
|
42
41
|
while next_char
|
43
42
|
case current_char
|
44
|
-
when /^\s
|
45
|
-
when
|
46
|
-
when
|
47
|
-
when
|
48
|
-
when
|
49
|
-
when /\d
|
50
|
-
when "'"
|
51
|
-
when
|
52
|
-
when /\w
|
53
|
-
when OPERATOR_CHARS
|
54
|
-
when SQLTree.identifier_quote_char
|
43
|
+
when /^\s?$/ then # whitespace, go to next character
|
44
|
+
when "(" then handle_token(SQLTree::Token::LPAREN, &block)
|
45
|
+
when ")" then handle_token(SQLTree::Token::RPAREN, &block)
|
46
|
+
when "." then handle_token(SQLTree::Token::DOT, &block)
|
47
|
+
when "," then handle_token(SQLTree::Token::COMMA, &block)
|
48
|
+
when /\d/ then tokenize_number(&block)
|
49
|
+
when "'" then tokenize_quoted_string(&block)
|
50
|
+
when "E", "x", 'X' then tokenize_possible_escaped_string(&block)
|
51
|
+
when /\w/ then tokenize_keyword(&block)
|
52
|
+
when OPERATOR_CHARS then tokenize_operator(&block)
|
53
|
+
when SQLTree.identifier_quote_char then tokenize_quoted_identifier(&block)
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
57
|
# Make sure to yield any tokens that are still stashed on the queue.
|
59
58
|
empty_keyword_queue!(&block)
|
60
59
|
end
|
61
|
-
|
60
|
+
alias_method :each, :each_token
|
62
61
|
|
63
62
|
def tokenize_possible_escaped_string(&block)
|
64
63
|
if peek_char == "'"
|
65
64
|
token = case current_char
|
66
|
-
when
|
65
|
+
when "E"
|
67
66
|
SQLTree::Token::STRING_ESCAPE
|
68
|
-
when
|
67
|
+
when "x", "X"
|
69
68
|
SQLTree::Token::BINARY_ESCAPE
|
70
69
|
end
|
71
70
|
handle_token(token, &block)
|
@@ -87,16 +86,16 @@ module SQLTree::Node
|
|
87
86
|
|
88
87
|
def to_sql(options = {})
|
89
88
|
raise "At least one SELECT expression is required" if self.select.empty?
|
90
|
-
sql =
|
91
|
-
sql << select.map { |s| s.to_sql(options) }.join(
|
92
|
-
sql << " FROM " << from.map { |f| f.to_sql(options) }.join(
|
89
|
+
sql = self.distinct ? "SELECT DISTINCT " : "SELECT "
|
90
|
+
sql << select.map { |s| s.to_sql(options) }.join(", ")
|
91
|
+
sql << " FROM " << from.map { |f| f.to_sql(options) }.join(", ") if from
|
93
92
|
sql << " WHERE " << where.to_sql(options) if where
|
94
|
-
sql << " GROUP BY " << group_by.map { |g| g.to_sql(options) }.join(
|
95
|
-
sql << " ORDER BY " << order_by.map { |o| o.to_sql(options) }.join(
|
93
|
+
sql << " GROUP BY " << group_by.map { |g| g.to_sql(options) }.join(", ") if group_by
|
94
|
+
sql << " ORDER BY " << order_by.map { |o| o.to_sql(options) }.join(", ") if order_by
|
96
95
|
sql << " HAVING " << having.to_sql(options) if having
|
97
|
-
sql << " LIMIT " << Array(limit).map {|f| f.to_sql(options) }.join(
|
96
|
+
sql << " LIMIT " << Array(limit).map { |f| f.to_sql(options) }.join(", ") if limit
|
98
97
|
sql << " OFFSET " << offset.to_sql(options) if offset
|
99
|
-
|
98
|
+
sql
|
100
99
|
end
|
101
100
|
|
102
101
|
def self.parse(tokens)
|
@@ -116,12 +115,12 @@ module SQLTree::Node
|
|
116
115
|
select_node.having = self.parse_having_clause(tokens) if SQLTree::Token::HAVING === tokens.peek
|
117
116
|
end
|
118
117
|
select_node.order_by = self.parse_order_clause(tokens) if SQLTree::Token::ORDER === tokens.peek
|
119
|
-
if SQLTree::Token::LIMIT === tokens.peek
|
118
|
+
if SQLTree::Token::LIMIT === tokens.peek && (list = self.parse_limit_clause(tokens))
|
120
119
|
select_node.offset = list.shift if list.size > 1
|
121
120
|
select_node.limit = list.shift
|
122
121
|
end
|
123
122
|
select_node.offset = self.parse_offset_clause(tokens) if SQLTree::Token::OFFSET === tokens.peek
|
124
|
-
|
123
|
+
select_node
|
125
124
|
end
|
126
125
|
|
127
126
|
def self.parse_limit_clause(tokens)
|
@@ -137,14 +136,14 @@ module SQLTree::Node
|
|
137
136
|
|
138
137
|
class SubQuery < SelectQuery
|
139
138
|
def to_sql(options = {})
|
140
|
-
"("+super(options)+")"
|
139
|
+
"(" + super(options) + ")"
|
141
140
|
end
|
142
141
|
|
143
142
|
def self.parse(tokens)
|
144
143
|
tokens.consume(SQLTree::Token::LPAREN)
|
145
144
|
select_node = super(tokens)
|
146
145
|
tokens.consume(SQLTree::Token::RPAREN)
|
147
|
-
|
146
|
+
select_node
|
148
147
|
end
|
149
148
|
end
|
150
149
|
|
@@ -152,14 +151,16 @@ module SQLTree::Node
|
|
152
151
|
leaf :index_hint
|
153
152
|
|
154
153
|
def initialize(table, table_alias = nil, index_hint = nil)
|
155
|
-
@table
|
154
|
+
@table = table
|
155
|
+
@table_alias = table_alias
|
156
|
+
@index_hint = index_hint
|
156
157
|
end
|
157
158
|
|
158
|
-
def to_sql(options={})
|
159
|
+
def to_sql(options = {})
|
159
160
|
sql = (SQLTree::Node::SubQuery === table) ? table.to_sql : quote_field_name(table)
|
160
161
|
sql << " AS " << quote_field_name(table_alias) if table_alias
|
161
162
|
sql << " " << index_hint.to_sql if index_hint
|
162
|
-
|
163
|
+
sql
|
163
164
|
end
|
164
165
|
|
165
166
|
def self.parse(tokens)
|
@@ -167,7 +168,7 @@ module SQLTree::Node
|
|
167
168
|
tokens.next
|
168
169
|
table_reference = self.new(tokens.current.literal)
|
169
170
|
if tokens.peek && !tokens.peek.possible_index_hint? &&
|
170
|
-
|
171
|
+
(SQLTree::Token::AS === tokens.peek || SQLTree::Token::Identifier === tokens.peek)
|
171
172
|
tokens.consume(SQLTree::Token::AS) if SQLTree::Token::AS === tokens.peek
|
172
173
|
table_reference.table_alias = tokens.next.literal
|
173
174
|
end
|
@@ -194,12 +195,14 @@ module SQLTree::Node
|
|
194
195
|
leaf :index_list
|
195
196
|
|
196
197
|
def initialize(hint_method, hint_key, index_list)
|
197
|
-
@hint_method
|
198
|
+
@hint_method = hint_method
|
199
|
+
@hint_key = hint_key
|
200
|
+
@index_list = index_list
|
198
201
|
end
|
199
202
|
|
200
|
-
def to_sql(options={})
|
203
|
+
def to_sql(options = {})
|
201
204
|
sql = "#{hint_method} #{hint_key} "
|
202
|
-
sql << "(#{index_list.map
|
205
|
+
sql << "(#{index_list.map(&:to_sql).join(' ')})"
|
203
206
|
sql
|
204
207
|
end
|
205
208
|
|
@@ -225,20 +228,20 @@ module SQLTree::Node
|
|
225
228
|
end
|
226
229
|
|
227
230
|
def self.parse_rhs(tokens, precedence, operator = nil)
|
228
|
-
if [
|
231
|
+
if ["IN", "NOT IN"].include?(operator)
|
229
232
|
if SQLTree::Token::SELECT === tokens.peek(2)
|
230
233
|
return SQLTree::Node::SubQuery.parse(tokens)
|
231
234
|
else
|
232
235
|
return List.parse(tokens)
|
233
236
|
end
|
234
|
-
elsif [
|
237
|
+
elsif ["IS", "IS NOT"].include?(operator)
|
235
238
|
tokens.consume(SQLTree::Token::NULL)
|
236
239
|
return SQLTree::Node::Expression::Value.new(nil)
|
237
|
-
elsif [
|
240
|
+
elsif ["BETWEEN"].include?(operator)
|
238
241
|
expr = parse_atomic(tokens)
|
239
242
|
operator = parse_operator(tokens)
|
240
243
|
rhs = parse_rhs(tokens, precedence, operator)
|
241
|
-
expr = self.new(:
|
244
|
+
expr = self.new(operator: operator, lhs: expr, rhs: rhs)
|
242
245
|
return expr
|
243
246
|
else
|
244
247
|
return parse(tokens, precedence + 1)
|
@@ -252,7 +255,7 @@ module SQLTree::Node
|
|
252
255
|
|
253
256
|
class Field < Variable
|
254
257
|
def to_sql(options = {})
|
255
|
-
@table.nil? ? quote_field_name(@name) : quote_field_name(@table) +
|
258
|
+
@table.nil? ? quote_field_name(@name) : quote_field_name(@table) + "." + quote_field_name(@name)
|
256
259
|
end
|
257
260
|
end
|
258
261
|
|
@@ -268,11 +271,11 @@ module SQLTree::Node
|
|
268
271
|
|
269
272
|
def to_sql(options = {})
|
270
273
|
case value
|
271
|
-
when nil
|
272
|
-
when String
|
273
|
-
when Numeric
|
274
|
-
when Date
|
275
|
-
when DateTime, Time
|
274
|
+
when nil then "NULL"
|
275
|
+
when String then "#{escape_string}#{quote_str(@value)}"
|
276
|
+
when Numeric then @value.to_s
|
277
|
+
when Date then @value.strftime("'%Y-%m-%d'")
|
278
|
+
when DateTime, Time then @value.strftime("'%Y-%m-%d %H:%M:%S'")
|
276
279
|
else raise "Don't know how te represent this value in SQL!"
|
277
280
|
end
|
278
281
|
end
|
@@ -321,14 +324,13 @@ module SQLTree::Node
|
|
321
324
|
end
|
322
325
|
|
323
326
|
class InsertQuery < Base
|
324
|
-
|
325
|
-
|
326
|
-
sql
|
327
|
-
sql <<
|
328
|
-
sql << 'VALUES'
|
327
|
+
def to_sql(options = {})
|
328
|
+
sql = "INSERT INTO #{table.to_sql(options)} "
|
329
|
+
sql << "(" + fields.map { |f| f.to_sql(options) }.join(", ") + ") " if fields
|
330
|
+
sql << "VALUES"
|
329
331
|
sql << values.map do |value|
|
330
|
-
|
331
|
-
|
332
|
+
" (" + value.map { |v| v.to_sql(options) }.join(", ") + ")"
|
333
|
+
end.join(",")
|
332
334
|
sql
|
333
335
|
end
|
334
336
|
|
@@ -344,7 +346,7 @@ module SQLTree::Node
|
|
344
346
|
values << parse_list(tokens)
|
345
347
|
tokens.consume(SQLTree::Token::RPAREN)
|
346
348
|
end
|
347
|
-
|
349
|
+
values
|
348
350
|
end
|
349
351
|
end
|
350
352
|
end
|
@@ -10,28 +10,8 @@ module ActiveRecord::Turntable
|
|
10
10
|
ar_version < Gem::Version.new(version)
|
11
11
|
end
|
12
12
|
|
13
|
-
def ar4?
|
14
|
-
ActiveRecord::VERSION::MAJOR == 4
|
15
|
-
end
|
16
|
-
|
17
|
-
def ar41_or_later?
|
18
|
-
ar_version_equals_or_later?("4.1")
|
19
|
-
end
|
20
|
-
|
21
|
-
def earlier_than_ar41?
|
22
|
-
ar_version_earlier_than?("4.1")
|
23
|
-
end
|
24
|
-
|
25
|
-
def ar42_or_later?
|
26
|
-
ar_version_equals_or_later?("4.2")
|
27
|
-
end
|
28
|
-
|
29
|
-
def earlier_than_ar42?
|
30
|
-
ar_version_earlier_than?("4.2")
|
31
|
-
end
|
32
|
-
|
33
13
|
def ar_version
|
34
|
-
ActiveRecord
|
14
|
+
ActiveRecord.gem_version
|
35
15
|
end
|
36
16
|
end
|
37
17
|
end
|
@@ -3,23 +3,14 @@
|
|
3
3
|
#
|
4
4
|
# ActiveRecord Sharding Plugin
|
5
5
|
#
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
|
15
|
-
# for 4.0.x series
|
16
|
-
module ActiveRecord
|
17
|
-
unless respond_to?(:gem_version)
|
18
|
-
class << self
|
19
|
-
alias_method :gem_version, :version
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
6
|
+
require "active_record/turntable/version"
|
7
|
+
require "active_record"
|
8
|
+
require "active_record/fixtures"
|
9
|
+
require "active_support/concern"
|
10
|
+
require "active_record/turntable/error"
|
11
|
+
require "active_record/turntable/util"
|
12
|
+
require "logger"
|
13
|
+
require "singleton"
|
23
14
|
|
24
15
|
module ActiveRecord::Turntable
|
25
16
|
extend ActiveSupport::Concern
|
@@ -37,12 +28,12 @@ module ActiveRecord::Turntable
|
|
37
28
|
autoload :Migration
|
38
29
|
autoload :Mixer
|
39
30
|
autoload :PoolProxy
|
31
|
+
autoload :QueryCache
|
40
32
|
autoload :Shard
|
41
33
|
autoload :ShardingCondition
|
42
34
|
autoload :SeqShard
|
43
35
|
autoload :Sequencer
|
44
36
|
end
|
45
|
-
autoload :Rack
|
46
37
|
autoload :Helpers
|
47
38
|
|
48
39
|
included do
|
@@ -56,7 +47,7 @@ module ActiveRecord::Turntable
|
|
56
47
|
def turntable_config_file
|
57
48
|
@@turntable_config_file ||=
|
58
49
|
File.join(defined?(::Rails) ?
|
59
|
-
::Rails.root.to_s : DEFAULT_PATH,
|
50
|
+
::Rails.root.to_s : DEFAULT_PATH, "config/turntable.yml")
|
60
51
|
end
|
61
52
|
|
62
53
|
def turntable_config_file=(filename)
|
@@ -66,6 +57,10 @@ module ActiveRecord::Turntable
|
|
66
57
|
def turntable_config
|
67
58
|
ActiveRecord::Turntable::Config.instance
|
68
59
|
end
|
60
|
+
|
61
|
+
def turntable_connection_classes
|
62
|
+
ActiveRecord::Turntable::Shard.connection_classes
|
63
|
+
end
|
69
64
|
end
|
70
65
|
|
71
66
|
require "active_record/turntable/railtie" if defined?(Rails)
|
@@ -1,2 +1,2 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "active_record"
|
2
|
+
require "active_record/turntable"
|
@@ -1,14 +1,14 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require
|
3
|
-
require File.join(File.dirname(__FILE__),
|
4
|
-
require
|
2
|
+
require "rspec"
|
3
|
+
require File.join(File.dirname(__FILE__), "../../spec/spec_helper")
|
4
|
+
require "benchmark"
|
5
5
|
|
6
6
|
def setup_algorithm(n, alg = "Range")
|
7
7
|
config = {
|
8
8
|
"shards" => n.times.map do |i|
|
9
|
-
{
|
10
|
-
end
|
11
|
-
}
|
9
|
+
{ connection: "connection_#{i}", less_than: (i + 1) * 100 }
|
10
|
+
end,
|
11
|
+
}.with_indifferent_access
|
12
12
|
"ActiveRecord::Turntable::Algorithm::#{alg}Algorithm".constantize.new(config)
|
13
13
|
end
|
14
14
|
|
@@ -18,15 +18,14 @@ Benchmark.bm(40) do |x|
|
|
18
18
|
algorithm = setup_algorithm(n, alg)
|
19
19
|
x.report("#{alg}: selrand(#{n}) * 1000") {
|
20
20
|
1000.times do
|
21
|
-
algorithm.calculate(rand(n*100))
|
21
|
+
algorithm.calculate(rand(n * 100))
|
22
22
|
end
|
23
23
|
}
|
24
24
|
x.report("#{alg}: sellast(#{n}) * 1000") {
|
25
25
|
1000.times do
|
26
|
-
algorithm.calculate(n*100-1)
|
26
|
+
algorithm.calculate(n * 100 - 1)
|
27
27
|
end
|
28
28
|
}
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
-
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "spec_helper"
|
2
2
|
|
3
3
|
describe ActiveRecord::Turntable::ActiveRecordExt::Association do
|
4
4
|
before(:all) do
|
@@ -11,7 +11,7 @@ describe ActiveRecord::Turntable::ActiveRecordExt::Association do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
let!(:user) do
|
14
|
-
user = User.new({:
|
14
|
+
user = User.new({ nickname: "user1" })
|
15
15
|
user.id = 1
|
16
16
|
user.save
|
17
17
|
user
|
@@ -49,7 +49,7 @@ describe ActiveRecord::Turntable::ActiveRecordExt::Association do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
it "its has_many targets should be assigned all related object" do
|
52
|
-
expect(subject.cards_users_histories).to include(*cards_users_histories.select { |history| history.cards_user_id == subject.id}
|
52
|
+
expect(subject.cards_users_histories).to include(*cards_users_histories.select { |history| history.cards_user_id == subject.id })
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
@@ -64,7 +64,7 @@ describe ActiveRecord::Turntable::ActiveRecordExt::Association do
|
|
64
64
|
end
|
65
65
|
|
66
66
|
it "its has_many targets should be assigned all related object" do
|
67
|
-
expect(subject.events_users_histories_with_foreign_shard_key).to include(*events_users_histories.select { |history| history.cards_user_id == subject.id}
|
67
|
+
expect(subject.events_users_histories_with_foreign_shard_key).to include(*events_users_histories.select { |history| history.cards_user_id == subject.id })
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|