client: add ExecQueryRelayLocalInfile for proxy-friendly LOAD DATA LOCAL INFILE#1164
Conversation
…CAL INFILE ExecQueryRelayLocalInfile is a COM_QUERY variant that handles the 0xfb LOCAL INFILE response via a caller-supplied callback instead of reading a local file. The callback receives the full 0xfb request packet and is responsible for forwarding the request to the application client, relaying file-data packets to this connection via WritePacket, and signalling EOF with an empty packet. A companion helper cleanupLocalInfileAfterRelayError re-synchronizes the connection if relayFile fails mid-transfer by sending the empty EOF packet and consuming the server's OK/ERR response. This is the proxy-friendly counterpart to the local-file reading in client/auth.go. It enables transparent LOAD DATA LOCAL INFILE relay in proxy implementations without filesystem access. Closes go-mysql-org#1161 Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
Code Review
This pull request introduces cleanupLocalInfileAfterRelayError and ExecQueryRelayLocalInfile to client/conn.go to support relaying LOCAL INFILE requests and handling connection resynchronization on errors. The review feedback correctly identifies several potential runtime panics where the code accesses the first element of a packet slice (resp, data, and data2) without first verifying that the slice is not empty. It is recommended to add length checks before accessing these slices and to consistently trace errors using errors.Trace.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Guard against empty packets before indexing response headers and wrap cleanupLocalInfileAfterRelayError I/O errors with errors.Trace for consistency with the rest of the client package. Co-authored-by: Cursor <cursoragent@cursor.com>
Cover direct OK/ERR responses, successful relay with file chunks, and relay failure with connection resynchronization using net.Pipe. Co-authored-by: Cursor <cursoragent@cursor.com>
COM_QUERY and LocalInFile_HEADER are already typed as byte, so the explicit byte() casts are redundant and fail the unconvert linter check. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Oh for previous PR codex found some problems: [P1] server/resp.go (line 31): when CLIENT_SESSION_TRACK is negotiated but SERVER_SESSION_STATE_CHANGED is not set, writeOK still appends a zero-length session-state block via AppendOKSessionTrackSuffix. In the MySQL OK-packet format, that field is only present when the status bit is set; otherwise the packet should stop after the length-encoded info string. This makes auth OK / normal OK packets malformed for strict clients. [P2] server/server_conf.go (line 137): the new SetCapability / UnsetCapability API lets callers flip any server capability bit, but some of those bits are not independently configurable. A concrete example is CLIENT_SSL: enabling it on a server without tlsConfig will advertise TLS in the greeting and then fail the SSLRequest path during handshake. The setter should either validate a safe whitelist or tie each flag to the underlying server config. Can you create a new PR to fix it? |
sure -> #1167 |
Make the callback signature func(localInfileRequestPayload []byte, c *Conn) error so callers can see how to write file data back via c.WritePacket. Document success vs error-path LOCAL INFILE termination in godoc. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the streaming WritePacket callback with a collect-then-send model: relayFile returns a reader over the complete file content and the library sends LOCAL INFILE data packets. On callback error only the empty terminator is sent (no partial file data upstream). Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace cancelLocalInfileRequest and the split success/error tail paths with sendLocalInfileContentAndAwaitResult. prepareLocalInfileReader validates the full reader (io.Seeker discard+seek-back, or io.ReadAll) before any file data is sent upstream, preventing partial transfers. Co-authored-by: Cursor <cursoragent@cursor.com>
Reuse a single write buffer, send a defensive terminator on mid-stream errors, pass filename-only to relayFile, expand godoc Notes, and consolidate tests into subtests including StreamWriteError. Co-authored-by: Cursor <cursoragent@cursor.com>
…CAL INFILE (go-mysql-org#1164) * client: add ExecQueryRelayLocalInfile for proxy-friendly LOAD DATA LOCAL INFILE ExecQueryRelayLocalInfile is a COM_QUERY variant that handles the 0xfb LOCAL INFILE response via a caller-supplied callback instead of reading a local file. The callback receives the full 0xfb request packet and is responsible for forwarding the request to the application client, relaying file-data packets to this connection via WritePacket, and signalling EOF with an empty packet. A companion helper cleanupLocalInfileAfterRelayError re-synchronizes the connection if relayFile fails mid-transfer by sending the empty EOF packet and consuming the server's OK/ERR response. This is the proxy-friendly counterpart to the local-file reading in client/auth.go. It enables transparent LOAD DATA LOCAL INFILE relay in proxy implementations without filesystem access. Closes go-mysql-org#1161 Co-authored-by: Cursor <cursoragent@cursor.com> * client: add empty-packet guards and errors.Trace in LOCAL INFILE relay Guard against empty packets before indexing response headers and wrap cleanupLocalInfileAfterRelayError I/O errors with errors.Trace for consistency with the rest of the client package. Co-authored-by: Cursor <cursoragent@cursor.com> * test: add unit tests for ExecQueryRelayLocalInfile Cover direct OK/ERR responses, successful relay with file chunks, and relay failure with connection resynchronization using net.Pipe. Co-authored-by: Cursor <cursoragent@cursor.com> * test: remove unnecessary byte() conversions flagged by unconvert linter COM_QUERY and LocalInFile_HEADER are already typed as byte, so the explicit byte() casts are redundant and fail the unconvert linter check. Co-authored-by: Cursor <cursoragent@cursor.com> * client: pass *Conn to ExecQueryRelayLocalInfile relay callback Make the callback signature func(localInfileRequestPayload []byte, c *Conn) error so callers can see how to write file data back via c.WritePacket. Document success vs error-path LOCAL INFILE termination in godoc. Co-authored-by: Cursor <cursoragent@cursor.com> * client: return io.Reader from ExecQueryRelayLocalInfile callback Replace the streaming WritePacket callback with a collect-then-send model: relayFile returns a reader over the complete file content and the library sends LOCAL INFILE data packets. On callback error only the empty terminator is sent (no partial file data upstream). Co-authored-by: Cursor <cursoragent@cursor.com> * client: add usage example to ExecQueryRelayLocalInfile godoc Co-authored-by: Cursor <cursoragent@cursor.com> * client: unify LOCAL INFILE send/await and validate reader before send Replace cancelLocalInfileRequest and the split success/error tail paths with sendLocalInfileContentAndAwaitResult. prepareLocalInfileReader validates the full reader (io.Seeker discard+seek-back, or io.ReadAll) before any file data is sent upstream, preventing partial transfers. Co-authored-by: Cursor <cursoragent@cursor.com> * client: address Jul 7 LOCAL INFILE review feedback Reuse a single write buffer, send a defensive terminator on mid-stream errors, pass filename-only to relayFile, expand godoc Notes, and consolidate tests into subtests including StreamWriteError. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: lance6716 <lance6716@gmail.com>
Summary
Add
(*Conn).ExecQueryRelayLocalInfileand the companion helpercleanupLocalInfileAfterRelayErrortoclient/conn.go.Closes #1161
Motivation
The existing
Execute/execpath (and the LOCAL INFILE handling inclient/auth.go) reads the file from the local filesystem. In a proxy scenario the file lives on the end-user's machine, not on the proxy. The proxy must relay the0xfbrequest and the subsequent file-data packets between the upstream MySQL server and the downstream client.There is no API today to intercept the
0xfb LOCAL INFILEresponse and hand off the transfer to a callback. This PR fills that gap.New API
cleanupLocalInfileAfterRelayErroris unexported; it sends the empty EOF packet and reads the server's OK/ERR to bring the connection back to a usable state whenrelayFilefails mid-transfer.Notes
CLIENT_LOCAL_FILESto be set on both the server side (see server: add CLIENT_LOCAL_FILES to default server capabilities #1163) and the client side (viaSetCapability).Made with Cursor