Merge remote-tracking branch 'upstream/main' into merge-gramsrv-9106877

This commit is contained in:
onysd 2026-08-03 23:29:20 +03:00
commit ac6a50c5ff
697 changed files with 100880 additions and 8052 deletions

View file

@ -35,42 +35,6 @@ func (q *Queries) AdvanceDialogReadInboxFloor(ctx context.Context, arg AdvanceDi
return err
}
const clearDialogAfterHistoryDelete = `-- name: ClearDialogAfterHistoryDelete :exec
UPDATE dialogs d
SET
top_message_id = 0,
top_message_date = 0,
read_inbox_max_id = GREATEST(d.read_inbox_max_id, d.top_message_id),
read_outbox_max_id = GREATEST(d.read_outbox_max_id, d.top_message_id),
unread_count = 0,
unread_mark = false,
unread_mentions_count = 0,
unread_reactions_count = (
SELECT COUNT(*)::int
FROM message_boxes m2
WHERE m2.owner_user_id = d.user_id
AND m2.peer_type = d.peer_type
AND m2.peer_id = d.peer_id
AND NOT m2.deleted
AND m2.reaction_unread
),
updated_at = now()
WHERE d.user_id = $1::bigint
AND d.peer_type = $2::text
AND d.peer_id = $3::bigint
`
type ClearDialogAfterHistoryDeleteParams struct {
UserID int64
PeerType string
PeerID int64
}
func (q *Queries) ClearDialogAfterHistoryDelete(ctx context.Context, arg ClearDialogAfterHistoryDeleteParams) error {
_, err := q.db.Exec(ctx, clearDialogAfterHistoryDelete, arg.UserID, arg.PeerType, arg.PeerID)
return err
}
const clearDialogDrafts = `-- name: ClearDialogDrafts :many
WITH doomed AS (
SELECT d.user_id, d.peer_type, d.peer_id, d.top_message_id

View file

@ -26,11 +26,13 @@ WHERE m.owner_user_id = $1::bigint
$7::text = ''
OR m.body ILIKE ('%' || $7::text || '%')
)
AND ($8::int <= 0 OR m.box_id < $8::int)
AND ($9::int <= 0 OR m.box_id > $9::int)
AND (NOT $10::boolean OR m.pinned)
AND ($8::int <= 0 OR m.message_date > $8::int)
AND ($9::int <= 0 OR m.message_date < $9::int)
AND ($10::int <= 0 OR m.box_id < $10::int)
AND ($11::int <= 0 OR m.box_id > $11::int)
AND (NOT $12::boolean OR m.pinned)
AND (
NOT $11::boolean
NOT $13::boolean
OR (
m.media->>'kind' = 'document'
AND EXISTS (
@ -42,25 +44,39 @@ WHERE m.owner_user_id = $1::bigint
)
)
AND (
$12::text = ''
OR (m.saved_peer_type = $12::text AND m.saved_peer_id = $13::bigint)
$14::text = ''
OR (m.saved_peer_type = $14::text AND m.saved_peer_id = $15::bigint)
)
AND (
cardinality($16::text[]) = 0
OR EXISTS (
SELECT 1
FROM saved_message_reaction_tags tag
WHERE tag.user_id = m.owner_user_id
AND tag.message_box_id = m.box_id
AND (tag.reaction_type || ':' || tag.reaction_value)
= ANY($16::text[])
)
)
`
type CountMessagesByUserParams struct {
OwnerUserID int64
HasPeer bool
PeerType string
PeerID int64
RestrictPeerIds bool
PeerIds []int64
Query string
MaxID int32
MinID int32
PinnedOnly bool
MusicOnly bool
SavedPeerType string
SavedPeerID int64
OwnerUserID int64
HasPeer bool
PeerType string
PeerID int64
RestrictPeerIds bool
PeerIds []int64
Query string
MinDate int32
MaxDate int32
MaxID int32
MinID int32
PinnedOnly bool
MusicOnly bool
SavedPeerType string
SavedPeerID int64
SavedReactionKeys []string
}
// ListMessagesByUser total CTE 的独立化:相同 base 过滤(不含分页 anchor),
@ -74,12 +90,15 @@ func (q *Queries) CountMessagesByUser(ctx context.Context, arg CountMessagesByUs
arg.RestrictPeerIds,
arg.PeerIds,
arg.Query,
arg.MinDate,
arg.MaxDate,
arg.MaxID,
arg.MinID,
arg.PinnedOnly,
arg.MusicOnly,
arg.SavedPeerType,
arg.SavedPeerID,
arg.SavedReactionKeys,
)
var total_count int32
err := row.Scan(&total_count)
@ -867,12 +886,13 @@ WITH target AS (
WHERE m.owner_user_id = $1::bigint
AND m.peer_type = $2::text
AND m.peer_id = $3::bigint
AND ($4::int <= 0 OR m.box_id <= $4::int)
AND ($5::int <= 0 OR m.message_date >= $5::int)
AND ($6::int <= 0 OR m.message_date <= $6::int)
AND ($4::int <= 0 OR m.box_id <> $4::int)
AND ($5::int <= 0 OR m.box_id <= $5::int)
AND ($6::int <= 0 OR m.message_date >= $6::int)
AND ($7::int <= 0 OR m.message_date <= $7::int)
AND NOT m.deleted
ORDER BY m.box_id DESC
LIMIT $7::int
LIMIT $8::int
FOR UPDATE SKIP LOCKED
),
updated AS (
@ -904,6 +924,7 @@ type DeleteMessageBoxesByPeerBatchParams struct {
OwnerUserID int64
PeerType string
PeerID int64
KeepBoxID int32
MaxID int32
MinDate int32
MaxDate int32
@ -924,6 +945,7 @@ func (q *Queries) DeleteMessageBoxesByPeerBatch(ctx context.Context, arg DeleteM
arg.OwnerUserID,
arg.PeerType,
arg.PeerID,
arg.KeepBoxID,
arg.MaxID,
arg.MinDate,
arg.MaxDate,
@ -2089,9 +2111,10 @@ SELECT EXISTS (
WHERE m.owner_user_id = $1::bigint
AND m.peer_type = $2::text
AND m.peer_id = $3::bigint
AND ($4::int <= 0 OR m.box_id <= $4::int)
AND ($5::int <= 0 OR m.message_date >= $5::int)
AND ($6::int <= 0 OR m.message_date <= $6::int)
AND ($4::int <= 0 OR m.box_id <> $4::int)
AND ($5::int <= 0 OR m.box_id <= $5::int)
AND ($6::int <= 0 OR m.message_date >= $6::int)
AND ($7::int <= 0 OR m.message_date <= $7::int)
AND NOT m.deleted
LIMIT 1
)::boolean AS more
@ -2101,6 +2124,7 @@ type HasDeletableMessageBoxByPeerParams struct {
OwnerUserID int64
PeerType string
PeerID int64
KeepBoxID int32
MaxID int32
MinDate int32
MaxDate int32
@ -2111,6 +2135,7 @@ func (q *Queries) HasDeletableMessageBoxByPeer(ctx context.Context, arg HasDelet
arg.OwnerUserID,
arg.PeerType,
arg.PeerID,
arg.KeepBoxID,
arg.MaxID,
arg.MinDate,
arg.MaxDate,
@ -2295,11 +2320,13 @@ WHERE m.owner_user_id = $1::bigint
$7::text = ''
OR m.body ILIKE ('%' || $7::text || '%')
)
AND ($8::int <= 0 OR m.box_id < $8::int)
AND ($9::int <= 0 OR m.box_id > $9::int)
AND (NOT $10::boolean OR m.pinned)
AND ($8::int <= 0 OR m.message_date > $8::int)
AND ($9::int <= 0 OR m.message_date < $9::int)
AND ($10::int <= 0 OR m.box_id < $10::int)
AND ($11::int <= 0 OR m.box_id > $11::int)
AND (NOT $12::boolean OR m.pinned)
AND (
NOT $11::boolean
NOT $13::boolean
OR (
m.media->>'kind' = 'document'
AND EXISTS (
@ -2311,36 +2338,50 @@ WHERE m.owner_user_id = $1::bigint
)
)
AND (
$12::text = ''
OR (m.saved_peer_type = $12::text AND m.saved_peer_id = $13::bigint)
$14::text = ''
OR (m.saved_peer_type = $14::text AND m.saved_peer_id = $15::bigint)
)
AND (
($14::int > 0 AND m.message_date < $14::int)
OR ($14::int <= 0 AND ($15::int <= 0 OR m.box_id < $15::int))
cardinality($16::text[]) = 0
OR EXISTS (
SELECT 1
FROM saved_message_reaction_tags tag
WHERE tag.user_id = m.owner_user_id
AND tag.message_box_id = m.box_id
AND (tag.reaction_type || ':' || tag.reaction_value)
= ANY($16::text[])
)
)
AND (
($17::int > 0 AND m.message_date < $17::int)
OR ($17::int <= 0 AND ($18::int <= 0 OR m.box_id < $18::int))
)
ORDER BY m.box_id DESC
OFFSET GREATEST($16::int, 0)
LIMIT $17::int
OFFSET GREATEST($19::int, 0)
LIMIT $20::int
`
type ListMessagesBackwardParams struct {
OwnerUserID int64
HasPeer bool
PeerType string
PeerID int64
RestrictPeerIds bool
PeerIds []int64
Query string
MaxID int32
MinID int32
PinnedOnly bool
MusicOnly bool
SavedPeerType string
SavedPeerID int64
OffsetDate int32
OffsetID int32
RowOffset int32
LimitCount int32
OwnerUserID int64
HasPeer bool
PeerType string
PeerID int64
RestrictPeerIds bool
PeerIds []int64
Query string
MinDate int32
MaxDate int32
MaxID int32
MinID int32
PinnedOnly bool
MusicOnly bool
SavedPeerType string
SavedPeerID int64
SavedReactionKeys []string
OffsetDate int32
OffsetID int32
RowOffset int32
LimitCount int32
}
type ListMessagesBackwardRow struct {
@ -2433,12 +2474,15 @@ func (q *Queries) ListMessagesBackward(ctx context.Context, arg ListMessagesBack
arg.RestrictPeerIds,
arg.PeerIds,
arg.Query,
arg.MinDate,
arg.MaxDate,
arg.MaxID,
arg.MinID,
arg.PinnedOnly,
arg.MusicOnly,
arg.SavedPeerType,
arg.SavedPeerID,
arg.SavedReactionKeys,
arg.OffsetDate,
arg.OffsetID,
arg.RowOffset,
@ -2641,11 +2685,13 @@ base AS NOT MATERIALIZED (
$11::text = ''
OR m.body ILIKE ('%' || $11::text || '%')
)
AND ($12::int <= 0 OR m.box_id < $12::int)
AND ($13::int <= 0 OR m.box_id > $13::int)
AND (NOT $14::boolean OR m.pinned)
AND ($12::int <= 0 OR m.message_date > $12::int)
AND ($13::int <= 0 OR m.message_date < $13::int)
AND ($14::int <= 0 OR m.box_id < $14::int)
AND ($15::int <= 0 OR m.box_id > $15::int)
AND (NOT $16::boolean OR m.pinned)
AND (
NOT $15::boolean
NOT $17::boolean
OR (
m.media->>'kind' = 'document'
AND EXISTS (
@ -2657,14 +2703,25 @@ base AS NOT MATERIALIZED (
)
)
AND (
$16::text = ''
OR (m.saved_peer_type = $16::text AND m.saved_peer_id = $17::bigint)
$18::text = ''
OR (m.saved_peer_type = $18::text AND m.saved_peer_id = $19::bigint)
)
AND (
cardinality($20::text[]) = 0
OR EXISTS (
SELECT 1
FROM saved_message_reaction_tags tag
WHERE tag.user_id = m.owner_user_id
AND tag.message_box_id = m.box_id
AND (tag.reaction_type || ':' || tag.reaction_value)
= ANY($20::text[])
)
)
),
total AS (
SELECT count(*)::int AS total_count
FROM base
WHERE $18::boolean
WHERE $21::boolean
),
backward AS (
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.ttl_period, b.expires_at, b.edit_date, b.hide_edited, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.reply_to_story_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.fwd_saved_from_peer_type, b.fwd_saved_from_peer_id, b.fwd_saved_from_msg_id, b.saved_peer_type, b.saved_peer_id, b.pts, b.media_json, b.media_unread, b.reaction_unread, b.pinned, b.via_bot_id, b.grouped_id, b.effect, b.reply_markup_json, b.rich_message_json, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_is_bot, b.peer_bot_info_version, b.peer_premium_until, b.peer_emoji_status_document_id, b.peer_emoji_status_until, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_is_bot, b.from_user_bot_info_version, b.from_user_premium_until, b.from_user_emoji_status_document_id, b.from_user_emoji_status_until, b.from_user_last_seen_at
@ -2811,24 +2868,27 @@ ORDER BY box_id DESC
`
type ListMessagesByUserParams struct {
OwnerUserID int64
OffsetID int32
OffsetDate int32
AddOffset int32
LimitCount int32
HasPeer bool
PeerType string
PeerID int64
RestrictPeerIds bool
PeerIds []int64
Query string
MaxID int32
MinID int32
PinnedOnly bool
MusicOnly bool
SavedPeerType string
SavedPeerID int64
NeedTotalCount bool
OwnerUserID int64
OffsetID int32
OffsetDate int32
AddOffset int32
LimitCount int32
HasPeer bool
PeerType string
PeerID int64
RestrictPeerIds bool
PeerIds []int64
Query string
MinDate int32
MaxDate int32
MaxID int32
MinID int32
PinnedOnly bool
MusicOnly bool
SavedPeerType string
SavedPeerID int64
SavedReactionKeys []string
NeedTotalCount bool
}
type ListMessagesByUserRow struct {
@ -2921,12 +2981,15 @@ func (q *Queries) ListMessagesByUser(ctx context.Context, arg ListMessagesByUser
arg.RestrictPeerIds,
arg.PeerIds,
arg.Query,
arg.MinDate,
arg.MaxDate,
arg.MaxID,
arg.MinID,
arg.PinnedOnly,
arg.MusicOnly,
arg.SavedPeerType,
arg.SavedPeerID,
arg.SavedReactionKeys,
arg.NeedTotalCount,
)
if err != nil {

View file

@ -68,8 +68,6 @@ type AccountPassword struct {
SrpBSecret []byte
SrpB []byte
RecoveryEmail string
RecoveryCode string
RecoveryCodeExpiresAt pgtype.Timestamptz
LoginEmail string
PasswordChangedAt pgtype.Timestamptz
}

View file

@ -423,7 +423,18 @@ func (q *Queries) GetUsersByPhones(ctx context.Context, phones []string) ([]User
}
const searchUsers = `-- name: SearchUsers :many
WITH matched AS (
WITH username_matches AS (
SELECT
peer_id,
bool_or(username_lower = $2::text) AS exact
FROM peer_usernames
WHERE peer_type = 'user'
AND active
AND collectible_id IS NOT NULL
AND username_lower LIKE $3::text || '%' ESCAPE '\'
GROUP BY peer_id
),
matched AS (
SELECT
u.id,
u.access_hash,
@ -453,27 +464,29 @@ WITH matched AS (
(c.contact_user_id IS NOT NULL)::boolean AS contact,
COALESCE(c.mutual, false)::boolean AS mutual,
CASE
WHEN $2::text <> '' AND u.phone = $2::text THEN 0
WHEN lower(u.username) = $3::text THEN 1
WHEN lower(COALESCE(NULLIF(c.contact_first_name, ''), u.first_name)) = $3::text THEN 2
WHEN lower(u.first_name) = $3::text THEN 3
WHEN $4::text <> '' AND u.phone = $4::text THEN 0
WHEN COALESCE(um.exact, false) OR lower(u.username) = $2::text THEN 1
WHEN lower(COALESCE(NULLIF(c.contact_first_name, ''), u.first_name)) = $2::text THEN 2
WHEN lower(u.first_name) = $2::text THEN 3
WHEN c.contact_user_id IS NOT NULL THEN 4
ELSE 5
END AS rank
FROM users u
LEFT JOIN contacts c ON c.user_id = $4::bigint AND c.contact_user_id = u.id
WHERE u.id <> $4::bigint
LEFT JOIN contacts c ON c.user_id = $5::bigint AND c.contact_user_id = u.id
LEFT JOIN username_matches um ON um.peer_id = u.id
WHERE u.id <> $5::bigint
AND u.deleted_at IS NULL
AND $3::text <> ''
AND $2::text <> ''
AND (
($2::text <> '' AND u.phone LIKE $2::text || '%')
OR lower(u.username) LIKE $5::text || '%' ESCAPE '\'
OR lower(u.first_name) LIKE '%' || $5::text || '%' ESCAPE '\'
OR lower(u.last_name) LIKE '%' || $5::text || '%' ESCAPE '\'
OR lower(trim(u.first_name || ' ' || u.last_name)) LIKE '%' || $5::text || '%' ESCAPE '\'
OR lower(c.contact_first_name) LIKE '%' || $5::text || '%' ESCAPE '\'
OR lower(c.contact_last_name) LIKE '%' || $5::text || '%' ESCAPE '\'
OR lower(trim(c.contact_first_name || ' ' || c.contact_last_name)) LIKE '%' || $5::text || '%' ESCAPE '\'
($4::text <> '' AND u.phone LIKE $4::text || '%')
OR um.peer_id IS NOT NULL
OR lower(u.username) LIKE $3::text || '%' ESCAPE '\'
OR lower(u.first_name) LIKE '%' || $3::text || '%' ESCAPE '\'
OR lower(u.last_name) LIKE '%' || $3::text || '%' ESCAPE '\'
OR lower(trim(u.first_name || ' ' || u.last_name)) LIKE '%' || $3::text || '%' ESCAPE '\'
OR lower(c.contact_first_name) LIKE '%' || $3::text || '%' ESCAPE '\'
OR lower(c.contact_last_name) LIKE '%' || $3::text || '%' ESCAPE '\'
OR lower(trim(c.contact_first_name || ' ' || c.contact_last_name)) LIKE '%' || $3::text || '%' ESCAPE '\'
)
)
SELECT
@ -511,10 +524,10 @@ LIMIT $1
type SearchUsersParams struct {
LimitCount int32
PhoneQuery string
QueryLower string
CurrentUserID int64
QueryLike string
PhoneQuery string
CurrentUserID int64
}
type SearchUsersRow struct {
@ -550,10 +563,10 @@ type SearchUsersRow struct {
func (q *Queries) SearchUsers(ctx context.Context, arg SearchUsersParams) ([]SearchUsersRow, error) {
rows, err := q.db.Query(ctx, searchUsers,
arg.LimitCount,
arg.PhoneQuery,
arg.QueryLower,
arg.CurrentUserID,
arg.QueryLike,
arg.PhoneQuery,
arg.CurrentUserID,
)
if err != nil {
return nil, err