2013-02-05 14:16:51 -05:00
class SqlBuilder
2017-07-27 21:20:09 -04:00
def initialize ( template , klass = nil )
2013-02-05 14:16:51 -05:00
@args = { }
@sql = template
@sections = { }
2013-02-10 07:37:24 -05:00
@klass = klass
2013-02-05 14:16:51 -05:00
end
2017-07-27 21:20:09 -04: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 02:10:26 -04:00
def secure_category ( secure_category_ids , category_alias = 'c' )
if secure_category_ids . present?
2017-07-27 21:20:09 -04: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 02:10:26 -04:00
else
2013-07-13 21:24:16 -04:00
where ( " NOT COALESCE( " << category_alias << " .read_restricted, false) " )
2013-06-05 02:10:26 -04:00
end
self
end
2013-05-12 20:48:32 -04:00
def to_sql
2013-02-05 14:16:51 -05:00
sql = @sql . dup
2017-07-27 21:20:09 -04:00
@sections . each do | k , v |
2013-02-05 14:16:51 -05:00
joined = nil
2013-02-25 11:42:20 -05:00
case k
2013-05-12 20:48:32 -04:00
when :select
joined = " SELECT " << v . join ( " , " )
2013-02-05 14:16:51 -05:00
when :where , :where2
2017-07-27 21:20:09 -04:00
joined = " WHERE " << v . map { | c | " ( " << c << " ) " } . join ( " AND " )
2013-02-05 14:16:51 -05:00
when :join
2017-07-27 21:20:09 -04:00
joined = v . map { | item | " JOIN " << item } . join ( " \n " )
2013-02-05 14:16:51 -05:00
when :left_join
2017-07-27 21:20:09 -04: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-12 20:48:32 -04:00
sql
end
def exec ( args = { } )
@args . merge! ( args )
2013-02-25 11:42:20 -05:00
2013-05-12 20:48:32 -04:00
sql = to_sql
2013-02-10 07:37:24 -05:00
if @klass
@klass . find_by_sql ( ActiveRecord :: Base . send ( :sanitize_sql_array , [ sql , @args ] ) )
else
2013-10-09 22:33:52 -04:00
if @args == { }
ActiveRecord :: Base . exec_sql ( sql )
else
2017-07-27 21:20:09 -04:00
ActiveRecord :: Base . exec_sql ( sql , @args )
2013-10-09 22:33:52 -04:00
end
2013-02-10 07:37:24 -05:00
end
2013-02-05 14:16:51 -05:00
end
2013-05-23 01:21:07 -04:00
2014-06-24 03:10:56 -04:00
def self . map_exec ( klass , sql , args = { } )
self . new ( sql ) . map_exec ( klass , args )
end
2015-05-13 02:45:59 -04:00
class RailsDateTimeDecoder < PG :: SimpleDecoder
2017-07-27 21:20:09 -04:00
def decode ( string , tuple = nil , field = nil )
2015-05-13 02:45:59 -04:00
if Rails . version > = " 4.2.0 "
@caster || = ActiveRecord :: Type :: DateTime . new
2017-08-31 00:06:56 -04:00
@caster . cast ( string )
2015-05-13 02:45:59 -04:00
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 )
2017-07-27 21:20:09 -04:00
# we don't need deprecations
self . default_type_map = PG :: TypeMapInRuby . new
2015-05-13 02:45:59 -04:00
end
end
def self . pg_type_map
conn = ActiveRecord :: Base . connection . raw_connection
@typemap || = ActiveRecordTypeMap . new ( conn )
end
2013-10-16 22:23:38 -04:00
def map_exec ( klass = OpenStruct , args = { } )
2013-05-23 01:21:07 -04:00
results = exec ( args )
2015-05-13 02:45:59 -04:00
results . type_map = SqlBuilder . pg_type_map
2013-05-23 01:21:07 -04:00
setters = results . fields . each_with_index . map do | f , index |
2015-05-13 02:45:59 -04:00
f . dup << " = "
2013-05-23 01:21:07 -04:00
end
2015-05-13 02:45:59 -04:00
2013-05-23 01:21:07 -04:00
values = results . values
values . map! do | row |
mapped = klass . new
2015-05-13 02:45:59 -04:00
setters . each_with_index do | name , index |
mapped . send name , row [ index ]
2013-05-23 01:21:07 -04:00
end
mapped
end
end
2013-02-10 07:37:24 -05:00
end
2013-02-05 14:16:51 -05:00
2013-02-25 11:42:20 -05:00
class ActiveRecord :: Base
2013-02-10 07:37:24 -05:00
def self . sql_builder ( template )
SqlBuilder . new ( template , self )
end
2013-02-05 14:16:51 -05:00
end