Cookbook

Database Advanced Features

This comprehensive guide covers Glueful's advanced database capabilities, including connection pooling, query optimization, performance monitoring, and enterprise-grade features for high-performance applications.

Overview

Glueful's database system provides enterprise-grade features designed for high-performance applications with advanced capabilities including:

Key Features

  • Connection Pooling: Multi-engine connection pool management with health monitoring and async maintenance
  • Modular Query Builder: Orchestrator pattern with 34+ specialized components replacing monolithic design
  • Advanced Monitoring: N+1 detection, pattern recognition, execution plan analysis, and profiling
  • Multi-Database Support: MySQL, PostgreSQL, and SQLite with driver-specific optimizations and SSL support
  • Transaction Management: Nested transactions with savepoints and automatic deadlock retry logic
  • Query Optimization: Automatic analysis, pattern recognition, and database-specific optimizations
  • Migration System: Complete schema management with batch tracking and checksum verification
  • Production Features: Query caching, soft deletes, pagination, connection leak detection

Architecture Components

  1. ConnectionPoolManager: Multi-engine pool orchestration with global statistics and performance scoring
  2. QueryBuilder: Lightweight orchestrator coordinating modular components via dependency injection
  3. DevelopmentQueryMonitor: Real-time N+1 detection and slow query analysis
  4. QueryProfilerService: Sampling-based profiling with configurable thresholds
  5. ExecutionPlanAnalyzer: EXPLAIN plan analysis with optimization recommendations
  6. MigrationManager: Full migration lifecycle with transaction safety and rollback support
  7. TransactionManager: Enterprise-grade transaction handling with savepoint management
  8. Schema Builders: Complete schema building system with database-agnostic SQL generation

Connection Pooling

Overview

Glueful's connection pooling system provides efficient connection management with automatic lifecycle handling, health monitoring, and statistics tracking.

Basic Usage

use Glueful\Database\Connection;

// Recommended: use the framework connection (pooling is automatic if enabled in config)
$db = container($context)->get('database'); // or: $db = new Connection();

// Fluent query via QueryBuilder
$results = $db
    ->table('users')
    ->select(['*'])
    ->where(['active' => 1])
    ->get();

// Advanced: access the underlying pool (rarely needed)
// Note: the pool manager is initialized after a Connection is created when pooling is enabled
$poolManager = Glueful\Database\Connection::getPoolManager();
if ($poolManager) {
    $mysqlPool = $poolManager->getPool('mysql');
    $pooled = $mysqlPool->acquire();
    try {
        // Low-level access, e.g., health checks via $pooled->query('SELECT 1')
    } finally {
        $mysqlPool->release($pooled);
    }
}

Pool Configuration

// config/database.php
return [
    'pooling' => [
        'enabled' => env('DB_POOLING_ENABLED', true),
        'defaults' => [
            'min_connections' => env('DB_POOL_MIN_CONNECTIONS', 2),
            'max_connections' => env('DB_POOL_MAX_CONNECTIONS', 10),
            'idle_timeout' => env('DB_POOL_IDLE_TIMEOUT', 300),
            'max_lifetime' => env('DB_POOL_MAX_LIFETIME', 3600),
            'acquisition_timeout' => env('DB_POOL_ACQUIRE_TIMEOUT', 30),
            'health_check_interval' => env('DB_POOL_HEALTH_CHECK_INTERVAL', 60),
            'health_check_timeout' => env('DB_POOL_HEALTH_CHECK_TIMEOUT', 5),
            'max_use_count' => env('DB_POOL_MAX_USE_COUNT', 1000),
            'retry_attempts' => env('DB_POOL_RETRY_ATTEMPTS', 3),
            'retry_delay' => env('DB_POOL_RETRY_DELAY', 100),
        ],
        'engines' => [
            'mysql' => [ 'max_connections' => 20, 'min_connections' => 5 ],
            'pgsql' => [ 'max_connections' => 15, 'min_connections' => 3 ],
            'sqlite' => [ 'max_connections' => 1, 'min_connections' => 1 ],
        ]
    ]
];

Pool Statistics and Monitoring

// Get statistics for all pools
$stats = $poolManager->getStats();
/*
[
    'mysql' => [
        'active_connections' => 12,
        'idle_connections' => 3,
        'total_connections' => 15,
        'total_created' => 45,
        'total_destroyed' => 30,
        'total_acquisitions' => 1250,
        'total_releases' => 1238,
        'total_timeouts' => 2,
        'total_health_checks' => 120,
        'failed_health_checks' => 0
    ]
]
*/

// Get aggregate statistics
$aggregate = $poolManager->getAggregateStats();

// Get health status
$health = $poolManager->getHealthStatus();
/*
[
    'mysql' => [
        'healthy' => true,
        'active_connections' => 12,
        'health_check_failure_rate' => 0.0,
        'timeout_rate' => 0.16
    ]
]
*/

Pooled Connection Features

use Glueful\Database\PooledConnection;

// Acquire pooled connection after a Connection has initialized pooling
$pool = Glueful\Database\Connection::getPoolManager()->getPool('mysql');
$connection = $pool->acquire();

// Get connection statistics
$stats = $connection->getStats();
/*
[
    'id' => 'conn_abc123',
    'age' => 125.45,          // seconds since creation
    'idle_time' => 5.23,      // seconds since last use
    'use_count' => 47,        // number of times used
    'in_transaction' => false,
    'is_healthy' => true,
    'created_at' => 1712345678.12,
    'last_used_at' => 1712345800.34
]
*/

// Check connection state
$isHealthy = $connection->isHealthy();
$inTransaction = $connection->isInTransaction();
$age = $connection->getAge();
$idleTime = $connection->getIdleTime();

// Maintenance worker
// The pool automatically starts an async maintenance worker in CLI contexts
// and runs on-demand in web requests (no manual start required).

Query Builder Architecture (Modular Design)

Orchestrator Pattern Implementation

The QueryBuilder uses a modern orchestrator pattern, replacing the monolithic 2,184-line version with a lightweight coordinator that delegates to specialized components:

use Glueful\Database\QueryBuilder;

// The QueryBuilder is constructed with 15 specialized components:
// - QueryStateInterface: Manages query state and metadata
// - WhereClauseInterface: Handles WHERE clause construction
// - SelectBuilderInterface: Builds SELECT queries
// - InsertBuilderInterface: Builds INSERT queries
// - UpdateBuilderInterface: Builds UPDATE queries
// - DeleteBuilderInterface: Builds DELETE queries
// - JoinClauseInterface: Handles table joins
// - QueryModifiersInterface: Manages ORDER BY, GROUP BY, HAVING, LIMIT
// - TransactionManagerInterface: Handles transactions and savepoints
// - QueryExecutorInterface: Executes queries
// - ResultProcessorInterface: Processes results
// - PaginationBuilderInterface: Handles pagination
// - SoftDeleteHandlerInterface: Manages soft deletes
// - QueryValidatorInterface: Validates SQL safety
// - QueryPurposeInterface: Tracks business purpose for logging/analysis

