mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 08:57:10 -06:00
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
This commit is contained in:
parent
cc3fc87dd7
commit
5f64fd0a21
3
Gemfile
3
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'
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -77,7 +77,7 @@ class Admin::UsersController < Admin::AdminController
|
||||
)
|
||||
SQL
|
||||
|
||||
UserHistory.exec_sql(
|
||||
DB.exec(
|
||||
sql,
|
||||
UserHistory.actions.slice(
|
||||
:silence_user,
|
||||
|
@ -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
|
||||
|
@ -15,7 +15,7 @@ UPDATE user_stats
|
||||
)
|
||||
SQL
|
||||
|
||||
UserStat.exec_sql(sql)
|
||||
DB.exec(sql)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -24,7 +24,7 @@ module Jobs
|
||||
end
|
||||
end
|
||||
|
||||
User.exec_sql <<~SQL
|
||||
DB.exec <<~SQL
|
||||
INSERT INTO user_emails (
|
||||
user_id,
|
||||
email,
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 (
|
||||
|
@ -155,14 +155,14 @@ SQL
|
||||
end
|
||||
|
||||
def self.ensure_consistency!
|
||||
exec_sql <<SQL
|
||||
DELETE FROM category_users
|
||||
WHERE user_id IN (
|
||||
SELECT cu.user_id FROM category_users cu
|
||||
LEFT JOIN users u ON u.id = cu.user_id
|
||||
WHERE u.id IS NULL
|
||||
)
|
||||
SQL
|
||||
DB.exec <<~SQL
|
||||
DELETE FROM category_users
|
||||
WHERE user_id IN (
|
||||
SELECT cu.user_id FROM category_users cu
|
||||
LEFT JOIN users u ON u.id = cu.user_id
|
||||
WHERE u.id IS NULL
|
||||
)
|
||||
SQL
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -12,13 +12,13 @@ module Positionable
|
||||
position = [[position_arg, 0].max, self.class.count - 1].min
|
||||
|
||||
if self.position.nil? || position > (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
|
||||
|
@ -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 <<SQL
|
||||
UPDATE user_stats s
|
||||
SET likes_given = d.likes_given,
|
||||
likes_received = d.likes_received,
|
||||
topic_count = d.topic_count,
|
||||
post_count = d.post_count
|
||||
DB.exec <<~SQL
|
||||
UPDATE user_stats s
|
||||
SET likes_given = d.likes_given,
|
||||
likes_received = d.likes_received,
|
||||
topic_count = d.topic_count,
|
||||
post_count = d.post_count
|
||||
|
||||
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
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}%"
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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')
|
||||
|
@ -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|
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -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 <<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
|
||||
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
|
||||
|
@ -79,7 +79,7 @@ WHERE tt.id = tt2.id AND
|
||||
#{filter2}
|
||||
SQL
|
||||
|
||||
Topic.exec_sql(sql)
|
||||
DB.exec(sql)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -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)
|
||||
|
@ -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 = <<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
|
||||
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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -11,7 +11,7 @@ class UserVisit < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def self.count_by_active_users(start_date, end_date)
|
||||
sql = <<SQL
|
||||
sql = <<~SQL
|
||||
WITH dau AS (
|
||||
SELECT date_trunc('day', user_visits.visited_at)::DATE AS date,
|
||||
count(distinct user_visits.user_id) AS dau
|
||||
@ -27,9 +27,9 @@ class UserVisit < ActiveRecord::Base
|
||||
WHERE user_visits.visited_at::DATE BETWEEN dau.date - 29 AND dau.date
|
||||
) AS mau
|
||||
FROM dau
|
||||
SQL
|
||||
SQL
|
||||
|
||||
UserVisit.exec_sql(sql, start_date: start_date, end_date: end_date).to_a
|
||||
DB.query_hash(sql, start_date: start_date, end_date: end_date)
|
||||
end
|
||||
|
||||
# A count of visits in a date range by day
|
||||
@ -42,16 +42,16 @@ SQL
|
||||
end
|
||||
|
||||
def self.ensure_consistency!
|
||||
exec_sql <<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
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
||||
|
2
config/initializers/000-mini_sql.rb
Normal file
2
config/initializers/000-mini_sql.rb
Normal file
@ -0,0 +1,2 @@
|
||||
require 'mini_sql_multisite_connection'
|
||||
::DB = MiniSqlMultisiteConnection.instance
|
@ -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"
|
||||
|
@ -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)"
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 (
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
(
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}', '')
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}';
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
38
lib/mini_sql_multisite_connection.rb
Normal file
38
lib/mini_sql_multisite_connection.rb
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user