Read a SQL file and process \i (include) directives. Args: file_path: Path to the SQL file tests_dir: Tests directory for resolving relative includes Returns: SQL content with includes resolved
(file_path: Path, tests_dir: Path)
| 28 | from typing import AsyncGenerator |
| 29 | |
| 30 | def read_sql_file(file_path: Path, tests_dir: Path) -> str: |
| 31 | """ |
| 32 | Read a SQL file and process \i (include) directives. |
| 33 | |
| 34 | Args: |
| 35 | file_path: Path to the SQL file |
| 36 | tests_dir: Tests directory for resolving relative includes |
| 37 | |
| 38 | Returns: |
| 39 | SQL content with includes resolved |
| 40 | """ |
| 41 | if not file_path.exists(): |
| 42 | raise FileNotFoundError(f"SQL file not found: {file_path}") |
| 43 | |
| 44 | with open(file_path, 'r') as f: |
| 45 | lines = f.readlines() |
| 46 | |
| 47 | result = [] |
| 48 | for line in lines: |
| 49 | stripped = line.strip() |
| 50 | |
| 51 | # Skip psql meta-commands except \i (include) |
| 52 | if stripped.startswith('\\'): |
| 53 | if stripped.startswith('\\i '): |
| 54 | # Extract the include file path |
| 55 | include_path = stripped[3:].strip() |
| 56 | # Resolve relative to tests_dir (paths are like "sql/tpch/customer.sql") |
| 57 | include_file = tests_dir / include_path |
| 58 | # Recursively read the included file |
| 59 | included_content = read_sql_file(include_file, tests_dir) |
| 60 | result.append(included_content) |
| 61 | # Skip other psql commands like \echo, \gset, \if, \endif, \quit, etc. |
| 62 | continue |
| 63 | |
| 64 | result.append(line) |
| 65 | |
| 66 | return ''.join(result) |
| 67 | |
| 68 | |
| 69 | async def execute_sql_file(conn: asyncpg.Connection, sql_content: str) -> None: |