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