2013-02-05 14:16:51 -05:00
class SqlBuilder
2013-02-10 23:37:24 +11:00
def initialize ( template , klass = nil )
2013-02-05 14:16:51 -05:00
@args = {}
@sql = template
@sections = {}
2013-02-10 23:37:24 +11:00
@klass = klass
2013-02-05 14:16:51 -05:00
end
2013-05-13 10:48:32 +10:00
[ :set , :where2 , :where , :order_by , :limit , :left_join , :join , :offset , :select ]. each do | k |
2013-02-05 14:16:51 -05:00
define_method k do | data , args = {} |
@args . merge! ( args )
@sections [ k ] ||= []
@sections [ k ] << data
self
end
end
2013-06-05 16:10:26 +10:00
def secure_category ( secure_category_ids , category_alias = 'c' )
if secure_category_ids . present?
2013-07-14 11:24:16 +10:00
where ( "NOT COALESCE(" << category_alias << ".read_restricted, false) OR " << category_alias << ".id IN (:secure_category_ids)" , secure_category_ids : secure_category_ids )
2013-06-05 16:10:26 +10:00
else
2013-07-14 11:24:16 +10:00
where ( "NOT COALESCE(" << category_alias << ".read_restricted, false)" )
2013-06-05 16:10:26 +10:00
end
self
end
2013-05-13 10:48:32 +10:00
def to_sql
2013-02-05 14:16:51 -05:00
sql = @sql . dup
@sections . each do | k , v |
joined = nil
2013-02-25 19:42:20 +03:00
case k
2013-05-13 10:48:32 +10:00
when :select
joined = "SELECT " << v . join ( " , " )
2013-02-05 14:16:51 -05:00
when :where , :where2
2013-05-23 15:21:07 +10:00
joined = "WHERE " << v . map { | c | "(" << c << ")" } . join ( " AND " )
2013-02-05 14:16:51 -05:00
when :join
2015-05-13 16:45:59 +10:00
joined = v . map { | item | "JOIN " << item } . join ( " \n " )
2013-02-05 14:16:51 -05:00
when :left_join
2015-05-13 16:45:59 +10:00
joined = v . map { | item | "LEFT JOIN " << item } . join ( " \n " )
2013-02-05 14:16:51 -05:00
when :limit
joined = "LIMIT " << v . last . to_s
when :offset
joined = "OFFSET " << v . last . to_s
when :order_by
joined = "ORDER BY " << v . join ( " , " )
when :set
joined = "SET " << v . join ( " , " )
end
sql . sub! ( "/* #{ k } */" , joined )
end
2013-05-13 10:48:32 +10:00
sql
end
def exec ( args = {})
@args . merge! ( args )
2013-02-25 19:42:20 +03:00
2013-05-13 10:48:32 +10:00
sql = to_sql
2013-02-10 23:37:24 +11:00
if @klass
@klass . find_by_sql ( ActiveRecord :: Base . send ( :sanitize_sql_array , [ sql , @args ] ))
else
2013-10-10 13:33:52 +11:00
if @args == {}
ActiveRecord :: Base . exec_sql ( sql )
else
ActiveRecord :: Base . exec_sql ( sql , @args )
end
2013-02-10 23:37:24 +11:00
end
2013-02-05 14:16:51 -05:00
end
2013-05-23 15:21:07 +10:00
2014-06-24 17:10:56 +10:00
def self . map_exec ( klass , sql , args = {})
self . new ( sql ) . map_exec ( klass , args )
end
2015-05-13 16:45:59 +10:00
class RailsDateTimeDecoder < PG :: SimpleDecoder
def decode ( string , tuple = nil , field = nil )
if Rails . version >= "4.2.0"
@caster ||= ActiveRecord :: Type :: DateTime . new
@caster . type_cast_from_database ( string )
else
ActiveRecord :: ConnectionAdapters :: Column . string_to_time string
end
end
end
class ActiveRecordTypeMap < PG :: BasicTypeMapForResults
def initialize ( connection )
super ( connection )
rm_coder 0 , 1114
add_coder RailsDateTimeDecoder . new ( name : "timestamp" , oid : 1114 , format : 0 )
# we don't need deprecations
self . default_type_map = PG :: TypeMapInRuby . new
end
end
def self . pg_type_map
conn = ActiveRecord :: Base . connection . raw_connection
@typemap ||= ActiveRecordTypeMap . new ( conn )
end
2013-10-17 13:23:38 +11:00
def map_exec ( klass = OpenStruct , args = {})
2013-05-23 15:21:07 +10:00
results = exec ( args )
2015-05-13 16:45:59 +10:00
results . type_map = SqlBuilder . pg_type_map
2013-05-23 15:21:07 +10:00
setters = results . fields . each_with_index . map do | f , index |
2015-05-13 16:45:59 +10:00
f . dup << "="
2013-05-23 15:21:07 +10:00
end
2015-05-13 16:45:59 +10:00
2013-05-23 15:21:07 +10:00
values = results . values
values . map! do | row |
mapped = klass . new
2015-05-13 16:45:59 +10:00
setters . each_with_index do | name , index |
mapped . send name , row [ index ]
2013-05-23 15:21:07 +10:00
end
mapped
end
end
2013-02-10 23:37:24 +11:00
end
2013-02-05 14:16:51 -05:00
2013-02-25 19:42:20 +03:00
class ActiveRecord :: Base
2013-02-10 23:37:24 +11:00
def self . sql_builder ( template )
SqlBuilder . new ( template , self )
end
2013-02-05 14:16:51 -05:00
end