From 97926a9172a5d7f5239027a106620f11c15934dd Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Tue, 15 Jul 2025 18:36:12 +0300 Subject: [PATCH 1/3] Put SQL queries into separate files for better maintainability and to get statistics --- CMakeLists.txt | 12 ++ src/cache/articles_cache.cpp | 3 +- src/cache/comments_cache.cpp | 3 +- src/db/add_comment.sql | 17 ++ src/db/delete_comment_by_id.sql | 3 + src/db/find_comment_by_id_and_slug.sql | 6 + src/db/find_comments_by_article_id.sql | 18 ++ src/db/find_id_article_by_slug.sql | 1 + src/db/find_user_by_id.sql | 1 + src/db/find_user_id_by_username.sql | 1 + src/db/following_user.sql | 13 ++ src/db/get_profile_by_username.sql | 9 + src/db/get_salt_by_email.sql | 1 + src/db/insert_user.sql | 3 + src/db/is_profile_following.sql | 1 + src/db/select_cached_comments.sql | 15 ++ src/db/select_full_article_info.sql | 33 +++ src/db/select_user_by_email_and_password.sql | 2 + src/db/sql.hpp | 200 +----------------- src/db/un_following_user.sql | 12 ++ src/db/update_user.sql | 9 + src/handlers/auth/auth_bearer.cpp | 3 +- src/handlers/comments/comment_delete.cpp | 4 +- src/handlers/comments/comment_post.cpp | 8 +- src/handlers/profiles/profiles.cpp | 2 +- src/handlers/profiles/profiles_follow.cpp | 7 +- .../profiles/profiles_follow_delete.cpp | 7 +- src/handlers/users/user_get.cpp | 2 +- src/handlers/users/user_put.cpp | 2 +- src/handlers/users/users.cpp | 3 +- src/handlers/users/users_login.cpp | 4 +- 31 files changed, 179 insertions(+), 226 deletions(-) create mode 100644 src/db/add_comment.sql create mode 100644 src/db/delete_comment_by_id.sql create mode 100644 src/db/find_comment_by_id_and_slug.sql create mode 100644 src/db/find_comments_by_article_id.sql create mode 100644 src/db/find_id_article_by_slug.sql create mode 100644 src/db/find_user_by_id.sql create mode 100644 src/db/find_user_id_by_username.sql create mode 100644 src/db/following_user.sql create mode 100644 src/db/get_profile_by_username.sql create mode 100644 src/db/get_salt_by_email.sql create mode 100644 src/db/insert_user.sql create mode 100644 src/db/is_profile_following.sql create mode 100644 src/db/select_cached_comments.sql create mode 100644 src/db/select_full_article_info.sql create mode 100644 src/db/select_user_by_email_and_password.sql create mode 100644 src/db/un_following_user.sql create mode 100644 src/db/update_user.sql diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f7b1ce..be8203c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,18 @@ userver_target_generate_chaotic(${PROJECT_NAME}-chgen ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}-chgen) + +userver_add_sql_library( + ${PROJECT_NAME}_sql + NAMESPACE + real_medium + OUTPUT_DIR + ${CMAKE_CURRENT_BINARY_DIR} + SQL_FILES + src/db/*.sql +) +target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}_sql) + target_link_libraries(${PROJECT_NAME}_objs PUBLIC cpp-jwt) find_package(ICU COMPONENTS uc i18n REQUIRED) diff --git a/src/cache/articles_cache.cpp b/src/cache/articles_cache.cpp index 98e6be0..f827dd5 100644 --- a/src/cache/articles_cache.cpp +++ b/src/cache/articles_cache.cpp @@ -4,8 +4,7 @@ namespace real_medium::cache::articles_cache { -userver::storages::postgres::Query ArticlesCachePolicy::kQuery = - userver::storages::postgres::Query(real_medium::sql::kSelectFullArticleInfo.c_str()); +userver::storages::postgres::Query ArticlesCachePolicy::kQuery = real_medium::sql::kSelectFullArticleInfo; void ArticlesCacheContainer::insert_or_assign(Key&& key, Article&& article) { auto article_ptr = std::make_shared(std::move(article)); diff --git a/src/cache/comments_cache.cpp b/src/cache/comments_cache.cpp index c2cac07..6bf426d 100644 --- a/src/cache/comments_cache.cpp +++ b/src/cache/comments_cache.cpp @@ -8,8 +8,7 @@ namespace real_medium::cache::comments_cache { -userver::storages::postgres::Query CommentCachePolicy::kQuery = - userver::storages::postgres::Query(real_medium::sql::kSelectCachedComments.c_str()); +userver::storages::postgres::Query CommentCachePolicy::kQuery = real_medium::sql::kSelectCachedComments; void CommentsCacheContainer::insert_or_assign( real_medium::cache::comments_cache::CommentsCacheContainer::Key&& comment_id, diff --git a/src/db/add_comment.sql b/src/db/add_comment.sql new file mode 100644 index 0000000..527fcc7 --- /dev/null +++ b/src/db/add_comment.sql @@ -0,0 +1,17 @@ +WITH comment AS ( + INSERT INTO real_medium.comments(body, user_id, article_id) + VALUES ($1, $2, $3) + RETURNING * +) +SELECT + comment.comment_id, + comment.created_at, + comment.updated_at, + comment.body, + ( + SELECT + ROW(users.username, users.bio, users.image, false)::real_medium.profile + FROM real_medium.users + WHERE user_id = $2 + ) AS author +FROM comment diff --git a/src/db/delete_comment_by_id.sql b/src/db/delete_comment_by_id.sql new file mode 100644 index 0000000..b203ba1 --- /dev/null +++ b/src/db/delete_comment_by_id.sql @@ -0,0 +1,3 @@ +DELETE FROM real_medium.comments +WHERE comment_id = $1 AND user_id = $2 +RETURNING * diff --git a/src/db/find_comment_by_id_and_slug.sql b/src/db/find_comment_by_id_and_slug.sql new file mode 100644 index 0000000..c422b2b --- /dev/null +++ b/src/db/find_comment_by_id_and_slug.sql @@ -0,0 +1,6 @@ +WITH article AS ( + SELECT article_id FROM real_medium.articles WHERE slug = $2 +) +SELECT * FROM real_medium.comments +JOIN article ON article.article_id = real_medium.comments.article_id +WHERE comment_id = $1 diff --git a/src/db/find_comments_by_article_id.sql b/src/db/find_comments_by_article_id.sql new file mode 100644 index 0000000..277d076 --- /dev/null +++ b/src/db/find_comments_by_article_id.sql @@ -0,0 +1,18 @@ +WITH comments AS ( + SELECT * FROM real_medium.comments WHERE article_id = $1 +) +SELECT + comments.comment_id, + comments.created_at, + comments.updated_at, + comments.body, + ( + SELECT + ROW(users.username, users.bio, users.image, + CASE WHEN EXISTS (SELECT 1 FROM real_medium.followers + WHERE followers.followed_user_id = comments.user_id AND followers.follower_user_id = $2) + THEN true ELSE false END)::real_medium.profile + FROM real_medium.users + WHERE user_id = comments.user_id + ) AS author +FROM comments diff --git a/src/db/find_id_article_by_slug.sql b/src/db/find_id_article_by_slug.sql new file mode 100644 index 0000000..15017c4 --- /dev/null +++ b/src/db/find_id_article_by_slug.sql @@ -0,0 +1 @@ +SELECT article_id FROM real_medium.articles WHERE slug = $1 diff --git a/src/db/find_user_by_id.sql b/src/db/find_user_by_id.sql new file mode 100644 index 0000000..87b97e1 --- /dev/null +++ b/src/db/find_user_by_id.sql @@ -0,0 +1 @@ +SELECT * FROM real_medium.users WHERE user_id = $1 diff --git a/src/db/find_user_id_by_username.sql b/src/db/find_user_id_by_username.sql new file mode 100644 index 0000000..6b7ccd9 --- /dev/null +++ b/src/db/find_user_id_by_username.sql @@ -0,0 +1 @@ +SELECT user_id FROM real_medium.users WHERE username = $1 diff --git a/src/db/following_user.sql b/src/db/following_user.sql new file mode 100644 index 0000000..c1fe937 --- /dev/null +++ b/src/db/following_user.sql @@ -0,0 +1,13 @@ +WITH profile AS ( + SELECT * FROM real_medium.users WHERE user_id = $1 +), following AS ( + INSERT INTO real_medium.followers(followed_user_id, follower_user_id) VALUES ($1, $2) + ON CONFLICT DO NOTHING + RETURNING * +) +SELECT + profile.username, + profile.bio, + profile.image, + CASE WHEN EXISTS (SELECT 1 FROM following) THEN TRUE ELSE FALSE END +FROM profile diff --git a/src/db/get_profile_by_username.sql b/src/db/get_profile_by_username.sql new file mode 100644 index 0000000..2b05bec --- /dev/null +++ b/src/db/get_profile_by_username.sql @@ -0,0 +1,9 @@ +WITH profile AS ( + SELECT * FROM real_medium.users WHERE username = $1 +) +SELECT profile.username, profile.bio, profile.image, + CASE WHEN EXISTS ( + SELECT 1 FROM real_medium.followers + WHERE followed_user_id = profile.user_id AND follower_user_id = $2 + ) THEN true ELSE false END AS following +FROM profile diff --git a/src/db/get_salt_by_email.sql b/src/db/get_salt_by_email.sql new file mode 100644 index 0000000..67137e2 --- /dev/null +++ b/src/db/get_salt_by_email.sql @@ -0,0 +1 @@ +SELECT salt FROM real_medium.users WHERE email = $1 diff --git a/src/db/insert_user.sql b/src/db/insert_user.sql new file mode 100644 index 0000000..e1c0d16 --- /dev/null +++ b/src/db/insert_user.sql @@ -0,0 +1,3 @@ +INSERT INTO real_medium.users(username, email, bio, image, password_hash, salt) +VALUES($1, $2, $3, $4, $5, $6) +RETURNING * diff --git a/src/db/is_profile_following.sql b/src/db/is_profile_following.sql new file mode 100644 index 0000000..8663eb0 --- /dev/null +++ b/src/db/is_profile_following.sql @@ -0,0 +1 @@ +RETURN EXISTS (SELECT 1 FROM real_medium.followers WHERE follower_user_id = $1 AND followed_user_id = $2) diff --git a/src/db/select_cached_comments.sql b/src/db/select_cached_comments.sql new file mode 100644 index 0000000..ccd75ee --- /dev/null +++ b/src/db/select_cached_comments.sql @@ -0,0 +1,15 @@ +SELECT c.comment_id, + c.created_at AS createdAt, + c.updated_at AS updatedAt, + c.body, + c.user_id, + a.slug, + ROW(u.user_id, u.username, u.email, u.bio, u.image, u.password_hash, u.salt)::real_medium.user AS author_info, + ARRAY( + SELECT follower_user_id + FROM real_medium.followers fl + WHERE fl.followed_user_id = c.user_id + ) AS following +FROM real_medium.comments c +JOIN real_medium.articles a ON a.article_id = c.article_id +JOIN real_medium.users u ON c.user_id = u.user_id diff --git a/src/db/select_full_article_info.sql b/src/db/select_full_article_info.sql new file mode 100644 index 0000000..d6966fc --- /dev/null +++ b/src/db/select_full_article_info.sql @@ -0,0 +1,33 @@ +SELECT a.article_id AS articleId, + a.title, + a.slug, + a.description, + a.body, + ARRAY( + SELECT tag_name + FROM real_medium.tag_list tl + JOIN real_medium.article_tag at ON tl.tag_id = at.tag_id + WHERE at.article_id = a.article_id + ) AS tags, + a.created_at AS createdAt, + a.updated_at AS updatedAt, + ARRAY( + SELECT user_id + FROM real_medium.favorites f + WHERE f.article_id = a.article_id + ) AS article_favorited_by_user_ids, + + ARRAY( + SELECT u.username + FROM real_medium.favorites f + JOIN real_medium.users u ON f.user_id = u.user_id + WHERE f.article_id = a.article_id + ) AS article_favorited_by_usernames, + ARRAY( + SELECT follower_user_id + FROM real_medium.followers fl + WHERE fl.followed_user_id = a.user_id + ) AS author_followed_by_user_ids, + ROW(u.user_id, u.username, u.email, u.bio, u.image, u.password_hash, u.salt)::real_medium.user AS author_info +FROM real_medium.articles a +JOIN real_medium.users u ON a.user_id = u.user_id diff --git a/src/db/select_user_by_email_and_password.sql b/src/db/select_user_by_email_and_password.sql new file mode 100644 index 0000000..aa947e1 --- /dev/null +++ b/src/db/select_user_by_email_and_password.sql @@ -0,0 +1,2 @@ +SELECT * FROM real_medium.users +WHERE email = $1 AND password_hash = $2 diff --git a/src/db/sql.hpp b/src/db/sql.hpp index 7395dde..1a3d998 100644 --- a/src/db/sql.hpp +++ b/src/db/sql.hpp @@ -2,150 +2,10 @@ #include -namespace real_medium::sql { - -inline constexpr userver::utils::zstring_view kInsertUser = R"~( -INSERT INTO real_medium.users(username, email, bio, image, password_hash, salt) -VALUES($1, $2, $3, $4, $5, $6) -RETURNING * -)~"; - -inline constexpr userver::utils::zstring_view kSelectUserByEmailAndPassword = R"~( -SELECT * FROM real_medium.users -WHERE email = $1 AND password_hash = $2 -)~"; - -inline constexpr userver::utils::zstring_view kUpdateUser = R"~( -UPDATE real_medium.users SET - username = COALESCE($2, username), - email = COALESCE($3, email), - bio = COALESCE($4, bio), - image = COALESCE($5, image), - password_hash = COALESCE($6, password_hash), - salt = COALESCE($7, salt) -WHERE user_id = $1 -RETURNING * -)~"; - -inline constexpr userver::utils::zstring_view kFindUserById = R"~( -SELECT * FROM real_medium.users WHERE user_id = $1 -)~"; - -inline constexpr userver::utils::zstring_view kFindUserIDByUsername = R"~( -SELECT user_id FROM real_medium.users WHERE username = $1 -)~"; - -// Comments -inline constexpr userver::utils::zstring_view kFindCommentByIdAndSlug = R"~( -WITH article AS ( - SELECT article_id FROM real_medium.articles WHERE slug = $2 -) -SELECT * FROM real_medium.comments -JOIN article ON article.article_id = real_medium.comments.article_id -WHERE comment_id = $1 -)~"; - -inline constexpr userver::utils::zstring_view kFindCommentsByArticleId = R"~( -WITH comments AS ( - SELECT * FROM real_medium.comments WHERE article_id = $1 -) -SELECT - comments.comment_id, - comments.created_at, - comments.updated_at, - comments.body, - ( - SELECT - ROW(users.username, users.bio, users.image, - CASE WHEN EXISTS (SELECT 1 FROM real_medium.followers - WHERE followers.followed_user_id = comments.user_id AND followers.follower_user_id = $2) - THEN true ELSE false END)::real_medium.profile - FROM real_medium.users - WHERE user_id = comments.user_id - ) AS author -FROM comments -)~"; - -inline constexpr userver::utils::zstring_view kDeleteCommentById = R"~( -DELETE FROM real_medium.comments WHERE comment_id = $1 AND user_id = $2 -RETURNING * -)~"; - -inline constexpr userver::utils::zstring_view kAddComment = R"~( -WITH comment AS ( - INSERT INTO real_medium.comments(body, user_id, article_id) - VALUES ($1, $2, $3) - RETURNING * -) -SELECT - comment.comment_id, - comment.created_at, - comment.updated_at, - comment.body, - ( - SELECT - ROW(users.username, users.bio, users.image, false)::real_medium.profile - FROM real_medium.users - WHERE user_id = $2 - ) AS author -FROM comment -)~"; - -inline constexpr userver::utils::zstring_view kFindIdArticleBySlug = R"~( -SELECT article_id FROM real_medium.articles WHERE slug = $1 -)~"; +// Codegenerated from *.sql files +#include -inline constexpr userver::utils::zstring_view kIsProfileFollowing = R"~( -RETURN EXISTS (SELECT 1 FROM real_medium.followers WHERE follower_user_id = $1 AND followed_user_id = $2); -)~"; - -// TODO: reuse common kIsProfileFollowing -inline constexpr userver::utils::zstring_view kGetProfileByUsername = R"~( -WITH profile AS ( - SELECT * FROM real_medium.users WHERE username = $1 -) -SELECT profile.username, profile.bio, profile.image, - CASE WHEN EXISTS ( - SELECT 1 FROM real_medium.followers - WHERE followed_user_id = profile.user_id AND follower_user_id = $2 - ) THEN true ELSE false END AS following -FROM profile -)~"; - -inline constexpr userver::utils::zstring_view kGetSaltByEmail = R"~( -SELECT salt FROM real_medium.users WHERE email = $1 -)~"; - -inline constexpr userver::utils::zstring_view KFollowingUser = R"~( -WITH profile AS ( - SELECT * FROM real_medium.users WHERE user_id = $1 -), following AS ( - INSERT INTO real_medium.followers(followed_user_id, follower_user_id) VALUES ($1, $2) - ON CONFLICT DO NOTHING - RETURNING * -) -SELECT - profile.username, - profile.bio, - profile.image, - CASE WHEN EXISTS (SELECT 1 FROM following) THEN TRUE ELSE FALSE END -FROM profile -)~"; - -inline constexpr userver::utils::zstring_view KUnFollowingUser = R"~( -WITH profile AS ( - SELECT * FROM real_medium.users WHERE user_id = $1 -), following AS ( - DELETE FROM real_medium.followers WHERE followed_user_id = $1 AND follower_user_id = $2 - RETURNING * -) -SELECT - profile.username, - profile.bio, - profile.image, - CASE WHEN EXISTS (SELECT 1 FROM following) THEN FALSE ELSE TRUE END -FROM profile -)~"; +namespace real_medium::sql { inline constexpr userver::utils::zstring_view kCreateArticle{R"~( SELECT real_medium.create_article($1, $2, $3, $4, $5, $6) @@ -209,58 +69,4 @@ inline constexpr userver::utils::zstring_view kFindArticlesByFilters{R"~( SELECT real_medium.get_articles_by_filters($1, $2, $3, $4, $5, $6) )~"}; -inline constexpr userver::utils::zstring_view kSelectFullArticleInfo = {R"~( -SELECT a.article_id AS articleId, - a.title, - a.slug, - a.description, - a.body, - ARRAY( - SELECT tag_name - FROM real_medium.tag_list tl - JOIN real_medium.article_tag at ON tl.tag_id = at.tag_id - WHERE at.article_id = a.article_id - ) AS tags, - a.created_at AS createdAt, - a.updated_at AS updatedAt, - ARRAY( - SELECT user_id - FROM real_medium.favorites f - WHERE f.article_id = a.article_id - ) AS article_favorited_by_user_ids, - - ARRAY( - SELECT u.username - FROM real_medium.favorites f - JOIN real_medium.users u ON f.user_id = u.user_id - WHERE f.article_id = a.article_id - ) AS article_favorited_by_usernames, - ARRAY( - SELECT follower_user_id - FROM real_medium.followers fl - WHERE fl.followed_user_id = a.user_id - ) AS author_followed_by_user_ids, - ROW(u.user_id, u.username, u.email, u.bio, u.image, u.password_hash, u.salt)::real_medium.user AS author_info -FROM real_medium.articles a -JOIN real_medium.users u ON a.user_id = u.user_id -)~"}; - -inline constexpr userver::utils::zstring_view kSelectCachedComments = {R"~( -SELECT c.comment_id, - c.created_at AS createdAt, - c.updated_at AS updatedAt, - c.body, - c.user_id, - a.slug, - ROW(u.user_id, u.username, u.email, u.bio, u.image, u.password_hash, u.salt)::real_medium.user AS author_info, - ARRAY( - SELECT follower_user_id - FROM real_medium.followers fl - WHERE fl.followed_user_id = c.user_id - ) AS following -FROM real_medium.comments c -JOIN real_medium.articles a ON a.article_id = c.article_id -JOIN real_medium.users u ON c.user_id = u.user_id -)~"}; - } // namespace real_medium::sql diff --git a/src/db/un_following_user.sql b/src/db/un_following_user.sql new file mode 100644 index 0000000..36b3af6 --- /dev/null +++ b/src/db/un_following_user.sql @@ -0,0 +1,12 @@ +WITH profile AS ( + SELECT * FROM real_medium.users WHERE user_id = $1 +), following AS ( + DELETE FROM real_medium.followers WHERE followed_user_id = $1 AND follower_user_id = $2 + RETURNING * +) +SELECT + profile.username, + profile.bio, + profile.image, + CASE WHEN EXISTS (SELECT 1 FROM following) THEN FALSE ELSE TRUE END +FROM profile diff --git a/src/db/update_user.sql b/src/db/update_user.sql new file mode 100644 index 0000000..6cc803e --- /dev/null +++ b/src/db/update_user.sql @@ -0,0 +1,9 @@ +UPDATE real_medium.users SET + username = COALESCE($2, username), + email = COALESCE($3, email), + bio = COALESCE($4, bio), + image = COALESCE($5, image), + password_hash = COALESCE($6, password_hash), + salt = COALESCE($7, salt) +WHERE user_id = $1 +RETURNING * diff --git a/src/handlers/auth/auth_bearer.cpp b/src/handlers/auth/auth_bearer.cpp index 9fc55f2..2474f33 100644 --- a/src/handlers/auth/auth_bearer.cpp +++ b/src/handlers/auth/auth_bearer.cpp @@ -70,8 +70,7 @@ AuthCheckerBearer::AuthCheckResult AuthCheckerBearer::CheckAuth( } auto id = payload.get_claim_value("id"); - const auto res = - pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, sql::kFindUserById.c_str(), id); + const auto res = pg_cluster_->Execute(userver::storages::postgres::ClusterHostType::kSlave, sql::kFindUserById, id); if (res.IsEmpty()) { return AuthCheckResult{ AuthCheckResult::Status::kTokenNotFound, diff --git a/src/handlers/comments/comment_delete.cpp b/src/handlers/comments/comment_delete.cpp index e1b0ea2..8dca5d8 100644 --- a/src/handlers/comments/comment_delete.cpp +++ b/src/handlers/comments/comment_delete.cpp @@ -17,7 +17,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const auto& slug = request.GetPathArg("slug"); const auto result_find_comment = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, sql::kFindCommentByIdAndSlug.c_str(), comment_id, slug + userver::storages::postgres::ClusterHostType::kMaster, sql::kFindCommentByIdAndSlug, comment_id, slug ); if (result_find_comment.IsEmpty()) { @@ -27,7 +27,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( } const auto result_delete_comment = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, sql::kDeleteCommentById.c_str(), comment_id, user_id + userver::storages::postgres::ClusterHostType::kMaster, sql::kDeleteCommentById, comment_id, user_id ); if (result_delete_comment.IsEmpty()) { diff --git a/src/handlers/comments/comment_post.cpp b/src/handlers/comments/comment_post.cpp index 445fe62..1cff2de 100644 --- a/src/handlers/comments/comment_post.cpp +++ b/src/handlers/comments/comment_post.cpp @@ -31,7 +31,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const auto& slug = request.GetPathArg("slug"); const auto res_find_article = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, sql::kFindIdArticleBySlug.c_str(), slug); + GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, sql::kFindIdArticleBySlug, slug); if (res_find_article.IsEmpty()) { auto& response = request.GetHttpResponse(); @@ -42,11 +42,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const auto article_id = res_find_article.AsSingleRow(); const auto res_ins_new_comment = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - sql::kAddComment.c_str(), - comment_body, - user_id, - article_id + userver::storages::postgres::ClusterHostType::kMaster, sql::kAddComment, comment_body, user_id, article_id ); if (res_ins_new_comment.IsEmpty()) { diff --git a/src/handlers/profiles/profiles.cpp b/src/handlers/profiles/profiles.cpp index c087a7e..bdbaf39 100644 --- a/src/handlers/profiles/profiles.cpp +++ b/src/handlers/profiles/profiles.cpp @@ -21,7 +21,7 @@ json::Value Handler::HandleRequestJsonThrow(const HttpRequest& request, const js const { auto user_id = context.GetData>("id"); const auto& username = request.GetPathArg("username"); - auto res = GetPg().Execute(ClusterHostType::kMaster, sql::kGetProfileByUsername.c_str(), username, user_id); + auto res = GetPg().Execute(ClusterHostType::kMaster, sql::kGetProfileByUsername, username, user_id); if (res.IsEmpty()) { auto& response = request.GetHttpResponse(); response.SetStatus(userver::server::http::HttpStatus::kNotFound); diff --git a/src/handlers/profiles/profiles_follow.cpp b/src/handlers/profiles/profiles_follow.cpp index 507b181..db9f67a 100644 --- a/src/handlers/profiles/profiles_follow.cpp +++ b/src/handlers/profiles/profiles_follow.cpp @@ -30,9 +30,8 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( return utils::error::MakeError("username", "It is null."); } - const auto res_find_id_username = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kSlave, sql::kFindUserIDByUsername.c_str(), username - ); + const auto res_find_id_username = + GetPg().Execute(userver::storages::postgres::ClusterHostType::kSlave, sql::kFindUserIdByUsername, username); if (res_find_id_username.IsEmpty()) { auto& response = request.GetHttpResponse(); response.SetStatus(userver::server::http::HttpStatus::kNotFound); @@ -48,7 +47,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( } const auto res_following = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kSlave, sql::KFollowingUser.c_str(), username_id, user_id + userver::storages::postgres::ClusterHostType::kSlave, sql::kFollowingUser, username_id, user_id ); const auto profile = res_following.AsSingleRow(userver::storages::postgres::kRowTag); diff --git a/src/handlers/profiles/profiles_follow_delete.cpp b/src/handlers/profiles/profiles_follow_delete.cpp index f1ef8ac..a1bac2b 100644 --- a/src/handlers/profiles/profiles_follow_delete.cpp +++ b/src/handlers/profiles/profiles_follow_delete.cpp @@ -29,9 +29,8 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( return utils::error::MakeError("username", "It is null."); } - const auto res_find_id_username = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kSlave, sql::kFindUserIDByUsername.c_str(), username - ); + const auto res_find_id_username = + GetPg().Execute(userver::storages::postgres::ClusterHostType::kSlave, sql::kFindUserIdByUsername, username); if (res_find_id_username.IsEmpty()) { auto& response = request.GetHttpResponse(); response.SetStatus(userver::server::http::HttpStatus::kNotFound); @@ -46,7 +45,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( } const auto res_unfollowing = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kSlave, sql::KUnFollowingUser.c_str(), username_id, user_id + userver::storages::postgres::ClusterHostType::kSlave, sql::kUnFollowingUser, username_id, user_id ); const auto profile = res_unfollowing.AsSingleRow(userver::storages::postgres::kRowTag); diff --git a/src/handlers/users/user_get.cpp b/src/handlers/users/user_get.cpp index a2202b1..b493c53 100644 --- a/src/handlers/users/user_get.cpp +++ b/src/handlers/users/user_get.cpp @@ -13,7 +13,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( auto user_id = context.GetData>("id"); const auto result = - GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, sql::kFindUserById.c_str(), user_id); + GetPg().Execute(userver::storages::postgres::ClusterHostType::kMaster, sql::kFindUserById, user_id); if (result.IsEmpty()) { auto& response = request.GetHttpResponse(); diff --git a/src/handlers/users/user_put.cpp b/src/handlers/users/user_put.cpp index 85dc88a..40bfd0a 100644 --- a/src/handlers/users/user_put.cpp +++ b/src/handlers/users/user_put.cpp @@ -34,7 +34,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const auto result = GetPg().Execute( userver::storages::postgres::ClusterHostType::kMaster, - sql::kUpdateUser.c_str(), + sql::kUpdateUser, user_id, user_change_data.username, user_change_data.email, diff --git a/src/handlers/users/users.cpp b/src/handlers/users/users.cpp index 967e95f..5c17a03 100644 --- a/src/handlers/users/users.cpp +++ b/src/handlers/users/users.cpp @@ -18,7 +18,6 @@ userver::formats::json::Value RegisterUser:: HandleRequestJsonThrow(const userver::server::http::HttpRequest& request, const userver::formats::json::Value& request_json, userver::server::request::RequestContext&) const { handlers::UserRegistrationDTO user_register = request_json["user"].As(); - ; try { validator::validate(user_register); @@ -33,7 +32,7 @@ userver::formats::json::Value RegisterUser:: try { auto query_result = GetPg().Execute( userver::storages::postgres::ClusterHostType::kMaster, - sql::kInsertUser.c_str(), + sql::kInsertUser, user_register.username, user_register.email, user_register.bio, diff --git a/src/handlers/users/users_login.cpp b/src/handlers/users/users_login.cpp index e5fe50f..83936f2 100644 --- a/src/handlers/users/users_login.cpp +++ b/src/handlers/users/users_login.cpp @@ -35,7 +35,7 @@ class LoginUser final : public Common { } auto salt = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, sql::kGetSaltByEmail.c_str(), user_login.email + userver::storages::postgres::ClusterHostType::kMaster, sql::kGetSaltByEmail, user_login.email ); if (salt.IsEmpty()) { auto& response = request.GetHttpResponse(); @@ -47,7 +47,7 @@ class LoginUser final : public Common { auto user_result = GetPg().Execute( userver::storages::postgres::ClusterHostType::kMaster, - sql::kSelectUserByEmailAndPassword.c_str(), + sql::kSelectUserByEmailAndPassword, user_login.email, password_hash ); From 52d34f433ad9025831334fadddf8430398bd64d2 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Tue, 15 Jul 2025 19:05:42 +0300 Subject: [PATCH 2/3] final portion of transitions --- src/db/create_article.sql | 1 + src/db/decrement_favorites_count.sql | 3 + src/db/delete_article_by_slug.sql | 1 + src/db/delete_favorite_pair.sql | 6 ++ src/db/find_articles_by_filters.sql | 1 + src/db/find_articles_by_followed_users.sql | 1 + src/db/get_article_id_by_slug.sql | 1 + src/db/get_article_with_author_profile.sql | 1 + ...et_article_with_author_profile_by_slug.sql | 1 + src/db/increment_favorites_count.sql | 3 + src/db/insert_favorite_pair.sql | 6 ++ src/db/sql.hpp | 70 +------------------ src/db/update_article_by_slug.sql | 1 + src/handlers/articles/articles_favorite.cpp | 6 +- src/handlers/articles/articles_post.cpp | 4 +- .../articles/articles_slug_delete.cpp | 7 +- src/handlers/articles/articles_slug_put.cpp | 4 +- src/handlers/articles/articles_unfavorite.cpp | 6 +- 18 files changed, 39 insertions(+), 84 deletions(-) create mode 100644 src/db/create_article.sql create mode 100644 src/db/decrement_favorites_count.sql create mode 100644 src/db/delete_article_by_slug.sql create mode 100644 src/db/delete_favorite_pair.sql create mode 100644 src/db/find_articles_by_filters.sql create mode 100644 src/db/find_articles_by_followed_users.sql create mode 100644 src/db/get_article_id_by_slug.sql create mode 100644 src/db/get_article_with_author_profile.sql create mode 100644 src/db/get_article_with_author_profile_by_slug.sql create mode 100644 src/db/increment_favorites_count.sql create mode 100644 src/db/insert_favorite_pair.sql create mode 100644 src/db/update_article_by_slug.sql diff --git a/src/db/create_article.sql b/src/db/create_article.sql new file mode 100644 index 0000000..6f54a46 --- /dev/null +++ b/src/db/create_article.sql @@ -0,0 +1 @@ +SELECT real_medium.create_article($1, $2, $3, $4, $5, $6) diff --git a/src/db/decrement_favorites_count.sql b/src/db/decrement_favorites_count.sql new file mode 100644 index 0000000..28b3924 --- /dev/null +++ b/src/db/decrement_favorites_count.sql @@ -0,0 +1,3 @@ +UPDATE real_medium.articles +SET favorites_count=favorites_count - 1 +WHERE article_id=$1 diff --git a/src/db/delete_article_by_slug.sql b/src/db/delete_article_by_slug.sql new file mode 100644 index 0000000..5383a26 --- /dev/null +++ b/src/db/delete_article_by_slug.sql @@ -0,0 +1 @@ +SELECT real_medium.delete_article_by_slug($1, $2) diff --git a/src/db/delete_favorite_pair.sql b/src/db/delete_favorite_pair.sql new file mode 100644 index 0000000..dc1062b --- /dev/null +++ b/src/db/delete_favorite_pair.sql @@ -0,0 +1,6 @@ +WITH tmp(article_id, user_id) AS ( + SELECT article_id, $1 FROM real_medium.articles WHERE slug=$2 +) +DELETE FROM real_medium.favorites +WHERE (article_id, user_id) IN (SELECT article_id, user_id FROM tmp) +RETURNING article_id diff --git a/src/db/find_articles_by_filters.sql b/src/db/find_articles_by_filters.sql new file mode 100644 index 0000000..4fe81d1 --- /dev/null +++ b/src/db/find_articles_by_filters.sql @@ -0,0 +1 @@ +SELECT real_medium.get_articles_by_filters($1, $2, $3, $4, $5, $6) diff --git a/src/db/find_articles_by_followed_users.sql b/src/db/find_articles_by_followed_users.sql new file mode 100644 index 0000000..1c8598e --- /dev/null +++ b/src/db/find_articles_by_followed_users.sql @@ -0,0 +1 @@ +SELECT real_medium.get_feed_articles($1, $2, $3) diff --git a/src/db/get_article_id_by_slug.sql b/src/db/get_article_id_by_slug.sql new file mode 100644 index 0000000..f90a0e4 --- /dev/null +++ b/src/db/get_article_id_by_slug.sql @@ -0,0 +1 @@ +SELECT real_medium.get_article_id_by_slug($1) diff --git a/src/db/get_article_with_author_profile.sql b/src/db/get_article_with_author_profile.sql new file mode 100644 index 0000000..d34f16a --- /dev/null +++ b/src/db/get_article_with_author_profile.sql @@ -0,0 +1 @@ +SELECT real_medium.get_article_with_author_profile($1, $2) diff --git a/src/db/get_article_with_author_profile_by_slug.sql b/src/db/get_article_with_author_profile_by_slug.sql new file mode 100644 index 0000000..2fdb628 --- /dev/null +++ b/src/db/get_article_with_author_profile_by_slug.sql @@ -0,0 +1 @@ +SELECT real_medium.get_article_with_author_profile_by_slug($1, $2) diff --git a/src/db/increment_favorites_count.sql b/src/db/increment_favorites_count.sql new file mode 100644 index 0000000..f9a7faf --- /dev/null +++ b/src/db/increment_favorites_count.sql @@ -0,0 +1,3 @@ +UPDATE real_medium.articles +SET favorites_count=favorites_count + 1 +WHERE article_id=$1 diff --git a/src/db/insert_favorite_pair.sql b/src/db/insert_favorite_pair.sql new file mode 100644 index 0000000..5d626f3 --- /dev/null +++ b/src/db/insert_favorite_pair.sql @@ -0,0 +1,6 @@ +WITH tmp(article_id, user_id) AS ( + SELECT article_id, $1 FROM real_medium.articles WHERE slug=$2 +) +INSERT INTO real_medium.favorites(article_id, user_id) (SELECT article_id, user_id FROM tmp) +ON CONFLICT DO NOTHING +RETURNING article_id diff --git a/src/db/sql.hpp b/src/db/sql.hpp index 1a3d998..8afddf1 100644 --- a/src/db/sql.hpp +++ b/src/db/sql.hpp @@ -1,72 +1,4 @@ #pragma once -#include - -// Codegenerated from *.sql files +// Followinf header is codegenerated from *.sql files #include - -namespace real_medium::sql { - -inline constexpr userver::utils::zstring_view kCreateArticle{R"~( -SELECT real_medium.create_article($1, $2, $3, $4, $5, $6) -)~"}; - -inline constexpr userver::utils::zstring_view kGetArticleWithAuthorProfile{R"~( -SELECT real_medium.get_article_with_author_profile($1, $2) -)~"}; - -inline constexpr userver::utils::zstring_view kGetArticleWithAuthorProfileBySlug{R"~( -SELECT real_medium.get_article_with_author_profile_by_slug($1, $2) -)~"}; - -inline constexpr userver::utils::zstring_view kInsertFavoritePair = R"~( -WITH tmp(article_id, user_id) AS ( - SELECT article_id, $1 FROM real_medium.articles WHERE slug=$2 -) -INSERT INTO real_medium.favorites(article_id, user_id) (SELECT article_id, user_id FROM tmp) -ON CONFLICT DO NOTHING -RETURNING article_id -)~"; - -inline constexpr userver::utils::zstring_view kIncrementFavoritesCount = R"~( -UPDATE real_medium.articles -SET favorites_count=favorites_count + 1 -WHERE article_id=$1 -)~"; - -inline constexpr userver::utils::zstring_view kDeleteFavoritePair = R"~( -WITH tmp(article_id, user_id) AS ( - SELECT article_id, $1 FROM real_medium.articles WHERE slug=$2 -) -DELETE FROM real_medium.favorites -WHERE (article_id, user_id) IN (SELECT article_id, user_id FROM tmp) -RETURNING article_id -)~"; - -inline constexpr userver::utils::zstring_view kDecrementFavoritesCount = R"~( -UPDATE real_medium.articles -SET favorites_count=favorites_count - 1 -WHERE article_id=$1 -)~"; - -inline constexpr userver::utils::zstring_view kFindArticlesByFollowedUsers = R"~( -SELECT real_medium.get_feed_articles($1, $2, $3) -)~"; - -inline constexpr userver::utils::zstring_view kGetArticleIdBySlug{R"~( -SELECT real_medium.get_article_id_by_slug($1) -)~"}; - -inline constexpr userver::utils::zstring_view kUpdateArticleBySlug{R"~( -SELECT real_medium.update_article_by_slug($1, $2, $3, $4, $5, $6) -)~"}; - -inline constexpr userver::utils::zstring_view kDeleteArticleBySlug{R"~( -SELECT real_medium.delete_article_by_slug($1, $2) -)~"}; - -inline constexpr userver::utils::zstring_view kFindArticlesByFilters{R"~( -SELECT real_medium.get_articles_by_filters($1, $2, $3, $4, $5, $6) -)~"}; - -} // namespace real_medium::sql diff --git a/src/db/update_article_by_slug.sql b/src/db/update_article_by_slug.sql new file mode 100644 index 0000000..0420a4c --- /dev/null +++ b/src/db/update_article_by_slug.sql @@ -0,0 +1 @@ +SELECT real_medium.update_article_by_slug($1, $2, $3, $4, $5, $6) diff --git a/src/handlers/articles/articles_favorite.cpp b/src/handlers/articles/articles_favorite.cpp index 5ca8ccf..1b07907 100644 --- a/src/handlers/articles/articles_favorite.cpp +++ b/src/handlers/articles/articles_favorite.cpp @@ -21,18 +21,18 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( userver::storages::postgres::Transaction::RW ); - auto res = transaction.Execute(sql::kInsertFavoritePair.c_str(), user_id, slug); + auto res = transaction.Execute(sql::kInsertFavoritePair, user_id, slug); if (!res.IsEmpty()) { auto article_id = res.AsSingleRow(); - transaction.Execute(sql::kIncrementFavoritesCount.c_str(), article_id); + transaction.Execute(sql::kIncrementFavoritesCount, article_id); transaction.Commit(); } const auto get_article_res = GetPg().Execute( userver::storages::postgres::ClusterHostType::kSlave, - real_medium::sql::kGetArticleWithAuthorProfileBySlug.c_str(), + real_medium::sql::kGetArticleWithAuthorProfileBySlug, slug, user_id ); diff --git a/src/handlers/articles/articles_post.cpp b/src/handlers/articles/articles_post.cpp index b3f3298..4afb68d 100644 --- a/src/handlers/articles/articles_post.cpp +++ b/src/handlers/articles/articles_post.cpp @@ -34,7 +34,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const auto res = GetPg().Execute( userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kCreateArticle.c_str(), + real_medium::sql::kCreateArticle, create_article_request.title, slug, create_article_request.body, @@ -55,7 +55,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const auto res = GetPg().Execute( userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kGetArticleWithAuthorProfile.c_str(), + real_medium::sql::kGetArticleWithAuthorProfile, article_id, user_id ); diff --git a/src/handlers/articles/articles_slug_delete.cpp b/src/handlers/articles/articles_slug_delete.cpp index 5913bf8..98e6806 100644 --- a/src/handlers/articles/articles_slug_delete.cpp +++ b/src/handlers/articles/articles_slug_delete.cpp @@ -14,17 +14,14 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( const auto& slug = request.GetPathArg("slug"); const auto user_id = context.GetData>("id"); auto res = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, real_medium::sql::kGetArticleIdBySlug.c_str(), slug + userver::storages::postgres::ClusterHostType::kMaster, real_medium::sql::kGetArticleIdBySlug, slug ); if (res.IsEmpty()) { request.SetResponseStatus(userver::server::http::HttpStatus::kNotFound); return {}; } res = GetPg().Execute( - userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kDeleteArticleBySlug.c_str(), - slug, - user_id + userver::storages::postgres::ClusterHostType::kMaster, real_medium::sql::kDeleteArticleBySlug, slug, user_id ); if (res.IsEmpty()) { diff --git a/src/handlers/articles/articles_slug_put.cpp b/src/handlers/articles/articles_slug_put.cpp index ff6ef88..5d5d1f1 100644 --- a/src/handlers/articles/articles_slug_put.cpp +++ b/src/handlers/articles/articles_slug_put.cpp @@ -32,7 +32,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( : std::nullopt; const auto res = GetPg().Execute( userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kUpdateArticleBySlug.c_str(), + real_medium::sql::kUpdateArticleBySlug, slug, user_id, update_request.title, @@ -55,7 +55,7 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( } const auto res = GetPg().Execute( userver::storages::postgres::ClusterHostType::kMaster, - real_medium::sql::kGetArticleWithAuthorProfile.c_str(), + real_medium::sql::kGetArticleWithAuthorProfile, article_id, user_id ); diff --git a/src/handlers/articles/articles_unfavorite.cpp b/src/handlers/articles/articles_unfavorite.cpp index 6ee0c60..1d3ccfa 100644 --- a/src/handlers/articles/articles_unfavorite.cpp +++ b/src/handlers/articles/articles_unfavorite.cpp @@ -19,18 +19,18 @@ userver::formats::json::Value Handler::HandleRequestJsonThrow( userver::storages::postgres::Transaction::RW ); - auto res = transaction.Execute(sql::kDeleteFavoritePair.c_str(), user_id, slug); + auto res = transaction.Execute(sql::kDeleteFavoritePair, user_id, slug); if (!res.IsEmpty()) { auto article_id = res.AsSingleRow(); - transaction.Execute(sql::kDecrementFavoritesCount.c_str(), article_id); + transaction.Execute(sql::kDecrementFavoritesCount, article_id); transaction.Commit(); } const auto get_article_res = GetPg().Execute( userver::storages::postgres::ClusterHostType::kSlave, - real_medium::sql::kGetArticleWithAuthorProfileBySlug.c_str(), + real_medium::sql::kGetArticleWithAuthorProfileBySlug, slug, user_id ); From f23f5f3f48e8426f658aa08bb3d2415b751dc9f6 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 28 Jul 2025 16:26:57 +0300 Subject: [PATCH 3/3] Update CMakeLists.txt --- CMakeLists.txt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index be8203c..3f7f5af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,12 +50,9 @@ target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}-chgen) userver_add_sql_library( ${PROJECT_NAME}_sql - NAMESPACE - real_medium - OUTPUT_DIR - ${CMAKE_CURRENT_BINARY_DIR} - SQL_FILES - src/db/*.sql + NAMESPACE real_medium + OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR} + SQL_FILES src/db/*.sql ) target_link_libraries(${PROJECT_NAME}_objs PUBLIC ${PROJECT_NAME}_sql)