querybuilder 1.1.3 → 1.1.4
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.
- data/History.txt +5 -0
- data/lib/query_builder/info.rb +1 -1
- data/lib/query_builder/processor.rb +10 -12
- data/lib/query_builder/query.rb +36 -14
- data/querybuilder.gemspec +2 -2
- data/test/database.rb +1 -0
- data/test/mock/dummy_processor.rb +1 -2
- data/test/querybuilder/basic.yml +1 -1
- data/test/querybuilder/filters.yml +9 -9
- metadata +4 -4
data/History.txt
CHANGED
data/lib/query_builder/info.rb
CHANGED
@@ -791,16 +791,14 @@ module QueryBuilder
|
|
791
791
|
end
|
792
792
|
|
793
793
|
# Add a new table and apply scoping when needed
|
794
|
-
def add_table(use_name, table_name = nil)
|
795
|
-
if use_name == main_table && first?
|
796
|
-
# we are now using final table
|
797
|
-
context[:table_alias] = use_name
|
798
|
-
avoid_alias = true
|
799
|
-
else
|
800
|
-
avoid_alias = false
|
801
|
-
end
|
802
|
-
|
794
|
+
def add_table(use_name, table_name = nil, type = nil, &block)
|
803
795
|
if use_name == main_table
|
796
|
+
if first?
|
797
|
+
# we are now using final table
|
798
|
+
context[:table_alias] = use_name
|
799
|
+
avoid_alias = true
|
800
|
+
end
|
801
|
+
|
804
802
|
if context[:scope_type] == :join
|
805
803
|
context[:scope_type] = nil
|
806
804
|
# pre scope
|
@@ -808,7 +806,7 @@ module QueryBuilder
|
|
808
806
|
@query.add_table(main_table, main_table, avoid_alias)
|
809
807
|
apply_scope(context[:scope])
|
810
808
|
end
|
811
|
-
@query.add_table(use_name, table_name, avoid_alias)
|
809
|
+
@query.add_table(use_name, table_name, avoid_alias, type, &block)
|
812
810
|
elsif context[:scope_type] == :filter
|
813
811
|
context[:scope_type] = nil
|
814
812
|
# post scope
|
@@ -816,12 +814,12 @@ module QueryBuilder
|
|
816
814
|
apply_scope(context[:scope] || default_scope(context))
|
817
815
|
else
|
818
816
|
# scope already applied / skip
|
819
|
-
@query.add_table(use_name, table_name, avoid_alias)
|
817
|
+
@query.add_table(use_name, table_name, avoid_alias, type, &block)
|
820
818
|
end
|
821
819
|
else
|
822
820
|
# no scope
|
823
821
|
# can only scope main_table
|
824
|
-
@query.add_table(use_name, table_name)
|
822
|
+
@query.add_table(use_name, table_name, true, type, &block)
|
825
823
|
end
|
826
824
|
end
|
827
825
|
|
data/lib/query_builder/query.rb
CHANGED
@@ -119,9 +119,9 @@ module QueryBuilder
|
|
119
119
|
|
120
120
|
# 'avoid_alias' is used when parsing the last element so that it takes the real table name (nodes, not no1). We need
|
121
121
|
# this because we can use 'OR' between parts and we thus need the same table reference.
|
122
|
-
def add_table(use_name, table_name = nil, avoid_alias = true)
|
122
|
+
def add_table(use_name, table_name = nil, avoid_alias = true, type = nil, &block)
|
123
123
|
alias_name = get_alias(use_name, table_name, avoid_alias)
|
124
|
-
add_alias_to_tables(table_name || use_name, alias_name)
|
124
|
+
add_alias_to_tables(table_name || use_name, alias_name, type, &block)
|
125
125
|
end
|
126
126
|
|
127
127
|
# Add a table to 'import' a key/value based field. This method ensures that
|
@@ -133,10 +133,8 @@ module QueryBuilder
|
|
133
133
|
# done, the index_table has been used for the given key in the current context
|
134
134
|
else
|
135
135
|
# insert the new table
|
136
|
-
add_table(use_name, index_table, false)
|
136
|
+
add_table(use_name, index_table, false, :left, &block)
|
137
137
|
alias_table = key_table[key] = table(use_name)
|
138
|
-
# Let caller configure the filter (join).
|
139
|
-
block.call(alias_table)
|
140
138
|
end
|
141
139
|
alias_table
|
142
140
|
end
|
@@ -225,27 +223,51 @@ module QueryBuilder
|
|
225
223
|
def quote_column_name(name)
|
226
224
|
connection.quote_column_name(name)
|
227
225
|
end
|
228
|
-
|
226
|
+
|
227
|
+
def has_table?(use_name)
|
228
|
+
!@table_alias[use_name].nil?
|
229
|
+
end
|
230
|
+
|
231
|
+
|
229
232
|
private
|
230
|
-
# Make sure each used table gets a unique name
|
233
|
+
# Make sure each used table gets a unique name. By default, this uses an alias
|
234
|
+
# But if 'avoid_alias' is true and it is the first call for this table, return the
|
235
|
+
# table without an alias.
|
231
236
|
def get_alias(use_name, table_name = nil, avoid_alias = true)
|
232
237
|
table_name ||= use_name
|
233
|
-
@table_alias[use_name] ||= []
|
234
|
-
if avoid_alias &&
|
238
|
+
list = (@table_alias[use_name] ||= [])
|
239
|
+
if avoid_alias && !list.include?(use_name)
|
235
240
|
alias_name = use_name
|
236
241
|
elsif @tables.include?(use_name)
|
237
242
|
# links, li1, li2, li3
|
238
|
-
alias_name = "#{use_name[0..1]}#{
|
243
|
+
alias_name = "#{use_name[0..1]}#{list.size}"
|
239
244
|
else
|
240
245
|
# ob1, obj2, objects
|
241
|
-
alias_name = "#{use_name[0..1]}#{
|
246
|
+
alias_name = "#{use_name[0..1]}#{list.size + 1}"
|
242
247
|
end
|
243
248
|
|
244
|
-
|
249
|
+
list << alias_name
|
245
250
|
alias_name
|
246
251
|
end
|
247
252
|
|
248
|
-
def add_alias_to_tables(table_name, alias_name)
|
253
|
+
def add_alias_to_tables(table_name, alias_name, type = nil, &block)
|
254
|
+
if type
|
255
|
+
key = "#{table_name}=#{type}=#{alias_name}"
|
256
|
+
|
257
|
+
@needed_join_tables[key] ||= begin
|
258
|
+
clause = "#{type.to_s.upcase} JOIN #{table_name}"
|
259
|
+
if alias_name && alias_name != table_name
|
260
|
+
clause << " AS #{alias_name}"
|
261
|
+
end
|
262
|
+
if block_given?
|
263
|
+
clause << " ON #{block.call(alias_name || table_name)}"
|
264
|
+
end
|
265
|
+
joins = (@join_tables[main_table] ||= [])
|
266
|
+
joins << clause
|
267
|
+
end
|
268
|
+
return
|
269
|
+
end
|
270
|
+
|
249
271
|
if alias_name != table_name
|
250
272
|
@tables << "#{table_name} AS #{alias_name}"
|
251
273
|
else
|
@@ -278,7 +300,7 @@ module QueryBuilder
|
|
278
300
|
|
279
301
|
group = @group
|
280
302
|
if !group && @distinct
|
281
|
-
key =
|
303
|
+
key = "#{main_table}.id"
|
282
304
|
|
283
305
|
case self.class.adapter
|
284
306
|
when 'postgresql'
|
data/querybuilder.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{querybuilder}
|
8
|
-
s.version = "1.1.
|
8
|
+
s.version = "1.1.4"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Gaspard Bucher"]
|
12
|
-
s.date = %q{2012-
|
12
|
+
s.date = %q{2012-10-10}
|
13
13
|
s.description = %q{QueryBuilder is an interpreter for the "pseudo sql" language. This language
|
14
14
|
can be used for two purposes:
|
15
15
|
|
data/test/database.rb
CHANGED
@@ -35,8 +35,7 @@ class DummyProcessor < QueryBuilder::Processor
|
|
35
35
|
elsif %w{age size}.include?(field_name)
|
36
36
|
tbl = add_key_value_table('idx', 'idx_nodes', field_name) do |tbl_name|
|
37
37
|
# This block is only executed once
|
38
|
-
|
39
|
-
add_filter "#{tbl_name}.key = #{quote(field_name)}"
|
38
|
+
"#{tbl_name}.node_id = #{table}.id AND #{tbl_name}.key = #{quote(field_name)}"
|
40
39
|
end
|
41
40
|
|
42
41
|
"#{tbl}.value"
|
data/test/querybuilder/basic.yml
CHANGED
@@ -89,7 +89,7 @@ after_process_callback:
|
|
89
89
|
context:
|
90
90
|
after_filter: '(1 = 1)'
|
91
91
|
src: "objects where name like 'a%' or name like 'b%' in site"
|
92
|
-
res: "%Q{SELECT objects.* FROM objects WHERE (1 = 1) AND (objects.name LIKE 'a%' OR objects.name LIKE 'b%') GROUP BY id}"
|
92
|
+
res: "%Q{SELECT objects.* FROM objects WHERE (1 = 1) AND (objects.name LIKE 'a%' OR objects.name LIKE 'b%') GROUP BY objects.id}"
|
93
93
|
|
94
94
|
function:
|
95
95
|
src: "objects where event_at.year = 2005"
|
@@ -93,36 +93,36 @@ equation_with_date_interval:
|
|
93
93
|
equation_and_or_par:
|
94
94
|
src: "objects where event_at > '2006-04-01' or name like 'foo%'"
|
95
95
|
sxp: '[:query, [:filter, [:relation, "objects"], [:or, [:>, [:field, "event_at"], [:string, "2006-04-01"]], [:like, [:field, "name"], [:string, "foo%"]]]]]'
|
96
|
-
res: "[%Q{SELECT objects.* FROM objects WHERE (objects.event_at > '2006-04-01' OR objects.name LIKE 'foo%') AND objects.parent_id = ? GROUP BY id}, id]"
|
96
|
+
res: "[%Q{SELECT objects.* FROM objects WHERE (objects.event_at > '2006-04-01' OR objects.name LIKE 'foo%') AND objects.parent_id = ? GROUP BY objects.id}, id]"
|
97
97
|
|
98
98
|
or_with_same_tables:
|
99
99
|
src: "objects where age = 5 or age = 7"
|
100
|
-
res: "[%Q{SELECT objects.* FROM idx_nodes AS id1
|
100
|
+
res: "[%Q{SELECT objects.* FROM objects LEFT JOIN idx_nodes AS id1 ON id1.node_id = objects.id AND id1.key = 'age' WHERE (id1.value = 5 OR id1.value = 7) AND objects.parent_id = ? GROUP BY objects.id}, id]"
|
101
101
|
|
102
102
|
or_with_missing_table:
|
103
|
-
src: "objects where age = 5 or 7"
|
104
|
-
res: "[%Q{SELECT objects.* FROM idx_nodes AS id1
|
103
|
+
src: "objects where age = 5 or id = 7"
|
104
|
+
res: "[%Q{SELECT objects.* FROM objects LEFT JOIN idx_nodes AS id1 ON id1.node_id = objects.id AND id1.key = 'age' WHERE (id1.value = 5 OR objects.id = 7) AND objects.parent_id = ? GROUP BY objects.id}, id]"
|
105
105
|
|
106
106
|
or_different_table:
|
107
107
|
src: "objects where age = 5 or size = 15"
|
108
|
-
res: "[%Q{SELECT objects.* FROM idx_nodes AS id1
|
108
|
+
res: "[%Q{SELECT objects.* FROM objects LEFT JOIN idx_nodes AS id1 ON id1.node_id = objects.id AND id1.key = 'age' WHERE (id1.value = 5 OR id1.value = 15) AND objects.parent_id = ? GROUP BY objects.id}, id]"
|
109
109
|
|
110
110
|
and_with_same_tables:
|
111
111
|
src: "objects where age > 5 and age < 7"
|
112
|
-
res: "[%Q{SELECT objects.* FROM idx_nodes AS id1
|
112
|
+
res: "[%Q{SELECT objects.* FROM objects LEFT JOIN idx_nodes AS id1 ON id1.node_id = objects.id AND id1.key = 'age' WHERE id1.value > 5 AND id1.value < 7 AND objects.parent_id = ?}, id]"
|
113
113
|
|
114
114
|
and_with_same_tables_different_key:
|
115
115
|
src: "objects where age > 5 and size = 10"
|
116
|
-
res: "[%Q{SELECT objects.* FROM
|
116
|
+
res: "[%Q{SELECT objects.* FROM objects LEFT JOIN idx_nodes AS id1 ON id1.node_id = objects.id AND id1.key = 'age' LEFT JOIN idx_nodes AS id2 ON id2.node_id = objects.id AND id2.key = 'size' WHERE id1.value > 5 AND id2.value = 10 AND objects.parent_id = ?}, id]"
|
117
117
|
|
118
118
|
|
119
119
|
filter_and_order_index:
|
120
120
|
src: "objects where age > 5 order by age asc"
|
121
|
-
res: "[%Q{SELECT objects.* FROM idx_nodes AS id1
|
121
|
+
res: "[%Q{SELECT objects.* FROM objects LEFT JOIN idx_nodes AS id1 ON id1.node_id = objects.id AND id1.key = 'age' WHERE id1.value > 5 AND objects.parent_id = ? ORDER BY id1.value ASC}, id]"
|
122
122
|
|
123
123
|
order_without_idx_filter:
|
124
124
|
src: "objects order by age asc"
|
125
|
-
res: "[%Q{SELECT objects.* FROM idx_nodes AS id1
|
125
|
+
res: "[%Q{SELECT objects.* FROM objects LEFT JOIN idx_nodes AS id1 ON id1.node_id = objects.id AND id1.key = 'age' WHERE objects.parent_id = ? ORDER BY id1.value ASC}, id]"
|
126
126
|
|
127
127
|
equation_par:
|
128
128
|
src: "objects where (1 > 2 or 2 > 3) and 4 = 5 "
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: querybuilder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 1.1.
|
9
|
+
- 4
|
10
|
+
version: 1.1.4
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Gaspard Bucher
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-10-10 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|