-
Notifications
You must be signed in to change notification settings - Fork 68
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
Conversation
7579b55
to
b130bdf
Compare
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. |
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 |
I would be in favor of that, using schema version 0 when parameter count is <= 255. |
fa989af
to
7f0d6bc
Compare
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.
} | ||
|
||
// EncodeExec encodes a Exec request. | ||
func EncodeExec(request *Message, db uint32, stmt uint32, values NamedValues) { | ||
// EncodeExecV0 encodes a Exec request. |
There was a problem hiding this comment.
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 ...
There was a problem hiding this comment.
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.
internal/protocol/message.go
Outdated
} | ||
|
||
n := uint8(l) // N of params | ||
if n == 0 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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))
There was a problem hiding this comment.
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.
internal/protocol/message.go
Outdated
// parameter count (new format). | ||
func (m *Message) putNamedValues32(values NamedValues) { | ||
l := len(values) | ||
if l > (1<<32) - 1 { |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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") |
There was a problem hiding this comment.
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?
@@ -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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
internal/protocol/message.go
Outdated
} | ||
|
||
n := uint8(l) // N of params | ||
if n == 0 { |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch.
c8c5727
to
22b58b5
Compare
Tabs only. Signed-off-by: Cole Miller <cole.miller@canonical.com>
Signed-off-by: Cole Miller <cole.miller@canonical.com>
Closes #210
Signed-off-by: Cole Miller cole.miller@canonical.com