protocol_guards_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package pgserver
  2. import (
  3. "bufio"
  4. "testing"
  5. )
  6. func guardConnection(t *testing.T) (*Connection, *countingConn) {
  7. t.Helper()
  8. c, _ := newDataConnection(t)
  9. conn := &countingConn{}
  10. c.conn = conn
  11. c.writer = bufio.NewWriter(conn)
  12. if _, err := c.executor.Execute(parseStmt(t, "CREATE TABLE t (id INTEGER PRIMARY KEY)")); err != nil {
  13. t.Fatal(err)
  14. }
  15. return c, conn
  16. }
  17. func guardBind(parameterFormats, resultFormats []int16) []byte {
  18. b := NewMessageBuilder()
  19. b.WriteString("p")
  20. b.WriteString("q")
  21. b.WriteInt16(int16(len(parameterFormats)))
  22. for _, format := range parameterFormats {
  23. b.WriteInt16(format)
  24. }
  25. b.WriteInt16(0)
  26. b.WriteInt16(int16(len(resultFormats)))
  27. for _, format := range resultFormats {
  28. b.WriteInt16(format)
  29. }
  30. return b.Bytes()
  31. }
  32. func TestUnsupportedExtendedRequestsFailBeforeSideEffects(t *testing.T) {
  33. for _, tc := range []struct {
  34. name string
  35. message *Message
  36. code string
  37. }{
  38. {"binary_results", &Message{Type: MsgBind, Data: guardBind(nil, []int16{1})}, ErrCodeFeatureNotSupported},
  39. {"per_column_results", &Message{Type: MsgBind, Data: guardBind(nil, []int16{0, 0})}, ErrCodeFeatureNotSupported},
  40. {"invalid_result_format", &Message{Type: MsgBind, Data: guardBind(nil, []int16{2})}, ErrCodeProtocolViolation},
  41. {"invalid_parameter_format", &Message{Type: MsgBind, Data: guardBind([]int16{2}, nil)}, ErrCodeProtocolViolation},
  42. {"parameter_format_count", &Message{Type: MsgBind, Data: guardBind([]int16{0, 0}, nil)}, ErrCodeProtocolViolation},
  43. {"bounded_execute", &Message{Type: MsgExecute, Data: []byte{'p', 0, 0, 0, 0, 1}}, ErrCodeFeatureNotSupported},
  44. {"negative_execute", &Message{Type: MsgExecute, Data: []byte{'p', 0, 255, 255, 255, 255}}, ErrCodeProtocolViolation},
  45. } {
  46. t.Run(tc.name, func(t *testing.T) {
  47. c, conn := guardConnection(t)
  48. query := "INSERT INTO t VALUES (1) RETURNING id"
  49. c.statements["q"] = &preparedStatement{query: query}
  50. c.portals["p"] = &portal{query: query}
  51. if err := c.handleMessage(tc.message); err != nil {
  52. t.Fatal(err)
  53. }
  54. // Requests pipelined after the error must be discarded until Sync.
  55. if err := c.handleMessage(&Message{Type: MsgExecute, Data: []byte{'p', 0, 0, 0, 0, 0}}); err != nil {
  56. t.Fatal(err)
  57. }
  58. if err := c.handleMessage(&Message{Type: MsgSync}); err != nil {
  59. t.Fatal(err)
  60. }
  61. messages := readAllMessages(t, conn.bytes())
  62. if len(messages) != 2 || errorCode(messages[0]) != tc.code || messages[1].Type != MsgReadyForQuery {
  63. t.Fatalf("unexpected recovery: %v", messages)
  64. }
  65. if result, err := c.executor.Execute(parseStmt(t, "SELECT COUNT(*) FROM t")); err != nil || result.Rows[0][0] != int64(0) {
  66. t.Fatalf("rejected request had side effects: %v %v", result, err)
  67. }
  68. })
  69. }
  70. }
  71. func TestDescribeDoesNotExecuteOrFailTransaction(t *testing.T) {
  72. c, conn := guardConnection(t)
  73. if err := c.handleMessage(&Message{Type: MsgQuery, Data: []byte("BEGIN\x00")}); err != nil {
  74. t.Fatal(err)
  75. }
  76. c.statements["q"] = &preparedStatement{query: "INSERT INTO t VALUES (1) RETURNING id", paramOIDs: []int32{20}}
  77. c.portals["p"] = &portal{query: "INSERT INTO t VALUES (1) RETURNING id"}
  78. if err := c.handleMessage(&Message{Type: MsgDescribe, Data: []byte{'S', 'q', 0}}); err != nil {
  79. t.Fatal(err)
  80. }
  81. if err := c.handleMessage(&Message{Type: MsgDescribe, Data: []byte{'P', 'p', 0}}); err != nil {
  82. t.Fatal(err)
  83. }
  84. if err := c.handleMessage(&Message{Type: MsgSync}); err != nil {
  85. t.Fatal(err)
  86. }
  87. messages := readAllMessages(t, conn.bytes())
  88. seenParam, seenNoData := false, 0
  89. for _, message := range messages {
  90. switch message.Type {
  91. case MsgParameterDescription:
  92. seenParam = true
  93. case MsgNoData:
  94. seenNoData++
  95. case MsgErrorResponse:
  96. t.Fatalf("Describe failed: %s", errorCode(message))
  97. }
  98. }
  99. if !seenParam || seenNoData != 2 {
  100. t.Fatalf("Describe messages = %v", messages)
  101. }
  102. if c.txStatus != TxStatusInBlock || c.extendedFailed {
  103. t.Fatalf("Describe changed transaction state: %c failed=%v", c.txStatus, c.extendedFailed)
  104. }
  105. if result, err := c.executor.Execute(parseStmt(t, "SELECT COUNT(*) FROM t")); err != nil || result.Rows[0][0] != int64(0) {
  106. t.Fatalf("Describe executed a side effect: %v %v", result, err)
  107. }
  108. if err := c.handleMessage(&Message{Type: MsgQuery, Data: []byte("ROLLBACK\x00")}); err != nil {
  109. t.Fatal(err)
  110. }
  111. }
  112. func TestParseRejectsMultipleCommands(t *testing.T) {
  113. c, conn := guardConnection(t)
  114. parse := NewMessageBuilder()
  115. parse.WriteString("q")
  116. parse.WriteString("SELECT 1; INSERT INTO t VALUES (1)")
  117. parse.WriteInt16(0)
  118. if err := c.handleMessage(&Message{Type: MsgParse, Data: parse.Bytes()}); err != nil {
  119. t.Fatal(err)
  120. }
  121. if _, ok := c.statements["q"]; ok {
  122. t.Fatal("multiple commands stored")
  123. }
  124. messages := readAllMessages(t, conn.bytes())
  125. if len(messages) != 1 || errorCode(messages[0]) != ErrCodeSyntaxError {
  126. t.Fatal(messages)
  127. }
  128. }
  129. func TestTypicalExtendedSequenceWithDescribeSucceeds(t *testing.T) {
  130. c, conn := guardConnection(t)
  131. if _, err := c.executor.Execute(parseStmt(t, "INSERT INTO t VALUES (7)")); err != nil {
  132. t.Fatal(err)
  133. }
  134. parse := NewMessageBuilder()
  135. parse.WriteString("")
  136. parse.WriteString("SELECT id FROM t WHERE id = $1")
  137. parse.WriteInt16(1)
  138. parse.WriteInt32(20)
  139. bind := NewMessageBuilder()
  140. bind.WriteString("")
  141. bind.WriteString("")
  142. bind.WriteInt16(0)
  143. bind.WriteInt16(1)
  144. bind.WriteInt32(1)
  145. bind.WriteBytes([]byte("7"))
  146. bind.WriteInt16(0)
  147. execute := NewMessageBuilder()
  148. execute.WriteString("")
  149. execute.WriteInt32(0)
  150. for _, msg := range []*Message{
  151. {Type: MsgParse, Data: parse.Bytes()},
  152. {Type: MsgBind, Data: bind.Bytes()},
  153. {Type: MsgDescribe, Data: []byte{'P', 0}},
  154. {Type: MsgExecute, Data: execute.Bytes()},
  155. {Type: MsgSync},
  156. } {
  157. if err := c.handleMessage(msg); err != nil {
  158. t.Fatal(err)
  159. }
  160. }
  161. messages := readAllMessages(t, conn.bytes())
  162. var types []byte
  163. var row []string
  164. for _, message := range messages {
  165. types = append(types, message.Type)
  166. if message.Type == MsgErrorResponse {
  167. t.Fatalf("extended SELECT failed: %s", errorCode(message))
  168. }
  169. if message.Type == MsgDataRow {
  170. row = dataRowValues(t, message)
  171. }
  172. }
  173. if string(types) != "12nTDCZ" {
  174. t.Fatalf("message sequence = %q", types)
  175. }
  176. if len(row) != 1 || row[0] != "7" {
  177. t.Fatalf("row = %v", row)
  178. }
  179. }