examples/basic_usage.py annotated source
Back to indexBasic Usage Examples
This module demonstrates practical usage patterns for the sqlite-ast-parser library. It shows how to parse different SQL statement types and inspect the resulting AST nodes.
What You'll Learn
- How to call
parse_sql()and interpret results - How to navigate AST node structures
- How to handle parse errors gracefully
- How to use
tokenize_sql()for low-level analysis
Examples Covered
- DML Statements: SELECT, INSERT, UPDATE, DELETE
- DDL Statements: CREATE TABLE with constraints
- Advanced Features: CTEs (WITH clause), compound queries (UNION), window functions
- Transaction Control: BEGIN, SAVEPOINT, COMMIT, ROLLBACK
- Error Handling: How to catch and report parse errors
- Tokenization: Low-level token inspection
Each example function demonstrates a specific SQL feature and shows how to access the relevant parts of the AST.
2425"""26Basic Usage Examples for SQLite SQL Parser2728Demonstrates how to use the parser for various SQL statements.29"""3031import sys32sys.path.insert(0, '..')3334from sqlite_parser import parse_sql, tokenize_sql35from sqlite_parser.errors import ParseError36SELECT Statement Parsing
The SELECT statement is the most complex SQL statement type, supporting: - Column selection with expressions and aliases - Table joins (INNER, LEFT, RIGHT, CROSS, NATURAL) - WHERE clauses for filtering - GROUP BY and HAVING for aggregation - ORDER BY for sorting - LIMIT and OFFSET for pagination
The parser returns a SelectStatement node with:
- select_core: Contains columns, FROM, WHERE, GROUP BY
- order_by: ORDER BY clause
- limit: LIMIT expression
- offset: OFFSET expression
5253def example_select():54 """Parse a SELECT statement"""55 print("=" * 60)56 print("SELECT Statement Example")57 print("=" * 60)5859 sql = """60 SELECT u.id, u.name, COUNT(o.id) as order_count61 FROM users u62 LEFT JOIN orders o ON u.id = o.user_id63 WHERE u.age > 1864 GROUP BY u.id, u.name65 HAVING COUNT(o.id) > 066 ORDER BY order_count DESC67 LIMIT 1068 """6970 ast = parse_sql(sql)71 print(f"Parsed {len(ast)} statement(s)")72 print(f"\nStatement type: {type(ast[0]).__name__}")73 print(f"Has WHERE clause: {ast[0].select_core.where is not None}")74 print(f"Has GROUP BY: {ast[0].select_core.group_by is not None}")75 print(f"Has ORDER BY: {ast[0].order_by is not None}")76 print(f"Has LIMIT: {ast[0].limit is not None}")777879def example_insert():80 """Parse an INSERT statement"""81 print("\n" + "=" * 60)82 print("INSERT Statement Example")83 print("=" * 60)8485 sql = "INSERT INTO users (name, email, age) VALUES ('Alice', 'alice@example.com', 25)"8687 ast = parse_sql(sql)88 stmt = ast[0]8990 print(f"Statement type: {type(stmt).__name__}")91 print(f"Table: {stmt.table.parts}")92 print(f"Columns: {stmt.columns}")93 print(f"Number of rows: {len(stmt.values.rows) if stmt.values else 0}")949596def example_update():97 """Parse an UPDATE statement"""98 print("\n" + "=" * 60)99 print("UPDATE Statement Example")100 print("=" * 60)101102 sql = "UPDATE users SET age = age + 1, updated_at = CURRENT_TIMESTAMP WHERE id = 42"103104 ast = parse_sql(sql)105 stmt = ast[0]106107 print(f"Statement type: {type(stmt).__name__}")108 print(f"Table: {stmt.table.parts}")109 print(f"Number of assignments: {len(stmt.assignments)}")110 print(f"Has WHERE clause: {stmt.where is not None}")111112113def example_delete():114 """Parse a DELETE statement"""115 print("\n" + "=" * 60)116 print("DELETE Statement Example")117 print("=" * 60)118119 sql = "DELETE FROM users WHERE created_at < date('now', '-1 year')"120121 ast = parse_sql(sql)122 stmt = ast[0]123124 print(f"Statement type: {type(stmt).__name__}")125 print(f"Table: {stmt.table.parts}")126 print(f"Has WHERE clause: {stmt.where is not None}")127128129def example_create_table():130 """Parse a CREATE TABLE statement"""131 print("\n" + "=" * 60)132 print("CREATE TABLE Statement Example")133 print("=" * 60)134135 sql = """136 CREATE TABLE IF NOT EXISTS products (137 id INTEGER PRIMARY KEY AUTOINCREMENT,138 name TEXT NOT NULL,139 price REAL CHECK(price > 0),140 created_at TEXT DEFAULT CURRENT_TIMESTAMP,141 UNIQUE(name)142 )143 """144145 ast = parse_sql(sql)146 stmt = ast[0]147148 print(f"Statement type: {type(stmt).__name__}")149 print(f"Table: {stmt.table_name.parts}")150 print(f"IF NOT EXISTS: {stmt.if_not_exists}")151 print(f"Number of columns: {len(stmt.columns)}")152 print(f"Number of table constraints: {len(stmt.constraints)}")153154 for col in stmt.columns:155 print(f" Column: {col.name} {col.type_name or '(no type)'}")156157Common Table Expressions (CTEs)
WITH clauses (CTEs) allow defining temporary named result sets that can be referenced in the main query. They're especially powerful with the RECURSIVE keyword for hierarchical or iterative queries.
Recursive CTE Pattern
- Anchor member: Initial SELECT (e.g.,
SELECT 1) - UNION ALL: Combines anchor with recursive member
- Recursive member: SELECT that references the CTE itself
- Termination: WHERE clause stops recursion
The AST provides:
- with_clause.recursive: Boolean indicating if RECURSIVE keyword present
- with_clause.ctes: List of CTE definitions
- Each CTE has name, optional columns, and select query
175176def example_with_cte():177 """Parse a WITH clause (CTE)"""178 print("\n" + "=" * 60)179 print("WITH Clause (CTE) Example")180 print("=" * 60)181182 sql = """183 WITH RECURSIVE cnt(x) AS (184 SELECT 1185 UNION ALL186 SELECT x+1 FROM cnt WHERE x < 10187 )188 SELECT x FROM cnt189 """190191 ast = parse_sql(sql)192 stmt = ast[0]193194 print(f"Statement type: {type(stmt).__name__}")195 print(f"Has WITH clause: {stmt.with_clause is not None}")196 print(f"Recursive: {stmt.with_clause.recursive}")197 print(f"Number of CTEs: {len(stmt.with_clause.ctes)}")198199200def example_compound_select():201 """Parse a compound SELECT with UNION"""202 print("\n" + "=" * 60)203 print("Compound SELECT (UNION) Example")204 print("=" * 60)205206 sql = """207 SELECT name FROM employees208 UNION209 SELECT name FROM contractors210 ORDER BY name211 """212213 ast = parse_sql(sql)214 stmt = ast[0]215216 print(f"Statement type: {type(stmt).__name__}")217 print(f"Number of compound parts: {len(stmt.compound_selects)}")218 if stmt.compound_selects:219 print(f"Compound operator: {stmt.compound_selects[0][0].value}")220221222def example_transactions():223 """Parse transaction control statements"""224 print("\n" + "=" * 60)225 print("Transaction Control Example")226 print("=" * 60)227228 sqls = [229 "BEGIN TRANSACTION",230 "SAVEPOINT sp1",231 "UPDATE users SET balance = balance - 100 WHERE id = 1",232 "UPDATE users SET balance = balance + 100 WHERE id = 2",233 "RELEASE SAVEPOINT sp1",234 "COMMIT"235 ]236237 for sql in sqls:238 ast = parse_sql(sql)239 print(f"{sql:60} -> {type(ast[0]).__name__}")240241Error Handling
The parser raises typed exceptions that provide detailed context about parsing failures.
All parser errors inherit from ParseError, making them easy to catch.
Error Information
Parse errors include: - Message: Human-readable description of the problem - Position: Line and column numbers where the error occurred - Context: The problematic line with a caret (^) pointing to the error
Best Practices
- Catch
ParseErrorfor all parser failures - Display the error message to users (it includes context)
- Use
try-exceptwhen parsing user-provided SQL - Log errors for debugging and monitoring
260261def example_error_handling():262 """Demonstrate error handling"""263 print("\n" + "=" * 60)264 print("Error Handling Example")265 print("=" * 60)266267 invalid_sql = "SELECT FROM" # Missing column list268269 try:270 ast = parse_sql(invalid_sql)271 except ParseError as e:272 print(f"Caught parse error:")273 print(f" {e}")274Low-Level Tokenization
The tokenize_sql() function provides access to tokens without building an AST.
This is useful for:
- Syntax highlighting
- Building custom parsers
- Debugging lexer issues
- Token-based analysis (counting keywords, identifying patterns)
Each token contains:
- type: TokenType enum value (e.g., SELECT, IDENTIFIER, NUMBER)
- value: The actual text (e.g., "SELECT", "users", "42")
- position: Line and column where the token starts
288289def example_tokenization():290 """Show tokenization"""291 print("\n" + "=" * 60)292 print("Tokenization Example")293 print("=" * 60)294295 sql = "SELECT * FROM users WHERE age > 18"296297 tokens = tokenize_sql(sql)298 print(f"Tokenized into {len(tokens)} tokens:\n")299300 for token in tokens[:10]: # Show first 10 tokens301 print(f" {token.type.name:20} {repr(token.value):20} at {token.position}")302303304def example_complex_query():305 """Parse a complex real-world query"""306 print("\n" + "=" * 60)307 print("Complex Query Example")308 print("=" * 60)309310 sql = """311 WITH monthly_sales AS (312 SELECT313 strftime('%Y-%m', order_date) as month,314 product_id,315 SUM(quantity) as total_quantity,316 SUM(price * quantity) as total_revenue317 FROM orders318 WHERE order_date >= date('now', '-12 months')319 GROUP BY month, product_id320 )321 SELECT322 p.name as product_name,323 ms.month,324 ms.total_quantity,325 ms.total_revenue,326 ROUND(ms.total_revenue / ms.total_quantity, 2) as avg_price,327 LAG(ms.total_revenue) OVER (328 PARTITION BY ms.product_id329 ORDER BY ms.month330 ) as prev_month_revenue331 FROM monthly_sales ms332 JOIN products p ON ms.product_id = p.id333 WHERE ms.total_quantity > 10334 ORDER BY ms.month DESC, ms.total_revenue DESC335 LIMIT 100336 """337338 ast = parse_sql(sql)339 stmt = ast[0]340341 print(f"Successfully parsed complex query!")342 print(f" Has CTE: {stmt.with_clause is not None}")343 print(f" Number of result columns: {len(stmt.select_core.columns)}")344 print(f" Has window functions: (check for OVER clauses)")345 print(f" Has joins: (check FROM clause)")346347348def main():349 """Run all examples"""350 print("\n" + "#" * 60)351 print("# SQLite SQL Parser - Usage Examples")352 print("#" * 60)353354 example_select()355 example_insert()356 example_update()357 example_delete()358 example_create_table()359 example_with_cte()360 example_compound_select()361 example_transactions()362 example_complex_query()363 example_tokenization()364 example_error_handling()365366 print("\n" + "#" * 60)367 print("# All examples completed successfully!")368 print("#" * 60)369370371if __name__ == "__main__":372 main()373