// All components are injected via DI container
$queryBuilder = container($context)->get(QueryBuilder::class);

Query Builder Advanced Features

Complex Query Construction

// Multi-table joins with complex conditions
$results = $db
    ->table('users')
    ->select(['users.*', 'profiles.bio', 'roles.name AS role_name'])
    ->leftJoin('profiles', 'profiles.user_id', '=', 'users.id')
    ->join('user_roles', 'user_roles.user_id', '=', 'users.id')
    ->join('roles', 'roles.id', '=', 'user_roles.role_id')
    ->where(['users.active' => 1])
    ->whereIn('roles.name', ['admin', 'moderator'])
    ->where('users.created_at', '>', '2024-01-01')
    ->orWhere(function ($q) {
        $q->whereNull('users.deleted_at');
        $q->orWhere('users.deleted_at', '>', date('Y-m-d H:i:s'));
    })
    ->orderBy(['users.last_login' => 'DESC', 'users.created_at' => 'ASC'])
    ->limit(50)
    ->get();
// Multi-column text search (build OR group with LIKE)
$users = $db
    ->table('users')
    ->select(['*'])
    ->where(function ($q) {
        $q->whereLike('username', '%john smith%');
        $q->orWhere('email', 'LIKE', '%john smith%');
        $q->orWhere('first_name', 'LIKE', '%john smith%');
        $q->orWhere('last_name', 'LIKE', '%john smith%');
    })
    ->orderBy(['username' => 'ASC'])
    ->get();

// Advanced filtering with multiple operators
$orders = $db
    ->table('orders')
    ->select(['*'])
    ->where(function ($q) {
        $q->whereIn('status', ['pending', 'processing']);
        $q->whereBetween('total', 100, 1000);
        $q->orWhere('customer_email', 'LIKE', '%@company.com');
    })
    ->where('created_at', '>=', '2024-01-01')
    ->get();

// JSON column searching (database-agnostic)
$logs = $db
    ->table('logs')
    ->select(['*'])
    ->whereJsonContains('metadata', 'login_failed')
    ->whereJsonContains('details', 'active', '$.status')  // MySQL path syntax
    ->get();

Query Building with Optimization

use Glueful\Database\Connection;
use Glueful\Database\QueryOptimizer;

$db = container($context)->get(Connection::class);

// Enable query optimization and caching
$optimizedResults = $db->table('orders')
    ->select(['orders.*', 'customers.name', 'products.title'])
    ->join('customers', 'customers.id', '=', 'orders.customer_id')
    ->join('order_items', 'order_items.order_id', '=', 'orders.id')
    ->join('products', 'products.id', '=', 'order_items.product_id')
    ->where(['orders.status' => 'completed'])
    ->orderBy(['orders.created_at' => 'DESC'])
    ->optimize()           // Enable query optimization
    ->cache(3600)         // Cache results for 1 hour
    ->withPurpose('Order history with customer and product details')
    ->get();

// Manual optimization with QueryOptimizer
$optimizer = new QueryOptimizer();
$optimizer->setConnection($db);
$optimized = $optimizer->optimizeQuery(
    "SELECT * FROM orders WHERE status = ? ORDER BY created_at DESC",
    ['completed']
);

Raw Expressions and Complex Queries

// Using raw expressions for complex calculations
$q = $db->table('orders');
$salesReport = $q
    ->select([
        'DATE(created_at) as date',
        $q->raw('COUNT(*) as order_count'),
        $q->raw('SUM(total) as daily_revenue'),
        $q->raw('AVG(total) as avg_order_value'),
        $q->raw('MAX(total) as highest_order')
    ])
    ->where(['status' => 'completed'])
    ->whereBetween('created_at', $startDate, $endDate)
    ->groupBy(['DATE(created_at)'])
    ->having(['order_count' => 5])  // At least 5 orders per day
    ->havingRaw('SUM(total) > ?', [1000])  // Daily revenue > $1000
    ->orderBy(['date' => 'DESC'])
    ->get();

Pagination with Optimization

// Optimized pagination with count query optimization
$page = $request->get('page', 1);
$perPage = $request->get('per_page', 20);

$paginatedResults = $db
    ->table('products')
    ->select(['*'])
    ->join('categories', 'categories.id', '=', 'products.category_id')
    ->where(['products.active' => 1])
    ->where(function ($q) use ($searchTerm) {
        $q->where('products.name', 'LIKE', "%{$searchTerm}%");
        $q->orWhere('products.description', 'LIKE', "%{$searchTerm}%");
    })
    ->orderBy(['products.featured' => 'DESC', 'products.created_at' => 'DESC'])
    ->paginate($page, $perPage);

/*
Returns:
[
    'data' => [...],
    'current_page' => 1,
    'per_page' => 20,
    'total' => 1250,
    'last_page' => 63,
    'has_more' => true,
    'from' => 1,
    'to' => 20,
    'execution_time_ms' => 45.67
]
*/

Query Optimization

Automatic Query Analysis and Optimization

use Glueful\Database\QueryOptimizer;

$optimizer = new QueryOptimizer();
$optimizer->setConnection($db);

// Analyze and optimize a complex query
$result = $optimizer->optimizeQuery(
    "SELECT u.*, p.bio FROM users u 
     LEFT JOIN profiles p ON p.user_id = u.id 
     WHERE u.status = ? AND u.created_at > ? 
     ORDER BY u.last_login DESC",
    ['active', '2024-01-01']
);

/*
Returns:
[
    'original_query' => '...',
    'optimized_query' => '...',
    'suggestions' => [
        [
            'type' => 'missing_index',
            'description' => 'Query may benefit from an index',
            'solution' => 'Add an index to the referenced column',
            'impact' => 'high'
        ],
        [
            'type' => 'inefficient_join',
            'description' => 'Join order could be optimized',
            'solution' => 'Reorder joins to start with most restrictive conditions',
            'impact' => 'medium'
        ]
    ],
    'estimated_improvement' => [
        'execution_time' => 25,    // 25% improvement
        'resource_usage' => 30,    // 30% less resources
        'confidence' => 'high'
    ]
]
*/

Database-Specific Optimizations

// MySQL-specific optimizations
$mysqlOptimizer = new QueryOptimizer();
$mysqlOptimizer->setConnection($mysqlConnection);

// Optimization may include:
// - STRAIGHT_JOIN hints for complex joins
// - Index usage optimization
// - WITH ROLLUP for aggregations
$optimized = $mysqlOptimizer->optimizeQuery($complexQuery, $params);

