From 69aceef180a7754420c675e140e229bc4beb8f7d Mon Sep 17 00:00:00 2001 From: Denis Knauf Date: Mon, 21 Nov 2011 10:06:46 +0100 Subject: [PATCH] aliased tables. firm: { "people[users]": {type: "User"}, "people[old]": {"age>:": 50 } # Every ToDo which has a User as Person or people older than 50. It is not an OR, it is an AND, but {people:{type:"User","age>":50}} or {"people":{type:"User"},"people":{"age>:":50} means every User older than 50. "users" and "old" are independet questions and both must be present, if firm should be true. --- lib/smql_to_ar.rb | 47 +++++++++++++++++++++++++++---- lib/smql_to_ar/condition_types.rb | 5 +++- lib/smql_to_ar/query_builder.rb | 37 ++++++++++++++++-------- 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/lib/smql_to_ar.rb b/lib/smql_to_ar.rb index 6d252af..1eedbd6 100644 --- a/lib/smql_to_ar.rb +++ b/lib/smql_to_ar.rb @@ -107,9 +107,9 @@ class SmqlToAR # Model der Relation `rel` von `model` def self.model_of model, rel - rel = rel.to_sym - r = model.reflections[ rel].andand.klass - r.nil? && :self == rel ? model : r + p model_of: rel.to_sym, model: model + r = model.reflections[ rel.to_sym].andand.klass + r.nil? && rel === :self ? model : r end # Eine Spalte in einer Tabelle, relativ zu `Column#model`. @@ -120,10 +120,43 @@ class SmqlToAR attr_reader :path, :col attr_accessor :model + class Col + attr_accessor :col, :as + def initialize col, as = nil + if col.kind_of? Col + @col, @as = col.col, col.as + elsif /^(.*?)\[(.*)\]$/ =~ col.to_s + @col, @as = $1, $2 + else + @col, @as = col.to_s, as.to_s + @as = nil if @as.blank? + end + end + + def to_s() @col end + def to_sym() to_s.to_sym end + def inspect() "#{@col}[@#{@as}]" end + def to_alias() "#{@col}[#{@as}]" end + + def == other + other = Col.new other + @col = other.col && @as == other.col + end + + def === other + other = Col.new other + @col == other.col + end + end + def initialize model, *col @model = model @last_model = nil - *@path, @col = *Array.wrap( col).collect( &it.to_s.split( /[.\/]/)).flatten.collect( &:to_sym).reject( &it==:self) + *@path, @col = *Array.wrap( col). + collect( &it.to_s.split( /[.\/]/)). + flatten. + collect( &Col.method( :new)). + reject( &it===:self) end def last_model @@ -132,9 +165,10 @@ class SmqlToAR def each model = @model + p each: self, path: @path @path.each do |rel| - rel = rel.to_sym - unless :self == rel + p rel: rel + unless rel === :self model = SmqlToAR.model_of model, rel return false unless model yield rel, model @@ -169,6 +203,7 @@ class SmqlToAR exe.call pp, model end end + def self?() !@col end def length() @path.length+(self.self? ? 0 : 1) end def size() @path.size+(self.self? ? 0 : 1) end diff --git a/lib/smql_to_ar/condition_types.rb b/lib/smql_to_ar/condition_types.rb index 05ce5c6..d91b86a 100644 --- a/lib/smql_to_ar/condition_types.rb +++ b/lib/smql_to_ar/condition_types.rb @@ -56,9 +56,11 @@ class SmqlToAR r = nil #p :try_parse => { :model => model, :colop => colop, :value => val } conditions.each do |c| - raise_unless colop =~ /^(?:\d*:)?(.*?)(\W*)$/, UnexpectedColOpError.new( model, colop, val) + raise_unless colop =~ /^(?:\d*:)?(.*?)((?:\W*(?!\])\W)?)$/, UnexpectedColOpError.new( model, colop, val) col, op = $1, $2 + p col: col, op: op col = split_keys( col).collect {|c| Column.new model, c } + p col: col r = c.try_parse model, col, op, val break if r end @@ -246,6 +248,7 @@ class SmqlToAR model, *sub = sub t = table + col.path + [col.col] col.joins.each {|j, m| builder.joins table+j, m } + p build_t: t builder.joins t, model b2 = 1 == sub.length ? builder : Or.new( builder) sub.each {|i| i.collect( &it.build( And.new( b2), t)); p 'or' => b2 } diff --git a/lib/smql_to_ar/query_builder.rb b/lib/smql_to_ar/query_builder.rb index ff3980d..76a6f8f 100644 --- a/lib/smql_to_ar/query_builder.rb +++ b/lib/smql_to_ar/query_builder.rb @@ -28,20 +28,27 @@ class SmqlToAR def to_i() @vid end end + class Aliases < Hash + def self.new prefix, *a, &e + e ||= lambda do |h, k| + j = Array.wrap( k).compact + h[k] = h.key?(j) ? h[j] : "#{prefix},#{j.collect( &:to_alias).join( ',')}" + end + super *a, &e + end + end + attr_reader :table_alias, :model, :table_model, :base_table, :_where, :_select, :_wobs, :_joins, :prefix, :_vid attr_accessor :logger, :limit, :offset def initialize model, prefix = nil @prefix = "smql" @logger = SmqlToAR.logger - @table_alias = Hash.new do |h, k| - j = Array.wrap( k).compact - h[k] = h.key?(j) ? h[j] : "#{@prefix},#{j.join(',')}" - end + @table_alias = Aliases.new @prefix @_vid, @_where, @_wobs, @model, @quoter = 0, SmqlToAR::And[], {}, model, model.connection - @base_table = [model.table_name.to_sym] + @base_table = [Column::Col.new( model.table_name)] @table_alias[ @base_table] = @base_table.first - t = quote_table_name @table_alias[ @base_table] + t = quote_table_name @base_table.first.col @_select, @_joins, @_joined, @_includes, @_order = ["DISTINCT #{t}.*"], "", [@base_table], [], [] @table_model = {@base_table => @model} end @@ -49,7 +56,7 @@ class SmqlToAR def vid() Vid.new( @_vid+=1) end def inspect - "#<#{self.class.name}:#{"0x%x"% (self.object_id<<1)}|#{@prefix}:#{@base_table}:#{@model} vid=#{@_vid} where=#{@_where} wobs=#{@_wobs} select=#{@_select} aliases=#{@_table_alias}>" + "#<#{self.class.name}:#{"0x%x"% (self.object_id<<1)}|#{@prefix}:#{@base_table}:#{@model} vid=#{@_vid} where=#{@_where} wobs=#{@_wobs} select=#{@_select} aliases=#{@table_alias}>" end # Jede via where uebergebene Condition wird geodert und alle zusammen werden geundet. @@ -71,14 +78,19 @@ class SmqlToAR end def quote_table_name name + name = case name + when Array, Column::Col then @table_alias[Array.wrap name] + else name.to_s + end @quoter.quote_table_name( name).gsub /"\."/, ',' end def column table, name - "#{quote_table_name table.kind_of?(String) ? table : @table_alias[table]}.#{quote_column_name name}" + "#{quote_table_name table}.#{quote_column_name name}" end def build_join orig, pretable, table, prekey, key + p build_join: {orig: orig, pretable: pretable, table: table, prekey: prekey, key: key}, alias: @table_alias " LEFT JOIN #{orig} AS #{quote_table_name table} ON #{column pretable, prekey} = #{column table, key} " end @@ -92,13 +104,14 @@ class SmqlToAR @table_model[ table] = model premodel = @table_model[ pretable] t = @table_alias[ table] - pt = quote_table_name @table_alias[ table[ 0...-1]] - refl = premodel.reflections[table.last] + pt = quote_table_name table[ 0...-1] + p table: table + refl = premodel.reflections[table.last.to_sym] case refl when ActiveRecord::Reflection::ThroughReflection through = refl.through_reflection - throughtable = table[0...-1]+[through.name.to_sym] - srctable = throughtable+[refl.source_reflection.name] + throughtable = table[0...-1]+[Column::Col.new( through.name, table.last.as)] + srctable = throughtable+[Column::Col.new( refl.source_reflection.name, table.last.as)] @table_model[ srctable] = model @table_alias[ table] = @table_alias[ srctable] join_ throughtable, through.klass, quote_table_name( through.table_name)