diff --git a/Gemfile b/Gemfile index bec856bd2a6..006b9175f3f 100644 --- a/Gemfile +++ b/Gemfile @@ -78,7 +78,8 @@ gem 'omniauth-oauth2', require: false gem 'omniauth-google-oauth2' gem 'oj' -gem 'pg', '~> 0.21.0' +gem 'pg' +gem 'mini_sql' gem 'pry-rails', require: false gem 'r2', '~> 0.2.5', require: false gem 'rake' diff --git a/Gemfile.lock b/Gemfile.lock index f4bea23c8fd..32b04e5d0df 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -176,6 +176,7 @@ GEM mini_portile2 (2.3.0) mini_racer (0.1.15) libv8 (~> 6.3) + mini_sql (0.1.4) mini_suffix (0.3.0) ffi (~> 1.9) minitest (5.11.3) @@ -240,7 +241,7 @@ GEM parallel (1.12.1) parser (2.5.1.0) ast (~> 2.4.0) - pg (0.21.0) + pg (1.0.0) powerpack (0.1.1) progress (3.4.0) pry (0.10.4) @@ -453,6 +454,7 @@ DEPENDENCIES message_bus mini_mime mini_racer + mini_sql mini_suffix minitest mocha @@ -471,7 +473,7 @@ DEPENDENCIES omniauth-twitter onebox (= 1.8.48) openid-redis-store - pg (~> 0.21.0) + pg pry-nav pry-rails puma diff --git a/app/controllers/admin/diagnostics_controller.rb b/app/controllers/admin/diagnostics_controller.rb index d10c559b4cd..81b076b0725 100644 --- a/app/controllers/admin/diagnostics_controller.rb +++ b/app/controllers/admin/diagnostics_controller.rb @@ -4,19 +4,6 @@ class Admin::DiagnosticsController < Admin::AdminController layout false skip_before_action :check_xhr - def dump_statement_cache - statements = Post.exec_sql("select * from pg_prepared_statements").to_a - text = "" - - statements.each do |row| - text << "name: #{row["name"]} sql: #{row["statement"]}\n" - end - - text << "\n\nCOUNT #{statements.count}" - - render plain: text - end - def memory_stats text = nil diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 66b5daf9267..70573a2e918 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -77,7 +77,7 @@ class Admin::UsersController < Admin::AdminController ) SQL - UserHistory.exec_sql( + DB.exec( sql, UserHistory.actions.slice( :silence_user, diff --git a/app/jobs/onceoff/create_tags_search_index.rb b/app/jobs/onceoff/create_tags_search_index.rb index 3e673bf2e69..4101aed0dc6 100644 --- a/app/jobs/onceoff/create_tags_search_index.rb +++ b/app/jobs/onceoff/create_tags_search_index.rb @@ -1,8 +1,8 @@ module Jobs class CreateTagsSearchIndex < Jobs::Onceoff def execute_onceoff(args) - Tag.exec_sql('select id, name from tags').each do |t| - SearchIndexer.update_tags_index(t['id'], t['name']) + DB.query('select id, name from tags').each do |t| + SearchIndexer.update_tags_index(t.id, t.name) end end end diff --git a/app/jobs/onceoff/fix_posts_read.rb b/app/jobs/onceoff/fix_posts_read.rb index 2505038723b..0f8d97918ae 100644 --- a/app/jobs/onceoff/fix_posts_read.rb +++ b/app/jobs/onceoff/fix_posts_read.rb @@ -15,7 +15,7 @@ UPDATE user_stats ) SQL - UserStat.exec_sql(sql) + DB.exec(sql) end end end diff --git a/app/jobs/onceoff/fix_primary_emails_for_staged_users.rb b/app/jobs/onceoff/fix_primary_emails_for_staged_users.rb index d024688da91..c9516e36553 100644 --- a/app/jobs/onceoff/fix_primary_emails_for_staged_users.rb +++ b/app/jobs/onceoff/fix_primary_emails_for_staged_users.rb @@ -24,7 +24,7 @@ module Jobs end end - User.exec_sql <<~SQL + DB.exec <<~SQL INSERT INTO user_emails ( user_id, email, diff --git a/app/jobs/onceoff/fix_retro_anniversary.rb b/app/jobs/onceoff/fix_retro_anniversary.rb index 3cd4142caf4..2d553c3a296 100644 --- a/app/jobs/onceoff/fix_retro_anniversary.rb +++ b/app/jobs/onceoff/fix_retro_anniversary.rb @@ -6,7 +6,7 @@ module Jobs def execute_onceoff(args) return unless SiteSetting.enable_badges - users = User.exec_sql <<~SQL + users = User.query <<~SQL SELECT ub.user_id, MIN(granted_at) AS first_granted_at, COUNT(*) FROM user_badges AS ub WHERE ub.badge_id = #{Badge::Anniversary} @@ -15,17 +15,17 @@ module Jobs SQL users.to_a.each do |u| - first = Time.zone.parse(u['first_granted_at']) + first = u.first_granted_at badges = UserBadge.where( "badge_id = ? AND user_id = ? AND granted_at > ?", Badge::Anniversary, - u['user_id'], + u.user_id, first ).order('granted_at') badges.each_with_index do |b, idx| award_date = (first + (idx + 1).years) - UserBadge.where(id: b['id']).update_all(["granted_at = ?", award_date]) + UserBadge.where(id: b.id).update_all(["granted_at = ?", award_date]) end end diff --git a/app/jobs/onceoff/init_category_tag_stats.rb b/app/jobs/onceoff/init_category_tag_stats.rb index 43abdc0b429..e9f256d9b5c 100644 --- a/app/jobs/onceoff/init_category_tag_stats.rb +++ b/app/jobs/onceoff/init_category_tag_stats.rb @@ -1,9 +1,9 @@ module Jobs class InitCategoryTagStats < Jobs::Onceoff def execute_onceoff(args) - CategoryTagStat.exec_sql "DELETE FROM category_tag_stats" + DB.exec "DELETE FROM category_tag_stats" - CategoryTagStat.exec_sql <<~SQL + DB.exec <<~SQL INSERT INTO category_tag_stats (category_id, tag_id, topic_count) SELECT topics.category_id, tags.id, COUNT(topics.id) FROM tags diff --git a/app/jobs/onceoff/migrate_censored_words.rb b/app/jobs/onceoff/migrate_censored_words.rb index 03ce4b358cb..4f7a2201e72 100644 --- a/app/jobs/onceoff/migrate_censored_words.rb +++ b/app/jobs/onceoff/migrate_censored_words.rb @@ -1,9 +1,9 @@ module Jobs class MigrateCensoredWords < Jobs::Onceoff def execute_onceoff(args) - row = WatchedWord.exec_sql("SELECT value FROM site_settings WHERE name = 'censored_words'") + row = DB.query_single("SELECT value FROM site_settings WHERE name = 'censored_words'") if row.count > 0 - row.first["value"].split('|').each do |word| + row.first.split('|').each do |word| WatchedWord.create(word: word, action: WatchedWord.actions[:censor]) end end diff --git a/app/jobs/onceoff/retro_recent_time_read.rb b/app/jobs/onceoff/retro_recent_time_read.rb index 1dd1ce0289f..b7bf3e7501a 100644 --- a/app/jobs/onceoff/retro_recent_time_read.rb +++ b/app/jobs/onceoff/retro_recent_time_read.rb @@ -15,7 +15,7 @@ module Jobs AND EXISTS (SELECT 1 FROM user_visits visits WHERE visits.user_id = uv1.user_id AND visits.posts_read > 0 LIMIT 1) SQL - UserVisit.exec_sql(sql) + DB.exec(sql) end end end diff --git a/app/jobs/regular/update_username.rb b/app/jobs/regular/update_username.rb index c59275b7164..d3cfe48d78f 100644 --- a/app/jobs/regular/update_username.rb +++ b/app/jobs/regular/update_username.rb @@ -60,7 +60,7 @@ module Jobs new_username: @new_username } - Notification.exec_sql(<<~SQL, params) + DB.exec(<<~SQL, params) UPDATE notifications SET data = (data :: JSONB || jsonb_strip_nulls( @@ -88,7 +88,7 @@ module Jobs end def update_post_custom_fields - PostCustomField.exec_sql(<<~SQL, old_username: @old_username, new_username: @new_username) + DB.exec(<<~SQL, old_username: @old_username, new_username: @new_username) UPDATE post_custom_fields SET value = :new_username WHERE name = 'action_code_who' AND value = :old_username diff --git a/app/jobs/scheduled/clean_up_crawler_stats.rb b/app/jobs/scheduled/clean_up_crawler_stats.rb index 816e91f0fd2..5fbf5d548a3 100644 --- a/app/jobs/scheduled/clean_up_crawler_stats.rb +++ b/app/jobs/scheduled/clean_up_crawler_stats.rb @@ -7,7 +7,7 @@ module Jobs WebCrawlerRequest.where('date < ?', WebCrawlerRequest.max_record_age.ago).delete_all # keep count of only the top user agents - WebCrawlerRequest.exec_sql <<~SQL + DB.exec <<~SQL WITH ranked_requests AS ( SELECT row_number() OVER (ORDER BY count DESC) as row_number, id FROM web_crawler_requests diff --git a/app/jobs/scheduled/grant_anniversary_badges.rb b/app/jobs/scheduled/grant_anniversary_badges.rb index 65eb67423c6..91094a192ea 100644 --- a/app/jobs/scheduled/grant_anniversary_badges.rb +++ b/app/jobs/scheduled/grant_anniversary_badges.rb @@ -13,7 +13,7 @@ module Jobs fmt_end_date = end_date.iso8601(6) fmt_start_date = start_date.iso8601(6) - results = User.exec_sql <<~SQL + user_ids = DB.query_single <<~SQL SELECT u.id AS user_id FROM users AS u INNER JOIN posts AS p ON p.user_id = u.id @@ -33,9 +33,6 @@ module Jobs HAVING COUNT(p.id) > 0 AND COUNT(ub.id) = 0 SQL - user_ids = results.column_values(0) - results.clear - User.where(id: user_ids).find_each do |user| BadgeGranter.grant(badge, user, created_at: end_date) end diff --git a/app/jobs/scheduled/grant_new_user_of_the_month_badges.rb b/app/jobs/scheduled/grant_new_user_of_the_month_badges.rb index 097c3cd99f4..d6a4a16d56c 100644 --- a/app/jobs/scheduled/grant_new_user_of_the_month_badges.rb +++ b/app/jobs/scheduled/grant_new_user_of_the_month_badges.rb @@ -55,7 +55,7 @@ module Jobs ELSE 1.0 END ELSE 0 - END) / (5 + COUNT(DISTINCT p.id)) AS score + END) / (5 + COUNT(DISTINCT p.id))::float AS score FROM users AS u INNER JOIN user_stats AS us ON u.id = us.user_id LEFT OUTER JOIN posts AS p ON p.user_id = u.id @@ -82,10 +82,7 @@ module Jobs LIMIT #{MAX_AWARDED} SQL - result = User.exec_sql(sql) - rval = result.map { |r| [r['id'].to_i, r['score'].to_f] }.to_h - result.clear - rval + Hash[*DB.query_single(sql)] end end diff --git a/app/models/badge.rb b/app/models/badge.rb index 9a64949980b..ed73631eab8 100644 --- a/app/models/badge.rb +++ b/app/models/badge.rb @@ -136,7 +136,7 @@ class Badge < ActiveRecord::Base end def self.ensure_consistency! - exec_sql <<-SQL.squish + DB.exec <<~SQL DELETE FROM user_badges USING user_badges ub LEFT JOIN users u ON u.id = ub.user_id @@ -144,7 +144,7 @@ class Badge < ActiveRecord::Base AND user_badges.id = ub.id SQL - exec_sql <<-SQL.squish + DB.exec <<~SQL WITH X AS ( SELECT badge_id , COUNT(user_id) users diff --git a/app/models/category.rb b/app/models/category.rb index 379cc5acc7c..f0c4e4b8001 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -143,14 +143,14 @@ class Category < ActiveRecord::Base .group("topics.category_id") .visible.to_sql - Category.exec_sql <<-SQL - UPDATE categories c - SET topic_count = x.topic_count, - post_count = x.post_count - FROM (#{topics_with_post_count}) x - WHERE x.category_id = c.id - AND (c.topic_count <> x.topic_count OR c.post_count <> x.post_count) -SQL + DB.exec <<~SQL + UPDATE categories c + SET topic_count = x.topic_count, + post_count = x.post_count + FROM (#{topics_with_post_count}) x + WHERE x.category_id = c.id + AND (c.topic_count <> x.topic_count OR c.post_count <> x.post_count) + SQL # Yes, there are a lot of queries happening below. # Performing a lot of queries is actually faster than using one big update diff --git a/app/models/category_tag_stat.rb b/app/models/category_tag_stat.rb index 8aff0447c8d..b5673bcdf50 100644 --- a/app/models/category_tag_stat.rb +++ b/app/models/category_tag_stat.rb @@ -19,7 +19,7 @@ class CategoryTagStat < ActiveRecord::Base SQL tag_ids = topic.tags.map(&:id) - updated_tag_ids = self.exec_sql(sql, tag_ids: tag_ids, category_id: to_category_id).map { |row| row['tag_id'] } + updated_tag_ids = DB.query_single(sql, tag_ids: tag_ids, category_id: to_category_id) (tag_ids - updated_tag_ids).each do |tag_id| CategoryTagStat.create!(tag_id: tag_id, category_id: to_category_id, topic_count: 1) @@ -41,7 +41,7 @@ class CategoryTagStat < ActiveRecord::Base # Recalculate all topic counts if they got out of sync def self.update_topic_counts - CategoryTagStat.exec_sql <<~SQL + DB.exec <<~SQL UPDATE category_tag_stats stats SET topic_count = x.topic_count FROM ( diff --git a/app/models/category_user.rb b/app/models/category_user.rb index 8d0cf159127..0ae88ecd423 100644 --- a/app/models/category_user.rb +++ b/app/models/category_user.rb @@ -155,14 +155,14 @@ SQL end def self.ensure_consistency! - exec_sql < (self.position) - self.exec_sql " + DB.exec " UPDATE #{self.class.table_name} SET position = position - 1 WHERE position > :current_position and position <= :new_position", current_position: self.position, new_position: position elsif position < self.position - self.exec_sql " + DB.exec " UPDATE #{self.class.table_name} SET position = position + 1 WHERE position >= :new_position and position < :current_position", @@ -28,7 +28,7 @@ module Positionable return end - self.exec_sql " + DB.exec " UPDATE #{self.class.table_name} SET position = :position WHERE id = :id", id: id, position: position diff --git a/app/models/directory_item.rb b/app/models/directory_item.rb index a31453bc022..b60a2f19d86 100644 --- a/app/models/directory_item.rb +++ b/app/models/directory_item.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DirectoryItem < ActiveRecord::Base belongs_to :user has_one :user_stat, foreign_key: :user_id, primary_key: :user_id @@ -42,7 +44,7 @@ class DirectoryItem < ActiveRecord::Base ActiveRecord::Base.transaction do # Delete records that belonged to users who have been deleted - exec_sql "DELETE FROM directory_items + DB.exec "DELETE FROM directory_items USING directory_items di LEFT JOIN users u ON (u.id = user_id AND u.active AND u.silenced_till IS NULL AND u.id > 0) WHERE di.id = directory_items.id AND @@ -50,7 +52,7 @@ class DirectoryItem < ActiveRecord::Base di.period_type = :period_type", period_type: period_types[period_type] # Create new records for users who don't have one yet - exec_sql "INSERT INTO directory_items(period_type, user_id, likes_received, likes_given, topics_entered, days_visited, posts_read, topic_count, post_count) + DB.exec "INSERT INTO directory_items(period_type, user_id, likes_received, likes_given, topics_entered, days_visited, posts_read, topic_count, post_count) SELECT :period_type, u.id, @@ -72,7 +74,7 @@ class DirectoryItem < ActiveRecord::Base # TODO # WARNING: post_count is a wrong name, it should be reply_count (excluding topic post) # - exec_sql "WITH x AS (SELECT + DB.exec "WITH x AS (SELECT u.id user_id, SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = :was_liked_type THEN 1 ELSE 0 END) likes_received, SUM(CASE WHEN p.id IS NOT NULL AND t.id IS NOT NULL AND ua.action_type = :like_type THEN 1 ELSE 0 END) likes_given, @@ -121,23 +123,22 @@ class DirectoryItem < ActiveRecord::Base regular_post_type: Post.types[:regular] if period_type == :all - exec_sql < d.likes_given OR - s.likes_received <> d.likes_received OR - s.topic_count <> d.topic_count OR - s.post_count <> d.post_count - ) - -SQL + FROM directory_items d + WHERE s.user_id = d.user_id AND + d.period_type = 1 AND + ( s.likes_given <> d.likes_given OR + s.likes_received <> d.likes_received OR + s.topic_count <> d.topic_count OR + s.post_count <> d.post_count + ) + SQL end end end diff --git a/app/models/draft.rb b/app/models/draft.rb index be412cc72a3..b008a6c51c9 100644 --- a/app/models/draft.rb +++ b/app/models/draft.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class Draft < ActiveRecord::Base NEW_TOPIC = 'new_topic' NEW_PRIVATE_MESSAGE = 'new_private_message' @@ -7,7 +9,7 @@ class Draft < ActiveRecord::Base d = find_draft(user, key) if d return if d.sequence > sequence - exec_sql("UPDATE drafts + DB.exec("UPDATE drafts SET data = :data, sequence = :sequence, revisions = revisions + 1 @@ -15,6 +17,8 @@ class Draft < ActiveRecord::Base else Draft.create!(user_id: user.id, draft_key: key, data: data, sequence: sequence) end + + true end def self.get(user, key, sequence) @@ -40,7 +44,7 @@ class Draft < ActiveRecord::Base end def self.cleanup! - exec_sql("DELETE FROM drafts where sequence < ( + DB.exec("DELETE FROM drafts where sequence < ( SELECT max(s.sequence) from draft_sequences s WHERE s.draft_key = drafts.draft_key AND s.user_id = drafts.user_id diff --git a/app/models/draft_sequence.rb b/app/models/draft_sequence.rb index 460556d004d..9629e8402c0 100644 --- a/app/models/draft_sequence.rb +++ b/app/models/draft_sequence.rb @@ -11,7 +11,7 @@ class DraftSequence < ActiveRecord::Base c.sequence ||= 0 c.sequence += 1 c.save! - exec_sql("DELETE FROM drafts WHERE user_id = :user_id AND draft_key = :draft_key AND sequence < :sequence", draft_key: key, user_id: user_id, sequence: c.sequence) + DB.exec("DELETE FROM drafts WHERE user_id = :user_id AND draft_key = :draft_key AND sequence < :sequence", draft_key: key, user_id: user_id, sequence: c.sequence) c.sequence end @@ -22,8 +22,8 @@ class DraftSequence < ActiveRecord::Base user_id = user.id unless user.is_a?(Integer) # perf critical path - r = exec_sql('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key).values - r.length.zero? ? 0 : r[0][0] + r, _ = DB.query_single('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key) + r.to_i end end diff --git a/app/models/emoji_set_site_setting.rb b/app/models/emoji_set_site_setting.rb index 952bd37a0dc..003eb770e4a 100644 --- a/app/models/emoji_set_site_setting.rb +++ b/app/models/emoji_set_site_setting.rb @@ -12,7 +12,7 @@ class EmojiSetSiteSetting < EnumSiteSetting after = "/images/emoji/#{site_setting.value}/" Scheduler::Defer.later("Fix Emoji Links") do - Post.exec_sql("UPDATE posts SET cooked = REPLACE(cooked, :before, :after) WHERE cooked LIKE :like", + DB.exec("UPDATE posts SET cooked = REPLACE(cooked, :before, :after) WHERE cooked LIKE :like", before: before, after: after, like: "%#{before}%" diff --git a/app/models/group.rb b/app/models/group.rb index fe0c647e394..60cb1c0282a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -296,7 +296,7 @@ class Group < ActiveRecord::Base "SELECT id FROM users WHERE id <= 0 OR trust_level < #{id - 10}" end - exec_sql <<-SQL + DB.exec <<-SQL DELETE FROM group_users USING (#{remove_subquery}) X WHERE group_id = #{group.id} @@ -318,7 +318,7 @@ class Group < ActiveRecord::Base "SELECT id FROM users WHERE id > 0" end - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO group_users (group_id, user_id, created_at, updated_at) SELECT #{group.id}, X.id, now(), now() FROM group_users @@ -341,7 +341,7 @@ class Group < ActiveRecord::Base end def self.reset_all_counters! - exec_sql <<-SQL + DB.exec <<-SQL WITH X AS ( SELECT group_id , COUNT(user_id) users @@ -362,7 +362,7 @@ class Group < ActiveRecord::Base end def self.refresh_has_messages! - exec_sql <<-SQL + DB.exec <<-SQL UPDATE groups g SET has_messages = false WHERE NOT EXISTS (SELECT tg.id FROM topic_allowed_groups tg @@ -534,7 +534,7 @@ class Group < ActiveRecord::Base ) SQL - Group.exec_sql(sql, group_id: self.id, user_ids: user_ids) + DB.exec(sql, group_id: self.id, user_ids: user_ids) user_attributes = {} @@ -551,7 +551,7 @@ class Group < ActiveRecord::Base end # update group user count - Group.exec_sql <<-SQL.squish + DB.exec <<~SQL UPDATE groups g SET user_count = (SELECT COUNT(gu.user_id) @@ -605,9 +605,10 @@ class Group < ActiveRecord::Base name_lower = self.name.downcase if self.will_save_change_to_name? && self.name_was&.downcase != name_lower - existing = Group.exec_sql( + + existing = DB.exec( User::USERNAME_EXISTS_SQL, username: name_lower - ).values.present? + ) > 0 if existing errors.add(:name, I18n.t("activerecord.errors.messages.taken")) @@ -649,15 +650,15 @@ class Group < ActiveRecord::Base return if new_record? && !self.title.present? if self.saved_change_to_title? - sql = <<-SQL.squish - UPDATE users - SET title = :title - WHERE (title = :title_was OR title = '' OR title IS NULL) - AND COALESCE(title,'') <> COALESCE(:title,'') - AND id IN (SELECT user_id FROM group_users WHERE group_id = :id) - SQL + sql = <<~SQL + UPDATE users + SET title = :title + WHERE (title = :title_was OR title = '' OR title IS NULL) + AND COALESCE(title,'') <> COALESCE(:title,'') + AND id IN (SELECT user_id FROM group_users WHERE group_id = :id) + SQL - self.class.exec_sql(sql, title: title, title_was: title_before_last_save, id: id) + DB.exec(sql, title: title, title_was: title_before_last_save, id: id) end end @@ -666,18 +667,19 @@ class Group < ActiveRecord::Base if self.saved_change_to_primary_group? sql = <<~SQL - UPDATE users - /*set*/ - /*where*/ - SQL + UPDATE users + /*set*/ + /*where*/ + SQL - builder = SqlBuilder.new(sql) - builder.where(" - id IN ( - SELECT user_id - FROM group_users - WHERE group_id = :id - )", id: id) + builder = DB.build(sql) + builder.where(<<~SQL, id: id) + id IN ( + SELECT user_id + FROM group_users + WHERE group_id = :id + ) + SQL if primary_group builder.set("primary_group_id = :id") diff --git a/app/models/group_user.rb b/app/models/group_user.rb index 5840b536649..ed528b3d3a0 100644 --- a/app/models/group_user.rb +++ b/app/models/group_user.rb @@ -25,7 +25,7 @@ class GroupUser < ActiveRecord::Base def set_primary_group if group.primary_group - self.class.exec_sql(" + DB.exec(" UPDATE users SET primary_group_id = :id WHERE id = :user_id", @@ -35,7 +35,7 @@ class GroupUser < ActiveRecord::Base end def remove_primary_group - self.class.exec_sql(" + DB.exec(" UPDATE users SET primary_group_id = NULL WHERE id = :user_id AND primary_group_id = :id", @@ -45,7 +45,7 @@ class GroupUser < ActiveRecord::Base def remove_title if group.title.present? - self.class.exec_sql(" + DB.exec(" UPDATE users SET title = NULL WHERE title = :title AND id = :id", id: user_id, title: group.title @@ -55,7 +55,7 @@ class GroupUser < ActiveRecord::Base def update_title if group.title.present? - self.class.exec_sql(" + DB.exec(" UPDATE users SET title = :title WHERE (title IS NULL OR title = '') AND id = :id", id: user_id, title: group.title diff --git a/app/models/incoming_link.rb b/app/models/incoming_link.rb index 0cc748a9a30..ef680dc4939 100644 --- a/app/models/incoming_link.rb +++ b/app/models/incoming_link.rb @@ -89,10 +89,10 @@ class IncomingLink < ActiveRecord::Base # Internal: Update appropriate link counts. def update_link_counts - exec_sql("UPDATE topics + DB.exec("UPDATE topics SET incoming_link_count = incoming_link_count + 1 WHERE id = (SELECT topic_id FROM posts where id = ?)", post_id) - exec_sql("UPDATE posts + DB.exec("UPDATE posts SET incoming_link_count = incoming_link_count + 1 WHERE id = ?", post_id) end diff --git a/app/models/notification.rb b/app/models/notification.rb index 8e213851bc7..7a294d81bf3 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -19,10 +19,10 @@ class Notification < ActiveRecord::Base after_commit :refresh_notification_count, on: [:create, :update, :destroy] def self.ensure_consistency! - Notification.exec_sql <<-SQL + DB.exec(<<~SQL, Notification.types[:private_message]) DELETE FROM notifications n - WHERE notification_type = #{Notification.types[:private_message]} + WHERE notification_type = ? AND NOT EXISTS ( SELECT 1 FROM posts p @@ -152,17 +152,15 @@ class Notification < ActiveRecord::Base if notifications.present? - ids = Notification.exec_sql(" + ids = DB.query_single(<<~SQL, count.to_i) SELECT n.id FROM notifications n WHERE n.notification_type = 6 AND n.user_id = #{user.id.to_i} AND NOT read ORDER BY n.id ASC - LIMIT #{count.to_i} - ").values.map do |x, _| - x.to_i - end + LIMIT ? + SQL if ids.length > 0 notifications += user diff --git a/app/models/post.rb b/app/models/post.rb index c797c471a88..4e04c736f26 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -669,17 +669,19 @@ class Post < ActiveRecord::Base end def reply_history(max_replies = 100, guardian = nil) - post_ids = Post.exec_sql("WITH RECURSIVE breadcrumb(id, reply_to_post_number) AS ( - SELECT p.id, p.reply_to_post_number FROM posts AS p - WHERE p.id = :post_id - UNION - SELECT p.id, p.reply_to_post_number FROM posts AS p, breadcrumb - WHERE breadcrumb.reply_to_post_number = p.post_number - AND p.topic_id = :topic_id - ) SELECT id from breadcrumb ORDER by id", post_id: id, topic_id: topic_id).to_a - - post_ids.map! { |r| r['id'].to_i } - .reject! { |post_id| post_id == id } + post_ids = DB.query_single(<<~SQL, post_id: id, topic_id: topic_id) + WITH RECURSIVE breadcrumb(id, reply_to_post_number) AS ( + SELECT p.id, p.reply_to_post_number FROM posts AS p + WHERE p.id = :post_id + UNION + SELECT p.id, p.reply_to_post_number FROM posts AS p, breadcrumb + WHERE breadcrumb.reply_to_post_number = p.post_number + AND p.topic_id = :topic_id + ) + SELECT id from breadcrumb + WHERE id <> :post_id + ORDER by id + SQL # [1,2,3][-10,-1] => nil post_ids = (post_ids[(0 - max_replies)..-1] || post_ids) @@ -741,11 +743,11 @@ class Post < ActiveRecord::Base def self.rebake_all_quoted_posts(user_id) return if user_id.blank? - Post.exec_sql <<-SQL + DB.exec(<<~SQL, user_id) WITH user_quoted_posts AS ( SELECT post_id FROM quoted_posts - WHERE quoted_post_id IN (SELECT id FROM posts WHERE user_id = #{user_id}) + WHERE quoted_post_id IN (SELECT id FROM posts WHERE user_id = ?) ) UPDATE posts SET baked_version = NULL diff --git a/app/models/post_action.rb b/app/models/post_action.rb index 479091d8afc..cf4d50d2dc2 100644 --- a/app/models/post_action.rb +++ b/app/models/post_action.rb @@ -340,7 +340,7 @@ SQL def self.copy(original_post, target_post) cols_to_copy = (column_names - %w{id post_id}).join(', ') - exec_sql <<~SQL + DB.exec <<~SQL INSERT INTO post_actions(post_id, #{cols_to_copy}) SELECT #{target_post.id}, #{cols_to_copy} FROM post_actions @@ -425,26 +425,29 @@ SQL # Returns the flag counts for a post, taking into account that some users # can weigh flags differently. def self.flag_counts_for(post_id) - flag_counts = exec_sql("SELECT SUM(CASE - WHEN pa.disagreed_at IS NULL AND pa.staff_took_action THEN :flags_required_to_hide_post - WHEN pa.disagreed_at IS NULL AND NOT pa.staff_took_action THEN 1 - ELSE 0 - END) AS new_flags, - SUM(CASE - WHEN pa.disagreed_at IS NOT NULL AND pa.staff_took_action THEN :flags_required_to_hide_post - WHEN pa.disagreed_at IS NOT NULL AND NOT pa.staff_took_action THEN 1 - ELSE 0 - END) AS old_flags - FROM post_actions AS pa - INNER JOIN users AS u ON u.id = pa.user_id - WHERE pa.post_id = :post_id - AND pa.post_action_type_id IN (:post_action_types) - AND pa.deleted_at IS NULL", - post_id: post_id, - post_action_types: PostActionType.auto_action_flag_types.values, - flags_required_to_hide_post: SiteSetting.flags_required_to_hide_post).first + params = { + post_id: post_id, + post_action_types: PostActionType.auto_action_flag_types.values, + flags_required_to_hide_post: SiteSetting.flags_required_to_hide_post + } - [flag_counts['old_flags'].to_i, flag_counts['new_flags'].to_i] + DB.query_single(<<~SQL, params) + SELECT COALESCE(SUM(CASE + WHEN pa.disagreed_at IS NOT NULL AND pa.staff_took_action THEN :flags_required_to_hide_post + WHEN pa.disagreed_at IS NOT NULL AND NOT pa.staff_took_action THEN 1 + ELSE 0 + END),0) AS old_flags, + COALESCE(SUM(CASE + WHEN pa.disagreed_at IS NULL AND pa.staff_took_action THEN :flags_required_to_hide_post + WHEN pa.disagreed_at IS NULL AND NOT pa.staff_took_action THEN 1 + ELSE 0 + END), 0) AS new_flags + FROM post_actions AS pa + INNER JOIN users AS u ON u.id = pa.user_id + WHERE pa.post_id = :post_id + AND pa.post_action_type_id in (:post_action_types) + AND pa.deleted_at IS NULL + SQL end def post_action_type_key diff --git a/app/models/post_revision.rb b/app/models/post_revision.rb index 0a7016966dd..b91f50765a2 100644 --- a/app/models/post_revision.rb +++ b/app/models/post_revision.rb @@ -10,7 +10,7 @@ class PostRevision < ActiveRecord::Base def self.ensure_consistency! # 1 - fix the numbers - PostRevision.exec_sql <<-SQL + DB.exec <<-SQL UPDATE post_revisions SET number = pr.rank FROM (SELECT id, 1 + ROW_NUMBER() OVER (PARTITION BY post_id ORDER BY number, created_at, updated_at) AS rank FROM post_revisions) AS pr @@ -19,7 +19,7 @@ class PostRevision < ActiveRecord::Base SQL # 2 - fix the versions on the posts - PostRevision.exec_sql <<-SQL + DB.exec <<-SQL UPDATE posts SET version = 1 + (SELECT COUNT(*) FROM post_revisions WHERE post_id = posts.id), public_version = 1 + (SELECT COUNT(*) FROM post_revisions pr WHERE post_id = posts.id AND pr.hidden = 'f') diff --git a/app/models/post_timing.rb b/app/models/post_timing.rb index 54ed9bbefcf..23d224c5e64 100644 --- a/app/models/post_timing.rb +++ b/app/models/post_timing.rb @@ -12,7 +12,7 @@ class PostTiming < ActiveRecord::Base def self.pretend_read(topic_id, actual_read_post_number, pretend_read_post_number) # This is done in SQL cause the logic is quite tricky and we want to do this in one db hit # - exec_sql("INSERT INTO post_timings(topic_id, user_id, post_number, msecs) + DB.exec("INSERT INTO post_timings(topic_id, user_id, post_number, msecs) SELECT :topic_id, user_id, :pretend_read_post_number, 1 FROM post_timings pt WHERE topic_id = :topic_id AND @@ -34,7 +34,7 @@ class PostTiming < ActiveRecord::Base def self.record_new_timing(args) begin - exec_sql("INSERT INTO post_timings (topic_id, user_id, post_number, msecs) + DB.exec("INSERT INTO post_timings (topic_id, user_id, post_number, msecs) SELECT :topic_id, :user_id, :post_number, :msecs WHERE NOT EXISTS(SELECT 1 FROM post_timings WHERE topic_id = :topic_id @@ -53,12 +53,13 @@ class PostTiming < ActiveRecord::Base # Increases a timer if a row exists, otherwise create it def self.record_timing(args) - rows = exec_sql_row_count("UPDATE post_timings - SET msecs = msecs + :msecs - WHERE topic_id = :topic_id - AND user_id = :user_id - AND post_number = :post_number", - args) + rows = DB.exec(<<~SQL, args) + UPDATE post_timings + SET msecs = msecs + :msecs + WHERE topic_id = :topic_id + AND user_id = :user_id + AND post_number = :post_number + SQL record_new_timing(args) if rows == 0 end @@ -115,9 +116,7 @@ class PostTiming < ActiveRecord::Base RETURNING x.idx SQL - result = exec_sql(sql) - result.type_map = SqlBuilder.pg_type_map - existing = Set.new(result.column_values(0)) + existing = Set.new(DB.query_single(sql)) sql = <<~SQL SELECT 1 FROM topics @@ -126,7 +125,7 @@ SQL id = :topic_id SQL - is_regular = Post.exec_sql(sql, topic_id: topic_id).cmd_tuples == 1 + is_regular = DB.exec(sql, topic_id: topic_id) == 1 new_posts_read = timings.size - existing.size if is_regular timings.each_with_index do |(post_number, time), index| diff --git a/app/models/quoted_post.rb b/app/models/quoted_post.rb index c6d24675f5b..26cc80ef8d9 100644 --- a/app/models/quoted_post.rb +++ b/app/models/quoted_post.rb @@ -11,7 +11,7 @@ class QuotedPost < ActiveRecord::Base uniq = {} - exec_sql("DELETE FROM quoted_posts WHERE post_id = :post_id", post_id: post.id) + DB.exec("DELETE FROM quoted_posts WHERE post_id = :post_id", post_id: post.id) doc.css("aside.quote[data-topic]").each do |a| topic_id = a['data-topic'].to_i @@ -23,7 +23,7 @@ class QuotedPost < ActiveRecord::Base begin # It would be so much nicer if we used post_id in quotes - exec_sql(<<~SQL, post_id: post.id, post_number: post_number, topic_id: topic_id) + DB.exec(<<~SQL, post_id: post.id, post_number: post_number, topic_id: topic_id) INSERT INTO quoted_posts (post_id, quoted_post_id, created_at, updated_at) SELECT :post_id, p.id, current_timestamp, current_timestamp FROM posts p diff --git a/app/models/screened_ip_address.rb b/app/models/screened_ip_address.rb index 0c23b574b4b..29729e622e2 100644 --- a/app/models/screened_ip_address.rb +++ b/app/models/screened_ip_address.rb @@ -94,8 +94,8 @@ class ScreenedIpAddress < ActiveRecord::Base end def self.star_subnets_query - @star_subnets_query ||= <<-SQL - SELECT network(inet(host(ip_address) || '/24')) AS ip_range + @star_subnets_query ||= <<~SQL + SELECT network(inet(host(ip_address) || '/24'))::text AS ip_range FROM screened_ip_addresses WHERE action_type = #{ScreenedIpAddress.actions[:block]} AND family(ip_address) = 4 @@ -106,9 +106,9 @@ class ScreenedIpAddress < ActiveRecord::Base end def self.star_star_subnets_query - @star_star_subnets_query ||= <<-SQL + @star_star_subnets_query ||= <<~SQL WITH weighted_subnets AS ( - SELECT network(inet(host(ip_address) || '/16')) AS ip_range, + SELECT network(inet(host(ip_address) || '/16'))::text AS ip_range, CASE masklen(ip_address) WHEN 32 THEN 1 WHEN 24 THEN :roll_up_weight @@ -127,12 +127,12 @@ class ScreenedIpAddress < ActiveRecord::Base def self.star_subnets min_count = SiteSetting.min_ban_entries_for_roll_up - ScreenedIpAddress.exec_sql(star_subnets_query, min_count: min_count).values.flatten + DB.query_single(star_subnets_query, min_count: min_count) end def self.star_star_subnets weight = SiteSetting.min_ban_entries_for_roll_up - ScreenedIpAddress.exec_sql(star_star_subnets_query, min_count: 10, roll_up_weight: weight).values.flatten + DB.query_single(star_star_subnets_query, min_count: 10, roll_up_weight: weight) end def self.roll_up(current_user = Discourse.system_user) @@ -143,7 +143,7 @@ class ScreenedIpAddress < ActiveRecord::Base subnets.each do |subnet| ScreenedIpAddress.create(ip_address: subnet) unless ScreenedIpAddress.where("? <<= ip_address", subnet).exists? - sql = <<-SQL + sql = <<~SQL UPDATE screened_ip_addresses SET match_count = sum_match_count , created_at = min_created_at @@ -160,7 +160,7 @@ class ScreenedIpAddress < ActiveRecord::Base WHERE ip_address = :ip_address SQL - ScreenedIpAddress.exec_sql(sql, ip_address: subnet) + DB.exec(sql, ip_address: subnet) ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block]) .where("family(ip_address) = 4") diff --git a/app/models/stylesheet_cache.rb b/app/models/stylesheet_cache.rb index 08b98c30859..52f6ef337e6 100644 --- a/app/models/stylesheet_cache.rb +++ b/app/models/stylesheet_cache.rb @@ -24,7 +24,7 @@ class StylesheetCache < ActiveRecord::Base .pluck(:id) .last - exec_sql("DELETE FROM stylesheet_cache where id < :id", id: remove_lower) + DB.exec("DELETE FROM stylesheet_cache where id < :id", id: remove_lower) end success diff --git a/app/models/tag.rb b/app/models/tag.rb index 46ef77bde7b..705d6768a5b 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -25,7 +25,7 @@ class Tag < ActiveRecord::Base end def self.update_topic_counts - Tag.exec_sql <<~SQL + DB.exec <<~SQL UPDATE tags t SET topic_count = x.topic_count FROM ( @@ -41,7 +41,7 @@ class Tag < ActiveRecord::Base AND x.topic_count <> t.topic_count SQL - Tag.exec_sql <<~SQL + DB.exec <<~SQL UPDATE tags t SET pm_topic_count = x.pm_topic_count FROM ( @@ -70,7 +70,7 @@ class Tag < ActiveRecord::Base filter_sql = guardian&.is_staff? ? '' : " AND tags.id NOT IN (#{DiscourseTagging.hidden_tags_query.select(:id).to_sql})" - tag_names_with_counts = Tag.exec_sql <<~SQL + tag_names_with_counts = DB.query <<~SQL SELECT tags.name as tag_name, SUM(stats.topic_count) AS sum_topic_count FROM category_tag_stats stats JOIN tags ON stats.tag_id = tags.id AND stats.topic_count > 0 @@ -81,7 +81,7 @@ class Tag < ActiveRecord::Base LIMIT #{limit} SQL - tag_names_with_counts.map { |row| row['tag_name'] } + tag_names_with_counts.map { |row| row.tag_name } end def self.pm_tags(limit_arg: nil, guardian: nil, allowed_user: nil) @@ -89,8 +89,8 @@ class Tag < ActiveRecord::Base limit = limit_arg || SiteSetting.max_tags_in_filter_list user_id = allowed_user.id - tag_names_with_counts = Tag.exec_sql <<~SQL - SELECT tags.name, COUNT(topics.id) AS topic_count + DB.query_hash(<<~SQL).map!(&:symbolize_keys!) + SELECT tags.name as id, tags.name as text, COUNT(topics.id) AS count FROM tags JOIN topic_tags ON tags.id = topic_tags.tag_id JOIN topics ON topics.id = topic_tags.topic_id @@ -109,8 +109,6 @@ class Tag < ActiveRecord::Base GROUP BY tags.name LIMIT #{limit} SQL - - tag_names_with_counts.map { |t| { id: t['name'], text: t['name'], count: t['topic_count'] } } end def self.include_tags? diff --git a/app/models/top_topic.rb b/app/models/top_topic.rb index 709148167c4..58703023b46 100644 --- a/app/models/top_topic.rb +++ b/app/models/top_topic.rb @@ -59,7 +59,7 @@ class TopTopic < ActiveRecord::Base end def self.remove_invisible_topics - exec_sql("WITH category_definition_topic_ids AS ( + DB.exec("WITH category_definition_topic_ids AS ( SELECT COALESCE(topic_id, 0) AS id FROM categories ), invisible_topic_ids AS ( SELECT id @@ -76,7 +76,7 @@ class TopTopic < ActiveRecord::Base end def self.add_new_visible_topics - exec_sql("WITH category_definition_topic_ids AS ( + DB.exec("WITH category_definition_topic_ids AS ( SELECT COALESCE(topic_id, 0) AS id FROM categories ), visible_topics AS ( SELECT t.id @@ -167,7 +167,7 @@ class TopTopic < ActiveRecord::Base time_filter = "topics.created_at < :from" end - sql = <<-SQL + sql = <<~SQL WITH top AS ( SELECT CASE WHEN #{time_filter} THEN 0 @@ -197,7 +197,7 @@ class TopTopic < ActiveRecord::Base AND #{period}_score <> top.score SQL - exec_sql(sql, from: start_of(period)) + DB.exec(sql, from: start_of(period)) end def self.start_of(period) @@ -211,7 +211,7 @@ class TopTopic < ActiveRecord::Base end def self.update_top_topics(period, sort, inner_join) - exec_sql("UPDATE top_topics + DB.exec("UPDATE top_topics SET #{period}_#{sort}_count = c.count FROM top_topics tt INNER JOIN (#{inner_join}) c ON tt.topic_id = c.topic_id diff --git a/app/models/topic.rb b/app/models/topic.rb index 9b1c1145ea0..b0e1e628018 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -352,7 +352,7 @@ class Topic < ActiveRecord::Base if !new_record? && !Discourse.readonly_mode? # make sure data is set in table, this also allows us to change algorithm # by simply nulling this column - exec_sql("UPDATE topics SET fancy_title = :fancy_title where id = :id", id: self.id, fancy_title: fancy_title) + DB.exec("UPDATE topics SET fancy_title = :fancy_title where id = :id", id: self.id, fancy_title: fancy_title) end end @@ -522,130 +522,140 @@ class Topic < ActiveRecord::Base # Atomically creates the next post number def self.next_post_number(topic_id, reply = false, whisper = false) - highest = exec_sql("SELECT coalesce(max(post_number),0) AS max FROM posts WHERE topic_id = ?", topic_id).first['max'].to_i + highest = DB.query_single("SELECT coalesce(max(post_number),0) AS max FROM posts WHERE topic_id = ?", topic_id).first.to_i if whisper - result = exec_sql("UPDATE topics - SET highest_staff_post_number = ? + 1 - WHERE id = ? - RETURNING highest_staff_post_number", highest, topic_id) + result = DB.query_single(<<~SQL, highest, topic_id) + UPDATE topics + SET highest_staff_post_number = ? + 1 + WHERE id = ? + RETURNING highest_staff_post_number + SQL - result.first['highest_staff_post_number'].to_i + result.first.to_i else reply_sql = reply ? ", reply_count = reply_count + 1" : "" - result = exec_sql("UPDATE topics - SET highest_staff_post_number = :highest + 1, - highest_post_number = :highest + 1#{reply_sql}, - posts_count = posts_count + 1 - WHERE id = :topic_id - RETURNING highest_post_number", highest: highest, topic_id: topic_id) + result = DB.query_single(<<~SQL, highest: highest, topic_id: topic_id) + UPDATE topics + SET highest_staff_post_number = :highest + 1, + highest_post_number = :highest + 1#{reply_sql}, + posts_count = posts_count + 1 + WHERE id = :topic_id + RETURNING highest_post_number + SQL - result.first['highest_post_number'].to_i + result.first.to_i end end def self.reset_all_highest! - exec_sql < 4 - GROUP BY topic_id -) -UPDATE topics -SET - highest_staff_post_number = X.highest_post_number, - highest_post_number = Y.highest_post_number, - last_posted_at = Y.last_posted_at, - posts_count = Y.posts_count -FROM X, Y -WHERE - X.topic_id = topics.id AND - Y.topic_id = topics.id AND ( - topics.highest_staff_post_number <> X.highest_post_number OR - topics.highest_post_number <> Y.highest_post_number OR - topics.last_posted_at <> Y.last_posted_at OR - topics.posts_count <> Y.posts_count - ) -SQL + DB.exec <<~SQL + WITH + X as ( + SELECT topic_id, + COALESCE(MAX(post_number), 0) highest_post_number + FROM posts + WHERE deleted_at IS NULL + GROUP BY topic_id + ), + Y as ( + SELECT topic_id, + coalesce(MAX(post_number), 0) highest_post_number, + count(*) posts_count, + max(created_at) last_posted_at + FROM posts + WHERE deleted_at IS NULL AND post_type <> 4 + GROUP BY topic_id + ) + UPDATE topics + SET + highest_staff_post_number = X.highest_post_number, + highest_post_number = Y.highest_post_number, + last_posted_at = Y.last_posted_at, + posts_count = Y.posts_count + FROM X, Y + WHERE + X.topic_id = topics.id AND + Y.topic_id = topics.id AND ( + topics.highest_staff_post_number <> X.highest_post_number OR + topics.highest_post_number <> Y.highest_post_number OR + topics.last_posted_at <> Y.last_posted_at OR + topics.posts_count <> Y.posts_count + ) + SQL end # If a post is deleted we have to update our highest post counters def self.reset_highest(topic_id) - result = exec_sql "UPDATE topics - SET - highest_staff_post_number = ( - SELECT COALESCE(MAX(post_number), 0) FROM posts - WHERE topic_id = :topic_id AND - deleted_at IS NULL - ), - highest_post_number = ( - SELECT COALESCE(MAX(post_number), 0) FROM posts - WHERE topic_id = :topic_id AND - deleted_at IS NULL AND - post_type <> 4 - ), - posts_count = ( - SELECT count(*) FROM posts - WHERE deleted_at IS NULL AND - topic_id = :topic_id AND - post_type <> 4 - ), + result = DB.query_single(<<~SQL, topic_id: topic_id) + UPDATE topics + SET + highest_staff_post_number = ( + SELECT COALESCE(MAX(post_number), 0) FROM posts + WHERE topic_id = :topic_id AND + deleted_at IS NULL + ), + highest_post_number = ( + SELECT COALESCE(MAX(post_number), 0) FROM posts + WHERE topic_id = :topic_id AND + deleted_at IS NULL AND + post_type <> 4 + ), + posts_count = ( + SELECT count(*) FROM posts + WHERE deleted_at IS NULL AND + topic_id = :topic_id AND + post_type <> 4 + ), - last_posted_at = ( - SELECT MAX(created_at) FROM posts - WHERE topic_id = :topic_id AND - deleted_at IS NULL AND - post_type <> 4 - ) - WHERE id = :topic_id - RETURNING highest_post_number", topic_id: topic_id + last_posted_at = ( + SELECT MAX(created_at) FROM posts + WHERE topic_id = :topic_id AND + deleted_at IS NULL AND + post_type <> 4 + ) + WHERE id = :topic_id + RETURNING highest_post_number + SQL - highest_post_number = result.first['highest_post_number'].to_i + highest_post_number = result.first.to_i # Update the forum topic user records - exec_sql "UPDATE topic_users - SET last_read_post_number = CASE - WHEN last_read_post_number > :highest THEN :highest - ELSE last_read_post_number - END, - highest_seen_post_number = CASE - WHEN highest_seen_post_number > :highest THEN :highest - ELSE highest_seen_post_number - END - WHERE topic_id = :topic_id", - highest: highest_post_number, - topic_id: topic_id + DB.exec(<<~SQL, highest: highest_post_number, topic_id: topic_id) + UPDATE topic_users + SET last_read_post_number = CASE + WHEN last_read_post_number > :highest THEN :highest + ELSE last_read_post_number + END, + highest_seen_post_number = CASE + WHEN highest_seen_post_number > :highest THEN :highest + ELSE highest_seen_post_number + END + WHERE topic_id = :topic_id + SQL end # This calculates the geometric mean of the posts and stores it with the topic def self.calculate_avg_time(min_topic_age = nil) - builder = SqlBuilder.new("UPDATE topics - SET avg_time = x.gmean - FROM (SELECT topic_id, - round(exp(avg(ln(avg_time)))) AS gmean - FROM posts - WHERE avg_time > 0 AND avg_time IS NOT NULL - GROUP BY topic_id) AS x - /*where*/") + builder = DB.build <<~SQL + UPDATE topics + SET avg_time = x.gmean + FROM (SELECT topic_id, + round(exp(avg(ln(avg_time)))) AS gmean + FROM posts + WHERE avg_time > 0 AND avg_time IS NOT NULL + GROUP BY topic_id) AS x + /*where*/ + SQL - builder.where("x.topic_id = topics.id AND - (topics.avg_time <> x.gmean OR topics.avg_time IS NULL)") + builder.where <<~SQL + x.topic_id = topics.id AND + (topics.avg_time <> x.gmean OR topics.avg_time IS NULL) + SQL if min_topic_age builder.where("topics.bumped_at > :bumped_at", bumped_at: min_topic_age) @@ -1179,30 +1189,30 @@ SQL # OR if you have it archived as a user explicitly sql = <<~SQL - SELECT 1 - WHERE - ( - SELECT count(*) FROM topic_allowed_groups tg - JOIN group_archived_messages gm - ON gm.topic_id = tg.topic_id AND - gm.group_id = tg.group_id - WHERE tg.group_id IN (SELECT g.group_id FROM group_users g WHERE g.user_id = :user_id) - AND tg.topic_id = :topic_id - ) = - ( - SELECT case when count(*) = 0 then -1 else count(*) end FROM topic_allowed_groups tg - WHERE tg.group_id IN (SELECT g.group_id FROM group_users g WHERE g.user_id = :user_id) - AND tg.topic_id = :topic_id - ) + SELECT 1 + WHERE + ( + SELECT count(*) FROM topic_allowed_groups tg + JOIN group_archived_messages gm + ON gm.topic_id = tg.topic_id AND + gm.group_id = tg.group_id + WHERE tg.group_id IN (SELECT g.group_id FROM group_users g WHERE g.user_id = :user_id) + AND tg.topic_id = :topic_id + ) = + ( + SELECT case when count(*) = 0 then -1 else count(*) end FROM topic_allowed_groups tg + WHERE tg.group_id IN (SELECT g.group_id FROM group_users g WHERE g.user_id = :user_id) + AND tg.topic_id = :topic_id + ) - UNION ALL + UNION ALL - SELECT 1 FROM topic_allowed_users tu - JOIN user_archived_messages um ON um.user_id = tu.user_id AND um.topic_id = tu.topic_id - WHERE tu.user_id = :user_id AND tu.topic_id = :topic_id -SQL + SELECT 1 FROM topic_allowed_users tu + JOIN user_archived_messages um ON um.user_id = tu.user_id AND um.topic_id = tu.topic_id + WHERE tu.user_id = :user_id AND tu.topic_id = :topic_id + SQL - User.exec_sql(sql, user_id: user.id, topic_id: id).to_a.length > 0 + DB.exec(sql, user_id: user.id, topic_id: id) > 0 end TIME_TO_FIRST_RESPONSE_SQL ||= <<-SQL @@ -1325,8 +1335,8 @@ SQL ) = 1 SQL - result = Topic.exec_sql(sql, private_message: Archetype.private_message, topic_id: self.id) - result.ntuples != 0 + result = DB.exec(sql, private_message: Archetype.private_message, topic_id: self.id) + result != 0 end def featured_link_root_domain diff --git a/app/models/topic_featured_users.rb b/app/models/topic_featured_users.rb index d8a8a406cb1..7b442423b24 100644 --- a/app/models/topic_featured_users.rb +++ b/app/models/topic_featured_users.rb @@ -79,7 +79,7 @@ WHERE tt.id = tt2.id AND #{filter2} SQL - Topic.exec_sql(sql) + DB.exec(sql) end private diff --git a/app/models/topic_link.rb b/app/models/topic_link.rb index 40fbcab8010..c7a745218d1 100644 --- a/app/models/topic_link.rb +++ b/app/models/topic_link.rb @@ -38,7 +38,7 @@ class TopicLink < ActiveRecord::Base def self.topic_map(guardian, topic_id) # Sam: complicated reports are really hard in AR - builder = SqlBuilder.new <<-SQL + builder = DB.build <<-SQL SELECT ftl.url, COALESCE(ft.title, ftl.title) AS title, ftl.link_topic_id, @@ -64,16 +64,16 @@ SQL builder.secure_category(guardian.secure_category_ids) - builder.exec.to_a + builder.query end def self.counts_for(guardian, topic, posts) return {} if posts.blank? - # Sam: I don't know how to write this cleanly in AR, - # in particular the securing logic is tricky and would fallback to SQL anyway - builder = SqlBuilder.new("SELECT + # Sam: this is not tidy in AR and also happens to be a critical path + # for topic view + builder = DB.build("SELECT l.post_id, l.url, l.clicks, @@ -91,10 +91,11 @@ SQL builder.where("COALESCE(t.archetype, 'regular') <> :archetype", archetype: Archetype.private_message) # not certain if pluck is right, cause it may interfere with caching - builder.where('l.post_id IN (:post_ids)', post_ids: posts.map(&:id)) + builder.where('l.post_id in (:post_ids)', post_ids: posts.map(&:id)) builder.secure_category(guardian.secure_category_ids) - builder.map_exec(OpenStruct).each_with_object({}) do |l, result| + result = {} + builder.query.each do |l| result[l.post_id] ||= [] result[l.post_id] << { url: l.url, clicks: l.clicks, @@ -102,6 +103,7 @@ SQL internal: l.internal, reflection: l.reflection } end + result end def self.extract_from(post) diff --git a/app/models/topic_user.rb b/app/models/topic_user.rb index a6cff083ded..b41fabb3daa 100644 --- a/app/models/topic_user.rb +++ b/app/models/topic_user.rb @@ -53,26 +53,26 @@ class TopicUser < ActiveRecord::Base def unwatch_categories!(user, category_ids) track_threshold = user.user_option.auto_track_topics_after_msecs - sql = < :track_threshold AND :track_threshold >= 0 THEN :tracking - ELSE :regular - end - FROM topics t - WHERE t.id = tu.topic_id AND tu.notification_level <> :muted AND category_id IN (:category_ids) AND tu.user_id = :user_id -SQL + sql = <<~SQL + UPDATE topic_users tu + SET notification_level = CASE + WHEN t.user_id = :user_id THEN :watching + WHEN total_msecs_viewed > :track_threshold AND :track_threshold >= 0 THEN :tracking + ELSE :regular + end + FROM topics t + WHERE t.id = tu.topic_id AND tu.notification_level <> :muted AND category_id IN (:category_ids) AND tu.user_id = :user_id + SQL - exec_sql(sql, - watching: notification_levels[:watching], - tracking: notification_levels[:tracking], - regular: notification_levels[:regular], - muted: notification_levels[:muted], - category_ids: category_ids, - user_id: user.id, - track_threshold: track_threshold - ) + DB.exec(sql, + watching: notification_levels[:watching], + tracking: notification_levels[:tracking], + regular: notification_levels[:regular], + muted: notification_levels[:muted], + category_ids: category_ids, + user_id: user.id, + track_threshold: track_threshold + ) end # Find the information specific to a user in a forum topic @@ -296,16 +296,15 @@ SQL # 86400000 = 1 day rows = if user.staff? - exec_sql(UPDATE_TOPIC_USER_SQL_STAFF, args).values + DB.query(UPDATE_TOPIC_USER_SQL_STAFF, args) else - exec_sql(UPDATE_TOPIC_USER_SQL, args).values + DB.query(UPDATE_TOPIC_USER_SQL, args) end if rows.length == 1 - before = rows[0][1].to_i - after = rows[0][0].to_i - - before_last_read = rows[0][2].to_i + before = rows[0].old_level.to_i + after = rows[0].notification_level.to_i + before_last_read = rows[0].last_read_post_number.to_i if before_last_read < post_number # The user read at least one new post @@ -333,9 +332,9 @@ SQL begin if user.staff? - exec_sql(INSERT_TOPIC_USER_SQL_STAFF, args) + DB.exec(INSERT_TOPIC_USER_SQL_STAFF, args) else - exec_sql(INSERT_TOPIC_USER_SQL, args) + DB.exec(INSERT_TOPIC_USER_SQL, args) end rescue PG::UniqueViolation # if record is inserted between two statements this can happen @@ -431,7 +430,7 @@ SQL ) SQL - TopicUser.exec_sql(sql, user_id: user_id, count: count) + DB.exec(sql, user_id: user_id, count: count) end def self.ensure_consistency!(topic_id = nil) diff --git a/app/models/trust_level3_requirements.rb b/app/models/trust_level3_requirements.rb index ac63e5ae6aa..7b856905087 100644 --- a/app/models/trust_level3_requirements.rb +++ b/app/models/trust_level3_requirements.rb @@ -122,7 +122,7 @@ class TrustLevel3Requirements AND uh.action IN (:silence_user, :unsilence_user, :suspend_user, :unsuspend_user) SQL - PenaltyCounts.new(UserHistory.exec_sql(sql, args).first) + PenaltyCounts.new(DB.query_hash(sql, args).first) end def min_days_visited diff --git a/app/models/user.rb b/app/models/user.rb index a20687a5d1c..e389a4ea4ce 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -208,7 +208,7 @@ class User < ActiveRecord::Base def self.username_available?(username, email = nil) lower = username.downcase return false if reserved_username?(lower) - return true if User.exec_sql(User::USERNAME_EXISTS_SQL, username: lower).count == 0 + return true if DB.exec(User::USERNAME_EXISTS_SQL, username: lower) == 0 # staged users can use the same username since they will take over the account email.present? && User.joins(:user_emails).exists?(staged: true, username_lower: lower, user_emails: { primary: true, email: email }) @@ -387,7 +387,8 @@ class User < ActiveRecord::Base AND NOT read SQL - User.exec_sql(sql, user_id: id, type: notification_type).getvalue(0, 0).to_i + # to avoid coalesce we do to_i + DB.query_single(sql, user_id: id, type: notification_type)[0].to_i end def unread_private_messages @@ -408,11 +409,11 @@ class User < ActiveRecord::Base AND NOT read SQL - User.exec_sql(sql, + DB.query_single(sql, user_id: id, seen_notification_id: seen_notification_id, pm: Notification.types[:private_message] - ).getvalue(0, 0).to_i + )[0].to_i end end @@ -446,7 +447,7 @@ class User < ActiveRecord::Base notification = notifications.visible.order('notifications.id desc').first json = NotificationSerializer.new(notification).as_json if notification - sql = " + sql = (<<~SQL).freeze SELECT * FROM ( SELECT n.id, n.read FROM notifications n LEFT JOIN topics t ON n.topic_id = t.id @@ -469,13 +470,13 @@ class User < ActiveRecord::Base ORDER BY n.id DESC LIMIT 20 ) AS y - " + SQL - recent = User.exec_sql(sql, + recent = DB.query(sql, user_id: id, type: Notification.types[:private_message] - ).values.map! do |id, read| - [id.to_i, read] + ).map! do |r| + [r.id, r.read] end payload = { @@ -1155,12 +1156,12 @@ class User < ActiveRecord::Base end USERNAME_EXISTS_SQL = <<~SQL - (SELECT users.id AS user_id FROM users + (SELECT users.id AS id, true as is_user FROM users WHERE users.username_lower = :username) UNION ALL - (SELECT groups.id AS group_id FROM groups + (SELECT groups.id, false as is_user FROM groups WHERE lower(groups.name) = :username) SQL @@ -1168,11 +1169,14 @@ class User < ActiveRecord::Base username_format_validator || begin lower = username.downcase - existing = User.exec_sql( + existing = DB.query( USERNAME_EXISTS_SQL, username: lower - ).to_a.first + ) - if will_save_change_to_username? && existing.present? && existing["user_id"] != self.id + user_id = existing.select { |u| u.is_user }.first&.id + same_user = user_id && user_id == self.id + + if will_save_change_to_username? && existing.present? && !same_user errors.add(:username, I18n.t(:'user.username.unique')) end end @@ -1200,7 +1204,7 @@ class User < ActiveRecord::Base end if values.present? - exec_sql("INSERT INTO category_users (user_id, category_id, notification_level) VALUES #{values.join(",")}") + DB.exec("INSERT INTO category_users (user_id, category_id, notification_level) VALUES #{values.join(",")}") end end diff --git a/app/models/user_action.rb b/app/models/user_action.rb index f36c034b0ec..981cb4a9c44 100644 --- a/app/models/user_action.rb +++ b/app/models/user_action.rb @@ -97,7 +97,8 @@ SQL AND t.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = :user_id) SQL - all, mine, unread = exec_sql(sql, user_id: user_id).values[0].map(&:to_i) + # map is there due to count returning nil + all, mine, unread = DB.query_single(sql, user_id: user_id).map(&:to_i) sql = <<-SQL SELECT g.name, COUNT(*) "count" @@ -112,8 +113,8 @@ SQL result = { all: all, mine: mine, unread: unread } - exec_sql(sql, user_id: user_id).each do |row| - (result[:groups] ||= []) << { name: row["name"], count: row["count"].to_i } + DB.query(sql, user_id: user_id).each do |row| + (result[:groups] ||= []) << { name: row.name, count: row.count.to_i } end result diff --git a/app/models/user_auth_token.rb b/app/models/user_auth_token.rb index 57eeaca8882..b7549486cab 100644 --- a/app/models/user_auth_token.rb +++ b/app/models/user_auth_token.rb @@ -131,7 +131,7 @@ class UserAuthToken < ActiveRecord::Base token = SecureRandom.hex(16) - result = UserAuthToken.exec_sql(" + result = DB.exec(" UPDATE user_auth_tokens SET auth_token_seen = false, @@ -150,7 +150,7 @@ class UserAuthToken < ActiveRecord::Base safeguard_time: 30.seconds.ago ) - if result.cmdtuples > 0 + if result > 0 reload self.unhashed_auth_token = token diff --git a/app/models/user_option.rb b/app/models/user_option.rb index 5077d8dc364..95c34d51f6a 100644 --- a/app/models/user_option.rb +++ b/app/models/user_option.rb @@ -6,10 +6,14 @@ class UserOption < ActiveRecord::Base after_save :update_tracked_topics def self.ensure_consistency! - exec_sql("SELECT u.id FROM users u - LEFT JOIN user_options o ON o.user_id = u.id - WHERE o.user_id IS NULL").values.each do |id, _| - UserOption.create(user_id: id.to_i) + sql = <<~SQL + SELECT u.id FROM users u + LEFT JOIN user_options o ON o.user_id = u.id + WHERE o.user_id IS NULL + SQL + + DB.query_single(sql).each do |id| + UserOption.create(user_id: id) end end diff --git a/app/models/user_stat.rb b/app/models/user_stat.rb index cf9ab05e2ca..86731032a39 100644 --- a/app/models/user_stat.rb +++ b/app/models/user_stat.rb @@ -23,34 +23,36 @@ class UserStat < ActiveRecord::Base # we also ensure we only touch the table if data changes # Update denormalized topics_entered - exec_sql "UPDATE user_stats SET topics_entered = X.c - FROM - (SELECT v.user_id, COUNT(topic_id) AS c - FROM topic_views AS v - WHERE v.user_id IN ( - SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at - ) - GROUP BY v.user_id) AS X - WHERE - X.user_id = user_stats.user_id AND - X.c <> topics_entered - ", seen_at: last_seen + DB.exec(<<~SQL, seen_at: last_seen) + UPDATE user_stats SET topics_entered = X.c + FROM + (SELECT v.user_id, COUNT(topic_id) AS c + FROM topic_views AS v + WHERE v.user_id IN ( + SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at + ) + GROUP BY v.user_id) AS X + WHERE + X.user_id = user_stats.user_id AND + X.c <> topics_entered + SQL # Update denormalzied posts_read_count - exec_sql "UPDATE user_stats SET posts_read_count = X.c - FROM - (SELECT pt.user_id, - COUNT(*) AS c - FROM users AS u - JOIN post_timings AS pt ON pt.user_id = u.id - JOIN topics t ON t.id = pt.topic_id - WHERE u.last_seen_at > :seen_at AND - t.archetype = 'regular' AND - t.deleted_at IS NULL - GROUP BY pt.user_id) AS X - WHERE X.user_id = user_stats.user_id AND - X.c <> posts_read_count - ", seen_at: last_seen + DB.exec(<<~SQL, seen_at: last_seen) + UPDATE user_stats SET posts_read_count = X.c + FROM + (SELECT pt.user_id, + COUNT(*) AS c + FROM users AS u + JOIN post_timings AS pt ON pt.user_id = u.id + JOIN topics t ON t.id = pt.topic_id + WHERE u.last_seen_at > :seen_at AND + t.archetype = 'regular' AND + t.deleted_at IS NULL + GROUP BY pt.user_id) AS X + WHERE X.user_id = user_stats.user_id AND + X.c <> posts_read_count + SQL end # topic_reply_count is a count of posts in other users' topics diff --git a/app/models/user_visit.rb b/app/models/user_visit.rb index 52bd2fab9c6..6fe21eae7bb 100644 --- a/app/models/user_visit.rb +++ b/app/models/user_visit.rb @@ -11,7 +11,7 @@ class UserVisit < ActiveRecord::Base end def self.count_by_active_users(start_date, end_date) - sql = < - ( - SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id - ) -SQL + DB.exec <<~SQL + UPDATE user_stats u set days_visited = + ( + SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id + ) + WHERE days_visited <> + ( + SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id + ) + SQL end end diff --git a/app/serializers/topic_link_serializer.rb b/app/serializers/topic_link_serializer.rb index 42367857c54..4ef9afc65b0 100644 --- a/app/serializers/topic_link_serializer.rb +++ b/app/serializers/topic_link_serializer.rb @@ -2,7 +2,7 @@ class TopicLinkSerializer < ApplicationSerializer attributes :url, :title, - :fancy_title, + # :fancy_title, :internal, :attachment, :reflection, @@ -11,44 +11,12 @@ class TopicLinkSerializer < ApplicationSerializer :domain, :root_domain, - def url - object['url'] - end - - def title - object['title'] - end - - def fancy_title - object['fancy_title'] - end - - def internal - object['internal'] == 't' - end - def attachment - Discourse.store.has_been_uploaded?(object['url']) - end - - def reflection - object['reflection'] == 't' - end - - def clicks - object['clicks'].to_i - end - - def user_id - object['user_id'].to_i + Discourse.store.has_been_uploaded?(object.url) end def include_user_id? - object['user_id'].present? - end - - def domain - object['domain'] + object.user_id.present? end def root_domain diff --git a/app/services/badge_granter.rb b/app/services/badge_granter.rb index 1ac69bce482..8446b5bcda7 100644 --- a/app/services/badge_granter.rb +++ b/app/services/badge_granter.rb @@ -204,17 +204,18 @@ class BadgeGranter end query_plan = nil - # HACK: active record is weird, force it to go down the sanitization path that cares not for % stuff - query_plan = ActiveRecord::Base.exec_sql("EXPLAIN #{sql} /*:backfill*/", params) if opts[:explain] + # HACK: active record sanitization too flexible, force it to go down the sanitization path that cares not for % stuff + # note mini_sql uses AR sanitizer at the moment (review if changed) + query_plan = DB.query_hash("EXPLAIN #{sql} /*:backfill*/", params) if opts[:explain] - sample = SqlBuilder.map_exec(OpenStruct, grants_sql, params).map(&:to_h) + sample = DB.query(grants_sql, params) sample.each do |result| - raise "Query returned a non-existent user ID:\n#{result[:id]}" unless User.find(result[:id]).present? - raise "Query did not return a badge grant time\n(Try using 'current_timestamp granted_at')" unless result[:granted_at] + raise "Query returned a non-existent user ID:\n#{result.id}" unless User.exists?(id: result.id) + raise "Query did not return a badge grant time\n(Try using 'current_timestamp granted_at')" unless result.granted_at if opts[:target_posts] - raise "Query did not return a post ID" unless result[:post_id] - raise "Query returned a non-existent post ID:\n#{result[:post_id]}" unless Post.find(result[:post_id]).present? + raise "Query did not return a post ID" unless result.post_id + raise "Query returned a non-existent post ID:\n#{result.post_id}" unless Post.exists?(result.post_id).present? end end @@ -258,28 +259,31 @@ class BadgeGranter WHERE ub.badge_id = :id AND q.user_id IS NULL )" - Badge.exec_sql(sql, id: badge.id, - post_ids: [-1], - user_ids: [-2], - backfill: true, - multiple_grant: true # cheat here, cause we only run on backfill and are deleting - ) if badge.auto_revoke && full_backfill + DB.exec( + sql, + id: badge.id, + post_ids: [-1], + user_ids: [-2], + backfill: true, + multiple_grant: true # cheat here, cause we only run on backfill and are deleting + ) if badge.auto_revoke && full_backfill - sql = " WITH w as ( - INSERT INTO user_badges(badge_id, user_id, granted_at, granted_by_id, post_id) - SELECT :id, q.user_id, q.granted_at, -1, #{post_id_field} - FROM ( #{badge.query} ) q - LEFT JOIN user_badges ub ON - ub.badge_id = :id AND ub.user_id = q.user_id - #{post_clause} - /*where*/ - RETURNING id, user_id, granted_at - ) - select w.*, username, locale, (u.admin OR u.moderator) AS staff FROM w - JOIN users u on u.id = w.user_id - " + sql = <<~SQL + WITH w as ( + INSERT INTO user_badges(badge_id, user_id, granted_at, granted_by_id, post_id) + SELECT :id, q.user_id, q.granted_at, -1, #{post_id_field} + FROM ( #{badge.query} ) q + LEFT JOIN user_badges ub ON + ub.badge_id = :id AND ub.user_id = q.user_id + #{post_clause} + /*where*/ + RETURNING id, user_id, granted_at + ) + select w.*, username, locale, (u.admin OR u.moderator) AS staff FROM w + JOIN users u on u.id = w.user_id + SQL - builder = SqlBuilder.new(sql) + builder = DB.build(sql) builder.where("ub.badge_id IS NULL AND q.user_id <> -1") if (post_ids || user_ids) && !badge.query.include?(":backfill") @@ -297,11 +301,12 @@ class BadgeGranter return end - builder.map_exec(OpenStruct, id: badge.id, - multiple_grant: badge.multiple_grant, - backfill: full_backfill, - post_ids: post_ids || [-2], - user_ids: user_ids || [-2]).each do |row| + builder.query( + id: badge.id, + multiple_grant: badge.multiple_grant, + backfill: full_backfill, + post_ids: post_ids || [-2], + user_ids: user_ids || [-2]).each do |row| # old bronze badges do not matter next if badge.badge_type_id == (BadgeType::Bronze) && row.granted_at < (2.days.ago) @@ -332,10 +337,11 @@ class BadgeGranter }.to_json) end - Badge.exec_sql("UPDATE user_badges SET notification_id = :notification_id WHERE id = :id", - notification_id: notification.id, - id: row.id - ) + DB.exec( + "UPDATE user_badges SET notification_id = :notification_id WHERE id = :id", + notification_id: notification.id, + id: row.id + ) end badge.reset_grant_count! @@ -345,21 +351,22 @@ class BadgeGranter end def self.revoke_ungranted_titles! - Badge.exec_sql("UPDATE users SET title = '' - WHERE NOT title IS NULL AND - title <> '' AND - EXISTS ( - SELECT 1 - FROM user_profiles - WHERE user_id = users.id AND badge_granted_title - ) AND - title NOT IN ( - SELECT name - FROM badges - WHERE allow_title AND enabled AND - badges.id IN (SELECT badge_id FROM user_badges ub where ub.user_id = users.id) - ) - ") + DB.exec <<~SQL + UPDATE users SET title = '' + WHERE NOT title IS NULL AND + title <> '' AND + EXISTS ( + SELECT 1 + FROM user_profiles + WHERE user_id = users.id AND badge_granted_title + ) AND + title NOT IN ( + SELECT name + FROM badges + WHERE allow_title AND enabled AND + badges.id IN (SELECT badge_id FROM user_badges ub where ub.user_id = users.id) + ) + SQL end end diff --git a/app/services/post_alerter.rb b/app/services/post_alerter.rb index 476856253b8..9b4e774b053 100644 --- a/app/services/post_alerter.rb +++ b/app/services/post_alerter.rb @@ -194,16 +194,18 @@ class PostAlerter } def group_stats(topic) + sql = <<~SQL + SELECT COUNT(*) FROM topics t + JOIN topic_allowed_groups g ON g.group_id = :group_id AND g.topic_id = t.id + LEFT JOIN group_archived_messages a ON a.topic_id = t.id AND a.group_id = g.group_id + WHERE a.id IS NULL AND t.deleted_at is NULL AND t.archetype = 'private_message' + SQL + topic.allowed_groups.map do |g| { group_id: g.id, group_name: g.name.downcase, - inbox_count: Topic.exec_sql( - "SELECT COUNT(*) FROM topics t - JOIN topic_allowed_groups g ON g.group_id = :group_id AND g.topic_id = t.id - LEFT JOIN group_archived_messages a ON a.topic_id = t.id AND a.group_id = g.group_id - WHERE a.id IS NULL AND t.deleted_at is NULL AND t.archetype = 'private_message'", - group_id: g.id).values[0][0].to_i + inbox_count: DB.query_single(sql, group_id: g.id).first.to_i } end end diff --git a/app/services/search_indexer.rb b/app/services/search_indexer.rb index 35ccd7a378b..6165be07058 100644 --- a/app/services/search_indexer.rb +++ b/app/services/search_indexer.rb @@ -61,7 +61,7 @@ class SearchIndexer # Would be nice to use AR here but not sure how to execut Postgres functions # when inserting data like this. - rows = Post.exec_sql_row_count(<<~SQL, params) + rows = DB.exec(<<~SQL, params) UPDATE #{table_name} SET raw_data = :raw_data, @@ -72,7 +72,7 @@ class SearchIndexer SQL if rows == 0 - Post.exec_sql(<<~SQL, params) + DB.exec(<<~SQL, params) INSERT INTO #{table_name} (#{foreign_key}, search_data, locale, raw_data, version) VALUES (:id, #{ranked_index}, :locale, :raw_data, :version) @@ -111,7 +111,7 @@ class SearchIndexer def self.queue_post_reindex(topic_id) return if @disabled - ActiveRecord::Base.exec_sql(<<~SQL, topic_id: topic_id) + DB.exec(<<~SQL, topic_id: topic_id) UPDATE post_search_data SET version = 0 WHERE post_id IN (SELECT id FROM posts WHERE topic_id = :topic_id) diff --git a/app/services/user_merger.rb b/app/services/user_merger.rb index e1d590926bb..4fc678b13f8 100644 --- a/app/services/user_merger.rb +++ b/app/services/user_merger.rb @@ -89,11 +89,13 @@ class UserMerger limit_reached = EXCLUDED.limit_reached SQL - GivenDailyLike.exec_sql(sql, - source_user_id: @source_user.id, - target_user_id: @target_user.id, - max_likes_per_day: SiteSetting.max_likes_per_day, - action_type_id: PostActionType.types[:like]) + DB.exec( + sql, + source_user_id: @source_user.id, + target_user_id: @target_user.id, + max_likes_per_day: SiteSetting.max_likes_per_day, + action_type_id: PostActionType.types[:like] + ) end def merge_post_timings @@ -107,7 +109,7 @@ class UserMerger AND t.topic_id = s.topic_id AND t.post_number = s.post_number SQL - PostTiming.exec_sql(sql, source_user_id: @source_user.id, target_user_id: @target_user.id) + DB.exec(sql, source_user_id: @source_user.id, target_user_id: @target_user.id) end def merge_user_visits @@ -123,7 +125,7 @@ class UserMerger AND t.visited_at = s.visited_at SQL - UserVisit.exec_sql(sql, source_user_id: @source_user.id, target_user_id: @target_user.id) + DB.exec(sql, source_user_id: @source_user.id, target_user_id: @target_user.id) end def update_site_settings @@ -136,7 +138,7 @@ class UserMerger def update_user_stats # topics_entered - UserStat.exec_sql(<<~SQL, target_user_id: @target_user.id) + DB.exec(<<~SQL, target_user_id: @target_user.id) UPDATE user_stats SET topics_entered = ( SELECT COUNT(topic_id) @@ -147,7 +149,7 @@ class UserMerger SQL # time_read and days_visited - UserStat.exec_sql(<<~SQL, target_user_id: @target_user.id) + DB.exec(<<~SQL, target_user_id: @target_user.id) UPDATE user_stats SET time_read = COALESCE(x.time_read, 0), days_visited = COALESCE(x.days_visited, 0) @@ -162,7 +164,7 @@ class UserMerger SQL # posts_read_count - UserStat.exec_sql(<<~SQL, target_user_id: @target_user.id) + DB.exec(<<~SQL, target_user_id: @target_user.id) UPDATE user_stats SET posts_read_count = ( SELECT COUNT(1) @@ -176,7 +178,7 @@ class UserMerger SQL # likes_given, likes_received, new_since, read_faq, first_post_created_at - UserStat.exec_sql(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id) + DB.exec(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id) UPDATE user_stats AS t SET likes_given = t.likes_given + s.likes_given, likes_received = t.likes_received + s.likes_received, @@ -189,7 +191,7 @@ class UserMerger end def merge_user_attributes - User.exec_sql(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id) + DB.exec(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id) UPDATE users AS t SET created_at = LEAST(t.created_at, s.created_at), updated_at = LEAST(t.updated_at, s.updated_at), @@ -213,7 +215,7 @@ class UserMerger WHERE t.id = :target_user_id AND s.id = :source_user_id SQL - UserProfile.exec_sql(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id) + DB.exec(<<~SQL, source_user_id: @source_user.id, target_user_id: @target_user.id) UPDATE user_profiles AS t SET location = COALESCE(t.location, s.location), website = COALESCE(t.website, s.website), diff --git a/app/services/user_updater.rb b/app/services/user_updater.rb index ade3e97db9f..eb9011cc21f 100644 --- a/app/services/user_updater.rb +++ b/app/services/user_updater.rb @@ -143,17 +143,18 @@ class UserUpdater MutedUser.where('user_id = ? AND muted_user_id not in (?)', user.id, desired_ids).destroy_all # SQL is easier here than figuring out how to do the same in AR - MutedUser.exec_sql("INSERT into muted_users(user_id, muted_user_id, created_at, updated_at) - SELECT :user_id, id, :now, :now - FROM users - WHERE - id in (:desired_ids) AND - id NOT IN ( - SELECT muted_user_id - FROM muted_users - WHERE user_id = :user_id - )", - now: Time.now, user_id: user.id, desired_ids: desired_ids) + DB.exec(<<~SQL, now: Time.now, user_id: user.id, desired_ids: desired_ids) + INSERT into muted_users(user_id, muted_user_id, created_at, updated_at) + SELECT :user_id, id, :now, :now + FROM users + WHERE + id in (:desired_ids) AND + id NOT IN ( + SELECT muted_user_id + FROM muted_users + WHERE user_id = :user_id + ) + SQL end end diff --git a/config/initializers/000-mini_sql.rb b/config/initializers/000-mini_sql.rb new file mode 100644 index 00000000000..35f8713b8bf --- /dev/null +++ b/config/initializers/000-mini_sql.rb @@ -0,0 +1,2 @@ +require 'mini_sql_multisite_connection' +::DB = MiniSqlMultisiteConnection.instance diff --git a/config/routes.rb b/config/routes.rb index 030fd7aad2d..0923afeab04 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -288,7 +288,6 @@ Discourse::Application.routes.draw do get "memory_stats" => "diagnostics#memory_stats", constraints: AdminConstraint.new get "dump_heap" => "diagnostics#dump_heap", constraints: AdminConstraint.new - get "dump_statement_cache" => "diagnostics#dump_statement_cache", constraints: AdminConstraint.new end # admin namespace get "email_preferences" => "email#preferences_redirect", :as => "email_preferences_redirect" diff --git a/db/fixtures/000_delayed_drops.rb b/db/fixtures/000_delayed_drops.rb index bf07dcb7251..a6a5eebde36 100644 --- a/db/fixtures/000_delayed_drops.rb +++ b/db/fixtures/000_delayed_drops.rb @@ -48,7 +48,7 @@ Migration::ColumnDropper.drop( }, on_drop: ->() { STDERR.puts "Removing superflous user stats columns!" - ActiveRecord::Base.exec_sql "DROP FUNCTION IF EXISTS first_unread_topic_for(int)" + DB.exec "DROP FUNCTION IF EXISTS first_unread_topic_for(int)" } ) diff --git a/db/fixtures/001_categories.rb b/db/fixtures/001_categories.rb index aef949349cd..982eee7dad8 100644 --- a/db/fixtures/001_categories.rb +++ b/db/fixtures/001_categories.rb @@ -10,18 +10,18 @@ uncat_id = -1 unless Numeric === uncat_id if uncat_id == -1 || !Category.exists?(uncat_id) puts "Seeding uncategorized category!" - result = Category.exec_sql "SELECT 1 FROM categories WHERE lower(name) = 'uncategorized'" + count = DB.exec "SELECT 1 FROM categories WHERE lower(name) = 'uncategorized'" name = 'Uncategorized' - name << SecureRandom.hex if result.count > 0 + name << SecureRandom.hex if count > 0 - result = Category.exec_sql "INSERT INTO categories + result = DB.query_single "INSERT INTO categories (name,color,slug,description,text_color, user_id, created_at, updated_at, position, name_lower) VALUES ('#{name}', 'AB9364', 'uncategorized', '', 'FFFFFF', -1, now(), now(), 1, '#{name.downcase}' ) RETURNING id " - category_id = result[0]["id"].to_i + category_id = result.first.to_i - Category.exec_sql "DELETE FROM site_settings where name = 'uncategorized_category_id'" - Category.exec_sql "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) + DB.exec "DELETE FROM site_settings where name = 'uncategorized_category_id'" + DB.exec "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) VALUES ('uncategorized_category_id', 3, #{category_id}, now(), now())" end diff --git a/db/fixtures/006_badges.rb b/db/fixtures/006_badges.rb index 18022ab4513..eae24fec16f 100644 --- a/db/fixtures/006_badges.rb +++ b/db/fixtures/006_badges.rb @@ -31,7 +31,7 @@ BadgeGrouping.seed do |g| end # BUGFIX -Badge.exec_sql <<-SQL.squish +DB.exec <<-SQL.squish UPDATE badges SET badge_grouping_id = -1 WHERE NOT EXISTS ( diff --git a/db/fixtures/500_lounge_category.rb b/db/fixtures/500_lounge_category.rb index 5fa93ddb1e6..2aa42f21cc8 100644 --- a/db/fixtures/500_lounge_category.rb +++ b/db/fixtures/500_lounge_category.rb @@ -36,7 +36,7 @@ unless Rails.env.test? end # Reset topic count because we don't count the description topic - Category.exec_sql "UPDATE categories SET topic_count = 0 WHERE id = #{lounge.id}" + DB.exec "UPDATE categories SET topic_count = 0 WHERE id = #{lounge.id}" end end end diff --git a/db/fixtures/501_meta_category.rb b/db/fixtures/501_meta_category.rb index 38ed96aaea0..38f89856bb9 100644 --- a/db/fixtures/501_meta_category.rb +++ b/db/fixtures/501_meta_category.rb @@ -25,7 +25,7 @@ unless Rails.env.test? end # Reset topic count because we don't count the description topic - Category.exec_sql "UPDATE categories SET topic_count = 0 WHERE id = #{meta.id}" + DB.exec "UPDATE categories SET topic_count = 0 WHERE id = #{meta.id}" end end end diff --git a/db/fixtures/502_staff_category.rb b/db/fixtures/502_staff_category.rb index 54faa6385bf..18b5c54337d 100644 --- a/db/fixtures/502_staff_category.rb +++ b/db/fixtures/502_staff_category.rb @@ -33,7 +33,7 @@ unless Rails.env.test? end # Reset topic count because we don't count the description topic - Category.exec_sql "UPDATE categories SET topic_count = 0 WHERE id = #{staff.id}" + DB.exec "UPDATE categories SET topic_count = 0 WHERE id = #{staff.id}" end end end diff --git a/db/migrate/20120816205538_add_starred_at_to_forum_thread_user.rb b/db/migrate/20120816205538_add_starred_at_to_forum_thread_user.rb index 2fc02d4287b..c2643594d43 100644 --- a/db/migrate/20120816205538_add_starred_at_to_forum_thread_user.rb +++ b/db/migrate/20120816205538_add_starred_at_to_forum_thread_user.rb @@ -1,7 +1,7 @@ class AddStarredAtToForumThreadUser < ActiveRecord::Migration[4.2] def up add_column :forum_thread_users, :starred_at, :datetime - User.exec_sql 'update forum_thread_users f set starred_at = COALESCE(created_at, ?) + DB.exec 'update forum_thread_users f set starred_at = COALESCE(created_at, ?) from ( select f1.forum_thread_id, f1.user_id, t.created_at from forum_thread_users f1 diff --git a/db/migrate/20121123054127_make_post_number_distinct.rb b/db/migrate/20121123054127_make_post_number_distinct.rb index 695ac7392c0..259cdbb9156 100644 --- a/db/migrate/20121123054127_make_post_number_distinct.rb +++ b/db/migrate/20121123054127_make_post_number_distinct.rb @@ -1,7 +1,7 @@ class MakePostNumberDistinct < ActiveRecord::Migration[4.2] def up - Topic.exec_sql('update posts p + DB.exec('update posts p set post_number = calc from ( diff --git a/db/migrate/20140120155706_add_lounge_category.rb b/db/migrate/20140120155706_add_lounge_category.rb index 0dac6e7b291..9e2517d8ddb 100644 --- a/db/migrate/20140120155706_add_lounge_category.rb +++ b/db/migrate/20140120155706_add_lounge_category.rb @@ -3,25 +3,25 @@ class AddLoungeCategory < ActiveRecord::Migration[4.2] return if Rails.env.test? I18n.overrides_disabled do - result = Category.exec_sql "SELECT 1 FROM site_settings where name = 'lounge_category_id'" - if result.count == 0 + result = DB.exec "SELECT 1 FROM site_settings where name = 'lounge_category_id'" + if result == 0 description = I18n.t('vip_category_description') default_name = I18n.t('vip_category_name') - name = if Category.exec_sql("SELECT 1 FROM categories where name = '#{default_name}'").count == 0 + name = if DB.exec("SELECT 1 FROM categories where name = '#{default_name}'") == 0 default_name else "CHANGE_ME" end - result = Category.exec_sql "INSERT INTO categories + result = DB.query_single "INSERT INTO categories (name, color, text_color, created_at, updated_at, user_id, slug, description, read_restricted, position) VALUES (:name, 'EEEEEE', '652D90', now(), now(), -1, '', :description, true, 3) RETURNING id", name: name, description: description - category_id = result[0]["id"].to_i + category_id = result.first.to_i - Category.exec_sql "UPDATE categories SET slug = :slug + DB.exec "UPDATE categories SET slug = :slug WHERE id = :category_id", slug: Slug.for(name, "#{category_id}-category"), category_id: category_id diff --git a/db/migrate/20140122043508_add_meta_category.rb b/db/migrate/20140122043508_add_meta_category.rb index 2b42b81643a..c4ee6b6b6bb 100644 --- a/db/migrate/20140122043508_add_meta_category.rb +++ b/db/migrate/20140122043508_add_meta_category.rb @@ -3,20 +3,20 @@ class AddMetaCategory < ActiveRecord::Migration[4.2] return if Rails.env.test? I18n.overrides_disabled do - result = Category.exec_sql "SELECT 1 FROM site_settings where name = 'meta_category_id'" - if result.count == 0 + result = DB.exec "SELECT 1 FROM site_settings where name = 'meta_category_id'" + if result == 0 description = I18n.t('meta_category_description') name = I18n.t('meta_category_name') - if Category.exec_sql("SELECT 1 FROM categories where name ilike :name", name: name).count == 0 - result = Category.exec_sql "INSERT INTO categories + if DB.exec("SELECT 1 FROM categories where name ilike :name", name: name) == 0 + result = DB.query_single "INSERT INTO categories (name, color, text_color, created_at, updated_at, user_id, slug, description, read_restricted, position) VALUES (:name, '808281', 'FFFFFF', now(), now(), -1, :slug, :description, true, 1) RETURNING id", name: name, slug: '', description: description - category_id = result[0]["id"].to_i + category_id = result.first.to_i - Category.exec_sql "UPDATE categories SET slug=:slug WHERE id=:category_id", + DB.exec "UPDATE categories SET slug=:slug WHERE id=:category_id", slug: Slug.for(name, "#{category_id}-category"), category_id: category_id execute "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) diff --git a/db/migrate/20140227201005_add_staff_category.rb b/db/migrate/20140227201005_add_staff_category.rb index 67a84806016..837daaaef0e 100644 --- a/db/migrate/20140227201005_add_staff_category.rb +++ b/db/migrate/20140227201005_add_staff_category.rb @@ -3,24 +3,24 @@ class AddStaffCategory < ActiveRecord::Migration[4.2] return if Rails.env.test? I18n.overrides_disabled do - result = Category.exec_sql "SELECT 1 FROM site_settings where name = 'staff_category_id'" - if result.count == 0 + result = DB.exec "SELECT 1 FROM site_settings where name = 'staff_category_id'" + if result == 0 description = I18n.t('staff_category_description') name = I18n.t('staff_category_name') - if Category.exec_sql("SELECT 1 FROM categories where name ilike :name", name: name).count == 0 + if DB.exec("SELECT 1 FROM categories where name ilike :name", name: name) == 0 - result = Category.exec_sql "INSERT INTO categories + result = DB.query_single "INSERT INTO categories (name, color, text_color, created_at, updated_at, user_id, slug, description, read_restricted, position) VALUES (:name, '283890', 'FFFFFF', now(), now(), -1, '', :description, true, 2) RETURNING id", name: name, description: description - category_id = result[0]["id"].to_i + category_id = result.first.to_i - Category.exec_sql "UPDATE categories SET slug=:slug WHERE id=:category_id", + DB.exec "UPDATE categories SET slug=:slug WHERE id=:category_id", slug: Slug.for(name, "#{category_id}-category"), category_id: category_id - execute "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) + DB.exec "INSERT INTO site_settings(name, data_type, value, created_at, updated_at) VALUES ('staff_category_id', 3, #{category_id.to_i}, now(), now())" end end diff --git a/db/migrate/20140515220111_init_fixed_category_positions_value.rb b/db/migrate/20140515220111_init_fixed_category_positions_value.rb index f9f454848f7..8b9a545b7ad 100644 --- a/db/migrate/20140515220111_init_fixed_category_positions_value.rb +++ b/db/migrate/20140515220111_init_fixed_category_positions_value.rb @@ -1,10 +1,10 @@ class InitFixedCategoryPositionsValue < ActiveRecord::Migration[4.2] def up # Look at existing categories to determine if positions have been specified - result = Category.exec_sql("SELECT count(*) FROM categories WHERE position IS NOT NULL") + result = DB.query_single("SELECT count(*) FROM categories WHERE position IS NOT NULL") # Greater than 4 because uncategorized, meta, staff, lounge all have positions by default - if result[0]['count'].to_i > 4 + if result.first.to_i > 4 execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('fixed_category_positions', 5, 't', now(), now())" end end diff --git a/db/migrate/20140521220115_google_openid_default_has_changed.rb b/db/migrate/20140521220115_google_openid_default_has_changed.rb index d76718c691f..bea872001e6 100644 --- a/db/migrate/20140521220115_google_openid_default_has_changed.rb +++ b/db/migrate/20140521220115_google_openid_default_has_changed.rb @@ -1,17 +1,17 @@ class GoogleOpenidDefaultHasChanged < ActiveRecord::Migration[4.2] def up - users_count_query = User.exec_sql("SELECT count(*) FROM users") - if users_count_query[0]['count'].to_i > 1 + users_count_query = DB.query_single("SELECT count(*) FROM users") + if users_count_query.first.to_i > 1 # This is an existing site. - result = User.exec_sql("SELECT count(*) FROM site_settings WHERE name = 'enable_google_logins'") - if result[0]['count'].to_i == 0 + result = DB.query_single("SELECT count(*) FROM site_settings WHERE name = 'enable_google_logins'") + if result.first.to_i == 0 # The old default was true, so add a row to keep it that way. execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('enable_google_logins', 5, 't', now(), now())" end # Don't enable the new Google setting on an existing site. - result = User.exec_sql("SELECT count(*) FROM site_settings WHERE name = 'enable_google_oauth2_logins'") - if result[0]['count'].to_i == 0 + result = DB.query_single("SELECT count(*) FROM site_settings WHERE name = 'enable_google_oauth2_logins'") + if result.first.to_i == 0 execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('enable_google_oauth2_logins', 5, 'f', now(), now())" end end diff --git a/db/migrate/20140604145431_disable_external_auths_by_default.rb b/db/migrate/20140604145431_disable_external_auths_by_default.rb index 8f47ba505ec..2cd19018df3 100644 --- a/db/migrate/20140604145431_disable_external_auths_by_default.rb +++ b/db/migrate/20140604145431_disable_external_auths_by_default.rb @@ -1,15 +1,15 @@ class DisableExternalAuthsByDefault < ActiveRecord::Migration[4.2] def enable_setting_if_default(name) - result = User.exec_sql("SELECT count(*) count FROM site_settings WHERE name = '#{name}'") - if result[0]['count'].to_i == 0 + result = DB.query_single("SELECT count(*) count FROM site_settings WHERE name = '#{name}'") + if result.first.to_i == 0 execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('#{name}', 5, 't', now(), now())" end end def up - users_count_query = User.exec_sql("SELECT count(*) FROM users") - if users_count_query[0]['count'].to_i > 1 + users_count_query = DB.query_single("SELECT count(*) FROM users") + if users_count_query.first.to_i > 1 # existing site, so keep settings as they are enable_setting_if_default 'enable_yahoo_logins' enable_setting_if_default 'enable_google_oauth2_logins' diff --git a/db/migrate/20140711193923_remove_email_in_address_setting.rb b/db/migrate/20140711193923_remove_email_in_address_setting.rb index a82dbe4dd08..a14d44c7635 100644 --- a/db/migrate/20140711193923_remove_email_in_address_setting.rb +++ b/db/migrate/20140711193923_remove_email_in_address_setting.rb @@ -1,16 +1,16 @@ class RemoveEmailInAddressSetting < ActiveRecord::Migration[4.2] def up - uncat_id = ActiveRecord::Base.exec_sql("SELECT value FROM site_settings WHERE name = 'uncategorized_category_id'").first - cat_id_r = ActiveRecord::Base.exec_sql("SELECT value FROM site_settings WHERE name = 'email_in_category'").first - email_r = ActiveRecord::Base.exec_sql("SELECT value FROM site_settings WHERE name = 'email_in_address'").first + uncat_id = DB.query_single("SELECT value FROM site_settings WHERE name = 'uncategorized_category_id'").first + cat_id_r = DB.query_single("SELECT value FROM site_settings WHERE name = 'email_in_category'").first + email_r = DB.query_single("SELECT value FROM site_settings WHERE name = 'email_in_address'").first if email_r category_id = uncat_id["value"].to_i category_id = cat_id_r["value"].to_i if cat_id_r email = email_r["value"] - ActiveRecord::Base.exec_sql("UPDATE categories SET email_in = ? WHERE id = ?", email, category_id) + DB.exec("UPDATE categories SET email_in = ? WHERE id = ?", email, category_id) end - ActiveRecord::Base.exec_sql("DELETE FROM site_settings WHERE name = 'email_in_category' OR name = 'email_in_address'") + DB.exec("DELETE FROM site_settings WHERE name = 'email_in_category' OR name = 'email_in_address'") end def down diff --git a/db/migrate/20141216112341_resolve_duplicate_group_names.rb b/db/migrate/20141216112341_resolve_duplicate_group_names.rb index c13b1f7b808..96cb718f8d8 100644 --- a/db/migrate/20141216112341_resolve_duplicate_group_names.rb +++ b/db/migrate/20141216112341_resolve_duplicate_group_names.rb @@ -1,14 +1,14 @@ class ResolveDuplicateGroupNames < ActiveRecord::Migration[4.2] def up - results = Group.exec_sql 'SELECT id FROM groups + results = DB.query_single 'SELECT id FROM groups WHERE name ILIKE (SELECT lower(name) FROM groups GROUP BY lower(name) HAVING count(*) > 1);' - groups = Group.where id: results.map { |r| r['id'] } + groups = Group.where id: results groups.group_by { |g| g.name.downcase }.each do |key, value| value.each_with_index do |dup, index| dup.update! name: "#{dup.name[0..18]}_#{index + 1}" if index > 0 diff --git a/db/migrate/20161025083648_fix_category_logo_and_background_urls.rb b/db/migrate/20161025083648_fix_category_logo_and_background_urls.rb index a3fe7b251b2..ea96cf0ee13 100644 --- a/db/migrate/20161025083648_fix_category_logo_and_background_urls.rb +++ b/db/migrate/20161025083648_fix_category_logo_and_background_urls.rb @@ -2,7 +2,7 @@ class FixCategoryLogoAndBackgroundUrls < ActiveRecord::Migration[4.2] def up return true if Discourse.asset_host.blank? - Category.exec_sql <<-SQL + DB.exec <<-SQL UPDATE categories SET logo_url = replace(logo_url, '#{Discourse.asset_host}', '') , background_url = replace(background_url, '#{Discourse.asset_host}', '') diff --git a/db/migrate/20170215151505_add_seen_at_to_user_auth_token.rb b/db/migrate/20170215151505_add_seen_at_to_user_auth_token.rb index 50441273217..4ed90b62230 100644 --- a/db/migrate/20170215151505_add_seen_at_to_user_auth_token.rb +++ b/db/migrate/20170215151505_add_seen_at_to_user_auth_token.rb @@ -1,7 +1,7 @@ class AddSeenAtToUserAuthToken < ActiveRecord::Migration[4.2] def up add_column :user_auth_tokens, :seen_at, :datetime - ActiveRecord::Base.exec_sql "UPDATE user_auth_tokens SET seen_at = :now WHERE auth_token_seen", now: Time.zone.now + DB.exec "UPDATE user_auth_tokens SET seen_at = :now WHERE auth_token_seen", now: Time.zone.now end def down diff --git a/db/migrate/20170728012754_split_public_in_groups.rb b/db/migrate/20170728012754_split_public_in_groups.rb index febb2f9779f..688baaceff1 100644 --- a/db/migrate/20170728012754_split_public_in_groups.rb +++ b/db/migrate/20170728012754_split_public_in_groups.rb @@ -3,7 +3,7 @@ class SplitPublicInGroups < ActiveRecord::Migration[4.2] add_column :groups, :public_exit, :boolean, default: false, null: false add_column :groups, :public_admission, :boolean, default: false, null: false - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL UPDATE groups SET public_exit = true, public_admission = true WHERE public = true diff --git a/db/migrate/20180308071922_drop_raise_read_only_function.rb b/db/migrate/20180308071922_drop_raise_read_only_function.rb index a90a2561c9f..d756aa7e14c 100644 --- a/db/migrate/20180308071922_drop_raise_read_only_function.rb +++ b/db/migrate/20180308071922_drop_raise_read_only_function.rb @@ -1,6 +1,6 @@ class DropRaiseReadOnlyFunction < ActiveRecord::Migration[5.1] def up - ActiveRecord::Base.exec_sql( + DB.exec( "DROP FUNCTION IF EXISTS raise_read_only() CASCADE;" ) end diff --git a/lib/backup_restore/backup_restore.rb b/lib/backup_restore/backup_restore.rb index a0a217556e3..0591b31fe11 100644 --- a/lib/backup_restore/backup_restore.rb +++ b/lib/backup_restore/backup_restore.rb @@ -76,7 +76,7 @@ module BackupRestore end def self.move_tables_between_schemas(source, destination) - User.exec_sql(move_tables_between_schemas_sql(source, destination)) + DB.exec(move_tables_between_schemas_sql(source, destination)) end def self.move_tables_between_schemas_sql(source, destination) @@ -196,7 +196,7 @@ module BackupRestore end def self.backup_tables_count - User.exec_sql("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = 'backup'")[0]['count'].to_i + DB.query_single("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = 'backup'").first.to_i end end diff --git a/lib/backup_restore/restorer.rb b/lib/backup_restore/restorer.rb index d2d46239be4..fefe6c45774 100644 --- a/lib/backup_restore/restorer.rb +++ b/lib/backup_restore/restorer.rb @@ -379,14 +379,14 @@ module BackupRestore @db_was_changed = true - User.exec_sql(sql) + DB.exec(sql) end def migrate_database log "Migrating the database..." Discourse::Application.load_tasks ENV["VERSION"] = @current_version.to_s - User.exec_sql("SET search_path = public, pg_catalog;") + DB.exec("SET search_path = public, pg_catalog;") Rake::Task["db:migrate"].invoke end diff --git a/lib/comment_migration.rb b/lib/comment_migration.rb index 79c1576dd7a..bd00d58c37d 100644 --- a/lib/comment_migration.rb +++ b/lib/comment_migration.rb @@ -13,10 +13,10 @@ class CommentMigration < ActiveRecord::Migration[4.2] comment = column[1] if column_name == :_table - ActiveRecord::Base.exec_sql "COMMENT ON TABLE #{table_name} IS ?", comment + DB.exec "COMMENT ON TABLE #{table_name} IS ?", comment puts " COMMENT ON TABLE #{table_name}" else - ActiveRecord::Base.exec_sql "COMMENT ON COLUMN #{table_name}.#{column_name} IS ?", comment + DB.exec "COMMENT ON COLUMN #{table_name}.#{column_name} IS ?", comment puts " COMMENT ON COLUMN #{table_name}.#{column_name}" end end @@ -35,10 +35,10 @@ class CommentMigration < ActiveRecord::Migration[4.2] comment = column[1] if column_name == :_table - ActiveRecord::Base.exec_sql "COMMENT ON TABLE #{table_name} IS ?", comment + DB.exec "COMMENT ON TABLE #{table_name} IS ?", comment puts " COMMENT ON TABLE #{table_name}" else - ActiveRecord::Base.exec_sql "COMMENT ON COLUMN #{table_name}.#{column_name} IS ?", comment + DB.exec "COMMENT ON COLUMN #{table_name}.#{column_name} IS ?", comment puts " COMMENT ON COLUMN #{table_name}.#{column_name}" end end diff --git a/lib/cooked_post_processor.rb b/lib/cooked_post_processor.rb index 12e2d35e056..93866749e54 100644 --- a/lib/cooked_post_processor.rb +++ b/lib/cooked_post_processor.rb @@ -73,7 +73,7 @@ class CookedPostProcessor PostUpload.transaction do PostUpload.where(post_id: @post.id).delete_all if upload_ids.size > 0 - PostUpload.exec_sql("INSERT INTO post_uploads (post_id, upload_id) VALUES #{values}") + DB.exec("INSERT INTO post_uploads (post_id, upload_id) VALUES #{values}") end end end diff --git a/lib/migration/base_dropper.rb b/lib/migration/base_dropper.rb index f36963d99ec..d8e2f03d932 100644 --- a/lib/migration/base_dropper.rb +++ b/lib/migration/base_dropper.rb @@ -48,7 +48,7 @@ module Migration "Discourse: #{column_name} in #{table_name} is readonly" : "Discourse: #{table_name} is read only" - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL CREATE OR REPLACE FUNCTION #{readonly_function_name(table_name, column_name)} RETURNS trigger AS $rcr$ BEGIN RAISE EXCEPTION '#{message}'; diff --git a/lib/migration/column_dropper.rb b/lib/migration/column_dropper.rb index d7176505d15..a594105622b 100644 --- a/lib/migration/column_dropper.rb +++ b/lib/migration/column_dropper.rb @@ -12,7 +12,7 @@ module Migration def self.mark_readonly(table_name, column_name) create_readonly_function(table_name, column_name) - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL CREATE TRIGGER #{readonly_trigger_name(table_name, column_name)} BEFORE INSERT OR UPDATE OF #{column_name} ON #{table_name} @@ -51,13 +51,13 @@ module Migration def execute_drop! @columns.each do |column| - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL DROP TRIGGER IF EXISTS #{BaseDropper.readonly_trigger_name(@table, column)} ON #{@table}; DROP FUNCTION IF EXISTS #{BaseDropper.readonly_function_name(@table, column)} CASCADE; SQL # safe cause it is protected on method entry, can not be passed in params - ActiveRecord::Base.exec_sql("ALTER TABLE #{@table} DROP COLUMN IF EXISTS #{column}") + DB.exec("ALTER TABLE #{@table} DROP COLUMN IF EXISTS #{column}") end end end diff --git a/lib/migration/table_dropper.rb b/lib/migration/table_dropper.rb index 78a9c7172f5..c95640b78dc 100644 --- a/lib/migration/table_dropper.rb +++ b/lib/migration/table_dropper.rb @@ -18,7 +18,7 @@ module Migration def self.read_only_table(table_name) create_readonly_function(table_name) - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL CREATE TRIGGER #{readonly_trigger_name(table_name)} BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE ON #{table_name} @@ -37,7 +37,7 @@ module Migration end def droppable? - builder = SqlBuilder.new(<<~SQL) + builder = DB.build(<<~SQL) SELECT 1 FROM INFORMATION_SCHEMA.TABLES /*where*/ @@ -52,7 +52,7 @@ module Migration .exec(old_name: @old_name, new_name: @new_name, delay: "#{@delay} seconds", - after_migration: @after_migration).to_a.length > 0 + after_migration: @after_migration) > 0 end def table_exists(table_name_placeholder) @@ -67,9 +67,9 @@ module Migration end def execute_drop! - ActiveRecord::Base.exec_sql("DROP TABLE IF EXISTS #{@old_name}") + DB.exec("DROP TABLE IF EXISTS #{@old_name}") - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL DROP FUNCTION IF EXISTS #{BaseDropper.readonly_function_name(@old_name)} CASCADE; SQL end diff --git a/lib/mini_sql_multisite_connection.rb b/lib/mini_sql_multisite_connection.rb new file mode 100644 index 00000000000..d2645a6af84 --- /dev/null +++ b/lib/mini_sql_multisite_connection.rb @@ -0,0 +1,38 @@ +class MiniSqlMultisiteConnection < MiniSql::Connection + + class CustomBuilder < MiniSql::Builder + + def initialize(connection, sql) + super + end + + def secure_category(secure_category_ids, category_alias = 'c') + if secure_category_ids.present? + where("NOT COALESCE(" << category_alias << ".read_restricted, false) OR " << category_alias << ".id in (:secure_category_ids)", secure_category_ids: secure_category_ids) + else + where("NOT COALESCE(" << category_alias << ".read_restricted, false)") + end + self + end + end + + class ParamEncoder + def encode(*sql_array) + # use active record to avoid any discrepencies + ActiveRecord::Base.send(:sanitize_sql_array, sql_array) + end + end + + def self.instance + new(nil, param_encoder: ParamEncoder.new, type_map: self.type_map(ActiveRecord::Base.connection.raw_connection)) + end + # we need a tiny adapter here so we always run against the + # correct multisite connection + def raw_connection + ActiveRecord::Base.connection.raw_connection + end + + def build(sql) + CustomBuilder.new(self, sql) + end +end diff --git a/lib/post_revisor.rb b/lib/post_revisor.rb index d74e50dd95b..b406351256c 100644 --- a/lib/post_revisor.rb +++ b/lib/post_revisor.rb @@ -566,7 +566,7 @@ class PostRevisor end def update_topic_word_counts - Topic.exec_sql("UPDATE topics + DB.exec("UPDATE topics SET word_count = ( SELECT SUM(COALESCE(posts.word_count, 0)) FROM posts diff --git a/lib/search.rb b/lib/search.rb index 054f7e5fd05..f9cfdbd1a49 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -790,9 +790,11 @@ class Search def self.ts_query(term: , ts_config: nil, joiner: "&", weight_filter: nil) - data = Post.exec_sql("SELECT TO_TSVECTOR(:config, :term)", - config: 'simple', - term: term).values[0][0] + data = DB.query_single( + "SELECT TO_TSVECTOR(:config, :term)", + config: 'simple', + term: term + ).first ts_config = ActiveRecord::Base.connection.quote(ts_config) if ts_config all_terms = data.scan(/'([^']+)'\:\d+/).flatten diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index e7782f1a859..1d1c5fa0eda 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -96,7 +96,7 @@ task 'db:stats' => 'environment' do SQL puts - print_table(Post.exec_sql(sql).to_a) + print_table(DB.query_hash(sql)) end desc 'Rebuild indexes' @@ -108,16 +108,14 @@ task 'db:rebuild_indexes' => 'environment' do Discourse.enable_readonly_mode backup_schema = Jobs::Importer::BACKUP_SCHEMA - table_names = User.exec_sql("select table_name from information_schema.tables where table_schema = 'public'").map do |row| - row['table_name'] - end + table_names = DB.query_single("select table_name from information_schema.tables where table_schema = 'public'") begin # Move all tables to the backup schema: - User.exec_sql("DROP SCHEMA IF EXISTS #{backup_schema} CASCADE") - User.exec_sql("CREATE SCHEMA #{backup_schema}") + DB.exec("DROP SCHEMA IF EXISTS #{backup_schema} CASCADE") + DB.exec("CREATE SCHEMA #{backup_schema}") table_names.each do |table_name| - User.exec_sql("ALTER TABLE public.#{table_name} SET SCHEMA #{backup_schema}") + DB.exec("ALTER TABLE public.#{table_name} SET SCHEMA #{backup_schema}") end # Create a new empty db @@ -126,25 +124,25 @@ task 'db:rebuild_indexes' => 'environment' do # Fetch index definitions from the new db index_definitions = {} table_names.each do |table_name| - index_definitions[table_name] = User.exec_sql("SELECT indexdef FROM pg_indexes WHERE tablename = '#{table_name}' and schemaname = 'public';").map { |x| x['indexdef'] } + index_definitions[table_name] = DB.query_single("SELECT indexdef FROM pg_indexes WHERE tablename = '#{table_name}' and schemaname = 'public';") end # Drop the new tables table_names.each do |table_name| - User.exec_sql("DROP TABLE public.#{table_name}") + DB.exec("DROP TABLE public.#{table_name}") end # Move the old tables back to the public schema table_names.each do |table_name| - User.exec_sql("ALTER TABLE #{backup_schema}.#{table_name} SET SCHEMA public") + DB.exec("ALTER TABLE #{backup_schema}.#{table_name} SET SCHEMA public") end # Drop their indexes - index_names = User.exec_sql("SELECT indexname FROM pg_indexes WHERE schemaname = 'public' AND tablename IN ('#{table_names.join("', '")}')").map { |x| x['indexname'] } + index_names = DB.query_single("SELECT indexname FROM pg_indexes WHERE schemaname = 'public' AND tablename IN ('#{table_names.join("', '")}')") index_names.each do |index_name| begin puts index_name - User.exec_sql("DROP INDEX public.#{index_name}") + DB.exec("DROP INDEX public.#{index_name}") rescue ActiveRecord::StatementInvalid # It's this: # PG::Error: ERROR: cannot drop index category_users_pkey because constraint category_users_pkey on table category_users requires it @@ -156,7 +154,7 @@ task 'db:rebuild_indexes' => 'environment' do table_names.each do |table_name| index_definitions[table_name].each do |index_def| begin - User.exec_sql(index_def) + DB.exec(index_def) rescue ActiveRecord::StatementInvalid # Trying to recreate a primary key end diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index ad291fd96a7..7d83eb0115c 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -29,7 +29,7 @@ MS_SPEND_CREATING_POST ||= 5000 def insert_post_timings log "Inserting post timings..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO post_timings (topic_id, post_number, user_id, msecs) SELECT topic_id, post_number, user_id, #{MS_SPEND_CREATING_POST} FROM posts @@ -41,7 +41,7 @@ end def insert_post_replies log "Inserting post replies..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO post_replies (post_id, reply_id, created_at, updated_at) SELECT p2.id, p.id, p.created_at, p.created_at FROM posts p @@ -53,7 +53,7 @@ end def insert_topic_users log "Inserting topic users..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO topic_users (user_id, topic_id, posted, last_read_post_number, highest_seen_post_number, first_visited_at, last_visited_at, total_msecs_viewed) SELECT user_id, topic_id, 't' , MAX(post_number), MAX(post_number), MIN(created_at), MAX(created_at), COUNT(id) * #{MS_SPEND_CREATING_POST} FROM posts @@ -66,7 +66,7 @@ end def insert_topic_views log "Inserting topic views..." - exec_sql <<-SQL + DB.exec <<-SQL WITH X AS ( SELECT topic_id, user_id, DATE(p.created_at) posted_at FROM posts p @@ -86,7 +86,7 @@ end def insert_user_actions log "Inserting user actions for NEW_TOPIC = 4..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) SELECT 4, p.user_id, topic_id, p.id, p.user_id, p.created_at, p.created_at FROM posts p @@ -100,7 +100,7 @@ def insert_user_actions log "Inserting user actions for REPLY = 5..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) SELECT 5, p.user_id, topic_id, p.id, p.user_id, p.created_at, p.created_at FROM posts p @@ -114,7 +114,7 @@ def insert_user_actions log "Inserting user actions for RESPONSE = 6..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO user_actions (action_type, user_id, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) SELECT 6, p.user_id, p.topic_id, p.id, p2.user_id, p.created_at, p.created_at FROM posts p @@ -137,7 +137,7 @@ end def insert_user_options log "Inserting user options..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO user_options ( user_id, email_always, @@ -189,7 +189,7 @@ end def insert_user_stats log "Inserting user stats..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO user_stats (user_id, new_since) SELECT id, created_at FROM users @@ -200,7 +200,7 @@ end def insert_user_visits log "Inserting user visits..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO user_visits (user_id, visited_at, posts_read) SELECT user_id, DATE(created_at), COUNT(*) FROM posts @@ -213,7 +213,7 @@ end def insert_draft_sequences log "Inserting draft sequences..." - exec_sql <<-SQL + DB.exec <<-SQL INSERT INTO draft_sequences (user_id, draft_key, sequence) SELECT user_id, CONCAT('#{Draft::EXISTING_TOPIC}', id), 1 FROM topics @@ -226,7 +226,7 @@ end def update_user_stats log "Updating user stats..." - exec_sql <<-SQL + DB.exec <<-SQL WITH X AS ( SELECT p.user_id , COUNT(p.id) posts @@ -283,7 +283,7 @@ end def update_posts log "Updating posts..." - exec_sql <<-SQL + DB.exec <<-SQL WITH Y AS ( SELECT post_id, COUNT(*) replies FROM post_replies GROUP BY post_id ) @@ -310,7 +310,7 @@ end def update_topics log "Updating topics..." - exec_sql <<-SQL + DB.exec <<-SQL WITH X AS ( SELECT topic_id , COUNT(*) posts @@ -350,7 +350,7 @@ end def update_categories log "Updating categories..." - exec_sql <<-SQL + DB.exec <<-SQL WITH X AS ( SELECT category_id , MAX(p.id) post_id @@ -382,7 +382,7 @@ end def update_users log "Updating users..." - exec_sql <<-SQL + DB.exec <<-SQL WITH X AS ( SELECT user_id , MIN(created_at) min_created_at @@ -406,7 +406,7 @@ end def update_groups log "Updating groups..." - exec_sql <<-SQL + DB.exec <<-SQL WITH X AS ( SELECT group_id, COUNT(*) count FROM group_users @@ -428,12 +428,6 @@ def log(message) puts "[#{DateTime.now.strftime("%Y-%m-%d %H:%M:%S")}] #{message}" end -def exec_sql(sql) - ActiveRecord::Base.transaction do - ActiveRecord::Base.exec_sql(sql) - end -end - task "import:create_phpbb_permalinks" => :environment do log 'Creating Permalinks...' @@ -477,7 +471,6 @@ task "import:remap_old_phpbb_permalinks" => :environment do # skip end end - i log "Done! #{i} posts remapped." end diff --git a/lib/tasks/posts.rake b/lib/tasks/posts.rake index 3c17bc1e3bd..9909bb3c01e 100644 --- a/lib/tasks/posts.rake +++ b/lib/tasks/posts.rake @@ -304,7 +304,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args| builder.where("topic_id = :topic_id") if args[:topic_id] builder.exec(topic_id: args[:topic_id]) - Notification.exec_sql(<<~SQL) + DB.exec(<<~SQL) UPDATE notifications AS x SET post_number = p.sort_order FROM posts AS p @@ -313,7 +313,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args| p.post_number < 0 SQL - PostTiming.exec_sql(<<~SQL) + DB.exec(<<~SQL) UPDATE post_timings AS x SET post_number = x.post_number * -1 FROM posts AS p @@ -329,7 +329,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args| p.post_number < 0; SQL - Post.exec_sql(<<~SQL) + DB.exec(<<~SQL) UPDATE posts AS x SET reply_to_post_number = p.sort_order FROM posts AS p @@ -338,7 +338,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args| p.post_number < 0; SQL - TopicUser.exec_sql(<<~SQL) + DB.exec(<<~SQL) UPDATE topic_users AS x SET last_read_post_number = p.sort_order FROM posts AS p @@ -362,7 +362,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args| SQL # finally update the post_number - Post.exec_sql(<<~SQL) + DB.exec(<<~SQL) UPDATE posts SET post_number = sort_order WHERE post_number < 0 diff --git a/lib/tasks/uploads.rake b/lib/tasks/uploads.rake index d69f3078a73..6e5b7fcb3e2 100644 --- a/lib/tasks/uploads.rake +++ b/lib/tasks/uploads.rake @@ -676,7 +676,7 @@ task "uploads:analyze", [:cache_path, :limit] => :environment do |_, args| printf "%-25s | %-25s | %-25s | %-25s\n", 'username', 'total size of uploads', 'number of uploads', 'number of optimized images' puts "-" * 110 - User.exec_sql(sql).values.each do |username, num_of_uploads, total_size_of_uploads, num_of_optimized_images| + DB.query_single(sql).each do |username, num_of_uploads, total_size_of_uploads, num_of_optimized_images| printf "%-25s | %-25s | %-25s | %-25s\n", username, helper.number_to_human_size(total_size_of_uploads), num_of_uploads, num_of_optimized_images end diff --git a/lib/topic_view.rb b/lib/topic_view.rb index d2bf87de10b..45e7f2bc3df 100644 --- a/lib/topic_view.rb +++ b/lib/topic_view.rb @@ -285,14 +285,14 @@ class TopicView sql = <<~SQL SELECT user_id, count(*) AS count_all FROM posts - WHERE id IN (:post_ids) + WHERE id in (:post_ids) AND user_id IS NOT NULL GROUP BY user_id ORDER BY count_all DESC LIMIT #{MAX_PARTICIPANTS} SQL - Hash[Post.exec_sql(sql, post_ids: post_ids).values] + Hash[*DB.query_single(sql, post_ids: post_ids)] end end @@ -306,7 +306,7 @@ class TopicView WHERE id IN (:post_ids) AND user_id IS NOT NULL SQL - Post.exec_sql(sql, post_ids: unfiltered_post_ids).getvalue(0, 0).to_i + DB.query_single(sql, post_ids: unfiltered_post_ids).first.to_i else participants.size end diff --git a/lib/topics_bulk_action.rb b/lib/topics_bulk_action.rb index b0b5e91b7a9..ce801f1e071 100644 --- a/lib/topics_bulk_action.rb +++ b/lib/topics_bulk_action.rb @@ -72,7 +72,7 @@ class TopicsBulkAction WHERE t.id = tu.topic_id AND tu.user_id = :user_id AND t.id IN (:topic_ids) " - Topic.exec_sql(sql, user_id: @user.id, topic_ids: @topic_ids) + DB.exec(sql, user_id: @user.id, topic_ids: @topic_ids) @changed_ids.concat @topic_ids end diff --git a/script/import_scripts/base.rb b/script/import_scripts/base.rb index 78c04ea6b1a..05d66b0d1d8 100644 --- a/script/import_scripts/base.rb +++ b/script/import_scripts/base.rb @@ -626,7 +626,7 @@ class ImportScripts::Base def update_topic_status puts "", "Updating topic status" - Topic.exec_sql(<<~SQL) + DB.exec(<<~SQL) UPDATE topics AS t SET closed = TRUE WHERE EXISTS( @@ -636,7 +636,7 @@ class ImportScripts::Base ) SQL - Topic.exec_sql(<<~SQL) + DB.exec(<<~SQL) UPDATE topics AS t SET archived = TRUE WHERE EXISTS( @@ -646,7 +646,7 @@ class ImportScripts::Base ) SQL - TopicCustomField.exec_sql(<<~SQL) + DB.exec(<<~SQL) DELETE FROM topic_custom_fields WHERE name IN ('import_closed', 'import_archived') SQL @@ -654,7 +654,7 @@ class ImportScripts::Base def update_bumped_at puts "", "Updating bumped_at on topics" - Post.exec_sql("update topics t set bumped_at = COALESCE((select max(created_at) from posts where topic_id = t.id and post_type = #{Post.types[:regular]}), bumped_at)") + DB.exec("update topics t set bumped_at = COALESCE((select max(created_at) from posts where topic_id = t.id and post_type = #{Post.types[:regular]}), bumped_at)") end def update_last_posted_at @@ -674,7 +674,7 @@ class ImportScripts::Base AND users.last_posted_at <> lpa.last_posted_at SQL - User.exec_sql(sql) + DB.exec(sql) end def update_user_stats @@ -707,7 +707,7 @@ class ImportScripts::Base AND user_stats.first_post_created_at <> sub.first_post_created_at SQL - User.exec_sql(sql) + DB.exec(sql) puts "", "Updating user post_count..." @@ -725,7 +725,7 @@ class ImportScripts::Base AND user_stats.post_count <> sub.post_count SQL - User.exec_sql(sql) + DB.exec(sql) puts "", "Updating user topic_count..." @@ -743,15 +743,15 @@ class ImportScripts::Base AND user_stats.topic_count <> sub.topic_count SQL - User.exec_sql(sql) + DB.exec(sql) end # scripts that are able to import last_seen_at from the source data should override this method def update_last_seen_at puts "", "Updating last seen at on users" - User.exec_sql("UPDATE users SET last_seen_at = created_at WHERE last_seen_at IS NULL") - User.exec_sql("UPDATE users SET last_seen_at = last_posted_at WHERE last_posted_at IS NOT NULL") + DB.exec("UPDATE users SET last_seen_at = created_at WHERE last_seen_at IS NULL") + DB.exec("UPDATE users SET last_seen_at = last_posted_at WHERE last_posted_at IS NOT NULL") end def update_feature_topic_users diff --git a/script/import_scripts/ipboard3.rb b/script/import_scripts/ipboard3.rb index 5a9812f85d5..53d808aa693 100644 --- a/script/import_scripts/ipboard3.rb +++ b/script/import_scripts/ipboard3.rb @@ -267,7 +267,7 @@ class ImportScripts::IPBoard3 < ImportScripts::Base WHERE id IN (SELECT topic_id FROM closed_topic_ids) SQL - Topic.exec_sql(sql, @closed_topic_ids) + DB.exec(sql, @closed_topic_ids) end def import_personal_topics diff --git a/script/import_scripts/jive_api.rb b/script/import_scripts/jive_api.rb index f8c3734a351..923904aef83 100644 --- a/script/import_scripts/jive_api.rb +++ b/script/import_scripts/jive_api.rb @@ -325,7 +325,7 @@ class ImportScripts::JiveApi < ImportScripts::Base def mark_topics_as_solved puts "", "Marking topics as solved..." - PostAction.exec_sql <<-SQL + DB.exec <<~SQL INSERT INTO topic_custom_fields (name, value, topic_id, created_at, updated_at) SELECT 'accepted_answer_post_id', pcf.post_id, p.topic_id, p.created_at, p.created_at FROM post_custom_fields pcf diff --git a/script/import_scripts/lithium.rb b/script/import_scripts/lithium.rb index a778f0831f2..d83fe105e37 100644 --- a/script/import_scripts/lithium.rb +++ b/script/import_scripts/lithium.rb @@ -535,7 +535,7 @@ class ImportScripts::Lithium < ImportScripts::Base end puts "loading data into temp table" - PostAction.exec_sql("create temp table like_data(user_id int, post_id int, created_at timestamp without time zone)") + DB.exec("create temp table like_data(user_id int, post_id int, created_at timestamp without time zone)") PostAction.transaction do results.each do |result| @@ -544,17 +544,17 @@ class ImportScripts::Lithium < ImportScripts::Base next unless result["user_id"] && result["post_id"] - PostAction.exec_sql("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)", - user_id: result["user_id"], - post_id: result["post_id"], - created_at: result["created_at"] - ) + DB.exec("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)", + user_id: result["user_id"], + post_id: result["post_id"], + created_at: result["created_at"] + ) end end puts "creating missing post actions" - PostAction.exec_sql <<-SQL + DB.exec <<~SQL INSERT INTO post_actions (post_id, user_id, post_action_type_id, created_at, updated_at) SELECT l.post_id, l.user_id, 2, l.created_at, l.created_at FROM like_data l @@ -563,7 +563,7 @@ class ImportScripts::Lithium < ImportScripts::Base SQL puts "creating missing user actions" - UserAction.exec_sql <<-SQL + DB.exec <<~SQL INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) SELECT pa.user_id, 1, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at FROM post_actions pa @@ -574,7 +574,7 @@ class ImportScripts::Lithium < ImportScripts::Base SQL # reverse action - UserAction.exec_sql <<-SQL + DB.exec <<~SQL INSERT INTO user_actions (user_id, action_type, target_topic_id, target_post_id, acting_user_id, created_at, updated_at) SELECT p.user_id, 2, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at FROM post_actions pa @@ -586,7 +586,7 @@ class ImportScripts::Lithium < ImportScripts::Base SQL puts "updating like counts on posts" - Post.exec_sql <<-SQL + DB.exec <<~SQL UPDATE posts SET like_count = coalesce(cnt,0) FROM ( SELECT post_id, count(*) cnt @@ -600,7 +600,7 @@ class ImportScripts::Lithium < ImportScripts::Base puts "updating like counts on topics" - Post.exec_sql <<-SQL + DB.exec <<-SQL UPDATE topics SET like_count = coalesce(cnt,0) FROM ( SELECT topic_id, sum(like_count) cnt @@ -627,7 +627,7 @@ class ImportScripts::Lithium < ImportScripts::Base end puts "loading data into temp table" - PostAction.exec_sql("create temp table accepted_data(post_id int primary key)") + DB.exec("create temp table accepted_data(post_id int primary key)") PostAction.transaction do results.each do |result| @@ -635,7 +635,7 @@ class ImportScripts::Lithium < ImportScripts::Base next unless result["post_id"] - PostAction.exec_sql("INSERT INTO accepted_data VALUES (:post_id)", + DB.exec("INSERT INTO accepted_data VALUES (:post_id)", post_id: result["post_id"] ) @@ -643,7 +643,7 @@ class ImportScripts::Lithium < ImportScripts::Base end puts "deleting dupe answers" - PostAction.exec_sql <<-SQL + DB.exec <<~SQL DELETE FROM accepted_data WHERE post_id NOT IN ( SELECT post_id FROM ( @@ -656,7 +656,7 @@ class ImportScripts::Lithium < ImportScripts::Base SQL puts "importing accepted answers" - PostAction.exec_sql <<-SQL + DB.exec <<~SQL INSERT into post_custom_fields (name, value, post_id, created_at, updated_at) SELECT 'is_accepted_answer', 'true', a.post_id, current_timestamp, current_timestamp FROM accepted_data a @@ -665,7 +665,7 @@ class ImportScripts::Lithium < ImportScripts::Base SQL puts "marking accepted topics" - PostAction.exec_sql <<-SQL + DB.exec <<~SQL INSERT into topic_custom_fields (name, value, topic_id, created_at, updated_at) SELECT 'accepted_answer_post_id', a.post_id::varchar, p.topic_id, current_timestamp, current_timestamp FROM accepted_data a @@ -797,10 +797,10 @@ class ImportScripts::Lithium < ImportScripts::Base results.map { |r| r["post_id"] }.each_slice(500) do |ids| mapped = ids.map { |id| existing_map[id] }.compact - Topic.exec_sql(" - UPDATE topics SET closed = true - WHERE id IN (SELECT topic_id FROM posts where id in (:ids)) - ", ids: mapped) if mapped.present? + DB.exec(<<~SQL, ids: mapped) if mapped.present? + UPDATE topics SET closed = true + WHERE id IN (SELECT topic_id FROM posts where id in (:ids)) + SQL end end @@ -819,8 +819,8 @@ class ImportScripts::Lithium < ImportScripts::Base WHERE pm.id IS NULL AND f.name = 'import_unique_id' SQL - r = Permalink.exec_sql sql - puts "#{r.cmd_tuples} permalinks to topics added!" + r = DB.exec sql + puts "#{r} permalinks to topics added!" sql = <<-SQL INSERT INTO permalinks (url, post_id, created_at, updated_at) @@ -831,8 +831,8 @@ SQL WHERE pm.id IS NULL AND f.name = 'import_unique_id' SQL - r = Permalink.exec_sql sql - puts "#{r.cmd_tuples} permalinks to posts added!" + r = DB.exec sql + puts "#{r} permalinks to posts added!" end diff --git a/script/import_scripts/modx.rb b/script/import_scripts/modx.rb index 682954bce0f..eeaa21be95a 100644 --- a/script/import_scripts/modx.rb +++ b/script/import_scripts/modx.rb @@ -236,7 +236,7 @@ FROM #{TABLE_PREFIX}discuss_users def not_mark_topics_as_solved puts "", "Marking topics as solved..." - PostAction.exec_sql <<-SQL + DB.exec <<~SQL INSERT INTO topic_custom_fields (name, value, topic_id, created_at, updated_at) SELECT 'accepted_answer_post_id', pcf.post_id, p.topic_id, p.created_at, p.created_at FROM post_custom_fields pcf @@ -469,7 +469,7 @@ FROM #{TABLE_PREFIX}discuss_users WHERE id IN (SELECT topic_id FROM closed_topic_ids) SQL - Topic.exec_sql(sql, closed_topic_ids) + DB.exec(sql, closed_topic_ids) end def not_post_process_posts diff --git a/script/import_scripts/stack_overflow.rb b/script/import_scripts/stack_overflow.rb index 1759f2eec8b..06754fa0b26 100644 --- a/script/import_scripts/stack_overflow.rb +++ b/script/import_scripts/stack_overflow.rb @@ -237,7 +237,7 @@ class ImportScripts::StackOverflow < ImportScripts::Base def mark_topics_as_solved puts "", "Marking topics as solved..." - Topic.exec_sql <<~SQL + DB.exec <<~SQL INSERT INTO topic_custom_fields (name, value, topic_id, created_at, updated_at) SELECT 'accepted_answer_post_id', pcf.post_id, p.topic_id, p.created_at, p.created_at FROM post_custom_fields pcf diff --git a/script/import_scripts/vbulletin.rb b/script/import_scripts/vbulletin.rb index 656c27e7b35..23923d0baa0 100644 --- a/script/import_scripts/vbulletin.rb +++ b/script/import_scripts/vbulletin.rb @@ -182,10 +182,8 @@ EOM next if user_ids_in_group.size == 0 values = user_ids_in_group.map { |user_id| "(#{group.id}, #{user_id}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" }.join(",") - User.exec_sql <<-SQL - BEGIN; - INSERT INTO group_users (group_id, user_id, created_at, updated_at) VALUES #{values}; - COMMIT; + DB.exec <<~SQL + INSERT INTO group_users (group_id, user_id, created_at, updated_at) VALUES #{values} SQL Group.reset_counters(group.id, :group_users) @@ -634,7 +632,7 @@ EOM WHERE id IN (SELECT topic_id FROM closed_topic_ids) SQL - Topic.exec_sql(sql, closed_topic_ids) + DB.exec(sql, closed_topic_ids) end def post_process_posts diff --git a/script/import_scripts/vbulletin5.rb b/script/import_scripts/vbulletin5.rb index da1ceea84bf..36a46745b5d 100644 --- a/script/import_scripts/vbulletin5.rb +++ b/script/import_scripts/vbulletin5.rb @@ -418,7 +418,7 @@ class ImportScripts::VBulletin < ImportScripts::Base WHERE id IN (SELECT topic_id FROM closed_topic_ids) SQL - Topic.exec_sql(sql, @closed_topic_ids) + DB.exec(sql, @closed_topic_ids) end def post_process_posts diff --git a/spec/components/concern/has_custom_fields_spec.rb b/spec/components/concern/has_custom_fields_spec.rb index 035e0260c5a..1a55fc9f7c4 100644 --- a/spec/components/concern/has_custom_fields_spec.rb +++ b/spec/components/concern/has_custom_fields_spec.rb @@ -4,8 +4,8 @@ describe HasCustomFields do context "custom_fields" do before do - Topic.exec_sql("create temporary table custom_fields_test_items(id SERIAL primary key)") - Topic.exec_sql("create temporary table custom_fields_test_item_custom_fields(id SERIAL primary key, custom_fields_test_item_id int, name varchar(256) not null, value text)") + DB.exec("create temporary table custom_fields_test_items(id SERIAL primary key)") + DB.exec("create temporary table custom_fields_test_item_custom_fields(id SERIAL primary key, custom_fields_test_item_id int, name varchar(256) not null, value text)") class CustomFieldsTestItem < ActiveRecord::Base include HasCustomFields @@ -17,8 +17,8 @@ describe HasCustomFields do end after do - Topic.exec_sql("drop table custom_fields_test_items") - Topic.exec_sql("drop table custom_fields_test_item_custom_fields") + DB.exec("drop table custom_fields_test_items") + DB.exec("drop table custom_fields_test_item_custom_fields") # import is making my life hard, we need to nuke this out of orbit des = ActiveSupport::DescendantsTracker.class_variable_get :@@direct_descendants @@ -75,7 +75,7 @@ describe HasCustomFields do # should be casted right after saving expect(test_item.custom_fields["a"]).to eq("0") - CustomFieldsTestItem.exec_sql("UPDATE custom_fields_test_item_custom_fields SET value='1' WHERE custom_fields_test_item_id=? AND name='a'", test_item.id) + DB.exec("UPDATE custom_fields_test_item_custom_fields SET value='1' WHERE custom_fields_test_item_id=? AND name='a'", test_item.id) # still the same, did not load expect(test_item.custom_fields["a"]).to eq("0") diff --git a/spec/components/concern/has_search_data_spec.rb b/spec/components/concern/has_search_data_spec.rb index 9cc3089f585..8c5441b2460 100644 --- a/spec/components/concern/has_search_data_spec.rb +++ b/spec/components/concern/has_search_data_spec.rb @@ -3,8 +3,8 @@ require "rails_helper" describe HasSearchData do context "belongs to its model" do before do - Topic.exec_sql("create temporary table model_items(id SERIAL primary key)") - Topic.exec_sql("create temporary table model_item_search_data(model_item_id int primary key, search_data tsvector, raw_data text, locale text)") + DB.exec("create temporary table model_items(id SERIAL primary key)") + DB.exec("create temporary table model_item_search_data(model_item_id int primary key, search_data tsvector, raw_data text, locale text)") class ModelItem < ActiveRecord::Base has_one :model_item_search_data, dependent: :destroy @@ -16,8 +16,8 @@ describe HasSearchData do end after do - Topic.exec_sql("drop table model_items") - Topic.exec_sql("drop table model_item_search_data") + DB.exec("drop table model_items") + DB.exec("drop table model_item_search_data") # import is making my life hard, we need to nuke this out of orbit des = ActiveSupport::DescendantsTracker.class_variable_get :@@direct_descendants diff --git a/spec/components/concern/positionable_spec.rb b/spec/components/concern/positionable_spec.rb index d8433d67115..4ab010a538e 100644 --- a/spec/components/concern/positionable_spec.rb +++ b/spec/components/concern/positionable_spec.rb @@ -12,11 +12,11 @@ describe Positionable do include Positionable end - Topic.exec_sql("create temporary table test_items(id int primary key, position int)") + DB.exec("create temporary table test_items(id int primary key, position int)") end after do - Topic.exec_sql("drop table test_items") + DB.exec("drop table test_items") # import is making my life hard, we need to nuke this out of orbit des = ActiveSupport::DescendantsTracker.class_variable_get :@@direct_descendants @@ -25,7 +25,7 @@ describe Positionable do it "can position stuff correctly" do 5.times do |i| - Topic.exec_sql("insert into test_items(id,position) values(#{i}, #{i})") + DB.exec("insert into test_items(id,position) values(#{i}, #{i})") end expect(positions).to eq([0, 1, 2, 3, 4]) diff --git a/spec/components/concern/searchable_spec.rb b/spec/components/concern/searchable_spec.rb index 365f2b16f87..5c55c7d1ff5 100644 --- a/spec/components/concern/searchable_spec.rb +++ b/spec/components/concern/searchable_spec.rb @@ -3,8 +3,8 @@ require "rails_helper" describe Searchable do context "has search data" do before do - Topic.exec_sql("create temporary table searchable_records(id SERIAL primary key)") - Topic.exec_sql("create temporary table searchable_record_search_data(searchable_record_id int primary key, search_data tsvector, raw_data text, locale text)") + DB.exec("create temporary table searchable_records(id SERIAL primary key)") + DB.exec("create temporary table searchable_record_search_data(searchable_record_id int primary key, search_data tsvector, raw_data text, locale text)") class SearchableRecord < ActiveRecord::Base include Searchable @@ -17,8 +17,8 @@ describe Searchable do end after do - Topic.exec_sql("drop table searchable_records") - Topic.exec_sql("drop table searchable_record_search_data") + DB.exec("drop table searchable_records") + DB.exec("drop table searchable_record_search_data") # import is making my life hard, we need to nuke this out of orbit des = ActiveSupport::DescendantsTracker.class_variable_get :@@direct_descendants diff --git a/spec/components/migration/column_dropper_spec.rb b/spec/components/migration/column_dropper_spec.rb index 9a0bd536e2b..b83aa32fc75 100644 --- a/spec/components/migration/column_dropper_spec.rb +++ b/spec/components/migration/column_dropper_spec.rb @@ -4,7 +4,7 @@ require_dependency 'migration/column_dropper' RSpec.describe Migration::ColumnDropper do def has_column?(table, column) - ActiveRecord::Base.exec_sql(<<~SQL, table: table, column: column).to_a.length == 1 + DB.exec(<<~SQL, table: table, column: column) == 1 SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE @@ -15,7 +15,7 @@ RSpec.describe Migration::ColumnDropper do end def update_first_migration_date(created_at) - ActiveRecord::Base.exec_sql(<<~SQL, created_at: created_at) + DB.exec(<<~SQL, created_at: created_at) UPDATE schema_migration_details SET created_at = :created_at WHERE id = (SELECT MIN(id) @@ -25,15 +25,13 @@ RSpec.describe Migration::ColumnDropper do describe ".drop" do let(:migration_name) do - ActiveRecord::Base - .exec_sql("SELECT name FROM schema_migration_details ORDER BY id DESC LIMIT 1") - .getvalue(0, 0) + DB.query_single("SELECT name FROM schema_migration_details ORDER BY id DESC LIMIT 1").first end before do - Topic.exec_sql "ALTER TABLE topics ADD COLUMN junk int" + DB.exec "ALTER TABLE topics ADD COLUMN junk int" - ActiveRecord::Base.exec_sql(<<~SQL, name: migration_name, created_at: 15.minutes.ago) + DB.exec(<<~SQL, name: migration_name, created_at: 15.minutes.ago) UPDATE schema_migration_details SET created_at = :created_at WHERE name = :name @@ -114,7 +112,7 @@ RSpec.describe Migration::ColumnDropper do let(:table_name) { "table_with_readonly_column" } before do - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL CREATE TABLE #{table_name} (topic_id INTEGER, email TEXT); INSERT INTO #{table_name} (topic_id, email) @@ -127,16 +125,14 @@ RSpec.describe Migration::ColumnDropper do after do ActiveRecord::Base.connection.reset! - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL DROP TABLE IF EXISTS #{table_name}; DROP TRIGGER IF EXISTS #{table_name}_email_readonly ON #{table_name}; SQL end it 'should be droppable' do - name = Topic - .exec_sql("SELECT name FROM schema_migration_details LIMIT 1") - .getvalue(0, 0) + name = DB.query_single("SELECT name FROM schema_migration_details LIMIT 1").first dropped_proc_called = false Migration::ColumnDropper.drop( @@ -152,7 +148,7 @@ RSpec.describe Migration::ColumnDropper do end it 'should prevent updates to the readonly column' do expect do - ActiveRecord::Base.connection.raw_connection.exec <<~SQL + DB.exec <<~SQL UPDATE #{table_name} SET email = 'testing@email.com' WHERE topic_id = 1; @@ -164,7 +160,7 @@ RSpec.describe Migration::ColumnDropper do end it 'should allow updates to the other columns' do - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL UPDATE #{table_name} SET topic_id = 2 WHERE topic_id = 1 @@ -188,14 +184,14 @@ RSpec.describe Migration::ColumnDropper do end it 'should allow insertions to the other columns' do - ActiveRecord::Base.exec_sql <<~SQL + DB.exec <<~SQL INSERT INTO #{table_name} (topic_id) VALUES (2); SQL expect( - ActiveRecord::Base.exec_sql("SELECT * FROM #{table_name} WHERE topic_id = 2;").values - ).to include([2, nil]) + DB.query_single("SELECT topic_id FROM #{table_name} WHERE topic_id = 2") + ).to eq([2]) end end end diff --git a/spec/components/migration/table_dropper_spec.rb b/spec/components/migration/table_dropper_spec.rb index 0b798f82bcb..7db3c64f1de 100644 --- a/spec/components/migration/table_dropper_spec.rb +++ b/spec/components/migration/table_dropper_spec.rb @@ -11,11 +11,11 @@ describe Migration::TableDropper do table_name = '#{table_name}' SQL - ActiveRecord::Base.exec_sql(sql).to_a.length > 0 + DB.exec(sql) > 0 end def update_first_migration_date(created_at) - ActiveRecord::Base.exec_sql(<<~SQL, created_at: created_at) + DB.exec(<<~SQL, created_at: created_at) UPDATE schema_migration_details SET created_at = :created_at WHERE id = (SELECT MIN(id) @@ -24,19 +24,17 @@ describe Migration::TableDropper do end def create_new_table - ActiveRecord::Base.exec_sql "CREATE TABLE table_with_new_name (topic_id INTEGER)" + DB.exec "CREATE TABLE table_with_new_name (topic_id INTEGER)" end let(:migration_name) do - ActiveRecord::Base - .exec_sql("SELECT name FROM schema_migration_details ORDER BY id DESC LIMIT 1") - .getvalue(0, 0) + DB.query_single("SELECT name FROM schema_migration_details ORDER BY id DESC LIMIT 1").first end before do - ActiveRecord::Base.exec_sql "CREATE TABLE table_with_old_name (topic_id INTEGER)" + DB.exec "CREATE TABLE table_with_old_name (topic_id INTEGER)" - ActiveRecord::Base.exec_sql(<<~SQL, name: migration_name, created_at: 15.minutes.ago) + DB.exec(<<~SQL, name: migration_name, created_at: 15.minutes.ago) UPDATE schema_migration_details SET created_at = :created_at WHERE name = :name diff --git a/spec/components/search_spec.rb b/spec/components/search_spec.rb index 0a70342d403..9ad75bf58cf 100644 --- a/spec/components/search_spec.rb +++ b/spec/components/search_spec.rb @@ -895,7 +895,7 @@ describe Search do str << "page page on Atmosphere](https://atmospherejs.com/grigio/babel)xxx: aaa.js:222 aaa'\"bbb" ts_query = Search.ts_query(term: str, ts_config: "simple") - Post.exec_sql("SELECT to_tsvector('bbb') @@ " << ts_query) + DB.exec("SELECT to_tsvector('bbb') @@ " << ts_query) end context '#word_to_date' do diff --git a/spec/jobs/toggle_topic_closed_spec.rb b/spec/jobs/toggle_topic_closed_spec.rb index 31908796cce..24b9d3c307b 100644 --- a/spec/jobs/toggle_topic_closed_spec.rb +++ b/spec/jobs/toggle_topic_closed_spec.rb @@ -53,7 +53,7 @@ describe Jobs::ToggleTopicClosed do user: admin ) - freeze_time(1.hour.from_now) do + freeze_time(61.minutes.from_now) do described_class.new.execute( topic_timer_id: topic.public_topic_timer.id, state: false diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 4027250bef9..30a3ba3f42e 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -406,7 +406,7 @@ describe Group do user = Fabricate(:user) user.change_trust_level!(TrustLevel[2]) - Group.exec_sql("UPDATE groups SET user_count = 0 WHERE id = #{Group::AUTO_GROUPS[:trust_level_2]}") + DB.exec("UPDATE groups SET user_count = 0 WHERE id = #{Group::AUTO_GROUPS[:trust_level_2]}") Group.refresh_automatic_groups! diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index f2bd06587da..0515e53cffb 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -1014,7 +1014,7 @@ describe Post do first_baked = post.baked_at first_cooked = post.cooked - Post.exec_sql("UPDATE posts SET cooked = 'frogs' WHERE id = ?", post.id) + DB.exec("UPDATE posts SET cooked = 'frogs' WHERE id = ?", [ post.id ]) post.reload post.expects(:publish_change_to_clients!).with(:rebaked) diff --git a/spec/models/topic_link_spec.rb b/spec/models/topic_link_spec.rb index 606ba5f7bae..d3d53a07280 100644 --- a/spec/models/topic_link_spec.rb +++ b/spec/models/topic_link_spec.rb @@ -330,7 +330,7 @@ http://b.com/#{'a' * 500} array = TopicLink.topic_map(Guardian.new, post.topic_id) expect(array.length).to eq(6) - expect(array[0]["clicks"]).to eq(1) + expect(array[0].clicks).to eq(1) end it 'secures internal links correctly' do diff --git a/spec/models/topic_user_spec.rb b/spec/models/topic_user_spec.rb index c0ed6c986ba..ed9de27c3ad 100644 --- a/spec/models/topic_user_spec.rb +++ b/spec/models/topic_user_spec.rb @@ -311,8 +311,10 @@ describe TopicUser do it 'should update tracking state when you reply' do new_user.user_option.update_column(:notification_level_when_replying, 3) post_creator.create - TopicUser.exec_sql("UPDATE topic_users set notification_level=2 - WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) + DB.exec("UPDATE topic_users set notification_level=2 + WHERE topic_id = :topic_id AND user_id = :user_id", + topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) + TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:watching]) tu = TopicUser.find_by(user_id: topic_new_user.user_id, topic_id: topic_new_user.topic_id) @@ -322,7 +324,7 @@ describe TopicUser do it 'should not update tracking state when you reply' do new_user.user_option.update_column(:notification_level_when_replying, 3) post_creator.create - TopicUser.exec_sql("UPDATE topic_users set notification_level=3 + DB.exec("UPDATE topic_users set notification_level=3 WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:tracking]) @@ -333,7 +335,7 @@ describe TopicUser do it 'should not update tracking state when state manually set to normal you reply' do new_user.user_option.update_column(:notification_level_when_replying, 3) post_creator.create - TopicUser.exec_sql("UPDATE topic_users set notification_level=1 + DB.exec("UPDATE topic_users set notification_level=1 WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:tracking]) @@ -344,7 +346,7 @@ describe TopicUser do it 'should not update tracking state when state manually set to muted you reply' do new_user.user_option.update_column(:notification_level_when_replying, 3) post_creator.create - TopicUser.exec_sql("UPDATE topic_users set notification_level=0 + DB.exec("UPDATE topic_users set notification_level=0 WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: topic_new_user.topic_id, user_id: topic_new_user.user_id) TopicUser.auto_notification(topic_new_user.user_id, topic_new_user.topic_id, TopicUser.notification_reasons[:created_post], TopicUser.notification_levels[:tracking]) @@ -417,7 +419,7 @@ describe TopicUser do p2 = Fabricate(:post, user: p1.user, topic: p1.topic, post_number: 2) p1.topic.notifier.watch_topic!(p1.user_id) - TopicUser.exec_sql("UPDATE topic_users set highest_seen_post_number=1, last_read_post_number=0 + DB.exec("UPDATE topic_users set highest_seen_post_number=1, last_read_post_number=0 WHERE topic_id = :topic_id AND user_id = :user_id", topic_id: p1.topic_id, user_id: p1.user_id) [p1, p2].each do |p|