From 5f64fd0a217552d1e031a28732a0e6000b090cc4 Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 19 Jun 2018 16:13:14 +1000 Subject: [PATCH] DEV: remove exec_sql and replace with mini_sql Introduce new patterns for direct sql that are safe and fast. MiniSql is not prone to memory bloat that can happen with direct PG usage. It also has an extremely fast materializer and very a convenient API - DB.exec(sql, *params) => runs sql returns row count - DB.query(sql, *params) => runs sql returns usable objects (not a hash) - DB.query_hash(sql, *params) => runs sql returns an array of hashes - DB.query_single(sql, *params) => runs sql and returns a flat one dimensional array - DB.build(sql) => returns a sql builder See more at: https://github.com/discourse/mini_sql --- Gemfile | 3 +- Gemfile.lock | 6 +- .../admin/diagnostics_controller.rb | 13 - app/controllers/admin/users_controller.rb | 2 +- app/jobs/onceoff/create_tags_search_index.rb | 4 +- app/jobs/onceoff/fix_posts_read.rb | 2 +- .../fix_primary_emails_for_staged_users.rb | 2 +- app/jobs/onceoff/fix_retro_anniversary.rb | 8 +- app/jobs/onceoff/init_category_tag_stats.rb | 4 +- app/jobs/onceoff/migrate_censored_words.rb | 4 +- app/jobs/onceoff/retro_recent_time_read.rb | 2 +- app/jobs/regular/update_username.rb | 4 +- app/jobs/scheduled/clean_up_crawler_stats.rb | 2 +- .../scheduled/grant_anniversary_badges.rb | 5 +- .../grant_new_user_of_the_month_badges.rb | 7 +- app/models/badge.rb | 4 +- app/models/category.rb | 16 +- app/models/category_tag_stat.rb | 4 +- app/models/category_user.rb | 16 +- app/models/concerns/positionable.rb | 6 +- app/models/directory_item.rb | 39 +-- app/models/draft.rb | 8 +- app/models/draft_sequence.rb | 6 +- app/models/emoji_set_site_setting.rb | 2 +- app/models/group.rb | 56 ++-- app/models/group_user.rb | 8 +- app/models/incoming_link.rb | 4 +- app/models/notification.rb | 12 +- app/models/post.rb | 28 +- app/models/post_action.rb | 43 +-- app/models/post_revision.rb | 4 +- app/models/post_timing.rb | 23 +- app/models/quoted_post.rb | 4 +- app/models/screened_ip_address.rb | 16 +- app/models/stylesheet_cache.rb | 2 +- app/models/tag.rb | 14 +- app/models/top_topic.rb | 10 +- app/models/topic.rb | 252 +++++++++--------- app/models/topic_featured_users.rb | 2 +- app/models/topic_link.rb | 16 +- app/models/topic_user.rb | 55 ++-- app/models/trust_level3_requirements.rb | 2 +- app/models/user.rb | 34 +-- app/models/user_action.rb | 7 +- app/models/user_auth_token.rb | 4 +- app/models/user_option.rb | 12 +- app/models/user_stat.rb | 54 ++-- app/models/user_visit.rb | 26 +- app/serializers/topic_link_serializer.rb | 38 +-- app/services/badge_granter.rb | 109 ++++---- app/services/post_alerter.rb | 14 +- app/services/search_indexer.rb | 6 +- app/services/user_merger.rb | 28 +- app/services/user_updater.rb | 23 +- config/initializers/000-mini_sql.rb | 2 + config/routes.rb | 1 - db/fixtures/000_delayed_drops.rb | 2 +- db/fixtures/001_categories.rb | 12 +- db/fixtures/006_badges.rb | 2 +- db/fixtures/500_lounge_category.rb | 2 +- db/fixtures/501_meta_category.rb | 2 +- db/fixtures/502_staff_category.rb | 2 +- ...538_add_starred_at_to_forum_thread_user.rb | 2 +- ...0121123054127_make_post_number_distinct.rb | 2 +- .../20140120155706_add_lounge_category.rb | 12 +- .../20140122043508_add_meta_category.rb | 12 +- .../20140227201005_add_staff_category.rb | 14 +- ...111_init_fixed_category_positions_value.rb | 4 +- ...20115_google_openid_default_has_changed.rb | 12 +- ...45431_disable_external_auths_by_default.rb | 8 +- ...1193923_remove_email_in_address_setting.rb | 10 +- ...216112341_resolve_duplicate_group_names.rb | 4 +- ...8_fix_category_logo_and_background_urls.rb | 2 +- ...15151505_add_seen_at_to_user_auth_token.rb | 2 +- .../20170728012754_split_public_in_groups.rb | 2 +- ...308071922_drop_raise_read_only_function.rb | 2 +- lib/backup_restore/backup_restore.rb | 4 +- lib/backup_restore/restorer.rb | 4 +- lib/comment_migration.rb | 8 +- lib/cooked_post_processor.rb | 2 +- lib/migration/base_dropper.rb | 2 +- lib/migration/column_dropper.rb | 6 +- lib/migration/table_dropper.rb | 10 +- lib/mini_sql_multisite_connection.rb | 38 +++ lib/post_revisor.rb | 2 +- lib/search.rb | 8 +- lib/tasks/db.rake | 24 +- lib/tasks/import.rake | 41 ++- lib/tasks/posts.rake | 10 +- lib/tasks/uploads.rake | 2 +- lib/topic_view.rb | 6 +- lib/topics_bulk_action.rb | 2 +- script/import_scripts/base.rb | 20 +- script/import_scripts/ipboard3.rb | 2 +- script/import_scripts/jive_api.rb | 2 +- script/import_scripts/lithium.rb | 48 ++-- script/import_scripts/modx.rb | 4 +- script/import_scripts/stack_overflow.rb | 2 +- script/import_scripts/vbulletin.rb | 8 +- script/import_scripts/vbulletin5.rb | 2 +- .../concern/has_custom_fields_spec.rb | 10 +- .../concern/has_search_data_spec.rb | 8 +- spec/components/concern/positionable_spec.rb | 6 +- spec/components/concern/searchable_spec.rb | 8 +- .../migration/column_dropper_spec.rb | 30 +-- .../migration/table_dropper_spec.rb | 14 +- spec/components/search_spec.rb | 2 +- spec/jobs/toggle_topic_closed_spec.rb | 2 +- spec/models/group_spec.rb | 2 +- spec/models/post_spec.rb | 2 +- spec/models/topic_link_spec.rb | 2 +- spec/models/topic_user_spec.rb | 14 +- 112 files changed, 782 insertions(+), 763 deletions(-) create mode 100644 config/initializers/000-mini_sql.rb create mode 100644 lib/mini_sql_multisite_connection.rb diff --git a/Gemfile b/Gemfile index bec856bd2a..006b9175f3 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 f4bea23c8f..32b04e5d0d 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 d10c559b4c..81b076b072 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 66b5daf926..70573a2e91 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 3e673bf2e6..4101aed0dc 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 2505038723..0f8d97918a 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 d024688da9..c9516e3655 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 3cd4142caf..2d553c3a29 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 43abdc0b42..e9f256d9b5 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 03ce4b358c..4f7a2201e7 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 1dd1ce0289..b7bf3e7501 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 c59275b716..d3cfe48d78 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 816e91f0fd..5fbf5d548a 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 65eb67423c..91094a192e 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 097c3cd99f..d6a4a16d56 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 9a64949980..ed73631eab 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 379cc5acc7..f0c4e4b800 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 8aff0447c8..b5673bcdf5 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 8d0cf15912..0ae88ecd42 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 a31453bc02..b60a2f19d8 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 be412cc72a..b008a6c51c 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 460556d004..9629e8402c 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 952bd37a0d..003eb770e4 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 fe0c647e39..60cb1c0282 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 5840b53664..ed528b3d3a 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 0cc748a9a3..ef680dc493 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 8e213851bc..7a294d81bf 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 c797c471a8..4e04c736f2 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 479091d8af..cf4d50d2dc 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 0a7016966d..b91f50765a 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 54ed9bbefc..23d224c5e6 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 c6d24675f5..26cc80ef8d 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 0c23b574b4..29729e622e 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 08b98c3085..52f6ef337e 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 46ef77bde7..705d6768a5 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 709148167c..58703023b4 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 9b1c1145ea..b0e1e62801 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 d8a8a406cb..7b442423b2 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 40fbcab801..c7a745218d 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 a6cff083de..b41fabb3da 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 ac63e5ae6a..7b85690508 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 a20687a5d1..e389a4ea4c 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 f36c034b0e..981cb4a9c4 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 57eeaca888..b7549486ca 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 5077d8dc36..95c34d51f6 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 cf9ab05e2c..86731032a3 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 52bd2fab9c..6fe21eae7b 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 42367857c5..4ef9afc65b 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 1ac69bce48..8446b5bcda 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 476856253b..9b4e774b05 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 35ccd7a378..6165be0705 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 e1d590926b..4fc678b13f 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 ade3e97db9..eb9011cc21 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 0000000000..35f8713b8b --- /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 030fd7aad2..0923afeab0 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 bf07dcb725..a6a5eebde3 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 aef949349c..982eee7dad 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 18022ab451..eae24fec16 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 5fa93ddb1e..2aa42f21cc 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 38ed96aaea..38f89856bb 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 54faa6385b..18b5c54337 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 2fc02d4287..c2643594d4 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 695ac7392c..259cdbb915 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 0dac6e7b29..9e2517d8dd 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 2b42b81643..c4ee6b6b6b 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 67a8480601..837daaaef0 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 f9f454848f..8b9a545b7a 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 d76718c691..bea872001e 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 8f47ba505e..2cd19018df 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 a82dbe4dd0..a14d44c763 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 c13b1f7b80..96cb718f8d 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 a3fe7b251b..ea96cf0ee1 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 5044127321..4ed90b6223 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 febb2f9779..688baaceff 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 a90a2561c9..d756aa7e14 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 a0a217556e..0591b31fe1 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 d2d46239be..fefe6c4577 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 79c1576dd7..bd00d58c37 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 12e2d35e05..93866749e5 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 f36963d99e..d8e2f03d93 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 d7176505d1..a594105622 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 78a9c7172f..c95640b78d 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 0000000000..d2645a6af8 --- /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 d74e50dd95..b406351256 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 054f7e5fd0..f9cfdbd1a4 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 e7782f1a85..1d1c5fa0ed 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 ad291fd96a..7d83eb0115 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 3c17bc1e3b..9909bb3c01 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 d69f3078a7..6e5b7fcb3e 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 d2bf87de10..45e7f2bc3d 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 b0b5e91b7a..ce801f1e07 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 78c04ea6b1..05d66b0d1d 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 5a9812f85d..53d808aa69 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 f8c3734a35..923904aef8 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 a778f0831f..d83fe105e3 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 682954bce0..eeaa21be95 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 1759f2eec8..06754fa0b2 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 656c27e7b3..23923d0baa 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 da1ceea84b..36a46745b5 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 035e0260c5..1a55fc9f7c 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 9cc3089f58..8c5441b246 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 d8433d6711..4ab010a538 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 365f2b16f8..5c55c7d1ff 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 9a0bd536e2..b83aa32fc7 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 0b798f82bc..7db3c64f1d 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 0a70342d40..9ad75bf58c 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 31908796cc..24b9d3c307 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 4027250bef..30a3ba3f42 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 f2bd06587d..0515e53cff 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 606ba5f7ba..d3d53a0728 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 c0ed6c986b..ed9de27c3a 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|