// PostgreSQL-specific optimizations
$pgsqlOptimizer = new QueryOptimizer();
$pgsqlOptimizer->setConnection($pgsqlConnection);

// May include specialized PostgreSQL optimizations
$optimized = $pgsqlOptimizer->optimizeQuery($complexQuery, $params);

Manual Optimization Triggers

// Enable optimizer on a specific query
$db
    ->table('orders')
    ->select(['*'])
    ->join('customers', 'customers.id', '=', 'orders.customer_id')
    ->where(['status' => 'pending'])
    ->optimize()
    ->get();

Performance Monitoring

Query Performance Analysis

use Glueful\Database\QueryLogger;

$logger = new QueryLogger($frameworkLogger);

// Configure performance monitoring
$logger->configure(
    enableDebug: true,
    enableTiming: true,
    maxLogSize: 500
);

// Configure N+1 detection
$logger->configureN1Detection(
    threshold: 5,      // 5 similar queries triggers detection
    timeWindow: 5      // within 5 seconds
);

// Get comprehensive statistics
$stats = $logger->getStatistics();
/*
[
    'total' => 1250,
    'select' => 980,
    'insert' => 125,
    'update' => 95,
    'delete' => 35,
    'other' => 15,
    'error' => 3,
    'total_time' => 15670.25  // milliseconds
]
*/

// Get average execution time
$avgTime = $queryLogger->getAverageExecutionTime(); // e.g., 12.54 ms

// Format execution time for display
$formattedTime = $queryLogger->formatExecutionTime(1250.5); // "1.25 s"

N+1 Query Detection

// Automatic N+1 detection with recommendations
// The logger will automatically detect patterns like:

// BAD: N+1 pattern
foreach ($users as $user) {
    $profile = $db->table('profiles')
        ->select(['*'])
        ->where(['user_id' => $user->id])
        ->first();
}

// Logger will detect this pattern and recommend:
// "Consider using eager loading or preloading related data in a single query
//  instead of multiple individual lookups"

// GOOD: Optimized approach
$userIds = array_column($users, 'id');
$profiles = $db->table('profiles')
    ->select(['*'])
    ->whereIn('user_id', $userIds)
    ->get();

// Map profiles to users
$profilesByUserId = [];
foreach ($profiles as $profile) {
    $profilesByUserId[$profile['user_id']] = $profile;
}

Query Complexity Analysis

use Glueful\Database\QueryAnalyzer;
use Glueful\Database\Tools\QueryPatternRecognizer;

// Queries are automatically analyzed for complexity
$analyzer = new QueryAnalyzer();
$patternRecognizer = new QueryPatternRecognizer();

$complexQuery = "
    SELECT
        u.username,
        COUNT(o.id) as order_count,
        SUM(o.total) as total_spent,
        AVG(o.total) as avg_order,
        ROW_NUMBER() OVER (PARTITION BY u.department ORDER BY SUM(o.total) DESC) as rank
    FROM users u
    LEFT JOIN orders o ON o.user_id = u.id
    LEFT JOIN order_items oi ON oi.order_id = o.id
    LEFT JOIN products p ON p.id = oi.product_id
    WHERE u.active = 1
    AND o.created_at > DATE_SUB(NOW(), INTERVAL 1 YEAR)
    GROUP BY u.id, u.username, u.department
    HAVING COUNT(o.id) > 5
    ORDER BY total_spent DESC, u.username
";

// Complexity factors analyzed:
// - JOIN operations (+1 each)
// - Subqueries (+2 each)
// - Aggregation functions (+1)
// - Window functions (+2)
// - GROUP BY/HAVING (+1 each)
// - UNION/INTERSECT/EXCEPT (+2 each)
// Results in complexity score for optimization prioritization

// Pattern recognition for optimization
$patterns = $patternRecognizer->analyze($complexQuery);
// Detects: N+1 patterns, missing indexes, inefficient joins, etc.

Development Monitoring and Profiling

DevelopmentQueryMonitor

use Glueful\Database\DevelopmentQueryMonitor;

// Enable advanced development monitoring with N+1 detection
Glueful\\Database\\DevelopmentQueryMonitor::enable();

// Configure via env (development only):
// - SLOW_QUERY_THRESHOLD (seconds)
// - N_PLUS_ONE_THRESHOLD (count)

// The monitor analyzes queries in real-time and provides:
// - N+1 query detection with stack traces
// - Slow query identification
// - Memory usage per query
// - Query pattern analysis
// - Performance recommendations

QueryProfilerService

use Glueful\Database\Tools\QueryProfilerService;

// Sampling-based profiling for production
// Configure via config($context, 'database.profiler'):
// - sampling_rate (0.0–1.0)
// - threshold (ms)
$profiler = new QueryProfilerService();

// Profile a specific execution
$result = $profiler->profile($query, $params, function () use ($pdo, $query, $params) {
    $stmt = $pdo->prepare($query);
    $stmt->execute($params);
    return $stmt->fetchAll();
});

// Get collected profiles
$profiles = $profiler->getProfiles();
/*
[
    'total_queries' => 10000,
    'sampled_queries' => 1000,
    'slow_queries' => 45,
    'average_time' => 12.5,
    'p95_time' => 89.3,
    'p99_time' => 234.7,
    'query_patterns' => [...],
    'recommendations' => [...]
]
*/

ExecutionPlanAnalyzer

use Glueful\Database\Tools\ExecutionPlanAnalyzer;

// Analyze query execution plans
$analyzer = new ExecutionPlanAnalyzer($connection);
$plan = $analyzer->getExecutionPlan($query, $params);
$analysis = $analyzer->analyzeExecutionPlan($plan);

/*
Returns:
[
    'execution_plan' => [...],
    'estimated_rows' => 1250,
    'estimated_cost' => 324.5,
    'index_usage' => [
        'users' => 'PRIMARY',
        'orders' => 'idx_user_date'
    ],
    'warnings' => [
        'Full table scan on orders table',
        'Using filesort for ORDER BY'
    ],
    'recommendations' => [
        'Consider adding index on orders.status',
        'Rewrite query to avoid filesort'
    ]
]
*/

Query Logging and Analytics

Business Context Logging

use Glueful\Database\Connection;

$db = container($context)->get(Connection::class);

// Add business context to queries for better debugging
$userProfile = $db->table('users')
    ->withPurpose('User profile page data loading')
    ->select(['users.*', 'profiles.bio', 'profiles.avatar_url'])
    ->join('profiles', 'profiles.user_id', '=', 'users.id')
    ->where(['users.id' => $userId])
    ->first();

// Query will be logged with business context
// Log entry will include: purpose, execution time, SQL, bindings

