ydb-platform / ydb-go-sdk

Pure Go native and database/sql driver for YDB
https://ydb.tech
Apache License 2.0
132 stars 69 forks source link

bug: Reading rows from the second `ResultSet` leads to unexpected error instead of `io.EOF` #1310

Closed pelageech closed 1 week ago

pelageech commented 1 week ago

Bug Report

YDB GO SDK version: 3.74.4

Environment MacOS Sonoma 14.2.1 amd64

Current behavior: Prints all the numbers and returns an error:

query: non-retryable error occurred on attempt No.1 (idempotent=true): set 1: get next row: received part with result set index = 1, current result set index = 0: critical violation of the logic - wrong result set index at `github.com/ydb-platform/ydb-go-sdk/v3/internal/query.(*resultSet).nextRow(result_set.go:149)` at `github.com/ydb-platform/ydb-go-sdk/v3/internal/query.do.func1(client.go:126)` at `github.com/ydb-platform/ydb-go-sdk/v3/internal/query.(*poolStub).With.func2(client.go:83)` at `github.com/ydb-platform/ydb-go-sdk/v3/retry.Retry.func1(retry.go:264)` at `github.com/ydb-platform/ydb-go-sdk/v3/retry.opWithRecover(retry.go:411)` at `github.com/ydb-platform/ydb-go-sdk/v3/retry.RetryWithResult(retry.go:356)` at `github.com/ydb-platform/ydb-go-sdk/v3/retry.Retry(retry.go:270)` at `github.com/ydb-platform/ydb-go-sdk/v3/internal/query.(*poolStub).With(client.go:89)` at `github.com/ydb-platform/ydb-go-sdk/v3/internal/query.do(client.go:134)`

Expected behavior: Just numbers without error.

Steps to reproduce: Run the code.

Related code:

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    driver, err := ydb.Open(ctx,
        "grpc://localhost:2136/local",
    )
    if err != nil {
        log.Fatal("open", err)
    }
    defer driver.Close(ctx)

    _, err = driver.Query().Execute(ctx, `CREATE TABLE IF NOT EXISTS example3 ( key UInt32, value UInt32, PRIMARY KEY (key) );`)
    if err != nil {
        log.Fatal("execute 1: ", err)
    }
    _, err = driver.Query().Execute(ctx, ` INSERT INTO example3 (key, value) VALUES (1, 2), (3, 4), (5, 6);`)
    if err != nil {
        log.Fatal("execute 2: ", err)
    }

    var i, j uint32
    err = driver.Query().Do( // Do retry operation on errors with best effort
        ctx, // context manage exiting from Do
        func(ctx context.Context, s query.Session) (err error) { // retry operation
            _, res, err := s.Execute(ctx, `SELECT CAST(42 AS Uint32); SELECT * FROM example3`)
            if err != nil {
                return err
            }

            set, err := res.NextResultSet(ctx)
            if err != nil {
                return fmt.Errorf("set 0: get next result set: %w", err)
            }
            for row, err := set.NextRow(ctx); !errors.Is(err, io.EOF); row, err = set.NextRow(ctx) {
                if err != nil {
                    return fmt.Errorf("set 0: get next row: %w", err)
                }
                if err := row.Scan(&i); err != nil {
                    return fmt.Errorf("set 0: scan row: %w", err)
                }
                fmt.Println(i)
            }

            set, err = res.NextResultSet(ctx)
            if err != nil {
                return err
            }

            for row, err := set.NextRow(ctx); !errors.Is(err, io.EOF); row, err = set.NextRow(ctx) {
                if err != nil { // after reading for the fourth time, an error is thrown instead of EOF
                    return fmt.Errorf("set 1: get next row: %w", err)
                }

                if err := row.Scan(&i, &j); err != nil {
                    return fmt.Errorf("set 1: scan row: %w", err)
                }
                fmt.Println(i, j)
            }
            _, err = res.NextResultSet(ctx)
            if !errors.Is(err, io.EOF) {
                return fmt.Errorf("get next result set: %w", err)
            }

            if res.Err() != nil {
                return fmt.Errorf("res.Err() = %w", res.Err())
            }
            return nil
        }, query.WithIdempotent(),
    )
    if err != nil {
        log.Fatal("query: ", err)
    }
}

Other information: When I get the second result set, it's read properly but it throws an error above instead of EOF after reading all the rows.

neyguvj commented 1 week ago

fixed in 3.74.5