Skip to content

Commit 83fc311

Browse files
committed
feat: view definitions
1 parent 53bb9fa commit 83fc311

7 files changed

Lines changed: 718 additions & 25 deletions

File tree

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,10 @@ See [docs/EXAMPLES.md](docs/EXAMPLES.md) for comprehensive usage examples.
8181
### DDL (Data Definition Language)
8282

8383
-**CREATE TABLE** - Columns, constraints, foreign keys, IF NOT EXISTS
84-
-**DROP** - TABLE/DATABASE/INDEX with IF EXISTS and CASCADE
84+
-**DROP** - TABLE/DATABASE/INDEX/VIEW with IF EXISTS and CASCADE
8585
-**ALTER TABLE** - ADD/DROP/MODIFY/CHANGE columns and constraints
8686
-**CREATE INDEX** - Simple and unique indexes with IF NOT EXISTS
87+
-**CREATE VIEW** - Views and materialized views with OR REPLACE, IF NOT EXISTS, WITH CHECK OPTION
8788

8889
### Transaction Control
8990

@@ -269,13 +270,13 @@ make dev-log
269270
- [x] Schema-aware parsing and validation
270271
- [x] Query execution plan analysis
271272
- [x] Stored procedures and functions
273+
- [x] View definitions (CREATE VIEW, CREATE MATERIALIZED VIEW)
272274
- [x] Performance benchmarking
273275
- [x] Dialect-specific optimizations
274276

275277
### 🚧 Planned Features
276278

277279
- [ ] Trigger parsing
278-
- [ ] View definitions (CREATE VIEW)
279280
- [ ] Real-time log monitoring
280281
- [ ] Integration with monitoring tools
281282

claude.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,12 +479,23 @@ go tool pprof cpu.prof
479479
- **10+ comprehensive tests** - All passing (MySQL, PostgreSQL, SQL Server)
480480
- **8 benchmarks** - 10-54μs procedure parsing
481481
- **650+ lines** - Complete procedure parser implementation
482+
- **View Support** ✅ 🆕
483+
- **CREATE VIEW** - Standard view creation with column lists
484+
- **CREATE OR REPLACE VIEW** - Idempotent view definitions
485+
- **CREATE VIEW IF NOT EXISTS** - Safe view creation
486+
- **CREATE MATERIALIZED VIEW** - PostgreSQL materialized views
487+
- **WITH CHECK OPTION** - Enforce view constraints
488+
- **DROP VIEW** - Simple and conditional (IF EXISTS) view dropping
489+
- **DROP MATERIALIZED VIEW** - PostgreSQL materialized view dropping
490+
- **Multi-dialect support** - MySQL (backticks), PostgreSQL (double quotes, MATERIALIZED), SQL Server (brackets)
491+
- **Schema-qualified views** - Support for schema.view_name syntax
492+
- **27 comprehensive tests** - All passing across all dialects
493+
- **231 lines of examples** - Complete examples in view_examples.sql
482494

483495
### 🚧 In Progress / Planned
484496
- [ ] Real-time log monitoring
485497
- [ ] Integration with monitoring tools
486498
- [ ] Control flow statements (IF, WHILE, FOR, LOOP)
487-
- [ ] Materialized views
488499
- [ ] Triggers
489500

490501
### ❌ Not Planned