// Complex query with purpose tracking
$dashboardData = $db->table('users')
    ->withPurpose('Admin dashboard user statistics')
    ->select(['users.status', $db->table('users')->raw('COUNT(*) as count')])
    ->where('created_at', '>=', date('Y-m-d', strtotime('-30 days')))
    ->groupBy(['status'])
    ->get();

Query Log Analysis

// Get detailed query log from a QueryLogger instance
// (for example, one used with a custom QueryExecutor)
$queryLogger = new Glueful\\Database\\QueryLogger();
$queryLog = $queryLogger->getQueryLog();

foreach ($queryLog as $entry) {
    echo "Query: {$entry['sql']}\n";
    echo "Type: {$entry['type']}\n";
    echo "Tables: " . implode(', ', $entry['tables']) . "\n";
    echo "Complexity: {$entry['complexity']}\n";
    echo "Execution time: {$entry['time']}\n";
    echo "Purpose: {$entry['purpose']}\n";
    if ($entry['error']) {
        echo "Error: {$entry['error']}\n";
    }
    echo "---\n";
}

Event-Driven Logging

// Listen to query execution events for custom logging
use Glueful\Events\EventService;
use Glueful\Events\Database\QueryExecutedEvent;

app($context, EventService::class)->addListener(
    QueryExecutedEvent::class,
    function (QueryExecutedEvent $event) {
        // Custom application-specific query logging
        if ($event->isSlow(1.0)) { // > 1 second
            $this->alertingService->sendSlowQueryAlert([
                'sql' => $event->getSql(),
                'execution_time' => $event->getExecutionTime(),
                'connection' => $event->getConnectionName(),
                'metadata' => $event->getMetadata()
            ]);
        }

        // Log to business analytics
        $this->analyticsService->trackDatabaseQuery([
            'query_type' => $event->getQueryType(),
            'tables' => $event->getMetadata('tables') ?? [],
            'execution_time' => $event->getExecutionTime(),
            'purpose' => $event->getMetadata('purpose')
        ]);
    }
);

Database Driver System

SSL/TLS Support

// config/database.php - SSL configuration for secure connections
return [
    'mysql' => [
        'driver' => 'mysql',
        'host' => env('DB_HOST'),
        'ssl' => [
            'enabled' => env('DB_SSL_ENABLED', false),
            'ca' => env('DB_SSL_CA_PATH'),
            'cert' => env('DB_SSL_CERT_PATH'),
            'key' => env('DB_SSL_KEY_PATH'),
            'cipher' => env('DB_SSL_CIPHER'),
            'verify' => env('DB_SSL_VERIFY', true)
        ]
    ],
    'pgsql' => [
        'driver' => 'pgsql',
        'sslmode' => env('PGSQL_SSLMODE', 'prefer'), // disable|allow|prefer|require|verify-ca|verify-full
        'sslcert' => env('PGSQL_SSL_CERT'),
        'sslkey' => env('PGSQL_SSL_KEY'),
        'sslrootcert' => env('PGSQL_SSL_ROOT_CERT')
    ]
];

Database Drivers

Multi-Database Support

use Glueful\Database\Connection;
use Glueful\Database\Driver\MySQLDriver;
use Glueful\Database\Driver\PostgreSQLDriver;
use Glueful\Database\Driver\SQLiteDriver;

$db = container($context)->get(Connection::class);

// Database-agnostic query building
$driver = $db->getDriver(); // Returns database-specific driver instance

// Driver-specific identifier wrapping (for reserved words)
$wrappedTable = $driver->wrapIdentifier('users');     // `users` for MySQL
$wrappedColumn = $driver->wrapIdentifier('user_name'); // `user_name` for MySQL

// Driver-specific features and optimizations
if ($driver instanceof MySQLDriver) {
    // MySQL-specific features
    $sql = "INSERT INTO users (username, email) VALUES (?, ?)
            ON DUPLICATE KEY UPDATE last_login = NOW()";
    $db->executeRaw($sql, ['john', 'john@example.com']);

} elseif ($driver instanceof PostgreSQLDriver) {
    // PostgreSQL-specific features with RETURNING
    $sql = "INSERT INTO users (username, email) VALUES (?, ?)
            ON CONFLICT (username) DO UPDATE SET last_login = NOW()
            RETURNING id";
    $result = $db->executeRaw($sql, ['john', 'john@example.com']);
    $newId = $result[0]['id'] ?? null;

} elseif ($driver instanceof SQLiteDriver) {
    // SQLite-specific features
    $sql = "INSERT OR REPLACE INTO users (username, email, last_login)
            VALUES (?, ?, datetime('now'))";
    $db->executeRaw($sql, ['john', 'john@example.com']);
}

Driver Capabilities

// Check driver capabilities
$capabilities = $connection->getCapabilities();
/*
[
    'supports_json' => true,
    'supports_window_functions' => true,
    'supports_upsert' => true,
    'supports_returning' => true,  // PostgreSQL
    'supports_full_text_search' => true,
    'max_identifier_length' => 64
]
*/

Migration System

Complete Schema Management

use Glueful\Database\Migrations\MigrationManager;
use Glueful\Database\Schema\Builders\SchemaBuilder;

// Migration manager with batch tracking and checksum verification
$migrationManager = container($context)->get(MigrationManager::class);

// Create a new migration
$migrationManager->create('create_users_table');

// Migration class example
class CreateUsersTable implements MigrationInterface
{
    public function up(SchemaBuilder $schema): void
    {
        $schema->create('users')
            ->id()
            ->string('username', 50)->unique()
            ->string('email', 255)->unique()
            ->text('bio')->nullable()
            ->json('preferences')->nullable()
            ->timestamps()
            ->softDeletes()
            ->index(['email', 'username'])
            ->build();
    }

    public function down(SchemaBuilder $schema): void
    {
        $schema->dropIfExists('users');
    }
}

// Run migrations with transaction safety
$migrationManager->run(); // All migrations in transaction
$migrationManager->rollback(steps: 2); // Rollback last 2 batches
$migrationManager->status(); // Check migration status with checksums

Schema Builder Features

// Advanced schema building with database-agnostic SQL generation
$schema = new SchemaBuilder($connection);

// Alter existing table
$schema->alter('users')
    ->addColumn('avatar_url', 'string', 500)->nullable()->after('email')
    ->modifyColumn('bio', 'longtext')
    ->dropColumn('old_field')
    ->renameColumn('username', 'user_name')
    ->addIndex(['created_at', 'status'], 'idx_user_activity')
    ->addForeignKey('role_id', 'roles', 'id')
        ->onDelete('cascade')
        ->onUpdate('restrict')
    ->build();

// Database-specific SQL generation
$mysqlGenerator = new MySQLSqlGenerator();
$postgresGenerator = new PostgreSQLSqlGenerator();
$sqliteGenerator = new SQLiteSqlGenerator();

