Skip to content

Commit 316ea56

Browse files
committed
Squashed commit of the following:
commit 0e119f8 Author: jszwec <[email protected]> Date: Sun Jan 15 13:41:56 2017 -0500 Multi-Results: review suggestion - get rid of a recursive call commit 8b062e7 Author: jszwec <[email protected]> Date: Sun Jan 15 11:50:06 2017 -0500 Multi-Results: fix hanging rows.Close() * rows.Close() would hang on readUntilEOF if some results were ignored before calling NextResultSet() commit 0ac9483 Author: jszwec <[email protected]> Date: Sun Jan 15 00:50:13 2017 -0500 Multi-Results improvements - support for binary protocol - support statements returning no results - remove emptyRows commit c823aa0 Author: jszwec <[email protected]> Date: Sun Jan 8 20:46:50 2017 -0500 Add Multi-Results support Fixes go-sql-driver#420
1 parent d64be06 commit 316ea56

File tree

6 files changed

+377
-86
lines changed

6 files changed

+377
-86
lines changed

AUTHORS

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Hanno Braun <mail at hannobraun.com>
2525
Henri Yandell <flamefew at gmail.com>
2626
Hirotaka Yamamoto <ymmt2005 at gmail.com>
2727
INADA Naoki <songofacandy at gmail.com>
28+
Jacek Szwec <szwec.jacek at gmail.com>
2829
James Harr <james.harr at gmail.com>
2930
Jian Zhen <zhenjl at gmail.com>
3031
Joshua Prunier <joshua.prunier at gmail.com>

connection.go

+24-10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ package mysql
1010

