GetQueryResult runs the query against a Postgres server to validate that the query is syntactically valid. It then tests the query against the Postgres parser and Postgres-Vitess AST converter to check the current level of support. It returns a string that may be inserted directly into a test source
(query string)
| 210 | // tests the query against the Postgres parser and Postgres-Vitess AST converter to check the current level of support. |
| 211 | // It returns a string that may be inserted directly into a test source file (two tabs are prefixed). |
| 212 | func GetQueryResult(query string) (string, error) { |
| 213 | var err error |
| 214 | ctx := context.Background() |
| 215 | if postgresVerificationConnection == nil { |
| 216 | connectionString := fmt.Sprintf("postgres://postgres:password@127.0.0.1:%d/", 5432) |
| 217 | postgresVerificationConnection, err = pgx.Connect(ctx, connectionString) |
| 218 | if err != nil { |
| 219 | return "", err |
| 220 | } |
| 221 | } |
| 222 | testQuery := fmt.Sprintf("DO $SYNTAX_CHECK$ BEGIN RETURN; %s; END; $SYNTAX_CHECK$;", query) |
| 223 | _, err = postgresVerificationConnection.Exec(ctx, testQuery) |
| 224 | if err != nil && strings.Contains(err.Error(), "syntax error") { |
| 225 | // We only care about syntax errors, as statements may rely on internal state, which is not what we're testing |
| 226 | // There are statements that will not execute inside our DO block due to how Postgres handles some queries, so |
| 227 | // to confirm that they're syntax errors, we'll run them outside the block. All such queries should be |
| 228 | // non-destructive, so this should be safe. All other queries will still return a syntax error. |
| 229 | _, err = postgresVerificationConnection.Exec(ctx, query) |
| 230 | // Run a ROLLBACK as some commands may put the connection (not the database) in a bad state |
| 231 | _, _ = postgresVerificationConnection.Exec(ctx, "ROLLBACK;") |
| 232 | if err != nil && strings.Contains(err.Error(), "syntax error") { |
| 233 | return "", fmt.Errorf("%s\n%s", err, query) |
| 234 | } |
| 235 | } |
| 236 | formattedQuery := strings.ReplaceAll(query, `"`, `\"`) |
| 237 | statements, err := parser.Parse(query) |
| 238 | if err != nil || len(statements) == 0 { |
| 239 | return fmt.Sprintf("\t\tUnimplemented(\"%s\"),\n", formattedQuery), nil |
| 240 | } |
| 241 | for _, statement := range statements { |
| 242 | vitessAST, err := func() (vitessAST sqlparser.Statement, err error) { |
| 243 | defer func() { |
| 244 | if recoverVal := recover(); recoverVal != nil { |
| 245 | vitessAST = nil |
| 246 | } |
| 247 | }() |
| 248 | return ast.Convert(statement) |
| 249 | }() |
| 250 | if err != nil || vitessAST == nil { |
| 251 | return fmt.Sprintf("\t\tParses(\"%s\"),\n", formattedQuery), nil |
| 252 | } |
| 253 | } |
| 254 | return fmt.Sprintf("\t\tConverts(\"%s\"),\n", formattedQuery), nil |
| 255 | } |