Compare commits
6 Commits
Author | SHA1 | Date |
---|---|---|
Denis Knauf | a4ce8a1e6b | |
Denis Knauf | 7f43e9fb86 | |
Denis Knauf | 3d922c5e47 | |
Denis Knauf | 7229fd185c | |
Denis Knauf | 08d1610987 | |
Denis Knauf | ce81d21bd8 |
|
@ -43,9 +43,9 @@ class SmqlToAR
|
|||
# Die eigentliche Exception wird in @data[:exception] hinterlegt.
|
||||
class SubSMQLError < SMQLError
|
||||
def initialize query, model, exception
|
||||
ex = {:class => exception.class, :message => exception.message}
|
||||
ex = {class: exception.class, message: exception.message}
|
||||
ex[:data] = exception.data if exception.respond_to? :data
|
||||
super :query => query, :model => model.to_s, :exception => ex
|
||||
super query: query, model: model.to_s, exception: ex
|
||||
set_backtrace exception.backtrace
|
||||
end
|
||||
end
|
||||
|
@ -55,49 +55,49 @@ class SmqlToAR
|
|||
# Malformed ColOp
|
||||
class UnexpectedColOpError < ParseError
|
||||
def initialize model, colop, val
|
||||
super :got => colop, :val => val, :model => model.to_s
|
||||
super got: colop, val: val, model: model.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class UnexpectedError < ParseError
|
||||
def initialize model, colop, val
|
||||
super :got => {colop => val}, :model => model.to_s
|
||||
super got: {colop => val}, model: model.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class NonExistingSelectableError < SMQLError
|
||||
def initialize got
|
||||
super :got => got
|
||||
super got: got
|
||||
end
|
||||
end
|
||||
|
||||
class NonExistingColumnError < SMQLError
|
||||
def initialize expected, got
|
||||
super :expected => expected, :got => got
|
||||
super expected: expected, got: got
|
||||
end
|
||||
end
|
||||
|
||||
class NonExistingRelationError < SMQLError
|
||||
def initialize expected, got
|
||||
super :expected => expected, :got => got
|
||||
super expected: expected, got: got
|
||||
end
|
||||
end
|
||||
|
||||
class ProtectedColumnError < SMQLError
|
||||
def initialize protected_column
|
||||
super :protected_column => protected_column
|
||||
super protected_column: protected_column
|
||||
end
|
||||
end
|
||||
|
||||
class RootOnlyFunctionError < SMQLError
|
||||
def initialize path
|
||||
super :path => path
|
||||
super path: path
|
||||
end
|
||||
end
|
||||
|
||||
class ConColumnError < SMQLError
|
||||
def initialize expected, got
|
||||
super :expected => expected, :got => got
|
||||
super expected: expected, got: got
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -243,8 +243,8 @@ class SmqlToAR
|
|||
refls = model.respond_to?( :reflections) && model.reflections
|
||||
refls && refls.each do |name, refl|
|
||||
r[model.name][name] = case refl
|
||||
when ActiveRecord::Reflection::ThroughReflection then {:macro => refl.macro, :model => refl.klass.name, :through => refl.through_reflection.name}
|
||||
when ActiveRecord::Reflection::AssociationReflection then {:macro => refl.macro, :model => refl.klass.name}
|
||||
when ActiveRecord::Reflection::ThroughReflection then {macro: refl.macro, model: refl.klass.name, through: refl.through_reflection.name}
|
||||
when ActiveRecord::Reflection::AssociationReflection then {macro: refl.macro, model: refl.klass.name}
|
||||
else raise "Ups: #{refl.class}"
|
||||
end
|
||||
models.push refl.klass unless r.keys.include? refl.klass.name
|
||||
|
@ -293,8 +293,26 @@ class SmqlToAR
|
|||
lib_dir = File.dirname __FILE__
|
||||
fj = lambda {|*a| File.join lib_dir, *a }
|
||||
Object.send :remove_const, :SmqlToAR
|
||||
load fj.call( 'smql_to_ar.rb')
|
||||
load fj.call( 'smql_to_ar', 'condition_types.rb')
|
||||
load fj.call( 'smql_to_ar', 'query_builder.rb')
|
||||
load fj[ 'smql_to_ar.rb']
|
||||
load fj[ 'smql_to_ar', 'condition_types.rb']
|
||||
load fj[ 'smql_to_ar', 'query_builder.rb']
|
||||
end
|
||||
|
||||
module ActiveRecordExtensions
|
||||
def self.included base
|
||||
base.extend ClassMethods
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def smql *params
|
||||
SmqlToAR.to_ar self, *params
|
||||
end
|
||||
|
||||
def smql_protected
|
||||
[]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.send :include, SmqlToAR::ActiveRecordExtensions
|
||||
|
|
|
@ -153,7 +153,7 @@ class SmqlToAR
|
|||
# 2) {"givenname=", ["Peter", "Hans"]} #=> ( givenname = 'Peter' OR givenname = 'Hans' )
|
||||
# 3) {"givenname|surname=", ["Peter", "Mueller"]}
|
||||
# #=> ( givenname = 'Peter' OR surname = 'Peter' ) AND ( givenname = 'Mueller' OR surname = 'Mueller' )
|
||||
def build builder, table
|
||||
def condition_build builder, table
|
||||
values = Hash[ @value.collect {|value| [ builder.vid, value ] } ]
|
||||
values.each {|k, v| builder.wobs k.to_sym => v }
|
||||
if 1 == @cols.length
|
||||
|
@ -174,6 +174,7 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build condition_build
|
||||
end
|
||||
|
||||
class NotInRange < Condition
|
||||
|
@ -190,7 +191,7 @@ class SmqlToAR
|
|||
super model, cols, val
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def not_in_range_build builder, table
|
||||
builder.wobs (v1 = builder.vid).to_sym => @value.begin, (v2 = builder.vid).to_sym => @value.end
|
||||
@cols.each do |col|
|
||||
col.joins builder, table
|
||||
|
@ -198,11 +199,13 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build not_in_range_build
|
||||
end
|
||||
InRange = simple_condition NotInRange, '..', '%s BETWEEN %s AND %s'
|
||||
|
||||
class NotOverlaps < Condition
|
||||
Operator, Where = '<!>', 'NOT (%s, %s) OVERLAPS (%s, %s)'
|
||||
# Every key-pair will be ORed. No multiple values possible.
|
||||
class Overlaps < Condition
|
||||
Operator, Where = '<=>', '(%s, %s) OVERLAPS (%s, %s)'
|
||||
Expected = [Range, lambda {|val|
|
||||
Array === val && 2 == val.length &&
|
||||
[Time, Date, String].any? {|v|v===val[0]} &&
|
||||
|
@ -225,7 +228,8 @@ class SmqlToAR
|
|||
super model, cols, val
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def overlaps_build builder, table
|
||||
builder = Or.new builder
|
||||
builder.wobs (v1 = builder.vid).to_sym => @value.begin, (v2 = builder.vid).to_sym => @value.end
|
||||
v1 = "TIMESTAMP #{v1}"
|
||||
v2 = "TIMESTAMP #{v2}"
|
||||
|
@ -236,15 +240,16 @@ class SmqlToAR
|
|||
builder.column( table+f.path, f.col), builder.column( table+s.path, s.col), v1, v2]
|
||||
end
|
||||
end
|
||||
alias build overlaps_build
|
||||
end
|
||||
Overlaps = simple_condition NotOverlaps, '<=>', '(%s, %s) OVERLAPS (%s, %s)'
|
||||
NotOverlaps = simple_condition Overlaps, '<=>', 'NOT (%s, %s) OVERLAPS (%s, %s)'
|
||||
|
||||
class NotIn < Condition
|
||||
Operator = '!|='
|
||||
Where = "%s NOT IN (%s)"
|
||||
Expected = [Array]
|
||||
|
||||
def build builder, table
|
||||
def not_in_build builder, table
|
||||
builder.wobs (v = builder.vid).to_sym => @value
|
||||
@cols.each do |col|
|
||||
col.joins builder, table
|
||||
|
@ -252,6 +257,7 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build not_in_build
|
||||
end
|
||||
|
||||
In = simple_condition NotIn, '|=', '%s IN (%s)', [Array]
|
||||
|
@ -290,7 +296,7 @@ class SmqlToAR
|
|||
raise_unless col.relation, NonExistingRelationError.new( %w[Relation], col)
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def equal_join_build builder, table
|
||||
if 2 < @cols.first.second.length
|
||||
b2, b3 = And, Or
|
||||
else
|
||||
|
@ -300,7 +306,7 @@ class SmqlToAR
|
|||
@cols.each do |col, sub|
|
||||
model, *sub = sub
|
||||
t = table + col.path + [col.col]
|
||||
col.joins.each {|j, m| builder.joins table+j, m }
|
||||
col.joins builder, table
|
||||
builder.joins t, model
|
||||
b4 = b3.new( b2)
|
||||
sub.each do |i|
|
||||
|
@ -310,6 +316,7 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build equal_join_build
|
||||
end
|
||||
|
||||
# Takes to Queries.
|
||||
|
@ -339,7 +346,7 @@ class SmqlToAR
|
|||
raise_unless col.child?, ConColumnError.new( [:Column], col)
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def sub_equal_join_build builder, table
|
||||
@cols.each do |col, sub|
|
||||
t = table+col.to_a
|
||||
builder.sub_joins t, col, *sub[0..1]
|
||||
|
@ -348,6 +355,7 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build sub_equal_join_build
|
||||
end
|
||||
=end
|
||||
|
||||
|
@ -372,7 +380,7 @@ class SmqlToAR
|
|||
raise_unless col.exist_in? || SmqlToAR.model_of( col.last_model, col.col), NonExistingSelectableError.new( col)
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def select_build builder, table
|
||||
@cols.each do |col|
|
||||
if col.exist_in?
|
||||
col.joins builder, table
|
||||
|
@ -384,6 +392,7 @@ class SmqlToAR
|
|||
end
|
||||
self
|
||||
end
|
||||
alias build select_build
|
||||
end
|
||||
|
||||
class Functions < Condition
|
||||
|
@ -432,7 +441,7 @@ class SmqlToAR
|
|||
super model, func, args
|
||||
end
|
||||
|
||||
def build builder, table
|
||||
def order_build builder, table
|
||||
return if @args.blank?
|
||||
@args.each do |o|
|
||||
col, o = o
|
||||
|
@ -442,26 +451,29 @@ class SmqlToAR
|
|||
builder.order t, col.col, o
|
||||
end
|
||||
end
|
||||
alias build order_build
|
||||
end
|
||||
|
||||
class Limit < Function
|
||||
Name = :limit
|
||||
Expected = [Fixnum]
|
||||
|
||||
def build builder, table
|
||||
def limit_build builder, table
|
||||
raise_unless 1 == table.length, RootOnlyFunctionError.new( table)
|
||||
builder.limit = Array.wrap(@args).first.to_i
|
||||
end
|
||||
alias build limit_build
|
||||
end
|
||||
|
||||
class Offset < Function
|
||||
Name = :offset
|
||||
Expected = [Fixnum]
|
||||
|
||||
def build builder, table
|
||||
def offset_build builder, table
|
||||
raise_unless 1 == table.length, RootOnlyFunctionError.new( table)
|
||||
builder.offset = Array.wrap(@args).first.to_i
|
||||
end
|
||||
alias build offset_build
|
||||
end
|
||||
|
||||
def self.new model, col, val
|
||||
|
|
|
@ -135,25 +135,25 @@ class SmqlToAR
|
|||
case refl
|
||||
when ActiveRecord::Reflection::ThroughReflection
|
||||
through = refl.through_reflection
|
||||
throughtable = table[0...-1]+[Column::Col.new( through.name, table.last.as)]
|
||||
srctable = throughtable+[Column::Col.new( refl.source_reflection.name, table.last.as)]
|
||||
through_table = table[0...-1]+[Column::Col.new( through.name, table.last.as)]
|
||||
srctable = through_table+[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)
|
||||
join_ srctable, refl.klass, query, throughtable
|
||||
join_ through_table, through.klass, quote_table_name( through.table_name)
|
||||
join_ srctable, refl.klass, query, through_table
|
||||
when ActiveRecord::Reflection::AssociationReflection
|
||||
case refl.macro
|
||||
when :has_many, :has_one
|
||||
@_joins += build_join query, pretable, t, premodel.primary_key, refl.primary_key_name
|
||||
@_joins += build_join query, pretable, t, premodel.primary_key, refl.foreign_key
|
||||
when :belongs_to
|
||||
@_joins += build_join query, pretable, t, refl.primary_key_name, premodel.primary_key
|
||||
@_joins += build_join query, pretable, t, refl.foreign_key, premodel.primary_key
|
||||
when :has_and_belongs_to_many
|
||||
jointable = [Column::Col.new(',')] + table
|
||||
@_joins += build_join refl.options[:join_table], pretable, @table_alias[jointable], premodel.primary_key, refl.primary_key_name
|
||||
@_joins += build_join refl.options[:join_table], pretable, @table_alias[ jointable], premodel.primary_key, refl.foreign_key
|
||||
@_joins += build_join query, jointable, t, refl.association_foreign_key, refl.association_primary_key
|
||||
else raise BuilderError, "Unkown reflection macro: #{refl.macro.inspect}"
|
||||
end
|
||||
else raise BuilderError, "Unkown reflection type: #{refl.class.name}"
|
||||
else raise BuilderError, "Unkown reflection type: #{refl.class.name} #{refl.macro.inspect}"
|
||||
end
|
||||
self
|
||||
end
|
||||
|
@ -248,12 +248,14 @@ class SmqlToAR
|
|||
def optimize!
|
||||
ext = []
|
||||
collect! do |sub|
|
||||
sub = sub.optimize! if sub.kind_of? Array
|
||||
sub = sub.optimize! if sub.kind_of? SubBuilder
|
||||
if self.class == sub.class
|
||||
ext.push *sub
|
||||
nil
|
||||
elsif sub.blank?
|
||||
nil
|
||||
elsif 1 == sub.size
|
||||
sub.first
|
||||
else
|
||||
sub
|
||||
end
|
||||
|
@ -267,21 +269,22 @@ class SmqlToAR
|
|||
end
|
||||
def default() SmqlToAR::And end
|
||||
def default_new( parent) default.new self, parent, false end
|
||||
def collect_build_where
|
||||
collect {|x| x.respond_to?( :build_where) ? x.build_where : x.to_s }
|
||||
def collect_build_where indent = nil
|
||||
indent = (indent||0) + 1
|
||||
collect {|x| "(#{x.respond_to?( :build_where) ? x.build_where( indent) : x.to_s})" }
|
||||
end
|
||||
end
|
||||
|
||||
class And < SubBuilder
|
||||
def default; SmqlToAR::Or; end
|
||||
def build_where
|
||||
collect_build_where.join ' AND '
|
||||
def build_where indent = nil
|
||||
collect_build_where( indent).join " AND\n"+"\t"*(indent||0)
|
||||
end
|
||||
end
|
||||
|
||||
class Or < SubBuilder
|
||||
def build_where
|
||||
collect_build_where.join ' OR '
|
||||
def build_where indent = nil
|
||||
collect_build_where( indent).join " OR\n"+"\t"*(indent||0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue