sql_tree 0.0.2 → 0.0.3
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/README.rdoc +12 -1
- data/Rakefile +1 -1
- data/lib/sql_tree/node/expression.rb +31 -31
- data/lib/sql_tree/node/field.rb +3 -3
- data/lib/sql_tree/node/join.rb +2 -2
- data/lib/sql_tree/node/ordering.rb +5 -5
- data/lib/sql_tree/node/select_expression.rb +9 -9
- data/lib/sql_tree/node/select_query.rb +27 -13
- data/lib/sql_tree/node/source.rb +6 -6
- data/lib/sql_tree/node/table_reference.rb +2 -2
- data/lib/sql_tree/node/value.rb +5 -5
- data/lib/sql_tree/node/variable.rb +3 -3
- data/lib/sql_tree/node.rb +5 -5
- data/lib/sql_tree/parser.rb +9 -9
- data/lib/sql_tree/token.rb +18 -18
- data/lib/sql_tree/tokenizer.rb +15 -15
- data/lib/sql_tree.rb +6 -6
- data/spec/integration/api_spec.rb +1 -1
- data/spec/integration/full_queries_spec.rb +12 -7
- data/spec/lib/matchers.rb +7 -7
- data/spec/spec_helper.rb +3 -3
- data/spec/unit/expression_node_spec.rb +8 -8
- data/spec/unit/leaf_node_spec.rb +3 -3
- data/spec/unit/select_query_spec.rb +9 -9
- data/spec/unit/tokenizer_spec.rb +17 -17
- data/sql_tree.gemspec +1 -1
- metadata +1 -1
    
        data/README.rdoc
    CHANGED
    
    | @@ -16,10 +16,21 @@ are supported. | |
| 16 16 | 
             
            The SQLTree library is distributed as a gem on Gemcutter.org. To install:
         | 
| 17 17 |  | 
| 18 18 | 
             
              gem install sql_tree --source http://gemcutter.org
         | 
| 19 | 
            +
              
         | 
| 20 | 
            +
            == Usage
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            Consider the following example:
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              require 'sql_tree'
         | 
| 25 | 
            +
              tree  = SQLTree["SELECT * FROM table WHERE field = 'value'"]
         | 
| 26 | 
            +
              where = SQLTree::Node::Expression["username = 'user' AND password = MD5('$secret')"]
         | 
| 27 | 
            +
              tree.where = where # replace WHERE expression
         | 
| 28 | 
            +
              puts tree.to_sql
         | 
| 29 | 
            +
              # "SELECT * FROM "table" WHERE (("username" = 'user') AND ("password" = MD5('$secret')))"
         | 
| 19 30 |  | 
| 20 31 | 
             
            == Additional information
         | 
| 21 32 |  | 
| 22 33 | 
             
            * See the project wiki at http://wiki.github.com/wvanbergen/sql_tree for more
         | 
| 23 34 | 
             
              information about using this library.
         | 
