DEV: remove all calls to SqlBuilder use DB.build instead
This is part of the migration to mini_sql, SqlBuilder.new is being deprecated and replaced with DB.build
This commit is contained in:
parent
76707eec1b
commit
cb824a6b33
14
Gemfile.lock
14
Gemfile.lock
|
@ -43,8 +43,8 @@ GEM
|
||||||
rake (>= 10.4, < 13.0)
|
rake (>= 10.4, < 13.0)
|
||||||
arel (9.0.0)
|
arel (9.0.0)
|
||||||
ast (2.4.0)
|
ast (2.4.0)
|
||||||
aws-eventstream (1.0.0)
|
aws-eventstream (1.0.1)
|
||||||
aws-partitions (1.91.0)
|
aws-partitions (1.92.0)
|
||||||
aws-sdk-core (3.21.2)
|
aws-sdk-core (3.21.2)
|
||||||
aws-eventstream (~> 1.0)
|
aws-eventstream (~> 1.0)
|
||||||
aws-partitions (~> 1.0)
|
aws-partitions (~> 1.0)
|
||||||
|
@ -53,7 +53,7 @@ GEM
|
||||||
aws-sdk-kms (1.5.0)
|
aws-sdk-kms (1.5.0)
|
||||||
aws-sdk-core (~> 3)
|
aws-sdk-core (~> 3)
|
||||||
aws-sigv4 (~> 1.0)
|
aws-sigv4 (~> 1.0)
|
||||||
aws-sdk-s3 (1.13.0)
|
aws-sdk-s3 (1.14.0)
|
||||||
aws-sdk-core (~> 3, >= 3.21.2)
|
aws-sdk-core (~> 3, >= 3.21.2)
|
||||||
aws-sdk-kms (~> 1)
|
aws-sdk-kms (~> 1)
|
||||||
aws-sigv4 (~> 1.0)
|
aws-sigv4 (~> 1.0)
|
||||||
|
@ -108,7 +108,7 @@ GEM
|
||||||
erubi (1.7.1)
|
erubi (1.7.1)
|
||||||
excon (0.62.0)
|
excon (0.62.0)
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
exifr (1.2.5)
|
exifr (1.3.4)
|
||||||
fabrication (2.20.1)
|
fabrication (2.20.1)
|
||||||
fakeweb (1.3.0)
|
fakeweb (1.3.0)
|
||||||
faraday (0.12.2)
|
faraday (0.12.2)
|
||||||
|
@ -130,7 +130,7 @@ GEM
|
||||||
guess_html_encoding (0.0.11)
|
guess_html_encoding (0.0.11)
|
||||||
hashdiff (0.3.7)
|
hashdiff (0.3.7)
|
||||||
hashie (3.5.7)
|
hashie (3.5.7)
|
||||||
highline (1.7.10)
|
highline (2.0.0)
|
||||||
hiredis (0.6.1)
|
hiredis (0.6.1)
|
||||||
hkdf (0.3.0)
|
hkdf (0.3.0)
|
||||||
htmlentities (4.3.4)
|
htmlentities (4.3.4)
|
||||||
|
@ -176,7 +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.5)
|
mini_sql (0.1.9)
|
||||||
mini_suffix (0.3.0)
|
mini_suffix (0.3.0)
|
||||||
ffi (~> 1.9)
|
ffi (~> 1.9)
|
||||||
minitest (5.11.3)
|
minitest (5.11.3)
|
||||||
|
@ -261,7 +261,7 @@ GEM
|
||||||
rack-openid (1.3.1)
|
rack-openid (1.3.1)
|
||||||
rack (>= 1.1.0)
|
rack (>= 1.1.0)
|
||||||
ruby-openid (>= 2.1.8)
|
ruby-openid (>= 2.1.8)
|
||||||
rack-protection (2.0.2)
|
rack-protection (2.0.3)
|
||||||
rack
|
rack
|
||||||
rack-test (1.0.0)
|
rack-test (1.0.0)
|
||||||
rack (>= 1.0, < 3)
|
rack (>= 1.0, < 3)
|
||||||
|
|
|
@ -64,13 +64,13 @@ class CategoryUser < ActiveRecord::Base
|
||||||
|
|
||||||
def self.auto_track(opts = {})
|
def self.auto_track(opts = {})
|
||||||
|
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
UPDATE topic_users tu
|
UPDATE topic_users tu
|
||||||
SET notification_level = :tracking,
|
SET notification_level = :tracking,
|
||||||
notifications_reason_id = :auto_track_category
|
notifications_reason_id = :auto_track_category
|
||||||
FROM topics t, category_users cu
|
FROM topics t, category_users cu
|
||||||
/*where*/
|
/*where*/
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
builder.where("tu.topic_id = t.id AND
|
builder.where("tu.topic_id = t.id AND
|
||||||
cu.category_id = t.category_id AND
|
cu.category_id = t.category_id AND
|
||||||
|
@ -90,45 +90,47 @@ SQL
|
||||||
builder.where("tu.user_id = :user_id", user_id: user_id)
|
builder.where("tu.user_id = :user_id", user_id: user_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
builder.exec(tracking: notification_levels[:tracking],
|
builder.exec(
|
||||||
regular: notification_levels[:regular],
|
tracking: notification_levels[:tracking],
|
||||||
auto_track_category: TopicUser.notification_reasons[:auto_track_category])
|
regular: notification_levels[:regular],
|
||||||
|
auto_track_category: TopicUser.notification_reasons[:auto_track_category]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.auto_watch(opts = {})
|
def self.auto_watch(opts = {})
|
||||||
|
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
UPDATE topic_users tu
|
UPDATE topic_users tu
|
||||||
SET notification_level =
|
SET notification_level =
|
||||||
CASE WHEN should_track THEN :tracking
|
CASE WHEN should_track THEN :tracking
|
||||||
WHEN should_watch THEN :watching
|
WHEN should_watch THEN :watching
|
||||||
ELSE notification_level
|
ELSE notification_level
|
||||||
END,
|
END,
|
||||||
notifications_reason_id =
|
notifications_reason_id =
|
||||||
CASE WHEN should_track THEN null
|
CASE WHEN should_track THEN null
|
||||||
WHEN should_watch THEN :auto_watch_category
|
WHEN should_watch THEN :auto_watch_category
|
||||||
ELSE notifications_reason_id
|
ELSE notifications_reason_id
|
||||||
END
|
END
|
||||||
FROM (
|
FROM (
|
||||||
SELECT tu1.topic_id,
|
SELECT tu1.topic_id,
|
||||||
tu1.user_id,
|
tu1.user_id,
|
||||||
CASE WHEN
|
CASE WHEN
|
||||||
cu.user_id IS NULL AND tu1.notification_level = :watching AND tu1.notifications_reason_id = :auto_watch_category THEN true
|
cu.user_id IS NULL AND tu1.notification_level = :watching AND tu1.notifications_reason_id = :auto_watch_category THEN true
|
||||||
|
ELSE false
|
||||||
|
END should_track,
|
||||||
|
CASE WHEN
|
||||||
|
cu.user_id IS NOT NULL AND tu1.notification_level in (:regular, :tracking) THEN true
|
||||||
ELSE false
|
ELSE false
|
||||||
END should_track,
|
END should_watch
|
||||||
CASE WHEN
|
|
||||||
cu.user_id IS NOT NULL AND tu1.notification_level in (:regular, :tracking) THEN true
|
|
||||||
ELSE false
|
|
||||||
END should_watch
|
|
||||||
|
|
||||||
FROM topic_users tu1
|
FROM topic_users tu1
|
||||||
JOIN topics t ON t.id = tu1.topic_id
|
JOIN topics t ON t.id = tu1.topic_id
|
||||||
LEFT JOIN category_users cu ON cu.category_id = t.category_id AND cu.user_id = tu1.user_id AND cu.notification_level = :watching
|
LEFT JOIN category_users cu ON cu.category_id = t.category_id AND cu.user_id = tu1.user_id AND cu.notification_level = :watching
|
||||||
/*where2*/
|
/*where2*/
|
||||||
) as X
|
) as X
|
||||||
|
|
||||||
/*where*/
|
/*where*/
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
builder.where("X.topic_id = tu.topic_id AND X.user_id = tu.user_id")
|
builder.where("X.topic_id = tu.topic_id AND X.user_id = tu.user_id")
|
||||||
builder.where("should_watch OR should_track")
|
builder.where("should_watch OR should_track")
|
||||||
|
@ -147,10 +149,12 @@ SQL
|
||||||
builder.where2("tu1.user_id = :user_id", user_id: user_id)
|
builder.where2("tu1.user_id = :user_id", user_id: user_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
builder.exec(watching: notification_levels[:watching],
|
builder.exec(
|
||||||
tracking: notification_levels[:tracking],
|
watching: notification_levels[:watching],
|
||||||
regular: notification_levels[:regular],
|
tracking: notification_levels[:tracking],
|
||||||
auto_watch_category: TopicUser.notification_reasons[:auto_watch_category])
|
regular: notification_levels[:regular],
|
||||||
|
auto_watch_category: TopicUser.notification_reasons[:auto_watch_category]
|
||||||
|
)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -567,7 +567,7 @@ class Post < ActiveRecord::Base
|
||||||
# each post.
|
# each post.
|
||||||
def self.calculate_avg_time(min_topic_age = nil)
|
def self.calculate_avg_time(min_topic_age = nil)
|
||||||
retry_lock_error do
|
retry_lock_error do
|
||||||
builder = SqlBuilder.new("UPDATE posts
|
builder = DB.build("UPDATE posts
|
||||||
SET avg_time = (x.gmean / 1000)
|
SET avg_time = (x.gmean / 1000)
|
||||||
FROM (SELECT post_timings.topic_id,
|
FROM (SELECT post_timings.topic_id,
|
||||||
post_timings.post_number,
|
post_timings.post_number,
|
||||||
|
@ -692,7 +692,7 @@ class Post < ActiveRecord::Base
|
||||||
MAX_REPLY_LEVEL ||= 1000
|
MAX_REPLY_LEVEL ||= 1000
|
||||||
|
|
||||||
def reply_ids(guardian = nil, only_replies_to_single_post: true)
|
def reply_ids(guardian = nil, only_replies_to_single_post: true)
|
||||||
builder = SqlBuilder.new(<<~SQL, Post)
|
builder = DB.build(<<~SQL)
|
||||||
WITH RECURSIVE breadcrumb(id, level) AS (
|
WITH RECURSIVE breadcrumb(id, level) AS (
|
||||||
SELECT :post_id, 0
|
SELECT :post_id, 0
|
||||||
UNION
|
UNION
|
||||||
|
@ -723,8 +723,8 @@ class Post < ActiveRecord::Base
|
||||||
# for example it skips a post when it contains 2 quotes (which are replies) from different posts
|
# for example it skips a post when it contains 2 quotes (which are replies) from different posts
|
||||||
builder.where("count = 1") if only_replies_to_single_post
|
builder.where("count = 1") if only_replies_to_single_post
|
||||||
|
|
||||||
replies = builder.exec(post_id: id, max_reply_level: MAX_REPLY_LEVEL).to_a
|
replies = builder.query_hash(post_id: id, max_reply_level: MAX_REPLY_LEVEL)
|
||||||
replies.map! { |r| { id: r["id"].to_i, level: r["level"].to_i } }
|
replies.each { |r| r.symbolize_keys! }
|
||||||
|
|
||||||
secured_ids = Post.secured(guardian).where(id: replies.map { |r| r[:id] }).pluck(:id).to_set
|
secured_ids = Post.secured(guardian).where(id: replies.map { |r| r[:id] }).pluck(:id).to_set
|
||||||
|
|
||||||
|
|
|
@ -101,18 +101,19 @@ class PostAction < ActiveRecord::Base
|
||||||
#
|
#
|
||||||
topic_ids = topics.map(&:id)
|
topic_ids = topics.map(&:id)
|
||||||
map = {}
|
map = {}
|
||||||
builder = SqlBuilder.new <<SQL
|
|
||||||
SELECT p.topic_id, p.post_number
|
|
||||||
FROM post_actions pa
|
|
||||||
JOIN posts p ON pa.post_id = p.id
|
|
||||||
WHERE p.deleted_at IS NULL AND pa.deleted_at IS NULL AND
|
|
||||||
pa.post_action_type_id = :post_action_type_id AND
|
|
||||||
pa.user_id = :user_id AND
|
|
||||||
p.topic_id IN (:topic_ids)
|
|
||||||
ORDER BY p.topic_id, p.post_number
|
|
||||||
SQL
|
|
||||||
|
|
||||||
builder.map_exec(OpenStruct, user_id: user.id, post_action_type_id: post_action_type_id, topic_ids: topic_ids).each do |row|
|
builder = DB.build <<~SQL
|
||||||
|
SELECT p.topic_id, p.post_number
|
||||||
|
FROM post_actions pa
|
||||||
|
JOIN posts p ON pa.post_id = p.id
|
||||||
|
WHERE p.deleted_at IS NULL AND pa.deleted_at IS NULL AND
|
||||||
|
pa.post_action_type_id = :post_action_type_id AND
|
||||||
|
pa.user_id = :user_id AND
|
||||||
|
p.topic_id IN (:topic_ids)
|
||||||
|
ORDER BY p.topic_id, p.post_number
|
||||||
|
SQL
|
||||||
|
|
||||||
|
builder.query(user_id: user.id, post_action_type_id: post_action_type_id, topic_ids: topic_ids).each do |row|
|
||||||
(map[row.topic_id] ||= []) << row.post_number
|
(map[row.topic_id] ||= []) << row.post_number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -66,40 +66,39 @@ class TagUser < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.auto_watch(opts)
|
def self.auto_watch(opts)
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
|
UPDATE topic_users
|
||||||
|
SET notification_level = CASE WHEN should_watch THEN :watching ELSE :tracking END,
|
||||||
|
notifications_reason_id = CASE WHEN should_watch THEN :auto_watch_tag ELSE NULL END
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT tu.topic_id, tu.user_id, CASE
|
||||||
|
WHEN MAX(tag_users.notification_level) = :watching THEN true
|
||||||
|
ELSE false
|
||||||
|
END
|
||||||
|
should_watch,
|
||||||
|
|
||||||
UPDATE topic_users
|
CASE WHEN MAX(tag_users.notification_level) IS NULL AND
|
||||||
SET notification_level = CASE WHEN should_watch THEN :watching ELSE :tracking END,
|
tu.notification_level = :watching AND
|
||||||
notifications_reason_id = CASE WHEN should_watch THEN :auto_watch_tag ELSE NULL END
|
tu.notifications_reason_id = :auto_watch_tag
|
||||||
FROM
|
THEN true
|
||||||
(
|
ELSE false
|
||||||
SELECT tu.topic_id, tu.user_id, CASE
|
END
|
||||||
WHEN MAX(tag_users.notification_level) = :watching THEN true
|
should_track
|
||||||
ELSE false
|
|
||||||
END
|
|
||||||
should_watch,
|
|
||||||
|
|
||||||
CASE WHEN MAX(tag_users.notification_level) IS NULL AND
|
FROM topic_users tu
|
||||||
tu.notification_level = :watching AND
|
LEFT JOIN topic_tags ON tu.topic_id = topic_tags.topic_id
|
||||||
tu.notifications_reason_id = :auto_watch_tag
|
LEFT JOIN tag_users ON tag_users.user_id = tu.user_id
|
||||||
THEN true
|
AND topic_tags.tag_id = tag_users.tag_id
|
||||||
ELSE false
|
AND tag_users.notification_level = :watching
|
||||||
END
|
/*where*/
|
||||||
should_track
|
GROUP BY tu.topic_id, tu.user_id, tu.notification_level, tu.notifications_reason_id
|
||||||
|
) AS X
|
||||||
|
WHERE X.topic_id = topic_users.topic_id AND
|
||||||
|
X.user_id = topic_users.user_id AND
|
||||||
|
(should_track OR should_watch)
|
||||||
|
|
||||||
FROM topic_users tu
|
SQL
|
||||||
LEFT JOIN topic_tags ON tu.topic_id = topic_tags.topic_id
|
|
||||||
LEFT JOIN tag_users ON tag_users.user_id = tu.user_id
|
|
||||||
AND topic_tags.tag_id = tag_users.tag_id
|
|
||||||
AND tag_users.notification_level = :watching
|
|
||||||
/*where*/
|
|
||||||
GROUP BY tu.topic_id, tu.user_id, tu.notification_level, tu.notifications_reason_id
|
|
||||||
) AS X
|
|
||||||
WHERE X.topic_id = topic_users.topic_id AND
|
|
||||||
X.user_id = topic_users.user_id AND
|
|
||||||
(should_track OR should_watch)
|
|
||||||
|
|
||||||
SQL
|
|
||||||
|
|
||||||
builder.where("tu.notification_level in (:tracking, :regular, :watching)")
|
builder.where("tu.notification_level in (:tracking, :regular, :watching)")
|
||||||
|
|
||||||
|
@ -120,23 +119,23 @@ SQL
|
||||||
|
|
||||||
def self.auto_track(opts)
|
def self.auto_track(opts)
|
||||||
|
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
UPDATE topic_users
|
UPDATE topic_users
|
||||||
SET notification_level = :tracking, notifications_reason_id = :auto_track_tag
|
SET notification_level = :tracking, notifications_reason_id = :auto_track_tag
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT tu.topic_id, tu.user_id
|
SELECT DISTINCT tu.topic_id, tu.user_id
|
||||||
FROM topic_users tu
|
FROM topic_users tu
|
||||||
JOIN topic_tags ON tu.topic_id = topic_tags.topic_id
|
JOIN topic_tags ON tu.topic_id = topic_tags.topic_id
|
||||||
JOIN tag_users ON tag_users.user_id = tu.user_id
|
JOIN tag_users ON tag_users.user_id = tu.user_id
|
||||||
AND topic_tags.tag_id = tag_users.tag_id
|
AND topic_tags.tag_id = tag_users.tag_id
|
||||||
AND tag_users.notification_level = :tracking
|
AND tag_users.notification_level = :tracking
|
||||||
/*where*/
|
/*where*/
|
||||||
) as X
|
) as X
|
||||||
WHERE
|
WHERE
|
||||||
topic_users.notification_level = :regular AND
|
topic_users.notification_level = :regular AND
|
||||||
topic_users.topic_id = X.topic_id AND
|
topic_users.topic_id = X.topic_id AND
|
||||||
topic_users.user_id = X.user_id
|
topic_users.user_id = X.user_id
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
if topic_id = opts[:topic_id]
|
if topic_id = opts[:topic_id]
|
||||||
builder.where("tu.topic_id = :topic_id", topic_id: topic_id)
|
builder.where("tu.topic_id = :topic_id", topic_id: topic_id)
|
||||||
|
|
|
@ -1241,7 +1241,7 @@ class Topic < ActiveRecord::Base
|
||||||
|
|
||||||
def self.time_to_first_response(sql, opts = nil)
|
def self.time_to_first_response(sql, opts = nil)
|
||||||
opts ||= {}
|
opts ||= {}
|
||||||
builder = SqlBuilder.new(sql)
|
builder = DB.build(sql)
|
||||||
builder.where("t.created_at >= :start_date", start_date: opts[:start_date]) if opts[:start_date]
|
builder.where("t.created_at >= :start_date", start_date: opts[:start_date]) if opts[:start_date]
|
||||||
builder.where("t.created_at < :end_date", end_date: opts[:end_date]) if opts[:end_date]
|
builder.where("t.created_at < :end_date", end_date: opts[:end_date]) if opts[:end_date]
|
||||||
builder.where("t.category_id = :category_id", category_id: opts[:category_id]) if opts[:category_id]
|
builder.where("t.category_id = :category_id", category_id: opts[:category_id]) if opts[:category_id]
|
||||||
|
@ -1253,7 +1253,7 @@ class Topic < ActiveRecord::Base
|
||||||
builder.where("p.user_id in (:user_ids)", user_ids: opts[:user_ids]) if opts[:user_ids]
|
builder.where("p.user_id in (:user_ids)", user_ids: opts[:user_ids]) if opts[:user_ids]
|
||||||
builder.where("p.post_type = :post_type", post_type: Post.types[:regular])
|
builder.where("p.post_type = :post_type", post_type: Post.types[:regular])
|
||||||
builder.where("EXTRACT(EPOCH FROM p.created_at - t.created_at) > 0")
|
builder.where("EXTRACT(EPOCH FROM p.created_at - t.created_at) > 0")
|
||||||
builder.exec
|
builder.query_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.time_to_first_response_per_day(start_date, end_date, opts = {})
|
def self.time_to_first_response_per_day(start_date, end_date, opts = {})
|
||||||
|
@ -1280,13 +1280,13 @@ class Topic < ActiveRecord::Base
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
def self.with_no_response_per_day(start_date, end_date, category_id = nil)
|
def self.with_no_response_per_day(start_date, end_date, category_id = nil)
|
||||||
builder = SqlBuilder.new(WITH_NO_RESPONSE_SQL)
|
builder = DB.build(WITH_NO_RESPONSE_SQL)
|
||||||
builder.where("t.created_at >= :start_date", start_date: start_date) if start_date
|
builder.where("t.created_at >= :start_date", start_date: start_date) if start_date
|
||||||
builder.where("t.created_at < :end_date", end_date: end_date) if end_date
|
builder.where("t.created_at < :end_date", end_date: end_date) if end_date
|
||||||
builder.where("t.category_id = :category_id", category_id: category_id) if category_id
|
builder.where("t.category_id = :category_id", category_id: category_id) if category_id
|
||||||
builder.where("t.archetype <> '#{Archetype.private_message}'")
|
builder.where("t.archetype <> '#{Archetype.private_message}'")
|
||||||
builder.where("t.deleted_at IS NULL")
|
builder.where("t.deleted_at IS NULL")
|
||||||
builder.exec
|
builder.query_hash
|
||||||
end
|
end
|
||||||
|
|
||||||
WITH_NO_RESPONSE_TOTAL_SQL ||= <<-SQL
|
WITH_NO_RESPONSE_TOTAL_SQL ||= <<-SQL
|
||||||
|
@ -1302,11 +1302,11 @@ class Topic < ActiveRecord::Base
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
def self.with_no_response_total(opts = {})
|
def self.with_no_response_total(opts = {})
|
||||||
builder = SqlBuilder.new(WITH_NO_RESPONSE_TOTAL_SQL)
|
builder = DB.build(WITH_NO_RESPONSE_TOTAL_SQL)
|
||||||
builder.where("t.category_id = :category_id", category_id: opts[:category_id]) if opts[:category_id]
|
builder.where("t.category_id = :category_id", category_id: opts[:category_id]) if opts[:category_id]
|
||||||
builder.where("t.archetype <> '#{Archetype.private_message}'")
|
builder.where("t.archetype <> '#{Archetype.private_message}'")
|
||||||
builder.where("t.deleted_at IS NULL")
|
builder.where("t.deleted_at IS NULL")
|
||||||
builder.exec.first["count"].to_i
|
builder.query_single.first.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def convert_to_public_topic(user)
|
def convert_to_public_topic(user)
|
||||||
|
|
|
@ -174,8 +174,12 @@ class TopicTrackingState
|
||||||
sql << "\nUNION ALL\n\n"
|
sql << "\nUNION ALL\n\n"
|
||||||
sql << report_raw_sql(topic_id: topic_id, skip_new: true, skip_order: true, staff: user.staff?)
|
sql << report_raw_sql(topic_id: topic_id, skip_new: true, skip_order: true, staff: user.staff?)
|
||||||
|
|
||||||
SqlBuilder.new(sql)
|
DB.query(
|
||||||
.map_exec(TopicTrackingState, user_id: user.id, topic_id: topic_id, min_new_topic_date: Time.at(SiteSetting.min_new_topics_time).to_datetime)
|
sql,
|
||||||
|
user_id: user.id,
|
||||||
|
topic_id: topic_id,
|
||||||
|
min_new_topic_date: Time.at(SiteSetting.min_new_topics_time).to_datetime
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.report_raw_sql(opts = nil)
|
def self.report_raw_sql(opts = nil)
|
||||||
|
|
|
@ -370,28 +370,28 @@ class TopicUser < ActiveRecord::Base
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
UPDATE topic_users tu
|
UPDATE topic_users tu
|
||||||
SET #{action_type_name} = x.state
|
SET #{action_type_name} = x.state
|
||||||
FROM (
|
FROM (
|
||||||
SELECT CASE WHEN EXISTS (
|
SELECT CASE WHEN EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM post_actions pa
|
FROM post_actions pa
|
||||||
JOIN posts p on p.id = pa.post_id
|
JOIN posts p on p.id = pa.post_id
|
||||||
JOIN topics t ON t.id = p.topic_id
|
JOIN topics t ON t.id = p.topic_id
|
||||||
WHERE pa.deleted_at IS NULL AND
|
WHERE pa.deleted_at IS NULL AND
|
||||||
p.deleted_at IS NULL AND
|
p.deleted_at IS NULL AND
|
||||||
t.deleted_at IS NULL AND
|
t.deleted_at IS NULL AND
|
||||||
pa.post_action_type_id = :action_type_id AND
|
pa.post_action_type_id = :action_type_id AND
|
||||||
tu2.topic_id = t.id AND
|
tu2.topic_id = t.id AND
|
||||||
tu2.user_id = pa.user_id
|
tu2.user_id = pa.user_id
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
) THEN true ELSE false END state, tu2.topic_id, tu2.user_id
|
) THEN true ELSE false END state, tu2.topic_id, tu2.user_id
|
||||||
FROM topic_users tu2
|
FROM topic_users tu2
|
||||||
/*where*/
|
/*where*/
|
||||||
) x
|
) x
|
||||||
WHERE x.topic_id = tu.topic_id AND x.user_id = tu.user_id AND x.state != tu.#{action_type_name}
|
WHERE x.topic_id = tu.topic_id AND x.user_id = tu.user_id AND x.state != tu.#{action_type_name}
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
if user_id
|
if user_id
|
||||||
builder.where("tu2.user_id = :user_id", user_id: user_id)
|
builder.where("tu2.user_id = :user_id", user_id: user_id)
|
||||||
|
@ -440,32 +440,31 @@ SQL
|
||||||
# we up these numbers so they are not in-sync
|
# we up these numbers so they are not in-sync
|
||||||
# the simple fix is to add a column here, but table is already quite big
|
# the simple fix is to add a column here, but table is already quite big
|
||||||
# long term we want to split up topic_users and allow for this better
|
# long term we want to split up topic_users and allow for this better
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
|
UPDATE topic_users t
|
||||||
|
SET
|
||||||
|
last_read_post_number = LEAST(GREATEST(last_read, last_read_post_number), max_post_number),
|
||||||
|
highest_seen_post_number = LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read))
|
||||||
|
FROM (
|
||||||
|
SELECT topic_id, user_id, MAX(post_number) last_read
|
||||||
|
FROM post_timings
|
||||||
|
GROUP BY topic_id, user_id
|
||||||
|
) as X
|
||||||
|
JOIN (
|
||||||
|
SELECT p.topic_id, MAX(p.post_number) max_post_number from posts p
|
||||||
|
GROUP BY p.topic_id
|
||||||
|
) as Y on Y.topic_id = X.topic_id
|
||||||
|
/*where*/
|
||||||
|
SQL
|
||||||
|
|
||||||
UPDATE topic_users t
|
builder.where <<~SQL
|
||||||
SET
|
X.topic_id = t.topic_id AND
|
||||||
last_read_post_number = LEAST(GREATEST(last_read, last_read_post_number), max_post_number),
|
X.user_id = t.user_id AND
|
||||||
highest_seen_post_number = LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read))
|
(
|
||||||
FROM (
|
last_read_post_number <> LEAST(GREATEST(last_read, last_read_post_number), max_post_number) OR
|
||||||
SELECT topic_id, user_id, MAX(post_number) last_read
|
highest_seen_post_number <> LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read))
|
||||||
FROM post_timings
|
)
|
||||||
GROUP BY topic_id, user_id
|
SQL
|
||||||
) as X
|
|
||||||
JOIN (
|
|
||||||
SELECT p.topic_id, MAX(p.post_number) max_post_number from posts p
|
|
||||||
GROUP BY p.topic_id
|
|
||||||
) as Y on Y.topic_id = X.topic_id
|
|
||||||
/*where*/
|
|
||||||
SQL
|
|
||||||
|
|
||||||
builder.where <<SQL
|
|
||||||
X.topic_id = t.topic_id AND
|
|
||||||
X.user_id = t.user_id AND
|
|
||||||
(
|
|
||||||
last_read_post_number <> LEAST(GREATEST(last_read, last_read_post_number), max_post_number) OR
|
|
||||||
highest_seen_post_number <> LEAST(max_post_number,GREATEST(t.highest_seen_post_number, last_read))
|
|
||||||
)
|
|
||||||
SQL
|
|
||||||
|
|
||||||
if topic_id
|
if topic_id
|
||||||
builder.where("t.topic_id = :topic_id", topic_id: topic_id)
|
builder.where("t.topic_id = :topic_id", topic_id: topic_id)
|
||||||
|
|
|
@ -28,7 +28,7 @@ class TopicViewItem < ActiveRecord::Base
|
||||||
/*where*/
|
/*where*/
|
||||||
)"
|
)"
|
||||||
|
|
||||||
builder = SqlBuilder.new(sql)
|
builder = DB.build(sql)
|
||||||
|
|
||||||
if !user_id
|
if !user_id
|
||||||
builder.where("ip_address = :ip_address AND topic_id = :topic_id AND user_id IS NULL")
|
builder.where("ip_address = :ip_address AND topic_id = :topic_id AND user_id IS NULL")
|
||||||
|
@ -41,7 +41,7 @@ class TopicViewItem < ActiveRecord::Base
|
||||||
|
|
||||||
Topic.where(id: topic_id).update_all 'views = views + 1'
|
Topic.where(id: topic_id).update_all 'views = views + 1'
|
||||||
|
|
||||||
if result.cmd_tuples > 0
|
if result > 0
|
||||||
UserStat.where(user_id: user_id).update_all 'topics_entered = topics_entered + 1' if user_id
|
UserStat.where(user_id: user_id).update_all 'topics_entered = topics_entered + 1' if user_id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -39,16 +39,6 @@ class UserAction < ActiveRecord::Base
|
||||||
ASSIGNED,
|
ASSIGNED,
|
||||||
].each_with_index.to_a.flatten]
|
].each_with_index.to_a.flatten]
|
||||||
|
|
||||||
# note, this is temporary until we upgrade to rails 4
|
|
||||||
# in rails 4 types are mapped correctly so you dont end up
|
|
||||||
# having strings where you would expect bools
|
|
||||||
class UserActionRow < OpenStruct
|
|
||||||
include ActiveModel::SerializerSupport
|
|
||||||
|
|
||||||
def as_json(options = nil)
|
|
||||||
@table.as_json(options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.last_action_in_topic(user_id, topic_id)
|
def self.last_action_in_topic(user_id, topic_id)
|
||||||
UserAction.where(user_id: user_id,
|
UserAction.where(user_id: user_id,
|
||||||
|
@ -59,25 +49,24 @@ class UserAction < ActiveRecord::Base
|
||||||
def self.stats(user_id, guardian)
|
def self.stats(user_id, guardian)
|
||||||
|
|
||||||
# Sam: I tried this in AR and it got complex
|
# Sam: I tried this in AR and it got complex
|
||||||
builder = UserAction.sql_builder <<SQL
|
builder = DB.build <<~SQL
|
||||||
|
|
||||||
SELECT action_type, COUNT(*) count
|
SELECT action_type, COUNT(*) count
|
||||||
FROM user_actions a
|
FROM user_actions a
|
||||||
LEFT JOIN topics t ON t.id = a.target_topic_id
|
LEFT JOIN topics t ON t.id = a.target_topic_id
|
||||||
LEFT JOIN posts p on p.id = a.target_post_id
|
LEFT JOIN posts p on p.id = a.target_post_id
|
||||||
LEFT JOIN posts p2 on p2.topic_id = a.target_topic_id and p2.post_number = 1
|
LEFT JOIN posts p2 on p2.topic_id = a.target_topic_id and p2.post_number = 1
|
||||||
LEFT JOIN categories c ON c.id = t.category_id
|
LEFT JOIN categories c ON c.id = t.category_id
|
||||||
/*where*/
|
/*where*/
|
||||||
GROUP BY action_type
|
GROUP BY action_type
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
builder.where('a.user_id = :user_id', user_id: user_id)
|
builder.where('a.user_id = :user_id', user_id: user_id)
|
||||||
|
|
||||||
apply_common_filters(builder, user_id, guardian)
|
apply_common_filters(builder, user_id, guardian)
|
||||||
|
|
||||||
results = builder.exec.to_a
|
results = builder.query
|
||||||
results.sort! { |a, b| ORDER[a.action_type] <=> ORDER[b.action_type] }
|
results.sort! { |a, b| ORDER[a.action_type] <=> ORDER[b.action_type] }
|
||||||
|
|
||||||
results
|
results
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -139,19 +128,50 @@ SQL
|
||||||
stream(action_id: action_id, guardian: guardian).first
|
stream(action_id: action_id, guardian: guardian).first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
NULL_QUEUED_STREAM_COLS = %i{
|
||||||
|
cooked
|
||||||
|
uploaded_avatar_id
|
||||||
|
acting_name
|
||||||
|
acting_username
|
||||||
|
acting_user_id
|
||||||
|
target_name
|
||||||
|
target_username
|
||||||
|
target_user_id
|
||||||
|
post_number
|
||||||
|
post_id
|
||||||
|
deleted
|
||||||
|
hidden
|
||||||
|
post_type
|
||||||
|
action_type
|
||||||
|
action_code
|
||||||
|
action_code_who
|
||||||
|
topic_closed
|
||||||
|
topic_id
|
||||||
|
topic_archived
|
||||||
|
}.map! { |s| "NULL as #{s}" }.join(", ")
|
||||||
|
|
||||||
def self.stream_queued(opts = nil)
|
def self.stream_queued(opts = nil)
|
||||||
opts ||= {}
|
opts ||= {}
|
||||||
|
|
||||||
offset = opts[:offset] || 0
|
offset = opts[:offset] || 0
|
||||||
limit = opts[:limit] || 60
|
limit = opts[:limit] || 60
|
||||||
|
|
||||||
builder = SqlBuilder.new <<-SQL
|
# this is somewhat ugly, but the serializer wants all these columns
|
||||||
|
# it is more correct to have an object with all the fields needed
|
||||||
|
# cause then we can catch and change if we ever add columns
|
||||||
|
builder = DB.build <<~SQL
|
||||||
SELECT
|
SELECT
|
||||||
a.id,
|
a.id,
|
||||||
t.title, a.action_type, a.created_at, t.id topic_id,
|
t.title,
|
||||||
u.username, u.name, u.id AS user_id,
|
a.action_type,
|
||||||
|
a.created_at,
|
||||||
|
t.id topic_id,
|
||||||
|
u.username,
|
||||||
|
u.name,
|
||||||
|
u.id AS user_id,
|
||||||
qp.raw,
|
qp.raw,
|
||||||
t.category_id
|
t.category_id,
|
||||||
|
#{NULL_QUEUED_STREAM_COLS}
|
||||||
FROM user_actions as a
|
FROM user_actions as a
|
||||||
JOIN queued_posts AS qp ON qp.id = a.queued_post_id
|
JOIN queued_posts AS qp ON qp.id = a.queued_post_id
|
||||||
LEFT OUTER JOIN topics t on t.id = qp.topic_id
|
LEFT OUTER JOIN topics t on t.id = qp.topic_id
|
||||||
|
@ -169,7 +189,7 @@ SQL
|
||||||
.order_by("a.created_at desc")
|
.order_by("a.created_at desc")
|
||||||
.offset(offset.to_i)
|
.offset(offset.to_i)
|
||||||
.limit(limit.to_i)
|
.limit(limit.to_i)
|
||||||
.map_exec(UserActionRow)
|
.query
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.stream(opts = nil)
|
def self.stream(opts = nil)
|
||||||
|
@ -197,7 +217,7 @@ SQL
|
||||||
|
|
||||||
# The weird thing is that target_post_id can be null, so it makes everything
|
# The weird thing is that target_post_id can be null, so it makes everything
|
||||||
# ever so more complex. Should we allow this, not sure.
|
# ever so more complex. Should we allow this, not sure.
|
||||||
builder = SqlBuilder.new <<-SQL
|
builder = DB.build <<~SQL
|
||||||
SELECT
|
SELECT
|
||||||
a.id,
|
a.id,
|
||||||
t.title, a.action_type, a.created_at, t.id topic_id,
|
t.title, a.action_type, a.created_at, t.id topic_id,
|
||||||
|
@ -249,7 +269,7 @@ SQL
|
||||||
.limit(limit.to_i)
|
.limit(limit.to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
builder.map_exec(UserActionRow)
|
builder.query
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.log_action!(hash)
|
def self.log_action!(hash)
|
||||||
|
@ -321,19 +341,19 @@ SQL
|
||||||
def self.synchronize_target_topic_ids(post_ids = nil)
|
def self.synchronize_target_topic_ids(post_ids = nil)
|
||||||
|
|
||||||
# nuke all dupes, using magic
|
# nuke all dupes, using magic
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
DELETE FROM user_actions USING user_actions ua2
|
DELETE FROM user_actions USING user_actions ua2
|
||||||
/*where*/
|
/*where*/
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
builder.where <<SQL
|
builder.where <<~SQL
|
||||||
user_actions.action_type = ua2.action_type AND
|
user_actions.action_type = ua2.action_type AND
|
||||||
user_actions.user_id = ua2.user_id AND
|
user_actions.user_id = ua2.user_id AND
|
||||||
user_actions.acting_user_id = ua2.acting_user_id AND
|
user_actions.acting_user_id = ua2.acting_user_id AND
|
||||||
user_actions.target_post_id = ua2.target_post_id AND
|
user_actions.target_post_id = ua2.target_post_id AND
|
||||||
user_actions.target_post_id > 0 AND
|
user_actions.target_post_id > 0 AND
|
||||||
user_actions.id > ua2.id
|
user_actions.id > ua2.id
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
if post_ids
|
if post_ids
|
||||||
builder.where("user_actions.target_post_id in (:post_ids)", post_ids: post_ids)
|
builder.where("user_actions.target_post_id in (:post_ids)", post_ids: post_ids)
|
||||||
|
@ -341,9 +361,11 @@ SQL
|
||||||
|
|
||||||
builder.exec
|
builder.exec
|
||||||
|
|
||||||
builder = SqlBuilder.new("UPDATE user_actions
|
builder = DB.build <<~SQL
|
||||||
SET target_topic_id = (select topic_id from posts where posts.id = target_post_id)
|
UPDATE user_actions
|
||||||
/*where*/")
|
SET target_topic_id = (select topic_id from posts where posts.id = target_post_id)
|
||||||
|
/*where*/
|
||||||
|
SQL
|
||||||
|
|
||||||
builder.where("target_topic_id <> (select topic_id from posts where posts.id = target_post_id)")
|
builder.where("target_topic_id <> (select topic_id from posts where posts.id = target_post_id)")
|
||||||
if post_ids
|
if post_ids
|
||||||
|
|
|
@ -25,7 +25,7 @@ class UserProfileView < ActiveRecord::Base
|
||||||
/*where*/
|
/*where*/
|
||||||
)"
|
)"
|
||||||
|
|
||||||
builder = SqlBuilder.new(sql)
|
builder = DB.build(sql)
|
||||||
|
|
||||||
if !user_id
|
if !user_id
|
||||||
builder.where("viewed_at = :viewed_at AND ip_address = :ip_address AND user_profile_id = :user_profile_id AND user_id IS NULL")
|
builder.where("viewed_at = :viewed_at AND ip_address = :ip_address AND user_profile_id = :user_profile_id AND user_id IS NULL")
|
||||||
|
@ -35,7 +35,7 @@ class UserProfileView < ActiveRecord::Base
|
||||||
|
|
||||||
result = builder.exec(user_profile_id: user_profile_id, ip_address: ip, viewed_at: at, user_id: user_id)
|
result = builder.exec(user_profile_id: user_profile_id, ip_address: ip, viewed_at: at, user_id: user_id)
|
||||||
|
|
||||||
if result.cmd_tuples > 0
|
if result > 0
|
||||||
UserProfile.find(user_profile_id).increment!(:views)
|
UserProfile.find(user_profile_id).increment!(:views)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -54,7 +54,7 @@ class UserSearch
|
||||||
users = users.includes(:user_search_data)
|
users = users.includes(:user_search_data)
|
||||||
.references(:user_search_data)
|
.references(:user_search_data)
|
||||||
.where("user_search_data.search_data @@ #{query}")
|
.where("user_search_data.search_data @@ #{query}")
|
||||||
.order(User.sql_fragment("CASE WHEN username_lower LIKE ? THEN 0 ELSE 1 END ASC", @term_like))
|
.order(DB.sql_fragment("CASE WHEN username_lower LIKE ? THEN 0 ELSE 1 END ASC", @term_like))
|
||||||
|
|
||||||
else
|
else
|
||||||
users = users.where("username_lower LIKE :term_like", term_like: @term_like)
|
users = users.where("username_lower LIKE :term_like", term_like: @term_like)
|
||||||
|
|
|
@ -184,7 +184,7 @@ class BadgeGranter
|
||||||
|
|
||||||
# hack to allow for params, otherwise sanitizer will trigger sprintf
|
# hack to allow for params, otherwise sanitizer will trigger sprintf
|
||||||
count_sql = "SELECT COUNT(*) count FROM (#{sql}) q WHERE :backfill = :backfill"
|
count_sql = "SELECT COUNT(*) count FROM (#{sql}) q WHERE :backfill = :backfill"
|
||||||
grant_count = SqlBuilder.map_exec(OpenStruct, count_sql, params).first.count.to_i
|
grant_count = DB.query_single(count_sql, params).first.to_i
|
||||||
|
|
||||||
grants_sql =
|
grants_sql =
|
||||||
if opts[:target_posts]
|
if opts[:target_posts]
|
||||||
|
|
|
@ -386,7 +386,7 @@ class UserMerger
|
||||||
conditions = Array.wrap(opts[:conditions])
|
conditions = Array.wrap(opts[:conditions])
|
||||||
updates = Array.wrap(opts[:updates])
|
updates = Array.wrap(opts[:updates])
|
||||||
|
|
||||||
builder = SqlBuilder.new(<<~SQL)
|
builder = DB.build(<<~SQL)
|
||||||
UPDATE #{table_name} AS x
|
UPDATE #{table_name} AS x
|
||||||
/*set*/
|
/*set*/
|
||||||
WHERE x.#{user_id_column_name} = :source_user_id AND NOT EXISTS(
|
WHERE x.#{user_id_column_name} = :source_user_id AND NOT EXISTS(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
class FixTosName < ActiveRecord::Migration[4.2]
|
class FixTosName < ActiveRecord::Migration[4.2]
|
||||||
def up
|
def up
|
||||||
I18n.overrides_disabled do
|
I18n.overrides_disabled do
|
||||||
execute ActiveRecord::Base.sql_fragment('UPDATE user_fields SET name = ? WHERE name = ?', I18n.t('terms_of_service.title'), I18n.t("terms_of_service.signup_form_message"))
|
execute DB.sql_fragment('UPDATE user_fields SET name = ? WHERE name = ?', I18n.t('terms_of_service.title'), I18n.t("terms_of_service.signup_form_message"))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -40,7 +40,7 @@ class AddThemes < ActiveRecord::Migration[4.2]
|
||||||
RETURNING *
|
RETURNING *
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
sql = ActiveRecord::Base.sql_fragment(sql, now: Time.zone.now, key: theme_key)
|
sql = DB.sql_fragment(sql, now: Time.zone.now, key: theme_key)
|
||||||
theme_id = execute(sql).to_a[0]["id"].to_i
|
theme_id = execute(sql).to_a[0]["id"].to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ SQL
|
||||||
INSERT INTO site_settings(name, data_type, value, created_at, updated_at)
|
INSERT INTO site_settings(name, data_type, value, created_at, updated_at)
|
||||||
VALUES('default_theme_key', 1, :key, :now, :now)
|
VALUES('default_theme_key', 1, :key, :now, :now)
|
||||||
SQL
|
SQL
|
||||||
sql = ActiveRecord::Base.sql_fragment(sql, now: Time.zone.now, key: theme_key)
|
sql = DB.sql_fragment(sql, now: Time.zone.now, key: theme_key)
|
||||||
execute(sql)
|
execute(sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,9 @@ module FlagQuery
|
||||||
|
|
||||||
post_ids = post_ids_relation.pluck(:post_id).uniq
|
post_ids = post_ids_relation.pluck(:post_id).uniq
|
||||||
|
|
||||||
posts = SqlBuilder.new("
|
posts = DB.query(<<~SQL, post_ids: post_ids)
|
||||||
SELECT p.id,
|
SELECT p.id,
|
||||||
p.cooked,
|
p.cooked as excerpt,
|
||||||
p.raw,
|
p.raw,
|
||||||
p.user_id,
|
p.user_id,
|
||||||
p.topic_id,
|
p.topic_id,
|
||||||
|
@ -43,10 +43,13 @@ module FlagQuery
|
||||||
p.hidden,
|
p.hidden,
|
||||||
p.deleted_at,
|
p.deleted_at,
|
||||||
p.user_deleted,
|
p.user_deleted,
|
||||||
|
NULL as post_actions,
|
||||||
|
NULL as post_action_ids,
|
||||||
(SELECT created_at FROM post_revisions WHERE post_id = p.id AND user_id = p.user_id ORDER BY created_at DESC LIMIT 1) AS last_revised_at,
|
(SELECT created_at FROM post_revisions WHERE post_id = p.id AND user_id = p.user_id ORDER BY created_at DESC LIMIT 1) AS last_revised_at,
|
||||||
(SELECT COUNT(*) FROM post_actions WHERE (disagreed_at IS NOT NULL OR agreed_at IS NOT NULL OR deferred_at IS NOT NULL) AND post_id = p.id)::int AS previous_flags_count
|
(SELECT COUNT(*) FROM post_actions WHERE (disagreed_at IS NOT NULL OR agreed_at IS NOT NULL OR deferred_at IS NOT NULL) AND post_id = p.id)::int AS previous_flags_count
|
||||||
FROM posts p
|
FROM posts p
|
||||||
WHERE p.id in (:post_ids)").map_exec(OpenStruct, post_ids: post_ids)
|
WHERE p.id in (:post_ids)
|
||||||
|
SQL
|
||||||
|
|
||||||
post_lookup = {}
|
post_lookup = {}
|
||||||
user_ids = Set.new
|
user_ids = Set.new
|
||||||
|
@ -55,8 +58,7 @@ module FlagQuery
|
||||||
posts.each do |p|
|
posts.each do |p|
|
||||||
user_ids << p.user_id
|
user_ids << p.user_id
|
||||||
topic_ids << p.topic_id
|
topic_ids << p.topic_id
|
||||||
p.excerpt = Post.excerpt(p.cooked)
|
p.excerpt = Post.excerpt(p.excerpt)
|
||||||
p.delete_field(:cooked)
|
|
||||||
post_lookup[p.id] = p
|
post_lookup[p.id] = p
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -127,7 +129,7 @@ module FlagQuery
|
||||||
# maintain order
|
# maintain order
|
||||||
posts = post_ids.map { |id| post_lookup[id] }
|
posts = post_ids.map { |id| post_lookup[id] }
|
||||||
# TODO: add serializer so we can skip this
|
# TODO: add serializer so we can skip this
|
||||||
posts.map!(&:marshal_dump)
|
posts.map!(&:to_h)
|
||||||
|
|
||||||
users = User.includes(:user_stat).where(id: user_ids.to_a).to_a
|
users = User.includes(:user_stat).where(id: user_ids.to_a).to_a
|
||||||
User.preload_custom_fields(users, User.whitelisted_user_custom_fields(guardian))
|
User.preload_custom_fields(users, User.whitelisted_user_custom_fields(guardian))
|
||||||
|
|
|
@ -32,21 +32,22 @@ module Migration
|
||||||
end
|
end
|
||||||
|
|
||||||
def droppable?
|
def droppable?
|
||||||
builder = SqlBuilder.new(<<~SQL)
|
builder = DB.build(<<~SQL)
|
||||||
SELECT 1
|
SELECT 1
|
||||||
FROM INFORMATION_SCHEMA.COLUMNS
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
/*where*/
|
/*where*/
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
builder.where("table_schema = 'public'")
|
builder
|
||||||
|
.where("table_schema = 'public'")
|
||||||
.where("table_name = :table")
|
.where("table_name = :table")
|
||||||
.where("column_name IN (:columns)")
|
.where("column_name IN (:columns)")
|
||||||
.where(previous_migration_done)
|
.where(previous_migration_done)
|
||||||
.exec(table: @table,
|
.exec(table: @table,
|
||||||
columns: @columns,
|
columns: @columns,
|
||||||
delay: "#{@delay} seconds",
|
delay: "#{@delay} seconds",
|
||||||
after_migration: @after_migration).to_a.length > 0
|
after_migration: @after_migration) > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute_drop!
|
def execute_drop!
|
||||||
|
|
|
@ -36,4 +36,13 @@ class MiniSqlMultisiteConnection < MiniSql::Connection
|
||||||
def build(sql)
|
def build(sql)
|
||||||
CustomBuilder.new(self, sql)
|
CustomBuilder.new(self, sql)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sql_fragment(query, *args)
|
||||||
|
if args.length > 0
|
||||||
|
param_encoder.encode(query, *args)
|
||||||
|
else
|
||||||
|
query
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,7 +32,7 @@ class ScoreCalculator
|
||||||
@weightings.each_key { |k| components << "COALESCE(posts.#{k}, 0) * :#{k}" }
|
@weightings.each_key { |k| components << "COALESCE(posts.#{k}, 0) * :#{k}" }
|
||||||
components = components.join(" + ")
|
components = components.join(" + ")
|
||||||
|
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<SQL
|
||||||
UPDATE posts p
|
UPDATE posts p
|
||||||
SET score = x.score
|
SET score = x.score
|
||||||
FROM (
|
FROM (
|
||||||
|
@ -48,66 +48,72 @@ SQL
|
||||||
|
|
||||||
filter_topics(builder, opts)
|
filter_topics(builder, opts)
|
||||||
|
|
||||||
while builder.exec.cmd_tuples == limit
|
while builder.exec == limit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_posts_rank(opts)
|
def update_posts_rank(opts)
|
||||||
limit = 20000
|
limit = 20000
|
||||||
|
|
||||||
builder = SqlBuilder.new <<SQL
|
builder = DB.build <<~SQL
|
||||||
UPDATE posts
|
UPDATE posts
|
||||||
SET percent_rank = X.percent_rank
|
SET percent_rank = X.percent_rank
|
||||||
FROM (
|
FROM (
|
||||||
SELECT posts.id, Y.percent_rank
|
SELECT posts.id, Y.percent_rank
|
||||||
FROM posts
|
FROM posts
|
||||||
JOIN (
|
JOIN (
|
||||||
SELECT id, percent_rank()
|
SELECT id, percent_rank()
|
||||||
OVER (PARTITION BY topic_id ORDER BY SCORE DESC) as percent_rank
|
OVER (PARTITION BY topic_id ORDER BY SCORE DESC) as percent_rank
|
||||||
FROM posts
|
FROM posts
|
||||||
) Y ON Y.id = posts.id
|
) Y ON Y.id = posts.id
|
||||||
JOIN topics ON posts.topic_id = topics.id
|
JOIN topics ON posts.topic_id = topics.id
|
||||||
/*where*/
|
/*where*/
|
||||||
LIMIT #{limit}
|
LIMIT #{limit}
|
||||||
) AS X
|
) AS X
|
||||||
WHERE posts.id = X.id
|
WHERE posts.id = X.id
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
builder.where("posts.percent_rank IS NULL OR Y.percent_rank <> posts.percent_rank")
|
builder.where("posts.percent_rank IS NULL OR Y.percent_rank <> posts.percent_rank")
|
||||||
|
|
||||||
filter_topics(builder, opts)
|
filter_topics(builder, opts)
|
||||||
|
|
||||||
while builder.exec.cmd_tuples == limit
|
while builder.exec == limit
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_topics_rank(opts)
|
def update_topics_rank(opts)
|
||||||
builder = SqlBuilder.new("UPDATE topics AS topics
|
builder = DB.build <<~SQL
|
||||||
SET has_summary = (topics.like_count >= :likes_required AND
|
UPDATE topics AS topics
|
||||||
topics.posts_count >= :posts_required AND
|
SET has_summary = (topics.like_count >= :likes_required AND
|
||||||
x.max_score >= :score_required),
|
topics.posts_count >= :posts_required AND
|
||||||
score = x.avg_score
|
x.max_score >= :score_required),
|
||||||
FROM (SELECT p.topic_id,
|
score = x.avg_score
|
||||||
MAX(p.score) AS max_score,
|
FROM (SELECT p.topic_id,
|
||||||
AVG(p.score) AS avg_score
|
MAX(p.score) AS max_score,
|
||||||
FROM posts AS p
|
AVG(p.score) AS avg_score
|
||||||
GROUP BY p.topic_id) AS x
|
FROM posts AS p
|
||||||
/*where*/")
|
GROUP BY p.topic_id) AS x
|
||||||
|
/*where*/
|
||||||
|
SQL
|
||||||
|
|
||||||
builder.where("x.topic_id = topics.id AND
|
defaults = {
|
||||||
(
|
likes_required: SiteSetting.summary_likes_required,
|
||||||
(topics.score <> x.avg_score OR topics.score IS NULL) OR
|
posts_required: SiteSetting.summary_posts_required,
|
||||||
(topics.has_summary IS NULL OR topics.has_summary <> (
|
score_required: SiteSetting.summary_score_threshold
|
||||||
topics.like_count >= :likes_required AND
|
}
|
||||||
topics.posts_count >= :posts_required AND
|
|
||||||
x.max_score >= :score_required
|
builder.where(<<~SQL, defaults)
|
||||||
))
|
x.topic_id = topics.id AND
|
||||||
)
|
(
|
||||||
",
|
(topics.score <> x.avg_score OR topics.score IS NULL) OR
|
||||||
likes_required: SiteSetting.summary_likes_required,
|
(topics.has_summary IS NULL OR topics.has_summary <> (
|
||||||
posts_required: SiteSetting.summary_posts_required,
|
topics.like_count >= :likes_required AND
|
||||||
score_required: SiteSetting.summary_score_threshold)
|
topics.posts_count >= :posts_required AND
|
||||||
|
x.max_score >= :score_required
|
||||||
|
))
|
||||||
|
)
|
||||||
|
SQL
|
||||||
|
|
||||||
filter_topics(builder, opts)
|
filter_topics(builder, opts)
|
||||||
|
|
||||||
|
@ -116,11 +122,13 @@ SQL
|
||||||
|
|
||||||
def update_topics_percent_rank(opts)
|
def update_topics_percent_rank(opts)
|
||||||
|
|
||||||
builder = SqlBuilder.new("UPDATE topics SET percent_rank = x.percent_rank
|
builder = DB.build <<~SQL
|
||||||
FROM (SELECT id, percent_rank()
|
UPDATE topics SET percent_rank = x.percent_rank
|
||||||
OVER (ORDER BY SCORE DESC) as percent_rank
|
FROM (SELECT id, percent_rank()
|
||||||
FROM topics) AS x
|
OVER (ORDER BY SCORE DESC) as percent_rank
|
||||||
/*where*/")
|
FROM topics) AS x
|
||||||
|
/*where*/
|
||||||
|
SQL
|
||||||
|
|
||||||
builder.where("x.id = topics.id AND (topics.percent_rank <> x.percent_rank OR topics.percent_rank IS NULL)")
|
builder.where("x.id = topics.id AND (topics.percent_rank <> x.percent_rank OR topics.percent_rank IS NULL)")
|
||||||
|
|
||||||
|
|
|
@ -14,15 +14,14 @@ class SiteSettings::DbProvider
|
||||||
return [] unless table_exists?
|
return [] unless table_exists?
|
||||||
|
|
||||||
# Not leaking out AR records, cause I want all editing to happen via this API
|
# Not leaking out AR records, cause I want all editing to happen via this API
|
||||||
SqlBuilder.new("SELECT name, data_type, value FROM #{@model.table_name}").map_exec(OpenStruct)
|
DB.query("SELECT name, data_type, value FROM #{@model.table_name}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def find(name)
|
def find(name)
|
||||||
return nil unless table_exists?
|
return nil unless table_exists?
|
||||||
|
|
||||||
# Not leaking out AR records, cause I want all editing to happen via this API
|
# Not leaking out AR records, cause I want all editing to happen via this API
|
||||||
SqlBuilder.new("SELECT name, data_type, value FROM #{@model.table_name} WHERE name = :name")
|
DB.query("SELECT name, data_type, value FROM #{@model.table_name} WHERE name = ?", name)
|
||||||
.map_exec(OpenStruct, name: name)
|
|
||||||
.first
|
.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
class SqlBuilder
|
class SqlBuilder
|
||||||
|
|
||||||
def initialize(template, klass = nil)
|
def initialize(template, klass = nil)
|
||||||
|
|
||||||
|
Discourse.deprecate("SqlBuilder is deprecated and will be removed, please use DB.build instead!")
|
||||||
|
|
||||||
@args = {}
|
@args = {}
|
||||||
@sql = template
|
@sql = template
|
||||||
@sections = {}
|
@sections = {}
|
||||||
|
@ -75,12 +78,8 @@ class SqlBuilder
|
||||||
|
|
||||||
class RailsDateTimeDecoder < PG::SimpleDecoder
|
class RailsDateTimeDecoder < PG::SimpleDecoder
|
||||||
def decode(string, tuple = nil, field = nil)
|
def decode(string, tuple = nil, field = nil)
|
||||||
if Rails.version >= "4.2.0"
|
@caster ||= ActiveRecord::Type::DateTime.new
|
||||||
@caster ||= ActiveRecord::Type::DateTime.new
|
@caster.cast(string)
|
||||||
@caster.cast(string)
|
|
||||||
else
|
|
||||||
ActiveRecord::ConnectionAdapters::Column.string_to_time string
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -118,9 +117,3 @@ class SqlBuilder
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
class ActiveRecord::Base
|
|
||||||
def self.sql_builder(template)
|
|
||||||
SqlBuilder.new(template, self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -283,7 +283,7 @@ task 'posts:reorder_posts', [:topic_id] => [:environment] do |_, args|
|
||||||
Post.transaction do
|
Post.transaction do
|
||||||
# update sort_order and flip post_number to prevent
|
# update sort_order and flip post_number to prevent
|
||||||
# unique constraint violations when updating post_number
|
# unique constraint violations when updating post_number
|
||||||
builder = SqlBuilder.new(<<~SQL)
|
builder = DB.build(<<~SQL)
|
||||||
WITH ordered_posts AS (
|
WITH ordered_posts AS (
|
||||||
SELECT
|
SELECT
|
||||||
id,
|
id,
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
# encoding: utf-8
|
|
||||||
require 'rails_helper'
|
|
||||||
require_dependency 'sql_builder'
|
|
||||||
|
|
||||||
describe SqlBuilder do
|
|
||||||
|
|
||||||
describe "attached" do
|
|
||||||
before do
|
|
||||||
@builder = Post.sql_builder("select * from posts /*where*/ /*limit*/")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should find a post by id" do
|
|
||||||
p = Fabricate(:post)
|
|
||||||
@builder.where('id = :id and topic_id = :topic_id', id: p.id, topic_id: p.topic_id)
|
|
||||||
p2 = @builder.exec.first
|
|
||||||
expect(p2.id).to eq(p.id)
|
|
||||||
expect(p2).to eq(p)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "map_exec" do
|
|
||||||
class SqlBuilder::TestClass
|
|
||||||
attr_accessor :int, :string, :date, :text, :bool
|
|
||||||
end
|
|
||||||
|
|
||||||
it "correctly maps to a klass" do
|
|
||||||
rows = SqlBuilder.new("SELECT
|
|
||||||
1 AS int,
|
|
||||||
'string' AS string,
|
|
||||||
CAST(NOW() at time zone 'utc' AS timestamp without time zone) AS date,
|
|
||||||
'text'::text AS text,
|
|
||||||
true AS bool")
|
|
||||||
.map_exec(SqlBuilder::TestClass)
|
|
||||||
|
|
||||||
expect(rows.count).to eq(1)
|
|
||||||
row = rows[0]
|
|
||||||
expect(row.int).to eq(1)
|
|
||||||
expect(row.string).to eq("string")
|
|
||||||
expect(row.text).to eq("text")
|
|
||||||
expect(row.bool).to eq(true)
|
|
||||||
expect(row.date).to be_within(10.seconds).of(DateTime.now)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "detached" do
|
|
||||||
before do
|
|
||||||
@builder = SqlBuilder.new("select * from (select :a A union all select :b) as X /*where*/ /*order_by*/ /*limit*/ /*offset*/")
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should allow for 1 param exec" do
|
|
||||||
expect(@builder.exec(a: 1, b: 2).values[0][0]).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should allow for a single where" do
|
|
||||||
@builder.where(":a = 1")
|
|
||||||
expect(@builder.exec(a: 1, b: 2).values[0][0]).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should allow where chaining" do
|
|
||||||
@builder.where(":a = 1")
|
|
||||||
@builder.where("2 = 1")
|
|
||||||
expect(@builder.exec(a: 1, b: 2).to_a.length).to eq(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "should allow order by" do
|
|
||||||
expect(@builder.order_by("A desc").limit(1)
|
|
||||||
.exec(a: 1, b: 2).values[0][0]).to eq(2)
|
|
||||||
end
|
|
||||||
it "should allow offset" do
|
|
||||||
expect(@builder.order_by("A desc").offset(1)
|
|
||||||
.exec(a: 1, b: 2).values[0][0]).to eq(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -43,7 +43,7 @@ describe UserAction do
|
||||||
end
|
end
|
||||||
|
|
||||||
def stats_for_user(viewer = nil)
|
def stats_for_user(viewer = nil)
|
||||||
UserAction.stats(user.id, Guardian.new(viewer)).map { |r| r["action_type"].to_i }.sort
|
UserAction.stats(user.id, Guardian.new(viewer)).map { |r| r.action_type.to_i }.sort
|
||||||
end
|
end
|
||||||
|
|
||||||
def stream(viewer = nil)
|
def stream(viewer = nil)
|
||||||
|
|
Loading…
Reference in New Issue