mirror of
https://github.com/discourse/discourse.git
synced 2025-04-16 20:59:06 +08: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 'omniauth-google-oauth2'
|
||||||
gem 'oj'
|
gem 'oj'
|
||||||
gem 'pg', '~> 0.21.0'
|
gem 'pg'
|
||||||
|
gem 'mini_sql'
|
||||||
gem 'pry-rails', require: false
|
gem 'pry-rails', require: false
|
||||||
gem 'r2', '~> 0.2.5', require: false
|
gem 'r2', '~> 0.2.5', require: false
|
||||||
gem 'rake'
|
gem 'rake'
|
||||||
|
@ -176,6 +176,7 @@ GEM
|
|||||||
mini_portile2 (2.3.0)
|
mini_portile2 (2.3.0)
|
||||||
mini_racer (0.1.15)
|
mini_racer (0.1.15)
|
||||||
libv8 (~> 6.3)
|
libv8 (~> 6.3)
|
||||||
|
mini_sql (0.1.4)
|
||||||
mini_suffix (0.3.0)
|
mini_suffix (0.3.0)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
minitest (5.11.3)
|
minitest (5.11.3)
|
||||||
@ -240,7 +241,7 @@ GEM
|
|||||||
parallel (1.12.1)
|
parallel (1.12.1)
|
||||||
parser (2.5.1.0)
|
parser (2.5.1.0)
|
||||||
ast (~> 2.4.0)
|
ast (~> 2.4.0)
|
||||||
pg (0.21.0)
|
pg (1.0.0)
|
||||||
powerpack (0.1.1)
|
powerpack (0.1.1)
|
||||||
progress (3.4.0)
|
progress (3.4.0)
|
||||||
pry (0.10.4)
|
pry (0.10.4)
|
||||||
@ -453,6 +454,7 @@ DEPENDENCIES
|
|||||||
message_bus
|
message_bus
|
||||||
mini_mime
|
mini_mime
|
||||||
mini_racer
|
mini_racer
|
||||||
|
mini_sql
|
||||||
mini_suffix
|
mini_suffix
|
||||||
minitest
|
minitest
|
||||||
mocha
|
mocha
|
||||||
@ -471,7 +473,7 @@ DEPENDENCIES
|
|||||||
omniauth-twitter
|
omniauth-twitter
|
||||||
onebox (= 1.8.48)
|
onebox (= 1.8.48)
|
||||||
openid-redis-store
|
openid-redis-store
|
||||||
pg (~> 0.21.0)
|
pg
|
||||||
pry-nav
|
pry-nav
|
||||||
pry-rails
|
pry-rails
|
||||||
puma
|
puma
|
||||||
|
@ -4,19 +4,6 @@ class Admin::DiagnosticsController < Admin::AdminController
|
|||||||
layout false
|
layout false
|
||||||
skip_before_action :check_xhr
|
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
|
def memory_stats
|
||||||
text = nil
|
text = nil
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ class Admin::UsersController < Admin::AdminController
|
|||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
UserHistory.exec_sql(
|
DB.exec(
|
||||||
sql,
|
sql,
|
||||||
UserHistory.actions.slice(
|
UserHistory.actions.slice(
|
||||||
:silence_user,
|
:silence_user,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
module Jobs
|
module Jobs
|
||||||
class CreateTagsSearchIndex < Jobs::Onceoff
|
class CreateTagsSearchIndex < Jobs::Onceoff
|
||||||
def execute_onceoff(args)
|
def execute_onceoff(args)
|
||||||
Tag.exec_sql('select id, name from tags').each do |t|
|
DB.query('select id, name from tags').each do |t|
|
||||||
SearchIndexer.update_tags_index(t['id'], t['name'])
|
SearchIndexer.update_tags_index(t.id, t.name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,7 +15,7 @@ UPDATE user_stats
|
|||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
UserStat.exec_sql(sql)
|
DB.exec(sql)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -24,7 +24,7 @@ module Jobs
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
User.exec_sql <<~SQL
|
DB.exec <<~SQL
|
||||||
INSERT INTO user_emails (
|
INSERT INTO user_emails (
|
||||||
user_id,
|
user_id,
|
||||||
email,
|
email,
|
||||||
|
@ -6,7 +6,7 @@ module Jobs
|
|||||||
def execute_onceoff(args)
|
def execute_onceoff(args)
|
||||||
return unless SiteSetting.enable_badges
|
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(*)
|
SELECT ub.user_id, MIN(granted_at) AS first_granted_at, COUNT(*)
|
||||||
FROM user_badges AS ub
|
FROM user_badges AS ub
|
||||||
WHERE ub.badge_id = #{Badge::Anniversary}
|
WHERE ub.badge_id = #{Badge::Anniversary}
|
||||||
@ -15,17 +15,17 @@ module Jobs
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
users.to_a.each do |u|
|
users.to_a.each do |u|
|
||||||
first = Time.zone.parse(u['first_granted_at'])
|
first = u.first_granted_at
|
||||||
badges = UserBadge.where(
|
badges = UserBadge.where(
|
||||||
"badge_id = ? AND user_id = ? AND granted_at > ?",
|
"badge_id = ? AND user_id = ? AND granted_at > ?",
|
||||||
Badge::Anniversary,
|
Badge::Anniversary,
|
||||||
u['user_id'],
|
u.user_id,
|
||||||
first
|
first
|
||||||
).order('granted_at')
|
).order('granted_at')
|
||||||
|
|
||||||
badges.each_with_index do |b, idx|
|
badges.each_with_index do |b, idx|
|
||||||
award_date = (first + (idx + 1).years)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
module Jobs
|
module Jobs
|
||||||
class InitCategoryTagStats < Jobs::Onceoff
|
class InitCategoryTagStats < Jobs::Onceoff
|
||||||
def execute_onceoff(args)
|
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)
|
INSERT INTO category_tag_stats (category_id, tag_id, topic_count)
|
||||||
SELECT topics.category_id, tags.id, COUNT(topics.id)
|
SELECT topics.category_id, tags.id, COUNT(topics.id)
|
||||||
FROM tags
|
FROM tags
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
module Jobs
|
module Jobs
|
||||||
class MigrateCensoredWords < Jobs::Onceoff
|
class MigrateCensoredWords < Jobs::Onceoff
|
||||||
def execute_onceoff(args)
|
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
|
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])
|
WatchedWord.create(word: word, action: WatchedWord.actions[:censor])
|
||||||
end
|
end
|
||||||
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)
|
AND EXISTS (SELECT 1 FROM user_visits visits WHERE visits.user_id = uv1.user_id AND visits.posts_read > 0 LIMIT 1)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
UserVisit.exec_sql(sql)
|
DB.exec(sql)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -60,7 +60,7 @@ module Jobs
|
|||||||
new_username: @new_username
|
new_username: @new_username
|
||||||
}
|
}
|
||||||
|
|
||||||
Notification.exec_sql(<<~SQL, params)
|
DB.exec(<<~SQL, params)
|
||||||
UPDATE notifications
|
UPDATE notifications
|
||||||
SET data = (data :: JSONB ||
|
SET data = (data :: JSONB ||
|
||||||
jsonb_strip_nulls(
|
jsonb_strip_nulls(
|
||||||
@ -88,7 +88,7 @@ module Jobs
|
|||||||
end
|
end
|
||||||
|
|
||||||
def update_post_custom_fields
|
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
|
UPDATE post_custom_fields
|
||||||
SET value = :new_username
|
SET value = :new_username
|
||||||
WHERE name = 'action_code_who' AND value = :old_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
|
WebCrawlerRequest.where('date < ?', WebCrawlerRequest.max_record_age.ago).delete_all
|
||||||
|
|
||||||
# keep count of only the top user agents
|
# keep count of only the top user agents
|
||||||
WebCrawlerRequest.exec_sql <<~SQL
|
DB.exec <<~SQL
|
||||||
WITH ranked_requests AS (
|
WITH ranked_requests AS (
|
||||||
SELECT row_number() OVER (ORDER BY count DESC) as row_number, id
|
SELECT row_number() OVER (ORDER BY count DESC) as row_number, id
|
||||||
FROM web_crawler_requests
|
FROM web_crawler_requests
|
||||||
|
@ -13,7 +13,7 @@ module Jobs
|
|||||||
fmt_end_date = end_date.iso8601(6)
|
fmt_end_date = end_date.iso8601(6)
|
||||||
fmt_start_date = start_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
|
SELECT u.id AS user_id
|
||||||
FROM users AS u
|
FROM users AS u
|
||||||
INNER JOIN posts AS p ON p.user_id = u.id
|
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
|
HAVING COUNT(p.id) > 0 AND COUNT(ub.id) = 0
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
user_ids = results.column_values(0)
|
|
||||||
results.clear
|
|
||||||
|
|
||||||
User.where(id: user_ids).find_each do |user|
|
User.where(id: user_ids).find_each do |user|
|
||||||
BadgeGranter.grant(badge, user, created_at: end_date)
|
BadgeGranter.grant(badge, user, created_at: end_date)
|
||||||
end
|
end
|
||||||
|
@ -55,7 +55,7 @@ module Jobs
|
|||||||
ELSE 1.0
|
ELSE 1.0
|
||||||
END
|
END
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END) / (5 + COUNT(DISTINCT p.id)) AS score
|
END) / (5 + COUNT(DISTINCT p.id))::float AS score
|
||||||
FROM users AS u
|
FROM users AS u
|
||||||
INNER JOIN user_stats AS us ON u.id = us.user_id
|
INNER JOIN user_stats AS us ON u.id = us.user_id
|
||||||
LEFT OUTER JOIN posts AS p ON p.user_id = u.id
|
LEFT OUTER JOIN posts AS p ON p.user_id = u.id
|
||||||
@ -82,10 +82,7 @@ module Jobs
|
|||||||
LIMIT #{MAX_AWARDED}
|
LIMIT #{MAX_AWARDED}
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
result = User.exec_sql(sql)
|
Hash[*DB.query_single(sql)]
|
||||||
rval = result.map { |r| [r['id'].to_i, r['score'].to_f] }.to_h
|
|
||||||
result.clear
|
|
||||||
rval
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -136,7 +136,7 @@ class Badge < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
exec_sql <<-SQL.squish
|
DB.exec <<~SQL
|
||||||
DELETE FROM user_badges
|
DELETE FROM user_badges
|
||||||
USING user_badges ub
|
USING user_badges ub
|
||||||
LEFT JOIN users u ON u.id = ub.user_id
|
LEFT JOIN users u ON u.id = ub.user_id
|
||||||
@ -144,7 +144,7 @@ class Badge < ActiveRecord::Base
|
|||||||
AND user_badges.id = ub.id
|
AND user_badges.id = ub.id
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
exec_sql <<-SQL.squish
|
DB.exec <<~SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT badge_id
|
SELECT badge_id
|
||||||
, COUNT(user_id) users
|
, COUNT(user_id) users
|
||||||
|
@ -143,14 +143,14 @@ class Category < ActiveRecord::Base
|
|||||||
.group("topics.category_id")
|
.group("topics.category_id")
|
||||||
.visible.to_sql
|
.visible.to_sql
|
||||||
|
|
||||||
Category.exec_sql <<-SQL
|
DB.exec <<~SQL
|
||||||
UPDATE categories c
|
UPDATE categories c
|
||||||
SET topic_count = x.topic_count,
|
SET topic_count = x.topic_count,
|
||||||
post_count = x.post_count
|
post_count = x.post_count
|
||||||
FROM (#{topics_with_post_count}) x
|
FROM (#{topics_with_post_count}) x
|
||||||
WHERE x.category_id = c.id
|
WHERE x.category_id = c.id
|
||||||
AND (c.topic_count <> x.topic_count OR c.post_count <> x.post_count)
|
AND (c.topic_count <> x.topic_count OR c.post_count <> x.post_count)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
# Yes, there are a lot of queries happening below.
|
# Yes, there are a lot of queries happening below.
|
||||||
# Performing a lot of queries is actually faster than using one big update
|
# Performing a lot of queries is actually faster than using one big update
|
||||||
|
@ -19,7 +19,7 @@ class CategoryTagStat < ActiveRecord::Base
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
tag_ids = topic.tags.map(&:id)
|
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|
|
(tag_ids - updated_tag_ids).each do |tag_id|
|
||||||
CategoryTagStat.create!(tag_id: tag_id, category_id: to_category_id, topic_count: 1)
|
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
|
# Recalculate all topic counts if they got out of sync
|
||||||
def self.update_topic_counts
|
def self.update_topic_counts
|
||||||
CategoryTagStat.exec_sql <<~SQL
|
DB.exec <<~SQL
|
||||||
UPDATE category_tag_stats stats
|
UPDATE category_tag_stats stats
|
||||||
SET topic_count = x.topic_count
|
SET topic_count = x.topic_count
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -155,14 +155,14 @@ SQL
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
exec_sql <<SQL
|
DB.exec <<~SQL
|
||||||
DELETE FROM category_users
|
DELETE FROM category_users
|
||||||
WHERE user_id IN (
|
WHERE user_id IN (
|
||||||
SELECT cu.user_id FROM category_users cu
|
SELECT cu.user_id FROM category_users cu
|
||||||
LEFT JOIN users u ON u.id = cu.user_id
|
LEFT JOIN users u ON u.id = cu.user_id
|
||||||
WHERE u.id IS NULL
|
WHERE u.id IS NULL
|
||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -12,13 +12,13 @@ module Positionable
|
|||||||
position = [[position_arg, 0].max, self.class.count - 1].min
|
position = [[position_arg, 0].max, self.class.count - 1].min
|
||||||
|
|
||||||
if self.position.nil? || position > (self.position)
|
if self.position.nil? || position > (self.position)
|
||||||
self.exec_sql "
|
DB.exec "
|
||||||
UPDATE #{self.class.table_name}
|
UPDATE #{self.class.table_name}
|
||||||
SET position = position - 1
|
SET position = position - 1
|
||||||
WHERE position > :current_position and position <= :new_position",
|
WHERE position > :current_position and position <= :new_position",
|
||||||
current_position: self.position, new_position: position
|
current_position: self.position, new_position: position
|
||||||
elsif position < self.position
|
elsif position < self.position
|
||||||
self.exec_sql "
|
DB.exec "
|
||||||
UPDATE #{self.class.table_name}
|
UPDATE #{self.class.table_name}
|
||||||
SET position = position + 1
|
SET position = position + 1
|
||||||
WHERE position >= :new_position and position < :current_position",
|
WHERE position >= :new_position and position < :current_position",
|
||||||
@ -28,7 +28,7 @@ module Positionable
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self.exec_sql "
|
DB.exec "
|
||||||
UPDATE #{self.class.table_name}
|
UPDATE #{self.class.table_name}
|
||||||
SET position = :position
|
SET position = :position
|
||||||
WHERE id = :id", id: id, position: position
|
WHERE id = :id", id: id, position: position
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class DirectoryItem < ActiveRecord::Base
|
class DirectoryItem < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
has_one :user_stat, foreign_key: :user_id, primary_key: :user_id
|
has_one :user_stat, foreign_key: :user_id, primary_key: :user_id
|
||||||
@ -42,7 +44,7 @@ class DirectoryItem < ActiveRecord::Base
|
|||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
# Delete records that belonged to users who have been deleted
|
# 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
|
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)
|
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
|
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]
|
di.period_type = :period_type", period_type: period_types[period_type]
|
||||||
|
|
||||||
# Create new records for users who don't have one yet
|
# 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
|
SELECT
|
||||||
:period_type,
|
:period_type,
|
||||||
u.id,
|
u.id,
|
||||||
@ -72,7 +74,7 @@ class DirectoryItem < ActiveRecord::Base
|
|||||||
# TODO
|
# TODO
|
||||||
# WARNING: post_count is a wrong name, it should be reply_count (excluding topic post)
|
# 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,
|
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 = :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,
|
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]
|
regular_post_type: Post.types[:regular]
|
||||||
|
|
||||||
if period_type == :all
|
if period_type == :all
|
||||||
exec_sql <<SQL
|
DB.exec <<~SQL
|
||||||
UPDATE user_stats s
|
UPDATE user_stats s
|
||||||
SET likes_given = d.likes_given,
|
SET likes_given = d.likes_given,
|
||||||
likes_received = d.likes_received,
|
likes_received = d.likes_received,
|
||||||
topic_count = d.topic_count,
|
topic_count = d.topic_count,
|
||||||
post_count = d.post_count
|
post_count = d.post_count
|
||||||
|
|
||||||
FROM directory_items d
|
FROM directory_items d
|
||||||
WHERE s.user_id = d.user_id AND
|
WHERE s.user_id = d.user_id AND
|
||||||
d.period_type = 1 AND
|
d.period_type = 1 AND
|
||||||
( s.likes_given <> d.likes_given OR
|
( s.likes_given <> d.likes_given OR
|
||||||
s.likes_received <> d.likes_received OR
|
s.likes_received <> d.likes_received OR
|
||||||
s.topic_count <> d.topic_count OR
|
s.topic_count <> d.topic_count OR
|
||||||
s.post_count <> d.post_count
|
s.post_count <> d.post_count
|
||||||
)
|
)
|
||||||
|
SQL
|
||||||
SQL
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Draft < ActiveRecord::Base
|
class Draft < ActiveRecord::Base
|
||||||
NEW_TOPIC = 'new_topic'
|
NEW_TOPIC = 'new_topic'
|
||||||
NEW_PRIVATE_MESSAGE = 'new_private_message'
|
NEW_PRIVATE_MESSAGE = 'new_private_message'
|
||||||
@ -7,7 +9,7 @@ class Draft < ActiveRecord::Base
|
|||||||
d = find_draft(user, key)
|
d = find_draft(user, key)
|
||||||
if d
|
if d
|
||||||
return if d.sequence > sequence
|
return if d.sequence > sequence
|
||||||
exec_sql("UPDATE drafts
|
DB.exec("UPDATE drafts
|
||||||
SET data = :data,
|
SET data = :data,
|
||||||
sequence = :sequence,
|
sequence = :sequence,
|
||||||
revisions = revisions + 1
|
revisions = revisions + 1
|
||||||
@ -15,6 +17,8 @@ class Draft < ActiveRecord::Base
|
|||||||
else
|
else
|
||||||
Draft.create!(user_id: user.id, draft_key: key, data: data, sequence: sequence)
|
Draft.create!(user_id: user.id, draft_key: key, data: data, sequence: sequence)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.get(user, key, sequence)
|
def self.get(user, key, sequence)
|
||||||
@ -40,7 +44,7 @@ class Draft < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.cleanup!
|
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
|
SELECT max(s.sequence) from draft_sequences s
|
||||||
WHERE s.draft_key = drafts.draft_key AND
|
WHERE s.draft_key = drafts.draft_key AND
|
||||||
s.user_id = drafts.user_id
|
s.user_id = drafts.user_id
|
||||||
|
@ -11,7 +11,7 @@ class DraftSequence < ActiveRecord::Base
|
|||||||
c.sequence ||= 0
|
c.sequence ||= 0
|
||||||
c.sequence += 1
|
c.sequence += 1
|
||||||
c.save!
|
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
|
c.sequence
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -22,8 +22,8 @@ class DraftSequence < ActiveRecord::Base
|
|||||||
user_id = user.id unless user.is_a?(Integer)
|
user_id = user.id unless user.is_a?(Integer)
|
||||||
|
|
||||||
# perf critical path
|
# perf critical path
|
||||||
r = exec_sql('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key).values
|
r, _ = DB.query_single('select sequence from draft_sequences where user_id = ? and draft_key = ?', user_id, key)
|
||||||
r.length.zero? ? 0 : r[0][0]
|
r.to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ class EmojiSetSiteSetting < EnumSiteSetting
|
|||||||
after = "/images/emoji/#{site_setting.value}/"
|
after = "/images/emoji/#{site_setting.value}/"
|
||||||
|
|
||||||
Scheduler::Defer.later("Fix Emoji Links") do
|
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,
|
before: before,
|
||||||
after: after,
|
after: after,
|
||||||
like: "%#{before}%"
|
like: "%#{before}%"
|
||||||
|
@ -296,7 +296,7 @@ class Group < ActiveRecord::Base
|
|||||||
"SELECT id FROM users WHERE id <= 0 OR trust_level < #{id - 10}"
|
"SELECT id FROM users WHERE id <= 0 OR trust_level < #{id - 10}"
|
||||||
end
|
end
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
DELETE FROM group_users
|
DELETE FROM group_users
|
||||||
USING (#{remove_subquery}) X
|
USING (#{remove_subquery}) X
|
||||||
WHERE group_id = #{group.id}
|
WHERE group_id = #{group.id}
|
||||||
@ -318,7 +318,7 @@ class Group < ActiveRecord::Base
|
|||||||
"SELECT id FROM users WHERE id > 0"
|
"SELECT id FROM users WHERE id > 0"
|
||||||
end
|
end
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO group_users (group_id, user_id, created_at, updated_at)
|
INSERT INTO group_users (group_id, user_id, created_at, updated_at)
|
||||||
SELECT #{group.id}, X.id, now(), now()
|
SELECT #{group.id}, X.id, now(), now()
|
||||||
FROM group_users
|
FROM group_users
|
||||||
@ -341,7 +341,7 @@ class Group < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.reset_all_counters!
|
def self.reset_all_counters!
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT group_id
|
SELECT group_id
|
||||||
, COUNT(user_id) users
|
, COUNT(user_id) users
|
||||||
@ -362,7 +362,7 @@ class Group < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.refresh_has_messages!
|
def self.refresh_has_messages!
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
UPDATE groups g SET has_messages = false
|
UPDATE groups g SET has_messages = false
|
||||||
WHERE NOT EXISTS (SELECT tg.id
|
WHERE NOT EXISTS (SELECT tg.id
|
||||||
FROM topic_allowed_groups tg
|
FROM topic_allowed_groups tg
|
||||||
@ -534,7 +534,7 @@ class Group < ActiveRecord::Base
|
|||||||
)
|
)
|
||||||
SQL
|
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 = {}
|
user_attributes = {}
|
||||||
|
|
||||||
@ -551,7 +551,7 @@ class Group < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
# update group user count
|
# update group user count
|
||||||
Group.exec_sql <<-SQL.squish
|
DB.exec <<~SQL
|
||||||
UPDATE groups g
|
UPDATE groups g
|
||||||
SET user_count =
|
SET user_count =
|
||||||
(SELECT COUNT(gu.user_id)
|
(SELECT COUNT(gu.user_id)
|
||||||
@ -605,9 +605,10 @@ class Group < ActiveRecord::Base
|
|||||||
name_lower = self.name.downcase
|
name_lower = self.name.downcase
|
||||||
|
|
||||||
if self.will_save_change_to_name? && self.name_was&.downcase != name_lower
|
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
|
User::USERNAME_EXISTS_SQL, username: name_lower
|
||||||
).values.present?
|
) > 0
|
||||||
|
|
||||||
if existing
|
if existing
|
||||||
errors.add(:name, I18n.t("activerecord.errors.messages.taken"))
|
errors.add(:name, I18n.t("activerecord.errors.messages.taken"))
|
||||||
@ -649,15 +650,15 @@ class Group < ActiveRecord::Base
|
|||||||
return if new_record? && !self.title.present?
|
return if new_record? && !self.title.present?
|
||||||
|
|
||||||
if self.saved_change_to_title?
|
if self.saved_change_to_title?
|
||||||
sql = <<-SQL.squish
|
sql = <<~SQL
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET title = :title
|
SET title = :title
|
||||||
WHERE (title = :title_was OR title = '' OR title IS NULL)
|
WHERE (title = :title_was OR title = '' OR title IS NULL)
|
||||||
AND COALESCE(title,'') <> COALESCE(:title,'')
|
AND COALESCE(title,'') <> COALESCE(:title,'')
|
||||||
AND id IN (SELECT user_id FROM group_users WHERE group_id = :id)
|
AND id IN (SELECT user_id FROM group_users WHERE group_id = :id)
|
||||||
SQL
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -666,18 +667,19 @@ class Group < ActiveRecord::Base
|
|||||||
|
|
||||||
if self.saved_change_to_primary_group?
|
if self.saved_change_to_primary_group?
|
||||||
sql = <<~SQL
|
sql = <<~SQL
|
||||||
UPDATE users
|
UPDATE users
|
||||||
/*set*/
|
/*set*/
|
||||||
/*where*/
|
/*where*/
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
builder = SqlBuilder.new(sql)
|
builder = DB.build(sql)
|
||||||
builder.where("
|
builder.where(<<~SQL, id: id)
|
||||||
id IN (
|
id IN (
|
||||||
SELECT user_id
|
SELECT user_id
|
||||||
FROM group_users
|
FROM group_users
|
||||||
WHERE group_id = :id
|
WHERE group_id = :id
|
||||||
)", id: id)
|
)
|
||||||
|
SQL
|
||||||
|
|
||||||
if primary_group
|
if primary_group
|
||||||
builder.set("primary_group_id = :id")
|
builder.set("primary_group_id = :id")
|
||||||
|
@ -25,7 +25,7 @@ class GroupUser < ActiveRecord::Base
|
|||||||
|
|
||||||
def set_primary_group
|
def set_primary_group
|
||||||
if group.primary_group
|
if group.primary_group
|
||||||
self.class.exec_sql("
|
DB.exec("
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET primary_group_id = :id
|
SET primary_group_id = :id
|
||||||
WHERE id = :user_id",
|
WHERE id = :user_id",
|
||||||
@ -35,7 +35,7 @@ class GroupUser < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def remove_primary_group
|
def remove_primary_group
|
||||||
self.class.exec_sql("
|
DB.exec("
|
||||||
UPDATE users
|
UPDATE users
|
||||||
SET primary_group_id = NULL
|
SET primary_group_id = NULL
|
||||||
WHERE id = :user_id AND primary_group_id = :id",
|
WHERE id = :user_id AND primary_group_id = :id",
|
||||||
@ -45,7 +45,7 @@ class GroupUser < ActiveRecord::Base
|
|||||||
|
|
||||||
def remove_title
|
def remove_title
|
||||||
if group.title.present?
|
if group.title.present?
|
||||||
self.class.exec_sql("
|
DB.exec("
|
||||||
UPDATE users SET title = NULL
|
UPDATE users SET title = NULL
|
||||||
WHERE title = :title AND id = :id",
|
WHERE title = :title AND id = :id",
|
||||||
id: user_id, title: group.title
|
id: user_id, title: group.title
|
||||||
@ -55,7 +55,7 @@ class GroupUser < ActiveRecord::Base
|
|||||||
|
|
||||||
def update_title
|
def update_title
|
||||||
if group.title.present?
|
if group.title.present?
|
||||||
self.class.exec_sql("
|
DB.exec("
|
||||||
UPDATE users SET title = :title
|
UPDATE users SET title = :title
|
||||||
WHERE (title IS NULL OR title = '') AND id = :id",
|
WHERE (title IS NULL OR title = '') AND id = :id",
|
||||||
id: user_id, title: group.title
|
id: user_id, title: group.title
|
||||||
|
@ -89,10 +89,10 @@ class IncomingLink < ActiveRecord::Base
|
|||||||
|
|
||||||
# Internal: Update appropriate link counts.
|
# Internal: Update appropriate link counts.
|
||||||
def update_link_counts
|
def update_link_counts
|
||||||
exec_sql("UPDATE topics
|
DB.exec("UPDATE topics
|
||||||
SET incoming_link_count = incoming_link_count + 1
|
SET incoming_link_count = incoming_link_count + 1
|
||||||
WHERE id = (SELECT topic_id FROM posts where id = ?)", post_id)
|
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
|
SET incoming_link_count = incoming_link_count + 1
|
||||||
WHERE id = ?", post_id)
|
WHERE id = ?", post_id)
|
||||||
end
|
end
|
||||||
|
@ -19,10 +19,10 @@ class Notification < ActiveRecord::Base
|
|||||||
after_commit :refresh_notification_count, on: [:create, :update, :destroy]
|
after_commit :refresh_notification_count, on: [:create, :update, :destroy]
|
||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
Notification.exec_sql <<-SQL
|
DB.exec(<<~SQL, Notification.types[:private_message])
|
||||||
DELETE
|
DELETE
|
||||||
FROM notifications n
|
FROM notifications n
|
||||||
WHERE notification_type = #{Notification.types[:private_message]}
|
WHERE notification_type = ?
|
||||||
AND NOT EXISTS (
|
AND NOT EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM posts p
|
FROM posts p
|
||||||
@ -152,17 +152,15 @@ class Notification < ActiveRecord::Base
|
|||||||
|
|
||||||
if notifications.present?
|
if notifications.present?
|
||||||
|
|
||||||
ids = Notification.exec_sql("
|
ids = DB.query_single(<<~SQL, count.to_i)
|
||||||
SELECT n.id FROM notifications n
|
SELECT n.id FROM notifications n
|
||||||
WHERE
|
WHERE
|
||||||
n.notification_type = 6 AND
|
n.notification_type = 6 AND
|
||||||
n.user_id = #{user.id.to_i} AND
|
n.user_id = #{user.id.to_i} AND
|
||||||
NOT read
|
NOT read
|
||||||
ORDER BY n.id ASC
|
ORDER BY n.id ASC
|
||||||
LIMIT #{count.to_i}
|
LIMIT ?
|
||||||
").values.map do |x, _|
|
SQL
|
||||||
x.to_i
|
|
||||||
end
|
|
||||||
|
|
||||||
if ids.length > 0
|
if ids.length > 0
|
||||||
notifications += user
|
notifications += user
|
||||||
|
@ -669,17 +669,19 @@ class Post < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def reply_history(max_replies = 100, guardian = nil)
|
def reply_history(max_replies = 100, guardian = nil)
|
||||||
post_ids = Post.exec_sql("WITH RECURSIVE breadcrumb(id, reply_to_post_number) AS (
|
post_ids = DB.query_single(<<~SQL, post_id: id, topic_id: topic_id)
|
||||||
SELECT p.id, p.reply_to_post_number FROM posts AS p
|
WITH RECURSIVE breadcrumb(id, reply_to_post_number) AS (
|
||||||
WHERE p.id = :post_id
|
SELECT p.id, p.reply_to_post_number FROM posts AS p
|
||||||
UNION
|
WHERE p.id = :post_id
|
||||||
SELECT p.id, p.reply_to_post_number FROM posts AS p, breadcrumb
|
UNION
|
||||||
WHERE breadcrumb.reply_to_post_number = p.post_number
|
SELECT p.id, p.reply_to_post_number FROM posts AS p, breadcrumb
|
||||||
AND p.topic_id = :topic_id
|
WHERE breadcrumb.reply_to_post_number = p.post_number
|
||||||
) SELECT id from breadcrumb ORDER by id", post_id: id, topic_id: topic_id).to_a
|
AND p.topic_id = :topic_id
|
||||||
|
)
|
||||||
post_ids.map! { |r| r['id'].to_i }
|
SELECT id from breadcrumb
|
||||||
.reject! { |post_id| post_id == id }
|
WHERE id <> :post_id
|
||||||
|
ORDER by id
|
||||||
|
SQL
|
||||||
|
|
||||||
# [1,2,3][-10,-1] => nil
|
# [1,2,3][-10,-1] => nil
|
||||||
post_ids = (post_ids[(0 - max_replies)..-1] || post_ids)
|
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)
|
def self.rebake_all_quoted_posts(user_id)
|
||||||
return if user_id.blank?
|
return if user_id.blank?
|
||||||
|
|
||||||
Post.exec_sql <<-SQL
|
DB.exec(<<~SQL, user_id)
|
||||||
WITH user_quoted_posts AS (
|
WITH user_quoted_posts AS (
|
||||||
SELECT post_id
|
SELECT post_id
|
||||||
FROM quoted_posts
|
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
|
UPDATE posts
|
||||||
SET baked_version = NULL
|
SET baked_version = NULL
|
||||||
|
@ -340,7 +340,7 @@ SQL
|
|||||||
def self.copy(original_post, target_post)
|
def self.copy(original_post, target_post)
|
||||||
cols_to_copy = (column_names - %w{id post_id}).join(', ')
|
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})
|
INSERT INTO post_actions(post_id, #{cols_to_copy})
|
||||||
SELECT #{target_post.id}, #{cols_to_copy}
|
SELECT #{target_post.id}, #{cols_to_copy}
|
||||||
FROM post_actions
|
FROM post_actions
|
||||||
@ -425,26 +425,29 @@ SQL
|
|||||||
# Returns the flag counts for a post, taking into account that some users
|
# Returns the flag counts for a post, taking into account that some users
|
||||||
# can weigh flags differently.
|
# can weigh flags differently.
|
||||||
def self.flag_counts_for(post_id)
|
def self.flag_counts_for(post_id)
|
||||||
flag_counts = exec_sql("SELECT SUM(CASE
|
params = {
|
||||||
WHEN pa.disagreed_at IS NULL AND pa.staff_took_action THEN :flags_required_to_hide_post
|
post_id: post_id,
|
||||||
WHEN pa.disagreed_at IS NULL AND NOT pa.staff_took_action THEN 1
|
post_action_types: PostActionType.auto_action_flag_types.values,
|
||||||
ELSE 0
|
flags_required_to_hide_post: SiteSetting.flags_required_to_hide_post
|
||||||
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
|
|
||||||
|
|
||||||
[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
|
end
|
||||||
|
|
||||||
def post_action_type_key
|
def post_action_type_key
|
||||||
|
@ -10,7 +10,7 @@ class PostRevision < ActiveRecord::Base
|
|||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
# 1 - fix the numbers
|
# 1 - fix the numbers
|
||||||
PostRevision.exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
UPDATE post_revisions
|
UPDATE post_revisions
|
||||||
SET number = pr.rank
|
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
|
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
|
SQL
|
||||||
|
|
||||||
# 2 - fix the versions on the posts
|
# 2 - fix the versions on the posts
|
||||||
PostRevision.exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
UPDATE posts
|
UPDATE posts
|
||||||
SET version = 1 + (SELECT COUNT(*) FROM post_revisions WHERE post_id = posts.id),
|
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')
|
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)
|
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
|
# 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
|
SELECT :topic_id, user_id, :pretend_read_post_number, 1
|
||||||
FROM post_timings pt
|
FROM post_timings pt
|
||||||
WHERE topic_id = :topic_id AND
|
WHERE topic_id = :topic_id AND
|
||||||
@ -34,7 +34,7 @@ class PostTiming < ActiveRecord::Base
|
|||||||
|
|
||||||
def self.record_new_timing(args)
|
def self.record_new_timing(args)
|
||||||
begin
|
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
|
SELECT :topic_id, :user_id, :post_number, :msecs
|
||||||
WHERE NOT EXISTS(SELECT 1 FROM post_timings
|
WHERE NOT EXISTS(SELECT 1 FROM post_timings
|
||||||
WHERE topic_id = :topic_id
|
WHERE topic_id = :topic_id
|
||||||
@ -53,12 +53,13 @@ class PostTiming < ActiveRecord::Base
|
|||||||
|
|
||||||
# Increases a timer if a row exists, otherwise create it
|
# Increases a timer if a row exists, otherwise create it
|
||||||
def self.record_timing(args)
|
def self.record_timing(args)
|
||||||
rows = exec_sql_row_count("UPDATE post_timings
|
rows = DB.exec(<<~SQL, args)
|
||||||
SET msecs = msecs + :msecs
|
UPDATE post_timings
|
||||||
WHERE topic_id = :topic_id
|
SET msecs = msecs + :msecs
|
||||||
AND user_id = :user_id
|
WHERE topic_id = :topic_id
|
||||||
AND post_number = :post_number",
|
AND user_id = :user_id
|
||||||
args)
|
AND post_number = :post_number
|
||||||
|
SQL
|
||||||
|
|
||||||
record_new_timing(args) if rows == 0
|
record_new_timing(args) if rows == 0
|
||||||
end
|
end
|
||||||
@ -115,9 +116,7 @@ class PostTiming < ActiveRecord::Base
|
|||||||
RETURNING x.idx
|
RETURNING x.idx
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
result = exec_sql(sql)
|
existing = Set.new(DB.query_single(sql))
|
||||||
result.type_map = SqlBuilder.pg_type_map
|
|
||||||
existing = Set.new(result.column_values(0))
|
|
||||||
|
|
||||||
sql = <<~SQL
|
sql = <<~SQL
|
||||||
SELECT 1 FROM topics
|
SELECT 1 FROM topics
|
||||||
@ -126,7 +125,7 @@ SQL
|
|||||||
id = :topic_id
|
id = :topic_id
|
||||||
SQL
|
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
|
new_posts_read = timings.size - existing.size if is_regular
|
||||||
|
|
||||||
timings.each_with_index do |(post_number, time), index|
|
timings.each_with_index do |(post_number, time), index|
|
||||||
|
@ -11,7 +11,7 @@ class QuotedPost < ActiveRecord::Base
|
|||||||
|
|
||||||
uniq = {}
|
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|
|
doc.css("aside.quote[data-topic]").each do |a|
|
||||||
topic_id = a['data-topic'].to_i
|
topic_id = a['data-topic'].to_i
|
||||||
@ -23,7 +23,7 @@ class QuotedPost < ActiveRecord::Base
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
# It would be so much nicer if we used post_id in quotes
|
# 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)
|
INSERT INTO quoted_posts (post_id, quoted_post_id, created_at, updated_at)
|
||||||
SELECT :post_id, p.id, current_timestamp, current_timestamp
|
SELECT :post_id, p.id, current_timestamp, current_timestamp
|
||||||
FROM posts p
|
FROM posts p
|
||||||
|
@ -94,8 +94,8 @@ class ScreenedIpAddress < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.star_subnets_query
|
def self.star_subnets_query
|
||||||
@star_subnets_query ||= <<-SQL
|
@star_subnets_query ||= <<~SQL
|
||||||
SELECT network(inet(host(ip_address) || '/24')) AS ip_range
|
SELECT network(inet(host(ip_address) || '/24'))::text AS ip_range
|
||||||
FROM screened_ip_addresses
|
FROM screened_ip_addresses
|
||||||
WHERE action_type = #{ScreenedIpAddress.actions[:block]}
|
WHERE action_type = #{ScreenedIpAddress.actions[:block]}
|
||||||
AND family(ip_address) = 4
|
AND family(ip_address) = 4
|
||||||
@ -106,9 +106,9 @@ class ScreenedIpAddress < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.star_star_subnets_query
|
def self.star_star_subnets_query
|
||||||
@star_star_subnets_query ||= <<-SQL
|
@star_star_subnets_query ||= <<~SQL
|
||||||
WITH weighted_subnets AS (
|
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)
|
CASE masklen(ip_address)
|
||||||
WHEN 32 THEN 1
|
WHEN 32 THEN 1
|
||||||
WHEN 24 THEN :roll_up_weight
|
WHEN 24 THEN :roll_up_weight
|
||||||
@ -127,12 +127,12 @@ class ScreenedIpAddress < ActiveRecord::Base
|
|||||||
|
|
||||||
def self.star_subnets
|
def self.star_subnets
|
||||||
min_count = SiteSetting.min_ban_entries_for_roll_up
|
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
|
end
|
||||||
|
|
||||||
def self.star_star_subnets
|
def self.star_star_subnets
|
||||||
weight = SiteSetting.min_ban_entries_for_roll_up
|
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
|
end
|
||||||
|
|
||||||
def self.roll_up(current_user = Discourse.system_user)
|
def self.roll_up(current_user = Discourse.system_user)
|
||||||
@ -143,7 +143,7 @@ class ScreenedIpAddress < ActiveRecord::Base
|
|||||||
subnets.each do |subnet|
|
subnets.each do |subnet|
|
||||||
ScreenedIpAddress.create(ip_address: subnet) unless ScreenedIpAddress.where("? <<= ip_address", subnet).exists?
|
ScreenedIpAddress.create(ip_address: subnet) unless ScreenedIpAddress.where("? <<= ip_address", subnet).exists?
|
||||||
|
|
||||||
sql = <<-SQL
|
sql = <<~SQL
|
||||||
UPDATE screened_ip_addresses
|
UPDATE screened_ip_addresses
|
||||||
SET match_count = sum_match_count
|
SET match_count = sum_match_count
|
||||||
, created_at = min_created_at
|
, created_at = min_created_at
|
||||||
@ -160,7 +160,7 @@ class ScreenedIpAddress < ActiveRecord::Base
|
|||||||
WHERE ip_address = :ip_address
|
WHERE ip_address = :ip_address
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
ScreenedIpAddress.exec_sql(sql, ip_address: subnet)
|
DB.exec(sql, ip_address: subnet)
|
||||||
|
|
||||||
ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block])
|
ScreenedIpAddress.where(action_type: ScreenedIpAddress.actions[:block])
|
||||||
.where("family(ip_address) = 4")
|
.where("family(ip_address) = 4")
|
||||||
|
@ -24,7 +24,7 @@ class StylesheetCache < ActiveRecord::Base
|
|||||||
.pluck(:id)
|
.pluck(:id)
|
||||||
.last
|
.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
|
end
|
||||||
|
|
||||||
success
|
success
|
||||||
|
@ -25,7 +25,7 @@ class Tag < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.update_topic_counts
|
def self.update_topic_counts
|
||||||
Tag.exec_sql <<~SQL
|
DB.exec <<~SQL
|
||||||
UPDATE tags t
|
UPDATE tags t
|
||||||
SET topic_count = x.topic_count
|
SET topic_count = x.topic_count
|
||||||
FROM (
|
FROM (
|
||||||
@ -41,7 +41,7 @@ class Tag < ActiveRecord::Base
|
|||||||
AND x.topic_count <> t.topic_count
|
AND x.topic_count <> t.topic_count
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Tag.exec_sql <<~SQL
|
DB.exec <<~SQL
|
||||||
UPDATE tags t
|
UPDATE tags t
|
||||||
SET pm_topic_count = x.pm_topic_count
|
SET pm_topic_count = x.pm_topic_count
|
||||||
FROM (
|
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})"
|
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
|
SELECT tags.name as tag_name, SUM(stats.topic_count) AS sum_topic_count
|
||||||
FROM category_tag_stats stats
|
FROM category_tag_stats stats
|
||||||
JOIN tags ON stats.tag_id = tags.id AND stats.topic_count > 0
|
JOIN tags ON stats.tag_id = tags.id AND stats.topic_count > 0
|
||||||
@ -81,7 +81,7 @@ class Tag < ActiveRecord::Base
|
|||||||
LIMIT #{limit}
|
LIMIT #{limit}
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
tag_names_with_counts.map { |row| row['tag_name'] }
|
tag_names_with_counts.map { |row| row.tag_name }
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.pm_tags(limit_arg: nil, guardian: nil, allowed_user: nil)
|
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
|
limit = limit_arg || SiteSetting.max_tags_in_filter_list
|
||||||
user_id = allowed_user.id
|
user_id = allowed_user.id
|
||||||
|
|
||||||
tag_names_with_counts = Tag.exec_sql <<~SQL
|
DB.query_hash(<<~SQL).map!(&:symbolize_keys!)
|
||||||
SELECT tags.name, COUNT(topics.id) AS topic_count
|
SELECT tags.name as id, tags.name as text, COUNT(topics.id) AS count
|
||||||
FROM tags
|
FROM tags
|
||||||
JOIN topic_tags ON tags.id = topic_tags.tag_id
|
JOIN topic_tags ON tags.id = topic_tags.tag_id
|
||||||
JOIN topics ON topics.id = topic_tags.topic_id
|
JOIN topics ON topics.id = topic_tags.topic_id
|
||||||
@ -109,8 +109,6 @@ class Tag < ActiveRecord::Base
|
|||||||
GROUP BY tags.name
|
GROUP BY tags.name
|
||||||
LIMIT #{limit}
|
LIMIT #{limit}
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
tag_names_with_counts.map { |t| { id: t['name'], text: t['name'], count: t['topic_count'] } }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.include_tags?
|
def self.include_tags?
|
||||||
|
@ -59,7 +59,7 @@ class TopTopic < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.remove_invisible_topics
|
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
|
SELECT COALESCE(topic_id, 0) AS id FROM categories
|
||||||
), invisible_topic_ids AS (
|
), invisible_topic_ids AS (
|
||||||
SELECT id
|
SELECT id
|
||||||
@ -76,7 +76,7 @@ class TopTopic < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.add_new_visible_topics
|
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
|
SELECT COALESCE(topic_id, 0) AS id FROM categories
|
||||||
), visible_topics AS (
|
), visible_topics AS (
|
||||||
SELECT t.id
|
SELECT t.id
|
||||||
@ -167,7 +167,7 @@ class TopTopic < ActiveRecord::Base
|
|||||||
time_filter = "topics.created_at < :from"
|
time_filter = "topics.created_at < :from"
|
||||||
end
|
end
|
||||||
|
|
||||||
sql = <<-SQL
|
sql = <<~SQL
|
||||||
WITH top AS (
|
WITH top AS (
|
||||||
SELECT CASE
|
SELECT CASE
|
||||||
WHEN #{time_filter} THEN 0
|
WHEN #{time_filter} THEN 0
|
||||||
@ -197,7 +197,7 @@ class TopTopic < ActiveRecord::Base
|
|||||||
AND #{period}_score <> top.score
|
AND #{period}_score <> top.score
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
exec_sql(sql, from: start_of(period))
|
DB.exec(sql, from: start_of(period))
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.start_of(period)
|
def self.start_of(period)
|
||||||
@ -211,7 +211,7 @@ class TopTopic < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.update_top_topics(period, sort, inner_join)
|
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
|
SET #{period}_#{sort}_count = c.count
|
||||||
FROM top_topics tt
|
FROM top_topics tt
|
||||||
INNER JOIN (#{inner_join}) c ON tt.topic_id = c.topic_id
|
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?
|
if !new_record? && !Discourse.readonly_mode?
|
||||||
# make sure data is set in table, this also allows us to change algorithm
|
# make sure data is set in table, this also allows us to change algorithm
|
||||||
# by simply nulling this column
|
# 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
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -522,130 +522,140 @@ class Topic < ActiveRecord::Base
|
|||||||
|
|
||||||
# Atomically creates the next post number
|
# Atomically creates the next post number
|
||||||
def self.next_post_number(topic_id, reply = false, whisper = false)
|
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
|
if whisper
|
||||||
|
|
||||||
result = exec_sql("UPDATE topics
|
result = DB.query_single(<<~SQL, highest, topic_id)
|
||||||
SET highest_staff_post_number = ? + 1
|
UPDATE topics
|
||||||
WHERE id = ?
|
SET highest_staff_post_number = ? + 1
|
||||||
RETURNING highest_staff_post_number", highest, topic_id)
|
WHERE id = ?
|
||||||
|
RETURNING highest_staff_post_number
|
||||||
|
SQL
|
||||||
|
|
||||||
result.first['highest_staff_post_number'].to_i
|
result.first.to_i
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
reply_sql = reply ? ", reply_count = reply_count + 1" : ""
|
reply_sql = reply ? ", reply_count = reply_count + 1" : ""
|
||||||
|
|
||||||
result = exec_sql("UPDATE topics
|
result = DB.query_single(<<~SQL, highest: highest, topic_id: topic_id)
|
||||||
SET highest_staff_post_number = :highest + 1,
|
UPDATE topics
|
||||||
highest_post_number = :highest + 1#{reply_sql},
|
SET highest_staff_post_number = :highest + 1,
|
||||||
posts_count = posts_count + 1
|
highest_post_number = :highest + 1#{reply_sql},
|
||||||
WHERE id = :topic_id
|
posts_count = posts_count + 1
|
||||||
RETURNING highest_post_number", highest: highest, topic_id: topic_id)
|
WHERE id = :topic_id
|
||||||
|
RETURNING highest_post_number
|
||||||
|
SQL
|
||||||
|
|
||||||
result.first['highest_post_number'].to_i
|
result.first.to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.reset_all_highest!
|
def self.reset_all_highest!
|
||||||
exec_sql <<SQL
|
DB.exec <<~SQL
|
||||||
WITH
|
WITH
|
||||||
X as (
|
X as (
|
||||||
SELECT topic_id,
|
SELECT topic_id,
|
||||||
COALESCE(MAX(post_number), 0) highest_post_number
|
COALESCE(MAX(post_number), 0) highest_post_number
|
||||||
FROM posts
|
FROM posts
|
||||||
WHERE deleted_at IS NULL
|
WHERE deleted_at IS NULL
|
||||||
GROUP BY topic_id
|
GROUP BY topic_id
|
||||||
),
|
),
|
||||||
Y as (
|
Y as (
|
||||||
SELECT topic_id,
|
SELECT topic_id,
|
||||||
coalesce(MAX(post_number), 0) highest_post_number,
|
coalesce(MAX(post_number), 0) highest_post_number,
|
||||||
count(*) posts_count,
|
count(*) posts_count,
|
||||||
max(created_at) last_posted_at
|
max(created_at) last_posted_at
|
||||||
FROM posts
|
FROM posts
|
||||||
WHERE deleted_at IS NULL AND post_type <> 4
|
WHERE deleted_at IS NULL AND post_type <> 4
|
||||||
GROUP BY topic_id
|
GROUP BY topic_id
|
||||||
)
|
)
|
||||||
UPDATE topics
|
UPDATE topics
|
||||||
SET
|
SET
|
||||||
highest_staff_post_number = X.highest_post_number,
|
highest_staff_post_number = X.highest_post_number,
|
||||||
highest_post_number = Y.highest_post_number,
|
highest_post_number = Y.highest_post_number,
|
||||||
last_posted_at = Y.last_posted_at,
|
last_posted_at = Y.last_posted_at,
|
||||||
posts_count = Y.posts_count
|
posts_count = Y.posts_count
|
||||||
FROM X, Y
|
FROM X, Y
|
||||||
WHERE
|
WHERE
|
||||||
X.topic_id = topics.id AND
|
X.topic_id = topics.id AND
|
||||||
Y.topic_id = topics.id AND (
|
Y.topic_id = topics.id AND (
|
||||||
topics.highest_staff_post_number <> X.highest_post_number OR
|
topics.highest_staff_post_number <> X.highest_post_number OR
|
||||||
topics.highest_post_number <> Y.highest_post_number OR
|
topics.highest_post_number <> Y.highest_post_number OR
|
||||||
topics.last_posted_at <> Y.last_posted_at OR
|
topics.last_posted_at <> Y.last_posted_at OR
|
||||||
topics.posts_count <> Y.posts_count
|
topics.posts_count <> Y.posts_count
|
||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
# If a post is deleted we have to update our highest post counters
|
# If a post is deleted we have to update our highest post counters
|
||||||
def self.reset_highest(topic_id)
|
def self.reset_highest(topic_id)
|
||||||
result = exec_sql "UPDATE topics
|
result = DB.query_single(<<~SQL, topic_id: topic_id)
|
||||||
SET
|
UPDATE topics
|
||||||
highest_staff_post_number = (
|
SET
|
||||||
SELECT COALESCE(MAX(post_number), 0) FROM posts
|
highest_staff_post_number = (
|
||||||
WHERE topic_id = :topic_id AND
|
SELECT COALESCE(MAX(post_number), 0) FROM posts
|
||||||
deleted_at IS NULL
|
WHERE topic_id = :topic_id AND
|
||||||
),
|
deleted_at IS NULL
|
||||||
highest_post_number = (
|
),
|
||||||
SELECT COALESCE(MAX(post_number), 0) FROM posts
|
highest_post_number = (
|
||||||
WHERE topic_id = :topic_id AND
|
SELECT COALESCE(MAX(post_number), 0) FROM posts
|
||||||
deleted_at IS NULL AND
|
WHERE topic_id = :topic_id AND
|
||||||
post_type <> 4
|
deleted_at IS NULL AND
|
||||||
),
|
post_type <> 4
|
||||||
posts_count = (
|
),
|
||||||
SELECT count(*) FROM posts
|
posts_count = (
|
||||||
WHERE deleted_at IS NULL AND
|
SELECT count(*) FROM posts
|
||||||
topic_id = :topic_id AND
|
WHERE deleted_at IS NULL AND
|
||||||
post_type <> 4
|
topic_id = :topic_id AND
|
||||||
),
|
post_type <> 4
|
||||||
|
),
|
||||||
|
|
||||||
last_posted_at = (
|
last_posted_at = (
|
||||||
SELECT MAX(created_at) FROM posts
|
SELECT MAX(created_at) FROM posts
|
||||||
WHERE topic_id = :topic_id AND
|
WHERE topic_id = :topic_id AND
|
||||||
deleted_at IS NULL AND
|
deleted_at IS NULL AND
|
||||||
post_type <> 4
|
post_type <> 4
|
||||||
)
|
)
|
||||||
WHERE id = :topic_id
|
WHERE id = :topic_id
|
||||||
RETURNING highest_post_number", topic_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
|
# Update the forum topic user records
|
||||||
exec_sql "UPDATE topic_users
|
DB.exec(<<~SQL, highest: highest_post_number, topic_id: topic_id)
|
||||||
SET last_read_post_number = CASE
|
UPDATE topic_users
|
||||||
WHEN last_read_post_number > :highest THEN :highest
|
SET last_read_post_number = CASE
|
||||||
ELSE last_read_post_number
|
WHEN last_read_post_number > :highest THEN :highest
|
||||||
END,
|
ELSE last_read_post_number
|
||||||
highest_seen_post_number = CASE
|
END,
|
||||||
WHEN highest_seen_post_number > :highest THEN :highest
|
highest_seen_post_number = CASE
|
||||||
ELSE highest_seen_post_number
|
WHEN highest_seen_post_number > :highest THEN :highest
|
||||||
END
|
ELSE highest_seen_post_number
|
||||||
WHERE topic_id = :topic_id",
|
END
|
||||||
highest: highest_post_number,
|
WHERE topic_id = :topic_id
|
||||||
topic_id: topic_id
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
# This calculates the geometric mean of the posts and stores it with the topic
|
# This calculates the geometric mean of the posts and stores it with the topic
|
||||||
def self.calculate_avg_time(min_topic_age = nil)
|
def self.calculate_avg_time(min_topic_age = nil)
|
||||||
builder = SqlBuilder.new("UPDATE topics
|
builder = DB.build <<~SQL
|
||||||
SET avg_time = x.gmean
|
UPDATE topics
|
||||||
FROM (SELECT topic_id,
|
SET avg_time = x.gmean
|
||||||
round(exp(avg(ln(avg_time)))) AS gmean
|
FROM (SELECT topic_id,
|
||||||
FROM posts
|
round(exp(avg(ln(avg_time)))) AS gmean
|
||||||
WHERE avg_time > 0 AND avg_time IS NOT NULL
|
FROM posts
|
||||||
GROUP BY topic_id) AS x
|
WHERE avg_time > 0 AND avg_time IS NOT NULL
|
||||||
/*where*/")
|
GROUP BY topic_id) AS x
|
||||||
|
/*where*/
|
||||||
|
SQL
|
||||||
|
|
||||||
builder.where("x.topic_id = topics.id AND
|
builder.where <<~SQL
|
||||||
(topics.avg_time <> x.gmean OR topics.avg_time IS NULL)")
|
x.topic_id = topics.id AND
|
||||||
|
(topics.avg_time <> x.gmean OR topics.avg_time IS NULL)
|
||||||
|
SQL
|
||||||
|
|
||||||
if min_topic_age
|
if min_topic_age
|
||||||
builder.where("topics.bumped_at > :bumped_at", bumped_at: 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
|
# OR if you have it archived as a user explicitly
|
||||||
|
|
||||||
sql = <<~SQL
|
sql = <<~SQL
|
||||||
SELECT 1
|
SELECT 1
|
||||||
WHERE
|
WHERE
|
||||||
(
|
(
|
||||||
SELECT count(*) FROM topic_allowed_groups tg
|
SELECT count(*) FROM topic_allowed_groups tg
|
||||||
JOIN group_archived_messages gm
|
JOIN group_archived_messages gm
|
||||||
ON gm.topic_id = tg.topic_id AND
|
ON gm.topic_id = tg.topic_id AND
|
||||||
gm.group_id = tg.group_id
|
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)
|
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
|
AND tg.topic_id = :topic_id
|
||||||
) =
|
) =
|
||||||
(
|
(
|
||||||
SELECT case when count(*) = 0 then -1 else count(*) end FROM topic_allowed_groups tg
|
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)
|
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
|
AND tg.topic_id = :topic_id
|
||||||
)
|
)
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
SELECT 1 FROM topic_allowed_users tu
|
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
|
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
|
WHERE tu.user_id = :user_id AND tu.topic_id = :topic_id
|
||||||
SQL
|
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
|
end
|
||||||
|
|
||||||
TIME_TO_FIRST_RESPONSE_SQL ||= <<-SQL
|
TIME_TO_FIRST_RESPONSE_SQL ||= <<-SQL
|
||||||
@ -1325,8 +1335,8 @@ SQL
|
|||||||
) = 1
|
) = 1
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
result = Topic.exec_sql(sql, private_message: Archetype.private_message, topic_id: self.id)
|
result = DB.exec(sql, private_message: Archetype.private_message, topic_id: self.id)
|
||||||
result.ntuples != 0
|
result != 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def featured_link_root_domain
|
def featured_link_root_domain
|
||||||
|
@ -79,7 +79,7 @@ WHERE tt.id = tt2.id AND
|
|||||||
#{filter2}
|
#{filter2}
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Topic.exec_sql(sql)
|
DB.exec(sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -38,7 +38,7 @@ class TopicLink < ActiveRecord::Base
|
|||||||
def self.topic_map(guardian, topic_id)
|
def self.topic_map(guardian, topic_id)
|
||||||
|
|
||||||
# Sam: complicated reports are really hard in AR
|
# Sam: complicated reports are really hard in AR
|
||||||
builder = SqlBuilder.new <<-SQL
|
builder = DB.build <<-SQL
|
||||||
SELECT ftl.url,
|
SELECT ftl.url,
|
||||||
COALESCE(ft.title, ftl.title) AS title,
|
COALESCE(ft.title, ftl.title) AS title,
|
||||||
ftl.link_topic_id,
|
ftl.link_topic_id,
|
||||||
@ -64,16 +64,16 @@ SQL
|
|||||||
|
|
||||||
builder.secure_category(guardian.secure_category_ids)
|
builder.secure_category(guardian.secure_category_ids)
|
||||||
|
|
||||||
builder.exec.to_a
|
builder.query
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.counts_for(guardian, topic, posts)
|
def self.counts_for(guardian, topic, posts)
|
||||||
return {} if posts.blank?
|
return {} if posts.blank?
|
||||||
|
|
||||||
# Sam: I don't know how to write this cleanly in AR,
|
# Sam: this is not tidy in AR and also happens to be a critical path
|
||||||
# in particular the securing logic is tricky and would fallback to SQL anyway
|
# for topic view
|
||||||
builder = SqlBuilder.new("SELECT
|
builder = DB.build("SELECT
|
||||||
l.post_id,
|
l.post_id,
|
||||||
l.url,
|
l.url,
|
||||||
l.clicks,
|
l.clicks,
|
||||||
@ -91,10 +91,11 @@ SQL
|
|||||||
builder.where("COALESCE(t.archetype, 'regular') <> :archetype", archetype: Archetype.private_message)
|
builder.where("COALESCE(t.archetype, 'regular') <> :archetype", archetype: Archetype.private_message)
|
||||||
|
|
||||||
# not certain if pluck is right, cause it may interfere with caching
|
# 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.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] ||= []
|
||||||
result[l.post_id] << { url: l.url,
|
result[l.post_id] << { url: l.url,
|
||||||
clicks: l.clicks,
|
clicks: l.clicks,
|
||||||
@ -102,6 +103,7 @@ SQL
|
|||||||
internal: l.internal,
|
internal: l.internal,
|
||||||
reflection: l.reflection }
|
reflection: l.reflection }
|
||||||
end
|
end
|
||||||
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.extract_from(post)
|
def self.extract_from(post)
|
||||||
|
@ -53,26 +53,26 @@ class TopicUser < ActiveRecord::Base
|
|||||||
def unwatch_categories!(user, category_ids)
|
def unwatch_categories!(user, category_ids)
|
||||||
track_threshold = user.user_option.auto_track_topics_after_msecs
|
track_threshold = user.user_option.auto_track_topics_after_msecs
|
||||||
|
|
||||||
sql = <<SQL
|
sql = <<~SQL
|
||||||
UPDATE topic_users tu
|
UPDATE topic_users tu
|
||||||
SET notification_level = CASE
|
SET notification_level = CASE
|
||||||
WHEN t.user_id = :user_id THEN :watching
|
WHEN t.user_id = :user_id THEN :watching
|
||||||
WHEN total_msecs_viewed > :track_threshold AND :track_threshold >= 0 THEN :tracking
|
WHEN total_msecs_viewed > :track_threshold AND :track_threshold >= 0 THEN :tracking
|
||||||
ELSE :regular
|
ELSE :regular
|
||||||
end
|
end
|
||||||
FROM topics t
|
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
|
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
|
||||||
|
|
||||||
exec_sql(sql,
|
DB.exec(sql,
|
||||||
watching: notification_levels[:watching],
|
watching: notification_levels[:watching],
|
||||||
tracking: notification_levels[:tracking],
|
tracking: notification_levels[:tracking],
|
||||||
regular: notification_levels[:regular],
|
regular: notification_levels[:regular],
|
||||||
muted: notification_levels[:muted],
|
muted: notification_levels[:muted],
|
||||||
category_ids: category_ids,
|
category_ids: category_ids,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
track_threshold: track_threshold
|
track_threshold: track_threshold
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Find the information specific to a user in a forum topic
|
# Find the information specific to a user in a forum topic
|
||||||
@ -296,16 +296,15 @@ SQL
|
|||||||
# 86400000 = 1 day
|
# 86400000 = 1 day
|
||||||
rows =
|
rows =
|
||||||
if user.staff?
|
if user.staff?
|
||||||
exec_sql(UPDATE_TOPIC_USER_SQL_STAFF, args).values
|
DB.query(UPDATE_TOPIC_USER_SQL_STAFF, args)
|
||||||
else
|
else
|
||||||
exec_sql(UPDATE_TOPIC_USER_SQL, args).values
|
DB.query(UPDATE_TOPIC_USER_SQL, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
if rows.length == 1
|
if rows.length == 1
|
||||||
before = rows[0][1].to_i
|
before = rows[0].old_level.to_i
|
||||||
after = rows[0][0].to_i
|
after = rows[0].notification_level.to_i
|
||||||
|
before_last_read = rows[0].last_read_post_number.to_i
|
||||||
before_last_read = rows[0][2].to_i
|
|
||||||
|
|
||||||
if before_last_read < post_number
|
if before_last_read < post_number
|
||||||
# The user read at least one new post
|
# The user read at least one new post
|
||||||
@ -333,9 +332,9 @@ SQL
|
|||||||
|
|
||||||
begin
|
begin
|
||||||
if user.staff?
|
if user.staff?
|
||||||
exec_sql(INSERT_TOPIC_USER_SQL_STAFF, args)
|
DB.exec(INSERT_TOPIC_USER_SQL_STAFF, args)
|
||||||
else
|
else
|
||||||
exec_sql(INSERT_TOPIC_USER_SQL, args)
|
DB.exec(INSERT_TOPIC_USER_SQL, args)
|
||||||
end
|
end
|
||||||
rescue PG::UniqueViolation
|
rescue PG::UniqueViolation
|
||||||
# if record is inserted between two statements this can happen
|
# if record is inserted between two statements this can happen
|
||||||
@ -431,7 +430,7 @@ SQL
|
|||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
TopicUser.exec_sql(sql, user_id: user_id, count: count)
|
DB.exec(sql, user_id: user_id, count: count)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_consistency!(topic_id = nil)
|
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)
|
AND uh.action IN (:silence_user, :unsilence_user, :suspend_user, :unsuspend_user)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
PenaltyCounts.new(UserHistory.exec_sql(sql, args).first)
|
PenaltyCounts.new(DB.query_hash(sql, args).first)
|
||||||
end
|
end
|
||||||
|
|
||||||
def min_days_visited
|
def min_days_visited
|
||||||
|
@ -208,7 +208,7 @@ class User < ActiveRecord::Base
|
|||||||
def self.username_available?(username, email = nil)
|
def self.username_available?(username, email = nil)
|
||||||
lower = username.downcase
|
lower = username.downcase
|
||||||
return false if reserved_username?(lower)
|
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
|
# 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 })
|
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
|
AND NOT read
|
||||||
SQL
|
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
|
end
|
||||||
|
|
||||||
def unread_private_messages
|
def unread_private_messages
|
||||||
@ -408,11 +409,11 @@ class User < ActiveRecord::Base
|
|||||||
AND NOT read
|
AND NOT read
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
User.exec_sql(sql,
|
DB.query_single(sql,
|
||||||
user_id: id,
|
user_id: id,
|
||||||
seen_notification_id: seen_notification_id,
|
seen_notification_id: seen_notification_id,
|
||||||
pm: Notification.types[:private_message]
|
pm: Notification.types[:private_message]
|
||||||
).getvalue(0, 0).to_i
|
)[0].to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -446,7 +447,7 @@ class User < ActiveRecord::Base
|
|||||||
notification = notifications.visible.order('notifications.id desc').first
|
notification = notifications.visible.order('notifications.id desc').first
|
||||||
json = NotificationSerializer.new(notification).as_json if notification
|
json = NotificationSerializer.new(notification).as_json if notification
|
||||||
|
|
||||||
sql = "
|
sql = (<<~SQL).freeze
|
||||||
SELECT * FROM (
|
SELECT * FROM (
|
||||||
SELECT n.id, n.read FROM notifications n
|
SELECT n.id, n.read FROM notifications n
|
||||||
LEFT JOIN topics t ON n.topic_id = t.id
|
LEFT JOIN topics t ON n.topic_id = t.id
|
||||||
@ -469,13 +470,13 @@ class User < ActiveRecord::Base
|
|||||||
ORDER BY n.id DESC
|
ORDER BY n.id DESC
|
||||||
LIMIT 20
|
LIMIT 20
|
||||||
) AS y
|
) AS y
|
||||||
"
|
SQL
|
||||||
|
|
||||||
recent = User.exec_sql(sql,
|
recent = DB.query(sql,
|
||||||
user_id: id,
|
user_id: id,
|
||||||
type: Notification.types[:private_message]
|
type: Notification.types[:private_message]
|
||||||
).values.map! do |id, read|
|
).map! do |r|
|
||||||
[id.to_i, read]
|
[r.id, r.read]
|
||||||
end
|
end
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
@ -1155,12 +1156,12 @@ class User < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
USERNAME_EXISTS_SQL = <<~SQL
|
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)
|
WHERE users.username_lower = :username)
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
(SELECT groups.id AS group_id FROM groups
|
(SELECT groups.id, false as is_user FROM groups
|
||||||
WHERE lower(groups.name) = :username)
|
WHERE lower(groups.name) = :username)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
@ -1168,11 +1169,14 @@ class User < ActiveRecord::Base
|
|||||||
username_format_validator || begin
|
username_format_validator || begin
|
||||||
lower = username.downcase
|
lower = username.downcase
|
||||||
|
|
||||||
existing = User.exec_sql(
|
existing = DB.query(
|
||||||
USERNAME_EXISTS_SQL, username: lower
|
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'))
|
errors.add(:username, I18n.t(:'user.username.unique'))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1200,7 +1204,7 @@ class User < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
if values.present?
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -97,7 +97,8 @@ SQL
|
|||||||
AND t.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = :user_id)
|
AND t.id IN (SELECT topic_id FROM topic_allowed_users WHERE user_id = :user_id)
|
||||||
SQL
|
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
|
sql = <<-SQL
|
||||||
SELECT g.name, COUNT(*) "count"
|
SELECT g.name, COUNT(*) "count"
|
||||||
@ -112,8 +113,8 @@ SQL
|
|||||||
|
|
||||||
result = { all: all, mine: mine, unread: unread }
|
result = { all: all, mine: mine, unread: unread }
|
||||||
|
|
||||||
exec_sql(sql, user_id: user_id).each do |row|
|
DB.query(sql, user_id: user_id).each do |row|
|
||||||
(result[:groups] ||= []) << { name: row["name"], count: row["count"].to_i }
|
(result[:groups] ||= []) << { name: row.name, count: row.count.to_i }
|
||||||
end
|
end
|
||||||
|
|
||||||
result
|
result
|
||||||
|
@ -131,7 +131,7 @@ class UserAuthToken < ActiveRecord::Base
|
|||||||
|
|
||||||
token = SecureRandom.hex(16)
|
token = SecureRandom.hex(16)
|
||||||
|
|
||||||
result = UserAuthToken.exec_sql("
|
result = DB.exec("
|
||||||
UPDATE user_auth_tokens
|
UPDATE user_auth_tokens
|
||||||
SET
|
SET
|
||||||
auth_token_seen = false,
|
auth_token_seen = false,
|
||||||
@ -150,7 +150,7 @@ class UserAuthToken < ActiveRecord::Base
|
|||||||
safeguard_time: 30.seconds.ago
|
safeguard_time: 30.seconds.ago
|
||||||
)
|
)
|
||||||
|
|
||||||
if result.cmdtuples > 0
|
if result > 0
|
||||||
reload
|
reload
|
||||||
self.unhashed_auth_token = token
|
self.unhashed_auth_token = token
|
||||||
|
|
||||||
|
@ -6,10 +6,14 @@ class UserOption < ActiveRecord::Base
|
|||||||
after_save :update_tracked_topics
|
after_save :update_tracked_topics
|
||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
exec_sql("SELECT u.id FROM users u
|
sql = <<~SQL
|
||||||
LEFT JOIN user_options o ON o.user_id = u.id
|
SELECT u.id FROM users u
|
||||||
WHERE o.user_id IS NULL").values.each do |id, _|
|
LEFT JOIN user_options o ON o.user_id = u.id
|
||||||
UserOption.create(user_id: id.to_i)
|
WHERE o.user_id IS NULL
|
||||||
|
SQL
|
||||||
|
|
||||||
|
DB.query_single(sql).each do |id|
|
||||||
|
UserOption.create(user_id: id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,34 +23,36 @@ class UserStat < ActiveRecord::Base
|
|||||||
# we also ensure we only touch the table if data changes
|
# we also ensure we only touch the table if data changes
|
||||||
|
|
||||||
# Update denormalized topics_entered
|
# Update denormalized topics_entered
|
||||||
exec_sql "UPDATE user_stats SET topics_entered = X.c
|
DB.exec(<<~SQL, seen_at: last_seen)
|
||||||
FROM
|
UPDATE user_stats SET topics_entered = X.c
|
||||||
(SELECT v.user_id, COUNT(topic_id) AS c
|
FROM
|
||||||
FROM topic_views AS v
|
(SELECT v.user_id, COUNT(topic_id) AS c
|
||||||
WHERE v.user_id IN (
|
FROM topic_views AS v
|
||||||
SELECT u1.id FROM users u1 where u1.last_seen_at > :seen_at
|
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
|
GROUP BY v.user_id) AS X
|
||||||
X.user_id = user_stats.user_id AND
|
WHERE
|
||||||
X.c <> topics_entered
|
X.user_id = user_stats.user_id AND
|
||||||
", seen_at: last_seen
|
X.c <> topics_entered
|
||||||
|
SQL
|
||||||
|
|
||||||
# Update denormalzied posts_read_count
|
# Update denormalzied posts_read_count
|
||||||
exec_sql "UPDATE user_stats SET posts_read_count = X.c
|
DB.exec(<<~SQL, seen_at: last_seen)
|
||||||
FROM
|
UPDATE user_stats SET posts_read_count = X.c
|
||||||
(SELECT pt.user_id,
|
FROM
|
||||||
COUNT(*) AS c
|
(SELECT pt.user_id,
|
||||||
FROM users AS u
|
COUNT(*) AS c
|
||||||
JOIN post_timings AS pt ON pt.user_id = u.id
|
FROM users AS u
|
||||||
JOIN topics t ON t.id = pt.topic_id
|
JOIN post_timings AS pt ON pt.user_id = u.id
|
||||||
WHERE u.last_seen_at > :seen_at AND
|
JOIN topics t ON t.id = pt.topic_id
|
||||||
t.archetype = 'regular' AND
|
WHERE u.last_seen_at > :seen_at AND
|
||||||
t.deleted_at IS NULL
|
t.archetype = 'regular' AND
|
||||||
GROUP BY pt.user_id) AS X
|
t.deleted_at IS NULL
|
||||||
WHERE X.user_id = user_stats.user_id AND
|
GROUP BY pt.user_id) AS X
|
||||||
X.c <> posts_read_count
|
WHERE X.user_id = user_stats.user_id AND
|
||||||
", seen_at: last_seen
|
X.c <> posts_read_count
|
||||||
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
# topic_reply_count is a count of posts in other users' topics
|
# topic_reply_count is a count of posts in other users' topics
|
||||||
|
@ -11,7 +11,7 @@ class UserVisit < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.count_by_active_users(start_date, end_date)
|
def self.count_by_active_users(start_date, end_date)
|
||||||
sql = <<SQL
|
sql = <<~SQL
|
||||||
WITH dau AS (
|
WITH dau AS (
|
||||||
SELECT date_trunc('day', user_visits.visited_at)::DATE AS date,
|
SELECT date_trunc('day', user_visits.visited_at)::DATE AS date,
|
||||||
count(distinct user_visits.user_id) AS dau
|
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
|
WHERE user_visits.visited_at::DATE BETWEEN dau.date - 29 AND dau.date
|
||||||
) AS mau
|
) AS mau
|
||||||
FROM dau
|
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
|
end
|
||||||
|
|
||||||
# A count of visits in a date range by day
|
# A count of visits in a date range by day
|
||||||
@ -42,16 +42,16 @@ SQL
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
exec_sql <<SQL
|
DB.exec <<~SQL
|
||||||
UPDATE user_stats u set days_visited =
|
UPDATE user_stats u set days_visited =
|
||||||
(
|
(
|
||||||
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id
|
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id
|
||||||
)
|
)
|
||||||
WHERE days_visited <>
|
WHERE days_visited <>
|
||||||
(
|
(
|
||||||
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id
|
SELECT COUNT(*) FROM user_visits v WHERE v.user_id = u.user_id
|
||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ class TopicLinkSerializer < ApplicationSerializer
|
|||||||
|
|
||||||
attributes :url,
|
attributes :url,
|
||||||
:title,
|
:title,
|
||||||
:fancy_title,
|
# :fancy_title,
|
||||||
:internal,
|
:internal,
|
||||||
:attachment,
|
:attachment,
|
||||||
:reflection,
|
:reflection,
|
||||||
@ -11,44 +11,12 @@ class TopicLinkSerializer < ApplicationSerializer
|
|||||||
:domain,
|
:domain,
|
||||||
:root_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
|
def attachment
|
||||||
Discourse.store.has_been_uploaded?(object['url'])
|
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_user_id?
|
def include_user_id?
|
||||||
object['user_id'].present?
|
object.user_id.present?
|
||||||
end
|
|
||||||
|
|
||||||
def domain
|
|
||||||
object['domain']
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def root_domain
|
def root_domain
|
||||||
|
@ -204,17 +204,18 @@ class BadgeGranter
|
|||||||
end
|
end
|
||||||
|
|
||||||
query_plan = nil
|
query_plan = nil
|
||||||
# HACK: active record is weird, force it to go down the sanitization path that cares not for % stuff
|
# HACK: active record sanitization too flexible, 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]
|
# 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|
|
sample.each do |result|
|
||||||
raise "Query returned a non-existent user ID:\n#{result[:id]}" unless User.find(result[:id]).present?
|
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]
|
raise "Query did not return a badge grant time\n(Try using 'current_timestamp granted_at')" unless result.granted_at
|
||||||
if opts[:target_posts]
|
if opts[:target_posts]
|
||||||
raise "Query did not return a post ID" unless result[:post_id]
|
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 returned a non-existent post ID:\n#{result.post_id}" unless Post.exists?(result.post_id).present?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -258,28 +259,31 @@ class BadgeGranter
|
|||||||
WHERE ub.badge_id = :id AND q.user_id IS NULL
|
WHERE ub.badge_id = :id AND q.user_id IS NULL
|
||||||
)"
|
)"
|
||||||
|
|
||||||
Badge.exec_sql(sql, id: badge.id,
|
DB.exec(
|
||||||
post_ids: [-1],
|
sql,
|
||||||
user_ids: [-2],
|
id: badge.id,
|
||||||
backfill: true,
|
post_ids: [-1],
|
||||||
multiple_grant: true # cheat here, cause we only run on backfill and are deleting
|
user_ids: [-2],
|
||||||
) if badge.auto_revoke && full_backfill
|
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 (
|
sql = <<~SQL
|
||||||
INSERT INTO user_badges(badge_id, user_id, granted_at, granted_by_id, post_id)
|
WITH w as (
|
||||||
SELECT :id, q.user_id, q.granted_at, -1, #{post_id_field}
|
INSERT INTO user_badges(badge_id, user_id, granted_at, granted_by_id, post_id)
|
||||||
FROM ( #{badge.query} ) q
|
SELECT :id, q.user_id, q.granted_at, -1, #{post_id_field}
|
||||||
LEFT JOIN user_badges ub ON
|
FROM ( #{badge.query} ) q
|
||||||
ub.badge_id = :id AND ub.user_id = q.user_id
|
LEFT JOIN user_badges ub ON
|
||||||
#{post_clause}
|
ub.badge_id = :id AND ub.user_id = q.user_id
|
||||||
/*where*/
|
#{post_clause}
|
||||||
RETURNING id, user_id, granted_at
|
/*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
|
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")
|
builder.where("ub.badge_id IS NULL AND q.user_id <> -1")
|
||||||
|
|
||||||
if (post_ids || user_ids) && !badge.query.include?(":backfill")
|
if (post_ids || user_ids) && !badge.query.include?(":backfill")
|
||||||
@ -297,11 +301,12 @@ class BadgeGranter
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
builder.map_exec(OpenStruct, id: badge.id,
|
builder.query(
|
||||||
multiple_grant: badge.multiple_grant,
|
id: badge.id,
|
||||||
backfill: full_backfill,
|
multiple_grant: badge.multiple_grant,
|
||||||
post_ids: post_ids || [-2],
|
backfill: full_backfill,
|
||||||
user_ids: user_ids || [-2]).each do |row|
|
post_ids: post_ids || [-2],
|
||||||
|
user_ids: user_ids || [-2]).each do |row|
|
||||||
|
|
||||||
# old bronze badges do not matter
|
# old bronze badges do not matter
|
||||||
next if badge.badge_type_id == (BadgeType::Bronze) && row.granted_at < (2.days.ago)
|
next if badge.badge_type_id == (BadgeType::Bronze) && row.granted_at < (2.days.ago)
|
||||||
@ -332,10 +337,11 @@ class BadgeGranter
|
|||||||
}.to_json)
|
}.to_json)
|
||||||
end
|
end
|
||||||
|
|
||||||
Badge.exec_sql("UPDATE user_badges SET notification_id = :notification_id WHERE id = :id",
|
DB.exec(
|
||||||
notification_id: notification.id,
|
"UPDATE user_badges SET notification_id = :notification_id WHERE id = :id",
|
||||||
id: row.id
|
notification_id: notification.id,
|
||||||
)
|
id: row.id
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
badge.reset_grant_count!
|
badge.reset_grant_count!
|
||||||
@ -345,21 +351,22 @@ class BadgeGranter
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.revoke_ungranted_titles!
|
def self.revoke_ungranted_titles!
|
||||||
Badge.exec_sql("UPDATE users SET title = ''
|
DB.exec <<~SQL
|
||||||
WHERE NOT title IS NULL AND
|
UPDATE users SET title = ''
|
||||||
title <> '' AND
|
WHERE NOT title IS NULL AND
|
||||||
EXISTS (
|
title <> '' AND
|
||||||
SELECT 1
|
EXISTS (
|
||||||
FROM user_profiles
|
SELECT 1
|
||||||
WHERE user_id = users.id AND badge_granted_title
|
FROM user_profiles
|
||||||
) AND
|
WHERE user_id = users.id AND badge_granted_title
|
||||||
title NOT IN (
|
) AND
|
||||||
SELECT name
|
title NOT IN (
|
||||||
FROM badges
|
SELECT name
|
||||||
WHERE allow_title AND enabled AND
|
FROM badges
|
||||||
badges.id IN (SELECT badge_id FROM user_badges ub where ub.user_id = users.id)
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -194,16 +194,18 @@ class PostAlerter
|
|||||||
}
|
}
|
||||||
|
|
||||||
def group_stats(topic)
|
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|
|
topic.allowed_groups.map do |g|
|
||||||
{
|
{
|
||||||
group_id: g.id,
|
group_id: g.id,
|
||||||
group_name: g.name.downcase,
|
group_name: g.name.downcase,
|
||||||
inbox_count: Topic.exec_sql(
|
inbox_count: DB.query_single(sql, group_id: g.id).first.to_i
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -61,7 +61,7 @@ class SearchIndexer
|
|||||||
|
|
||||||
# Would be nice to use AR here but not sure how to execut Postgres functions
|
# Would be nice to use AR here but not sure how to execut Postgres functions
|
||||||
# when inserting data like this.
|
# when inserting data like this.
|
||||||
rows = Post.exec_sql_row_count(<<~SQL, params)
|
rows = DB.exec(<<~SQL, params)
|
||||||
UPDATE #{table_name}
|
UPDATE #{table_name}
|
||||||
SET
|
SET
|
||||||
raw_data = :raw_data,
|
raw_data = :raw_data,
|
||||||
@ -72,7 +72,7 @@ class SearchIndexer
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
if rows == 0
|
if rows == 0
|
||||||
Post.exec_sql(<<~SQL, params)
|
DB.exec(<<~SQL, params)
|
||||||
INSERT INTO #{table_name}
|
INSERT INTO #{table_name}
|
||||||
(#{foreign_key}, search_data, locale, raw_data, version)
|
(#{foreign_key}, search_data, locale, raw_data, version)
|
||||||
VALUES (:id, #{ranked_index}, :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)
|
def self.queue_post_reindex(topic_id)
|
||||||
return if @disabled
|
return if @disabled
|
||||||
|
|
||||||
ActiveRecord::Base.exec_sql(<<~SQL, topic_id: topic_id)
|
DB.exec(<<~SQL, topic_id: topic_id)
|
||||||
UPDATE post_search_data
|
UPDATE post_search_data
|
||||||
SET version = 0
|
SET version = 0
|
||||||
WHERE post_id IN (SELECT id FROM posts WHERE topic_id = :topic_id)
|
WHERE post_id IN (SELECT id FROM posts WHERE topic_id = :topic_id)
|
||||||
|
@ -89,11 +89,13 @@ class UserMerger
|
|||||||
limit_reached = EXCLUDED.limit_reached
|
limit_reached = EXCLUDED.limit_reached
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
GivenDailyLike.exec_sql(sql,
|
DB.exec(
|
||||||
source_user_id: @source_user.id,
|
sql,
|
||||||
target_user_id: @target_user.id,
|
source_user_id: @source_user.id,
|
||||||
max_likes_per_day: SiteSetting.max_likes_per_day,
|
target_user_id: @target_user.id,
|
||||||
action_type_id: PostActionType.types[:like])
|
max_likes_per_day: SiteSetting.max_likes_per_day,
|
||||||
|
action_type_id: PostActionType.types[:like]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def merge_post_timings
|
def merge_post_timings
|
||||||
@ -107,7 +109,7 @@ class UserMerger
|
|||||||
AND t.topic_id = s.topic_id AND t.post_number = s.post_number
|
AND t.topic_id = s.topic_id AND t.post_number = s.post_number
|
||||||
SQL
|
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
|
end
|
||||||
|
|
||||||
def merge_user_visits
|
def merge_user_visits
|
||||||
@ -123,7 +125,7 @@ class UserMerger
|
|||||||
AND t.visited_at = s.visited_at
|
AND t.visited_at = s.visited_at
|
||||||
SQL
|
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
|
end
|
||||||
|
|
||||||
def update_site_settings
|
def update_site_settings
|
||||||
@ -136,7 +138,7 @@ class UserMerger
|
|||||||
|
|
||||||
def update_user_stats
|
def update_user_stats
|
||||||
# topics_entered
|
# topics_entered
|
||||||
UserStat.exec_sql(<<~SQL, target_user_id: @target_user.id)
|
DB.exec(<<~SQL, target_user_id: @target_user.id)
|
||||||
UPDATE user_stats
|
UPDATE user_stats
|
||||||
SET topics_entered = (
|
SET topics_entered = (
|
||||||
SELECT COUNT(topic_id)
|
SELECT COUNT(topic_id)
|
||||||
@ -147,7 +149,7 @@ class UserMerger
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
# time_read and days_visited
|
# 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
|
UPDATE user_stats
|
||||||
SET time_read = COALESCE(x.time_read, 0),
|
SET time_read = COALESCE(x.time_read, 0),
|
||||||
days_visited = COALESCE(x.days_visited, 0)
|
days_visited = COALESCE(x.days_visited, 0)
|
||||||
@ -162,7 +164,7 @@ class UserMerger
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
# posts_read_count
|
# 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
|
UPDATE user_stats
|
||||||
SET posts_read_count = (
|
SET posts_read_count = (
|
||||||
SELECT COUNT(1)
|
SELECT COUNT(1)
|
||||||
@ -176,7 +178,7 @@ class UserMerger
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
# likes_given, likes_received, new_since, read_faq, first_post_created_at
|
# 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
|
UPDATE user_stats AS t
|
||||||
SET likes_given = t.likes_given + s.likes_given,
|
SET likes_given = t.likes_given + s.likes_given,
|
||||||
likes_received = t.likes_received + s.likes_received,
|
likes_received = t.likes_received + s.likes_received,
|
||||||
@ -189,7 +191,7 @@ class UserMerger
|
|||||||
end
|
end
|
||||||
|
|
||||||
def merge_user_attributes
|
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
|
UPDATE users AS t
|
||||||
SET created_at = LEAST(t.created_at, s.created_at),
|
SET created_at = LEAST(t.created_at, s.created_at),
|
||||||
updated_at = LEAST(t.updated_at, s.updated_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
|
WHERE t.id = :target_user_id AND s.id = :source_user_id
|
||||||
SQL
|
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
|
UPDATE user_profiles AS t
|
||||||
SET location = COALESCE(t.location, s.location),
|
SET location = COALESCE(t.location, s.location),
|
||||||
website = COALESCE(t.website, s.website),
|
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
|
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
|
# 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)
|
DB.exec(<<~SQL, now: Time.now, user_id: user.id, desired_ids: desired_ids)
|
||||||
SELECT :user_id, id, :now, :now
|
INSERT into muted_users(user_id, muted_user_id, created_at, updated_at)
|
||||||
FROM users
|
SELECT :user_id, id, :now, :now
|
||||||
WHERE
|
FROM users
|
||||||
id in (:desired_ids) AND
|
WHERE
|
||||||
id NOT IN (
|
id in (:desired_ids) AND
|
||||||
SELECT muted_user_id
|
id NOT IN (
|
||||||
FROM muted_users
|
SELECT muted_user_id
|
||||||
WHERE user_id = :user_id
|
FROM muted_users
|
||||||
)",
|
WHERE user_id = :user_id
|
||||||
now: Time.now, user_id: user.id, desired_ids: desired_ids)
|
)
|
||||||
|
SQL
|
||||||
end
|
end
|
||||||
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 "memory_stats" => "diagnostics#memory_stats", constraints: AdminConstraint.new
|
||||||
get "dump_heap" => "diagnostics#dump_heap", 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
|
end # admin namespace
|
||||||
|
|
||||||
get "email_preferences" => "email#preferences_redirect", :as => "email_preferences_redirect"
|
get "email_preferences" => "email#preferences_redirect", :as => "email_preferences_redirect"
|
||||||
|
@ -48,7 +48,7 @@ Migration::ColumnDropper.drop(
|
|||||||
},
|
},
|
||||||
on_drop: ->() {
|
on_drop: ->() {
|
||||||
STDERR.puts "Removing superflous user stats columns!"
|
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)
|
if uncat_id == -1 || !Category.exists?(uncat_id)
|
||||||
puts "Seeding uncategorized category!"
|
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 = '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)
|
(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}' )
|
VALUES ('#{name}', 'AB9364', 'uncategorized', '', 'FFFFFF', -1, now(), now(), 1, '#{name.downcase}' )
|
||||||
RETURNING id
|
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'"
|
DB.exec "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 "INSERT INTO site_settings(name, data_type, value, created_at, updated_at)
|
||||||
VALUES ('uncategorized_category_id', 3, #{category_id}, now(), now())"
|
VALUES ('uncategorized_category_id', 3, #{category_id}, now(), now())"
|
||||||
end
|
end
|
||||||
|
@ -31,7 +31,7 @@ BadgeGrouping.seed do |g|
|
|||||||
end
|
end
|
||||||
|
|
||||||
# BUGFIX
|
# BUGFIX
|
||||||
Badge.exec_sql <<-SQL.squish
|
DB.exec <<-SQL.squish
|
||||||
UPDATE badges
|
UPDATE badges
|
||||||
SET badge_grouping_id = -1
|
SET badge_grouping_id = -1
|
||||||
WHERE NOT EXISTS (
|
WHERE NOT EXISTS (
|
||||||
|
@ -36,7 +36,7 @@ unless Rails.env.test?
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Reset topic count because we don't count the description topic
|
# 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
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,7 +25,7 @@ unless Rails.env.test?
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Reset topic count because we don't count the description topic
|
# 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
|
end
|
||||||
end
|
end
|
||||||
|
@ -33,7 +33,7 @@ unless Rails.env.test?
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Reset topic count because we don't count the description topic
|
# 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
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
class AddStarredAtToForumThreadUser < ActiveRecord::Migration[4.2]
|
class AddStarredAtToForumThreadUser < ActiveRecord::Migration[4.2]
|
||||||
def up
|
def up
|
||||||
add_column :forum_thread_users, :starred_at, :datetime
|
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
|
from
|
||||||
(
|
(
|
||||||
select f1.forum_thread_id, f1.user_id, t.created_at from forum_thread_users f1
|
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]
|
class MakePostNumberDistinct < ActiveRecord::Migration[4.2]
|
||||||
def up
|
def up
|
||||||
|
|
||||||
Topic.exec_sql('update posts p
|
DB.exec('update posts p
|
||||||
set post_number = calc
|
set post_number = calc
|
||||||
from
|
from
|
||||||
(
|
(
|
||||||
|
@ -3,25 +3,25 @@ class AddLoungeCategory < ActiveRecord::Migration[4.2]
|
|||||||
return if Rails.env.test?
|
return if Rails.env.test?
|
||||||
|
|
||||||
I18n.overrides_disabled do
|
I18n.overrides_disabled do
|
||||||
result = Category.exec_sql "SELECT 1 FROM site_settings where name = 'lounge_category_id'"
|
result = DB.exec "SELECT 1 FROM site_settings where name = 'lounge_category_id'"
|
||||||
if result.count == 0
|
if result == 0
|
||||||
description = I18n.t('vip_category_description')
|
description = I18n.t('vip_category_description')
|
||||||
|
|
||||||
default_name = I18n.t('vip_category_name')
|
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
|
default_name
|
||||||
else
|
else
|
||||||
"CHANGE_ME"
|
"CHANGE_ME"
|
||||||
end
|
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)
|
(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)
|
VALUES (:name, 'EEEEEE', '652D90', now(), now(), -1, '', :description, true, 3)
|
||||||
RETURNING id", name: name, description: description
|
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",
|
WHERE id = :category_id",
|
||||||
slug: Slug.for(name, "#{category_id}-category"), category_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?
|
return if Rails.env.test?
|
||||||
|
|
||||||
I18n.overrides_disabled do
|
I18n.overrides_disabled do
|
||||||
result = Category.exec_sql "SELECT 1 FROM site_settings where name = 'meta_category_id'"
|
result = DB.exec "SELECT 1 FROM site_settings where name = 'meta_category_id'"
|
||||||
if result.count == 0
|
if result == 0
|
||||||
description = I18n.t('meta_category_description')
|
description = I18n.t('meta_category_description')
|
||||||
name = I18n.t('meta_category_name')
|
name = I18n.t('meta_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)
|
(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)
|
VALUES (:name, '808281', 'FFFFFF', now(), now(), -1, :slug, :description, true, 1)
|
||||||
RETURNING id", name: name, slug: '', description: description
|
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
|
slug: Slug.for(name, "#{category_id}-category"), category_id: category_id
|
||||||
|
|
||||||
execute "INSERT INTO site_settings(name, data_type, value, created_at, updated_at)
|
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?
|
return if Rails.env.test?
|
||||||
|
|
||||||
I18n.overrides_disabled do
|
I18n.overrides_disabled do
|
||||||
result = Category.exec_sql "SELECT 1 FROM site_settings where name = 'staff_category_id'"
|
result = DB.exec "SELECT 1 FROM site_settings where name = 'staff_category_id'"
|
||||||
if result.count == 0
|
if result == 0
|
||||||
description = I18n.t('staff_category_description')
|
description = I18n.t('staff_category_description')
|
||||||
name = I18n.t('staff_category_name')
|
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)
|
(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)
|
VALUES (:name, '283890', 'FFFFFF', now(), now(), -1, '', :description, true, 2)
|
||||||
RETURNING id", name: name, description: description
|
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
|
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())"
|
VALUES ('staff_category_id', 3, #{category_id.to_i}, now(), now())"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
class InitFixedCategoryPositionsValue < ActiveRecord::Migration[4.2]
|
class InitFixedCategoryPositionsValue < ActiveRecord::Migration[4.2]
|
||||||
def up
|
def up
|
||||||
# Look at existing categories to determine if positions have been specified
|
# 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
|
# 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())"
|
execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('fixed_category_positions', 5, 't', now(), now())"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
class GoogleOpenidDefaultHasChanged < ActiveRecord::Migration[4.2]
|
class GoogleOpenidDefaultHasChanged < ActiveRecord::Migration[4.2]
|
||||||
def up
|
def up
|
||||||
users_count_query = User.exec_sql("SELECT count(*) FROM users")
|
users_count_query = DB.query_single("SELECT count(*) FROM users")
|
||||||
if users_count_query[0]['count'].to_i > 1
|
if users_count_query.first.to_i > 1
|
||||||
# This is an existing site.
|
# This is an existing site.
|
||||||
result = User.exec_sql("SELECT count(*) FROM site_settings WHERE name = 'enable_google_logins'")
|
result = DB.query_single("SELECT count(*) FROM site_settings WHERE name = 'enable_google_logins'")
|
||||||
if result[0]['count'].to_i == 0
|
if result.first.to_i == 0
|
||||||
# The old default was true, so add a row to keep it that way.
|
# 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())"
|
execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('enable_google_logins', 5, 't', now(), now())"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Don't enable the new Google setting on an existing site.
|
# 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'")
|
result = DB.query_single("SELECT count(*) FROM site_settings WHERE name = 'enable_google_oauth2_logins'")
|
||||||
if result[0]['count'].to_i == 0
|
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())"
|
execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('enable_google_oauth2_logins', 5, 'f', now(), now())"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
class DisableExternalAuthsByDefault < ActiveRecord::Migration[4.2]
|
class DisableExternalAuthsByDefault < ActiveRecord::Migration[4.2]
|
||||||
|
|
||||||
def enable_setting_if_default(name)
|
def enable_setting_if_default(name)
|
||||||
result = User.exec_sql("SELECT count(*) count FROM site_settings WHERE name = '#{name}'")
|
result = DB.query_single("SELECT count(*) count FROM site_settings WHERE name = '#{name}'")
|
||||||
if result[0]['count'].to_i == 0
|
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())"
|
execute "INSERT INTO site_settings (name, data_type, value, created_at, updated_at) VALUES ('#{name}', 5, 't', now(), now())"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def up
|
def up
|
||||||
users_count_query = User.exec_sql("SELECT count(*) FROM users")
|
users_count_query = DB.query_single("SELECT count(*) FROM users")
|
||||||
if users_count_query[0]['count'].to_i > 1
|
if users_count_query.first.to_i > 1
|
||||||
# existing site, so keep settings as they are
|
# existing site, so keep settings as they are
|
||||||
enable_setting_if_default 'enable_yahoo_logins'
|
enable_setting_if_default 'enable_yahoo_logins'
|
||||||
enable_setting_if_default 'enable_google_oauth2_logins'
|
enable_setting_if_default 'enable_google_oauth2_logins'
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
class RemoveEmailInAddressSetting < ActiveRecord::Migration[4.2]
|
class RemoveEmailInAddressSetting < ActiveRecord::Migration[4.2]
|
||||||
def up
|
def up
|
||||||
uncat_id = ActiveRecord::Base.exec_sql("SELECT value FROM site_settings WHERE name = 'uncategorized_category_id'").first
|
uncat_id = DB.query_single("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
|
cat_id_r = DB.query_single("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
|
email_r = DB.query_single("SELECT value FROM site_settings WHERE name = 'email_in_address'").first
|
||||||
if email_r
|
if email_r
|
||||||
category_id = uncat_id["value"].to_i
|
category_id = uncat_id["value"].to_i
|
||||||
category_id = cat_id_r["value"].to_i if cat_id_r
|
category_id = cat_id_r["value"].to_i if cat_id_r
|
||||||
email = email_r["value"]
|
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
|
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
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
class ResolveDuplicateGroupNames < ActiveRecord::Migration[4.2]
|
class ResolveDuplicateGroupNames < ActiveRecord::Migration[4.2]
|
||||||
|
|
||||||
def up
|
def up
|
||||||
results = Group.exec_sql 'SELECT id FROM groups
|
results = DB.query_single 'SELECT id FROM groups
|
||||||
WHERE name ILIKE
|
WHERE name ILIKE
|
||||||
(SELECT lower(name)
|
(SELECT lower(name)
|
||||||
FROM groups
|
FROM groups
|
||||||
GROUP BY lower(name)
|
GROUP BY lower(name)
|
||||||
HAVING count(*) > 1);'
|
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|
|
groups.group_by { |g| g.name.downcase }.each do |key, value|
|
||||||
value.each_with_index do |dup, index|
|
value.each_with_index do |dup, index|
|
||||||
dup.update! name: "#{dup.name[0..18]}_#{index + 1}" if index > 0
|
dup.update! name: "#{dup.name[0..18]}_#{index + 1}" if index > 0
|
||||||
|
@ -2,7 +2,7 @@ class FixCategoryLogoAndBackgroundUrls < ActiveRecord::Migration[4.2]
|
|||||||
def up
|
def up
|
||||||
return true if Discourse.asset_host.blank?
|
return true if Discourse.asset_host.blank?
|
||||||
|
|
||||||
Category.exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
UPDATE categories
|
UPDATE categories
|
||||||
SET logo_url = replace(logo_url, '#{Discourse.asset_host}', '')
|
SET logo_url = replace(logo_url, '#{Discourse.asset_host}', '')
|
||||||
, background_url = replace(background_url, '#{Discourse.asset_host}', '')
|
, background_url = replace(background_url, '#{Discourse.asset_host}', '')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
class AddSeenAtToUserAuthToken < ActiveRecord::Migration[4.2]
|
class AddSeenAtToUserAuthToken < ActiveRecord::Migration[4.2]
|
||||||
def up
|
def up
|
||||||
add_column :user_auth_tokens, :seen_at, :datetime
|
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
|
end
|
||||||
|
|
||||||
def down
|
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_exit, :boolean, default: false, null: false
|
||||||
add_column :groups, :public_admission, :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
|
UPDATE groups
|
||||||
SET public_exit = true, public_admission = true
|
SET public_exit = true, public_admission = true
|
||||||
WHERE public = true
|
WHERE public = true
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
class DropRaiseReadOnlyFunction < ActiveRecord::Migration[5.1]
|
class DropRaiseReadOnlyFunction < ActiveRecord::Migration[5.1]
|
||||||
def up
|
def up
|
||||||
ActiveRecord::Base.exec_sql(
|
DB.exec(
|
||||||
"DROP FUNCTION IF EXISTS raise_read_only() CASCADE;"
|
"DROP FUNCTION IF EXISTS raise_read_only() CASCADE;"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -76,7 +76,7 @@ module BackupRestore
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.move_tables_between_schemas(source, destination)
|
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
|
end
|
||||||
|
|
||||||
def self.move_tables_between_schemas_sql(source, destination)
|
def self.move_tables_between_schemas_sql(source, destination)
|
||||||
@ -196,7 +196,7 @@ module BackupRestore
|
|||||||
end
|
end
|
||||||
|
|
||||||
def self.backup_tables_count
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -379,14 +379,14 @@ module BackupRestore
|
|||||||
|
|
||||||
@db_was_changed = true
|
@db_was_changed = true
|
||||||
|
|
||||||
User.exec_sql(sql)
|
DB.exec(sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
def migrate_database
|
def migrate_database
|
||||||
log "Migrating the database..."
|
log "Migrating the database..."
|
||||||
Discourse::Application.load_tasks
|
Discourse::Application.load_tasks
|
||||||
ENV["VERSION"] = @current_version.to_s
|
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
|
Rake::Task["db:migrate"].invoke
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -13,10 +13,10 @@ class CommentMigration < ActiveRecord::Migration[4.2]
|
|||||||
comment = column[1]
|
comment = column[1]
|
||||||
|
|
||||||
if column_name == :_table
|
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}"
|
puts " COMMENT ON TABLE #{table_name}"
|
||||||
else
|
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}"
|
puts " COMMENT ON COLUMN #{table_name}.#{column_name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -35,10 +35,10 @@ class CommentMigration < ActiveRecord::Migration[4.2]
|
|||||||
comment = column[1]
|
comment = column[1]
|
||||||
|
|
||||||
if column_name == :_table
|
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}"
|
puts " COMMENT ON TABLE #{table_name}"
|
||||||
else
|
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}"
|
puts " COMMENT ON COLUMN #{table_name}.#{column_name}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -73,7 +73,7 @@ class CookedPostProcessor
|
|||||||
PostUpload.transaction do
|
PostUpload.transaction do
|
||||||
PostUpload.where(post_id: @post.id).delete_all
|
PostUpload.where(post_id: @post.id).delete_all
|
||||||
if upload_ids.size > 0
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -48,7 +48,7 @@ module Migration
|
|||||||
"Discourse: #{column_name} in #{table_name} is readonly" :
|
"Discourse: #{column_name} in #{table_name} is readonly" :
|
||||||
"Discourse: #{table_name} is read only"
|
"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$
|
CREATE OR REPLACE FUNCTION #{readonly_function_name(table_name, column_name)} RETURNS trigger AS $rcr$
|
||||||
BEGIN
|
BEGIN
|
||||||
RAISE EXCEPTION '#{message}';
|
RAISE EXCEPTION '#{message}';
|
||||||
|
@ -12,7 +12,7 @@ module Migration
|
|||||||
def self.mark_readonly(table_name, column_name)
|
def self.mark_readonly(table_name, column_name)
|
||||||
create_readonly_function(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)}
|
CREATE TRIGGER #{readonly_trigger_name(table_name, column_name)}
|
||||||
BEFORE INSERT OR UPDATE OF #{column_name}
|
BEFORE INSERT OR UPDATE OF #{column_name}
|
||||||
ON #{table_name}
|
ON #{table_name}
|
||||||
@ -51,13 +51,13 @@ module Migration
|
|||||||
|
|
||||||
def execute_drop!
|
def execute_drop!
|
||||||
@columns.each do |column|
|
@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 TRIGGER IF EXISTS #{BaseDropper.readonly_trigger_name(@table, column)} ON #{@table};
|
||||||
DROP FUNCTION IF EXISTS #{BaseDropper.readonly_function_name(@table, column)} CASCADE;
|
DROP FUNCTION IF EXISTS #{BaseDropper.readonly_function_name(@table, column)} CASCADE;
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
# safe cause it is protected on method entry, can not be passed in params
|
# 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
|
end
|
||||||
end
|
end
|
||||||
|
@ -18,7 +18,7 @@ module Migration
|
|||||||
def self.read_only_table(table_name)
|
def self.read_only_table(table_name)
|
||||||
create_readonly_function(table_name)
|
create_readonly_function(table_name)
|
||||||
|
|
||||||
ActiveRecord::Base.exec_sql <<~SQL
|
DB.exec <<~SQL
|
||||||
CREATE TRIGGER #{readonly_trigger_name(table_name)}
|
CREATE TRIGGER #{readonly_trigger_name(table_name)}
|
||||||
BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE
|
BEFORE INSERT OR UPDATE OR DELETE OR TRUNCATE
|
||||||
ON #{table_name}
|
ON #{table_name}
|
||||||
@ -37,7 +37,7 @@ module Migration
|
|||||||
end
|
end
|
||||||
|
|
||||||
def droppable?
|
def droppable?
|
||||||
builder = SqlBuilder.new(<<~SQL)
|
builder = DB.build(<<~SQL)
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM INFORMATION_SCHEMA.TABLES
|
FROM INFORMATION_SCHEMA.TABLES
|
||||||
/*where*/
|
/*where*/
|
||||||
@ -52,7 +52,7 @@ module Migration
|
|||||||
.exec(old_name: @old_name,
|
.exec(old_name: @old_name,
|
||||||
new_name: @new_name,
|
new_name: @new_name,
|
||||||
delay: "#{@delay} seconds",
|
delay: "#{@delay} seconds",
|
||||||
after_migration: @after_migration).to_a.length > 0
|
after_migration: @after_migration) > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def table_exists(table_name_placeholder)
|
def table_exists(table_name_placeholder)
|
||||||
@ -67,9 +67,9 @@ module Migration
|
|||||||
end
|
end
|
||||||
|
|
||||||
def execute_drop!
|
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;
|
DROP FUNCTION IF EXISTS #{BaseDropper.readonly_function_name(@old_name)} CASCADE;
|
||||||
SQL
|
SQL
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
def update_topic_word_counts
|
def update_topic_word_counts
|
||||||
Topic.exec_sql("UPDATE topics
|
DB.exec("UPDATE topics
|
||||||
SET word_count = (
|
SET word_count = (
|
||||||
SELECT SUM(COALESCE(posts.word_count, 0))
|
SELECT SUM(COALESCE(posts.word_count, 0))
|
||||||
FROM posts
|
FROM posts
|
||||||
|
@ -790,9 +790,11 @@ class Search
|
|||||||
|
|
||||||
def self.ts_query(term: , ts_config: nil, joiner: "&", weight_filter: nil)
|
def self.ts_query(term: , ts_config: nil, joiner: "&", weight_filter: nil)
|
||||||
|
|
||||||
data = Post.exec_sql("SELECT TO_TSVECTOR(:config, :term)",
|
data = DB.query_single(
|
||||||
config: 'simple',
|
"SELECT TO_TSVECTOR(:config, :term)",
|
||||||
term: term).values[0][0]
|
config: 'simple',
|
||||||
|
term: term
|
||||||
|
).first
|
||||||
|
|
||||||
ts_config = ActiveRecord::Base.connection.quote(ts_config) if ts_config
|
ts_config = ActiveRecord::Base.connection.quote(ts_config) if ts_config
|
||||||
all_terms = data.scan(/'([^']+)'\:\d+/).flatten
|
all_terms = data.scan(/'([^']+)'\:\d+/).flatten
|
||||||
|
@ -96,7 +96,7 @@ task 'db:stats' => 'environment' do
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
puts
|
puts
|
||||||
print_table(Post.exec_sql(sql).to_a)
|
print_table(DB.query_hash(sql))
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'Rebuild indexes'
|
desc 'Rebuild indexes'
|
||||||
@ -108,16 +108,14 @@ task 'db:rebuild_indexes' => 'environment' do
|
|||||||
Discourse.enable_readonly_mode
|
Discourse.enable_readonly_mode
|
||||||
|
|
||||||
backup_schema = Jobs::Importer::BACKUP_SCHEMA
|
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|
|
table_names = DB.query_single("select table_name from information_schema.tables where table_schema = 'public'")
|
||||||
row['table_name']
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
# Move all tables to the backup schema:
|
# Move all tables to the backup schema:
|
||||||
User.exec_sql("DROP SCHEMA IF EXISTS #{backup_schema} CASCADE")
|
DB.exec("DROP SCHEMA IF EXISTS #{backup_schema} CASCADE")
|
||||||
User.exec_sql("CREATE SCHEMA #{backup_schema}")
|
DB.exec("CREATE SCHEMA #{backup_schema}")
|
||||||
table_names.each do |table_name|
|
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
|
end
|
||||||
|
|
||||||
# Create a new empty db
|
# Create a new empty db
|
||||||
@ -126,25 +124,25 @@ task 'db:rebuild_indexes' => 'environment' do
|
|||||||
# Fetch index definitions from the new db
|
# Fetch index definitions from the new db
|
||||||
index_definitions = {}
|
index_definitions = {}
|
||||||
table_names.each do |table_name|
|
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
|
end
|
||||||
|
|
||||||
# Drop the new tables
|
# Drop the new tables
|
||||||
table_names.each do |table_name|
|
table_names.each do |table_name|
|
||||||
User.exec_sql("DROP TABLE public.#{table_name}")
|
DB.exec("DROP TABLE public.#{table_name}")
|
||||||
end
|
end
|
||||||
|
|
||||||
# Move the old tables back to the public schema
|
# Move the old tables back to the public schema
|
||||||
table_names.each do |table_name|
|
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
|
end
|
||||||
|
|
||||||
# Drop their indexes
|
# 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|
|
index_names.each do |index_name|
|
||||||
begin
|
begin
|
||||||
puts index_name
|
puts index_name
|
||||||
User.exec_sql("DROP INDEX public.#{index_name}")
|
DB.exec("DROP INDEX public.#{index_name}")
|
||||||
rescue ActiveRecord::StatementInvalid
|
rescue ActiveRecord::StatementInvalid
|
||||||
# It's this:
|
# It's this:
|
||||||
# PG::Error: ERROR: cannot drop index category_users_pkey because constraint category_users_pkey on table category_users requires it
|
# 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|
|
table_names.each do |table_name|
|
||||||
index_definitions[table_name].each do |index_def|
|
index_definitions[table_name].each do |index_def|
|
||||||
begin
|
begin
|
||||||
User.exec_sql(index_def)
|
DB.exec(index_def)
|
||||||
rescue ActiveRecord::StatementInvalid
|
rescue ActiveRecord::StatementInvalid
|
||||||
# Trying to recreate a primary key
|
# Trying to recreate a primary key
|
||||||
end
|
end
|
||||||
|
@ -29,7 +29,7 @@ MS_SPEND_CREATING_POST ||= 5000
|
|||||||
def insert_post_timings
|
def insert_post_timings
|
||||||
log "Inserting post timings..."
|
log "Inserting post timings..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO post_timings (topic_id, post_number, user_id, msecs)
|
INSERT INTO post_timings (topic_id, post_number, user_id, msecs)
|
||||||
SELECT topic_id, post_number, user_id, #{MS_SPEND_CREATING_POST}
|
SELECT topic_id, post_number, user_id, #{MS_SPEND_CREATING_POST}
|
||||||
FROM posts
|
FROM posts
|
||||||
@ -41,7 +41,7 @@ end
|
|||||||
def insert_post_replies
|
def insert_post_replies
|
||||||
log "Inserting post replies..."
|
log "Inserting post replies..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO post_replies (post_id, reply_id, created_at, updated_at)
|
INSERT INTO post_replies (post_id, reply_id, created_at, updated_at)
|
||||||
SELECT p2.id, p.id, p.created_at, p.created_at
|
SELECT p2.id, p.id, p.created_at, p.created_at
|
||||||
FROM posts p
|
FROM posts p
|
||||||
@ -53,7 +53,7 @@ end
|
|||||||
def insert_topic_users
|
def insert_topic_users
|
||||||
log "Inserting 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)
|
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}
|
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
|
FROM posts
|
||||||
@ -66,7 +66,7 @@ end
|
|||||||
def insert_topic_views
|
def insert_topic_views
|
||||||
log "Inserting topic views..."
|
log "Inserting topic views..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT topic_id, user_id, DATE(p.created_at) posted_at
|
SELECT topic_id, user_id, DATE(p.created_at) posted_at
|
||||||
FROM posts p
|
FROM posts p
|
||||||
@ -86,7 +86,7 @@ end
|
|||||||
def insert_user_actions
|
def insert_user_actions
|
||||||
log "Inserting user actions for NEW_TOPIC = 4..."
|
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)
|
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
|
SELECT 4, p.user_id, topic_id, p.id, p.user_id, p.created_at, p.created_at
|
||||||
FROM posts p
|
FROM posts p
|
||||||
@ -100,7 +100,7 @@ def insert_user_actions
|
|||||||
|
|
||||||
log "Inserting user actions for REPLY = 5..."
|
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)
|
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
|
SELECT 5, p.user_id, topic_id, p.id, p.user_id, p.created_at, p.created_at
|
||||||
FROM posts p
|
FROM posts p
|
||||||
@ -114,7 +114,7 @@ def insert_user_actions
|
|||||||
|
|
||||||
log "Inserting user actions for RESPONSE = 6..."
|
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)
|
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
|
SELECT 6, p.user_id, p.topic_id, p.id, p2.user_id, p.created_at, p.created_at
|
||||||
FROM posts p
|
FROM posts p
|
||||||
@ -137,7 +137,7 @@ end
|
|||||||
def insert_user_options
|
def insert_user_options
|
||||||
log "Inserting user options..."
|
log "Inserting user options..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO user_options (
|
INSERT INTO user_options (
|
||||||
user_id,
|
user_id,
|
||||||
email_always,
|
email_always,
|
||||||
@ -189,7 +189,7 @@ end
|
|||||||
def insert_user_stats
|
def insert_user_stats
|
||||||
log "Inserting user stats..."
|
log "Inserting user stats..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO user_stats (user_id, new_since)
|
INSERT INTO user_stats (user_id, new_since)
|
||||||
SELECT id, created_at
|
SELECT id, created_at
|
||||||
FROM users
|
FROM users
|
||||||
@ -200,7 +200,7 @@ end
|
|||||||
def insert_user_visits
|
def insert_user_visits
|
||||||
log "Inserting user visits..."
|
log "Inserting user visits..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO user_visits (user_id, visited_at, posts_read)
|
INSERT INTO user_visits (user_id, visited_at, posts_read)
|
||||||
SELECT user_id, DATE(created_at), COUNT(*)
|
SELECT user_id, DATE(created_at), COUNT(*)
|
||||||
FROM posts
|
FROM posts
|
||||||
@ -213,7 +213,7 @@ end
|
|||||||
def insert_draft_sequences
|
def insert_draft_sequences
|
||||||
log "Inserting draft sequences..."
|
log "Inserting draft sequences..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
INSERT INTO draft_sequences (user_id, draft_key, sequence)
|
INSERT INTO draft_sequences (user_id, draft_key, sequence)
|
||||||
SELECT user_id, CONCAT('#{Draft::EXISTING_TOPIC}', id), 1
|
SELECT user_id, CONCAT('#{Draft::EXISTING_TOPIC}', id), 1
|
||||||
FROM topics
|
FROM topics
|
||||||
@ -226,7 +226,7 @@ end
|
|||||||
def update_user_stats
|
def update_user_stats
|
||||||
log "Updating user stats..."
|
log "Updating user stats..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT p.user_id
|
SELECT p.user_id
|
||||||
, COUNT(p.id) posts
|
, COUNT(p.id) posts
|
||||||
@ -283,7 +283,7 @@ end
|
|||||||
def update_posts
|
def update_posts
|
||||||
log "Updating posts..."
|
log "Updating posts..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH Y AS (
|
WITH Y AS (
|
||||||
SELECT post_id, COUNT(*) replies FROM post_replies GROUP BY post_id
|
SELECT post_id, COUNT(*) replies FROM post_replies GROUP BY post_id
|
||||||
)
|
)
|
||||||
@ -310,7 +310,7 @@ end
|
|||||||
def update_topics
|
def update_topics
|
||||||
log "Updating topics..."
|
log "Updating topics..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT topic_id
|
SELECT topic_id
|
||||||
, COUNT(*) posts
|
, COUNT(*) posts
|
||||||
@ -350,7 +350,7 @@ end
|
|||||||
def update_categories
|
def update_categories
|
||||||
log "Updating categories..."
|
log "Updating categories..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT category_id
|
SELECT category_id
|
||||||
, MAX(p.id) post_id
|
, MAX(p.id) post_id
|
||||||
@ -382,7 +382,7 @@ end
|
|||||||
def update_users
|
def update_users
|
||||||
log "Updating users..."
|
log "Updating users..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT user_id
|
SELECT user_id
|
||||||
, MIN(created_at) min_created_at
|
, MIN(created_at) min_created_at
|
||||||
@ -406,7 +406,7 @@ end
|
|||||||
def update_groups
|
def update_groups
|
||||||
log "Updating groups..."
|
log "Updating groups..."
|
||||||
|
|
||||||
exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
WITH X AS (
|
WITH X AS (
|
||||||
SELECT group_id, COUNT(*) count
|
SELECT group_id, COUNT(*) count
|
||||||
FROM group_users
|
FROM group_users
|
||||||
@ -428,12 +428,6 @@ def log(message)
|
|||||||
puts "[#{DateTime.now.strftime("%Y-%m-%d %H:%M:%S")}] #{message}"
|
puts "[#{DateTime.now.strftime("%Y-%m-%d %H:%M:%S")}] #{message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def exec_sql(sql)
|
|
||||||
ActiveRecord::Base.transaction do
|
|
||||||
ActiveRecord::Base.exec_sql(sql)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
task "import:create_phpbb_permalinks" => :environment do
|
task "import:create_phpbb_permalinks" => :environment do
|
||||||
log 'Creating Permalinks...'
|
log 'Creating Permalinks...'
|
||||||
|
|
||||||
@ -477,7 +471,6 @@ task "import:remap_old_phpbb_permalinks" => :environment do
|
|||||||
# skip
|
# skip
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
i
|
|
||||||
|
|
||||||
log "Done! #{i} posts remapped."
|
log "Done! #{i} posts remapped."
|
||||||
end
|
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.where("topic_id = :topic_id") if args[:topic_id]
|
||||||
builder.exec(topic_id: args[:topic_id])
|
builder.exec(topic_id: args[:topic_id])
|
||||||
|
|
||||||
Notification.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE notifications AS x
|
UPDATE notifications AS x
|
||||||
SET post_number = p.sort_order
|
SET post_number = p.sort_order
|
||||||
FROM posts AS p
|
FROM posts AS p
|
||||||
@ -313,7 +313,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args|
|
|||||||
p.post_number < 0
|
p.post_number < 0
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
PostTiming.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE post_timings AS x
|
UPDATE post_timings AS x
|
||||||
SET post_number = x.post_number * -1
|
SET post_number = x.post_number * -1
|
||||||
FROM posts AS p
|
FROM posts AS p
|
||||||
@ -329,7 +329,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args|
|
|||||||
p.post_number < 0;
|
p.post_number < 0;
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Post.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE posts AS x
|
UPDATE posts AS x
|
||||||
SET reply_to_post_number = p.sort_order
|
SET reply_to_post_number = p.sort_order
|
||||||
FROM posts AS p
|
FROM posts AS p
|
||||||
@ -338,7 +338,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args|
|
|||||||
p.post_number < 0;
|
p.post_number < 0;
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
TopicUser.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE topic_users AS x
|
UPDATE topic_users AS x
|
||||||
SET last_read_post_number = p.sort_order
|
SET last_read_post_number = p.sort_order
|
||||||
FROM posts AS p
|
FROM posts AS p
|
||||||
@ -362,7 +362,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args|
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
# finally update the post_number
|
# finally update the post_number
|
||||||
Post.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE posts
|
UPDATE posts
|
||||||
SET post_number = sort_order
|
SET post_number = sort_order
|
||||||
WHERE post_number < 0
|
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'
|
printf "%-25s | %-25s | %-25s | %-25s\n", 'username', 'total size of uploads', 'number of uploads', 'number of optimized images'
|
||||||
puts "-" * 110
|
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
|
printf "%-25s | %-25s | %-25s | %-25s\n", username, helper.number_to_human_size(total_size_of_uploads), num_of_uploads, num_of_optimized_images
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -285,14 +285,14 @@ class TopicView
|
|||||||
sql = <<~SQL
|
sql = <<~SQL
|
||||||
SELECT user_id, count(*) AS count_all
|
SELECT user_id, count(*) AS count_all
|
||||||
FROM posts
|
FROM posts
|
||||||
WHERE id IN (:post_ids)
|
WHERE id in (:post_ids)
|
||||||
AND user_id IS NOT NULL
|
AND user_id IS NOT NULL
|
||||||
GROUP BY user_id
|
GROUP BY user_id
|
||||||
ORDER BY count_all DESC
|
ORDER BY count_all DESC
|
||||||
LIMIT #{MAX_PARTICIPANTS}
|
LIMIT #{MAX_PARTICIPANTS}
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Hash[Post.exec_sql(sql, post_ids: post_ids).values]
|
Hash[*DB.query_single(sql, post_ids: post_ids)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ class TopicView
|
|||||||
WHERE id IN (:post_ids)
|
WHERE id IN (:post_ids)
|
||||||
AND user_id IS NOT NULL
|
AND user_id IS NOT NULL
|
||||||
SQL
|
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
|
else
|
||||||
participants.size
|
participants.size
|
||||||
end
|
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)
|
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
|
@changed_ids.concat @topic_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -626,7 +626,7 @@ class ImportScripts::Base
|
|||||||
def update_topic_status
|
def update_topic_status
|
||||||
puts "", "Updating topic status"
|
puts "", "Updating topic status"
|
||||||
|
|
||||||
Topic.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE topics AS t
|
UPDATE topics AS t
|
||||||
SET closed = TRUE
|
SET closed = TRUE
|
||||||
WHERE EXISTS(
|
WHERE EXISTS(
|
||||||
@ -636,7 +636,7 @@ class ImportScripts::Base
|
|||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Topic.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
UPDATE topics AS t
|
UPDATE topics AS t
|
||||||
SET archived = TRUE
|
SET archived = TRUE
|
||||||
WHERE EXISTS(
|
WHERE EXISTS(
|
||||||
@ -646,7 +646,7 @@ class ImportScripts::Base
|
|||||||
)
|
)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
TopicCustomField.exec_sql(<<~SQL)
|
DB.exec(<<~SQL)
|
||||||
DELETE FROM topic_custom_fields
|
DELETE FROM topic_custom_fields
|
||||||
WHERE name IN ('import_closed', 'import_archived')
|
WHERE name IN ('import_closed', 'import_archived')
|
||||||
SQL
|
SQL
|
||||||
@ -654,7 +654,7 @@ class ImportScripts::Base
|
|||||||
|
|
||||||
def update_bumped_at
|
def update_bumped_at
|
||||||
puts "", "Updating bumped_at on topics"
|
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
|
end
|
||||||
|
|
||||||
def update_last_posted_at
|
def update_last_posted_at
|
||||||
@ -674,7 +674,7 @@ class ImportScripts::Base
|
|||||||
AND users.last_posted_at <> lpa.last_posted_at
|
AND users.last_posted_at <> lpa.last_posted_at
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
User.exec_sql(sql)
|
DB.exec(sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_user_stats
|
def update_user_stats
|
||||||
@ -707,7 +707,7 @@ class ImportScripts::Base
|
|||||||
AND user_stats.first_post_created_at <> sub.first_post_created_at
|
AND user_stats.first_post_created_at <> sub.first_post_created_at
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
User.exec_sql(sql)
|
DB.exec(sql)
|
||||||
|
|
||||||
puts "", "Updating user post_count..."
|
puts "", "Updating user post_count..."
|
||||||
|
|
||||||
@ -725,7 +725,7 @@ class ImportScripts::Base
|
|||||||
AND user_stats.post_count <> sub.post_count
|
AND user_stats.post_count <> sub.post_count
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
User.exec_sql(sql)
|
DB.exec(sql)
|
||||||
|
|
||||||
puts "", "Updating user topic_count..."
|
puts "", "Updating user topic_count..."
|
||||||
|
|
||||||
@ -743,15 +743,15 @@ class ImportScripts::Base
|
|||||||
AND user_stats.topic_count <> sub.topic_count
|
AND user_stats.topic_count <> sub.topic_count
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
User.exec_sql(sql)
|
DB.exec(sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
# scripts that are able to import last_seen_at from the source data should override this method
|
# scripts that are able to import last_seen_at from the source data should override this method
|
||||||
def update_last_seen_at
|
def update_last_seen_at
|
||||||
puts "", "Updating last seen at on users"
|
puts "", "Updating last seen at on users"
|
||||||
|
|
||||||
User.exec_sql("UPDATE users SET last_seen_at = created_at WHERE last_seen_at IS NULL")
|
DB.exec("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 = last_posted_at WHERE last_posted_at IS NOT NULL")
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_feature_topic_users
|
def update_feature_topic_users
|
||||||
|
@ -267,7 +267,7 @@ class ImportScripts::IPBoard3 < ImportScripts::Base
|
|||||||
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Topic.exec_sql(sql, @closed_topic_ids)
|
DB.exec(sql, @closed_topic_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
def import_personal_topics
|
def import_personal_topics
|
||||||
|
@ -325,7 +325,7 @@ class ImportScripts::JiveApi < ImportScripts::Base
|
|||||||
def mark_topics_as_solved
|
def mark_topics_as_solved
|
||||||
puts "", "Marking 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)
|
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
|
SELECT 'accepted_answer_post_id', pcf.post_id, p.topic_id, p.created_at, p.created_at
|
||||||
FROM post_custom_fields pcf
|
FROM post_custom_fields pcf
|
||||||
|
@ -535,7 +535,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
puts "loading data into temp table"
|
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
|
PostAction.transaction do
|
||||||
results.each do |result|
|
results.each do |result|
|
||||||
|
|
||||||
@ -544,17 +544,17 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
|
|
||||||
next unless result["user_id"] && result["post_id"]
|
next unless result["user_id"] && result["post_id"]
|
||||||
|
|
||||||
PostAction.exec_sql("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)",
|
DB.exec("INSERT INTO like_data VALUES (:user_id,:post_id,:created_at)",
|
||||||
user_id: result["user_id"],
|
user_id: result["user_id"],
|
||||||
post_id: result["post_id"],
|
post_id: result["post_id"],
|
||||||
created_at: result["created_at"]
|
created_at: result["created_at"]
|
||||||
)
|
)
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "creating missing post actions"
|
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)
|
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
|
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
|
SQL
|
||||||
|
|
||||||
puts "creating missing user actions"
|
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)
|
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
|
SELECT pa.user_id, 1, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
|
||||||
FROM post_actions pa
|
FROM post_actions pa
|
||||||
@ -574,7 +574,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
# reverse action
|
# 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)
|
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
|
SELECT p.user_id, 2, p.topic_id, p.id, pa.user_id, pa.created_at, pa.created_at
|
||||||
FROM post_actions pa
|
FROM post_actions pa
|
||||||
@ -586,7 +586,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
SQL
|
SQL
|
||||||
puts "updating like counts on posts"
|
puts "updating like counts on posts"
|
||||||
|
|
||||||
Post.exec_sql <<-SQL
|
DB.exec <<~SQL
|
||||||
UPDATE posts SET like_count = coalesce(cnt,0)
|
UPDATE posts SET like_count = coalesce(cnt,0)
|
||||||
FROM (
|
FROM (
|
||||||
SELECT post_id, count(*) cnt
|
SELECT post_id, count(*) cnt
|
||||||
@ -600,7 +600,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
|
|
||||||
puts "updating like counts on topics"
|
puts "updating like counts on topics"
|
||||||
|
|
||||||
Post.exec_sql <<-SQL
|
DB.exec <<-SQL
|
||||||
UPDATE topics SET like_count = coalesce(cnt,0)
|
UPDATE topics SET like_count = coalesce(cnt,0)
|
||||||
FROM (
|
FROM (
|
||||||
SELECT topic_id, sum(like_count) cnt
|
SELECT topic_id, sum(like_count) cnt
|
||||||
@ -627,7 +627,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
puts "loading data into temp table"
|
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
|
PostAction.transaction do
|
||||||
results.each do |result|
|
results.each do |result|
|
||||||
|
|
||||||
@ -635,7 +635,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
|
|
||||||
next unless result["post_id"]
|
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"]
|
post_id: result["post_id"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -643,7 +643,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
puts "deleting dupe answers"
|
puts "deleting dupe answers"
|
||||||
PostAction.exec_sql <<-SQL
|
DB.exec <<~SQL
|
||||||
DELETE FROM accepted_data WHERE post_id NOT IN (
|
DELETE FROM accepted_data WHERE post_id NOT IN (
|
||||||
SELECT post_id FROM
|
SELECT post_id FROM
|
||||||
(
|
(
|
||||||
@ -656,7 +656,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
puts "importing accepted answers"
|
puts "importing accepted answers"
|
||||||
PostAction.exec_sql <<-SQL
|
DB.exec <<~SQL
|
||||||
INSERT into post_custom_fields (name, value, post_id, created_at, updated_at)
|
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
|
SELECT 'is_accepted_answer', 'true', a.post_id, current_timestamp, current_timestamp
|
||||||
FROM accepted_data a
|
FROM accepted_data a
|
||||||
@ -665,7 +665,7 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
SQL
|
SQL
|
||||||
|
|
||||||
puts "marking accepted topics"
|
puts "marking accepted topics"
|
||||||
PostAction.exec_sql <<-SQL
|
DB.exec <<~SQL
|
||||||
INSERT into topic_custom_fields (name, value, topic_id, created_at, updated_at)
|
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
|
SELECT 'accepted_answer_post_id', a.post_id::varchar, p.topic_id, current_timestamp, current_timestamp
|
||||||
FROM accepted_data a
|
FROM accepted_data a
|
||||||
@ -797,10 +797,10 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
|
|
||||||
results.map { |r| r["post_id"] }.each_slice(500) do |ids|
|
results.map { |r| r["post_id"] }.each_slice(500) do |ids|
|
||||||
mapped = ids.map { |id| existing_map[id] }.compact
|
mapped = ids.map { |id| existing_map[id] }.compact
|
||||||
Topic.exec_sql("
|
DB.exec(<<~SQL, ids: mapped) if mapped.present?
|
||||||
UPDATE topics SET closed = true
|
UPDATE topics SET closed = true
|
||||||
WHERE id IN (SELECT topic_id FROM posts where id in (:ids))
|
WHERE id IN (SELECT topic_id FROM posts where id in (:ids))
|
||||||
", ids: mapped) if mapped.present?
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@ -819,8 +819,8 @@ class ImportScripts::Lithium < ImportScripts::Base
|
|||||||
WHERE pm.id IS NULL AND f.name = 'import_unique_id'
|
WHERE pm.id IS NULL AND f.name = 'import_unique_id'
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
r = Permalink.exec_sql sql
|
r = DB.exec sql
|
||||||
puts "#{r.cmd_tuples} permalinks to topics added!"
|
puts "#{r} permalinks to topics added!"
|
||||||
|
|
||||||
sql = <<-SQL
|
sql = <<-SQL
|
||||||
INSERT INTO permalinks (url, post_id, created_at, updated_at)
|
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'
|
WHERE pm.id IS NULL AND f.name = 'import_unique_id'
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
r = Permalink.exec_sql sql
|
r = DB.exec sql
|
||||||
puts "#{r.cmd_tuples} permalinks to posts added!"
|
puts "#{r} permalinks to posts added!"
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -236,7 +236,7 @@ FROM #{TABLE_PREFIX}discuss_users
|
|||||||
def not_mark_topics_as_solved
|
def not_mark_topics_as_solved
|
||||||
puts "", "Marking 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)
|
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
|
SELECT 'accepted_answer_post_id', pcf.post_id, p.topic_id, p.created_at, p.created_at
|
||||||
FROM post_custom_fields pcf
|
FROM post_custom_fields pcf
|
||||||
@ -469,7 +469,7 @@ FROM #{TABLE_PREFIX}discuss_users
|
|||||||
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Topic.exec_sql(sql, closed_topic_ids)
|
DB.exec(sql, closed_topic_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
def not_post_process_posts
|
def not_post_process_posts
|
||||||
|
@ -237,7 +237,7 @@ class ImportScripts::StackOverflow < ImportScripts::Base
|
|||||||
def mark_topics_as_solved
|
def mark_topics_as_solved
|
||||||
puts "", "Marking 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)
|
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
|
SELECT 'accepted_answer_post_id', pcf.post_id, p.topic_id, p.created_at, p.created_at
|
||||||
FROM post_custom_fields pcf
|
FROM post_custom_fields pcf
|
||||||
|
@ -182,10 +182,8 @@ EOM
|
|||||||
next if user_ids_in_group.size == 0
|
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(",")
|
values = user_ids_in_group.map { |user_id| "(#{group.id}, #{user_id}, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)" }.join(",")
|
||||||
|
|
||||||
User.exec_sql <<-SQL
|
DB.exec <<~SQL
|
||||||
BEGIN;
|
INSERT INTO group_users (group_id, user_id, created_at, updated_at) VALUES #{values}
|
||||||
INSERT INTO group_users (group_id, user_id, created_at, updated_at) VALUES #{values};
|
|
||||||
COMMIT;
|
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Group.reset_counters(group.id, :group_users)
|
Group.reset_counters(group.id, :group_users)
|
||||||
@ -634,7 +632,7 @@ EOM
|
|||||||
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Topic.exec_sql(sql, closed_topic_ids)
|
DB.exec(sql, closed_topic_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
def post_process_posts
|
def post_process_posts
|
||||||
|
@ -418,7 +418,7 @@ class ImportScripts::VBulletin < ImportScripts::Base
|
|||||||
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
WHERE id IN (SELECT topic_id FROM closed_topic_ids)
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
Topic.exec_sql(sql, @closed_topic_ids)
|
DB.exec(sql, @closed_topic_ids)
|
||||||
end
|
end
|
||||||
|
|
||||||
def post_process_posts
|
def post_process_posts
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user