querybuilder 0.5.9 → 0.7.0

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.
Files changed (46) hide show
  1. data/History.txt +29 -25
  2. data/Manifest.txt +20 -9
  3. data/README.rdoc +73 -10
  4. data/Rakefile +62 -30
  5. data/lib/extconf.rb +3 -0
  6. data/lib/query_builder.rb +39 -898
  7. data/lib/query_builder/error.rb +7 -0
  8. data/lib/query_builder/info.rb +3 -0
  9. data/lib/query_builder/parser.rb +80 -0
  10. data/lib/query_builder/processor.rb +714 -0
  11. data/lib/query_builder/query.rb +273 -0
  12. data/lib/querybuilder_ext.c +1870 -0
  13. data/lib/querybuilder_ext.rl +418 -0
  14. data/lib/querybuilder_rb.rb +1686 -0
  15. data/lib/querybuilder_rb.rl +214 -0
  16. data/lib/querybuilder_syntax.rl +47 -0
  17. data/old_QueryBuilder.rb +946 -0
  18. data/querybuilder.gemspec +42 -15
  19. data/tasks/build.rake +20 -0
  20. data/test/dummy_test.rb +21 -0
  21. data/test/mock/custom_queries/test.yml +5 -4
  22. data/test/mock/dummy.rb +9 -0
  23. data/test/mock/dummy_processor.rb +160 -0
  24. data/test/mock/queries/bar.yml +1 -1
  25. data/test/mock/queries/foo.yml +2 -2
  26. data/test/mock/user_processor.rb +34 -0
  27. data/test/query_test.rb +38 -0
  28. data/test/querybuilder/basic.yml +91 -0
  29. data/test/{query_builder → querybuilder}/custom.yml +11 -11
  30. data/test/querybuilder/errors.yml +32 -0
  31. data/test/querybuilder/filters.yml +115 -0
  32. data/test/querybuilder/group.yml +7 -0
  33. data/test/querybuilder/joins.yml +37 -0
  34. data/test/querybuilder/mixed.yml +18 -0
  35. data/test/querybuilder/rubyless.yml +15 -0
  36. data/test/querybuilder_test.rb +111 -0
  37. data/test/test_helper.rb +8 -3
  38. metadata +66 -19
  39. data/test/mock/dummy_query.rb +0 -114
  40. data/test/mock/user_query.rb +0 -55
  41. data/test/query_builder/basic.yml +0 -60
  42. data/test/query_builder/errors.yml +0 -50
  43. data/test/query_builder/filters.yml +0 -43
  44. data/test/query_builder/joins.yml +0 -25
  45. data/test/query_builder/mixed.yml +0 -12
  46. data/test/query_builder_test.rb +0 -36
@@ -1,114 +0,0 @@
1
- class DummyQuery < QueryBuilder
2
- set_main_table 'objects'
3
- set_main_class 'DummyQueryClass'
4
-
5
- load_custom_queries File.join(File.dirname(__FILE__), '*')
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
@@ -1,55 +0,0 @@
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
@@ -1,60 +0,0 @@
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
- count_sql:
59
- src: "objects in project"
60
- count: "[\"SELECT COUNT(*) FROM objects WHERE objects.project_id = ?\", project_id]"
@@ -1,50 +0,0 @@
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
-
@@ -1,43 +0,0 @@
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 ASC\""
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]"
@@ -1,25 +0,0 @@
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]"
@@ -1,12 +0,0 @@
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]"
@@ -1,36 +0,0 @@
1
- require File.dirname(__FILE__) + '/test_helper.rb'
2
-
3
-
4
- class QueryBuilderTest < 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