queryRetryStopOnError runs the query and stops on encountering errors. The error is both present in the returned QueryResult and error, the caller decides what to do.
( ctx context.Context, stores *store.Store, user *store.UserMessage, instance *store.InstanceMessage, database *store.DatabaseMessage, driver db.Driver, conn *sql.Conn, statement string, queryContext db.QueryContext, licenseService *enterprise.LicenseService, optionalAccessCheck accessCheckFunc, schemaSyncer *schemasync.Syncer, )
| 849 | // queryRetryStopOnError runs the query and stops on encountering errors. |
| 850 | // The error is both present in the returned QueryResult and error, the caller decides what to do. |
| 851 | func queryRetryStopOnError( |
| 852 | ctx context.Context, |
| 853 | stores *store.Store, |
| 854 | user *store.UserMessage, |
| 855 | instance *store.InstanceMessage, |
| 856 | database *store.DatabaseMessage, |
| 857 | driver db.Driver, |
| 858 | conn *sql.Conn, |
| 859 | statement string, |
| 860 | queryContext db.QueryContext, |
| 861 | licenseService *enterprise.LicenseService, |
| 862 | optionalAccessCheck accessCheckFunc, |
| 863 | schemaSyncer *schemasync.Syncer, |
| 864 | ) ([]*v1pb.QueryResult, []*parserbase.QuerySpan, time.Duration, error) { |
| 865 | // MSSQL requires statements to be executed as a batch to preserve variable scope. |
| 866 | // For example, "DECLARE @meta int = 1; SELECT @meta" must be sent as one batch, |
| 867 | // otherwise the second statement will fail with "variable not defined" error. |
| 868 | // Split for span analysis, but pass original statement for batch execution. |
| 869 | if instance.Metadata.GetEngine() == storepb.Engine_MSSQL { |
| 870 | statements, err := parserbase.SplitMultiSQL(instance.Metadata.GetEngine(), statement) |
| 871 | if err != nil { |
| 872 | return nil, nil, 0, err |
| 873 | } |
| 874 | return queryRetry(ctx, stores, user, instance, database, driver, conn, statements, statement, queryContext, licenseService, optionalAccessCheck, schemaSyncer, hasMultipleStatements(statements)) |
| 875 | } |
| 876 | |
| 877 | // Split the statement into individual SQLs |
| 878 | statements, err := parserbase.SplitMultiSQL(instance.Metadata.GetEngine(), statement) |
| 879 | if err != nil { |
| 880 | // Engines without splitter support (MongoDB, Redis, Elasticsearch) fall back to |
| 881 | // treating the entire statement as a single unit. These engines also don't have |
| 882 | // GetQuerySpan support, so queryRetry will return nil spans (old behavior). |
| 883 | return queryRetry(ctx, stores, user, instance, database, driver, conn, []parserbase.Statement{{Text: statement}}, statement, queryContext, licenseService, optionalAccessCheck, schemaSyncer, false) |
| 884 | } |
| 885 | |
| 886 | // A multi-statement batch can change the session schema mid-batch (e.g. SET search_path), |
| 887 | // which the per-statement access check below cannot track; flag it so DML/DDL falls back |
| 888 | // to the database-level check. SUP-222. |
| 889 | multiStatement := hasMultipleStatements(statements) |
| 890 | |
| 891 | var allResults []*v1pb.QueryResult |
| 892 | var allSpans []*parserbase.QuerySpan |
| 893 | var totalDuration time.Duration |
| 894 | |
| 895 | for _, stmt := range statements { |
| 896 | // Skip empty statements |
| 897 | if stmt.Empty { |
| 898 | continue |
| 899 | } |
| 900 | |
| 901 | results, spans, duration, err := queryRetry(ctx, stores, user, instance, database, driver, conn, []parserbase.Statement{stmt}, stmt.Text, queryContext, licenseService, optionalAccessCheck, schemaSyncer, multiStatement) |
| 902 | totalDuration += duration |
| 903 | |
| 904 | if err != nil { |
| 905 | allResults = append(allResults, &v1pb.QueryResult{ |
| 906 | Error: err.Error(), |
| 907 | Statement: stmt.Text, |
| 908 | }) |
no test coverage detected