querybuilder 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/QueryBuilder.rb'}"
9
+ puts "Loading QueryBuilder gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,66 @@
1
+ empty:
2
+ src: ""
3
+ res: "[\"SELECT objects.* FROM objects WHERE objects.parent_id = ?\", id]"
4
+
5
+ objects:
6
+ res: "[\"SELECT objects.* FROM objects WHERE objects.parent_id = ?\", id]"
7
+
8
+ objects_in_project:
9
+ res: "[\"SELECT objects.* FROM objects WHERE objects.project_id = ?\", project_id]"
10
+
11
+ objects_in_site:
12
+ res: "\"SELECT objects.* FROM objects\""
13
+
14
+ recipients:
15
+ res: "[\"SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ?\", id]"
16
+ sql: "SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = [[123]]"
17
+
18
+ letters_in_project:
19
+ res: "[\"SELECT objects.* FROM objects WHERE objects.kpath LIKE 'NNL%' AND objects.project_id = ?\", project_id]"
20
+
21
+ parent:
22
+ res: "[\"SELECT objects.* FROM objects WHERE objects.id = ?\", parent_id]"
23
+
24
+ order_single_table:
25
+ src: "objects in project order by name ASC"
26
+ res: "[\"SELECT objects.* FROM objects WHERE objects.project_id = ? ORDER BY objects.name ASC\", project_id]"
27
+
28
+ order_many_tables:
29
+ src: "recipients order by name ASC"
30
+ res: "[\"SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ? ORDER BY objects.name ASC\", id]"
31
+
32
+ order_many_params:
33
+ src: "objects in project order by name ASC, id DESC"
34
+ res: "[\"SELECT objects.* FROM objects WHERE objects.project_id = ? ORDER BY objects.name ASC, objects.id DESC\", project_id]"
35
+
36
+ limit:
37
+ src: "objects in project limit 2"
38
+ res: "[\"SELECT objects.* FROM objects WHERE objects.project_id = ? LIMIT 2\", project_id]"
39
+
40
+ offset_in_limit:
41
+ src: "objects in project limit 3,2"
42
+ res: "[\"SELECT objects.* FROM objects WHERE objects.project_id = ? LIMIT 2 OFFSET 3\", project_id]"
43
+
44
+ offset:
45
+ src: "objects in project limit 2 offset 3"
46
+ res: "[\"SELECT objects.* FROM objects WHERE objects.project_id = ? LIMIT 2 OFFSET 3\", project_id]"
47
+
48
+ paginate:
49
+ src: "objects in site limit 2 paginate p"
50
+ res: "[\"SELECT objects.* FROM objects LIMIT 2 OFFSET ?\", ((p.to_i > 0 ? p.to_i : 1)-1)*2]"
51
+
52
+ recipients_or_objects:
53
+ src:
54
+ - "recipients"
55
+ - "objects"
56
+ res: "[\"SELECT objects.* FROM objects,links WHERE ((objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ?) OR (objects.parent_id = ?)) GROUP BY objects.id\", id, id]"
57
+
58
+ custom_query:
59
+ context:
60
+ custom_query_group: test
61
+ src: "abc"
62
+ res: "\"SELECT a,34 AS number,c FROM test WHERE 3 AND 2 AND 1 ORDER BY a\""
63
+
64
+ count_sql:
65
+ src: "objects in project"
66
+ count: "[\"SELECT COUNT(*) FROM objects WHERE objects.project_id = ?\", project_id]"
@@ -0,0 +1,50 @@
1
+ bad_relation:
2
+ src: "bolobolo"
3
+ res: "unknown relation 'bolobolo'"
4
+
5
+ bad_relation_in_alternate_query:
6
+ src:
7
+ - "bolobolo"
8
+ - "letters in project"
9
+ res: "unknown relation 'bolobolo'"
10
+
11
+ order:
12
+ src: "objects in project order by bad ASC"
13
+ res: "invalid field 'bad'"
14
+
15
+ offset_without_limit:
16
+ src: "objects in project offset 3"
17
+ res: "invalid offset clause '3' (used without limit)"
18
+
19
+ ignore_warnings:
20
+ context:
21
+ ignore_warnings: yes
22
+ src:
23
+ - 'letters'
24
+ - 'foo'
25
+ res: "[\"SELECT objects.* FROM objects WHERE objects.kpath LIKE 'NNL%' AND objects.parent_id = ?\", id]"
26
+
27
+ ignore_warnings_all_bad:
28
+ context:
29
+ ignore_warnings: yes
30
+ src:
31
+ - 'baz'
32
+ - 'foo'
33
+ res: "\"SELECT objects.* FROM objects WHERE 0\""
34
+
35
+ do_not_ignore_warnings:
36
+ context:
37
+ ignore_warnings: no
38
+ src:
39
+ - 'letters'
40
+ - 'foo'
41
+ res: "unknown relation 'foo'"
42
+
43
+ bad_equation:
44
+ src: "objects where event_at > 2006-04-01'"
45
+ res: "invalid clause \"event_at > 2006-04-01'\" near \"06-0\""
46
+
47
+ bad_plus_plus:
48
+ src: "objects where 1 + 3 + + 5 > event_at"
49
+ res: "invalid clause \"1 + 3 + + 5 > event_at\" near \"+ + \""
50
+
@@ -0,0 +1,43 @@
1
+ simple:
2
+ src: "objects where name = 'foo' in site"
3
+ res: "[\"SELECT objects.* FROM objects WHERE objects.name = ?\", \"foo\"]"
4
+
5
+ recipients_that_are_clients:
6
+ src: "recipients where kpath like 'NRCC%'"
7
+ res: "[\"SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ? AND objects.kpath LIKE ?\", id, \"NRCC%\"]"
8
+
9
+ pages_in_site_name_like:
10
+ src: "letters where name like 'a%' in site"
11
+ res: "[\"SELECT objects.* FROM objects WHERE objects.kpath LIKE 'NNL%' AND objects.name LIKE ?\", \"a%\"]"
12
+
13
+ pages_in_site_name_not_like:
14
+ src: "letters where name not like 'a%' in site"
15
+ res: "[\"SELECT objects.* FROM objects WHERE objects.kpath LIKE 'NNL%' AND objects.name NOT LIKE ?\", \"a%\"]"
16
+
17
+ letters_group_by_name:
18
+ res: "[\"SELECT objects.* FROM objects WHERE objects.kpath LIKE 'NNL%' AND objects.parent_id = ? GROUP BY objects.name\", id]"
19
+
20
+ recipients_group_by_name:
21
+ res: "[\"SELECT objects.* FROM objects,links WHERE objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ? GROUP BY objects.name\", id]"
22
+
23
+ custom_query_having:
24
+ context:
25
+ custom_query_group: test
26
+ src: "abc where number > 12"
27
+ res: "\"SELECT a,34 AS number,c FROM test WHERE (34) > 12 AND 3 AND 2 AND 1 ORDER BY a\""
28
+
29
+ equation_in_filter:
30
+ src: "objects where event_at > REF_DATE + custom_a months"
31
+ res: "[\"SELECT objects.* FROM objects WHERE objects.parent_id = ? AND objects.event_at > now() + INTERVAL objects.custom_a MONTH\", id]"
32
+
33
+ equation_in_filter_date:
34
+ src: "objects where event_at > '2006-04-01' + 6 week"
35
+ res: "[\"SELECT objects.* FROM objects WHERE objects.parent_id = ? AND objects.event_at > ? + INTERVAL 6 WEEK\", id, \"2006-04-01\"]"
36
+
37
+ equation_and_or_par:
38
+ src: "objects where event_at > '2006-04-01' or name like 'foo%'"
39
+ res: "[\"SELECT objects.* FROM objects WHERE objects.parent_id = ? AND (objects.event_at > ? OR objects.name LIKE ?)\", id, \"2006-04-01\", \"foo%\"]"
40
+
41
+ equation_par:
42
+ src: "objects where (1 > 2 and 2 > 3) or 4 = 5"
43
+ res: "[\"SELECT objects.* FROM objects WHERE objects.parent_id = ? AND ((1 > 2 AND 2 > 3) OR 4 = 5)\", id]"
@@ -0,0 +1,25 @@
1
+
2
+ icons_from_recipients:
3
+ res: "[\"SELECT ob1.* FROM objects,links,objects AS ob1,links AS li1 WHERE ob1.id = li1.source_id AND li1.relation_id = 5 AND li1.target_id = objects.id AND objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ? GROUP BY ob1.id\", id]"
4
+
5
+ letters_from_recipients:
6
+ res: "[\"SELECT ob1.* FROM objects,links,objects AS ob1 WHERE ob1.kpath LIKE 'NNL%' AND ob1.parent_id = objects.id AND objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ? GROUP BY ob1.id\", id]"
7
+
8
+ objects_from_recipients:
9
+ res: "[\"SELECT ob1.* FROM objects,links,objects AS ob1 WHERE ob1.parent_id = objects.id AND objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ? GROUP BY ob1.id\", id]"
10
+
11
+ parent_from_parent:
12
+ res: "[\"SELECT ob1.* FROM objects,objects AS ob1 WHERE ob1.id = objects.parent_id AND objects.id = ? GROUP BY ob1.id\", parent_id]"
13
+
14
+ children_from_objects_in_project:
15
+ res: "[\"SELECT ob1.* FROM objects,objects AS ob1 WHERE ob1.parent_id = objects.id AND objects.project_id = ? GROUP BY ob1.id\", project_id]"
16
+
17
+ tags:
18
+ res: "\"SELECT objects.* FROM objects INNER JOIN tags ON objects.id = tags.node_id\""
19
+
20
+ letters_in_project_from_letters:
21
+ res: "[\"SELECT ob1.* FROM objects,objects AS ob1 WHERE ob1.kpath LIKE 'NNL%' AND ob1.project_id = objects.id AND objects.kpath LIKE 'NNL%' AND objects.parent_id = ? GROUP BY ob1.id\", id]"
22
+
23
+ letters_in_project_from_letters_group_by:
24
+ src: "letters in project from letters group by name,id"
25
+ res: "[\"SELECT ob1.* FROM objects,objects AS ob1 WHERE ob1.kpath LIKE 'NNL%' AND ob1.project_id = objects.id AND objects.kpath LIKE 'NNL%' AND objects.parent_id = ? GROUP BY ob1.name, ob1.id\", id]"
@@ -0,0 +1,12 @@
1
+ users_from_objects:
2
+ res: "TestUser: [\"SELECT users.* FROM objects,users WHERE users.node_id = objects.id AND objects.parent_id = ? ORDER BY users.name ASC, users.first_name ASC\", id]"
3
+
4
+ users_where_name_from_objects:
5
+ src: "users where name eq 'bob' from objects"
6
+ res: "TestUser: [\"SELECT users.* FROM objects,users WHERE users.name = ? AND users.node_id = objects.id AND objects.parent_id = ? ORDER BY users.name ASC, users.first_name ASC\", \"bob\", id]"
7
+
8
+ objects_from_users_from_objects:
9
+ res: "TestUser: [\"SELECT users.* FROM objects,users,objects AS ob1 WHERE ob1.id = users.node_id AND users.node_id = objects.id AND objects.parent_id = ? ORDER BY users.name ASC, users.first_name ASC\", id]"
10
+
11
+ users:
12
+ res: "TestUser: [\"SELECT users.* FROM users WHERE users.node_id = objects.id AND objects.parent_id = ? ORDER BY users.name ASC, users.first_name ASC\", id]"
@@ -0,0 +1,14 @@
1
+ DummyQuery:
2
+ # form_dates
3
+ abc:
4
+ select:
5
+ - 'a'
6
+ - '34 AS number'
7
+ - 'c'
8
+ tables:
9
+ - 'test'
10
+ where:
11
+ - '1'
12
+ - '2'
13
+ - '3'
14
+ order: 'a'
@@ -0,0 +1,114 @@
1
+ class DummyQuery < QueryBuilder
2
+ set_main_table 'objects'
3
+ set_main_class 'DummyQueryClass'
4
+
5
+ load_custom_queries File.join(File.dirname(__FILE__), 'custom_queries')
6
+
7
+ # Build joins and filters from a relation.
8
+ def parse_relation(rel, context)
9
+ unless context_relation(rel, context) || direct_filter(rel, context) || join_relation(rel, context)
10
+ @errors << "unknown relation '#{rel}'"
11
+ end
12
+ end
13
+
14
+ # default context filter is to search in the current node's children (in self)
15
+ def default_context_filter
16
+ 'self'
17
+ end
18
+
19
+ private
20
+
21
+ # Root filters (relations that can be solved without a join). Think 'in clause' (in self, in parent).
22
+ def context_filter_fields(clause, is_last = false)
23
+ case clause
24
+ when 'self'
25
+ ['parent_id', 'id']
26
+ when 'parent'
27
+ is_last ? ['parent_id', 'parent_id'] : ['parent_id', 'id']
28
+ when 'project'
29
+ is_last ? ['project_id', 'project_id'] : ['project_id', 'id']
30
+ when 'site', main_table
31
+ :void
32
+ else
33
+ nil
34
+ end
35
+ end
36
+
37
+ def context_relation(clause, context)
38
+ case clause
39
+ when 'self'
40
+ fields = ['id', 'id']
41
+ when 'parent'
42
+ fields = ['id', 'parent_id']
43
+ when 'project'
44
+ fields = ['id', 'project_id']
45
+ when main_table, 'children'
46
+ parse_context(default_context_filter) unless context
47
+ return true # dummy clause: does nothing
48
+ else
49
+ return false
50
+ end
51
+
52
+ @where << "#{field_or_attr(fields[0])} = #{field_or_attr(fields[1], table(main_table,-1))}"
53
+ end
54
+
55
+ # Direct filter
56
+ def direct_filter(rel, context)
57
+ case rel
58
+ when 'letters'
59
+ parse_context(default_context_filter) unless context
60
+ @where << "#{table}.kpath LIKE 'NNL%'"
61
+ when 'clients'
62
+ parse_context(default_context_filter) unless context
63
+ @where << "#{table}.kpath LIKE 'NRCC%'"
64
+ else
65
+ return false
66
+ end
67
+ end
68
+
69
+ def parse_change_class(rel, is_last)
70
+ case rel
71
+ when 'users'
72
+ parse_context(default_context_filter, true) if is_last
73
+ add_table('users')
74
+ @where << "#{table('users')}.node_id = #{field_or_attr('id')}"
75
+ return TestUserQuery # class change
76
+ else
77
+ return nil
78
+ end
79
+ end
80
+
81
+ # Filters that need a join
82
+ def join_relation(rel, context)
83
+ case rel
84
+ when 'recipients'
85
+ fields = ['source_id', 4, 'target_id']
86
+ when 'icons'
87
+ fields = ['target_id', 5, 'source_id']
88
+ when 'tags'
89
+ # just to test joins
90
+ needs_join_table('objects', 'INNER', 'tags', 'TABLE1.id = TABLE2.node_id')
91
+ return true
92
+ else
93
+ return false
94
+ end
95
+
96
+ add_table('links')
97
+ # source --> target
98
+ @where << "#{field_or_attr('id')} = #{table('links')}.#{fields[2]} AND #{table('links')}.relation_id = #{fields[1]} AND #{table('links')}.#{fields[0]} = #{field_or_attr('id', table(main_table,-1))}"
99
+ end
100
+
101
+ # Overwrite this and take car to check for valid fields.
102
+ def map_field(fld, table_name, is_null = false)
103
+ if ['id', 'parent_id', 'project_id', 'section_id', 'kpath', 'name', 'event_at', 'custom_a'].include?(fld)
104
+ "#{table_name}.#{fld}"
105
+ else
106
+ # error, raise
107
+ end
108
+ end
109
+ end
110
+
111
+ class DummyQueryClass
112
+ def self.connection; self; end
113
+ def self.quote(obj); "[[#{obj}]]"; end
114
+ end
@@ -0,0 +1,55 @@
1
+
2
+ class TestUser
3
+ end
4
+
5
+ class TestUserQuery < QueryBuilder
6
+ set_main_table 'users'
7
+ set_main_class 'TestUser'
8
+
9
+ # Default sort order
10
+ def default_order_clause
11
+ "name ASC, first_name ASC"
12
+ end
13
+
14
+ def default_context_filter
15
+ 'self'
16
+ end
17
+
18
+ def parse_change_class(rel, is_last)
19
+ case rel
20
+ when 'objects'
21
+ parse_context(default_context_filter, true) if is_last
22
+ add_table('objects')
23
+ @where << "#{table('objects')}.id = #{field_or_attr('node_id')}"
24
+ return TestUserQuery # class change
25
+ else
26
+ return nil
27
+ end
28
+ end
29
+
30
+ def parse_relation(clause, context)
31
+ return nil
32
+ end
33
+
34
+ def context_filter_fields(clause, is_last = false)
35
+ nil
36
+ end
37
+
38
+ def parse_context(clause, is_last = false)
39
+
40
+ if fields = context_filter_fields(clause, is_last)
41
+ @where << "#{field_or_attr(fields[0])} = #{field_or_attr(fields[1], table(main_table,-1))}" if fields != :void
42
+ else
43
+ @errors << "invalid context '#{clause}'"
44
+ end
45
+ end
46
+
47
+ # Overwrite this and take car to check for valid fields.
48
+ def map_field(fld, table_name, context = nil)
49
+ if ['id', 'name', 'first_name', 'node_id'].include?(fld)
50
+ "#{table_name}.#{fld}"
51
+ else
52
+ # TODO: error, raise / ignore ?
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+
4
+ class DummyQueryBuilder < Test::Unit::TestCase
5
+ yamltest
6
+
7
+ def id; 123; end
8
+ def parent_id; 333; end
9
+ def project_id; 9999; end
10
+ def connection; self; end
11
+
12
+
13
+ def yt_parse(key, source, opts)
14
+ opts = Hash[*(opts.map{|k,v| [k.to_sym, v]}.flatten)]
15
+ query = DummyQuery.new(source, opts)
16
+
17
+ case key
18
+ when 'res'
19
+ (query.main_class != DummyQueryClass ? "#{query.main_class.to_s}: " : '') + if res = query.to_s
20
+ res
21
+ else
22
+ query.errors.join(", ")
23
+ end
24
+ when 'sql'
25
+ query.sql(binding)
26
+ when 'count'
27
+ query.to_s(:count)
28
+ when 'count_sql'
29
+ query.sql(binding, :count)
30
+ else
31
+ "parse not implemented for '#{key}' in query_builder_test.rb"
32
+ end
33
+ end
34
+
35
+ yt_make
36
+ end