| 24 | 
            -
            * This plugin is written by Willem van Bergen and is MIT licensed (see the | 
| 35 | 
            +
            * This plugin is written by Willem van Bergen and is MIT licensed (see the
         | 
| 25 36 | 
             
              LICENSE file).
         | 
    
        data/Rakefile
    CHANGED
    
    
| @@ -5,11 +5,11 @@ module SQLTree::Node | |
| 5 5 | 
             
              # This is an asbtract class and should not be used directly. Use
         | 
| 6 6 | 
             
              # one of the subclasses instead.
         | 
| 7 7 | 
             
              class Expression < Base
         | 
| 8 | 
            -
             | 
| 8 | 
            +
             | 
| 9 9 | 
             
                def self.parse(tokens)
         | 
| 10 10 | 
             
                  SQLTree::Node::LogicalExpression.parse(tokens)
         | 
| 11 11 | 
             
                end
         | 
| 12 | 
            -
             | 
| 12 | 
            +
             | 
| 13 13 | 
             
                # Parses a single, atomic SQL expression. This can be either:
         | 
| 14 14 | 
             
                # * a full expression (or set of expressions) within parentheses.
         | 
| 15 15 | 
             
                # * a logical NOT expression
         | 
| @@ -21,7 +21,7 @@ module SQLTree::Node | |
| 21 21 | 
             
                  when SQLTree::Token::LPAREN
         | 
| 22 22 | 
             
                    tokens.consume(SQLTree::Token::LPAREN)
         | 
| 23 23 | 
             
                    expr = SQLTree::Node::Expression.parse(tokens)
         | 
| 24 | 
            -
                    tokens.consume(SQLTree::Token::RPAREN) | 
| 24 | 
            +
                    tokens.consume(SQLTree::Token::RPAREN)
         | 
| 25 25 | 
             
                    expr
         | 
| 26 26 | 
             
                  when SQLTree::Token::NOT
         | 
| 27 27 | 
             
                    SQLTree::Node::LogicalNotExpression.parse(tokens)
         | 
| @@ -36,23 +36,23 @@ module SQLTree::Node | |
| 36 36 | 
             
                  end
         | 
| 37 37 | 
             
                end
         | 
| 38 38 | 
             
              end
         | 
| 39 | 
            -
             | 
| 39 | 
            +
             | 
| 40 40 | 
             
              class LogicalNotExpression < Expression
         | 
| 41 | 
            -
             | 
| 41 | 
            +
             | 
| 42 42 | 
             
                attr_accessor :expression
         | 
| 43 | 
            -
             | 
| 43 | 
            +
             | 
| 44 44 | 
             
                def initialize(expression)
         | 
| 45 45 | 
             
                  @expression = expression
         | 
| 46 46 | 
             
                end
         | 
| 47 | 
            -
             | 
| 47 | 
            +
             | 
| 48 48 | 
             
                def to_sql
         | 
| 49 49 | 
             
                  "NOT(#{@expression.to_sql})"
         | 
| 50 50 | 
             
                end
         | 
| 51 | 
            -
             | 
| 51 | 
            +
             | 
| 52 52 | 
             
                def ==(other)
         | 
| 53 53 | 
             
                  other.kind_of?(self.class) && other.expression == self.expression
         | 
| 54 54 | 
             
                end
         | 
| 55 | 
            -
             | 
| 55 | 
            +
             | 
| 56 56 | 
             
                def self.parse(tokens)
         | 
| 57 57 | 
             
                  tokens.consume(SQLTree::Token::NOT)
         | 
| 58 58 | 
             
                  self.new(SQLTree::Node::Expression.parse(tokens))
         | 
| @@ -74,29 +74,29 @@ module SQLTree::Node | |
| 74 74 | 
             
                def ==(other)
         | 
| 75 75 | 
             
                  self.operator == other.operator && self.expressions == other.expressions
         | 
| 76 76 | 
             
                end
         | 
| 77 | 
            -
             | 
| 77 | 
            +
             | 
| 78 78 | 
             
                def self.parse(tokens)
         | 
| 79 79 | 
             
                  expr = ComparisonExpression.parse(tokens)
         | 
| 80 80 | 
             
                  while [SQLTree::Token::AND, SQLTree::Token::OR].include?(tokens.peek)
         | 
| 81 81 | 
             
                    expr = SQLTree::Node::LogicalExpression.new(tokens.next.literal, [expr, ComparisonExpression.parse(tokens)])
         | 
| 82 | 
            -
                  end | 
| 82 | 
            +
                  end
         | 
| 83 83 | 
             
                  return expr
         | 
| 84 84 | 
             
                end
         | 
| 85 85 | 
             
              end
         | 
| 86 86 |  | 
| 87 87 | 
             
              class ComparisonExpression < Expression
         | 
| 88 88 | 
             
                attr_accessor :lhs, :rhs, :operator
         | 
| 89 | 
            -
             | 
| 89 | 
            +
             | 
| 90 90 | 
             
                def initialize(operator, lhs, rhs)
         | 
| 91 91 | 
             
                  @lhs = lhs
         | 
| 92 92 | 
             
                  @rhs = rhs
         | 
| 93 93 | 
             
                  @operator = operator
         | 
| 94 94 | 
             
                end
         | 
| 95 | 
            -
             | 
| 95 | 
            +
             | 
| 96 96 | 
             
                def to_sql
         | 
| 97 97 | 
             
                  "(#{@lhs.to_sql} #{@operator} #{@rhs.to_sql})"
         | 
| 98 98 | 
             
                end
         | 
| 99 | 
            -
             | 
| 99 | 
            +
             | 
| 100 100 | 
             
                def self.parse_comparison_operator(tokens)
         | 
| 101 101 | 
             
                  operator_token = tokens.next
         | 
| 102 102 | 
             
                  if SQLTree::Token::IS === operator_token
         | 
| @@ -117,7 +117,7 @@ module SQLTree::Node | |
| 117 117 | 
             
                    operator_token.literal
         | 
| 118 118 | 
             
                  end
         | 
| 119 119 | 
             
                end
         | 
| 120 | 
            -
             | 
| 120 | 
            +
             | 
| 121 121 | 
             
                def self.parse(tokens)
         | 
| 122 122 | 
             
                  lhs = SQLTree::Node::ArithmeticExpression.parse(tokens)
         | 
| 123 123 | 
             
                  while SQLTree::Token::COMPARISON_OPERATORS.include?(tokens.peek)
         | 
| @@ -125,16 +125,16 @@ module SQLTree::Node | |
| 125 125 | 
             
                    rhs = ['IN', 'NOT IN'].include?(comparison_operator) ?
         | 
| 126 126 | 
             
                            SQLTree::Node::SetExpression.parse(tokens)   :
         | 
| 127 127 | 
             
                            SQLTree::Node::ArithmeticExpression.parse(tokens)
         | 
| 128 | 
            -
             | 
| 128 | 
            +
             | 
| 129 129 | 
             
                    lhs = self.new(comparison_operator, lhs, rhs)
         | 
| 130 130 | 
             
                  end
         | 
| 131 131 | 
             
                  return lhs
         | 
| 132 132 | 
             
                end
         | 
| 133 133 | 
             
              end
         | 
| 134 | 
            -
             | 
| 134 | 
            +
             | 
| 135 135 | 
             
              class SetExpression < Expression
         | 
| 136 136 | 
             
                attr_accessor :items
         | 
| 137 | 
            -
             | 
| 137 | 
            +
             | 
| 138 138 | 
             
                def initialize(items = [])
         | 
| 139 139 | 
             
                  @items = items
         | 
| 140 140 | 
             
                end
         | 
| @@ -142,7 +142,7 @@ module SQLTree::Node | |
| 142 142 | 
             
                def to_sql
         | 
| 143 143 | 
             
                  "(#{items.map {|i| i.to_sql}.join(', ')})"
         | 
| 144 144 | 
             
                end
         | 
| 145 | 
            -
             | 
| 145 | 
            +
             | 
| 146 146 | 
             
                def self.parse(tokens)
         | 
| 147 147 | 
             
                  tokens.consume(SQLTree::Token::LPAREN)
         | 
| 148 148 | 
             
                  items = [SQLTree::Node::Expression.parse(tokens)]
         | 
| @@ -151,23 +151,23 @@ module SQLTree::Node | |
| 151 151 | 
             
                    items << SQLTree::Node::Expression.parse(tokens)
         | 
| 152 152 | 
             
                  end
         | 
| 153 153 | 
             
                  tokens.consume(SQLTree::Token::RPAREN)
         | 
| 154 | 
            -
             | 
| 154 | 
            +
             | 
| 155 155 | 
             
                  self.new(items)
         | 
| 156 156 | 
             
                end
         | 
| 157 157 | 
             
              end
         | 
| 158 | 
            -
             | 
| 158 | 
            +
             | 
| 159 159 | 
             
              class FunctionExpression < Expression
         | 
| 160 160 | 
             
                attr_accessor :function, :arguments
         | 
| 161 | 
            -
             | 
| 161 | 
            +
             | 
| 162 162 | 
             
                def initialize(function, arguments = [])
         | 
| 163 163 | 
             
                  @function = function
         | 
| 164 164 | 
             
                  @arguments = arguments
         | 
| 165 165 | 
             
                end
         | 
| 166 | 
            -
             | 
| 166 | 
            +
             | 
| 167 167 | 
             
                def to_sql
         | 
| 168 168 | 
             
                  "#{@function}(" + @arguments.map { |e| e.to_sql }.join(', ') + ")"
         | 
| 169 169 | 
             
                end
         | 
| 170 | 
            -
             | 
| 170 | 
            +
             | 
| 171 171 | 
             
                def self.parse(tokens)
         | 
| 172 172 | 
             
                  expr = self.new(tokens.next.literal)
         | 
| 173 173 | 
             
                  tokens.consume(SQLTree::Token::LPAREN)
         | 
| @@ -176,27 +176,27 @@ module SQLTree::Node | |
| 176 176 | 
             
                    tokens.consume(SQLTree::Token::COMMA) if tokens.peek == SQLTree::Token::COMMA
         | 
| 177 177 | 
             
                  end
         | 
| 178 178 | 
             
                  tokens.consume(SQLTree::Token::RPAREN)
         | 
| 179 | 
            -
                  return expr | 
| 179 | 
            +
                  return expr
         | 
| 180 180 | 
             
                end
         | 
| 181 181 | 
             
              end
         | 
| 182 | 
            -
             | 
| 182 | 
            +
             | 
| 183 183 | 
             
              class ArithmeticExpression < Expression
         | 
| 184 184 | 
             
                attr_accessor :lhs, :rhs, :operator
         | 
| 185 | 
            -
             | 
| 185 | 
            +
             | 
| 186 186 | 
             
                def initialize(operator, lhs, rhs)
         | 
| 187 187 | 
             
                  @lhs = lhs
         | 
| 188 188 | 
             
                  @rhs = rhs
         | 
| 189 189 | 
             
                  @operator = operator
         | 
| 190 190 | 
             
                end
         | 
| 191 | 
            -
             | 
| 191 | 
            +
             | 
| 192 192 | 
             
                def to_sql
         | 
| 193 193 | 
             
                  "(#{@lhs.to_sql} #{@operator} #{@rhs.to_sql})"
         | 
| 194 194 | 
             
                end
         | 
| 195 | 
            -
             | 
| 195 | 
            +
             | 
| 196 196 | 
             
                def self.parse(tokens)
         | 
| 197 197 | 
             
                  self.parse_primary(tokens)
         | 
| 198 198 | 
             
                end
         | 
| 199 | 
            -
             | 
| 199 | 
            +
             | 
| 200 200 | 
             
                def self.parse_primary(tokens)
         | 
| 201 201 | 
             
                  expr = self.parse_secondary(tokens)
         | 
| 202 202 | 
             
                  while [SQLTree::Token::PLUS, SQLTree::Token::MINUS].include?(tokens.peek)
         | 
| @@ -204,7 +204,7 @@ module SQLTree::Node | |
| 204 204 | 
             
                  end
         | 
| 205 205 | 
             
                  return expr
         | 
| 206 206 | 
             
                end
         | 
| 207 | 
            -
             | 
| 207 | 
            +
             | 
| 208 208 | 
             
                def self.parse_secondary(tokens)
         | 
| 209 209 | 
             
                  expr = Expression.parse_atomic(tokens)
         | 
| 210 210 | 
             
                  while [SQLTree::Token::PLUS, SQLTree::Token::MINUS].include?(tokens.peek)
         | 
    
        data/lib/sql_tree/node/field.rb
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module SQLTree::Node
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class Field < Base
         | 
| 4 4 |  | 
| 5 5 | 
             
                attr_accessor :name, :table
         | 
| @@ -20,11 +20,11 @@ module SQLTree::Node | |
| 20 20 | 
             
                def to_sql
         | 
| 21 21 | 
             
                  @table.nil? ? quote_var(@name) : quote_var(@table) + '.' + quote_var(@name)
         | 
| 22 22 | 
             
                end
         | 
| 23 | 
            -
             | 
| 23 | 
            +
             | 
| 24 24 | 
             
                def ==(other)
         | 
| 25 25 | 
             
                  other.name == self.name && other.table == self.table
         | 
| 26 26 | 
             
                end
         | 
| 27 | 
            -
             | 
| 27 | 
            +
             | 
| 28 28 | 
             
                def self.parse(tokens)
         | 
| 29 29 | 
             
                  field_or_table = case tokens.next
         | 
| 30 30 | 
             
                    when SQLTree::Token::MULTIPLY then :all
         | 
    
        data/lib/sql_tree/node/join.rb
    CHANGED
    
    | @@ -15,7 +15,7 @@ module SQLTree::Node | |
| 15 15 | 
             
                  join_sql
         | 
| 16 16 | 
             
                end
         | 
| 17 17 |  | 
| 18 | 
            -
                def table | 
| 18 | 
            +
                def table
         | 
| 19 19 | 
             
                  table_reference.table
         | 
| 20 20 | 
             
                end
         | 
| 21 21 |  | 
| @@ -43,7 +43,7 @@ module SQLTree::Node | |
| 43 43 | 
             
                end
         | 
| 44 44 |  | 
| 45 45 | 
             
                def ==(other)
         | 
| 46 | 
            -
                  other.table = self.table && other.table_alias == self.table_alias && | 
| 46 | 
            +
                  other.table = self.table && other.table_alias == self.table_alias &&
         | 
| 47 47 | 
             
                    other.join_type == self.join_type && other.join_expression == self.join_expression
         | 
| 48 48 | 
             
                end
         | 
| 49 49 | 
             
              end
         | 
| @@ -1,19 +1,19 @@ | |
| 1 1 | 
             
            module SQLTree::Node
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class Ordering < Base
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
                attr_accessor :expression, :direction
         | 
| 6 | 
            -
             | 
| 6 | 
            +
             | 
| 7 7 | 
             
                def initialize(expression, direction = nil)
         | 
| 8 8 | 
             
                  @expression, @direction = expression, direction
         | 
| 9 9 | 
             
                end
         | 
| 10 | 
            -
             | 
| 10 | 
            +
             | 
| 11 11 | 
             
                def to_sql
         | 
| 12 12 | 
             
                  sql = expression.to_sql
         | 
| 13 13 | 
             
                  sql << " #{direction.to_s.upcase}" if direction
         | 
| 14 14 | 
             
                  sql
         | 
| 15 15 | 
             
                end
         | 
| 16 | 
            -
             | 
| 16 | 
            +
             | 
| 17 17 | 
             
                def self.parse(tokens)
         | 
| 18 18 | 
             
                  ordering = self.new(SQLTree::Node::Expression.parse(tokens))
         | 
| 19 19 | 
             
                  if tokens.peek && tokens.peek.direction?
         | 
| @@ -1,20 +1,20 @@ | |
| 1 1 | 
             
            module SQLTree::Node
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class SelectExpression < Base
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
                attr_accessor :expression, :variable
         | 
| 6 | 
            -
             | 
| 6 | 
            +
             | 
| 7 7 | 
             
                def initialize(expression, variable = nil)
         | 
| 8 8 | 
             
                  @expression = expression
         | 
| 9 9 | 
             
                  @variable   = variable
         | 
| 10 10 | 
             
                end
         | 
| 11 | 
            -
             | 
| 11 | 
            +
             | 
| 12 12 | 
             
                def to_sql
         | 
| 13 13 | 
             
                  sql = @expression.to_sql
         | 
| 14 14 | 
             
                  sql << " AS " << quote_var(@variable) if @variable
         | 
| 15 15 | 
             
                  return sql
         | 
| 16 16 | 
             
                end
         | 
| 17 | 
            -
             | 
| 17 | 
            +
             | 
| 18 18 | 
             
                def self.parse(tokens)
         | 
| 19 19 | 
             
                  if tokens.peek == SQLTree::Token::MULTIPLY
         | 
| 20 20 | 
             
                    tokens.consume(SQLTree::Token::MULTIPLY)
         | 
| @@ -29,17 +29,17 @@ module SQLTree::Node | |
| 29 29 | 
             
                    return expr
         | 
| 30 30 | 
             
                  end
         | 
| 31 31 | 
             
                end
         | 
| 32 | 
            -
             | 
| 32 | 
            +
             | 
| 33 33 | 
             
                def ==(other)
         | 
| 34 34 | 
             
                  other.expression == self.expression && other.variable == self.variable
         | 
| 35 | 
            -
                end | 
| 35 | 
            +
                end
         | 
| 36 36 | 
             
              end
         | 
| 37 | 
            -
             | 
| 37 | 
            +
             | 
| 38 38 | 
             
              class AllFieldsExpression < Expression
         | 
| 39 39 | 
             
                def to_sql
         | 
| 40 40 | 
             
                  '*'
         | 
| 41 41 | 
             
                end
         | 
| 42 42 | 
             
              end
         | 
| 43 | 
            -
             | 
| 43 | 
            +
             | 
| 44 44 | 
             
              ALL_FIELDS = AllFieldsExpression.new
         | 
| 45 45 | 
             
            end
         | 
| @@ -1,24 +1,26 @@ | |
| 1 1 | 
             
            module SQLTree::Node
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class SelectQuery < Base
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
                attr_accessor :distinct, :select, :from, :where, :group_by, :having, :order_by, :limit
         | 
| 6 | 
            -
             | 
| 6 | 
            +
             | 
| 7 7 | 
             
                def initialize
         | 
| 8 8 | 
             
                  @distinct = false
         | 
| 9 9 | 
             
                  @select   = []
         | 
| 10 10 | 
             
                end
         | 
| 11 | 
            -
             | 
| 11 | 
            +
             | 
| 12 12 | 
             
                def to_sql
         | 
| 13 13 | 
             
                  raise "At least one SELECT expression is required" if self.select.empty?
         | 
| 14 14 | 
             
                  sql = (self.distinct) ? "SELECT DISTINCT " : "SELECT "
         | 
| 15 15 | 
             
                  sql << select.map { |s| s.to_sql }.join(', ')
         | 
| 16 | 
            -
                  sql << " FROM " | 
| 17 | 
            -
                  sql << " WHERE " | 
| 16 | 
            +
                  sql << " FROM "     << from.map { |f| f.to_sql }.join(', ')
         | 
| 17 | 
            +
                  sql << " WHERE "    << where.to_sql if where
         | 
| 18 | 
            +
                  sql << " GROUP BY " << group_by.map { |g| g.to_sql }.join(', ') if group_by
         | 
| 18 19 | 
             
                  sql << " ORDER BY " << order_by.map { |o| o.to_sql }.join(', ') if order_by
         | 
| 20 | 
            +
                  sql << " HAVING "   << having.to_sql if having
         | 
| 19 21 | 
             
                  return sql
         | 
| 20 22 | 
             
                end
         | 
| 21 | 
            -
             | 
| 23 | 
            +
             | 
| 22 24 | 
             
                # Uses the provided initialized parser to parse a SELECT query.
         | 
| 23 25 | 
             
                def self.parse(tokens)
         | 
| 24 26 | 
             
                  select_node = self.new
         | 
| @@ -32,10 +34,14 @@ module SQLTree::Node | |
| 32 34 | 
             
                  select_node.select   = self.parse_select_clause(tokens)
         | 
| 33 35 | 
             
                  select_node.from     = self.parse_from_clause(tokens)   if tokens.peek == SQLTree::Token::FROM
         | 
| 34 36 | 
             
                  select_node.where    = self.parse_where_clause(tokens)  if tokens.peek == SQLTree::Token::WHERE
         | 
| 37 | 
            +
                  if tokens.peek == SQLTree::Token::GROUP
         | 
| 38 | 
            +
                    select_node.group_by = self.parse_group_clause(tokens)
         | 
| 39 | 
            +
                    select_node.having   = self.parse_having_clause(tokens) if tokens.peek == SQLTree::Token::HAVING
         | 
| 40 | 
            +
                  end
         | 
| 35 41 | 
             
                  select_node.order_by = self.parse_order_clause(tokens)  if tokens.peek == SQLTree::Token::ORDER
         | 
| 36 42 | 
             
                  return select_node
         | 
| 37 43 | 
             
                end
         | 
| 38 | 
            -
             | 
| 44 | 
            +
             | 
| 39 45 | 
             
                def self.parse_select_clause(tokens)
         | 
| 40 46 | 
             
                  expressions = [SQLTree::Node::SelectExpression.parse(tokens)]
         | 
| 41 47 | 
             
                  while tokens.peek == SQLTree::Token::COMMA
         | 
| @@ -44,7 +50,7 @@ module SQLTree::Node | |
| 44 50 | 
             
                  end
         | 
| 45 51 | 
             
                  return expressions
         | 
| 46 52 | 
             
                end
         | 
| 47 | 
            -
             | 
| 53 | 
            +
             | 
| 48 54 | 
             
                def self.parse_from_clause(tokens)
         | 
| 49 55 | 
             
                  tokens.consume(SQLTree::Token::FROM)
         | 
| 50 56 | 
             
                  sources = [SQLTree::Node::Source.parse(tokens)]
         | 
| @@ -54,20 +60,28 @@ module SQLTree::Node | |
| 54 60 | 
             
                  end
         | 
| 55 61 | 
             
                  return sources
         | 
| 56 62 | 
             
                end
         | 
| 57 | 
            -
             | 
| 63 | 
            +
             | 
| 58 64 | 
             
                def self.parse_where_clause(tokens)
         | 
| 59 65 | 
             
                  tokens.consume(SQLTree::Token::WHERE)
         | 
| 60 66 | 
             
                  Expression.parse(tokens)
         | 
| 61 67 | 
             
                end
         | 
| 62 68 |  | 
| 63 69 | 
             
                def self.parse_group_clause(tokens)
         | 
| 64 | 
            -
                   | 
| 70 | 
            +
                  tokens.consume(SQLTree::Token::GROUP)
         | 
| 71 | 
            +
                  tokens.consume(SQLTree::Token::BY)
         | 
| 72 | 
            +
                  exprs = [SQLTree::Node::Expression.parse(tokens)]
         | 
| 73 | 
            +
                  while tokens.peek == SQLTree::Token::COMMA
         | 
| 74 | 
            +
                    tokens.consume(SQLTree::Token::COMMA)
         | 
| 75 | 
            +
                    exprs << SQLTree::Node::Expression.parse(tokens)
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                  return exprs
         | 
| 65 78 | 
             
                end
         | 
| 66 79 |  | 
| 67 80 | 
             
                def self.parse_having_clause(tokens)
         | 
| 68 | 
            -
                   | 
| 81 | 
            +
                  tokens.consume(SQLTree::Token::HAVING)
         | 
| 82 | 
            +
                  SQLTree::Node::Expression.parse(tokens)
         | 
| 69 83 | 
             
                end
         | 
| 70 | 
            -
             | 
| 84 | 
            +
             | 
| 71 85 | 
             
                def self.parse_order_clause(tokens)
         | 
| 72 86 | 
             
                  tokens.consume(SQLTree::Token::ORDER)
         | 
| 73 87 | 
             
                  tokens.consume(SQLTree::Token::BY)
         | 
    
        data/lib/sql_tree/node/source.rb
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            module SQLTree::Node
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class Source < Base
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
                attr_accessor :table_reference, :joins
         | 
| 6 | 
            -
             | 
| 6 | 
            +
             | 
| 7 7 | 
             
                def initialize(table_reference, joins = [])
         | 
| 8 8 | 
             
                  @table_reference, @joins = table_reference, joins
         | 
| 9 9 | 
             
                end
         | 
| 10 | 
            -
             | 
| 10 | 
            +
             | 
| 11 11 | 
             
                def table
         | 
| 12 12 | 
             
                  table_reference.table
         | 
| 13 13 | 
             
                end
         | 
| @@ -15,7 +15,7 @@ module SQLTree::Node | |
| 15 15 | 
             
                def table_alias
         | 
| 16 16 | 
             
                  table_reference.table_alias
         | 
| 17 17 | 
             
                end
         | 
| 18 | 
            -
             | 
| 18 | 
            +
             | 
| 19 19 | 
             
                def to_sql
         | 
| 20 20 | 
             
                  sql = table_reference.to_sql
         | 
| 21 21 | 
             
                  sql << ' ' << joins.map { |j| j.to_sql }.join(' ') if joins.any?
         | 
| @@ -33,5 +33,5 @@ module SQLTree::Node | |
| 33 33 | 
             
                  end
         | 
| 34 34 | 
             
                  return source
         | 
| 35 35 | 
             
                end
         | 
| 36 | 
            -
              end | 
| 36 | 
            +
              end
         | 
| 37 37 | 
             
            end
         | 
| @@ -17,7 +17,7 @@ module SQLTree::Node | |
| 17 17 | 
             
                def ==(other)
         | 
| 18 18 | 
             
                  other.table = self.table && other.table_alias == self.table_alias
         | 
| 19 19 | 
             
                end
         | 
| 20 | 
            -
             | 
| 20 | 
            +
             | 
| 21 21 | 
             
                def self.parse(tokens)
         | 
| 22 22 | 
             
                  if SQLTree::Token::Variable === tokens.next
         | 
| 23 23 | 
             
                    table_reference = self.new(tokens.current.literal)
         | 
| @@ -26,7 +26,7 @@ module SQLTree::Node | |
| 26 26 | 
             
                      table_reference.table_alias = SQLTree::Node::Variable.parse(tokens).name
         | 
| 27 27 | 
             
                    end
         | 
| 28 28 | 
             
                    return table_reference
         | 
| 29 | 
            -
                  else | 
| 29 | 
            +
                  else
         | 
| 30 30 | 
             
                    raise SQLTree::Parser::UnexpectedToken.new(tokens.current)
         | 
| 31 31 | 
             
                  end
         | 
| 32 32 | 
             
                end
         | 
    
        data/lib/sql_tree/node/value.rb
    CHANGED
    
    | @@ -1,12 +1,12 @@ | |
| 1 1 | 
             
            module SQLTree::Node
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class Value < Base
         | 
| 4 4 | 
             
                attr_accessor :value
         | 
| 5 5 |  | 
| 6 6 | 
             
                def initialize(value)
         | 
| 7 7 | 
             
                  @value = value
         | 
| 8 8 | 
             
                end
         | 
| 9 | 
            -
             | 
| 9 | 
            +
             | 
| 10 10 | 
             
                def to_sql
         | 
| 11 11 | 
             
                  case value
         | 
| 12 12 | 
             
                  when nil    then 'NULL'
         | 
| @@ -14,11 +14,11 @@ module SQLTree::Node | |
| 14 14 | 
             
                  else             @value.to_s
         | 
| 15 15 | 
             
                  end
         | 
| 16 16 | 
             
                end
         | 
| 17 | 
            -
             | 
| 17 | 
            +
             | 
| 18 18 | 
             
                def ==(other)
         | 
| 19 19 | 
             
                  other.kind_of?(self.class) && other.value == self.value
         | 
| 20 20 | 
             
                end
         | 
| 21 | 
            -
             | 
| 21 | 
            +
             | 
| 22 22 | 
             
                def self.parse(tokens)
         | 
| 23 23 | 
             
                  case tokens.next
         | 
| 24 24 | 
             
                  when SQLTree::Token::String, SQLTree::Token::Number
         | 
| @@ -27,7 +27,7 @@ module SQLTree::Node | |
| 27 27 | 
             
                    SQLTree::Node::Value.new(nil)
         | 
| 28 28 | 
             
                  else
         | 
| 29 29 | 
             
                    raise SQLTree::Parser::UnexpectedToken.new(tokens.current, :literal)
         | 
| 30 | 
            -
                  end | 
| 30 | 
            +
                  end
         | 
| 31 31 | 
             
                end
         | 
| 32 32 | 
             
              end
         | 
| 33 33 | 
             
            end
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module SQLTree::Node
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class Variable < Base
         | 
| 4 4 |  | 
| 5 5 | 
             
                attr_accessor :name
         | 
| @@ -7,11 +7,11 @@ module SQLTree::Node | |
| 7 7 | 
             
                def initialize(name)
         | 
| 8 8 | 
             
                  @name = name
         | 
| 9 9 | 
             
                end
         | 
| 10 | 
            -
             | 
| 10 | 
            +
             | 
| 11 11 | 
             
                def to_sql
         | 
| 12 12 | 
             
                  quote_var(@name)
         | 
| 13 13 | 
             
                end
         | 
| 14 | 
            -
             | 
| 14 | 
            +
             | 
| 15 15 | 
             
                def ==(other)
         | 
| 16 16 | 
             
                  other.name == self.name
         | 
| 17 17 | 
             
                end
         | 
    
        data/lib/sql_tree/node.rb
    CHANGED
    
    | @@ -10,23 +10,23 @@ module SQLTree::Node | |
| 10 10 | 
             
                def inspect
         | 
| 11 11 | 
             
                  "#{self.class.name}[#{self.to_sql}]"
         | 
| 12 12 | 
             
                end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                # Quotes a variable name so that it can be safely used within | 
| 13 | 
            +
             | 
| 14 | 
            +
                # Quotes a variable name so that it can be safely used within
         | 
| 15 15 | 
             
                # SQL queries.
         | 
| 16 16 | 
             
                def quote_var(name)
         | 
| 17 17 | 
             
                  "\"#{name}\""
         | 
| 18 18 | 
             
                end
         | 
| 19 | 
            -
             | 
| 19 | 
            +
             | 
| 20 20 | 
             
                # Quotes a string so that it can be used within an SQL query.
         | 
| 21 21 | 
             
                def quote_str(str)
         | 
| 22 22 | 
             
                  "'#{str.gsub(/\'/, "''")}'"
         | 
| 23 23 | 
             
                end
         | 
| 24 | 
            -
             | 
| 24 | 
            +
             | 
| 25 25 | 
             
                # This method should be implemented by a subclass.
         | 
| 26 26 | 
             
                def self.parse(tokens)
         | 
| 27 27 | 
             
                  raise 'Only implemented in subclasses!'
         | 
| 28 28 | 
             
                end
         | 
| 29 | 
            -
             | 
| 29 | 
            +
             | 
| 30 30 | 
             
                # Parses a string, expecting it to be parsable to an instance of
         | 
| 31 31 | 
             
                # the current class.
         | 
| 32 32 | 
             
                def self.[](sql, options = {})
         | 
    
        data/lib/sql_tree/parser.rb
    CHANGED
    
    | @@ -1,9 +1,9 @@ | |
| 1 1 | 
             
            class SQLTree::Parser
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              class UnexpectedToken < StandardError
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
                attr_reader :expected_token, :actual_token
         | 
| 6 | 
            -
             | 
| 6 | 
            +
             | 
| 7 7 | 
             
                def initialize(actual_token, expected_token = nil)
         | 
| 8 8 | 
             
                  @expected_token, @actual_token = expected_token, actual_token
         | 
| 9 9 | 
             
                  message =  "Unexpected token: found #{actual_token.inspect}"
         | 
| @@ -11,7 +11,7 @@ class SQLTree::Parser | |
| 11 11 | 
             
                  message << '!'
         | 
| 12 12 | 
             
                  super(message)
         | 
| 13 13 | 
             
                end
         | 
| 14 | 
            -
              end | 
| 14 | 
            +
              end
         | 
| 15 15 |  | 
| 16 16 | 
             
              def self.parse(sql_string, options = {})
         | 
| 17 17 | 
             
                self.new(sql_string, options).parse!
         | 
| @@ -31,21 +31,21 @@ class SQLTree::Parser | |
| 31 31 | 
             
              def current
         | 
| 32 32 | 
             
                @current_token
         | 
| 33 33 | 
             
              end
         | 
| 34 | 
            -
             | 
| 34 | 
            +
             | 
| 35 35 | 
             
              def next
         | 
| 36 36 | 
             
                @current_token = @tokens.shift
         | 
| 37 37 | 
             
              end
         | 
| 38 | 
            -
             | 
| 38 | 
            +
             | 
| 39 39 | 
             
              def consume(*checks)
         | 
| 40 40 | 
             
                checks.each do |check|
         | 
| 41 41 | 
             
                  raise UnexpectedToken.new(self.current, check) unless check == self.next
         | 
| 42 42 | 
             
                end
         | 
| 43 43 | 
             
              end
         | 
| 44 | 
            -
             | 
| 44 | 
            +
             | 
| 45 45 | 
             
              def peek(distance = 1)
         | 
| 46 46 | 
             
                @tokens[distance - 1]
         | 
| 47 47 | 
             
              end
         | 
| 48 | 
            -
             | 
| 48 | 
            +
             | 
| 49 49 | 
             
              def peek_tokens(amount)
         | 
| 50 50 | 
             
                @tokens[0, amount]
         | 
| 51 51 | 
             
              end
         | 
| @@ -53,7 +53,7 @@ class SQLTree::Parser | |
| 53 53 | 
             
              def debug
         | 
| 54 54 | 
             
                puts @tokens.inspect
         | 
| 55 55 | 
             
              end
         | 
| 56 | 
            -
             | 
| 56 | 
            +
             | 
| 57 57 | 
             
              def parse!
         | 
| 58 58 | 
             
                case self.peek
         | 
| 59 59 | 
             
                when SQLTree::Token::SELECT then SQLTree::Node::SelectQuery.parse(self)
         | 
    
        data/lib/sql_tree/token.rb
    CHANGED
    
    | @@ -8,60 +8,60 @@ class SQLTree::Token | |
| 8 8 |  | 
| 9 9 | 
             
              # For some tokens, the encountered literal value is important
         | 
| 10 10 | 
             
              # during the parsing phase (e.g. strings and variable names).
         | 
| 11 | 
            -
              # Therefore, the literal value encountered that represented the | 
| 11 | 
            +
              # Therefore, the literal value encountered that represented the
         | 
| 12 12 | 
             
              # token in the original SQL query string is stored.
         | 
| 13 13 | 
             
              attr_accessor :literal
         | 
| 14 14 |  | 
| 15 15 | 
             
              # Creates a token instance with a given literal representation.
         | 
| 16 16 | 
             
              #
         | 
| 17 | 
            -
              # <tt>literal<tt>:: The literal string value that was encountered | 
| 17 | 
            +
              # <tt>literal<tt>:: The literal string value that was encountered
         | 
| 18 18 | 
             
              #                   while tokenizing.
         | 
| 19 19 | 
             
              def initialize(literal)
         | 
| 20 20 | 
             
                @literal = literal
         | 
| 21 21 | 
             
              end
         | 
| 22 | 
            -
             | 
| 22 | 
            +
             | 
| 23 23 | 
             
              # Compares two tokens. Tokens are considered equal when they are
         | 
| 24 24 | 
             
              # instances of the same class, i.e. do literal is not used.
         | 
| 25 25 | 
             
              def ==(other)
         | 
| 26 26 | 
             
                other.class == self.class
         | 
| 27 27 | 
             
              end
         | 
| 28 | 
            -
             | 
| 28 | 
            +
             | 
| 29 29 | 
             
              def inspect # :nodoc:
         | 
| 30 30 | 
             
                literal
         | 
| 31 31 | 
             
              end
         | 
| 32 | 
            -
             | 
| 32 | 
            +
             | 
| 33 33 | 
             
              def join?
         | 
| 34 | 
            -
                [SQLTree::Token::JOIN, SQLTree::Token::LEFT, SQLTree::Token::RIGHT, | 
| 35 | 
            -
                  SQLTree::Token::INNER, SQLTree::Token::OUTER, SQLTree::Token::NATURAL, | 
| 34 | 
            +
                [SQLTree::Token::JOIN, SQLTree::Token::LEFT, SQLTree::Token::RIGHT,
         | 
| 35 | 
            +
                  SQLTree::Token::INNER, SQLTree::Token::OUTER, SQLTree::Token::NATURAL,
         | 
| 36 36 | 
             
                  SQLTree::Token::FULL].include?(self)
         | 
| 37 37 | 
             
              end
         | 
| 38 | 
            -
             | 
| 38 | 
            +
             | 
| 39 39 | 
             
              def direction?
         | 
| 40 40 | 
             
                [SQLTree::Token::ASC, SQLTree::Token::DESC].include?(self)
         | 
| 41 41 | 
             
              end
         | 
| 42 | 
            -
             | 
| 42 | 
            +
             | 
| 43 43 | 
             
              ###################################################################
         | 
| 44 44 | 
             
              # DYNAMIC TOKEN TYPES
         | 
| 45 45 | 
             
              ###################################################################
         | 
| 46 46 |  | 
| 47 47 | 
             
              # The <tt>SQLTree::Token::Value</tt> class is the base class for
         | 
| 48 | 
            -
              # every dynamic token. A dynamic token is a token for which the | 
| 48 | 
            +
              # every dynamic token. A dynamic token is a token for which the
         | 
| 49 49 | 
             
              # literal value used remains impoirtant during parsing.
         | 
| 50 50 | 
             
              class Value < SQLTree::Token
         | 
| 51 | 
            -
             | 
| 51 | 
            +
             | 
| 52 52 | 
             
                def inspect # :nodoc:
         | 
| 53 53 | 
             
                  "#<#{self.class.name.split('::').last}:#{literal.inspect}>"
         | 
| 54 54 | 
             
                end
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                # Compares two tokens. For values, the literal encountered value | 
| 55 | 
            +
             | 
| 56 | 
            +
                # Compares two tokens. For values, the literal encountered value
         | 
| 57 57 | 
             
                # of the token is also taken into account besides the class.
         | 
| 58 58 | 
             
                def ==(other)
         | 
| 59 59 | 
             
                  other.class == self.class && @literal == other.literal
         | 
| 60 60 | 
             
                end
         | 
| 61 61 | 
             
              end
         | 
| 62 | 
            -
             | 
| 62 | 
            +
             | 
| 63 63 | 
             
              # The <tt>SQLTree::Token::Variable</tt> class represents SQL
         | 
| 64 | 
            -
              # variables. The variable name is stored in the literal as string, | 
| 64 | 
            +
              # variables. The variable name is stored in the literal as string,
         | 
| 65 65 | 
             
              # without quotes if they were present.
         | 
| 66 66 | 
             
              class Variable < SQLTree::Token::Value
         | 
| 67 67 | 
             
              end
         | 
| @@ -76,7 +76,7 @@ class SQLTree::Token | |
| 76 76 | 
             
              # literal.
         | 
| 77 77 | 
             
              class Number < SQLTree::Token::Value
         | 
| 78 78 | 
             
              end
         | 
| 79 | 
            -
             | 
| 79 | 
            +
             | 
| 80 80 | 
             
              ###################################################################
         | 
| 81 81 | 
             
              # STATIC TOKEN TYPES
         | 
| 82 82 | 
             
              ###################################################################
         | 
| @@ -111,7 +111,7 @@ class SQLTree::Token | |
| 111 111 | 
             
              COMMA  = Class.new(SQLTree::Token).new(',')
         | 
| 112 112 |  | 
| 113 113 | 
             
              # A list of all the SQL reserverd keywords.
         | 
| 114 | 
            -
              KEYWORDS = %w{SELECT FROM WHERE GROUP HAVING ORDER DISTINCT LEFT RIGHT INNER FULL OUTER NATURAL JOIN USING | 
| 114 | 
            +
              KEYWORDS = %w{SELECT FROM WHERE GROUP HAVING ORDER DISTINCT LEFT RIGHT INNER FULL OUTER NATURAL JOIN USING
         | 
| 115 115 | 
             
                            AND OR NOT AS ON IS NULL BY LIKE ILIKE BETWEEN IN ASC DESC}
         | 
| 116 116 |  | 
| 117 117 | 
             
              # Create a token for all the reserved keywords in SQL
         | 
| @@ -129,7 +129,7 @@ class SQLTree::Token | |
| 129 129 | 
             
              OPERATORS_HASH.each_pair do |literal, symbol|
         | 
| 130 130 | 
             
                self.const_set(symbol.to_s.upcase, Class.new(SQLTree::Token::Operator).new(literal)) unless self.const_defined?(symbol.to_s.upcase)
         | 
| 131 131 | 
             
              end
         | 
| 132 | 
            -
             | 
| 132 | 
            +
             | 
| 133 133 | 
             
              COMPARISON_OPERATORS = COMPARISON_OPERATORS_HASH.map { |(literal, symbol)| const_get(symbol.to_s.upcase) } +
         | 
| 134 134 | 
             
                [SQLTree::Token::IN, SQLTree::Token::IS, SQLTree::Token::BETWEEN, SQLTree::Token::LIKE, SQLTree::Token::ILIKE, SQLTree::Token::NOT]
         | 
| 135 135 | 
             
            end
         | 
    
        data/lib/sql_tree/tokenizer.rb
    CHANGED
    
    | @@ -11,17 +11,17 @@ | |
| 11 11 | 
             
            # the <tt>each_token</tt> (aliased to <tt>each</tt>) will yield every
         | 
| 12 12 | 
             
            # token one by one.
         | 
| 13 13 | 
             
            class SQLTree::Tokenizer
         | 
| 14 | 
            -
             | 
| 14 | 
            +
             | 
| 15 15 | 
             
              include Enumerable
         | 
| 16 | 
            -
             | 
| 16 | 
            +
             | 
| 17 17 | 
             
              # The keyword queue, on which kywords are placed before they are yielded
         | 
| 18 18 | 
             
              # to the parser, to enable keyword combining (e.g. NOT LIKE)
         | 
| 19 19 | 
             
              attr_reader :keyword_queue
         | 
| 20 | 
            -
             | 
| 20 | 
            +
             | 
| 21 21 | 
             
              def initialize # :nodoc:
         | 
| 22 22 | 
             
                @keyword_queue = []
         | 
| 23 23 | 
             
              end
         | 
| 24 | 
            -
             | 
| 24 | 
            +
             | 
| 25 25 | 
             
              # Returns an array of tokens for the given string.
         | 
| 26 26 | 
             
              # <tt>string</tt>:: the string to tokenize
         | 
| 27 27 | 
             
              def tokenize(string)
         | 
| @@ -29,7 +29,7 @@ class SQLTree::Tokenizer | |
| 29 29 | 
             
                @current_char_pos = -1
         | 
| 30 30 | 
             
                self.entries
         | 
| 31 31 | 
             
              end
         | 
| 32 | 
            -
             | 
| 32 | 
            +
             | 
| 33 33 | 
             
              # Returns the current character that is being tokenized
         | 
| 34 34 | 
             
              def current_char
         | 
| 35 35 | 
             
                @current_char
         | 
| @@ -51,7 +51,7 @@ class SQLTree::Tokenizer | |
| 51 51 |  | 
| 52 52 | 
             
              # Combines several tokens to a single token if possible, and
         | 
| 53 53 | 
             
              # yields teh result, or yields every single token if they cannot
         | 
| 54 | 
            -
              # be combined. | 
| 54 | 
            +
              # be combined.
         | 
| 55 55 | 
             
              # <tt>token</tt>:: the token to yield or combine
         | 
| 56 56 | 
             
              # <tt>block</tt>:: the block to yield tokens and combined tokens to.
         | 
| 57 57 | 
             
              def handle_token(token, &block) # :yields: SQLTree::Token
         | 
| @@ -62,7 +62,7 @@ class SQLTree::Tokenizer | |
| 62 62 | 
             
                  block.call(token)
         | 
| 63 63 | 
             
                end
         | 
| 64 64 | 
             
              end
         | 
| 65 | 
            -
             | 
| 65 | 
            +
             | 
| 66 66 | 
             
              # This method ensures that every keyword currently in the queue is
         | 
| 67 67 | 
             
              # yielded. This method get called by <tt>handle_token</tt> when it
         | 
| 68 68 | 
             
              # knows for sure that the keywords on the queue cannot be combined
         | 
| @@ -73,10 +73,10 @@ class SQLTree::Tokenizer | |
| 73 73 | 
             
              end
         | 
| 74 74 |  | 
| 75 75 | 
             
              # Iterator method that yields each token that is encountered in the
         | 
| 76 | 
            -
              # SQL stream. These tokens are passed to the SQL parser to construct | 
| 76 | 
            +
              # SQL stream. These tokens are passed to the SQL parser to construct
         | 
| 77 77 | 
             
              # a syntax tree for the SQL query.
         | 
| 78 78 | 
             
              #
         | 
| 79 | 
            -
              # This method is aliased to <tt>:each</tt> to make the Enumerable | 
| 79 | 
            +
              # This method is aliased to <tt>:each</tt> to make the Enumerable
         | 
| 80 80 | 
             
              # methods work on this method.
         | 
| 81 81 | 
             
              def each_token(&block) # :yields: SQLTree::Token
         | 
| 82 82 | 
             
                while next_char
         | 
| @@ -93,13 +93,13 @@ class SQLTree::Tokenizer | |
| 93 93 | 
             
                  when '"';            tokenize_quoted_variable(&block)     # TODO: allow MySQL quoting mode
         | 
| 94 94 | 
             
                  end
         | 
| 95 95 | 
             
                end
         | 
| 96 | 
            -
             | 
| 96 | 
            +
             | 
| 97 97 | 
             
                # Make sure to yield any tokens that are still stashed on the queue.
         | 
| 98 98 | 
             
                empty_keyword_queue!(&block)
         | 
| 99 99 | 
             
              end
         | 
| 100 | 
            -
             | 
| 100 | 
            +
             | 
| 101 101 | 
             
              alias :each :each_token
         | 
| 102 | 
            -
             | 
| 102 | 
            +
             | 
| 103 103 | 
             
              # Tokenizes a eyword in the code. This can either be a reserved SQL keyword
         | 
| 104 104 | 
             
              # or a variable. This method will yield variables directly. Keywords will be
         | 
| 105 105 | 
             
              # yielded with a delay, because they may need to be combined with other
         | 
| @@ -107,7 +107,7 @@ class SQLTree::Tokenizer | |
| 107 107 | 
             
              def tokenize_keyword(&block) # :yields: SQLTree::Token
         | 
| 108 108 | 
             
                literal = current_char
         | 
| 109 109 | 
             
                literal << next_char while /[\w]/ =~ peek_char
         | 
| 110 | 
            -
             | 
| 110 | 
            +
             | 
| 111 111 | 
             
                if SQLTree::Token::KEYWORDS.include?(literal.upcase)
         | 
| 112 112 | 
             
                  handle_token(SQLTree::Token.const_get(literal.upcase), &block)
         | 
| 113 113 | 
             
                else
         | 
| @@ -125,7 +125,7 @@ class SQLTree::Tokenizer | |
| 125 125 | 
             
                  dot_encountered = true if peek_char == '.'
         | 
| 126 126 | 
             
                  number << next_char
         | 
| 127 127 | 
             
                end
         | 
| 128 | 
            -
             | 
| 128 | 
            +
             | 
| 129 129 | 
             
                if dot_encountered
         | 
| 130 130 | 
             
                  handle_token(SQLTree::Token::Number.new(number.to_f), &block)
         | 
| 131 131 | 
             
                else
         | 
| @@ -160,7 +160,7 @@ class SQLTree::Tokenizer | |
| 160 160 | 
             
              # A regular expression that matches all operator characters.
         | 
| 161 161 | 
             
              OPERATOR_CHARS = /\=|<|>|!|\-|\+|\/|\*|\%/
         | 
| 162 162 |  | 
| 163 | 
            -
              # Tokenizes an operator in the SQL stream. This method will yield the | 
| 163 | 
            +
              # Tokenizes an operator in the SQL stream. This method will yield the
         | 
| 164 164 | 
             
              # operator token when the last character of the token is encountered.
         | 
| 165 165 | 
             
              def tokenize_operator(&block) # :yields: SQLTree::Token
         | 
| 166 166 | 
             
                operator = current_char
         | 
    
        data/lib/sql_tree.rb
    CHANGED
    
    | @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            # The SQLTree module is the basic namespace for the sql_tree gem.
         | 
| 2 2 | 
             
            #
         | 
| 3 3 | 
             
            # It contains the shorthand parse method (i.e. <tt>SQLTree[sql_query]</tt>)
         | 
| 4 | 
            -
            # and some helper methods that are used by the gem. It also requires the | 
| 4 | 
            +
            # and some helper methods that are used by the gem. It also requires the
         | 
| 5 5 | 
             
            # necessary files for the gem to function properly.
         | 
| 6 6 | 
             
            module SQLTree
         | 
| 7 | 
            -
             | 
| 7 | 
            +
             | 
| 8 8 | 
             
              # Loads constants in the SQLTree namespace using self.load_default_class_file(base, const)
         | 
| 9 9 | 
             
              # <tt>const</tt>:: The constant that is not yet loaded in the SQLTree namespace. This should be passed as a string or symbol.
         | 
| 10 10 | 
             
              def self.const_missing(const)
         | 
| @@ -18,14 +18,14 @@ module SQLTree | |
| 18 18 | 
             
              def self.load_default_class_file(base, const)
         | 
| 19 19 | 
             
                require "#{to_underscore("#{base.name}::#{const}")}"
         | 
| 20 20 | 
             
                base.const_get(const) if base.const_defined?(const)
         | 
| 21 | 
            -
              end | 
| 22 | 
            -
             | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 23 | 
             
              # The <tt>[]</tt> method is a shorthand for the <tt>SQLTree::Parser.parse</tt>
         | 
| 24 24 | 
             
              # method to parse an SQL query and return a SQL syntax tree.
         | 
| 25 25 | 
             
              def self.[](query, options = {})
         | 
| 26 26 | 
             
                SQLTree::Parser.parse(query)
         | 
| 27 27 | 
             
              end
         | 
| 28 | 
            -
             | 
| 28 | 
            +
             | 
| 29 29 | 
             
              # Convert a string/symbol in camelcase (RequestLogAnalyzer::Controller) to underscores (request_log_analyzer/controller)
         | 
| 30 30 | 
             
              # This function can be used to load the file (using require) in which the given constant is defined.
         | 
| 31 31 | 
             
              # <tt>str</tt>:: The string to convert in the following format: <tt>ModuleName::ClassName</tt>
         | 
| @@ -38,5 +38,5 @@ module SQLTree | |
| 38 38 | 
             
              # <tt>str</tt>:: The string to convert in the following format: <tt>module_name/class_name</tt>
         | 
| 39 39 | 
             
              def self.to_camelcase(str)
         | 
| 40 40 | 
             
                str.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
         | 
| 41 | 
            -
              end | 
| 41 | 
            +
              end
         | 
| 42 42 | 
             
            end
         | 
| @@ -5,32 +5,37 @@ describe SQLTree, 'parsing and generating SQL' do | |
| 5 5 | 
             
              it "should parse and generate SQL fo a simple list query" do
         | 
| 6 6 | 
             
                SQLTree["SELECT * FROM table"].to_sql.should == 'SELECT * FROM "table"'
         | 
| 7 7 | 
             
              end
         | 
| 8 | 
            -
             | 
| 8 | 
            +
             | 
| 9 9 | 
             
              it "should parse and generate the DISTINCT keyword" do
         | 
| 10 10 | 
             
                SQLTree["SELECT DISTINCT * FROM table"].to_sql.should == 'SELECT DISTINCT * FROM "table"'
         | 
| 11 11 | 
             
              end
         | 
| 12 | 
            -
             | 
| 12 | 
            +
             | 
| 13 13 | 
             
              it 'should parse and generate table aliases' do
         | 
| 14 14 | 
             
                SQLTree["SELECT a.* FROM table AS a"].to_sql.should == 'SELECT "a".* FROM "table" AS "a"'
         | 
| 15 15 | 
             
              end
         | 
| 16 | 
            -
             | 
| 16 | 
            +
             | 
| 17 17 | 
             
              it "should parse and generate an ORDER BY clause" do
         | 
| 18 | 
            -
                SQLTree["SELECT * FROM table ORDER BY field1, field2"].to_sql.should == | 
| 18 | 
            +
                SQLTree["SELECT * FROM table ORDER BY field1, field2"].to_sql.should ==
         | 
| 19 19 | 
             
                        'SELECT * FROM "table" ORDER BY "field1", "field2"'
         | 
| 20 20 | 
             
              end
         | 
| 21 | 
            -
             | 
| 21 | 
            +
             | 
| 22 22 | 
             
              it "should parse and generate an expression in the SELECT clause" do
         | 
| 23 23 | 
             
                SQLTree['SELECT MD5( a)  AS  a,    b  > 0  AS  test  FROM  table'].to_sql.should ==
         | 
| 24 24 | 
             
                        'SELECT MD5("a") AS "a", ("b" > 0) AS "test" FROM "table"'
         | 
| 25 25 | 
             
              end
         | 
| 26 | 
            -
             | 
| 26 | 
            +
             | 
| 27 27 | 
             
              it "should parse and generate a complex FROM clause" do
         | 
| 28 28 | 
             
                SQLTree['SELECT * FROM  a  LEFT JOIN  b  ON ( a.id    = b.a_id),      c  AS  d'].to_sql.should ==
         | 
| 29 29 | 
             
                        'SELECT * FROM "a" LEFT JOIN "b" ON ("a"."id" = "b"."a_id"), "c" AS "d"'
         | 
| 30 30 | 
             
              end
         | 
| 31 | 
            -
             | 
| 31 | 
            +
             | 
| 32 32 | 
             
              it "should parse and generate a WHERE clause" do
         | 
| 33 33 | 
             
                SQLTree['SELECT * FROM  t  WHERE (   field  > 4  OR  NOW() >  timestamp)   AND   other_field  IS NOT NULL'].to_sql.should ==
         | 
| 34 34 | 
             
                        'SELECT * FROM "t" WHERE ((("field" > 4) OR (NOW() > "timestamp")) AND ("other_field" IS NOT NULL))'
         | 
| 35 35 | 
             
              end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              it "should parse and generate a GROUP BY and HAVING clause" do
         | 
| 38 | 
            +
                SQLTree['SELECT SUM( field1 ) FROM  t  GROUP BY  field1,  MD5( field2 ) HAVING  SUM( field1 ) > 10'].to_sql.should ==
         | 
| 39 | 
            +
                        'SELECT SUM("field1") FROM "t" GROUP BY "field1", MD5("field2") HAVING (SUM("field1") > 10)'
         | 
| 40 | 
            +
              end
         | 
| 36 41 | 
             
            end
         | 
    
        data/spec/lib/matchers.rb
    CHANGED
    
    | @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            class TokenizeTo
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 3 | 
             
              def initialize(expected_tokens)
         | 
| 4 4 | 
             
                @expected_tokens = expected_tokens.map do |t|
         | 
| 5 5 | 
             
                  case t
         | 
| @@ -11,24 +11,24 @@ class TokenizeTo | |
| 11 11 | 
             
                  end
         | 
| 12 12 | 
             
                end
         | 
| 13 13 | 
             
              end
         | 
| 14 | 
            -
             | 
| 14 | 
            +
             | 
| 15 15 | 
             
              def matches?(found_tokens)
         | 
| 16 16 | 
             
                @found_tokens = found_tokens
         | 
| 17 17 | 
             
                return @found_tokens == @expected_tokens
         | 
| 18 18 | 
             
              end
         | 
| 19 | 
            -
             | 
| 19 | 
            +
             | 
| 20 20 | 
             
              def description
         | 
| 21 21 | 
             
                "expected to tokenized to #{@expected_tokens.inspect}"
         | 
| 22 22 | 
             
              end
         | 
| 23 | 
            -
             | 
| 23 | 
            +
             | 
| 24 24 | 
             
              def failure_message
         | 
| 25 25 | 
             
                " #{@expected_tokens.inspect} expected, but found #{@found_tokens.inspect}"
         | 
| 26 26 | 
             
              end
         | 
| 27 | 
            -
             | 
| 27 | 
            +
             | 
| 28 28 | 
             
              def negative_failure_message
         | 
| 29 29 | 
             
                " expected not to be tokenized to #{@expected_tokens.inspect}"
         | 
| 30 | 
            -
              end | 
| 31 | 
            -
             | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 32 | 
             
            end
         | 
| 33 33 |  | 
| 34 34 | 
             
            def tokenize_to(*expected_tokens)
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -11,16 +11,16 @@ module SQLTree::Spec | |
| 11 11 | 
             
                  SQLTree::Node.const_get(const)
         | 
| 12 12 | 
             
                end
         | 
| 13 13 | 
             
              end
         | 
| 14 | 
            -
             | 
| 14 | 
            +
             | 
| 15 15 | 
             
              module TokenLoader
         | 
| 16 16 | 
             
                def self.const_missing(const)
         | 
| 17 17 | 
             
                  SQLTree::Token.const_get(const)
         | 
| 18 18 | 
             
                end
         | 
| 19 | 
            -
              end | 
| 19 | 
            +
              end
         | 
| 20 20 | 
             
            end
         | 
| 21 21 |  | 
| 22 22 | 
             
            Spec::Runner.configure do |config|
         | 
| 23 | 
            -
             | 
| 23 | 
            +
             | 
| 24 24 | 
             
            end
         | 
| 25 25 |  | 
| 26 26 | 
             
            require "#{File.dirname(__FILE__)}/lib/matchers"
         | 
| @@ -35,12 +35,12 @@ describe SQLTree::Node::Expression do | |
| 35 35 | 
             
                  logical = SQLTree::Node::Expression['1 AND 2 AND 3']
         | 
| 36 36 | 
             
                  logical.should == SQLTree::Node::Expression['(1 AND 2) AND 3']
         | 
| 37 37 | 
             
                end
         | 
| 38 | 
            -
             | 
| 38 | 
            +
             | 
| 39 39 | 
             
                it "should nest expressions correctly when parentheses are used" do
         | 
| 40 40 | 
             
                  logical = SQLTree::Node::Expression['1 AND (2 AND 3)']
         | 
| 41 41 | 
             
                  logical.should_not == SQLTree::Node::Expression['(1 AND 2) AND 3']
         | 
| 42 42 | 
             
                end
         | 
| 43 | 
            -
             | 
| 43 | 
            +
             | 
| 44 44 | 
             
                it "should parse a NOT expression without parenteheses correctly" do
         | 
| 45 45 | 
             
                  SQLTree::Node::Expression['NOT 1'].should == SQLTree::Node::LogicalNotExpression.new(SQLTree::Node::Value.new(1))
         | 
| 46 46 | 
             
                end
         | 
| @@ -55,21 +55,21 @@ describe SQLTree::Node::Expression do | |
| 55 55 | 
             
                  comparison.lhs.should      == SQLTree::Node::Value.new(1)
         | 
| 56 56 | 
             
                  comparison.rhs.should      == SQLTree::Node::Value.new(2)
         | 
| 57 57 | 
             
                end
         | 
| 58 | 
            -
             | 
| 58 | 
            +
             | 
| 59 59 | 
             
                it "should parse an IS NULL expression corectly" do
         | 
| 60 60 | 
             
                  comparison = SQLTree::Node::Expression['field IS NULL']
         | 
| 61 61 | 
             
                  comparison.operator.should == 'IS'
         | 
| 62 62 | 
             
                  comparison.lhs.should == SQLTree::Node::Variable.new('field')
         | 
| 63 63 | 
             
                  comparison.rhs.should == SQLTree::Node::Value.new(nil)
         | 
| 64 64 | 
             
                end
         | 
| 65 | 
            -
             | 
| 65 | 
            +
             | 
| 66 66 | 
             
                it "should parse an IS NOT NULL expression corectly" do
         | 
| 67 67 | 
             
                  comparison = SQLTree::Node::Expression['field IS NOT NULL']
         | 
| 68 68 | 
             
                  comparison.operator.should == 'IS NOT'
         | 
| 69 69 | 
             
                  comparison.lhs.should == SQLTree::Node::Variable.new('field')
         | 
| 70 70 | 
             
                  comparison.rhs.should == SQLTree::Node::Value.new(nil)
         | 
| 71 71 | 
             
                end
         | 
| 72 | 
            -
             | 
| 72 | 
            +
             | 
| 73 73 | 
             
                it "should parse a LIKE expression corectly" do
         | 
| 74 74 | 
             
                  comparison = SQLTree::Node::Expression["field LIKE '%search%"]
         | 
| 75 75 | 
             
                  comparison.operator.should == 'LIKE'
         | 
| @@ -83,20 +83,20 @@ describe SQLTree::Node::Expression do | |
| 83 83 | 
             
                  comparison.lhs.should == SQLTree::Node::Variable.new('field')
         | 
| 84 84 | 
             
                  comparison.rhs.should == SQLTree::Node::Value.new('%search%')
         | 
| 85 85 | 
             
                end
         | 
| 86 | 
            -
             | 
| 86 | 
            +
             | 
| 87 87 | 
             
                it "should parse an IN expression correctly" do
         | 
| 88 88 | 
             
                  comparison = SQLTree::Node::Expression["field IN (1,2,3,4)"]
         | 
| 89 89 | 
             
                  comparison.operator.should == 'IN'
         | 
| 90 90 | 
             
                  comparison.lhs.should == SQLTree::Node::Variable.new('field')
         | 
| 91 91 | 
             
                  comparison.rhs.should be_kind_of(SQLTree::Node::SetExpression)
         | 
| 92 92 | 
             
                end
         | 
| 93 | 
            -
             | 
| 93 | 
            +
             | 
| 94 94 | 
             
                it "should parse a NOT IN expression correctly" do
         | 
| 95 95 | 
             
                  comparison = SQLTree::Node::Expression["field NOT IN (1>2, 3+6, 99)"]
         | 
| 96 96 | 
             
                  comparison.operator.should == 'NOT IN'
         | 
| 97 97 | 
             
                  comparison.lhs.should == SQLTree::Node::Variable.new('field')
         | 
| 98 98 | 
             
                  comparison.rhs.should be_kind_of(SQLTree::Node::SetExpression)
         | 
| 99 99 | 
             
                end
         | 
| 100 | 
            -
             | 
| 100 | 
            +
             | 
| 101 101 | 
             
              end
         | 
| 102 102 | 
             
            end
         | 
    
        data/spec/unit/leaf_node_spec.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            require "#{File.dirname(__FILE__)}/../spec_helper"
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe SQLTree::Node::Value do
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
              describe '.parse' do
         | 
| 6 6 | 
             
                it "should not parse a field name" do
         | 
| 7 7 | 
             
                  lambda { SQLTree::Node::Value['field_name'] }.should raise_error(SQLTree::Parser::UnexpectedToken)
         | 
| @@ -14,11 +14,11 @@ describe SQLTree::Node::Value do | |
| 14 14 | 
             
                it "should parse a string correctly" do
         | 
| 15 15 | 
             
                  SQLTree::Node::Value["'123'"].value.should == '123'
         | 
| 16 16 | 
             
                end
         | 
| 17 | 
            -
             | 
| 17 | 
            +
             | 
| 18 18 | 
             
                it "should parse a NULL value correctly" do
         | 
| 19 19 | 
             
                  SQLTree::Node::Value['NULL'].value.should == nil
         | 
| 20 20 | 
             
                end
         | 
| 21 | 
            -
             | 
| 21 | 
            +
             | 
| 22 22 | 
             
              end
         | 
| 23 23 | 
             
            end
         | 
| 24 24 |  | 
| @@ -29,23 +29,23 @@ describe SQLTree::Node::Join do | |
| 29 29 | 
             
              it "should parse a join table" do
         | 
| 30 30 | 
             
                SQLTree::Node::Join['LEFT JOIN table ON other.field = table.field'].table.should == 'table'
         | 
| 31 31 | 
             
              end
         | 
| 32 | 
            -
             | 
| 32 | 
            +
             | 
| 33 33 | 
             
              it "should parse the join type" do
         | 
| 34 34 | 
             
                SQLTree::Node::Join['LEFT JOIN table ON other.field = table.field'].join_type.should == :left
         | 
| 35 35 | 
             
              end
         | 
| 36 | 
            -
             | 
| 36 | 
            +
             | 
| 37 37 | 
             
              it "should parse the join expression" do
         | 
| 38 38 | 
             
                SQLTree::Node::Join['LEFT JOIN table ON other.field = table.field'].join_expression.should be_kind_of(SQLTree::Node::Expression)
         | 
| 39 39 | 
             
              end
         | 
| 40 | 
            -
             | 
| 40 | 
            +
             | 
| 41 41 | 
             
              it "should not parse a table alias" do
         | 
| 42 42 | 
             
                SQLTree::Node::Join['LEFT JOIN table ON other.field = table.field'].table_alias.should be_nil
         | 
| 43 | 
            -
              end | 
| 44 | 
            -
             | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 45 | 
             
              it "should parse a table alias with AS" do
         | 
| 46 46 | 
             
                SQLTree::Node::Join['LEFT JOIN table AS t ON other.field = table.field'].table_alias.should == 't'
         | 
| 47 47 | 
             
              end
         | 
| 48 | 
            -
             | 
| 48 | 
            +
             | 
| 49 49 | 
             
              it "should parse a table alias without AS" do
         | 
| 50 50 | 
             
                SQLTree::Node::Join['LEFT JOIN table t ON other.field = table.field'].table_alias.should == 't'
         | 
| 51 51 | 
             
              end
         | 
| @@ -58,20 +58,20 @@ describe SQLTree::Node::Ordering do | |
| 58 58 | 
             
                ordering.expression.name.should  == 'field'
         | 
| 59 59 | 
             
                ordering.direction.should == :asc
         | 
| 60 60 | 
             
              end
         | 
| 61 | 
            -
             | 
| 61 | 
            +
             | 
| 62 62 | 
             
              it "should parse an ordering without direction" do
         | 
| 63 63 | 
             
                ordering = SQLTree::Node::Ordering["table.field"]
         | 
| 64 64 | 
             
                ordering.expression.table.should == 'table'
         | 
| 65 65 | 
             
                ordering.expression.name.should  == 'field'
         | 
| 66 66 | 
             
                ordering.direction.should be_nil
         | 
| 67 67 | 
             
              end
         | 
| 68 | 
            -
             | 
| 68 | 
            +
             | 
| 69 69 | 
             
              it "should parse an ordering without direction" do
         | 
| 70 70 | 
             
                ordering = SQLTree::Node::Ordering["MD5(3 + 6) DESC"]
         | 
| 71 71 | 
             
                ordering.expression.should be_kind_of(SQLTree::Node::FunctionExpression)
         | 
| 72 72 | 
             
                ordering.direction.should == :desc
         | 
| 73 73 | 
             
              end
         | 
| 74 | 
            -
             | 
| 74 | 
            +
             | 
| 75 75 | 
             
              it "shoulde parse multiple orderings" do
         | 
| 76 76 | 
             
                tree = SQLTree['SELECT * FROM table ORDER BY field1 ASC, field2 DESC']
         | 
| 77 77 | 
             
                tree.order_by.should have(2).items
         | 
    
        data/spec/unit/tokenizer_spec.rb
    CHANGED
    
    | @@ -1,11 +1,11 @@ | |
| 1 1 | 
             
            require "#{File.dirname(__FILE__)}/../spec_helper"
         | 
| 2 2 |  | 
| 3 3 | 
             
            describe SQLTree::Tokenizer do
         | 
| 4 | 
            -
             | 
| 4 | 
            +
             | 
| 5 5 | 
             
              before(:all) do
         | 
| 6 6 | 
             
                @tokenizer = SQLTree::Tokenizer.new
         | 
| 7 7 | 
             
              end
         | 
| 8 | 
            -
             | 
| 8 | 
            +
             | 
| 9 9 | 
             
              context "recognizing single tokens" do
         | 
| 10 10 | 
             
                it "should tokenize SQL query keywords" do
         | 
| 11 11 | 
             
                  @tokenizer.tokenize('WHERE').should tokenize_to(:where)
         | 
| @@ -14,7 +14,7 @@ describe SQLTree::Tokenizer do | |
| 14 14 | 
             
                it "should tokenize expression keywords" do
         | 
| 15 15 | 
             
                  @tokenizer.tokenize('and').should tokenize_to(:and)
         | 
| 16 16 | 
             
                end
         | 
| 17 | 
            -
             | 
| 17 | 
            +
             | 
| 18 18 | 
             
                it "should tokenize muliple separate keywords" do
         | 
| 19 19 | 
             
                  @tokenizer.tokenize('SELECT DISTINCT').should tokenize_to(:select, :distinct)
         | 
| 20 20 | 
             
                end
         | 
| @@ -38,49 +38,49 @@ describe SQLTree::Tokenizer do | |
| 38 38 | 
             
                it "should tokenize strings" do
         | 
| 39 39 | 
             
                  @tokenizer.tokenize("'hello' '  world  '").should tokenize_to('hello', '  world  ')
         | 
| 40 40 | 
             
                end
         | 
| 41 | 
            -
             | 
| 41 | 
            +
             | 
| 42 42 | 
             
                it "should tokenize numbers" do
         | 
| 43 43 | 
             
                  @tokenizer.tokenize("1 -2 3.14 -4.0").should tokenize_to(1, -2, 3.14, -4.0)
         | 
| 44 | 
            -
                end | 
| 44 | 
            +
                end
         | 
| 45 45 |  | 
| 46 46 | 
             
                it "should tokenize logical operators" do
         | 
| 47 47 | 
             
                  @tokenizer.tokenize("< = <> >=").should tokenize_to(:lt, :eq, :ne, :gte)
         | 
| 48 48 | 
             
                end
         | 
| 49 | 
            -
             | 
| 49 | 
            +
             | 
| 50 50 | 
             
                it "should tokenize arithmetic operators" do
         | 
| 51 51 | 
             
                  @tokenizer.tokenize("+ - / * %").should tokenize_to(:plus, :minus, :divide, :multiply, :modulo)
         | 
| 52 | 
            -
                end | 
| 53 | 
            -
             | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 54 | 
             
                it "should tokenize parentheses" do
         | 
| 55 55 | 
             
                  @tokenizer.tokenize("(a)").should tokenize_to(lparen, sql_var('a'), rparen)
         | 
| 56 | 
            -
                end | 
| 57 | 
            -
             | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 58 | 
             
                it "should tokenize dots" do
         | 
| 59 59 | 
             
                  @tokenizer.tokenize('a."b"').should tokenize_to(sql_var('a'), dot, sql_var('b'))
         | 
| 60 | 
            -
                end | 
| 61 | 
            -
             | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 62 | 
             
                it "should tokenize commas" do
         | 
| 63 63 | 
             
                  @tokenizer.tokenize('a , "b"').should tokenize_to(sql_var('a'), comma, sql_var('b'))
         | 
| 64 64 | 
             
                end
         | 
| 65 65 | 
             
              end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
              # # Combined tokens are disabled for now; | 
| 66 | 
            +
             | 
| 67 | 
            +
              # # Combined tokens are disabled for now;
         | 
| 68 68 | 
             
              # # Combination is currently done in the parsing phase.
         | 
| 69 69 | 
             
              # context "combining double keywords" do
         | 
| 70 70 | 
             
              #   it "should tokenize double keywords" do
         | 
| 71 71 | 
             
              #     @tokenizer.tokenize('NOT LIKE').should tokenize_to(:not_like)
         | 
| 72 72 | 
             
              #   end
         | 
| 73 73 | 
             
              # end
         | 
| 74 | 
            -
             | 
| 74 | 
            +
             | 
| 75 75 | 
             
              context "when tokenizing full queries or query fragments" do
         | 
| 76 76 | 
             
                it "should tokenize a full SQL query" do
         | 
| 77 77 | 
             
                  @tokenizer.tokenize("SELECT a.* FROM a_table AS a WHERE a.id > 1").should tokenize_to(
         | 
| 78 78 | 
             
                    :select,  sql_var('a'), dot, :multiply, :from, sql_var('a_table'), :as, sql_var('a'), :where, sql_var('a'), dot, sql_var('id'), :gt, 1)
         | 
| 79 79 | 
             
                end
         | 
| 80 | 
            -
             | 
| 80 | 
            +
             | 
| 81 81 | 
             
                it "should tokenize a function call" do
         | 
| 82 82 | 
             
                  @tokenizer.tokenize("MD5('test')").should tokenize_to(sql_var('MD5'), lparen, 'test', rparen)
         | 
| 83 83 | 
             
                end
         | 
| 84 84 | 
             
              end
         | 
| 85 | 
            -
             | 
| 85 | 
            +
             | 
| 86 86 | 
             
            end
         | 
    
        data/sql_tree.gemspec
    CHANGED
    
    | @@ -3,7 +3,7 @@ Gem::Specification.new do |s| | |
| 3 3 |  | 
| 4 4 | 
             
              # Do not modify the version and date values by hand, because this will
         | 
| 5 5 | 
             
              # automatically by them gem release script.
         | 
| 6 | 
            -
              s.version = "0.0. | 
| 6 | 
            +
              s.version = "0.0.3"
         | 
| 7 7 | 
             
              s.date    = "2009-10-09"
         | 
| 8 8 |  | 
| 9 9 | 
             
              s.summary = "A pure Ruby library to represent SQL queries with a syntax tree for inspection and modification."
         |