Files
michaelschiemer/tests/debug/test-jsonb-gin-index.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

235 lines
7.7 KiB
PHP

<?php
declare(strict_types=1);
echo "Testing PostgreSQL JSONB with GIN Index Support\n";
echo "===============================================\n\n";
try {
// Create PDO connection
$pdo = new PDO(
'pgsql:host=db;dbname=michaelschiemer',
'postgres',
'StartSimple2024!'
);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "✅ Database connection established\n\n";
// Drop test table if exists
echo "Dropping test table if exists...\n";
$pdo->exec("DROP TABLE IF EXISTS test_jsonb_table");
echo "✅ Cleanup complete\n\n";
// Create test table with JSONB column
echo "Creating test table with JSONB column...\n";
$pdo->exec("
CREATE TABLE test_jsonb_table (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
metadata JSONB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
");
echo "✅ Table created successfully\n\n";
// Create GIN index on JSONB column
echo "Creating GIN index on JSONB column...\n";
$pdo->exec("CREATE INDEX idx_metadata_gin ON test_jsonb_table USING GIN (metadata)");
echo "✅ GIN index created successfully\n\n";
// Verify index was created with correct method
echo "Verifying index method...\n";
$stmt = $pdo->query("
SELECT
indexname,
indexdef
FROM pg_indexes
WHERE tablename = 'test_jsonb_table'
AND indexname = 'idx_metadata_gin'
");
$indexInfo = $stmt->fetch(PDO::FETCH_ASSOC);
if ($indexInfo && str_contains($indexInfo['indexdef'], 'USING gin')) {
echo "✅ Index confirmed to use GIN method\n";
echo " Definition: {$indexInfo['indexdef']}\n\n";
} else {
echo "❌ Index not found or not using GIN method\n\n";
exit(1);
}
// Insert test data with JSONB
echo "Inserting test data...\n";
$testData = [
[
'name' => 'Product A',
'metadata' => json_encode([
'category' => 'electronics',
'price' => 299.99,
'tags' => ['new', 'sale', 'featured'],
'specs' => [
'cpu' => 'Intel i7',
'ram' => '16GB',
'storage' => '512GB SSD'
]
])
],
[
'name' => 'Product B',
'metadata' => json_encode([
'category' => 'electronics',
'price' => 499.99,
'tags' => ['premium', 'featured'],
'specs' => [
'cpu' => 'AMD Ryzen 9',
'ram' => '32GB',
'storage' => '1TB SSD'
]
])
],
[
'name' => 'Product C',
'metadata' => json_encode([
'category' => 'books',
'price' => 19.99,
'tags' => ['bestseller'],
'author' => 'John Doe',
'isbn' => '978-3-16-148410-0'
])
]
];
$insertStmt = $pdo->prepare("
INSERT INTO test_jsonb_table (name, metadata)
VALUES (:name, :metadata)
");
foreach ($testData as $data) {
$insertStmt->execute([
'name' => $data['name'],
'metadata' => $data['metadata']
]);
}
echo "✅ Test data inserted (3 products)\n\n";
// Test JSONB queries with different operators
echo "Testing JSONB queries:\n";
echo "=====================\n\n";
// Test 1: Contains operator (@>)
echo "1. Query: Find all electronics\n";
$stmt = $pdo->query("
SELECT name, metadata->>'category' as category, metadata->>'price' as price
FROM test_jsonb_table
WHERE metadata @> '{\"category\": \"electronics\"}'
");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo " Found " . count($results) . " products:\n";
foreach ($results as $row) {
echo " - {$row['name']}: {$row['category']} (\${$row['price']})\n";
}
echo "\n";
// Test 2: JSON path query (using jsonb_exists to avoid PDO parameter conflict with ?)
echo "2. Query: Find products with 'featured' tag\n";
$stmt = $pdo->query("
SELECT name, metadata->'tags' as tags
FROM test_jsonb_table
WHERE jsonb_exists(metadata->'tags', 'featured')
");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo " Found " . count($results) . " products:\n";
foreach ($results as $row) {
echo " - {$row['name']}: " . $row['tags'] . "\n";
}
echo "\n";
// Test 3: Nested JSON query
echo "3. Query: Find products with specific CPU\n";
$stmt = $pdo->query("
SELECT name, metadata->'specs'->>'cpu' as cpu, metadata->'specs'->>'ram' as ram
FROM test_jsonb_table
WHERE metadata->'specs'->>'cpu' LIKE '%Intel%'
");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo " Found " . count($results) . " products:\n";
foreach ($results as $row) {
echo " - {$row['name']}: {$row['cpu']}, {$row['ram']} RAM\n";
}
echo "\n";
// Test 4: Query by price range
echo "4. Query: Find products under $100\n";
$stmt = $pdo->query("
SELECT name, (metadata->>'price')::numeric as price
FROM test_jsonb_table
WHERE (metadata->>'price')::numeric < 100
");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo " Found " . count($results) . " products:\n";
foreach ($results as $row) {
echo " - {$row['name']}: \${$row['price']}\n";
}
echo "\n";
// Test 5: Existence check (using jsonb_exists to avoid PDO parameter conflict)
echo "5. Query: Find products with author field\n";
$stmt = $pdo->query("
SELECT name, metadata->>'author' as author
FROM test_jsonb_table
WHERE jsonb_exists(metadata, 'author')
");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo " Found " . count($results) . " products:\n";
foreach ($results as $row) {
echo " - {$row['name']}: by {$row['author']}\n";
}
echo "\n";
// Verify GIN index is being used via EXPLAIN
echo "Verifying GIN index usage with EXPLAIN:\n";
$stmt = $pdo->query("
EXPLAIN (FORMAT JSON)
SELECT name FROM test_jsonb_table
WHERE metadata @> '{\"category\": \"electronics\"}'
");
$explainResult = $stmt->fetch(PDO::FETCH_ASSOC);
$explainJson = json_decode($explainResult['QUERY PLAN'], true);
$usesGinIndex = false;
$planStr = json_encode($explainJson, JSON_PRETTY_PRINT);
if (str_contains($planStr, 'idx_metadata_gin') || str_contains($planStr, 'Bitmap Index Scan')) {
echo "✅ Query optimizer is using GIN index\n";
$usesGinIndex = true;
} else {
echo "⚠️ GIN index may not be used (table might be too small for index to be beneficial)\n";
}
echo "\n";
// Cleanup
echo "Cleaning up...\n";
$pdo->exec("DROP TABLE IF EXISTS test_jsonb_table");
echo "✅ Test table dropped\n";
echo "\n✅ All JSONB with GIN Index tests passed!\n";
echo "\nSummary:\n";
echo "========\n";
echo "✅ JSONB column type supported\n";
echo "✅ GIN index created successfully\n";
echo "✅ Contains operator (@>) works\n";
echo "✅ Existence check (jsonb_exists) works\n";
echo "✅ JSON path queries work\n";
echo "✅ Nested JSON queries work\n";
echo "✅ Type casting works\n";
if ($usesGinIndex) {
echo "✅ Query optimizer uses GIN index\n";
}
echo "\nNote: Using jsonb_exists() instead of ? operator due to PDO parameter placeholder conflict\n";
} catch (\Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
echo "Stack trace:\n" . $e->getTraceAsString() . "\n";
exit(1);
}