mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 14:25:02 +00:00
code: dbal,hpp,cpp (16 files)
This commit is contained in:
28
dbal/cpp/src/query/ast.hpp
Normal file
28
dbal/cpp/src/query/ast.hpp
Normal file
@@ -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<ASTNode> root;
|
||||
|
||||
AST() : root(nullptr) {}
|
||||
explicit AST(std::shared_ptr<ASTNode> r) : root(r) {}
|
||||
|
||||
std::string toString() const {
|
||||
return ast_node_to_string(root);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dbal::query
|
||||
20
dbal/cpp/src/query/ast_add_child.hpp
Normal file
20
dbal/cpp/src/query/ast_add_child.hpp
Normal file
@@ -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<ASTNode> child) {
|
||||
parent.children.push_back(child);
|
||||
}
|
||||
|
||||
} // namespace dbal::query
|
||||
32
dbal/cpp/src/query/ast_node.hpp
Normal file
32
dbal/cpp/src/query/ast_node.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file ast_node.hpp
|
||||
* @brief AST node type and structure
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace dbal::query {
|
||||
|
||||
enum class NodeType {
|
||||
Select,
|
||||
Insert,
|
||||
Update,
|
||||
Delete,
|
||||
Where,
|
||||
Join,
|
||||
OrderBy,
|
||||
Limit
|
||||
};
|
||||
|
||||
struct ASTNode {
|
||||
NodeType type;
|
||||
std::string value;
|
||||
std::vector<std::shared_ptr<ASTNode>> children;
|
||||
|
||||
ASTNode(NodeType t, const std::string& v = "") : type(t), value(v) {}
|
||||
};
|
||||
|
||||
} // namespace dbal::query
|
||||
26
dbal/cpp/src/query/ast_to_string.hpp
Normal file
26
dbal/cpp/src/query/ast_to_string.hpp
Normal file
@@ -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<ASTNode>& 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
|
||||
56
dbal/cpp/src/query/builder.hpp
Normal file
56
dbal/cpp/src/query/builder.hpp
Normal file
@@ -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<std::string>& 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
|
||||
49
dbal/cpp/src/query/builder_build.hpp
Normal file
49
dbal/cpp/src/query/builder_build.hpp
Normal file
@@ -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
|
||||
20
dbal/cpp/src/query/builder_from.hpp
Normal file
20
dbal/cpp/src/query/builder_from.hpp
Normal file
@@ -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
|
||||
20
dbal/cpp/src/query/builder_limit.hpp
Normal file
20
dbal/cpp/src/query/builder_limit.hpp
Normal file
@@ -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
|
||||
21
dbal/cpp/src/query/builder_order_by.hpp
Normal file
21
dbal/cpp/src/query/builder_order_by.hpp
Normal file
@@ -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
|
||||
21
dbal/cpp/src/query/builder_select.hpp
Normal file
21
dbal/cpp/src/query/builder_select.hpp
Normal file
@@ -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<std::string>& columns) {
|
||||
state.query_type = "SELECT";
|
||||
state.columns = columns;
|
||||
}
|
||||
|
||||
} // namespace dbal::query
|
||||
24
dbal/cpp/src/query/builder_state.hpp
Normal file
24
dbal/cpp/src/query/builder_state.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file builder_state.hpp
|
||||
* @brief Query builder state
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dbal::query {
|
||||
|
||||
/**
|
||||
* Query builder state
|
||||
*/
|
||||
struct BuilderState {
|
||||
std::string query_type;
|
||||
std::vector<std::string> columns;
|
||||
std::string table;
|
||||
std::vector<std::string> conditions;
|
||||
std::string order_by;
|
||||
int limit = 0;
|
||||
};
|
||||
|
||||
} // namespace dbal::query
|
||||
20
dbal/cpp/src/query/builder_where.hpp
Normal file
20
dbal/cpp/src/query/builder_where.hpp
Normal file
@@ -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
|
||||
22
dbal/cpp/src/query/normalize.hpp
Normal file
22
dbal/cpp/src/query/normalize.hpp
Normal file
@@ -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
|
||||
58
dbal/cpp/src/query/query_normalize.hpp
Normal file
58
dbal/cpp/src/query/query_normalize.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file query_normalize.hpp
|
||||
* @brief Query string normalization
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
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<unsigned char>(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
|
||||
@@ -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<Result<boolean>> {
|
||||
if (!id) {
|
||||
return { success: false, error: { code: 'VALIDATION_ERROR', message: 'ID required' } };
|
||||
export const deleteUser = async (store: InMemoryStore, id: string): Promise<Result<boolean>> => {
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -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<Result<User>> {
|
||||
if (!id) {
|
||||
return { success: false, error: { code: 'VALIDATION_ERROR', message: 'ID required' } };
|
||||
): Promise<Result<User>> => {
|
||||
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 }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user