1111
import (
1212
"database/sql/driver"
13+
"io"
1314
"net"
1415
"strconv"
1516
"strings"
@@ -289,22 +290,29 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
289290
// Internal function to execute commands
290291
func (mc *mysqlConn) exec(query string) error {
291292
// Send command
292-
err := mc.writeCommandPacketStr(comQuery, query)
293-
if err != nil {
293+
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
294294
return err
295295
}
296296

297297
// Read Result
298298
resLen, err := mc.readResultSetHeaderPacket()
299-
if err == nil && resLen > 0 {
300-
if err = mc.readUntilEOF(); err != nil {
299+
if err != nil {
300+
return err
301+
}
302+
303+
if resLen > 0 {
304+
// columns
305+
if err := mc.readUntilEOF(); err != nil {
301306
return err
302307
}
303308

304-
err = mc.readUntilEOF()
309+
// rows
310+
if err := mc.readUntilEOF(); err != nil {
311+
return err
312+
}
305313
}
306314

307-
return err
315+
return mc.discardResults()
308316
}
309317

310318
func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
@@ -335,11 +343,17 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
335343
rows.mc = mc
336344

337345
if resLen == 0 {
338-
// no columns, no more data
339-
return emptyRows{}, nil
346+
rows.rs.done = true
347+
348+
switch err := rows.NextResultSet(); err {
349+
case nil, io.EOF:
350+
return rows, nil
351+
default:
352+
return nil, err
353+
}
340354
}
341355
// Columns
342-
rows.columns, err = mc.readColumns(resLen)
356+
rows.rs.columns, err = mc.readColumns(resLen)
343357
return rows, err
344358
}
345359
}
@@ -359,7 +373,7 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
359373
if err == nil {
360374
rows := new(textRows)
361375
rows.mc = mc
362-
rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
376+
rows.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
363377

364378
if resLen > 0 {
365379
// Columns

driver_go18_test.go

+190
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
// +build go1.8
2+
3+
package mysql
4+
5+
import (
6+
"database/sql"
7+
"fmt"
8+
"reflect"
9+
"testing"
10+
)
11+
12+
func TestMultiResultSet(t *testing.T) {
13+
type result struct {
14+
values [][]int
15+
columns []string
16+
}
17+
18+
// checkRows is a helper test function to validate rows containing 3 result
19+
// sets with specific values and columns. The basic query would look like this:
20+
//
21+
// SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
22+
// SELECT 0 UNION SELECT 1;
23+
// SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;
24+
//
25+
// to distinguish test cases the first string argument is put in front of
26+
// every error or fatal message.
27+
checkRows := func(desc string, rows *sql.Rows, dbt *DBTest) {
28+
expected := []result{
29+
{
30+
values: [][]int{{1, 2}, {3, 4}},
31+
columns: []string{"col1", "col2"},
32+
},
33+
{
34+
values: [][]int{{1, 2, 3}, {4, 5, 6}},
35+
columns: []string{"col1", "col2", "col3"},
36+
},
37+
}
38+
39+
var res1 result
40+
for rows.Next() {
41+
var res [2]int
42+
if err := rows.Scan(&res[0], &res[1]); err != nil {
43+
dbt.Fatal(err)
44+
}
45+
res1.values = append(res1.values, res[:])
46+
}
47+
48+
cols, err := rows.Columns()
49+
if err != nil {
50+
dbt.Fatal(desc, err)
51+
}
52+
res1.columns = cols
53+
54+
if !reflect.DeepEqual(expected[0], res1) {
55+
dbt.Error(desc, "want =", expected[0], "got =", res1)
56+
}
57+
58+
if !rows.NextResultSet() {
59+
dbt.Fatal(desc, "expected next result set")
60+
}
61+
62+
// ignoring one result set
63+
64+
if !rows.NextResultSet() {
65+
dbt.Fatal(desc, "expected next result set")
66+
}
67+
68+
var res2 result
69+
cols, err = rows.Columns()
70+
if err != nil {
71+
dbt.Fatal(desc, err)
72+
}
73+
res2.columns = cols
74+
75+
for rows.Next() {
76+
var res [3]int
77+
if err := rows.Scan(&res[0], &res[1], &res[2]); err != nil {
78+
dbt.Fatal(desc, err)
79+
}
80+
res2.values = append(res2.values, res[:])
81+
}
82+
83+
if !reflect.DeepEqual(expected[1], res2) {
84+
dbt.Error(desc, "want =", expected[1], "got =", res2)
85+
}
86+
87+
if rows.NextResultSet() {
88+
dbt.Error(desc, "unexpected next result set")
89+
}
90+
91+
if err := rows.Err(); err != nil {
92+
dbt.Error(desc, err)
93+
}
94+
}
95+
96+
runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
97+
rows := dbt.mustQuery(`DO 1;
98+
SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
99+
DO 1;
100+
SELECT 0 UNION SELECT 1;
101+
SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;`)
102+
defer rows.Close()
103+
checkRows("query: ", rows, dbt)
104+
})
105+
106+
runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
107+
queries := []string{
108+
`
109+
DROP PROCEDURE IF EXISTS test_mrss;
110+
CREATE PROCEDURE test_mrss()
111+
BEGIN
112+
DO 1;
113+
SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
114+
DO 1;
115+
SELECT 0 UNION SELECT 1;
116+
SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;
117+
END
118+
`,
119+
`
120+
DROP PROCEDURE IF EXISTS test_mrss;
121+
CREATE PROCEDURE test_mrss()
122+
BEGIN
123+
SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
124+
SELECT 0 UNION SELECT 1;
125+
SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;
126+
END
127+
`,
128+
}
129+
130+
defer dbt.mustExec("DROP PROCEDURE IF EXISTS test_mrss")
131+
132+
for i, query := range queries {
133+
dbt.mustExec(query)
134+
135+
stmt, err := dbt.db.Prepare("CALL test_mrss()")
136+
if err != nil {
137+
dbt.Fatalf("%v (i=%d)", err, i)
138+
}
139+
defer stmt.Close()
140+
141+
for j := 0; j < 2; j++ {
142+
rows, err := stmt.Query()
143+
if err != nil {
144+
dbt.Fatalf("%v (i=%d) (j=%d)", err, i, j)
145+
}
146+
checkRows(fmt.Sprintf("prepared stmt query (i=%d) (j=%d): ", i, j), rows, dbt)
147+
}
148+
}
149+
})
150+
}
151+
152+
func TestMultiResultSetNoSelect(t *testing.T) {
153+
runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
154+
rows := dbt.mustQuery("DO 1; DO 2;")
155+
defer rows.Close()
156+
157+
if rows.Next() {
158+
dbt.Error("unexpected row")
159+
}
160+
161+
if rows.NextResultSet() {
162+
dbt.Error("unexpected next result set")
163+
}
164+
165+
if err := rows.Err(); err != nil {
166+
dbt.Error("expected nil; got ", err)
167+
}
168+
})
169+
}
170+
171+
// tests if rows are set in a proper state if some results were ignored before
172+
// calling rows.NextResultSet.
173+
func TestSkipResults(t *testing.T) {
174+
runTests(t, dsn, func(dbt *DBTest) {
175+
rows := dbt.mustQuery("SELECT 1, 2")
176+
defer rows.Close()
177+
178+
if !rows.Next() {
179+
dbt.Error("expected row")
180+
}
181+
182+
if rows.NextResultSet() {
183+
dbt.Error("unexpected next result set")
184+
}
185+
186+
if err := rows.Err(); err != nil {
187+
dbt.Error("expected nil; got ", err)
188+
}
189+
})
190+
}

0 commit comments

Comments
 (0)