querybuilder 0.7.0 → 0.8.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.
- data/.gitignore +2 -1
- data/History.txt +8 -0
- data/Rakefile +3 -2
- data/lib/query_builder/info.rb +1 -1
- data/lib/query_builder/processor.rb +84 -13
- data/lib/query_builder/query.rb +32 -17
- data/lib/querybuilder_ext.c +937 -902
- data/lib/querybuilder_ext.rl +9 -1
- data/lib/querybuilder_rb.rb +704 -679
- data/lib/querybuilder_rb.rl +8 -1
- data/lib/querybuilder_syntax.rl +1 -1
- data/{tasks → lib/tasks}/build.rake +0 -0
- data/querybuilder.gemspec +6 -4
- data/test/database.rb +20 -0
- data/test/mock/dummy_processor.rb +17 -0
- data/test/querybuilder/basic.yml +4 -3
- data/test/querybuilder/errors.yml +9 -0
- data/test/querybuilder/filters.yml +10 -2
- data/test/querybuilder/joins.yml +5 -0
- data/test/test_helper.rb +1 -0
- metadata +6 -4
    
        data/lib/querybuilder_rb.rl
    CHANGED
    
    | @@ -87,6 +87,7 @@ module QueryBuilder | |
| 87 87 |  | 
| 88 88 | 
             
                action goto_expr_p {
         | 
| 89 89 | 
             
                  # remember current machine state 'cs'
         | 
| 90 | 
            +
                  par_count += 1
         | 
| 90 91 | 
             
                  last << [:par, cs]
         | 
| 91 92 | 
             
                  stack.push last.last
         | 
| 92 93 | 
             
                  last = last.last
         | 
| @@ -96,6 +97,7 @@ module QueryBuilder | |
| 96 97 | 
             
                action expr_close {
         | 
| 97 98 | 
             
                  pop_stack(stack, :par_close)
         | 
| 98 99 | 
             
                  # reset machine state 'cs'
         | 
| 100 | 
            +
                  par_count -= 1
         | 
| 99 101 | 
             
                  cs = stack.last.delete_at(1)
         | 
| 100 102 | 
             
                  # one more time to remove [:par...] line
         | 
| 101 103 | 
             
                  stack.pop
         | 
| @@ -195,6 +197,7 @@ module QueryBuilder | |
| 195 197 | 
             
                  else
         | 
| 196 198 | 
             
                    data = "#{arg}\n"
         | 
| 197 199 | 
             
                  end
         | 
| 200 | 
            +
                  par_count = 0
         | 
| 198 201 | 
             
                  stack = [[:query]]
         | 
| 199 202 | 
             
                  last  = stack.last
         | 
| 200 203 | 
             
                  str_buf         = ""
         | 
| @@ -206,7 +209,11 @@ module QueryBuilder | |
| 206 209 | 
             
                  if p < pe
         | 
| 207 210 | 
             
                    p = p - 3
         | 
| 208 211 | 
             
                    p = 0 if p < 0
         | 
| 209 | 
            -
                    raise QueryBuilder::SyntaxError.new("Syntax error near #{data[p..- | 
| 212 | 
            +
                    raise QueryBuilder::SyntaxError.new("Syntax error near #{data[p..-2].inspect}.")
         | 
| 213 | 
            +
                  end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                  if par_count > 0
         | 
| 216 | 
            +
                    raise QueryBuilder::SyntaxError.new("Missing closing parenthesis in #{data[0..-2].inspect}.")
         | 
| 210 217 | 
             
                  end
         | 
| 211 218 | 
             
                  stack.first
         | 
| 212 219 | 
             
                end
         | 
    
        data/lib/querybuilder_syntax.rl
    CHANGED
    
    | @@ -8,7 +8,7 @@ | |
| 8 8 | 
             
              # The where CLAUSE can contain the following operators
         | 
| 9 9 |  | 
| 10 10 | 
             
              ws       = ' ' | '\t' | '\n';
         | 
| 11 | 
            -
              var      = ws* ([a-zA-Z_]+) $str_a;
         | 
| 11 | 
            +
              var      = ws* ([a-zA-Z_:]+) $str_a;
         | 
| 12 12 | 
             
              dquote   = ([^"\\] | '\n') $str_a | ('\\' (any | '\n') $str_a);
         | 
| 13 13 | 
             
              squote   = ([^'\\] | '\n') $str_a | ('\\' (any | '\n') $str_a);
         | 
| 14 14 | 
             
              string   = ws* ("'" squote* "'" >string | '"' dquote* '"' >dstring);
         | 
| 
            File without changes
         | 
    
        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 = "0. | 
| 8 | 
            +
              s.version = "0.8.0"
         | 
| 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{2010- | 
| 12 | 
            +
              s.date = %q{2010-07-22}
         | 
| 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 |  | 
| @@ -38,12 +38,13 @@ Gem::Specification.new do |s| | |
| 38 38 | 
             
                 "lib/querybuilder_rb.rb",
         | 
| 39 39 | 
             
                 "lib/querybuilder_rb.rl",
         | 
| 40 40 | 
             
                 "lib/querybuilder_syntax.rl",
         | 
| 41 | 
            +
                 "lib/tasks/build.rake",
         | 
| 41 42 | 
             
                 "old_QueryBuilder.rb",
         | 
| 42 43 | 
             
                 "querybuilder.gemspec",
         | 
| 43 44 | 
             
                 "script/console",
         | 
| 44 45 | 
             
                 "script/destroy",
         | 
| 45 46 | 
             
                 "script/generate",
         | 
| 46 | 
            -
                 " | 
| 47 | 
            +
                 "test/database.rb",
         | 
| 47 48 | 
             
                 "test/dummy_test.rb",
         | 
| 48 49 | 
             
                 "test/mock/custom_queries/test.yml",
         | 
| 49 50 | 
             
                 "test/mock/dummy.rb",
         | 
| @@ -69,7 +70,8 @@ Gem::Specification.new do |s| | |
| 69 70 | 
             
              s.rubygems_version = %q{1.3.6}
         | 
| 70 71 | 
             
              s.summary = %q{QueryBuilder is an interpreter for the "pseudo sql" language.}
         | 
| 71 72 | 
             
              s.test_files = [
         | 
| 72 | 
            -
                "test/ | 
| 73 | 
            +
                "test/database.rb",
         | 
| 74 | 
            +
                 "test/dummy_test.rb",
         | 
| 73 75 | 
             
                 "test/mock/dummy.rb",
         | 
| 74 76 | 
             
                 "test/mock/dummy_processor.rb",
         | 
| 75 77 | 
             
                 "test/mock/user_processor.rb",
         | 
    
        data/test/database.rb
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            require 'active_record'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            begin
         | 
| 4 | 
            +
              class QueryBuilderTestMigration < ActiveRecord::Migration
         | 
| 5 | 
            +
                def self.down
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def self.up
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              ActiveRecord::Base.establish_connection(:adapter=>'sqlite3', :database=>':memory:')
         | 
| 13 | 
            +
              log_path = Pathname(__FILE__).dirname + '../log/test.log'
         | 
| 14 | 
            +
              Dir.mkdir(log_path.dirname) unless File.exist?(log_path.dirname)
         | 
| 15 | 
            +
              ActiveRecord::Base.logger = Logger.new(File.open(log_path, 'wb'))
         | 
| 16 | 
            +
              ActiveRecord::Migration.verbose = false
         | 
| 17 | 
            +
              #PropertyMigration.migrate(:down)
         | 
| 18 | 
            +
              QueryBuilderTestMigration.migrate(:up)
         | 
| 19 | 
            +
              ActiveRecord::Migration.verbose = true
         | 
| 20 | 
            +
            end
         | 
| @@ -32,11 +32,28 @@ class DummyProcessor < QueryBuilder::Processor | |
| 32 32 | 
             
                  "#{table}.#{fld_name}"
         | 
| 33 33 | 
             
                elsif fld_name == 'REF_DATE'
         | 
| 34 34 | 
             
                  context[:ref_date] ? insert_bind(context[:ref_date]) : 'now()'
         | 
| 35 | 
            +
                elsif fld_name == 'index'
         | 
| 36 | 
            +
                  add_table('index')
         | 
| 37 | 
            +
                  add_filter "#{table('index')}.node_id = #{field_or_attr('id', table(self.class.main_table))}"
         | 
| 38 | 
            +
                  "#{table('index')}.value"
         | 
| 35 39 | 
             
                else
         | 
| 36 40 | 
             
                  super # raises an error
         | 
| 37 41 | 
             
                end
         | 
| 38 42 | 
             
              end
         | 
| 39 43 |  | 
| 44 | 
            +
              def resolve_missing_table(query, table_alias, table_name)
         | 
| 45 | 
            +
                case table_name
         | 
| 46 | 
            +
                when 'index'
         | 
| 47 | 
            +
                  query.where.insert 0, "#{table_alias}.id = 0"
         | 
| 48 | 
            +
                when 'links'
         | 
| 49 | 
            +
                  query.where.insert 0, "#{table_alias}.id = 0"
         | 
| 50 | 
            +
                else
         | 
| 51 | 
            +
                  # Raise an error
         | 
| 52 | 
            +
                  super
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
                # do nothing
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 40 57 | 
             
              # We do special things with 'class ='
         | 
| 41 58 | 
             
              def process_equal(left, right)
         | 
| 42 59 | 
             
                if left == [:field, 'class'] && right[0] == :string
         | 
    
        data/test/querybuilder/basic.yml
    CHANGED
    
    | @@ -70,11 +70,12 @@ paginate: | |
| 70 70 | 
             
            recipients_or_objects:
         | 
| 71 71 | 
             
              src: recipients or objects
         | 
| 72 72 | 
             
              sxp: '[:query, [:clause_or, [:relation, "recipients"], [:relation, "objects"]]]'
         | 
| 73 | 
            -
              res: "[%Q{SELECT objects.* FROM links,objects 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]"
         | 
| 73 | 
            +
              res: "[%Q{SELECT objects.* FROM links,objects WHERE ((objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ?) OR (objects.parent_id = ? AND links.id = 0)) GROUP BY objects.id}, id, id]"
         | 
| 74 74 |  | 
| 75 75 | 
             
            recipients_or_objects_or_letters:
         | 
| 76 76 | 
             
              src: recipients or objects or letters
         | 
| 77 | 
            -
              res: "[%Q{SELECT objects.* FROM links,objects WHERE ((objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ?) OR objects.parent_id = ? OR (objects.kpath LIKE 'NNL%' AND objects.parent_id = ?)) GROUP BY objects.id}, id, id, id]"
         | 
| 77 | 
            +
              res: "[%Q{SELECT objects.* FROM links,objects WHERE ((objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ?) OR (links.id = 0 AND objects.parent_id = ?) OR (links.id = 0 AND objects.kpath LIKE 'NNL%' AND objects.parent_id = ?)) GROUP BY objects.id}, id, id, id]"
         | 
| 78 | 
            +
              res: "[%Q{SELECT objects.* FROM links,objects WHERE ((objects.id = links.target_id AND links.relation_id = 4 AND links.source_id = ?) OR (objects.parent_id = ? AND links.id = 0) OR (objects.kpath LIKE 'NNL%' AND objects.parent_id = ? AND links.id = 0)) GROUP BY objects.id}, id, id, id]"
         | 
| 78 79 |  | 
| 79 80 | 
             
            or_clause_with_filter:
         | 
| 80 81 | 
             
              src: "(recipients where name = 'foo') or objects"
         | 
| @@ -88,4 +89,4 @@ after_process_callback: | |
| 88 89 | 
             
              context:
         | 
| 89 90 | 
             
                after_filter: '(1 = 1)'
         | 
| 90 91 | 
             
              src: "objects where name like 'a%' or name like 'b%' in site"
         | 
| 91 | 
            -
              res: "%Q{SELECT objects.* FROM objects WHERE (1 = 1) AND (objects.name LIKE 'a%' OR objects.name LIKE 'b%')}"
         | 
| 92 | 
            +
              res: "%Q{SELECT objects.* FROM objects WHERE (1 = 1) AND (objects.name LIKE 'a%' OR objects.name LIKE 'b%') GROUP BY id}"
         | 
| @@ -30,3 +30,12 @@ or_clause_with_filter: | |
| 30 30 | 
             
              src: "recipients where name = 'foo' or objects"
         | 
| 31 31 | 
             
              sxp: '[:query, [:filter, [:relation, "recipients"], [:or, [:"=", [:field, "name"], [:string, "foo"]], [:field, "objects"]]]]'
         | 
| 32 32 | 
             
              res: "Unknown field 'objects'."
         | 
| 33 | 
            +
             | 
| 34 | 
            +
             | 
| 35 | 
            +
            unmatched_open_par:
         | 
| 36 | 
            +
              src: "objects where event_at is null or (name is not null"
         | 
| 37 | 
            +
              res: "Missing closing parenthesis in \"objects where event_at is null or (name is not null\"."
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            unmatched_close_par:
         | 
| 40 | 
            +
              src: "objects where event_at is null )"
         | 
| 41 | 
            +
              res: "Syntax error near \"ll )\"."
         | 
| @@ -83,7 +83,15 @@ equation_with_date_interval: | |
| 83 83 | 
             
            equation_and_or_par:
         | 
| 84 84 | 
             
              src: "objects where event_at > '2006-04-01' or name like 'foo%'"
         | 
| 85 85 | 
             
              sxp: '[:query, [:filter, [:relation, "objects"], [:or, [:>, [:field, "event_at"], [:string, "2006-04-01"]], [:like, [:field, "name"], [:string, "foo%"]]]]]'
         | 
| 86 | 
            -
              res: "[%Q{SELECT objects.* FROM objects WHERE (objects.event_at > '2006-04-01' OR objects.name LIKE 'foo%') AND objects.parent_id = ?}, id]"
         | 
| 86 | 
            +
              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]"
         | 
| 87 | 
            +
             | 
| 88 | 
            +
            or_with_same_tables:
         | 
| 89 | 
            +
              src: "objects where index = 5 or index = 7"
         | 
| 90 | 
            +
              res: "[%Q{SELECT objects.* FROM index,objects WHERE ((index.value = 5 AND index.node_id = objects.id) OR (index.value = 7 AND index.node_id = objects.id)) AND objects.parent_id = ? GROUP BY objects.id}, id]"
         | 
| 91 | 
            +
             | 
| 92 | 
            +
            or_with_missing_table:
         | 
| 93 | 
            +
              src: "objects where index = 5 or 7"
         | 
| 94 | 
            +
              res: "[%Q{SELECT objects.* FROM index,objects WHERE ((index.value = 5 AND index.node_id = objects.id) OR (7 AND index.id = 0)) AND objects.parent_id = ? GROUP BY objects.id}, id]"
         | 
| 87 95 |  | 
| 88 96 | 
             
            equation_par:
         | 
| 89 97 | 
             
              src: "objects where (1 > 2 or 2 > 3) and 4 = 5 "
         | 
| @@ -112,4 +120,4 @@ functions: | |
| 112 120 | 
             
            filter_empty_literal:
         | 
| 113 121 | 
             
              src: "objects where \"\" = ''"
         | 
| 114 122 | 
             
              sxp: '[:query, [:filter, [:relation, "objects"], [:"=", [:dstring, ""], [:string, ""]]]]'
         | 
| 115 | 
            -
              sql: "SELECT objects.* FROM objects WHERE '' = '' AND objects.parent_id = 123"
         | 
| 123 | 
            +
              sql: "SELECT objects.* FROM objects WHERE '' = '' AND objects.parent_id = 123"
         | 
    
        data/test/querybuilder/joins.yml
    CHANGED
    
    | @@ -26,6 +26,11 @@ tags: | |
| 26 26 | 
             
            complex_from_with_scopes:
         | 
| 27 27 | 
             
              src: "letters where name = 'foo' in project from letters in section"
         | 
| 28 28 | 
             
              sxp: '[:query, [:from, [:scope, [:filter, [:relation, "letters"], [:"=", [:field, "name"], [:string, "foo"]]], "project"], [:scope, [:relation, "letters"], "section"]]]'
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            complex_from_with_scopes_and_typed_scope:
         | 
| 31 | 
            +
              # instead of 'project', we give it a class with 'jobs:project'
         | 
| 32 | 
            +
              src: "letters where name = 'foo' in jobs:project from letters in section"
         | 
| 33 | 
            +
              sxp: '[:query, [:from, [:scope, [:filter, [:relation, "letters"], [:"=", [:field, "name"], [:string, "foo"]]], "jobs:project"], [:scope, [:relation, "letters"], "section"]]]'
         | 
| 29 34 |  | 
| 30 35 | 
             
            letters_in_project_from_letters:
         | 
| 31 36 | 
             
              sxp: '[:query, [:from, [:scope, [:relation, "letters"], "project"], [:relation, "letters"]]]'
         | 
    
        data/test/test_helper.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version | |
| 4 4 | 
             
              prerelease: false
         | 
| 5 5 | 
             
              segments: 
         | 
| 6 6 | 
             
              - 0
         | 
| 7 | 
            -
              -  | 
| 7 | 
            +
              - 8
         | 
| 8 8 | 
             
              - 0
         | 
| 9 | 
            -
              version: 0. | 
| 9 | 
            +
              version: 0.8.0
         | 
| 10 10 | 
             
            platform: ruby
         | 
| 11 11 | 
             
            authors: 
         | 
| 12 12 | 
             
            - Gaspard Bucher
         | 
| @@ -14,7 +14,7 @@ autorequire: | |
| 14 14 | 
             
            bindir: bin
         | 
| 15 15 | 
             
            cert_chain: []
         | 
| 16 16 |  | 
| 17 | 
            -
            date: 2010- | 
| 17 | 
            +
            date: 2010-07-22 00:00:00 +02:00
         | 
| 18 18 | 
             
            default_executable: 
         | 
| 19 19 | 
             
            dependencies: 
         | 
| 20 20 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| @@ -89,12 +89,13 @@ files: | |
| 89 89 | 
             
            - lib/querybuilder_rb.rb
         | 
| 90 90 | 
             
            - lib/querybuilder_rb.rl
         | 
| 91 91 | 
             
            - lib/querybuilder_syntax.rl
         | 
| 92 | 
            +
            - lib/tasks/build.rake
         | 
| 92 93 | 
             
            - old_QueryBuilder.rb
         | 
| 93 94 | 
             
            - querybuilder.gemspec
         | 
| 94 95 | 
             
            - script/console
         | 
| 95 96 | 
             
            - script/destroy
         | 
| 96 97 | 
             
            - script/generate
         | 
| 97 | 
            -
            -  | 
| 98 | 
            +
            - test/database.rb
         | 
| 98 99 | 
             
            - test/dummy_test.rb
         | 
| 99 100 | 
             
            - test/mock/custom_queries/test.yml
         | 
| 100 101 | 
             
            - test/mock/dummy.rb
         | 
| @@ -144,6 +145,7 @@ signing_key: | |
| 144 145 | 
             
            specification_version: 3
         | 
| 145 146 | 
             
            summary: QueryBuilder is an interpreter for the "pseudo sql" language.
         | 
| 146 147 | 
             
            test_files: 
         | 
| 148 | 
            +
            - test/database.rb
         | 
| 147 149 | 
             
            - test/dummy_test.rb
         | 
| 148 150 | 
             
            - test/mock/dummy.rb
         | 
| 149 151 | 
             
            - test/mock/dummy_processor.rb
         |