Execute SQL content that may contain multiple statements. This function splits the SQL by semicolons and executes each statement individually, which is more reliable than passing everything to execute() at once. Handles DO blocks ($$..$$) and other multi-line constructs. Args:
(conn: asyncpg.Connection, sql_content: str)
| 67 | |
| 68 | |
| 69 | async def execute_sql_file(conn: asyncpg.Connection, sql_content: str) -> None: |
| 70 | """ |
| 71 | Execute SQL content that may contain multiple statements. |
| 72 | |
| 73 | This function splits the SQL by semicolons and executes each statement |
| 74 | individually, which is more reliable than passing everything to execute() |
| 75 | at once. Handles DO blocks ($$..$$) and other multi-line constructs. |
| 76 | |
| 77 | Args: |
| 78 | conn: Database connection |
| 79 | sql_content: SQL content to execute |
| 80 | """ |
| 81 | statements = [] |
| 82 | current_statement = [] |
| 83 | in_dollar_quote = False # Track if we're inside a $$ ... $$ block |
| 84 | dollar_quote_tag = None # Store the dollar quote tag (e.g., $$ or $BODY$) |
| 85 | |
| 86 | for line in sql_content.split('\n'): |
| 87 | stripped = line.strip() |
| 88 | |
| 89 | # Skip empty lines and comments (unless we're in a dollar quote block) |
| 90 | if not in_dollar_quote and (not stripped or stripped.startswith('--')): |
| 91 | continue |
| 92 | |
| 93 | current_statement.append(line) |
| 94 | |
| 95 | # Check for dollar quotes ($$, $tag$, etc.) |
| 96 | # These are used in DO blocks, functions, etc. and can contain semicolons |
| 97 | for i, char in enumerate(stripped): |
| 98 | if char == '$': |
| 99 | # Try to match a dollar quote tag |
| 100 | end_idx = stripped.find('$', i + 1) |
| 101 | if end_idx != -1: |
| 102 | tag = stripped[i:end_idx + 1] |
| 103 | if in_dollar_quote: |
| 104 | if tag == dollar_quote_tag: |
| 105 | in_dollar_quote = False |
| 106 | dollar_quote_tag = None |
| 107 | else: |
| 108 | in_dollar_quote = True |
| 109 | dollar_quote_tag = tag |
| 110 | break |
| 111 | |
| 112 | # Only treat semicolon as statement terminator if not in dollar quote |
| 113 | if not in_dollar_quote and stripped.endswith(';'): |
| 114 | stmt = '\n'.join(current_statement).strip() |
| 115 | if stmt and not stmt.startswith('--'): |
| 116 | statements.append(stmt) |
| 117 | current_statement = [] |
| 118 | |
| 119 | # Execute each statement |
| 120 | for stmt in statements: |
| 121 | if stmt.strip(): |
| 122 | try: |
| 123 | await conn.execute(stmt) |
| 124 | except Exception as e: |
| 125 | # Print first 200 chars of failing statement for debugging |
| 126 | print(f"Failed to execute statement: {stmt[:200]}...") |