|
| 1 | +import { deparseSync } from '../../src'; |
| 2 | +import { parse } from 'libpg-query'; |
| 3 | +import { expectParseDeparse } from '../../test-utils'; |
| 4 | + |
| 5 | +describe('Pretty Misc SQL formatting', () => { |
| 6 | + const misc1Sql = `WITH recent_orders AS ( |
| 7 | + SELECT o.id, o.user_id, o.created_at |
| 8 | + FROM orders o |
| 9 | + WHERE o.created_at > NOW() - INTERVAL '30 days' |
| 10 | +), high_value_orders AS ( |
| 11 | + SELECT r.user_id, COUNT(*) AS order_count, SUM(oi.price * oi.quantity) AS total_spent |
| 12 | + FROM recent_orders r |
| 13 | + JOIN order_items oi ON r.id = oi.order_id |
| 14 | + GROUP BY r.user_id |
| 15 | +) |
| 16 | +SELECT u.id, u.name, h.total_spent |
| 17 | +FROM users u |
| 18 | +JOIN high_value_orders h ON u.id = h.user_id |
| 19 | +WHERE h.total_spent > 1000 |
| 20 | +ORDER BY h.total_spent DESC`; |
| 21 | + |
| 22 | + const misc2Sql = `SELECT |
| 23 | + department, |
| 24 | + employee_id, |
| 25 | + COUNT(*) FILTER (WHERE status = 'active') OVER (PARTITION BY department) AS active_count, |
| 26 | + RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS salary_rank |
| 27 | +FROM employee_status |
| 28 | +GROUP BY GROUPING SETS ((department), (department, employee_id))`; |
| 29 | + |
| 30 | + const misc3Sql = `SELECT u.id, u.name, j.key, j.value |
| 31 | +FROM users u, |
| 32 | +LATERAL jsonb_each_text(u.preferences) AS j(key, value) |
| 33 | +WHERE j.key LIKE 'notif_%' AND j.value::boolean = true`; |
| 34 | + |
| 35 | + const misc4Sql = `SELECT p.id, p.title, |
| 36 | + CASE |
| 37 | + WHEN EXISTS ( |
| 38 | + SELECT 1 FROM reviews r |
| 39 | + WHERE r.product_id = p.id AND r.rating >= 4 |
| 40 | + ) THEN 'Popular' |
| 41 | + ELSE 'Unrated' |
| 42 | + END AS status |
| 43 | +FROM products p |
| 44 | +WHERE p.archived = false`; |
| 45 | + |
| 46 | + const misc5Sql = `WITH logs AS ( |
| 47 | + SELECT id, payload::json->>'event' AS event, (payload::json->>'ts')::timestamp AS ts |
| 48 | + FROM event_log |
| 49 | + WHERE ts > NOW() - INTERVAL '7 days' |
| 50 | +) |
| 51 | +SELECT event, COUNT(*) AS freq |
| 52 | +FROM ( |
| 53 | + SELECT DISTINCT event, ts::date AS event_day |
| 54 | + FROM logs |
| 55 | +) d |
| 56 | +GROUP BY event |
| 57 | +ORDER BY freq DESC`; |
| 58 | + |
| 59 | + const misc6Sql = `SELECT |
| 60 | + o.id AS order_id, |
| 61 | + u.name AS user_name, |
| 62 | + p.name AS product_name, |
| 63 | + s.status, |
| 64 | + sh.shipped_at, |
| 65 | + r.refund_amount |
| 66 | +FROM orders o |
| 67 | +JOIN users u |
| 68 | + ON o.user_id = u.id |
| 69 | +JOIN order_items oi |
| 70 | + ON oi.order_id = o.id |
| 71 | +JOIN products p |
| 72 | + ON ( |
| 73 | + (p.id = oi.product_id AND p.available = true) |
| 74 | + OR |
| 75 | + (p.sku = oi.product_sku AND (p.discontinued = false OR p.replacement_id IS NOT NULL)) |
| 76 | + ) |
| 77 | +LEFT JOIN shipping sh |
| 78 | + ON ( |
| 79 | + sh.order_id = o.id |
| 80 | + AND ( |
| 81 | + (sh.carrier = 'UPS' AND sh.tracking_number IS NOT NULL) |
| 82 | + OR |
| 83 | + (sh.carrier = 'FedEx' AND sh.shipped_at > o.created_at + INTERVAL '1 day') |
| 84 | + ) |
| 85 | + ) |
| 86 | +LEFT JOIN statuses s |
| 87 | + ON s.id = o.status_id |
| 88 | + AND ( |
| 89 | + s.name != 'cancelled' |
| 90 | + OR (s.name = 'cancelled' AND s.updated_at > NOW() - INTERVAL '7 days') |
| 91 | + ) |
| 92 | +LEFT JOIN refunds r |
| 93 | + ON r.order_id = o.id |
| 94 | + AND ( |
| 95 | + (r.status = 'approved' AND r.processed_at IS NOT NULL) |
| 96 | + OR |
| 97 | + (r.status = 'pending' AND r.requested_at < NOW() - INTERVAL '14 days') |
| 98 | + ) |
| 99 | +WHERE o.created_at > NOW() - INTERVAL '90 days' |
| 100 | + AND u.active = true |
| 101 | + AND ( |
| 102 | + s.status = 'shipped' |
| 103 | + OR ( |
| 104 | + s.status = 'processing' |
| 105 | + AND EXISTS ( |
| 106 | + SELECT 1 FROM order_notes n WHERE n.order_id = o.id AND n.note ILIKE '%expedite%' |
| 107 | + ) |
| 108 | + ) |
| 109 | + ) |
| 110 | +ORDER BY o.created_at DESC`; |
| 111 | + |
| 112 | + it('should format misc-1: Complex CTE with joins and aggregation', async () => { |
| 113 | + const result = await expectParseDeparse(misc1Sql, { pretty: true }); |
| 114 | + expect(result).toMatchSnapshot(); |
| 115 | + }); |
| 116 | + |
| 117 | + it('should format misc-2: Window functions with FILTER and GROUPING SETS', async () => { |
| 118 | + const result = await expectParseDeparse(misc2Sql, { pretty: true }); |
| 119 | + expect(result).toMatchSnapshot(); |
| 120 | + }); |
| 121 | + |
| 122 | + it('should format misc-3: LATERAL joins with JSON functions', async () => { |
| 123 | + const result = await expectParseDeparse(misc3Sql, { pretty: true }); |
| 124 | + expect(result).toMatchSnapshot(); |
| 125 | + }); |
| 126 | + |
| 127 | + it('should format misc-4: EXISTS with nested subqueries and CASE', async () => { |
| 128 | + const result = await expectParseDeparse(misc4Sql, { pretty: true }); |
| 129 | + expect(result).toMatchSnapshot(); |
| 130 | + }); |
| 131 | + |
| 132 | + it('should format misc-5: Nested CTEs with type casts and subqueries', async () => { |
| 133 | + const result = await expectParseDeparse(misc5Sql, { pretty: true }); |
| 134 | + expect(result).toMatchSnapshot(); |
| 135 | + }); |
| 136 | + |
| 137 | + it('should format misc-6: Complex multi-table joins with nested conditions', async () => { |
| 138 | + const result = await expectParseDeparse(misc6Sql, { pretty: true }); |
| 139 | + expect(result).toMatchSnapshot(); |
| 140 | + }); |
| 141 | + |
| 142 | + it('should validate AST equivalence for all misc cases', async () => { |
| 143 | + const testCases = [misc1Sql, misc2Sql, misc3Sql, misc4Sql, misc5Sql, misc6Sql]; |
| 144 | + |
| 145 | + for (const sql of testCases) { |
| 146 | + await expectParseDeparse(sql, { pretty: false }); |
| 147 | + await expectParseDeparse(sql, { pretty: true }); |
| 148 | + } |
| 149 | + }); |
| 150 | +}); |
0 commit comments