Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/protocol: Expose 32-bit parameter count support #243

Merged
merged 2 commits into from
May 2, 2023

Conversation

cole-miller
Copy link
Contributor

Closes #210

Signed-off-by: Cole Miller cole.miller@canonical.com

@cole-miller cole-miller force-pushed the more-params branch 6 times, most recently from 7579b55 to b130bdf Compare April 11, 2023 23:56
@cole-miller cole-miller marked this pull request as ready for review April 12, 2023 00:09
@cole-miller
Copy link
Contributor Author

Note that there's no attempt in this PR to figure out whether the remote server has the patch canonical/dqlite#407 or retry the request if it's refused due to an unrecognized schema version. If we care about the combination of new go-dqlite + old dqlite, I can add such code.

@freeekanayaka
Copy link
Contributor

Looks good to me. I'm agnostic about support for go-dqlite + old dqlite, although this change might hit some users.

One approach would be to use schema 0 if there are at most 256 parameters, and schema 1 if there are more. In that way the only difference with an old dqlite server would be that the old go-dqlite would throw a panic, and this new go-dqlite would get an error from the server. It would be a bit of work to implement though since the code doesn't quite support switching between schema versions.

@MathieuBordere
Copy link
Contributor

One approach would be to use schema 0 if there are at most 256 parameters, and schema 1 if there are more. In that way the only difference with an old dqlite server would be that the old go-dqlite would throw a panic, and this new go-dqlite would get an error from the server. It would be a bit of work to implement though since the code doesn't quite support switching between schema versions.

I would be in favor of that, using schema version 0 when parameter count is <= 255.

@cole-miller cole-miller force-pushed the more-params branch 4 times, most recently from fa989af to 7f0d6bc Compare April 12, 2023 14:59
@cole-miller
Copy link
Contributor Author

Implemented the schema-switching suggestion; this should be ready to go.

@@ -15,13 +15,13 @@ func DecodeFailure(response *Message) (code uint64, message string, err error) {
e := ErrRequest{}
e.Code = response.getUint64()
e.Description = response.getString()
err = e
Copy link
Member

@TheSignPainter98 TheSignPainter98 Apr 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing these whitespace changes come from the changes to schema.sh. Is it worth checking gofmt in the CI to avoid this problem coming back in future? I imagine this would probably be a separate PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

driver/driver.go Show resolved Hide resolved
}

// EncodeExec encodes a Exec request.
func EncodeExec(request *Message, db uint32, stmt uint32, values NamedValues) {
// EncodeExecV0 encodes a Exec request.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment doesn't highlight the difference between EncodeExecV0 and EncodeExecV1---the choice between each is unclear. As this comes up in quite a few places, is it worth putting a doc at the top of the file to establish the convention? Perhaps something like:

// Many functions in this file have two versions, distinguished
// by their suffix. Those named FooV0 handle queries which accept
// at most 255 arguments. Those named FooV1 handle queries with
// arbitrarily-many arguments but ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be possible to do this, but it would make schema.sh more complicated and these functions are all internal, so I don't think that documenting them in detail is urgent.

}

n := uint8(l) // N of params
if n == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow this early return on len(values) == 0 would feel more natural sooner. Would it be better to have it just before the l > 255 check?

The same would hold for Message.putNamedValues32

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it can happen earlier, because the point of the l > 255 check is to be sure that the n := uint8(l) cast is safe.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow, the > 255 check makes the != 0 check look independent to me; is this not possible?

l := len(values)
if l == 0 {
    return
}
if l > 255 {
    panic("...")
}

m.putUint8(uint8(l))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, sorry I misunderstood what you meant. Yes, your code looks correct, and I agree it feels slightly more natural.

// parameter count (new format).
func (m *Message) putNamedValues32(values NamedValues) {
l := len(values)
if l > (1<<32) - 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be clearer to use math.MaxUint32 here? Admittedly this would feel strange

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, seems reasonable

l := len(values)
if l > 255 {
// safeguard, should have been checked beforehand.
panic("too many parameters")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Admittedly this isn't a convention elsewhere (so likely not an issue for this PR) but would it be better to have "internal error: ..." to highlight that the user isn't at-fault for these panics?

internal/protocol/schema.sh Show resolved Hide resolved
@@ -252,7 +252,7 @@ func TestStmt_ExecTooManyParams(t *testing.T) {
values[i] = int64(1)
}
_, err = stmt.Exec(values)
require.Errorf(t, err, "too many parameters (256) max = 255")
require.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to not only check that no error occurred, but that the parameters where actually correctly handled, i.e. 256 rows were inserted, with values ranging from 1 to 255.

@@ -330,7 +330,7 @@ func TestStmt_QueryTooManyParams(t *testing.T) {
values[i] = int64(1)
}
_, err = stmt.Query(values)
require.Errorf(t, err, "too many parameters (256) max = 255")
require.NoError(t, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. It would be good to do something like:

  • insert 300 rows, with values ranging from 1 to 300
  • run the SELECT statement in the test
  • check that exactly 256 rows are returned, with the expected values

Perhaps it'd be worth using a slightly larger value than 256, say 260 or 270, just to test not only the boundary value but also beyond.

}

n := uint8(l) // N of params
if n == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it can happen earlier, because the point of the l > 255 check is to be sure that the n := uint8(l) cast is safe.

@@ -15,13 +15,13 @@ func DecodeFailure(response *Message) (code uint64, message string, err error) {
e := ErrRequest{}
e.Code = response.getUint64()
e.Description = response.getString()
err = e
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

internal/protocol/schema.sh Show resolved Hide resolved
@cole-miller cole-miller force-pushed the more-params branch 7 times, most recently from c8c5727 to 22b58b5 Compare April 14, 2023 20:43
Tabs only.

Signed-off-by: Cole Miller <cole.miller@canonical.com>
Signed-off-by: Cole Miller <cole.miller@canonical.com>
@cole-miller cole-miller merged commit 49cc09c into canonical:master May 2, 2023
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support for schema v1 requests with more parameters
4 participants