Files
michaelschiemer/src/Framework/Database/Exception/QuerySyntaxException.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

257 lines
8.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\Database\Exception;
use App\Framework\Database\ValueObjects\SqlState;
use App\Framework\Exception\Core\DatabaseErrorCode;
use App\Framework\Exception\ExceptionContext;
/**
* Query Syntax Exception
*
* Thrown when a database query contains syntax errors or references
* invalid database objects (tables, columns, functions).
*
* This exception represents SQLSTATE class 42 (Syntax Error or Access Violation) errors:
* - 42000: Syntax error or access violation
* - 42501: Insufficient privilege
* - 42601: Syntax error
* - 42P01: Undefined table
* - 42703: Undefined column
* - 42883: Undefined function
* - 42S02: Table not found (MySQL)
* - 42S22: Column not found (MySQL)
*/
final class QuerySyntaxException extends DatabaseException
{
/**
* Create exception for general query syntax error
*
* @param string $query The SQL query that failed
* @param SqlState $sqlState The SQLSTATE error code
* @param string|null $errorMessage Optional database error message
* @param \Throwable|null $previous Previous exception
*/
public static function forQuery(
string $query,
SqlState $sqlState,
?string $errorMessage = null,
?\Throwable $previous = null
): self {
$message = "SQL syntax error: {$errorMessage}";
$context = ExceptionContext::forOperation('query.parse', 'Database')
->withData([
'query' => self::truncateQuery($query),
'sqlstate' => $sqlState->code,
'sqlstate_class' => $sqlState->getClass(),
'error_category' => 'Syntax Error or Access Violation',
])
->withDebug([
'full_query' => $query,
'sqlstate_subclass' => $sqlState->getSubclass(),
]);
return self::fromContext($message, $context, DatabaseErrorCode::QUERY_SYNTAX_ERROR, $previous, 500, $sqlState);
}
/**
* Create exception for undefined table
*
* @param string $tableName The table name that was not found
* @param SqlState $sqlState The SQLSTATE error code (typically 42P01 or 42S02)
* @param string|null $query Optional SQL query
*/
public static function tableNotFound(
string $tableName,
SqlState $sqlState,
?string $query = null
): self {
$message = "Table '{$tableName}' does not exist";
$context = ExceptionContext::forOperation('query.validate', 'Database')
->withData([
'table_name' => $tableName,
'sqlstate' => $sqlState->code,
'error_category' => 'Syntax Error or Access Violation',
]);
if ($query !== null) {
$context = $context->withDebug(['query' => self::truncateQuery($query)]);
}
return self::fromContext($message, $context, DatabaseErrorCode::QUERY_SYNTAX_ERROR, null, 500, $sqlState);
}
/**
* Create exception for undefined column
*
* @param string $columnName The column name that was not found
* @param string|null $tableName Optional table name
* @param SqlState $sqlState The SQLSTATE error code (typically 42703 or 42S22)
* @param string|null $query Optional SQL query
*/
public static function columnNotFound(
string $columnName,
?string $tableName,
SqlState $sqlState,
?string $query = null
): self {
$message = $tableName !== null
? "Column '{$tableName}.{$columnName}' does not exist"
: "Column '{$columnName}' does not exist";
$context = ExceptionContext::forOperation('query.validate', 'Database')
->withData([
'column_name' => $columnName,
'sqlstate' => $sqlState->code,
'error_category' => 'Syntax Error or Access Violation',
]);
if ($tableName !== null) {
$context = $context->withData(['table_name' => $tableName]);
}
if ($query !== null) {
$context = $context->withDebug(['query' => self::truncateQuery($query)]);
}
return self::fromContext($message, $context, DatabaseErrorCode::QUERY_SYNTAX_ERROR, null, 500, $sqlState);
}
/**
* Create exception for undefined function
*
* @param string $functionName The function name that was not found
* @param SqlState $sqlState The SQLSTATE error code (typically 42883)
* @param string|null $query Optional SQL query
*/
public static function functionNotFound(
string $functionName,
SqlState $sqlState,
?string $query = null
): self {
$message = "Function '{$functionName}' does not exist";
$context = ExceptionContext::forOperation('query.validate', 'Database')
->withData([
'function_name' => $functionName,
'sqlstate' => $sqlState->code,
'error_category' => 'Syntax Error or Access Violation',
]);
if ($query !== null) {
$context = $context->withDebug(['query' => self::truncateQuery($query)]);
}
return self::fromContext($message, $context, DatabaseErrorCode::QUERY_SYNTAX_ERROR, null, 500, $sqlState);
}
/**
* Create exception for insufficient privileges
*
* @param string $operation The operation that was attempted (e.g., SELECT, INSERT, DROP)
* @param string|null $objectName Optional database object name
* @param SqlState $sqlState The SQLSTATE error code (typically 42501)
*/
public static function insufficientPrivileges(
string $operation,
?string $objectName,
SqlState $sqlState
): self {
$message = $objectName !== null
? "Insufficient privileges to {$operation} on '{$objectName}'"
: "Insufficient privileges to perform {$operation}";
$context = ExceptionContext::forOperation('query.authorize', 'Database')
->withData([
'operation' => $operation,
'sqlstate' => $sqlState->code,
'error_category' => 'Syntax Error or Access Violation',
]);
if ($objectName !== null) {
$context = $context->withData(['object_name' => $objectName]);
}
return self::fromContext($message, $context, DatabaseErrorCode::QUERY_SYNTAX_ERROR, null, 500, $sqlState);
}
/**
* Create exception for ambiguous column reference
*
* @param string $columnName The ambiguous column name
* @param SqlState $sqlState The SQLSTATE error code
* @param string|null $query Optional SQL query
*/
public static function ambiguousColumn(
string $columnName,
SqlState $sqlState,
?string $query = null
): self {
$message = "Ambiguous column reference '{$columnName}' - specify table name";
$context = ExceptionContext::forOperation('query.validate', 'Database')
->withData([
'column_name' => $columnName,
'sqlstate' => $sqlState->code,
'error_category' => 'Syntax Error or Access Violation',
]);
if ($query !== null) {
$context = $context->withDebug(['query' => self::truncateQuery($query)]);
}
return self::fromContext($message, $context, DatabaseErrorCode::QUERY_SYNTAX_ERROR, null, 500, $sqlState);
}
/**
* Create exception for invalid SQL syntax at specific position
*
* @param string $query The SQL query
* @param int $position Error position in query
* @param string $nearText Text near the error
* @param SqlState $sqlState The SQLSTATE error code
*/
public static function syntaxErrorAt(
string $query,
int $position,
string $nearText,
SqlState $sqlState
): self {
$message = "Syntax error at position {$position} near '{$nearText}'";
$context = ExceptionContext::forOperation('query.parse', 'Database')
->withData([
'error_position' => $position,
'near_text' => $nearText,
'query' => self::truncateQuery($query),
'sqlstate' => $sqlState->code,
'error_category' => 'Syntax Error or Access Violation',
])
->withDebug([
'full_query' => $query,
]);
return self::fromContext($message, $context, DatabaseErrorCode::QUERY_SYNTAX_ERROR, null, 500, $sqlState);
}
/**
* Truncate query for safe logging
*
* @param string $query The SQL query
* @param int $maxLength Maximum length
* @return string Truncated query
*/
private static function truncateQuery(string $query, int $maxLength = 200): string
{
if (strlen($query) <= $maxLength) {
return $query;
}
return substr($query, 0, $maxLength) . '... (truncated)';
}
}