examples/queries/view_examples.sql

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
-- SQL Parser Go - VIEW Examples
2+
-- Demonstrates CREATE VIEW and DROP VIEW parsing across different dialects
3+
4+
-- ============================================================================
5+
-- Simple CREATE VIEW
6+
-- ============================================================================
7+
8+
-- Basic view
9+
CREATE VIEW active_users AS
10+
SELECT id, name, email, created_at
11+
FROM users
12+
WHERE active = 1;
13+
14+
-- View with aggregate functions
15+
CREATE VIEW user_stats AS
16+
SELECT user_id, COUNT(*) as order_count, SUM(total) as total_spent
17+
FROM orders
18+
GROUP BY user_id;
19+
20+
-- ============================================================================
21+
-- CREATE VIEW with schema
22+
-- ============================================================================
23+
24+
CREATE VIEW myschema.customer_orders AS
25+
SELECT u.name, o.order_id, o.total, o.created_at
26+
FROM users u
27+
JOIN orders o ON u.id = o.user_id
28+
WHERE o.status = 'completed';
29+
30+
-- ============================================================================
31+
-- CREATE OR REPLACE VIEW
32+
-- ============================================================================
33+
34+
CREATE OR REPLACE VIEW high_value_orders AS
35+
SELECT * FROM orders
36+
WHERE total > 1000
37+
ORDER BY total DESC;
38+
39+
-- ============================================================================
40+
-- CREATE VIEW IF NOT EXISTS
41+
-- ============================================================================
42+
43+
CREATE VIEW IF NOT EXISTS recent_orders AS
44+
SELECT * FROM orders
45+
WHERE created_at > DATE_SUB(NOW(), INTERVAL 30 DAY);
46+
47+
-- ============================================================================
48+
-- CREATE VIEW with column list
49+
-- ============================================================================
50+
51+
CREATE VIEW user_summary (user_id, total_orders, total_amount, avg_amount) AS
52+
SELECT user_id, COUNT(*), SUM(total), AVG(total)
53+
FROM orders
54+
GROUP BY user_id;
55+
56+
-- ============================================================================
57+
-- CREATE VIEW with WITH CHECK OPTION
58+
-- ============================================================================
59+
60+
-- MySQL/PostgreSQL: WITH CHECK OPTION ensures inserts/updates through view respect WHERE clause
61+
CREATE VIEW premium_customers AS
62+
SELECT * FROM users
63+
WHERE subscription_type = 'premium'
64+
WITH CHECK OPTION;
65+
66+
-- ============================================================================
67+
-- CREATE MATERIALIZED VIEW (PostgreSQL)
68+
-- ============================================================================
69+
70+
-- Materialized views store the query result physically
71+
CREATE MATERIALIZED VIEW sales_summary AS
72+
SELECT product_id, SUM(amount) as total_sales, COUNT(*) as sale_count
73+
FROM sales
74+
GROUP BY product_id;
75+
76+
-- With IF NOT EXISTS
77+
CREATE MATERIALIZED VIEW IF NOT EXISTS monthly_revenue AS
78+
SELECT
79+
DATE_TRUNC('month', order_date) as month,
80+
SUM(total) as revenue,
81+
COUNT(*) as order_count
82+
FROM orders
83+
GROUP BY DATE_TRUNC('month', order_date);
84+
85+
-- ============================================================================
86+
-- CREATE OR REPLACE MATERIALIZED VIEW
87+
-- ============================================================================
88+
89+
CREATE OR REPLACE MATERIALIZED VIEW top_products AS
90+
SELECT product_id, product_name, SUM(quantity) as total_sold
91+
FROM order_items
92+
JOIN products ON order_items.product_id = products.id
93+
GROUP BY product_id, product_name
94+
ORDER BY total_sold DESC
95+
LIMIT 100;
96+
97+
-- ============================================================================
98+
-- Complex Views with Subqueries
99+
-- ============================================================================
100+
101+
CREATE VIEW above_average_salaries AS
102+
SELECT * FROM employees
103+
WHERE salary > (SELECT AVG(salary) FROM employees);
104+
105+
CREATE VIEW department_stats AS
106+
SELECT
107+
d.name as department,
108+
COUNT(e.id) as employee_count,
109+
AVG(e.salary) as avg_salary,
110+
(SELECT MAX(salary) FROM employees WHERE department_id = d.id) as max_salary
111+
FROM departments d
112+
LEFT JOIN employees e ON d.id = e.department_id
113+
GROUP BY d.id, d.name;
114+
115+
-- ============================================================================
116+
-- Views with CTEs (Common Table Expressions)
117+
-- ============================================================================
118+
119+
CREATE VIEW quarterly_sales AS
120+
WITH quarterly_data AS (
121+
SELECT
122+
EXTRACT(YEAR FROM order_date) as year,
123+
EXTRACT(QUARTER FROM order_date) as quarter,
124+
SUM(total) as revenue
125+
FROM orders
126+
GROUP BY year, quarter
127+
)
128+
SELECT * FROM quarterly_data
129+
ORDER BY year DESC, quarter DESC;
130+
131+
-- ============================================================================
132+
-- Views with Window Functions
133+
-- ============================================================================
134+
135+
CREATE VIEW employee_rankings AS
136+
SELECT
137+
id,
138+
name,
139+
department_id,
140+
salary,
141+
ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) as dept_rank,
142+
RANK() OVER (ORDER BY salary DESC) as company_rank
143+
FROM employees;
144+
145+
-- ============================================================================
146+
-- DROP VIEW
147+
-- ============================================================================
148+
149+
-- Simple DROP
150+
DROP VIEW active_users;
151+
152+
-- DROP IF EXISTS
153+
DROP VIEW IF EXISTS user_stats;
154+
155+
-- DROP with schema
156+
DROP VIEW myschema.customer_orders;
157+
158+
-- DROP with CASCADE (PostgreSQL - removes dependent views)
159+
DROP VIEW IF EXISTS sales_summary CASCADE;
160+
161+
-- ============================================================================
162+
-- DROP MATERIALIZED VIEW (PostgreSQL)
163+
-- ============================================================================
164+
165+
DROP MATERIALIZED VIEW sales_summary;
166+
167+
DROP MATERIALIZED VIEW IF EXISTS monthly_revenue;
168+
169+
-- ============================================================================
170+
-- Dialect-Specific Examples
171+
-- ============================================================================
172+
173+
-- MySQL with backticks
174+
CREATE VIEW `user_orders` AS
175+
SELECT `users`.`id`, `users`.`name`, COUNT(`orders`.`id`) as `order_count`
176+
FROM `users`
177+
LEFT JOIN `orders` ON `users`.`id` = `orders`.`user_id`
178+
GROUP BY `users`.`id`, `users`.`name`;
179+
180+
-- PostgreSQL with double quotes
181+
CREATE VIEW "user_orders" AS
182+
SELECT "users"."id", "users"."name", COUNT("orders"."id") as "order_count"
183+
FROM "users"
184+
LEFT JOIN "orders" ON "users"."id" = "orders"."user_id"
185+
GROUP BY "users"."id", "users"."name";
186+
187+
-- SQL Server with brackets
188+
CREATE VIEW [user_orders] AS
189+
SELECT [users].[id], [users].[name], COUNT([orders].[id]) as [order_count]
190+
FROM [users]
191+
LEFT JOIN [orders] ON [users].[id] = [orders].[user_id]
192+
GROUP BY [users].[id], [users].[name];
193+
194+
-- ============================================================================
195+
-- Real-World Examples
196+
-- ============================================================================
197+
198+
-- E-commerce: Active product catalog
199+
CREATE VIEW active_products AS
200+
SELECT p.id, p.name, p.price, c.name as category, i.quantity
201+
FROM products p
202+
JOIN categories c ON p.category_id = c.id
203+
JOIN inventory i ON p.id = i.product_id
204+
WHERE p.is_active = 1 AND i.quantity > 0;
205+
206+
-- Analytics: Daily order summary
207+
CREATE VIEW daily_order_summary AS
208+
SELECT
209+
DATE(created_at) as order_date,
210+
COUNT(*) as total_orders,
211+
SUM(total) as total_revenue,
212+
AVG(total) as avg_order_value,
213+
COUNT(DISTINCT user_id) as unique_customers
214+
FROM orders
215+
WHERE status = 'completed'
216+
GROUP BY DATE(created_at);
217+
218+
-- User management: User permissions view
219+
CREATE VIEW user_permissions AS
220+
SELECT
221+
u.id as user_id,
222+
u.username,
223+
u.email,
224+
r.name as role,
225+
GROUP_CONCAT(p.name) as permissions
226+
FROM users u
227+
JOIN user_roles ur ON u.id = ur.user_id
228+
JOIN roles r ON ur.role_id = r.id
229+
JOIN role_permissions rp ON r.id = rp.role_id
230+
JOIN permissions p ON rp.permission_id = p.id
231+
GROUP BY u.id, u.username, u.email, r.name;