Advanced Query Patterns

Bulk Operations

// Bulk insert with batch processing
$users = [
    ['username' => 'user1', 'email' => 'user1@example.com'],
    ['username' => 'user2', 'email' => 'user2@example.com'],
    // ... 1000+ records
];

// Method 1: Using insertBatch for efficient bulk inserts
$batchSize = 100;
$totalInserted = 0;

foreach (array_chunk($users, $batchSize) as $batch) {
    $inserted = $db->table('users')->insertBatch($batch);
    $totalInserted += $inserted;
}

// Method 2: Using transactions for consistency with individual inserts
$totalInserted = $db->transaction(function () use ($db, $users) {
    $count = 0;
    foreach ($users as $user) {
        $db->table('users')->insert($user);
        $count++;
    }
    return $count;
});

// Bulk update with conditions
$affectedRows = $db->table('users')
    ->where(['active' => 1])
    ->where('created_at', '<', date('Y-m-d', strtotime('-30 days')))
    ->update(['last_seen' => date('Y-m-d H:i:s')]);

// Alternative: Raw SQL for better performance on very large updates
$db->executeRaw(
    "UPDATE users SET last_seen = ? WHERE active = 1 AND created_at < ?",
    [date('Y-m-d H:i:s'), date('Y-m-d', strtotime('-30 days'))]
);

Upsert Operations

use Glueful\Database\Connection;

$db = container($context)->get(Connection::class);

// Database-agnostic upsert using QueryBuilder
// Automatically generates the correct SQL for MySQL, PostgreSQL, or SQLite
$affected = $db->table('user_stats')
    ->upsert(
        [
            'user_id' => 1,
            'login_count' => 1,
            'last_login' => date('Y-m-d H:i:s')
        ],
        ['login_count', 'last_login'] // Columns to update on duplicate/conflict
    );

// Batch upsert with multiple records
$records = [
    ['user_id' => 1, 'login_count' => 1, 'last_login' => date('Y-m-d H:i:s')],
    ['user_id' => 2, 'login_count' => 1, 'last_login' => date('Y-m-d H:i:s')],
    ['user_id' => 3, 'login_count' => 1, 'last_login' => date('Y-m-d H:i:s')]
];

foreach ($records as $record) {
    $db->table('user_stats')->upsert(
        $record,
        ['login_count', 'last_login']
    );
}

// Under the hood, the framework generates appropriate SQL:
// - MySQL: INSERT ... ON DUPLICATE KEY UPDATE
// - PostgreSQL: INSERT ... ON CONFLICT DO UPDATE
// - SQLite: INSERT ... ON CONFLICT DO UPDATE

// For custom upsert logic, you can still use raw SQL
if ($db->getDriver() instanceof \Glueful\Database\Driver\MySQLDriver) {
    $db->executeRaw(
        "INSERT INTO user_stats (user_id, login_count, last_login)
         VALUES (?, ?, ?)
         ON DUPLICATE KEY UPDATE
         login_count = login_count + VALUES(login_count),
         last_login = VALUES(last_login)",
        [1, 1, date('Y-m-d H:i:s')]
    );
}

Soft Delete Management

use Glueful\Database\Connection;
use Glueful\Database\Features\SoftDeleteHandler;

$db = container($context)->get(Connection::class);

// Soft delete functionality is built into QueryBuilder
// By default, delete() performs soft delete if enabled

// Normal delete (soft delete if enabled)
$deleted = $db->table('users')
    ->where(['id' => $userId])
    ->delete(); // Sets deleted_at timestamp

// Include soft-deleted records in queries
$allUsers = $db->table('users')
    ->withTrashed() // Include soft-deleted records
    ->select(['*'])
    ->get();

// Only soft-deleted records
$trashedUsers = $db->table('users')
    ->onlyTrashed() // Only soft-deleted records
    ->select(['*'])
    ->get();

// Default behavior (exclude soft-deleted records)
$activeUsers = $db->table('users')
    ->select(['*'])
    ->get(); // Automatically excludes soft-deleted

// Restore soft-deleted records
$restored = $db->table('users')
    ->where(['id' => $userId])
    ->restore(); // Sets deleted_at to null

// Multiple restore with conditions
$restored = $db->table('users')
    ->where('deleted_at', '>', date('Y-m-d', strtotime('-30 days')))
    ->restore(); // Restore recently deleted users

// Force delete (permanent deletion, bypasses soft delete)
// Note: This uses DeleteBuilder.forceDelete() internally
$deleted = $db->table('users')
    ->where(['id' => $userId])
    ->delete(); // If you need force delete, use raw SQL for now

// Alternative: Direct force delete using raw SQL
$db->executeRaw('DELETE FROM users WHERE id = ?', [$userId]);

// Configure soft delete column (if different from 'deleted_at')
// This is typically configured at the application level
$softDeleteHandler = container($context)->get(SoftDeleteHandler::class);
$softDeleteHandler->setSoftDeleteColumn('archived_at');

Window Functions and Analytics

use Glueful\Database\Connection;
use Glueful\Database\RawExpression;

$db = container($context)->get(Connection::class);

// Complex analytics queries with window functions (MySQL 8.0+, PostgreSQL)
$salesAnalytics = $db->table('sales')
    ->select([
        'date',
        'amount',
        'region',
        $db->table('sales')->raw('SUM(amount) OVER (PARTITION BY region) as region_total'),
        $db->table('sales')->raw('RANK() OVER (PARTITION BY region ORDER BY amount DESC) as region_rank'),
        $db->table('sales')->raw('LAG(amount, 1) OVER (ORDER BY date) as previous_day_amount'),
        $db->table('sales')->raw('AVG(amount) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg_7_days')
    ])
    ->whereBetween('date', $startDate, $endDate)
    ->orderBy(['date' => 'ASC'])
    ->get();

// Alternative: Using selectRaw for cleaner syntax
$salesReport = $db->table('sales')
    ->select(['date', 'amount', 'region'])
    ->selectRaw('SUM(amount) OVER (PARTITION BY region) as region_total')
    ->selectRaw('RANK() OVER (PARTITION BY region ORDER BY amount DESC) as region_rank')
    ->selectRaw('LAG(amount, 1) OVER (ORDER BY date) as previous_day_amount')
    ->selectRaw('AVG(amount) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) as moving_avg_7_days')
    ->whereBetween('date', $startDate, $endDate)
    ->orderBy(['date' => 'ASC'])
    ->get();

// Running totals and cumulative calculations
$cumulativeRevenue = $db->table('daily_revenue')
    ->select(['date', 'revenue'])
    ->selectRaw('SUM(revenue) OVER (ORDER BY date) as cumulative_revenue')
    ->selectRaw('AVG(revenue) OVER (ORDER BY date ROWS BETWEEN 29 PRECEDING AND CURRENT ROW) as rolling_30_day_avg')
    ->where('date', '>=', $startDate)
    ->orderBy(['date' => 'ASC'])
    ->get();

