cassie-queries 0.0.1.a3 → 0.0.1.a4
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/lib/cassie/queries/instrumentation.rb +2 -1
- data/lib/cassie/queries/logging/cql_execution_event.rb +8 -4
- data/lib/cassie/queries/logging/logger.rb +1 -1
- data/lib/cassie/queries/pagination/cursors.rb +15 -0
- data/lib/cassie/queries/pagination/page_size.rb +7 -0
- data/lib/cassie/queries/pagination.rb +37 -0
- data/lib/cassie/queries/statement/assignment.rb +36 -0
- data/lib/cassie/queries/statement/assignments.rb +67 -0
- data/lib/cassie/queries/statement/deleting.rb +60 -0
- data/lib/cassie/queries/statement/fetching.rb +55 -0
- data/lib/cassie/queries/statement/inserting.rb +37 -0
- data/lib/cassie/queries/statement/limiting.rb +25 -0
- data/lib/cassie/queries/statement/loading.rb +16 -0
- data/lib/cassie/queries/statement/mapping.rb +66 -0
- data/lib/cassie/queries/statement/ordering.rb +11 -0
- data/lib/cassie/queries/statement/preparation/cache.rb +55 -0
- data/lib/cassie/queries/statement/preparation.rb +47 -0
- data/lib/cassie/queries/statement/relation.rb +59 -0
- data/lib/cassie/queries/statement/relations.rb +64 -0
- data/lib/cassie/queries/statement/selection.rb +63 -0
- data/lib/cassie/queries/statement/updating.rb +39 -0
- data/lib/cassie/queries/statement.rb +60 -18
- data/lib/cassie/query.rb +11 -2
- metadata +49 -18
- data/lib/cassie/queries/prepared_statement.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43d357d81ed74c4e0e24a0536842f3cb0c135681
|
4
|
+
data.tar.gz: 246a0d7be210bbdea3bc1c1ce3f70fdd445d7eb7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4561e775d74984005a279c954b4ad638da526830b6a1ab52a82f17def1208604180b168b2011b9baed3e993e9e0a0e5e730e88e70698604b8a439c91781541a1
|
7
|
+
data.tar.gz: 4922dbc93622b6cbc63bf89643e149165fd0484b91b8b617b61acde5291b02f8dc17dcff3e8916ffc57bcc9241e949f00ef8ecb0239d0603faf6101a0db8d76f
|
@@ -9,7 +9,8 @@ module Cassie::Queries
|
|
9
9
|
def instrument #:nodoc:
|
10
10
|
instrumenter.instrument("cql.execute") do |payload|
|
11
11
|
execution_val = yield # execution populates #result
|
12
|
-
|
12
|
+
|
13
|
+
payload[:execution_info] = result.execution_info if result.respond_to?(:execution_info)
|
13
14
|
execution_val
|
14
15
|
end
|
15
16
|
end
|
@@ -19,10 +19,14 @@ module Cassie::Queries::Logging
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def statement
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
if execution_info
|
23
|
+
statement = execution_info.statement
|
24
|
+
str = statement.cql
|
25
|
+
str += " [#{statement.params}]" if statement.respond_to? :params
|
26
|
+
str
|
27
|
+
else
|
28
|
+
"CQL executed: (`execution_info` was not present?)"
|
29
|
+
end
|
26
30
|
end
|
27
31
|
|
28
32
|
def traced?
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Cassie::Queries::Pagination
|
2
|
+
module Cursors
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
def max_cursor(field, opts={})
|
7
|
+
relation field, :lteq, opts
|
8
|
+
end
|
9
|
+
|
10
|
+
def since_cursor(field, opts={})
|
11
|
+
relation field, :gt, opts
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'pagination/cursors'
|
2
|
+
require_relative 'pagination/page_size'
|
3
|
+
|
4
|
+
module Cassie::Queries
|
5
|
+
module Pagination
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include Cursors
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def inherited(subclass)
|
14
|
+
subclass.page_size = page_size
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def page_size
|
19
|
+
return @page_size if defined?(@page_size)
|
20
|
+
Cassie::Queries::Pagination::PageSize.default
|
21
|
+
end
|
22
|
+
|
23
|
+
def page_size=(val)
|
24
|
+
@page_size = val
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def page_size
|
29
|
+
return @page_size if defined?(@page_size)
|
30
|
+
self.class.page_size
|
31
|
+
end
|
32
|
+
|
33
|
+
def page_size=(val)
|
34
|
+
@page_size = val
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Cassie::Queries::Statement
|
2
|
+
# set "username = ?", value: :username
|
3
|
+
# set "favs = favs + ?" value: "{ 'movie' : 'Cassablanca' }"
|
4
|
+
# set :username
|
5
|
+
class Assignment
|
6
|
+
# https://cassandra.apache.org/doc/cql3/CQL.html#updateStmt
|
7
|
+
|
8
|
+
attr_reader :identifier
|
9
|
+
|
10
|
+
def initialize(identifier, opts={})
|
11
|
+
if String === identifier
|
12
|
+
# custom assignment is being defined:
|
13
|
+
#
|
14
|
+
# `assign "username = ?", value: :username`
|
15
|
+
@cql = identifier
|
16
|
+
@custom = true
|
17
|
+
else
|
18
|
+
@identifier = identifier
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_insert_cql
|
23
|
+
return @cql if defined?(@cql)
|
24
|
+
identifier
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_update_cql
|
28
|
+
return @cql if defined?(@cql)
|
29
|
+
"#{identifier} = ?"
|
30
|
+
end
|
31
|
+
|
32
|
+
def custom?
|
33
|
+
!!@custom
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require_relative 'assignment'
|
2
|
+
require_relative 'mapping'
|
3
|
+
|
4
|
+
module Cassie::Queries::Statement
|
5
|
+
module Assignments
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
include Mapping
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def set(identifier, opts={})
|
14
|
+
assignment = Assignment.new(identifier, opts)
|
15
|
+
opts[:value] ||= identifier.to_sym
|
16
|
+
|
17
|
+
if Symbol === opts[:value]
|
18
|
+
define_term_methods(opts[:value])
|
19
|
+
end
|
20
|
+
|
21
|
+
assignments[assignment] = opts
|
22
|
+
end
|
23
|
+
|
24
|
+
def assignments
|
25
|
+
@assignments ||= {}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def assignments
|
30
|
+
self.class.assignments
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_update_and_bindings
|
34
|
+
cql = ""
|
35
|
+
bindings = []
|
36
|
+
assignment_strings = []
|
37
|
+
|
38
|
+
assignments.each do |a, opts|
|
39
|
+
if eval_if_opt?(opts[:if])
|
40
|
+
assignment_strings << a.to_update_cql
|
41
|
+
bindings << eval_value_opt(opts[:value])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
cql = "#{assignment_strings.join(', ')}" unless bindings.empty?
|
46
|
+
|
47
|
+
[cql , bindings]
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_insert_and_bindings
|
51
|
+
identifiers = []
|
52
|
+
bindings = []
|
53
|
+
|
54
|
+
assignments.each do |a, opts|
|
55
|
+
if eval_if_opt?(opts[:if])
|
56
|
+
identifiers << a.to_insert_cql
|
57
|
+
bindings << eval_value_opt(opts[:value])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
identifiers_cql = "#{identifiers.join(', ')}"
|
62
|
+
terms_cql = Array.new(identifiers.count){"?"}.join(", ")
|
63
|
+
|
64
|
+
[identifiers_cql, terms_cql , bindings]
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative 'relations'
|
2
|
+
|
3
|
+
module Cassie::Queries::Statement
|
4
|
+
module Deleting
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
#TODO: accept block to add specific selectors and aliases
|
9
|
+
# select :table do |t|
|
10
|
+
# t.id
|
11
|
+
# t.name as: :username
|
12
|
+
# end
|
13
|
+
def delete(table)
|
14
|
+
include Relations
|
15
|
+
|
16
|
+
self.table = table
|
17
|
+
self.identifier = :delete
|
18
|
+
|
19
|
+
yield(self) if block_given?
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO rename to identifiers and extract
|
23
|
+
def selectors
|
24
|
+
@selectors ||= []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete(opts={})
|
29
|
+
execute
|
30
|
+
execution_successful?
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
|
35
|
+
def build_delete_cql_and_bindings
|
36
|
+
where_str, bindings = build_where_and_bindings
|
37
|
+
|
38
|
+
cql = %(
|
39
|
+
DELETE #{build_delete_clause}
|
40
|
+
FROM #{table}
|
41
|
+
#{where_str}
|
42
|
+
).squish + ";"
|
43
|
+
|
44
|
+
[cql, bindings]
|
45
|
+
end
|
46
|
+
|
47
|
+
# a select clause is built up of selectors
|
48
|
+
def selectors
|
49
|
+
self.class.selectors
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_delete_clause
|
53
|
+
str = if selectors.empty?
|
54
|
+
''
|
55
|
+
else
|
56
|
+
selectors.join(', ')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require_relative 'relation'
|
2
|
+
require_relative 'loading'
|
3
|
+
|
4
|
+
class Cassie::Queries::RecordNotFound < StandardError; end
|
5
|
+
|
6
|
+
module Cassie::Queries::Statement
|
7
|
+
module Fetching
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
include Loading
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns array of rows or empty array
|
15
|
+
#
|
16
|
+
# query.fetch(id: 1)
|
17
|
+
# => [{id: 1, name: 'eprothro'}]
|
18
|
+
def fetch(args={})
|
19
|
+
args.each do |k, v|
|
20
|
+
setter = "#{k}="
|
21
|
+
send(setter, v) if respond_to? setter
|
22
|
+
end
|
23
|
+
|
24
|
+
execute
|
25
|
+
result.rows
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns first result or nil
|
29
|
+
#
|
30
|
+
# query.find(id: 1)
|
31
|
+
# => {id: 1, name: 'eprothro'}
|
32
|
+
#
|
33
|
+
# query.find(id: 2)
|
34
|
+
# => nil
|
35
|
+
def find(args={})
|
36
|
+
old_limit = defined?(@limit) ? @limit : nil
|
37
|
+
self.limit = 1
|
38
|
+
|
39
|
+
fetch.first
|
40
|
+
ensure
|
41
|
+
self.limit = old_limit
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns first result or raises RecordNotFound
|
45
|
+
#
|
46
|
+
# query.find!(id: 1)
|
47
|
+
# => {id: 1, name: 'eprothro'}
|
48
|
+
#
|
49
|
+
# query.find!(id: 2)
|
50
|
+
# RecordNotFound: RecordNotFound
|
51
|
+
def find!(args={})
|
52
|
+
find || raise(Cassie::Queries::RecordNotFound)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'assignments'
|
2
|
+
|
3
|
+
module Cassie::Queries::Statement
|
4
|
+
module Inserting
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def insert(table)
|
9
|
+
include Assignments
|
10
|
+
|
11
|
+
self.table = table
|
12
|
+
self.identifier = :insert
|
13
|
+
|
14
|
+
yield(self) if block_given?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def insert(opts={})
|
19
|
+
execute
|
20
|
+
execution_successful?
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def build_insert_cql_and_bindings
|
26
|
+
identifiers_str, terms_str, bindings = build_insert_and_bindings
|
27
|
+
|
28
|
+
cql = %(
|
29
|
+
INSERT INTO #{table}
|
30
|
+
(#{identifiers_str})
|
31
|
+
VALUES (#{terms_str})
|
32
|
+
).squish + ";"
|
33
|
+
|
34
|
+
[cql, bindings]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Cassie::Queries::Statement
|
2
|
+
module Limiting
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
attr_accessor :limit
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :limit
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def limit
|
14
|
+
@limit || self.class.limit
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def build_limit_str
|
20
|
+
return "" if limit.nil?
|
21
|
+
|
22
|
+
"LIMIT #{limit}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Cassie::Queries::Statement
|
2
|
+
module Loading
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def fetch(args={})
|
6
|
+
rows = super(args)
|
7
|
+
rows.map {|r| build_resource(r) }
|
8
|
+
end
|
9
|
+
|
10
|
+
# When class doesn't override
|
11
|
+
# simply return a struct with the row data
|
12
|
+
def build_resource(row)
|
13
|
+
Struct.new(*row.keys).new(*row.values)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Cassie::Queries::Statement
|
2
|
+
module Mapping
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
MAPPED_METHODS = [:insert, :update, :delete].freeze
|
6
|
+
|
7
|
+
included do
|
8
|
+
MAPPED_METHODS.each do |method|
|
9
|
+
next unless method_defined?(method)
|
10
|
+
|
11
|
+
define_method(method) do |resource=nil, opts={}|
|
12
|
+
return super(opts) if _resource.nil? && resource.nil?
|
13
|
+
|
14
|
+
self._resource = resource
|
15
|
+
|
16
|
+
if super(opts)
|
17
|
+
_resource
|
18
|
+
else
|
19
|
+
false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
def map_from(resource_name)
|
27
|
+
attr_accessor resource_name
|
28
|
+
|
29
|
+
define_method "_resource" do
|
30
|
+
send resource_name
|
31
|
+
end
|
32
|
+
|
33
|
+
define_method "_resource=" do |val|
|
34
|
+
send("#{resource_name}=", val)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def define_term_methods(field)
|
41
|
+
getter = field
|
42
|
+
setter = "#{field}="
|
43
|
+
|
44
|
+
if method_defined?(getter) || method_defined?(setter)
|
45
|
+
raise "accessor or getter already defined for #{field}. Fix the collisions by using the `:value` option."
|
46
|
+
else
|
47
|
+
# Order of prefrence for finding term value
|
48
|
+
# 1. overriden getter instance method
|
49
|
+
# 2. value set by setter instance method
|
50
|
+
# 3. (Eventually) Mapping getter instance method
|
51
|
+
# 4. instance resource getter instance method
|
52
|
+
define_method getter do
|
53
|
+
if instance_variable_defined?("@#{field}")
|
54
|
+
return instance_variable_get("@#{field}")
|
55
|
+
end
|
56
|
+
_resource.send(field) if(_resource && _resource.respond_to?(field))
|
57
|
+
end
|
58
|
+
|
59
|
+
define_method setter do |val|
|
60
|
+
instance_variable_set("@#{field}", val)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Cassie::Queries::Statement
|
2
|
+
module Preparation
|
3
|
+
class Cache
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
clear
|
8
|
+
@monitor = Monitor.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def write(key, value)
|
12
|
+
synchronize do
|
13
|
+
@data[key] = value
|
14
|
+
end
|
15
|
+
end
|
16
|
+
def read(key)
|
17
|
+
synchronize do
|
18
|
+
data[key]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
def fetch(key)
|
22
|
+
return read(key) if data.has_key?(key)
|
23
|
+
write(key, yield) if block_given?
|
24
|
+
end
|
25
|
+
def clear
|
26
|
+
@data = {}
|
27
|
+
end
|
28
|
+
def close
|
29
|
+
clear
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def synchronize(&block)
|
35
|
+
@monitor.synchronize(&block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.cache
|
40
|
+
@cache ||= init_cache
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.init_cache
|
44
|
+
previous_cache = defined?(@cache) ? @cache : nil
|
45
|
+
|
46
|
+
#TODO: research why memory story is blowing up when
|
47
|
+
# serializing the Cassandra prepared statement result
|
48
|
+
# @cache = ActiveSupport::Cache::MemoryStore.new
|
49
|
+
@cache = Cache.new
|
50
|
+
|
51
|
+
previous_cache.close if previous_cache
|
52
|
+
@cache
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'preparation/cache'
|
2
|
+
|
3
|
+
module Cassie::Queries::Statement
|
4
|
+
module Preparation
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class << self
|
9
|
+
attr_accessor :prepare
|
10
|
+
end
|
11
|
+
self.prepare = true
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
def inherited(subclass)
|
16
|
+
subclass.prepare = prepare
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def prepare?
|
21
|
+
!!prepare
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def statement
|
26
|
+
statement = super
|
27
|
+
|
28
|
+
if self.class.prepare?
|
29
|
+
key = statement.cql if statement.respond_to?(:cql)
|
30
|
+
key ||= statement.to_s
|
31
|
+
|
32
|
+
unbound = statement_cache.fetch(key) do
|
33
|
+
session.prepare(statement)
|
34
|
+
end
|
35
|
+
unbound.bind(statement.params)
|
36
|
+
else
|
37
|
+
statement
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def statement_cache
|
44
|
+
Cassie::Queries::Statement::Preparation.cache
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Cassie::Queries::Statement
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# relation "username = ?", value: :username
|
5
|
+
# relation :username, :eq
|
6
|
+
# relation :username, :eq, value: :method
|
7
|
+
# relation :phone, :in
|
8
|
+
class Relation
|
9
|
+
# https://cassandra.apache.org/doc/cql3/CQL.html#selectStmt
|
10
|
+
|
11
|
+
OPERATIONS = {
|
12
|
+
eq: "=",
|
13
|
+
lt: "<",
|
14
|
+
lteq: "<=",
|
15
|
+
gt: ">",
|
16
|
+
gteq: ">=",
|
17
|
+
contains: "CONTAINS",
|
18
|
+
contains_key: "CONTAINS KEY",
|
19
|
+
}
|
20
|
+
|
21
|
+
attr_reader :identifier,
|
22
|
+
:op_type
|
23
|
+
|
24
|
+
def initialize(identifier, op_type, opts={})
|
25
|
+
if Hash === op_type
|
26
|
+
# custom relation is being defined:
|
27
|
+
# `relation "username = ?", value: :username`
|
28
|
+
|
29
|
+
# swap the 2nd arg that sucked in options hash
|
30
|
+
opts.merge!(op_type)
|
31
|
+
|
32
|
+
@cql = identifier
|
33
|
+
@custom = true
|
34
|
+
else
|
35
|
+
@identifier = identifier
|
36
|
+
@op_type = op_type.to_sym
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def custom?
|
41
|
+
!!@custom
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_cql
|
45
|
+
cql
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def cql
|
51
|
+
# we always generate bound statements
|
52
|
+
@cql ||= "#{identifier} #{OPERATIONS[op_type]} ?"
|
53
|
+
end
|
54
|
+
|
55
|
+
def op_type
|
56
|
+
@op_type
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require_relative 'relation'
|
2
|
+
|
3
|
+
module Cassie::Queries::Statement
|
4
|
+
module Relations
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
# where :username, :eq, value: :method
|
9
|
+
# where :phone, :in
|
10
|
+
# were "username = ?", value: :username
|
11
|
+
def where(identifier, op, opts={})
|
12
|
+
relation = Relation.new(identifier, op, opts)
|
13
|
+
|
14
|
+
opts[:value] ||= identifier.to_sym
|
15
|
+
|
16
|
+
if Symbol === opts[:value]
|
17
|
+
define_term_methods(opts[:value])
|
18
|
+
end
|
19
|
+
|
20
|
+
relations[relation] = opts
|
21
|
+
end
|
22
|
+
|
23
|
+
# a where clause is built up of multiple 'relations'
|
24
|
+
def relations
|
25
|
+
@relations ||= {}
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def define_term_methods(name)
|
31
|
+
#TODO: this should probably only raise
|
32
|
+
# if value option was nil and we
|
33
|
+
# are implicilty creating getter/setters.
|
34
|
+
if method_defined?(name) || method_defined?("#{name}=")
|
35
|
+
raise "accessor or getter already defined for #{name}. Fix the collions by using the `:value` option."
|
36
|
+
else
|
37
|
+
attr_accessor name
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# a where clause is built up of multiple 'relations'
|
43
|
+
def relations
|
44
|
+
self.class.relations
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_where_and_bindings
|
48
|
+
cql = ""
|
49
|
+
bindings = []
|
50
|
+
relation_strings = []
|
51
|
+
|
52
|
+
relations.each do |r, opts|
|
53
|
+
if eval_if_opt?(opts[:if])
|
54
|
+
relation_strings << r.to_cql
|
55
|
+
bindings << eval_value_opt(opts[:value])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
cql = "WHERE #{relation_strings.join(' AND ')}" unless bindings.empty?
|
60
|
+
|
61
|
+
[cql , bindings]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require_relative 'relations'
|
2
|
+
require_relative 'limiting'
|
3
|
+
require_relative 'ordering'
|
4
|
+
require_relative 'fetching'
|
5
|
+
|
6
|
+
module Cassie::Queries::Statement
|
7
|
+
module Selection
|
8
|
+
extend ::ActiveSupport::Concern
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
#TODO: accept block to add specific selectors and aliases
|
12
|
+
# select :table do |t|
|
13
|
+
# t.id
|
14
|
+
# t.name as: :username
|
15
|
+
# end
|
16
|
+
def select(table)
|
17
|
+
include Relations
|
18
|
+
include Limiting
|
19
|
+
include Ordering
|
20
|
+
include Fetching
|
21
|
+
|
22
|
+
self.table = table
|
23
|
+
self.identifier = :select
|
24
|
+
|
25
|
+
yield(self) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
# a select clause is built up of selectors
|
29
|
+
def selectors
|
30
|
+
@selectors ||= []
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def build_select_cql_and_bindings
|
37
|
+
where_str, bindings = build_where_and_bindings
|
38
|
+
|
39
|
+
cql = %(
|
40
|
+
SELECT #{build_select_clause}
|
41
|
+
FROM #{table}
|
42
|
+
#{where_str}
|
43
|
+
#{build_order_str}
|
44
|
+
#{build_limit_str}
|
45
|
+
).squish + ";"
|
46
|
+
|
47
|
+
[cql, bindings]
|
48
|
+
end
|
49
|
+
|
50
|
+
# a select clause is built up of selectors
|
51
|
+
def selectors
|
52
|
+
self.class.selectors
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_select_clause
|
56
|
+
str = if selectors.empty?
|
57
|
+
'*'
|
58
|
+
else
|
59
|
+
selectors.join(', ')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'assignments'
|
2
|
+
|
3
|
+
module Cassie::Queries::Statement
|
4
|
+
module Updating
|
5
|
+
extend ::ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def update(table)
|
9
|
+
include Relations
|
10
|
+
include Assignments
|
11
|
+
|
12
|
+
self.table = table
|
13
|
+
self.identifier = :update
|
14
|
+
|
15
|
+
yield(self) if block_given?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def update(opts={})
|
20
|
+
execute
|
21
|
+
execution_successful?
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def build_update_cql_and_bindings
|
27
|
+
assignment_str, update_bindings = build_update_and_bindings
|
28
|
+
where_str, where_bindings = build_where_and_bindings
|
29
|
+
|
30
|
+
cql = %(
|
31
|
+
UPDATE #{table}
|
32
|
+
SET #{assignment_str}
|
33
|
+
#{where_str}
|
34
|
+
).squish + ";"
|
35
|
+
|
36
|
+
[cql, update_bindings + where_bindings]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,48 +1,60 @@
|
|
1
1
|
require 'active_support/core_ext/string/filters'
|
2
|
+
require_relative 'statement/preparation'
|
3
|
+
require_relative 'statement/selection'
|
4
|
+
require_relative 'statement/deleting'
|
5
|
+
require_relative 'statement/updating'
|
6
|
+
require_relative 'statement/inserting'
|
7
|
+
|
2
8
|
|
3
9
|
module Cassie::Queries
|
4
10
|
module Statement
|
11
|
+
# https://cassandra.apache.org/doc/cql3/CQL.html#selectStmt
|
5
12
|
extend ::ActiveSupport::Concern
|
6
13
|
|
7
14
|
included do
|
15
|
+
include Preparation
|
16
|
+
include Selection
|
17
|
+
include Deleting
|
18
|
+
include Updating
|
19
|
+
include Inserting
|
20
|
+
|
8
21
|
attr_reader :result
|
9
|
-
end
|
10
22
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
raise "a cql statement has already been defined and cannot be changed"
|
15
|
-
else
|
16
|
-
self.const_set(:STATEMENT, statement.squish)
|
17
|
-
end
|
23
|
+
class << self
|
24
|
+
attr_accessor :table
|
25
|
+
attr_accessor :identifier
|
18
26
|
end
|
27
|
+
end
|
19
28
|
|
20
|
-
|
21
|
-
|
22
|
-
rescue NameError
|
23
|
-
end
|
29
|
+
def table
|
30
|
+
self.class.table
|
24
31
|
end
|
25
32
|
|
26
33
|
# Executes the statment, populates result
|
27
34
|
# returns true or false indicating a successful execution or not
|
28
35
|
def execute
|
29
|
-
@result = session.execute(statement
|
36
|
+
@result = session.execute(statement)
|
30
37
|
execution_successful?
|
31
38
|
end
|
32
39
|
|
40
|
+
# returns a CQL string, or a Cassandra::Statement
|
41
|
+
# that is ready for execution
|
33
42
|
def statement
|
34
|
-
|
43
|
+
Cassandra::Statements::Simple.new(*build_cql_and_bindings)
|
35
44
|
end
|
36
45
|
|
37
46
|
protected
|
38
47
|
|
39
|
-
def
|
40
|
-
|
48
|
+
def build_cql_and_bindings
|
49
|
+
if identifier
|
50
|
+
send "build_#{identifier}_cql_and_bindings"
|
51
|
+
else
|
52
|
+
[cql, bindings]
|
53
|
+
end
|
41
54
|
end
|
42
55
|
|
43
|
-
private
|
44
|
-
|
45
56
|
def execution_successful?
|
57
|
+
#TODO: rethink this, it knows too much
|
46
58
|
raise "execution not complete, no results to parse" unless result
|
47
59
|
|
48
60
|
# empty select
|
@@ -53,5 +65,35 @@ module Cassie::Queries
|
|
53
65
|
|
54
66
|
true
|
55
67
|
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def identifier
|
72
|
+
self.class.identifier
|
73
|
+
end
|
74
|
+
|
75
|
+
def eval_if_opt?(value)
|
76
|
+
case value
|
77
|
+
when nil
|
78
|
+
true # if is true by default
|
79
|
+
when Symbol
|
80
|
+
!!send(value)
|
81
|
+
when String
|
82
|
+
!!eval(value)
|
83
|
+
else
|
84
|
+
!!value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def eval_value_opt(value)
|
89
|
+
case value
|
90
|
+
when Symbol
|
91
|
+
send(value)
|
92
|
+
when String
|
93
|
+
eval(value)
|
94
|
+
else
|
95
|
+
value
|
96
|
+
end
|
97
|
+
end
|
56
98
|
end
|
57
99
|
end
|
data/lib/cassie/query.rb
CHANGED
@@ -2,15 +2,24 @@ module Cassie
|
|
2
2
|
require 'cassandra'
|
3
3
|
require_relative 'queries/session'
|
4
4
|
require_relative 'queries/statement'
|
5
|
-
require_relative 'queries/
|
5
|
+
require_relative 'queries/pagination'
|
6
6
|
require_relative 'queries/instrumentation'
|
7
7
|
require_relative 'queries/logging'
|
8
8
|
|
9
9
|
class Query
|
10
10
|
include Queries::Session
|
11
11
|
include Queries::Statement
|
12
|
-
include Queries::
|
12
|
+
include Queries::Pagination
|
13
13
|
include Queries::Instrumentation
|
14
14
|
include Queries::Logging
|
15
|
+
|
16
|
+
def initialize(*args)
|
17
|
+
value = super(*args)
|
18
|
+
after_initialize(*args)
|
19
|
+
value
|
20
|
+
end
|
21
|
+
|
22
|
+
def after_initialize(*args)
|
23
|
+
end
|
15
24
|
end
|
16
25
|
end
|
metadata
CHANGED
@@ -1,88 +1,119 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cassie-queries
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.a4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Prothro
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-12-
|
11
|
+
date: 2015-12-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cassandra-driver
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '2.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '2.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: activesupport
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '4.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '4.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - ~>
|
45
|
+
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '3.4'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - ~>
|
52
|
+
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.4'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: byebug
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
|
70
|
-
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: benchmark-ips
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: "`cassie-queries` is a lightweight interface adapter allowing easy use
|
84
|
+
of the functionality provided by the `cassandra-driver`."
|
71
85
|
email: evan.prothro@gmail.com
|
72
86
|
executables: []
|
73
87
|
extensions: []
|
74
88
|
extra_rdoc_files: []
|
75
89
|
files:
|
90
|
+
- lib/cassie-queries.rb
|
76
91
|
- lib/cassie/queries/instrumentation.rb
|
92
|
+
- lib/cassie/queries/logging.rb
|
77
93
|
- lib/cassie/queries/logging/cql_execution_event.rb
|
78
94
|
- lib/cassie/queries/logging/logger.rb
|
79
95
|
- lib/cassie/queries/logging/subscription.rb
|
80
|
-
- lib/cassie/queries/
|
81
|
-
- lib/cassie/queries/
|
96
|
+
- lib/cassie/queries/pagination.rb
|
97
|
+
- lib/cassie/queries/pagination/cursors.rb
|
98
|
+
- lib/cassie/queries/pagination/page_size.rb
|
82
99
|
- lib/cassie/queries/session.rb
|
83
100
|
- lib/cassie/queries/statement.rb
|
101
|
+
- lib/cassie/queries/statement/assignment.rb
|
102
|
+
- lib/cassie/queries/statement/assignments.rb
|
103
|
+
- lib/cassie/queries/statement/deleting.rb
|
104
|
+
- lib/cassie/queries/statement/fetching.rb
|
105
|
+
- lib/cassie/queries/statement/inserting.rb
|
106
|
+
- lib/cassie/queries/statement/limiting.rb
|
107
|
+
- lib/cassie/queries/statement/loading.rb
|
108
|
+
- lib/cassie/queries/statement/mapping.rb
|
109
|
+
- lib/cassie/queries/statement/ordering.rb
|
110
|
+
- lib/cassie/queries/statement/preparation.rb
|
111
|
+
- lib/cassie/queries/statement/preparation/cache.rb
|
112
|
+
- lib/cassie/queries/statement/relation.rb
|
113
|
+
- lib/cassie/queries/statement/relations.rb
|
114
|
+
- lib/cassie/queries/statement/selection.rb
|
115
|
+
- lib/cassie/queries/statement/updating.rb
|
84
116
|
- lib/cassie/query.rb
|
85
|
-
- lib/cassie-queries.rb
|
86
117
|
homepage: https://github.com/eprothro/cassie-queries
|
87
118
|
licenses:
|
88
119
|
- MIT
|
@@ -93,17 +124,17 @@ require_paths:
|
|
93
124
|
- lib
|
94
125
|
required_ruby_version: !ruby/object:Gem::Requirement
|
95
126
|
requirements:
|
96
|
-
- -
|
127
|
+
- - ">="
|
97
128
|
- !ruby/object:Gem::Version
|
98
129
|
version: '0'
|
99
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
131
|
requirements:
|
101
|
-
- -
|
132
|
+
- - ">"
|
102
133
|
- !ruby/object:Gem::Version
|
103
134
|
version: 1.3.1
|
104
135
|
requirements: []
|
105
136
|
rubyforge_project:
|
106
|
-
rubygems_version: 2.
|
137
|
+
rubygems_version: 2.5.1
|
107
138
|
signing_key:
|
108
139
|
specification_version: 4
|
109
140
|
summary: Apache Cassandra query management
|
@@ -1,40 +0,0 @@
|
|
1
|
-
module Cassie::Queries
|
2
|
-
module PreparedStatement
|
3
|
-
extend ::ActiveSupport::Concern
|
4
|
-
|
5
|
-
included do
|
6
|
-
class << self
|
7
|
-
attr_accessor :prepare
|
8
|
-
end
|
9
|
-
self.prepare = true
|
10
|
-
end
|
11
|
-
|
12
|
-
module ClassMethods
|
13
|
-
def inherited(subclass)
|
14
|
-
subclass.prepare = prepare
|
15
|
-
end
|
16
|
-
|
17
|
-
def prepare?
|
18
|
-
!!prepare
|
19
|
-
end
|
20
|
-
|
21
|
-
def prepared_statement
|
22
|
-
# use class instance variable to esnure only 1
|
23
|
-
# statement is prepared per process
|
24
|
-
# no mutex required in MRI because of GIL
|
25
|
-
#
|
26
|
-
# note: cassandra-driver handles the case
|
27
|
-
# of executing a prepared statement
|
28
|
-
# on a host where it has not been prepared
|
29
|
-
# yet, by re-preparing.
|
30
|
-
@prepared_statement ||= begin
|
31
|
-
session.prepare(statement)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def statement
|
37
|
-
self.class.prepare? ? self.class.prepared_statement : super
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|