1package mssql
2
3import (
4	"context"
5	"encoding/binary"
6	"errors"
7	"fmt"
8	"io"
9	"net"
10	"strconv"
11	"strings"
12)
13
14//go:generate stringer -type token
15
16type token byte
17
18// token ids
19const (
20	tokenReturnStatus token = 121 // 0x79
21	tokenColMetadata  token = 129 // 0x81
22	tokenOrder        token = 169 // 0xA9
23	tokenError        token = 170 // 0xAA
24	tokenInfo         token = 171 // 0xAB
25	tokenReturnValue  token = 0xAC
26	tokenLoginAck     token = 173 // 0xad
27	tokenRow          token = 209 // 0xd1
28	tokenNbcRow       token = 210 // 0xd2
29	tokenEnvChange    token = 227 // 0xE3
30	tokenSSPI         token = 237 // 0xED
31	tokenDone         token = 253 // 0xFD
32	tokenDoneProc     token = 254
33	tokenDoneInProc   token = 255
34)
35
36// done flags
37// https://msdn.microsoft.com/en-us/library/dd340421.aspx
38const (
39	doneFinal    = 0
40	doneMore     = 1
41	doneError    = 2
42	doneInxact   = 4
43	doneCount    = 0x10
44	doneAttn     = 0x20
45	doneSrvError = 0x100
46)
47
48// ENVCHANGE types
49// http://msdn.microsoft.com/en-us/library/dd303449.aspx
50const (
51	envTypDatabase           = 1
52	envTypLanguage           = 2
53	envTypCharset            = 3
54	envTypPacketSize         = 4
55	envSortId                = 5
56	envSortFlags             = 6
57	envSqlCollation          = 7
58	envTypBeginTran          = 8
59	envTypCommitTran         = 9
60	envTypRollbackTran       = 10
61	envEnlistDTC             = 11
62	envDefectTran            = 12
63	envDatabaseMirrorPartner = 13
64	envPromoteTran           = 15
65	envTranMgrAddr           = 16
66	envTranEnded             = 17
67	envResetConnAck          = 18
68	envStartedInstanceName   = 19
69	envRouting               = 20
70)
71
72// COLMETADATA flags
73// https://msdn.microsoft.com/en-us/library/dd357363.aspx
74const (
75	colFlagNullable = 1
76	// TODO implement more flags
77)
78
79// interface for all tokens
80type tokenStruct interface{}
81
82type orderStruct struct {
83	ColIds []uint16
84}
85
86type doneStruct struct {
87	Status   uint16
88	CurCmd   uint16
89	RowCount uint64
90	errors   []Error
91}
92
93func (d doneStruct) isError() bool {
94	return d.Status&doneError != 0 || len(d.errors) > 0
95}
96
97func (d doneStruct) getError() Error {
98	if len(d.errors) > 0 {
99		return d.errors[len(d.errors)-1]
100	} else {
101		return Error{Message: "Request failed but didn't provide reason"}
102	}
103}
104
105type doneInProcStruct doneStruct
106
107var doneFlags2str = map[uint16]string{
108	doneFinal:    "final",
109	doneMore:     "more",
110	doneError:    "error",
111	doneInxact:   "inxact",
112	doneCount:    "count",
113	doneAttn:     "attn",
114	doneSrvError: "srverror",
115}
116
117func doneFlags2Str(flags uint16) string {
118	strs := make([]string, 0, len(doneFlags2str))
119	for flag, tag := range doneFlags2str {
120		if flags&flag != 0 {
121			strs = append(strs, tag)
122		}
123	}
124	return strings.Join(strs, "|")
125}
126
127// ENVCHANGE stream
128// http://msdn.microsoft.com/en-us/library/dd303449.aspx
129func processEnvChg(sess *tdsSession) {
130	size := sess.buf.uint16()
131	r := &io.LimitedReader{R: sess.buf, N: int64(size)}
132	for {
133		var err error
134		var envtype uint8
135		err = binary.Read(r, binary.LittleEndian, &envtype)
136		if err == io.EOF {
137			return
138		}
139		if err != nil {
140			badStreamPanic(err)
141		}
142		switch envtype {
143		case envTypDatabase:
144			sess.database, err = readBVarChar(r)
145			if err != nil {
146				badStreamPanic(err)
147			}
148			_, err = readBVarChar(r)
149			if err != nil {
150				badStreamPanic(err)
151			}
152		case envTypLanguage:
153			// currently ignored
154			// new value
155			if _, err = readBVarChar(r); err != nil {
156				badStreamPanic(err)
157			}
158			// old value
159			if _, err = readBVarChar(r); err != nil {
160				badStreamPanic(err)
161			}
162		case envTypCharset:
163			// currently ignored
164			// new value
165			if _, err = readBVarChar(r); err != nil {
166				badStreamPanic(err)
167			}
168			// old value
169			if _, err = readBVarChar(r); err != nil {
170				badStreamPanic(err)
171			}
172		case envTypPacketSize:
173			packetsize, err := readBVarChar(r)
174			if err != nil {
175				badStreamPanic(err)
176			}
177			_, err = readBVarChar(r)
178			if err != nil {
179				badStreamPanic(err)
180			}
181			packetsizei, err := strconv.Atoi(packetsize)
182			if err != nil {
183				badStreamPanicf("Invalid Packet size value returned from server (%s): %s", packetsize, err.Error())
184			}
185			sess.buf.ResizeBuffer(packetsizei)
186		case envSortId:
187			// currently ignored
188			// new value
189			if _, err = readBVarChar(r); err != nil {
190				badStreamPanic(err)
191			}
192			// old value, should be 0
193			if _, err = readBVarChar(r); err != nil {
194				badStreamPanic(err)
195			}
196		case envSortFlags:
197			// currently ignored
198			// new value
199			if _, err = readBVarChar(r); err != nil {
200				badStreamPanic(err)
201			}
202			// old value, should be 0
203			if _, err = readBVarChar(r); err != nil {
204				badStreamPanic(err)
205			}
206		case envSqlCollation:
207			// currently ignored
208			var collationSize uint8
209			err = binary.Read(r, binary.LittleEndian, &collationSize)
210			if err != nil {
211				badStreamPanic(err)
212			}
213
214			// SQL Collation data should contain 5 bytes in length
215			if collationSize != 5 {
216				badStreamPanicf("Invalid SQL Collation size value returned from server: %d", collationSize)
217			}
218
219			// 4 bytes, contains: LCID ColFlags Version
220			var info uint32
221			err = binary.Read(r, binary.LittleEndian, &info)
222			if err != nil {
223				badStreamPanic(err)
224			}
225
226			// 1 byte, contains: sortID
227			var sortID uint8
228			err = binary.Read(r, binary.LittleEndian, &sortID)
229			if err != nil {
230				badStreamPanic(err)
231			}
232
233			// old value, should be 0
234			if _, err = readBVarChar(r); err != nil {
235				badStreamPanic(err)
236			}
237		case envTypBeginTran:
238			tranid, err := readBVarByte(r)
239			if len(tranid) != 8 {
240				badStreamPanicf("invalid size of transaction identifier: %d", len(tranid))
241			}
242			sess.tranid = binary.LittleEndian.Uint64(tranid)
243			if err != nil {
244				badStreamPanic(err)
245			}
246			if sess.logFlags&logTransaction != 0 {
247				sess.log.Printf("BEGIN TRANSACTION %x\n", sess.tranid)
248			}
249			_, err = readBVarByte(r)
250			if err != nil {
251				badStreamPanic(err)
252			}
253		case envTypCommitTran, envTypRollbackTran:
254			_, err = readBVarByte(r)
255			if err != nil {
256				badStreamPanic(err)
257			}
258			_, err = readBVarByte(r)
259			if err != nil {
260				badStreamPanic(err)
261			}
262			if sess.logFlags&logTransaction != 0 {
263				if envtype == envTypCommitTran {
264					sess.log.Printf("COMMIT TRANSACTION %x\n", sess.tranid)
265				} else {
266					sess.log.Printf("ROLLBACK TRANSACTION %x\n", sess.tranid)
267				}
268			}
269			sess.tranid = 0
270		case envEnlistDTC:
271			// currently ignored
272			// new value, should be 0
273			if _, err = readBVarChar(r); err != nil {
274				badStreamPanic(err)
275			}
276			// old value
277			if _, err = readBVarChar(r); err != nil {
278				badStreamPanic(err)
279			}
280		case envDefectTran:
281			// currently ignored
282			// new value
283			if _, err = readBVarChar(r); err != nil {
284				badStreamPanic(err)
285			}
286			// old value, should be 0
287			if _, err = readBVarChar(r); err != nil {
288				badStreamPanic(err)
289			}
290		case envDatabaseMirrorPartner:
291			sess.partner, err = readBVarChar(r)
292			if err != nil {
293				badStreamPanic(err)
294			}
295			_, err = readBVarChar(r)
296			if err != nil {
297				badStreamPanic(err)
298			}
299		case envPromoteTran:
300			// currently ignored
301			// old value, should be 0
302			if _, err = readBVarChar(r); err != nil {
303				badStreamPanic(err)
304			}
305			// dtc token
306			// spec says it should be L_VARBYTE, so this code might be wrong
307			if _, err = readBVarChar(r); err != nil {
308				badStreamPanic(err)
309			}
310		case envTranMgrAddr:
311			// currently ignored
312			// old value, should be 0
313			if _, err = readBVarChar(r); err != nil {
314				badStreamPanic(err)
315			}
316			// XACT_MANAGER_ADDRESS = B_VARBYTE
317			if _, err = readBVarChar(r); err != nil {
318				badStreamPanic(err)
319			}
320		case envTranEnded:
321			// currently ignored
322			// old value, B_VARBYTE
323			if _, err = readBVarChar(r); err != nil {
324				badStreamPanic(err)
325			}
326			// should be 0
327			if _, err = readBVarChar(r); err != nil {
328				badStreamPanic(err)
329			}
330		case envResetConnAck:
331			// currently ignored
332			// old value, should be 0
333			if _, err = readBVarChar(r); err != nil {
334				badStreamPanic(err)
335			}
336			// should be 0
337			if _, err = readBVarChar(r); err != nil {
338				badStreamPanic(err)
339			}
340		case envStartedInstanceName:
341			// currently ignored
342			// old value, should be 0
343			if _, err = readBVarChar(r); err != nil {
344				badStreamPanic(err)
345			}
346			// instance name
347			if _, err = readBVarChar(r); err != nil {
348				badStreamPanic(err)
349			}
350		case envRouting:
351			// RoutingData message is:
352			// ValueLength                 USHORT
353			// Protocol (TCP = 0)          BYTE
354			// ProtocolProperty (new port) USHORT
355			// AlternateServer             US_VARCHAR
356			_, err := readUshort(r)
357			if err != nil {
358				badStreamPanic(err)
359			}
360			protocol, err := readByte(r)
361			if err != nil || protocol != 0 {
362				badStreamPanic(err)
363			}
364			newPort, err := readUshort(r)
365			if err != nil {
366				badStreamPanic(err)
367			}
368			newServer, err := readUsVarChar(r)
369			if err != nil {
370				badStreamPanic(err)
371			}
372			// consume the OLDVALUE = %x00 %x00
373			_, err = readUshort(r)
374			if err != nil {
375				badStreamPanic(err)
376			}
377			sess.routedServer = newServer
378			sess.routedPort = newPort
379		default:
380			// ignore rest of records because we don't know how to skip those
381			sess.log.Printf("WARN: Unknown ENVCHANGE record detected with type id = %d\n", envtype)
382			break
383		}
384
385	}
386}
387
388// http://msdn.microsoft.com/en-us/library/dd358180.aspx
389func parseReturnStatus(r *tdsBuffer) ReturnStatus {
390	return ReturnStatus(r.int32())
391}
392
393func parseOrder(r *tdsBuffer) (res orderStruct) {
394	len := int(r.uint16())
395	res.ColIds = make([]uint16, len/2)
396	for i := 0; i < len/2; i++ {
397		res.ColIds[i] = r.uint16()
398	}
399	return res
400}
401
402// https://msdn.microsoft.com/en-us/library/dd340421.aspx
403func parseDone(r *tdsBuffer) (res doneStruct) {
404	res.Status = r.uint16()
405	res.CurCmd = r.uint16()
406	res.RowCount = r.uint64()
407	return res
408}
409
410// https://msdn.microsoft.com/en-us/library/dd340553.aspx
411func parseDoneInProc(r *tdsBuffer) (res doneInProcStruct) {
412	res.Status = r.uint16()
413	res.CurCmd = r.uint16()
414	res.RowCount = r.uint64()
415	return res
416}
417
418type sspiMsg []byte
419
420func parseSSPIMsg(r *tdsBuffer) sspiMsg {
421	size := r.uint16()
422	buf := make([]byte, size)
423	r.ReadFull(buf)
424	return sspiMsg(buf)
425}
426
427type loginAckStruct struct {
428	Interface  uint8
429	TDSVersion uint32
430	ProgName   string
431	ProgVer    uint32
432}
433
434func parseLoginAck(r *tdsBuffer) loginAckStruct {
435	size := r.uint16()
436	buf := make([]byte, size)
437	r.ReadFull(buf)
438	var res loginAckStruct
439	res.Interface = buf[0]
440	res.TDSVersion = binary.BigEndian.Uint32(buf[1:])
441	prognamelen := buf[1+4]
442	var err error
443	if res.ProgName, err = ucs22str(buf[1+4+1 : 1+4+1+prognamelen*2]); err != nil {
444		badStreamPanic(err)
445	}
446	res.ProgVer = binary.BigEndian.Uint32(buf[size-4:])
447	return res
448}
449
450// http://msdn.microsoft.com/en-us/library/dd357363.aspx
451func parseColMetadata72(r *tdsBuffer) (columns []columnStruct) {
452	count := r.uint16()
453	if count == 0xffff {
454		// no metadata is sent
455		return nil
456	}
457	columns = make([]columnStruct, count)
458	for i := range columns {
459		column := &columns[i]
460		column.UserType = r.uint32()
461		column.Flags = r.uint16()
462
463		// parsing TYPE_INFO structure
464		column.ti = readTypeInfo(r)
465		column.ColName = r.BVarChar()
466	}
467	return columns
468}
469
470// http://msdn.microsoft.com/en-us/library/dd357254.aspx
471func parseRow(r *tdsBuffer, columns []columnStruct, row []interface{}) {
472	for i, column := range columns {
473		row[i] = column.ti.Reader(&column.ti, r)
474	}
475}
476
477// http://msdn.microsoft.com/en-us/library/dd304783.aspx
478func parseNbcRow(r *tdsBuffer, columns []columnStruct, row []interface{}) {
479	bitlen := (len(columns) + 7) / 8
480	pres := make([]byte, bitlen)
481	r.ReadFull(pres)
482	for i, col := range columns {
483		if pres[i/8]&(1<<(uint(i)%8)) != 0 {
484			row[i] = nil
485			continue
486		}
487		row[i] = col.ti.Reader(&col.ti, r)
488	}
489}
490
491// http://msdn.microsoft.com/en-us/library/dd304156.aspx
492func parseError72(r *tdsBuffer) (res Error) {
493	length := r.uint16()
494	_ = length // ignore length
495	res.Number = r.int32()
496	res.State = r.byte()
497	res.Class = r.byte()
498	res.Message = r.UsVarChar()
499	res.ServerName = r.BVarChar()
500	res.ProcName = r.BVarChar()
501	res.LineNo = r.int32()
502	return
503}
504
505// http://msdn.microsoft.com/en-us/library/dd304156.aspx
506func parseInfo(r *tdsBuffer) (res Error) {
507	length := r.uint16()
508	_ = length // ignore length
509	res.Number = r.int32()
510	res.State = r.byte()
511	res.Class = r.byte()
512	res.Message = r.UsVarChar()
513	res.ServerName = r.BVarChar()
514	res.ProcName = r.BVarChar()
515	res.LineNo = r.int32()
516	return
517}
518
519// https://msdn.microsoft.com/en-us/library/dd303881.aspx
520func parseReturnValue(r *tdsBuffer) (nv namedValue) {
521	/*
522		ParamOrdinal
523		ParamName
524		Status
525		UserType
526		Flags
527		TypeInfo
528		CryptoMetadata
529		Value
530	*/
531	r.uint16()
532	nv.Name = r.BVarChar()
533	r.byte()
534	r.uint32() // UserType (uint16 prior to 7.2)
535	r.uint16()
536	ti := readTypeInfo(r)
537	nv.Value = ti.Reader(&ti, r)
538	return
539}
540
541func processSingleResponse(sess *tdsSession, ch chan tokenStruct, outs map[string]interface{}) {
542	defer func() {
543		if err := recover(); err != nil {
544			if sess.logFlags&logErrors != 0 {
545				sess.log.Printf("ERROR: Intercepted panic %v", err)
546			}
547			ch <- err
548		}
549		close(ch)
550	}()
551
552	packet_type, err := sess.buf.BeginRead()
553	if err != nil {
554		if sess.logFlags&logErrors != 0 {
555			sess.log.Printf("ERROR: BeginRead failed %v", err)
556		}
557		ch <- err
558		return
559	}
560	if packet_type != packReply {
561		badStreamPanic(fmt.Errorf("unexpected packet type in reply: got %v, expected %v", packet_type, packReply))
562	}
563	var columns []columnStruct
564	errs := make([]Error, 0, 5)
565	for {
566		token := token(sess.buf.byte())
567		if sess.logFlags&logDebug != 0 {
568			sess.log.Printf("got token %v", token)
569		}
570		switch token {
571		case tokenSSPI:
572			ch <- parseSSPIMsg(sess.buf)
573			return
574		case tokenReturnStatus:
575			returnStatus := parseReturnStatus(sess.buf)
576			ch <- returnStatus
577		case tokenLoginAck:
578			loginAck := parseLoginAck(sess.buf)
579			ch <- loginAck
580		case tokenOrder:
581			order := parseOrder(sess.buf)
582			ch <- order
583		case tokenDoneInProc:
584			done := parseDoneInProc(sess.buf)
585			if sess.logFlags&logRows != 0 && done.Status&doneCount != 0 {
586				sess.log.Printf("(%d row(s) affected)\n", done.RowCount)
587			}
588			ch <- done
589		case tokenDone, tokenDoneProc:
590			done := parseDone(sess.buf)
591			done.errors = errs
592			if sess.logFlags&logDebug != 0 {
593				sess.log.Printf("got DONE or DONEPROC status=%d", done.Status)
594			}
595			if done.Status&doneSrvError != 0 {
596				ch <- errors.New("SQL Server had internal error")
597				return
598			}
599			if sess.logFlags&logRows != 0 && done.Status&doneCount != 0 {
600				sess.log.Printf("(%d row(s) affected)\n", done.RowCount)
601			}
602			ch <- done
603			if done.Status&doneMore == 0 {
604				return
605			}
606		case tokenColMetadata:
607			columns = parseColMetadata72(sess.buf)
608			ch <- columns
609		case tokenRow:
610			row := make([]interface{}, len(columns))
611			parseRow(sess.buf, columns, row)
612			ch <- row
613		case tokenNbcRow:
614			row := make([]interface{}, len(columns))
615			parseNbcRow(sess.buf, columns, row)
616			ch <- row
617		case tokenEnvChange:
618			processEnvChg(sess)
619		case tokenError:
620			err := parseError72(sess.buf)
621			if sess.logFlags&logDebug != 0 {
622				sess.log.Printf("got ERROR %d %s", err.Number, err.Message)
623			}
624			errs = append(errs, err)
625			if sess.logFlags&logErrors != 0 {
626				sess.log.Println(err.Message)
627			}
628		case tokenInfo:
629			info := parseInfo(sess.buf)
630			if sess.logFlags&logDebug != 0 {
631				sess.log.Printf("got INFO %d %s", info.Number, info.Message)
632			}
633			if sess.logFlags&logMessages != 0 {
634				sess.log.Println(info.Message)
635			}
636		case tokenReturnValue:
637			nv := parseReturnValue(sess.buf)
638			if len(nv.Name) > 0 {
639				name := nv.Name[1:] // Remove the leading "@".
640				if ov, has := outs[name]; has {
641					err = scanIntoOut(name, nv.Value, ov)
642					if err != nil {
643						fmt.Println("scan error", err)
644						ch <- err
645					}
646				}
647			}
648		default:
649			badStreamPanic(fmt.Errorf("unknown token type returned: %v", token))
650		}
651	}
652}
653
654type parseRespIter byte
655
656const (
657	parseRespIterContinue parseRespIter = iota // Continue parsing current token.
658	parseRespIterNext                          // Fetch the next token.
659	parseRespIterDone                          // Done with parsing the response.
660)
661
662type parseRespState byte
663
664const (
665	parseRespStateNormal  parseRespState = iota // Normal response state.
666	parseRespStateCancel                        // Query is canceled, wait for server to confirm.
667	parseRespStateClosing                       // Waiting for tokens to come through.
668)
669
670type parseResp struct {
671	sess        *tdsSession
672	ctxDone     <-chan struct{}
673	state       parseRespState
674	cancelError error
675}
676
677func (ts *parseResp) sendAttention(ch chan tokenStruct) parseRespIter {
678	if err := sendAttention(ts.sess.buf); err != nil {
679		ts.dlogf("failed to send attention signal %v", err)
680		ch <- err
681		return parseRespIterDone
682	}
683	ts.state = parseRespStateCancel
684	return parseRespIterContinue
685}
686
687func (ts *parseResp) dlog(msg string) {
688	if ts.sess.logFlags&logDebug != 0 {
689		ts.sess.log.Println(msg)
690	}
691}
692func (ts *parseResp) dlogf(f string, v ...interface{}) {
693	if ts.sess.logFlags&logDebug != 0 {
694		ts.sess.log.Printf(f, v...)
695	}
696}
697
698func (ts *parseResp) iter(ctx context.Context, ch chan tokenStruct, tokChan chan tokenStruct) parseRespIter {
699	switch ts.state {
700	default:
701		panic("unknown state")
702	case parseRespStateNormal:
703		select {
704		case tok, ok := <-tokChan:
705			if !ok {
706				ts.dlog("response finished")
707				return parseRespIterDone
708			}
709			if err, ok := tok.(net.Error); ok && err.Timeout() {
710				ts.cancelError = err
711				ts.dlog("got timeout error, sending attention signal to server")
712				return ts.sendAttention(ch)
713			}
714			// Pass the token along.
715			ch <- tok
716			return parseRespIterContinue
717
718		case <-ts.ctxDone:
719			ts.ctxDone = nil
720			ts.dlog("got cancel message, sending attention signal to server")
721			return ts.sendAttention(ch)
722		}
723	case parseRespStateCancel: // Read all responses until a DONE or error is received.Auth
724		select {
725		case tok, ok := <-tokChan:
726			if !ok {
727				ts.dlog("response finished but waiting for attention ack")
728				return parseRespIterNext
729			}
730			switch tok := tok.(type) {
731			default:
732				// Ignore all other tokens while waiting.
733				// The TDS spec says other tokens may arrive after an attention
734				// signal is sent. Ignore these tokens and continue looking for
735				// a DONE with attention confirm mark.
736			case doneStruct:
737				if tok.Status&doneAttn != 0 {
738					ts.dlog("got cancellation confirmation from server")
739					if ts.cancelError != nil {
740						ch <- ts.cancelError
741						ts.cancelError = nil
742					} else {
743						ch <- ctx.Err()
744					}
745					return parseRespIterDone
746				}
747
748			// If an error happens during cancel, pass it along and just stop.
749			// We are uncertain to receive more tokens.
750			case error:
751				ch <- tok
752				ts.state = parseRespStateClosing
753			}
754			return parseRespIterContinue
755		case <-ts.ctxDone:
756			ts.ctxDone = nil
757			ts.state = parseRespStateClosing
758			return parseRespIterContinue
759		}
760	case parseRespStateClosing: // Wait for current token chan to close.
761		if _, ok := <-tokChan; !ok {
762			ts.dlog("response finished")
763			return parseRespIterDone
764		}
765		return parseRespIterContinue
766	}
767}
768
769func processResponse(ctx context.Context, sess *tdsSession, ch chan tokenStruct, outs map[string]interface{}) {
770	ts := &parseResp{
771		sess:    sess,
772		ctxDone: ctx.Done(),
773	}
774	defer func() {
775		// Ensure any remaining error is piped through
776		// or the query may look like it executed when it actually failed.
777		if ts.cancelError != nil {
778			ch <- ts.cancelError
779			ts.cancelError = nil
780		}
781		close(ch)
782	}()
783
784	// Loop over multiple responses.
785	for {
786		ts.dlog("initiating response reading")
787
788		tokChan := make(chan tokenStruct)
789		go processSingleResponse(sess, tokChan, outs)
790
791		// Loop over multiple tokens in response.
792	tokensLoop:
793		for {
794			switch ts.iter(ctx, ch, tokChan) {
795			case parseRespIterContinue:
796				// Nothing, continue to next token.
797			case parseRespIterNext:
798				break tokensLoop
799			case parseRespIterDone:
800				return
801			}
802		}
803	}
804}
805