Кажется, вам нужна функция LISTAGG -
SELECT U.user_name, LISTAGG(B.title || 'at' || B.date, ',') WITHIN GROUP (ORDER BY B.date)
FROM users U
JOIN book B ON U.book_id = B.id
GROUP BY U.user_name
Если вам строго необходимо использовать метод RECURSIVE CTE, вы можете использовать -
WITH TMP AS (SELECT U.user_name, B.title || 'at' || B.date book_detail, ROW_NUMBER() OVER (PARTITION BY user_name) RN
FROM users U
JOIN book B ON U.book_id = B.id),
cte AS (SELECT user_name, book_detail, RN
FROM TMP
WHERE RN = 1
UNION ALL
SELECT T.user_name, C.book_detail || ', ' || T.book_detail, C.RN+1
FROM TMP T
JOIN cte C ON U.id = C.id
WHERE T.user_name = C.user_name
and C.RN + 1 = T.rn)
SELECT user_name, book_detail
FROM cte C1
WHERE RN = (SELECT MAX(RN)
FROM cte C2
WHERE C1.user_name = C2.user_name)