pkg/lexer/tokens.go

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,28 @@ const (
4949
DROP
5050
ALTER
5151
TABLE
52-
WITH // CTE
53-
RECURSIVE // Recursive CTE
54-
OVER // Window functions
55-
PARTITION // Window functions
56-
ROWS // Window frame
57-
RANGE // Window frame
58-
UNBOUNDED // Window frame
59-
PRECEDING // Window frame
60-
FOLLOWING // Window frame
61-
CURRENT // Window frame
62-
ROW // Window frame
63-
INTERSECT // Set operations
64-
EXCEPT // Set operations
65-
CASE // CASE expression
66-
WHEN // CASE expression
67-
THEN // CASE expression
68-
ELSE // CASE expression
69-
END // CASE/CTE end
52+
VIEW // CREATE VIEW
53+
MATERIALIZED // MATERIALIZED VIEW (PostgreSQL)
54+
WITH // CTE or WITH CHECK OPTION
55+
RECURSIVE // Recursive CTE
56+
CHECK // WITH CHECK OPTION
57+
OPTION // WITH CHECK OPTION
58+
OVER // Window functions
59+
PARTITION // Window functions
60+
ROWS // Window frame
61+
RANGE // Window frame
62+
UNBOUNDED // Window frame
63+
PRECEDING // Window frame
64+
FOLLOWING // Window frame
65+
CURRENT // Window frame
66+
ROW // Window frame
67+
INTERSECT // Set operations
68+
EXCEPT // Set operations
69+
CASE // CASE expression
70+
WHEN // CASE expression
71+
THEN // CASE expression
72+
ELSE // CASE expression
73+
END // CASE/CTE end
7074

7175
// DDL Keywords
7276
PRIMARY // PRIMARY KEY
@@ -204,6 +208,10 @@ var keywords = map[string]TokenType{
204208
"DROP": DROP,
205209
"ALTER": ALTER,
206210
"TABLE": TABLE,
211+
"VIEW": VIEW,
212+
"MATERIALIZED": MATERIALIZED,
213+
"CHECK": CHECK,
214+
"OPTION": OPTION,
207215
"LIKE": LIKE,
208216
"BETWEEN": BETWEEN,
209217
"IS": IS,
@@ -398,6 +406,14 @@ func (tt TokenType) String() string {
398406
return "ALTER"
399407
case TABLE:
400408
return "TABLE"
409+
case VIEW:
410+
return "VIEW"
411+
case MATERIALIZED:
412+
return "MATERIALIZED"
413+
case CHECK:
414+
return "CHECK"
415+
case OPTION:
416+
return "OPTION"
401417
case ASSIGN:
402418
return "ASSIGN"
403419
case EQ:

0 commit comments

Comments
 (0)