// Percentile and distribution analysis
$performanceAnalysis = $db->table('employee_sales')
    ->select(['employee_id', 'department', 'total_sales'])
    ->selectRaw('PERCENT_RANK() OVER (ORDER BY total_sales) as sales_percentile')
    ->selectRaw('NTILE(4) OVER (ORDER BY total_sales) as quartile')
    ->selectRaw('DENSE_RANK() OVER (PARTITION BY department ORDER BY total_sales DESC) as dept_rank')
    ->where('year', '=', date('Y'))
    ->get();

Transaction Management

Nested Transactions with Savepoints

use Glueful\Database\Connection;
use Glueful\Database\Transaction\TransactionManager;

$db = container($context)->get(Connection::class);

// Automatic deadlock handling with retry
$result = $db->transaction(function () use ($db, $orderData, $inventoryUpdates) {
    // Create order (Glueful uses application-generated UUIDs, not auto-increment ids)
    $orderId = \Glueful\Helpers\Utils::generateNanoID();
    $db->table('orders')->insert(['uuid' => $orderId] + $orderData);

    // Create order items in a nested transaction (uses savepoints)
    $db->transaction(function () use ($db, $orderId, $orderData) {
        foreach ($orderData['items'] as $item) {
            $db->table('order_items')->insert([
                'order_id' => $orderId,
                'product_id' => $item['product_id'],
                'quantity' => $item['quantity'],
                'price' => $item['price']
            ]);
        }
    });

    // Update inventory with a raw expression (raw() takes a single SQL string)
    foreach ($inventoryUpdates as $update) {
        $qty = (int) $update['quantity'];
        $db->table('inventory')
            ->where(['product_id' => $update['product_id']])
            ->update([
                'quantity' => $db->table('inventory')->raw("quantity - {$qty}")
            ]);
    }

    return $orderId;
});

// Alternative: Using TransactionManager directly for more control
$transactionManager = container($context)->get(TransactionManager::class);
$transactionManager->setMaxRetries(3);

$result = $transactionManager->transaction(function () use ($db, $orderData, $transactionManager) {
    $orderId = \Glueful\Helpers\Utils::generateNanoID();
    $db->table('orders')->insert(['uuid' => $orderId] + $orderData);

    // Nested transaction with savepoint
    $transactionManager->transaction(function () use ($db, $orderId, $orderData) {
        foreach ($orderData['items'] as $item) {
            $db->table('order_items')->insert([
                'order_id' => $orderId,
                'product_id' => $item['product_id'],
                'quantity' => $item['quantity']
            ]);
        }
    });

    return $orderId;
});

Manual Transaction Control

Prefer the high-level $query->transaction(fn(...) => ...) API. If you need explicit control, use TransactionManager:

use Glueful\\Database\\Transaction\\TransactionManager;

$tm = container($context)->get(TransactionManager::class);

try {
    $tm->begin();

    // Complex multi-step operation (insert() returns affected rows; use a
    // generated UUID for relationships)
    $userId = \Glueful\Helpers\Utils::generateNanoID();
    $db->table('users')->insert(['uuid' => $userId] + $userData);
    $db->table('profiles')->insert(array_merge($profileData, ['user_id' => $userId]));

    // Nested savepoint
    $tm->begin();
    try {
        $db->table('user_preferences')->insert(['user_id' => $userId, 'theme' => 'dark']);
        $tm->commit(); // Commit savepoint
    } catch (Exception $e) {
        $tm->rollback(); // Roll back to previous savepoint
        // Continue with main transaction
    }

    $tm->commit(); // Commit main transaction
} catch (Exception $e) {
    $tm->rollback();
    throw $e;
}

Transaction State Monitoring

// Check transaction state via TransactionManager
if ($tm->isActive()) {
    echo "Transaction level: " . $tm->getLevel();
}

// Pool statistics via ConnectionPoolManager
$poolManager = Glueful\\Database\\Connection::getPoolManager();
if ($poolManager) {
    $stats = $poolManager->getStats(); // Per-engine stats
}

Advanced Features

Query Purpose Tracking

use Glueful\Database\Features\QueryPurpose;

// Track business purpose for queries
$queryBuilder->withPurpose('User dashboard data loading')
    ->select(['*'])
    ->from('users')
    ->where(['active' => 1])
    ->get();

// Purpose is logged for debugging and optimization

Connection Validation and Health Checks

use Glueful\Database\ConnectionValidator;

$validator = new ConnectionValidator();

// Validate connection health
$isHealthy = $validator->validate($connection);

// Get detailed validation results
$results = $validator->getValidationResults();
/*
[
    'ping_successful' => true,
    'response_time_ms' => 1.2,
    'server_version' => '8.0.31',
    'charset_valid' => true,
    'timezone_valid' => true,
    'ssl_enabled' => true
]
*/

Query Hashing and Caching

use Glueful\Database\QueryHasher;
use Glueful\Database\QueryCacheService;

$hasher = new QueryHasher();
$cacheService = new QueryCacheService($cache, $hasher);

// Automatic query result caching
$results = $queryBuilder
    ->cache(ttl: 3600, tags: ['users', 'active'])
    ->select(['*'])
    ->from('users')
    ->where(['active' => 1])
    ->get();

// Cache invalidation by tags
$cacheService->invalidateTags(['users']);

Soft Delete Management

use Glueful\Database\Features\SoftDeleteHandler;

$softDeleteHandler = new SoftDeleteHandler();

// Configure soft delete column
$softDeleteHandler->setSoftDeleteColumn('deleted_at');

// Query with soft delete awareness
$activeUsers = $queryBuilder
    ->withoutTrashed() // Exclude soft deleted
    ->select(['*'])
    ->from('users')
    ->get();

$allUsers = $queryBuilder
    ->withTrashed() // Include soft deleted
    ->select(['*'])
    ->from('users')
    ->get();

$trashedOnly = $queryBuilder
    ->onlyTrashed() // Only soft deleted
    ->select(['*'])
    ->from('users')
    ->get();

Configuration

Database Configuration

