Skip to content

Commit e93ec67

Browse files
committed
Smallish extended query logic refactoring
This is preparatory work for the binary_parameters patch, which needs to reuse parts of this logic.
1 parent 8b4ab4b commit e93ec67

File tree

1 file changed

+135
-114
lines changed

1 file changed

+135
-114
lines changed

conn.go

+135-114
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ func decideColumnFormats(colTyps []oid.Oid, forceText bool) (colFmts []format, c
633633
}
634634
}
635635

636-
func (cn *conn) prepareTo(q, stmtName string) (_ *stmt, err error) {
636+
func (cn *conn) prepareTo(q, stmtName string) *stmt {
637637
st := &stmt{cn: cn, name: stmtName}
638638

639639
b := cn.writeBuf('P')
@@ -648,33 +648,11 @@ func (cn *conn) prepareTo(q, stmtName string) (_ *stmt, err error) {
648648
b.next('S')
649649
cn.send(b)
650650

651-
for {
652-
t, r := cn.recv1()
653-
switch t {
654-
case '1':
655-
case 't':
656-
nparams := r.int16()
657-
st.paramTyps = make([]oid.Oid, nparams)
658-
659-
for i := range st.paramTyps {
660-
st.paramTyps[i] = r.oid()
661-
}
662-
case 'T':
663-
st.colNames, st.colTyps = parseStatementRowDescribe(r)
664-
st.colFmts, st.colFmtData = decideColumnFormats(st.colTyps, cn.disablePreparedBinaryResult)
665-
case 'n':
666-
// no data
667-
st.colFmtData = colFmtDataAllText
668-
case 'Z':
669-
cn.processReadyForQuery(r)
670-
return st, err
671-
case 'E':
672-
err = parseError(r)
673-
default:
674-
cn.bad = true
675-
errorf("unexpected describe rows response: %q", t)
676-
}
677-
}
651+
cn.readParseResponse()
652+
st.paramTyps, st.colNames, st.colTyps = cn.readStatementDescribeResponse()
653+
st.colFmts, st.colFmtData = decideColumnFormats(st.colTyps, cn.disablePreparedBinaryResult)
654+
cn.readReadyForQuery()
655+
return st
678656
}
679657

680658
func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) {
@@ -686,7 +664,7 @@ func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) {
686664
if len(q) >= 4 && strings.EqualFold(q[:4], "COPY") {
687665
return cn.prepareCopyIn(q)
688666
}
689-
return cn.prepareTo(q, cn.gname())
667+
return cn.prepareTo(q, cn.gname()), nil
690668
}
691669

692670
func (cn *conn) Close() (err error) {
@@ -718,11 +696,7 @@ func (cn *conn) Query(query string, args []driver.Value) (_ driver.Rows, err err
718696
return cn.simpleQuery(query)
719697
}
720698

721-
st, err := cn.prepareTo(query, "")
722-
if err != nil {
723-
panic(err)
724-
}
725-
699+
st := cn.prepareTo(query, "")
726700
st.exec(args)
727701
return &rows{
728702
cn: cn,
@@ -750,10 +724,7 @@ func (cn *conn) Exec(query string, args []driver.Value) (_ driver.Result, err er
750724
// Use the unnamed statement to defer planning until bind
751725
// time, or else value-based selectivity estimates cannot be
752726
// used.
753-
st, err := cn.prepareTo(query, "")
754-
if err != nil {
755-
panic(err)
756-
}
727+
st := cn.prepareTo(query, "")
757728

758729
r, err := st.Exec(args)
759730
if err != nil {
@@ -1214,25 +1185,8 @@ func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) {
12141185
defer st.cn.errRecover(&err)
12151186

12161187
st.exec(v)
1217-
1218-
for {
1219-
t, r := st.cn.recv1()
1220-
switch t {
1221-
case 'E':
1222-
err = parseError(r)
1223-
case 'C':
1224-
res, _ = st.cn.parseComplete(r.string())
1225-
case 'Z':
1226-
st.cn.processReadyForQuery(r)
1227-
// done
1228-
return
1229-
case 'T', 'D', 'I':
1230-
// ignore any results
1231-
default:
1232-
st.cn.bad = true
1233-
errorf("unknown exec response: %q", t)
1234-
}
1235-
}
1188+
res, _, err = st.cn.readExecuteResponse("simple query")
1189+
return res, err
12361190
}
12371191

12381192
func (st *stmt) exec(v []driver.Value) {
@@ -1243,16 +1197,17 @@ func (st *stmt) exec(v []driver.Value) {
12431197
errorf("got %d parameters but the statement requires %d", len(v), len(st.paramTyps))
12441198
}
12451199

1246-
w := st.cn.writeBuf('B')
1247-
w.byte(0)
1200+
cn := st.cn
1201+
w := cn.writeBuf('B')
1202+
w.byte(0) // unnamed portal
12481203
w.string(st.name)
12491204
w.int16(0)
12501205
w.int16(len(v))
12511206
for i, x := range v {
12521207
if x == nil {
12531208
w.int32(-1)
12541209
} else {
1255-
b := encode(&st.cn.parameterStatus, x, st.paramTyps[i])
1210+
b := encode(&cn.parameterStatus, x, st.paramTyps[i])
12561211
w.int32(len(b))
12571212
w.bytes(b)
12581213
}
@@ -1264,62 +1219,11 @@ func (st *stmt) exec(v []driver.Value) {
12641219
w.int32(0)
12651220

12661221
w.next('S')
1267-
st.cn.send(w)
1222+
cn.send(w)
12681223

1269-
var err error
1270-
for {
1271-
t, r := st.cn.recv1()
1272-
switch t {
1273-
case 'E':
1274-
err = parseError(r)
1275-
case '2':
1276-
if err != nil {
1277-
panic(err)
1278-
}
1279-
goto workaround
1280-
case 'Z':
1281-
st.cn.processReadyForQuery(r)
1282-
if err != nil {
1283-
panic(err)
1284-
}
1285-
return
1286-
default:
1287-
st.cn.bad = true
1288-
errorf("unexpected bind response: %q", t)
1289-
}
1290-
}
1224+
cn.readBindResponse()
1225+
cn.postExecuteWorkaround()
12911226

1292-
// Work around a bug in sql.DB.QueryRow: in Go 1.2 and earlier it ignores
1293-
// any errors from rows.Next, which masks errors that happened during the
1294-
// execution of the query. To avoid the problem in common cases, we wait
1295-
// here for one more message from the database. If it's not an error the
1296-
// query will likely succeed (or perhaps has already, if it's a
1297-
// CommandComplete), so we push the message into the conn struct; recv1
1298-
// will return it as the next message for rows.Next or rows.Close.
1299-
// However, if it's an error, we wait until ReadyForQuery and then return
1300-
// the error to our caller.
1301-
workaround:
1302-
for {
1303-
t, r := st.cn.recv1()
1304-
switch t {
1305-
case 'E':
1306-
err = parseError(r)
1307-
case 'C', 'D', 'I':
1308-
// the query didn't fail, but we can't process this message
1309-
st.cn.saveMessage(t, r)
1310-
return
1311-
case 'Z':
1312-
if err == nil {
1313-
st.cn.bad = true
1314-
errorf("unexpected ReadyForQuery during extended query execution")
1315-
}
1316-
st.cn.processReadyForQuery(r)
1317-
panic(err)
1318-
default:
1319-
st.cn.bad = true
1320-
errorf("unexpected message during query execution: %q", t)
1321-
}
1322-
}
13231227
}
13241228

13251229
func (st *stmt) NumInput() int {
@@ -1500,6 +1404,123 @@ func (c *conn) processReadyForQuery(r *readBuf) {
15001404
c.txnStatus = transactionStatus(r.byte())
15011405
}
15021406

1407+
func (cn *conn) readReadyForQuery() {
1408+
t, r := cn.recv1()
1409+
switch t {
1410+
case 'Z':
1411+
cn.processReadyForQuery(r)
1412+
return
1413+
default:
1414+
cn.bad = true
1415+
errorf("unexpected message %q; expected ReadyForQuery", t)
1416+
}
1417+
}
1418+
1419+
func (cn *conn) readParseResponse() {
1420+
t, r := cn.recv1()
1421+
switch t {
1422+
case '1':
1423+
return
1424+
case 'E':
1425+
err := parseError(r)
1426+
cn.readReadyForQuery()
1427+
panic(err)
1428+
default:
1429+
cn.bad = true
1430+
errorf("unexpected Parse response %q", t)
1431+
}
1432+
}
1433+
1434+
func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames []string, colTyps []oid.Oid) {
1435+
for {
1436+
t, r := cn.recv1()
1437+
switch t {
1438+
case 't':
1439+
nparams := r.int16()
1440+
paramTyps = make([]oid.Oid, nparams)
1441+
for i := range paramTyps {
1442+
paramTyps[i] = r.oid()
1443+
}
1444+
case 'n':
1445+
return paramTyps, nil, nil
1446+
case 'T':
1447+
colNames, colTyps = parseStatementRowDescribe(r)
1448+
return paramTyps, colNames, colTyps
1449+
case 'E':
1450+
err := parseError(r)
1451+
cn.readReadyForQuery()
1452+
panic(err)
1453+
default:
1454+
cn.bad = true
1455+
errorf("unexpected Describe statement response %q", t)
1456+
}
1457+
}
1458+
}
1459+
1460+
func (cn *conn) readBindResponse() {
1461+
t, r := cn.recv1()
1462+
switch t {
1463+
case '2':
1464+
return
1465+
case 'E':
1466+
err := parseError(r)
1467+
cn.readReadyForQuery()
1468+
panic(err)
1469+
default:
1470+
cn.bad = true
1471+
errorf("unexpected Bind response %q", t)
1472+
}
1473+
}
1474+
1475+
func (cn *conn) postExecuteWorkaround() {
1476+
// Work around a bug in sql.DB.QueryRow: in Go 1.2 and earlier it ignores
1477+
// any errors from rows.Next, which masks errors that happened during the
1478+
// execution of the query. To avoid the problem in common cases, we wait
1479+
// here for one more message from the database. If it's not an error the
1480+
// query will likely succeed (or perhaps has already, if it's a
1481+
// CommandComplete), so we push the message into the conn struct; recv1
1482+
// will return it as the next message for rows.Next or rows.Close.
1483+
// However, if it's an error, we wait until ReadyForQuery and then return
1484+
// the error to our caller.
1485+
for {
1486+
t, r := cn.recv1()
1487+
switch t {
1488+
case 'E':
1489+
err := parseError(r)
1490+
cn.readReadyForQuery()
1491+
panic(err)
1492+
case 'C', 'D', 'I':
1493+
// the query didn't fail, but we can't process this message
1494+
cn.saveMessage(t, r)
1495+
return
1496+
default:
1497+
cn.bad = true
1498+
errorf("unexpected message during extended query execution: %q", t)
1499+
}
1500+
}
1501+
}
1502+
1503+
// Only for Exec(), since we ignore the returned data
1504+
func (cn *conn) readExecuteResponse(protocolState string) (res driver.Result, commandTag string, err error) {
1505+
for {
1506+
t, r := cn.recv1()
1507+
switch t {
1508+
case 'C':
1509+
res, commandTag = cn.parseComplete(r.string())
1510+
case 'Z':
1511+
cn.processReadyForQuery(r)
1512+
return res, commandTag, err
1513+
case 'E':
1514+
err = parseError(r)
1515+
case 'T', 'D', 'I':
1516+
// ignore any results
1517+
default:
1518+
cn.bad = true
1519+
errorf("unknown %s response: %q", protocolState, t)
1520+
}
1521+
}
1522+
}
1523+
15031524
func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []oid.Oid) {
15041525
n := r.int16()
15051526
colNames = make([]string, n)

0 commit comments

Comments
 (0)