parsePgquery parses SQL using the pgquery parser
(sql string)
| 98 | |
| 99 | // parsePgquery parses SQL using the pgquery parser |
| 100 | func (p PostgresParser) parsePgquery(sql string) ([]database.DDLStatement, error) { |
| 101 | result, err := go_pgquery.Parse(sql) |
| 102 | if err != nil { |
| 103 | return nil, err |
| 104 | } |
| 105 | |
| 106 | var statements []database.DDLStatement |
| 107 | for _, rawStmt := range result.Stmts { |
| 108 | var ddl string |
| 109 | if rawStmt.StmtLen == 0 { |
| 110 | ddl = sql[rawStmt.StmtLocation:] |
| 111 | } else { |
| 112 | ddl = sql[rawStmt.StmtLocation : rawStmt.StmtLocation+rawStmt.StmtLen] |
| 113 | } |
| 114 | ddl = strings.TrimSpace(ddl) |
| 115 | |
| 116 | // Attempt to convert pgquery AST to our generic AST format |
| 117 | stmt, err := p.parseStmt(rawStmt.Stmt) |
| 118 | if err != nil { |
| 119 | // Check if this is a validation error (should not fallback) |
| 120 | if _, ok := err.(validationError); ok { |
| 121 | return nil, err |
| 122 | } |
| 123 | |
| 124 | // In Auto mode, if we can't convert the pgquery AST to generic AST, |
| 125 | // try parsing this individual statement with the generic parser directly. |
| 126 | // This handles cases where the generic parser can parse the statement |
| 127 | // but we haven't implemented the AST conversion from pgquery yet. |
| 128 | var stmts []database.DDLStatement |
| 129 | if p.mode == PsqldefParserModeAuto { |
| 130 | slog.Debug("pgquery AST conversion failed, trying generic parser for this statement", "error", err.Error()) |
| 131 | stmts, err = p.parser.Parse(ddl) |
| 132 | } |
| 133 | if err != nil { |
| 134 | return nil, err |
| 135 | } |
| 136 | |
| 137 | statements = append(statements, stmts...) |
| 138 | continue |
| 139 | } |
| 140 | |
| 141 | // In Auto mode, an Ignore means pgquery has no AST conversion for this node |
| 142 | // type (e.g. CREATE FUNCTION). The generic parser does support these, so retry |
| 143 | // the single statement before keeping the Ignore. Without this the desired |
| 144 | // schema silently loses the statement while the current schema (introspected |
| 145 | // from the DB) keeps it, producing a destructive diff such as a spurious |
| 146 | // DROP FUNCTION. Pgquery-only mode keeps the Ignore unchanged (PR #1116 intent). |
| 147 | if _, isIgnore := stmt.(*parser.Ignore); isIgnore && p.mode == PsqldefParserModeAuto { |
| 148 | stmts, retryErr := p.parser.Parse(ddl) |
| 149 | if retryErr == nil { |
| 150 | statements = append(statements, stmts...) |
| 151 | continue |
| 152 | } |
| 153 | slog.Debug("generic parser also failed for Ignore-d statement, keeping Ignore", "error", retryErr.Error()) |
| 154 | } |
| 155 | |
| 156 | statements = append(statements, database.DDLStatement{ |
| 157 | DDL: ddl, |