// config/database.php
return [
    'default' => env('DB_CONNECTION', 'mysql'),

    // Database role configuration (primary/backup)
    'roles' => [
        'primary' => env('DB_PRIMARY', 'mysql'),
        'backup' => env('DB_BACKUP', 'mysql_replica')
    ],

    'connections' => [
        'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', 3306),
            'db' => env('DB_DATABASE'),
            'user' => env('DB_USERNAME'),
            'pass' => env('DB_PASSWORD'),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'strict' => true,
            'engine' => 'InnoDB',

            // SSL configuration
            'ssl' => [
                'enabled' => env('DB_SSL_ENABLED', false),
                'ca' => env('DB_SSL_CA'),
                'cert' => env('DB_SSL_CERT'),
                'key' => env('DB_SSL_KEY')
            ],

            // Advanced MySQL options
            'options' => [
                PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
                PDO::MYSQL_ATTR_INIT_COMMAND => "SET sql_mode='STRICT_ALL_TABLES'",
                PDO::ATTR_PERSISTENT => env('DB_PERSISTENT', false)
            ]
        ],

        'pgsql' => [
            'driver' => 'pgsql',
            'host' => env('PGSQL_HOST', '127.0.0.1'),
            'port' => env('PGSQL_PORT', 5432),
            'db' => env('PGSQL_DATABASE'),
            'user' => env('PGSQL_USERNAME'),
            'pass' => env('PGSQL_PASSWORD'),
            'sslmode' => env('PGSQL_SSLMODE', 'prefer'),
            'schema' => env('PGSQL_SCHEMA', 'public'),
            'charset' => 'utf8',
            'application_name' => env('APP_NAME', 'glueful')
        ],

        'sqlite' => [
            'driver' => 'sqlite',
            'database' => env('SQLITE_DATABASE', database_path('database.sqlite')),
            'foreign_key_constraints' => true,
            'busy_timeout' => 5000,
            'journal_mode' => 'WAL'
        ]
    ],

    // Connection pooling configuration
    'pooling' => [
        'enabled' => env('DB_POOLING_ENABLED', true),
        'defaults' => [
            'min_connections' => env('DB_POOL_MIN_CONNECTIONS', 2),
            'max_connections' => env('DB_POOL_MAX_CONNECTIONS', 10),
            'idle_timeout' => env('DB_POOL_IDLE_TIMEOUT', 300),
            'max_lifetime' => env('DB_POOL_MAX_LIFETIME', 3600),
            'acquisition_timeout' => env('DB_POOL_ACQUIRE_TIMEOUT', 30),
            'health_check_interval' => env('DB_POOL_HEALTH_CHECK_INTERVAL', 60),
            'health_check_timeout' => env('DB_POOL_HEALTH_CHECK_TIMEOUT', 5),
            'max_use_count' => env('DB_POOL_MAX_USE_COUNT', 1000),
            'retry_attempts' => env('DB_POOL_RETRY_ATTEMPTS', 3),
            'retry_delay' => env('DB_POOL_RETRY_DELAY', 100),
            'enable_async_maintenance' => env('DB_POOL_ASYNC_MAINTENANCE', false),
            'maintenance_worker' => env('DB_POOL_WORKER', 'fork') // fork|react|swoole
        ],
        'engines' => [
            'mysql' => [
                'max_connections' => env('MYSQL_POOL_MAX', 20),
                'min_connections' => env('MYSQL_POOL_MIN', 5)
            ],
            'pgsql' => [
                'max_connections' => env('PGSQL_POOL_MAX', 15),
                'min_connections' => env('PGSQL_POOL_MIN', 3)
            ],
            'sqlite' => [
                'max_connections' => 1, // SQLite limitation
                'min_connections' => 1
            ]
        ]
    ]
];

Query Optimization Configuration

// config/database_optimization.php
return [
    'query_optimization' => [
        'enabled' => env('DB_OPTIMIZATION_ENABLED', true),
        'default_threshold' => env('DB_OPTIMIZATION_THRESHOLD', 10.0), // 10% improvement required
        'cache_optimizations' => env('DB_OPTIMIZATION_CACHE', true),
        
        'engines' => [
            'mysql' => [
                'use_straight_join' => true,
                'optimize_group_by' => true,
                'index_hints' => true
            ],
            'pgsql' => [
                'use_query_planner_hints' => false,
                'optimize_window_functions' => true
            ]
        ]
    ],
    
    'query_analysis' => [
        'enabled' => env('DB_ANALYSIS_ENABLED', true),
        'complexity_threshold' => 5, // Queries with complexity > 5 get extra analysis
        'execution_plan_analysis' => env('DB_ANALYZE_EXECUTION_PLANS', false)
    ]
];

Performance Monitoring Configuration

// config/database_monitoring.php
return [
    'query_logging' => [
        'enabled' => env('DB_QUERY_LOGGING_ENABLED', true),
        'debug_mode' => env('DB_DEBUG_MODE', false),
        'max_log_size' => env('DB_MAX_LOG_SIZE', 500),
        
        'slow_query_detection' => [
            'enabled' => env('DB_SLOW_QUERY_DETECTION', true),
            'threshold_ms' => env('DB_SLOW_QUERY_THRESHOLD', 200),
            'log_level' => 'warning'
        ],
        
        'n1_detection' => [
            'enabled' => env('DB_N1_DETECTION_ENABLED', true),
            'threshold' => env('DB_N1_THRESHOLD', 5),
            'time_window' => env('DB_N1_TIME_WINDOW', 5)
        ]
    ],
    
    'performance_monitoring' => [
        'enabled' => env('DB_PERFORMANCE_MONITORING', true),
        'track_query_complexity' => true,
        'track_table_usage' => true,
        'emit_events' => true
    ]
];

Production Optimization

High-Performance Configuration

// Production-optimized settings
return [
    'mysql' => [
        'options' => [
            PDO::ATTR_PERSISTENT => true,              // Use persistent connections
            PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, // Buffer query results
            PDO::MYSQL_ATTR_INIT_COMMAND => 
                "SET sql_mode='STRICT_ALL_TABLES', " .
                "SESSION query_cache_type='ON', " .
                "SESSION query_cache_size=67108864",    // 64MB query cache
        ]
    ],
    
    'pooling' => [
        'defaults' => [
            'min_connections' => 10,    // Higher minimum for production
            'max_connections' => 50,    // Higher maximum for production
            'acquire_timeout' => 10,    // Faster timeout
            'idle_timeout' => 600,      // 10 minutes
            'health_check_interval' => 30,
            'max_connection_age' => 1800 // 30 minutes
        ]
    ],
    
    'query_optimization' => [
        'enabled' => true,
        'default_threshold' => 5.0,     // Lower threshold for more optimizations
        'cache_optimizations' => true
    ],
    
    'query_logging' => [
        'debug_mode' => false,          // Disable debug mode in production
        'max_log_size' => 100,          // Smaller log size
        'slow_query_detection' => [
            'threshold_ms' => 100       // Lower threshold for production monitoring
        ]
    ]
];

Monitoring and Alerting

// Production monitoring setup
class DatabaseMonitoringService
{
    private QueryLogger $logger;
    private ConnectionPoolManager $poolManager;
    
