From e3df845abdb74da3fed56d24f8965dd9bb6ba3fe Mon Sep 17 00:00:00 2001 From: JohnDoe6345789 Date: Fri, 26 Dec 2025 01:30:51 +0000 Subject: [PATCH] code: dbal,hpp,cpp (16 files) --- dbal/cpp/src/query/ast.hpp | 28 ++++++++ dbal/cpp/src/query/ast_add_child.hpp | 20 ++++++ dbal/cpp/src/query/ast_node.hpp | 32 +++++++++ dbal/cpp/src/query/ast_to_string.hpp | 26 +++++++ dbal/cpp/src/query/builder.hpp | 56 +++++++++++++++ dbal/cpp/src/query/builder_build.hpp | 49 +++++++++++++ dbal/cpp/src/query/builder_from.hpp | 20 ++++++ dbal/cpp/src/query/builder_limit.hpp | 20 ++++++ dbal/cpp/src/query/builder_order_by.hpp | 21 ++++++ dbal/cpp/src/query/builder_select.hpp | 21 ++++++ dbal/cpp/src/query/builder_state.hpp | 24 +++++++ dbal/cpp/src/query/builder_where.hpp | 20 ++++++ dbal/cpp/src/query/normalize.hpp | 22 ++++++ dbal/cpp/src/query/query_normalize.hpp | 58 +++++++++++++++ dbal/ts/src/core/entities/user/delete-user.ts | 24 ++++--- dbal/ts/src/core/entities/user/update-user.ts | 70 +++++++++---------- 16 files changed, 465 insertions(+), 46 deletions(-) create mode 100644 dbal/cpp/src/query/ast.hpp create mode 100644 dbal/cpp/src/query/ast_add_child.hpp create mode 100644 dbal/cpp/src/query/ast_node.hpp create mode 100644 dbal/cpp/src/query/ast_to_string.hpp create mode 100644 dbal/cpp/src/query/builder.hpp create mode 100644 dbal/cpp/src/query/builder_build.hpp create mode 100644 dbal/cpp/src/query/builder_from.hpp create mode 100644 dbal/cpp/src/query/builder_limit.hpp create mode 100644 dbal/cpp/src/query/builder_order_by.hpp create mode 100644 dbal/cpp/src/query/builder_select.hpp create mode 100644 dbal/cpp/src/query/builder_state.hpp create mode 100644 dbal/cpp/src/query/builder_where.hpp create mode 100644 dbal/cpp/src/query/normalize.hpp create mode 100644 dbal/cpp/src/query/query_normalize.hpp diff --git a/dbal/cpp/src/query/ast.hpp b/dbal/cpp/src/query/ast.hpp new file mode 100644 index 000000000..bc5ccc1c0 --- /dev/null +++ b/dbal/cpp/src/query/ast.hpp @@ -0,0 +1,28 @@ +#pragma once +/** + * @file ast.hpp + * @brief AST class (wrapper) + */ + +#include "ast_node.hpp" +#include "ast_add_child.hpp" +#include "ast_to_string.hpp" + +namespace dbal::query { + +/** + * AST wrapper class + */ +class AST { +public: + std::shared_ptr root; + + AST() : root(nullptr) {} + explicit AST(std::shared_ptr r) : root(r) {} + + std::string toString() const { + return ast_node_to_string(root); + } +}; + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/ast_add_child.hpp b/dbal/cpp/src/query/ast_add_child.hpp new file mode 100644 index 000000000..3b9d6d4db --- /dev/null +++ b/dbal/cpp/src/query/ast_add_child.hpp @@ -0,0 +1,20 @@ +#pragma once +/** + * @file ast_add_child.hpp + * @brief Add child to AST node + */ + +#include "ast_node.hpp" + +namespace dbal::query { + +/** + * Add a child node to parent + * @param parent Parent node + * @param child Child node to add + */ +inline void ast_add_child(ASTNode& parent, std::shared_ptr child) { + parent.children.push_back(child); +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/ast_node.hpp b/dbal/cpp/src/query/ast_node.hpp new file mode 100644 index 000000000..2cbea60a7 --- /dev/null +++ b/dbal/cpp/src/query/ast_node.hpp @@ -0,0 +1,32 @@ +#pragma once +/** + * @file ast_node.hpp + * @brief AST node type and structure + */ + +#include +#include +#include + +namespace dbal::query { + +enum class NodeType { + Select, + Insert, + Update, + Delete, + Where, + Join, + OrderBy, + Limit +}; + +struct ASTNode { + NodeType type; + std::string value; + std::vector> children; + + ASTNode(NodeType t, const std::string& v = "") : type(t), value(v) {} +}; + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/ast_to_string.hpp b/dbal/cpp/src/query/ast_to_string.hpp new file mode 100644 index 000000000..582120570 --- /dev/null +++ b/dbal/cpp/src/query/ast_to_string.hpp @@ -0,0 +1,26 @@ +#pragma once +/** + * @file ast_to_string.hpp + * @brief Convert AST to string + */ + +#include "ast_node.hpp" + +namespace dbal::query { + +/** + * Convert AST node to string representation + * @param node Node to convert + * @return String representation + */ +inline std::string ast_node_to_string(const std::shared_ptr& node) { + if (!node) return ""; + + std::string result = node->value; + for (const auto& child : node->children) { + result += " " + ast_node_to_string(child); + } + return result; +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder.hpp b/dbal/cpp/src/query/builder.hpp new file mode 100644 index 000000000..0f736b8af --- /dev/null +++ b/dbal/cpp/src/query/builder.hpp @@ -0,0 +1,56 @@ +#pragma once +/** + * @file builder.hpp + * @brief QueryBuilder class (wrapper) + */ + +#include "builder_state.hpp" +#include "builder_select.hpp" +#include "builder_from.hpp" +#include "builder_where.hpp" +#include "builder_order_by.hpp" +#include "builder_limit.hpp" +#include "builder_build.hpp" + +namespace dbal::query { + +/** + * Query builder class + * Thin wrapper around builder functions + */ +class QueryBuilder { +public: + QueryBuilder& select(const std::vector& columns) { + builder_select(state_, columns); + return *this; + } + + QueryBuilder& from(const std::string& table) { + builder_from(state_, table); + return *this; + } + + QueryBuilder& where(const std::string& condition) { + builder_where(state_, condition); + return *this; + } + + QueryBuilder& orderBy(const std::string& column, const std::string& direction = "ASC") { + builder_order_by(state_, column, direction); + return *this; + } + + QueryBuilder& limit(int lim) { + builder_limit(state_, lim); + return *this; + } + + std::string build() const { + return builder_build(state_); + } + +private: + BuilderState state_; +}; + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder_build.hpp b/dbal/cpp/src/query/builder_build.hpp new file mode 100644 index 000000000..766b0480a --- /dev/null +++ b/dbal/cpp/src/query/builder_build.hpp @@ -0,0 +1,49 @@ +#pragma once +/** + * @file builder_build.hpp + * @brief Build query string from state + */ + +#include "builder_state.hpp" + +namespace dbal::query { + +/** + * Build SQL query string from state + * @param state Builder state + * @return SQL query string + */ +inline std::string builder_build(const BuilderState& state) { + std::string query = state.query_type + " "; + + if (!state.columns.empty()) { + for (size_t i = 0; i < state.columns.size(); ++i) { + query += state.columns[i]; + if (i < state.columns.size() - 1) query += ", "; + } + } else { + query += "*"; + } + + query += " FROM " + state.table; + + if (!state.conditions.empty()) { + query += " WHERE "; + for (size_t i = 0; i < state.conditions.size(); ++i) { + query += state.conditions[i]; + if (i < state.conditions.size() - 1) query += " AND "; + } + } + + if (!state.order_by.empty()) { + query += " ORDER BY " + state.order_by; + } + + if (state.limit > 0) { + query += " LIMIT " + std::to_string(state.limit); + } + + return query; +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder_from.hpp b/dbal/cpp/src/query/builder_from.hpp new file mode 100644 index 000000000..d8a4d40a5 --- /dev/null +++ b/dbal/cpp/src/query/builder_from.hpp @@ -0,0 +1,20 @@ +#pragma once +/** + * @file builder_from.hpp + * @brief Set FROM table + */ + +#include "builder_state.hpp" + +namespace dbal::query { + +/** + * Set table for query + * @param state Builder state + * @param table Table name + */ +inline void builder_from(BuilderState& state, const std::string& table) { + state.table = table; +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder_limit.hpp b/dbal/cpp/src/query/builder_limit.hpp new file mode 100644 index 000000000..a95783528 --- /dev/null +++ b/dbal/cpp/src/query/builder_limit.hpp @@ -0,0 +1,20 @@ +#pragma once +/** + * @file builder_limit.hpp + * @brief Set LIMIT clause + */ + +#include "builder_state.hpp" + +namespace dbal::query { + +/** + * Set LIMIT clause + * @param state Builder state + * @param limit Max rows + */ +inline void builder_limit(BuilderState& state, int limit) { + state.limit = limit; +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder_order_by.hpp b/dbal/cpp/src/query/builder_order_by.hpp new file mode 100644 index 000000000..3591c443b --- /dev/null +++ b/dbal/cpp/src/query/builder_order_by.hpp @@ -0,0 +1,21 @@ +#pragma once +/** + * @file builder_order_by.hpp + * @brief Set ORDER BY clause + */ + +#include "builder_state.hpp" + +namespace dbal::query { + +/** + * Set ORDER BY clause + * @param state Builder state + * @param column Column to order by + * @param direction ASC or DESC + */ +inline void builder_order_by(BuilderState& state, const std::string& column, const std::string& direction = "ASC") { + state.order_by = column + " " + direction; +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder_select.hpp b/dbal/cpp/src/query/builder_select.hpp new file mode 100644 index 000000000..a996be756 --- /dev/null +++ b/dbal/cpp/src/query/builder_select.hpp @@ -0,0 +1,21 @@ +#pragma once +/** + * @file builder_select.hpp + * @brief Set SELECT columns + */ + +#include "builder_state.hpp" + +namespace dbal::query { + +/** + * Set columns for SELECT query + * @param state Builder state + * @param columns Columns to select + */ +inline void builder_select(BuilderState& state, const std::vector& columns) { + state.query_type = "SELECT"; + state.columns = columns; +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder_state.hpp b/dbal/cpp/src/query/builder_state.hpp new file mode 100644 index 000000000..421237e35 --- /dev/null +++ b/dbal/cpp/src/query/builder_state.hpp @@ -0,0 +1,24 @@ +#pragma once +/** + * @file builder_state.hpp + * @brief Query builder state + */ + +#include +#include + +namespace dbal::query { + +/** + * Query builder state + */ +struct BuilderState { + std::string query_type; + std::vector columns; + std::string table; + std::vector conditions; + std::string order_by; + int limit = 0; +}; + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/builder_where.hpp b/dbal/cpp/src/query/builder_where.hpp new file mode 100644 index 000000000..0955cb8ee --- /dev/null +++ b/dbal/cpp/src/query/builder_where.hpp @@ -0,0 +1,20 @@ +#pragma once +/** + * @file builder_where.hpp + * @brief Add WHERE condition + */ + +#include "builder_state.hpp" + +namespace dbal::query { + +/** + * Add WHERE condition + * @param state Builder state + * @param condition Condition string + */ +inline void builder_where(BuilderState& state, const std::string& condition) { + state.conditions.push_back(condition); +} + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/normalize.hpp b/dbal/cpp/src/query/normalize.hpp new file mode 100644 index 000000000..b033c3422 --- /dev/null +++ b/dbal/cpp/src/query/normalize.hpp @@ -0,0 +1,22 @@ +#pragma once +/** + * @file query_normalize.hpp wrapper + * @brief QueryNormalizer class + */ + +#include "query_normalize.hpp" + +namespace dbal::query { + +/** + * Query normalizer class + * Thin wrapper around normalize functions + */ +class QueryNormalizer { +public: + static std::string normalize(const std::string& query) { + return query_normalize(query); + } +}; + +} // namespace dbal::query diff --git a/dbal/cpp/src/query/query_normalize.hpp b/dbal/cpp/src/query/query_normalize.hpp new file mode 100644 index 000000000..bcdf5a86b --- /dev/null +++ b/dbal/cpp/src/query/query_normalize.hpp @@ -0,0 +1,58 @@ +#pragma once +/** + * @file query_normalize.hpp + * @brief Query string normalization + */ + +#include +#include +#include + +namespace dbal::query { + +/** + * Remove extra whitespace from string + * @param str Input string + * @return String with single spaces + */ +inline std::string query_remove_extra_whitespace(const std::string& str) { + std::string result; + bool lastWasSpace = false; + + for (char c : str) { + if (std::isspace(static_cast(c))) { + if (!lastWasSpace) { + result += ' '; + lastWasSpace = true; + } + } else { + result += c; + lastWasSpace = false; + } + } + + size_t start = result.find_first_not_of(' '); + size_t end = result.find_last_not_of(' '); + + if (start != std::string::npos && end != std::string::npos) { + return result.substr(start, end - start + 1); + } + + return result; +} + +/** + * Normalize a query string (uppercase, trim whitespace) + * @param query Input query + * @return Normalized query + */ +inline std::string query_normalize(const std::string& query) { + std::string normalized = query; + + std::transform(normalized.begin(), normalized.end(), normalized.begin(), + [](unsigned char c) { return std::toupper(c); }); + + return query_remove_extra_whitespace(normalized); +} + +} // namespace dbal::query diff --git a/dbal/ts/src/core/entities/user/delete-user.ts b/dbal/ts/src/core/entities/user/delete-user.ts index 3f5308cde..f09fc0ba7 100644 --- a/dbal/ts/src/core/entities/user/delete-user.ts +++ b/dbal/ts/src/core/entities/user/delete-user.ts @@ -2,25 +2,27 @@ * @file delete-user.ts * @description Delete user operation */ -import type { Result } from '../types'; -import type { InMemoryStore } from '../store/in-memory-store'; +import type { Result } from '../../types' +import type { InMemoryStore } from '../../store/in-memory-store' +import { validateId } from '../../validation/validate-id' /** * Delete a user by ID */ -export async function deleteUser(store: InMemoryStore, id: string): Promise> { - if (!id) { - return { success: false, error: { code: 'VALIDATION_ERROR', message: 'ID required' } }; +export const deleteUser = async (store: InMemoryStore, id: string): Promise> => { + const idErrors = validateId(id) + if (idErrors.length > 0) { + return { success: false, error: { code: 'VALIDATION_ERROR', message: idErrors[0] } } } - const user = store.users.get(id); + const user = store.users.get(id) if (!user) { - return { success: false, error: { code: 'NOT_FOUND', message: `User not found: ${id}` } }; + return { success: false, error: { code: 'NOT_FOUND', message: `User not found: ${id}` } } } - store.userEmails.delete(user.email); - store.userUsernames.delete(user.username); - store.users.delete(id); + store.users.delete(id) + store.usersByEmail.delete(user.email) + store.usersByUsername.delete(user.username) - return { success: true, data: true }; + return { success: true, data: true } } diff --git a/dbal/ts/src/core/entities/user/update-user.ts b/dbal/ts/src/core/entities/user/update-user.ts index 3d25b3c7a..ea85af7d0 100644 --- a/dbal/ts/src/core/entities/user/update-user.ts +++ b/dbal/ts/src/core/entities/user/update-user.ts @@ -2,57 +2,57 @@ * @file update-user.ts * @description Update user operation */ -import type { User, UpdateUserInput, Result } from '../types'; -import type { InMemoryStore } from '../store/in-memory-store'; -import { validateEmail, validateUsername } from '../validation/user-validation'; +import type { Result, UpdateUserInput, User } from '../../types' +import type { InMemoryStore } from '../../store/in-memory-store' +import { validateId } from '../../validation/validate-id' +import { validateUserUpdate } from '../../validation/validate-user-update' /** * Update an existing user */ -export async function updateUser( +export const updateUser = async ( store: InMemoryStore, id: string, input: UpdateUserInput -): Promise> { - if (!id) { - return { success: false, error: { code: 'VALIDATION_ERROR', message: 'ID required' } }; +): Promise> => { + const idErrors = validateId(id) + if (idErrors.length > 0) { + return { success: false, error: { code: 'VALIDATION_ERROR', message: idErrors[0] } } } - const user = store.users.get(id); + const user = store.users.get(id) if (!user) { - return { success: false, error: { code: 'NOT_FOUND', message: `User not found: ${id}` } }; + return { success: false, error: { code: 'NOT_FOUND', message: `User not found: ${id}` } } } - if (input.email !== undefined) { - if (!validateEmail(input.email)) { - return { success: false, error: { code: 'VALIDATION_ERROR', message: 'Invalid email' } }; - } - const existingId = store.userEmails.get(input.email); - if (existingId && existingId !== id) { - return { success: false, error: { code: 'CONFLICT', message: 'Email exists' } }; - } - store.userEmails.delete(user.email); - store.userEmails.set(input.email, id); - user.email = input.email; + const validationErrors = validateUserUpdate(input) + if (validationErrors.length > 0) { + return { success: false, error: { code: 'VALIDATION_ERROR', message: validationErrors[0] } } } - if (input.username !== undefined) { - if (!validateUsername(input.username)) { - return { success: false, error: { code: 'VALIDATION_ERROR', message: 'Invalid username' } }; + if (input.username && input.username !== user.username) { + if (store.usersByUsername.has(input.username)) { + return { success: false, error: { code: 'CONFLICT', message: 'Username already exists' } } } - const existingId = store.userUsernames.get(input.username); - if (existingId && existingId !== id) { - return { success: false, error: { code: 'CONFLICT', message: 'Username exists' } }; - } - store.userUsernames.delete(user.username); - store.userUsernames.set(input.username, id); - user.username = input.username; + store.usersByUsername.delete(user.username) + store.usersByUsername.set(input.username, id) + user.username = input.username } - if (input.name !== undefined) user.name = input.name; - if (input.level !== undefined) user.level = input.level; - if (input.isActive !== undefined) user.isActive = input.isActive; + if (input.email && input.email !== user.email) { + if (store.usersByEmail.has(input.email)) { + return { success: false, error: { code: 'CONFLICT', message: 'Email already exists' } } + } + store.usersByEmail.delete(user.email) + store.usersByEmail.set(input.email, id) + user.email = input.email + } - user.updatedAt = new Date(); - return { success: true, data: user }; + if (input.role) { + user.role = input.role + } + + user.updatedAt = new Date() + + return { success: true, data: user } }