I'd like to see packets handled as a stream such that a call to e.g.
"nextPacket(reader)" returns the next packet on the reader, irrespective
of type. This would fit in perfectly with your "read header, read entire
packet" approach too.
If we could make something like this work, it would probably greatly
simplify stuff like stmt.getExecuteResult() in 0.2.x
The only thing I see stopping this is the apparent overlap between
things like Result and Field packets -- I might just be half asleep, but
it seems to me whether a packet is a Result or a Field seems to depend
upon the context (i.e. "have we received a field EOF yet?").
The approach that makes sense to me at a glance is to read all the field
packets immediately after the result packet and treat them as *part* of
the result packet. This would somewhat bastardize what is meant by a
"packet", but it would keep the packet processing stateless and thus
eliminate the need for all that big-switch, state-checking horribleness
in 0.2.
Implementation for nextPacket(r) would thus be something like:
1. Read header from r
2. Read full packet body from r
3. Does first byte indicate OK packet? Process & return OK packet.
4. Does first byte indicate error packet? Process & return error packet.
5. Does first byte indicate result packet? Process packet to determine
field count. Process fields to EOF packet. Return result packet.
6. <other packet types Sleepy Tom has forgotten>
N. Unknown packet -> error.
This also provides a natural solution for the store/use problem (my
"priority 2"): Upon seeing a result packet, code calling nextPacket()
can make the assumption that a set of rows are available and can call
e.g. nextRowPacket() all the way up to the next EOF. Whether that's done
all at once (ala mysql_store_result) or leave it to a result set to
return each row one-by-one (ala mysql_use_result) is left to the caller.
If we were to go this way, "Result" might best be an interface so that
we can provide the two different implementations seamlessly.
Happy to provide example code if that helps, but right now I need some
sleep. :) Happy to hear any input.
Cheers,
Tom
On 11/01/11 00:14, Phil Bayfield wrote:
> This stems from the original issue thread on Github:
> https://github.com/Philio/GoMySQL/issues#issue/36
>
> First steps I've taken is to clean up the instantiation and connection
> code. Seehttps://github.com/Philio/GoMySQL/blob/dev/mysql.go
>
> The MySQL struct is now called Client, plus we have 3 new functions,
> NewClient, DialTCP, DialUnix. The idea here is to simplify and fall in
> line with the "Go way of thinking", which I think this does quite
> nicely.
>
> I've also added a bunch of empty functions which will include some
> additional functionality (really it's wrapping what can be done with
> queries or existing functions in a nice way) e.g. transactions, using
> prepare to directly prepare a statement from an SQL statement with a
> single method call.
>
> Main thing that needs discussion, finalising a plan on is the best way
> to handle packets.
>
> Possible ways to improve/build on the simple retrieve and process
> methodology of 0.2:
>
> 1. Separate the read/write/parse process in a way that we would read
> the whole packet, attempt to process it then act, see original post on
> Github.
>
> 2. Adopt the C API method, seehttps://github.com/Philio/GoMySQL/issues#issue/36/comment/657628
>
> 3. Some other way, perhaps we could also incorporate multi-threading/
> go routines to improve efficiency.
--
Tom Lee
http://tomlee.co
ph 0450 112 893
Regarding packet processing:
I'd like to see packets handled as a stream such that a call to e.g. "nextPacket(reader)" returns the next packet on the reader, irrespective of type. This would fit in perfectly with your "read header, read entire packet" approach too.
If we could make something like this work, it would probably greatly simplify stuff like stmt.getExecuteResult() in 0.2.x
The only thing I see stopping this is the apparent overlap between things like Result and Field packets -- I might just be half asleep, but it seems to me whether a packet is a Result or a Field seems to depend upon the context (i.e. "have we received a field EOF yet?").
The approach that makes sense to me at a glance is to read all the field packets immediately after the result packet and treat them as *part* of the result packet. This would somewhat bastardize what is meant by a "packet", but it would keep the packet processing stateless and thus eliminate the need for all that big-switch, state-checking horribleness in 0.2.
Implementation for nextPacket(r) would thus be something like:
1. Read header from r
2. Read full packet body from r
3. Does first byte indicate OK packet? Process & return OK packet.
4. Does first byte indicate error packet? Process & return error packet.
5. Does first byte indicate result packet? Process packet to determine field count. Process fields to EOF packet. Return result packet.
6. <other packet types Sleepy Tom has forgotten>
N. Unknown packet -> error.
This also provides a natural solution for the store/use problem (my "priority 2"): Upon seeing a result packet, code calling nextPacket() can make the assumption that a set of rows are available and can call e.g. nextRowPacket() all the way up to the next EOF. Whether that's done all at once (ala mysql_store_result) or leave it to a result set to return each row one-by-one (ala mysql_use_result) is left to the caller. If we were to go this way, "Result" might best be an interface so that we can provide the two different implementations seamlessly.
This is the work flow for the C API: mysql_query("select * from blah") This returns an integer success value. mysql_store_result() -> Retrieves the entire result and returns a pointer to it. (What we do now automatically.) mysql_use_result() -> Also returns a pointer to the result also but does not retrieve it yet, each row is retrieved one by one as and when requested by the client. So we could have something like: func (client *Client) Query(sql string) (resCode int, err os.Error) {}
func (c *Client) Query(sql string) os.ErrorSeems more idiomatic to simply look at the error than rely on a status code and an error. What do you think?
Here we would send the query command to the server, read the first result packet (and probably store it for later) and return a response to indicate that the query was successful, resCode could in fact probably just be a boolean success value. func (client *Client) StoreResult() (res *Result, err os.Error) {} Here we would retrieve and process the entire result set and return a pointer to it. func (client *Client) UseResult() (res *Result, err os.Error) {} Here we would retrieve the first few params up until a field EOF packet is received, but leave the row data for now. Row retrieval would be initiated every time we request a row from the result.
func (client *Client) FreeResult() {} Remove the current result from memory, if there are unreceived packets "on the wire" we need to remove them.
_, err := client.Query("SELECT * FROM blah")
if err != nil {
// ...
}
res, err := client.UseResult() // or client.StoreResult()
if err != nil {
// ...
}
defer client.FreeResult()
// ...
err := client.Query("SELECT * FROM blah")Again, looks reasonable -- but maybe still some room for improvement.
if err != nil {
// ...
}
res, err := client.UseResult() // or client.StoreResult()
if err != nil {
// ...
}
defer res.Free()
// ...
res, err := client.Query("SELECT * FROM blah")Looks nice, but might complicate the implementation due to implementation details being slightly different depending on whether you call Use() or Store().
if err != nil {
// handle error
}
defer res.Free()
res.Use() // if the user doesn't explicitly call Use() or Store() prior to requesting a row, default to something sane
// ...
I'd suggest that resCode wasn't necessary i.e.:
On 11/01/11 18:34, Phil Bayfield wrote:This is the work flow for the C API: mysql_query("select * from blah") This returns an integer success value. mysql_store_result() -> Retrieves the entire result and returns a pointer to it. (What we do now automatically.) mysql_use_result() -> Also returns a pointer to the result also but does not retrieve it yet, each row is retrieved one by one as and when requested by the client. So we could have something like: func (client *Client) Query(sql string) (resCode int, err os.Error) {}
func (c *Client) Query(sql string) os.ErrorSeems more idiomatic to simply look at the error than rely on a status code and an error. What do you think?
Option C
Move responsibility for all Result related stuff into the Result itself once Query has been called.
res, err := client.Query("SELECT * FROM blah")Looks nice, but might complicate the implementation due to implementation details being slightly different depending on whether you call Use() or Store().
if err != nil {
// handle error
}
defer res.Free()
res.Use() // if the user doesn't explicitly call Use() or Store() prior to requesting a row, default to something sane
// ...
Lastly, I have a slight concern that it's possible to forget to call UseResult()/StoreResult() for both Option A and B, which might leave junk on the wire. We may need to weave some magic to discard "old" result sets prior to sending & receiving additional packets.
--
You received this message because you are subscribed to the Google Groups "GoMySQL" group.
To post to this group, send email to gom...@googlegroups.com.
To unsubscribe from this group, send email to gomysql+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/gomysql?hl=en.
We'll also need for this...
func (client *Client) MoreResults() os.Error {} // Check if more results are availablefunc (client *Client) NextResult() os.Error {} // Move to the next result set (this explains it well: http://dev.mysql.com/doc/refman/5.5/en/mysql-next-result.html)
So you would do something like...
res := client.UseResult()....res.Free()
if client.MoreResults() {client.NextResult()res = client.UseResult()}
-- Tom Lee http://tomlee.co ph 0450 112 893
Two threads can't send a query to the MySQL server at the same time on the same connection. In particular, you have to ensure that between calls to mysql_query()
and mysql_store_result()
no other thread is using the same connection.
Many threads can access different result sets that are retrieved with mysql_store_result()
.
If you use mysql_use_result()
, you must ensure that no other thread is using the same connection until the result set is closed. However, it really is best for threaded clients that share the same connection to usemysql_store_result()
.
If you want to use multiple threads on the same connection, you must have a mutex lock around your pair ofmysql_query()
and mysql_store_result()
calls. Once mysql_store_result()
is ready, the lock can be released and other threads may query the same connection.
If you use POSIX threads, you can use pthread_mutex_lock()
and pthread_mutex_unlock()
to establish and release a mutex lock.
--
You received this message because you are subscribed to the Google Groups "GoMySQL" group.
To post to this group, send email to gom...@googlegroups.com.
To unsubscribe from this group, send email to gomysql+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/gomysql?hl=en.
Regarding Close() vs. Free() ... this might keep the API consistent for
connections & results. I know I've used APIs in the past where there's
confusion about whether I should be calling close, free, disconnect or
dispose :) Not too bothered either way though in the case of results.
A broader question in my mind that's come up as part of this discussion
is whether we want to essentially "Goify" the C API or if we try to nice
it up.
I think 0.2 leaned towards the latter, but I'm +0 for either ... I'll
just be wrapping it all up in go-dbi anyway ;)
Cheers,
Tom
> --
> You received this message because you are subscribed to the Google
> Groups "GoMySQL" group.
> To post to this group, send email to gom...@googlegroups.com.
> To unsubscribe from this group, send email to
> gomysql+u...@googlegroups.com.
> For more options, visit this group at
> http://groups.google.com/group/gomysql?hl=en.
I don't mind of whatever the name is/will be :-)But I like the mimic of go-psql, to be more precise it's option D (mixed b and c):res, err = client.Query("SELECT * FROM table")if err!= nil {// do stuff}res.Close() //Free() whatever
I think it's worth to mention that go-pgsql have this "cursor" behavior. In GoMySQL AFAIR you fetch everything that comes in the response. Go-pgsql gives "client programmer" chance to decide if he want to get (means parse and convert to a proper types) all results and rows or just one or none. I like it that way. For me as a ORM programmer it means quite big efficiency bonus in some cases.
--
You received this message because you are subscribed to the Google Groups "GoMySQL" group.
To post to this group, send email to gom...@googlegroups.com.
To unsubscribe from this group, send email to gomysql+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/gomysql?hl=en.
That's an interesting point: if I understand correctly, you're suggesting we should always treat query results as the Use() case, and leave users to decide whether they want to pull everything back at once?