@@ -679,10 +679,202 @@ INSERT INTO performance_action (
679679 NOW()
680680) ON CONFLICT DO NOTHING;
681681
682+ -- --------------------------------------------------------------------------
683+ -- Query Trends / Workload Analysis analytics (unlocks the Performance tab)
684+ -- The legacy slow_query_history JSON alone does NOT populate Query Trends —
685+ -- that path needs slow_log_source_config + query_fingerprints + slow_query_run.
686+ -- --------------------------------------------------------------------------
687+
688+ INSERT INTO slow_log_source_config (
689+ id, connection_id, provider_type, enabled, auto_schedule_enabled,
690+ bucket_name, object_prefix, s3_region, refresh_frequency_minutes,
691+ consecutive_dry_runs, created_at, updated_at, last_processed_at
692+ ) VALUES (
693+ 'seed-log-source-' || '${connection_id} ',
694+ '${connection_id} ',
695+ 'S3',
696+ true,
697+ false,
698+ 'deepsql-demo-slow-logs',
699+ 'postgres/demo-shop/',
700+ 'us-east-1',
701+ 60,
702+ 0,
703+ NOW() - INTERVAL '7 days',
704+ NOW(),
705+ NOW() - INTERVAL '1 hour'
706+ ) ON CONFLICT (id) DO UPDATE SET
707+ enabled = EXCLUDED.enabled,
708+ auto_schedule_enabled = false,
709+ updated_at = NOW();
710+
711+ INSERT INTO connection_analytics_config (
712+ connection_id, daily_analysis_enabled, tenant_column,
713+ customer_lookup_table, customer_lookup_id_col, customer_lookup_name_col,
714+ created_at, updated_at
715+ ) VALUES (
716+ '${connection_id} ', true, 'customer_id',
717+ 'customers', 'id', 'email',
718+ NOW() - INTERVAL '7 days', NOW()
719+ ) ON CONFLICT (connection_id) DO UPDATE SET
720+ daily_analysis_enabled = true,
721+ tenant_column = 'customer_id',
722+ customer_lookup_table = 'customers',
723+ customer_lookup_id_col = 'id',
724+ customer_lookup_name_col = 'email',
725+ updated_at = NOW();
726+
727+ DELETE FROM slow_query_customer_day WHERE connection_id = '${connection_id} ' AND id LIKE 'seed-%';
728+ DELETE FROM slow_query_customer WHERE connection_id = '${connection_id} ' AND id LIKE 'seed-%';
729+ DELETE FROM slow_query_sample WHERE connection_id = '${connection_id} ' AND id LIKE 'seed-%';
730+ DELETE FROM slow_query_run WHERE connection_id = '${connection_id} ' AND id LIKE 'seed-%';
731+ DELETE FROM query_fingerprints WHERE connection_id = '${connection_id} ' AND id LIKE 'seed-%';
732+
733+ INSERT INTO query_fingerprints (
734+ id, connection_id, fingerprint, normalized_query, sample_query, query_type,
735+ normalization_version, affected_tables,
736+ current_avg_time_ms, current_max_time_ms, current_call_count,
737+ current_rows_examined, current_rows_sent,
738+ baseline_avg_time_ms, baseline_max_time_ms, baseline_call_count, baseline_date,
739+ first_seen_at, last_seen_at, observation_count,
740+ is_regressing, trend_direction, trend_percentage,
741+ performance_history, created_at, updated_at
742+ ) VALUES
743+ (
744+ 'seed-fp-orders-lower', '${connection_id} ', 'a1b2c3d4e5f60718',
745+ 'SELECT * FROM orders WHERE LOWER(status) = ? AND total_amount > ?',
746+ 'SELECT * FROM orders WHERE LOWER(status) = ''delivered'' AND total_amount > 100',
747+ 'SELECT', 1, '["orders"]'::json,
748+ 1.87, 45.2, 1250, 15000, 420,
749+ 1.10, 28.0, 800, NOW() - INTERVAL '14 days',
750+ NOW() - INTERVAL '21 days', NOW() - INTERVAL '1 hour', 12,
751+ true, 'DEGRADING', 70.0,
752+ '[{"timestamp":"2026-08-07T10:00:00","avgTimeMs":1.1,"callCount":800},{"timestamp":"2026-08-14T09:00:00","avgTimeMs":1.87,"callCount":1250}]'::json,
753+ NOW() - INTERVAL '21 days', NOW()
754+ ),
755+ (
756+ 'seed-fp-orders-join', '${connection_id} ', 'b2c3d4e5f6071829',
757+ 'SELECT o.*, c.* FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.status = ? AND o.payment_status = ?',
758+ 'SELECT o.*, c.* FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.status = ''pending'' AND o.payment_status = ''paid''',
759+ 'SELECT', 1, '["orders","customers"]'::json,
760+ 2.08, 38.7, 890, 5200, 310,
761+ 1.85, 30.0, 700, NOW() - INTERVAL '14 days',
762+ NOW() - INTERVAL '18 days', NOW() - INTERVAL '2 hours', 10,
763+ false, 'STABLE', 12.0,
764+ '[{"timestamp":"2026-08-07T10:00:00","avgTimeMs":1.85,"callCount":700},{"timestamp":"2026-08-14T08:00:00","avgTimeMs":2.08,"callCount":890}]'::json,
765+ NOW() - INTERVAL '18 days', NOW()
766+ ),
767+ (
768+ 'seed-fp-product-avg', '${connection_id} ', 'c3d4e5f60718293a',
769+ 'SELECT p.*, AVG(r.rating) FROM products p LEFT JOIN product_reviews r ON p.id = r.product_id GROUP BY p.id',
770+ 'SELECT p.*, AVG(r.rating) FROM products p LEFT JOIN product_reviews r ON p.id = r.product_id GROUP BY p.id',
771+ 'SELECT', 1, '["products","product_reviews"]'::json,
772+ 0.59, 12.3, 2100, 100, 100,
773+ 0.55, 10.0, 1800, NOW() - INTERVAL '14 days',
774+ NOW() - INTERVAL '30 days', NOW() - INTERVAL '30 minutes', 15,
775+ false, 'IMPROVING', -7.0,
776+ '[{"timestamp":"2026-08-07T10:00:00","avgTimeMs":0.55,"callCount":1800},{"timestamp":"2026-08-14T10:00:00","avgTimeMs":0.59,"callCount":2100}]'::json,
777+ NOW() - INTERVAL '30 days', NOW()
778+ ),
779+ (
780+ 'seed-fp-audit-scan', '${connection_id} ', 'd4e5f60718293a4b',
781+ 'SELECT * FROM audit_log WHERE table_name = ? ORDER BY changed_at DESC LIMIT ?',
782+ 'SELECT * FROM audit_log WHERE table_name = ''orders'' ORDER BY changed_at DESC LIMIT 1000',
783+ 'SELECT', 1, '["audit_log"]'::json,
784+ 7.68, 89.4, 450, 50000, 1000,
785+ 3.20, 40.0, 200, NOW() - INTERVAL '14 days',
786+ NOW() - INTERVAL '12 days', NOW() - INTERVAL '45 minutes', 8,
787+ true, 'CRITICAL', 140.0,
788+ '[{"timestamp":"2026-08-07T10:00:00","avgTimeMs":3.2,"callCount":200},{"timestamp":"2026-08-14T09:30:00","avgTimeMs":7.68,"callCount":450}]'::json,
789+ NOW() - INTERVAL '12 days', NOW()
790+ );
791+
792+ INSERT INTO slow_query_run (
793+ id, connection_id, analysis_run_id, fingerprint, analyzed_on, captured_at,
794+ calls_cumulative, calls_delta, total_exec_ms_cumulative, total_exec_ms_delta,
795+ mean_exec_ms, max_exec_ms, p95_exec_ms,
796+ rows_examined_delta, rows_sent_delta,
797+ regression_factor, counter_reset, prev_run_id, created_at
798+ ) VALUES
799+ ('seed-run-o1-d6', '${connection_id} ', 'seed-analysis-d6', 'a1b2c3d4e5f60718', CURRENT_DATE - 6, NOW() - INTERVAL '6 days',
800+ 600, 600, 660.0, 660.0, 1.10, 28.0, 2.1, 9000, 250, NULL, false, NULL, NOW() - INTERVAL '6 days'),
801+ ('seed-run-o2-d6', '${connection_id} ', 'seed-analysis-d6', 'b2c3d4e5f6071829', CURRENT_DATE - 6, NOW() - INTERVAL '6 days',
802+ 500, 500, 925.0, 925.0, 1.85, 30.0, 3.2, 3000, 180, NULL, false, NULL, NOW() - INTERVAL '6 days'),
803+ ('seed-run-p1-d6', '${connection_id} ', 'seed-analysis-d6', 'c3d4e5f60718293a', CURRENT_DATE - 6, NOW() - INTERVAL '6 days',
804+ 1500, 1500, 825.0, 825.0, 0.55, 10.0, 0.9, 100, 100, NULL, false, NULL, NOW() - INTERVAL '6 days'),
805+ ('seed-run-a1-d6', '${connection_id} ', 'seed-analysis-d6', 'd4e5f60718293a4b', CURRENT_DATE - 6, NOW() - INTERVAL '6 days',
806+ 180, 180, 576.0, 576.0, 3.20, 40.0, 6.5, 20000, 1000, NULL, false, NULL, NOW() - INTERVAL '6 days'),
807+ ('seed-run-o1-d3', '${connection_id} ', 'seed-analysis-d3', 'a1b2c3d4e5f60718', CURRENT_DATE - 3, NOW() - INTERVAL '3 days',
808+ 950, 350, 1330.0, 670.0, 1.91, 36.0, 3.4, 5500, 140, 1.74, false, 'seed-run-o1-d6', NOW() - INTERVAL '3 days'),
809+ ('seed-run-o2-d3', '${connection_id} ', 'seed-analysis-d3', 'b2c3d4e5f6071829', CURRENT_DATE - 3, NOW() - INTERVAL '3 days',
810+ 720, 220, 1381.0, 456.0, 2.07, 34.0, 3.8, 1800, 90, 1.12, false, 'seed-run-o2-d6', NOW() - INTERVAL '3 days'),
811+ ('seed-run-p1-d3', '${connection_id} ', 'seed-analysis-d3', 'c3d4e5f60718293a', CURRENT_DATE - 3, NOW() - INTERVAL '3 days',
812+ 1850, 350, 1036.0, 211.0, 0.60, 11.0, 1.0, 100, 100, 1.09, false, 'seed-run-p1-d6', NOW() - INTERVAL '3 days'),
813+ ('seed-run-a1-d3', '${connection_id} ', 'seed-analysis-d3', 'd4e5f60718293a4b', CURRENT_DATE - 3, NOW() - INTERVAL '3 days',
814+ 310, 130, 1488.0, 912.0, 7.02, 72.0, 14.0, 28000, 1000, 2.19, false, 'seed-run-a1-d6', NOW() - INTERVAL '3 days'),
815+ ('seed-run-o1-d0', '${connection_id} ', 'seed-analysis-d0', 'a1b2c3d4e5f60718', CURRENT_DATE, NOW() - INTERVAL '1 hour',
816+ 1250, 300, 2337.5, 1007.5, 3.36, 45.2, 5.8, 6000, 120, 1.76, false, 'seed-run-o1-d3', NOW()),
817+ ('seed-run-o2-d0', '${connection_id} ', 'seed-analysis-d0', 'b2c3d4e5f6071829', CURRENT_DATE, NOW() - INTERVAL '1 hour',
818+ 890, 170, 1850.2, 469.2, 2.76, 38.7, 4.9, 2200, 80, 1.33, false, 'seed-run-o2-d3', NOW()),
819+ ('seed-run-p1-d0', '${connection_id} ', 'seed-analysis-d0', 'c3d4e5f60718293a', CURRENT_DATE, NOW() - INTERVAL '1 hour',
820+ 2100, 250, 1239.0, 203.0, 0.81, 12.3, 1.3, 100, 100, 1.35, false, 'seed-run-p1-d3', NOW()),
821+ ('seed-run-a1-d0', '${connection_id} ', 'seed-analysis-d0', 'd4e5f60718293a4b', CURRENT_DATE, NOW() - INTERVAL '1 hour',
822+ 450, 140, 3456.0, 1968.0, 14.06, 89.4, 28.0, 25000, 1000, 2.00, false, 'seed-run-a1-d3', NOW());
823+
824+ INSERT INTO slow_query_sample (
825+ id, connection_id, fingerprint, customer_id, captured_at, ingested_at,
826+ exec_ms, rows_examined, rows_sent, source, raw_sql
827+ ) VALUES
828+ ('seed-sample-1', '${connection_id} ', 'a1b2c3d4e5f60718', '1001', NOW() - INTERVAL '2 hours', NOW() - INTERVAL '1 hour',
829+ 42.5, 18000, 120, 'SLOW_LOG', 'SELECT * FROM orders WHERE LOWER(status) = ''delivered'' AND total_amount > 100 /* cust=1001 */'),
830+ ('seed-sample-2', '${connection_id} ', 'a1b2c3d4e5f60718', '1002', NOW() - INTERVAL '90 minutes', NOW() - INTERVAL '1 hour',
831+ 38.1, 16000, 95, 'SLOW_LOG', 'SELECT * FROM orders WHERE LOWER(status) = ''delivered'' AND total_amount > 250 /* cust=1002 */'),
832+ ('seed-sample-3', '${connection_id} ', 'd4e5f60718293a4b', '1001', NOW() - INTERVAL '80 minutes', NOW() - INTERVAL '1 hour',
833+ 88.2, 52000, 1000, 'SLOW_LOG', 'SELECT * FROM audit_log WHERE table_name = ''orders'' ORDER BY changed_at DESC LIMIT 1000 /* cust=1001 */'),
834+ ('seed-sample-4', '${connection_id} ', 'b2c3d4e5f6071829', '1003', NOW() - INTERVAL '70 minutes', NOW() - INTERVAL '1 hour',
835+ 29.4, 4800, 40, 'SLOW_LOG', 'SELECT o.*, c.* FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.status = ''pending'' AND o.payment_status = ''paid'' /* cust=1003 */'),
836+ ('seed-sample-5', '${connection_id} ', 'c3d4e5f60718293a', NULL, NOW() - INTERVAL '60 minutes', NOW() - INTERVAL '1 hour',
837+ 11.2, 100, 100, 'SLOW_LOG', 'SELECT p.*, AVG(r.rating) FROM products p LEFT JOIN product_reviews r ON p.id = r.product_id GROUP BY p.id');
838+
839+ INSERT INTO slow_query_customer (
840+ id, connection_id, customer_id, customer_name, tenant_column,
841+ first_seen_at, last_seen_at, name_resolved_at, created_at
842+ ) VALUES
843+ ('seed-cust-1001', '${connection_id} ', '1001', 'acme@demo.local', 'customer_id', NOW() - INTERVAL '10 days', NOW() - INTERVAL '70 minutes', NOW() - INTERVAL '1 day', NOW()),
844+ ('seed-cust-1002', '${connection_id} ', '1002', 'globex@demo.local', 'customer_id', NOW() - INTERVAL '8 days', NOW() - INTERVAL '90 minutes', NOW() - INTERVAL '1 day', NOW()),
845+ ('seed-cust-1003', '${connection_id} ', '1003', 'initech@demo.local', 'customer_id', NOW() - INTERVAL '5 days', NOW() - INTERVAL '70 minutes', NOW() - INTERVAL '1 day', NOW());
846+
847+ INSERT INTO slow_query_customer_day (
848+ id, connection_id, fingerprint, customer_id, day,
849+ sample_count, mean_exec_ms, max_exec_ms, total_exec_ms,
850+ prev_day_mean_ms, regression_factor, created_at
851+ ) VALUES
852+ ('seed-cday-1', '${connection_id} ', 'a1b2c3d4e5f60718', '1001', CURRENT_DATE, 18, 4.2, 42.5, 75.6, 2.1, 2.0, NOW()),
853+ ('seed-cday-2', '${connection_id} ', 'a1b2c3d4e5f60718', '1002', CURRENT_DATE, 12, 3.8, 38.1, 45.6, 2.4, 1.58, NOW()),
854+ ('seed-cday-3', '${connection_id} ', 'd4e5f60718293a4b', '1001', CURRENT_DATE, 9, 15.1, 88.2, 135.9, 7.0, 2.16, NOW()),
855+ ('seed-cday-4', '${connection_id} ', 'b2c3d4e5f6071829', '1003', CURRENT_DATE, 14, 2.9, 29.4, 40.6, 2.2, 1.32, NOW());
856+
682857SELECT 'Performance seed data inserted successfully' AS status;
683858EOSQL
684859
685- echo " Performance data seeded."
860+ echo " Performance data seeded (history + Query Trends analytics + log source)."
861+
862+ # Optional: exercise demo_shop so pg_stat_statements has matching patterns
863+ echo " Simulating demo_shop workload (slow-query patterns)..."
864+ compose exec -T postgres psql -U postgres -d demo_shop -v ON_ERROR_STOP=1 << 'EOWORK ' >/dev/null || echo " Note: demo_shop workload simulation skipped (DB missing?)"
865+ DO $$
866+ DECLARE i int;
867+ BEGIN
868+ FOR i IN 1..25 LOOP
869+ PERFORM count(*) FROM orders WHERE LOWER(status) = 'delivered' AND total_amount > 100;
870+ PERFORM count(*) FROM orders o JOIN customers c ON o.customer_id = c.id
871+ WHERE o.status = 'pending' AND o.payment_status = 'paid';
872+ PERFORM p.id FROM products p
873+ LEFT JOIN product_reviews r ON p.id = r.product_id GROUP BY p.id;
874+ PERFORM 1 FROM audit_log WHERE table_name = 'orders' ORDER BY changed_at DESC LIMIT 1000;
875+ END LOOP;
876+ END $$;
877+ EOWORK
686878else
687879 echo " Skipping performance data (no connection ID available)"
688880fi
@@ -795,7 +987,9 @@ if [[ -n "${connection_id:-}" ]]; then
795987echo " - Connection ID: ${connection_id} "
796988fi
797989echo " - Saved queries in SQL Editor"
798- echo " - Sample slow query analysis"
990+ echo " - Sample slow query analysis (legacy history JSON)"
991+ echo " - Slow-log source + Query Trends analytics (fingerprints / runs / samples)"
992+ echo " - Per-customer rollups for the By Customer view"
799993echo " - Index recommendations"
800994echo " - Performance actions"
801995echo " - Sample agent conversation"
@@ -808,7 +1002,8 @@ echo ""
8081002echo " Next steps:"
8091003echo " 1. Open http://localhost:${DEEPSQL_FRONTEND_PORT:- 3000} "
8101004echo " 2. Select '${DEEPSQL_SEED_CONNECTION_NAME} ' connection"
811- echo " 3. Explore the Schema, Slow Queries, and Actions tabs"
812- echo " 4. Try the SQL Editor with pre-saved queries"
813- echo " 5. Ask the Agent about the database"
1005+ echo " 3. Open Performance — Query Trends, By Customer, and Workload tabs"
1006+ echo " 4. On Workload, click Run analysis for a fresh holistic report"
1007+ echo " 5. Try the SQL Editor with pre-saved queries"
1008+ echo " 6. Ask the Agent about the database"
8141009echo " "
0 commit comments