Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 41 additions & 11 deletions scripts/history.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,23 @@ fi

# Parse to "from \x1f to \x1f body \x1f at \x1f id" rows (no jq; cf. lib/hooks-json.sh).
_arr="[$(printf '%s' "$HIST_JSONL" | paste -sd, -)]"
ROWS=$(agmsg_sqlite ':memory:' "
SELECT json_extract(value,'\$.from') || char(31) ||
json_extract(value,'\$.to') || char(31) ||
replace(replace(json_extract(value,'\$.body'), char(10), '\n'), char(9), '\t') || char(31) ||
json_extract(value,'\$.at') || char(31) ||
json_extract(value,'\$.id')
FROM json_each('$(printf '%s' "$_arr" | sed "s/'/''/g")');
")
# #777: same argv-length exposure on the display path. Capping --limit does not bound this
# one either, because a single long body can carry it past the ceiling on its own.
_agmsg_rows_sql=$(mktemp "${TMPDIR:-/tmp}/agmsg-history-rows.XXXXXX") || exit 13
trap 'rm -f "$_agmsg_rows_sql"' EXIT HUP INT TERM
{
printf "%s\n" "SELECT json_extract(value,'\$.from') || char(31) ||"
printf "%s\n" " json_extract(value,'\$.to') || char(31) ||"
printf "%s\n" " replace(replace(json_extract(value,'\$.body'), char(10), '\n'), char(9), '\t') || char(31) ||"
printf "%s\n" " json_extract(value,'\$.at') || char(31) ||"
printf "%s\n" " json_extract(value,'\$.id')"
printf "FROM json_each('"
printf '%s' "$_arr" | sed "s/'/''/g"
printf "');\n"
} > "$_agmsg_rows_sql"
ROWS=$(agmsg_sqlite ':memory:' < "$_agmsg_rows_sql")
rm -f "$_agmsg_rows_sql"
trap - EXIT HUP INT TERM

# Read-state for the ●(unread)/○(read) marker (G2(c)): read-state is
# recipient-scoped and not carried on a history record, so derive it by unioning
Expand All @@ -61,9 +70,30 @@ while IFS= read -r r; do
u=$(storage_list_unread "$TEAM" "$r") || continue
[ -n "$u" ] || continue
uarr="[$(printf '%s' "$u" | paste -sd, -)]"
ids=$(agmsg_sqlite ':memory:' "
SELECT json_extract(value,'\$.id') FROM json_each('$(printf '%s' "$uarr" | sed "s/'/''/g")');
")
# #777: a recipient's unread backlog grows independently of the display limit, so
# interpolating it into one argv element eventually exceeds the ceiling on a SINGLE
# argument -- on Linux `MAX_ARG_STRLEN`, 131,072 bytes. Measured: the failing
# statement for a 2,079-message team was 125,945 bytes, which is nowhere near
# `ARG_MAX` (2,097,152 here) because ARG_MAX bounds argv plus environment in total,
# not any one element of it. The distinction decides the repair: splitting one long
# statement into several shorter arguments satisfies MAX_ARG_STRLEN and leaves
# ARG_MAX untouched, and a reader who has the wrong limit in mind reaches for the
# wrong fix. Note also that MAX_ARG_STRLEN is a kernel constant with no getconf key,
# so the limit that bites is the one the tools cannot show you.
#
# Pass the statement on stdin instead, mirroring drivers/storage/sqlite-sync.sh:1082.
# printf is a bash builtin, so feeding it a large value does not exec at all and can
# hit neither ceiling.
_agmsg_unread_sql=$(mktemp "${TMPDIR:-/tmp}/agmsg-history-unread.XXXXXX") || continue
trap 'rm -f "$_agmsg_unread_sql"' EXIT HUP INT TERM
{
printf "SELECT json_extract(value,'\$.id') FROM json_each('"
printf '%s' "$uarr" | sed "s/'/''/g"
printf "');\n"
} > "$_agmsg_unread_sql"
ids=$(agmsg_sqlite ':memory:' < "$_agmsg_unread_sql")
rm -f "$_agmsg_unread_sql"
trap - EXIT HUP INT TERM
UNREAD_IDS+="$ids"$'\n'
done <<< "$RECIPIENTS"

Expand Down
Loading