    public function getHealthMetrics(): array
    {
        $poolStats = $this->poolManager->getAggregateStats();
        $queryStats = $this->logger->getStatistics();
        
        return [
            'database_health' => [
                'total_connections' => $poolStats['total_active_connections'] + $poolStats['total_idle_connections'],
                'active_connections' => $poolStats['total_active_connections'],
                'connection_pool_utilization' => $this->calculatePoolUtilization($poolStats),
                'average_query_time' => $this->logger->getAverageExecutionTime(),
                'slow_query_rate' => $this->calculateSlowQueryRate($queryStats),
                'error_rate' => $this->calculateErrorRate($queryStats),
                'n1_detections_last_hour' => $this->getN1DetectionsCount()
            ]
        ];
    }
    
    public function checkAlerts(): array
    {
        $alerts = [];
        $metrics = $this->getHealthMetrics()['database_health'];
        
        // Connection pool alerts
        if ($metrics['connection_pool_utilization'] > 0.9) {
            $alerts[] = 'Connection pool utilization above 90%';
        }
        
        // Performance alerts
        if ($metrics['average_query_time'] > 500) { // 500ms
            $alerts[] = 'Average query time above 500ms';
        }
        
        if ($metrics['slow_query_rate'] > 0.1) { // 10%
            $alerts[] = 'Slow query rate above 10%';
        }
        
        // Error rate alerts
        if ($metrics['error_rate'] > 0.05) { // 5%
            $alerts[] = 'Database error rate above 5%';
        }
        
        return $alerts;
    }
}

Performance Optimization Best Practices

use Glueful\Database\Connection;
use Glueful\Database\ConnectionPoolManager;
use Glueful\Database\QueryLogger;
use Glueful\Database\QueryOptimizer;
use Glueful\Database\Tools\ExecutionPlanAnalyzer;

// 1. Use connection pooling in production (automatic when enabled in config)
$db = container($context)->get(Connection::class); // Pooling is automatic if enabled

// Manual pool management (rarely needed)
$poolManager = container($context)->get(ConnectionPoolManager::class);
$pool = $poolManager->getPool('mysql');
$connection = $pool->acquire();
try {
    // Use connection
} finally {
    $pool->release($connection);
}

// 2. Enable query optimization and caching for complex queries
$results = $db->table('complex_table')
    ->select(['complex_table.*', 'related_table.name'])
    ->join('related_table', 'related_table.id', '=', 'complex_table.related_id')
    ->where(['complex_table.status' => 'active'])
    ->optimize()           // Enable query optimization
    ->cache(300)          // Cache results for 5 minutes
    ->withPurpose('Complex data aggregation')  // Track purpose for monitoring
    ->get();

// 3. Use bulk operations to prevent N+1 queries
$users = $userService->getActiveUsers();
$userIds = array_column($users, 'id');

// Efficient: Single query for all profiles
$profiles = $db->table('profiles')
    ->select(['*'])
    ->whereIn('user_id', $userIds)
    ->get();

// Bulk insert with batching
$records = [...]; // Large dataset
$batchSize = 100;
foreach (array_chunk($records, $batchSize) as $batch) {
    $db->table('logs')->insertBatch($batch);
}

// 4. Monitor and analyze query performance
$queryLogger = container($context)->get(QueryLogger::class);
$queryLogger->configure(
    enableDebug: false,     // Disable debug in production
    enableTiming: true,     // Track execution times
    maxLogSize: 100        // Limit log size
);
$queryLogger->configureN1Detection(
    threshold: 5,          // Detect after 5 similar queries
    timeWindow: 5          // Within 5 seconds
);

// 5. Analyze execution plans for optimization
$analyzer = new ExecutionPlanAnalyzer($db);
$query = "SELECT u.*, p.* FROM users u
          JOIN profiles p ON p.user_id = u.id
          WHERE u.active = 1";
$analysis = $analyzer->analyze($query, []);

if (!empty($analysis['warnings'])) {
    // Address performance warnings
    foreach ($analysis['recommendations'] as $recommendation) {
        $logger->warning('Query optimization needed: ' . $recommendation);
    }
}

// 6. Use transactions for bulk operations
$db->transaction(function () use ($db, $orderData) {
    $orderId = \Glueful\Helpers\Utils::generateNanoID();
    $db->table('orders')->insert(['uuid' => $orderId] + $orderData);

    foreach ($orderData['items'] as $item) {
        $db->table('order_items')->insert([
            'order_id' => $orderId,
            'product_id' => $item['product_id'],
            'quantity' => $item['quantity']
        ]);
    }

    return $orderId;
});

// 7. Use pagination for large result sets
$page = $request->get('page', 1);
$results = $db->table('products')
    ->select(['*'])
    ->where(['active' => 1])
    ->orderBy(['created_at' => 'DESC'])
    ->paginate($page, perPage: 25);

// 8. Optimize with index hints and query purpose tracking
$result = $db->table('users')
    ->withPurpose('User dashboard data loading')
    ->select(['users.*', 'profiles.bio', 'user_stats.login_count'])
    ->join('profiles', 'profiles.user_id', '=', 'users.id')
    ->join('user_stats', 'user_stats.user_id', '=', 'users.id')
    ->where(['users.active' => 1])
    ->whereNotNull('users.last_login')
    ->orderBy(['users.last_login' => 'DESC'])
    ->limit(20)
    ->enableDebug(false)  // Disable debug in production
    ->get();

Summary

Glueful's advanced database system represents a modern, enterprise-grade implementation that significantly exceeds typical framework capabilities:

Architecture Highlights

  • Modular Design: Orchestrator pattern with 34+ specialized components replacing monolithic approach
  • Dependency Injection: All components properly injected for testability and flexibility
  • Production Ready: SSL support, connection leak detection, performance scoring

Key Features

  • Advanced Connection Pooling: Multi-engine pools with async maintenance workers (ReactPHP/Swoole/Fork)
  • Sophisticated Monitoring: N+1 detection, execution plan analysis, pattern recognition, profiling
  • Complete Migration System: Schema management with batch tracking and checksum verification
  • Enterprise Transactions: Nested transactions, savepoints, automatic deadlock retry
  • Query Optimization: Pattern recognition, database-specific optimizations, caching
  • Multi-Database Support: MySQL, PostgreSQL, SQLite with SSL and driver-specific features
  • Developer Experience: Purpose tracking, health checks, soft deletes, advanced pagination

Performance & Reliability

  • Connection Management: Health monitoring, leak detection, automatic recycling
  • Query Analysis: Real-time profiling, slow query detection, memory tracking
  • Optimization: Automatic query optimization, execution plan analysis, result caching
  • Production Features: Configurable pooling, SSL/TLS support, comprehensive logging

The implementation provides a sophisticated, production-ready database layer suitable for high-traffic, distributed applications while maintaining excellent developer experience through modular design and comprehensive tooling.