2
0

protocol_guards_test.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. {"describe_statement", &Message{Type: MsgDescribe, Data: []byte{'S', 'q', 0}}, ErrCodeFeatureNotSupported},
  44. {"describe_portal", &Message{Type: MsgDescribe, Data: []byte{'P', 'p', 0}}, ErrCodeFeatureNotSupported},
  45. {"bounded_execute", &Message{Type: MsgExecute, Data: []byte{'p', 0, 0, 0, 0, 1}}, ErrCodeFeatureNotSupported},
  46. {"negative_execute", &Message{Type: MsgExecute, Data: []byte{'p', 0, 255, 255, 255, 255}}, ErrCodeProtocolViolation},
  47. } {
  48. t.Run(tc.name, func(t *testing.T) {
  49. c, conn := guardConnection(t)
  50. query := "INSERT INTO t VALUES (1) RETURNING id"
  51. c.statements["q"] = &preparedStatement{query: query}
  52. c.portals["p"] = &portal{query: query}
  53. if err := c.handleMessage(tc.message); err != nil {
  54. t.Fatal(err)
  55. }
  56. // Requests pipelined after the error must be discarded until Sync.
  57. if err := c.handleMessage(&Message{Type: MsgExecute, Data: []byte{'p', 0, 0, 0, 0, 0}}); err != nil {
  58. t.Fatal(err)
  59. }
  60. if err := c.handleMessage(&Message{Type: MsgSync}); err != nil {
  61. t.Fatal(err)
  62. }
  63. messages := readAllMessages(t, conn.bytes())
  64. if len(messages) != 2 || errorCode(messages[0]) != tc.code || messages[1].Type != MsgReadyForQuery {
  65. t.Fatalf("unexpected recovery: %v", messages)
  66. }
  67. if result, err := c.executor.Execute(parseStmt(t, "SELECT COUNT(*) FROM t")); err != nil || result.Rows[0][0] != int64(0) {
  68. t.Fatalf("rejected request had side effects: %v %v", result, err)
  69. }
  70. })
  71. }
  72. }
  73. func TestDescribeFailurePreservesFailedTransactionState(t *testing.T) {
  74. c, conn := guardConnection(t)
  75. if err := c.handleMessage(&Message{Type: MsgQuery, Data: []byte("BEGIN\x00")}); err != nil {
  76. t.Fatal(err)
  77. }
  78. c.portals["p"] = &portal{query: "INSERT INTO t VALUES (1) RETURNING id"}
  79. if err := c.handleMessage(&Message{Type: MsgDescribe, Data: []byte{'P', 'p', 0}}); err != nil {
  80. t.Fatal(err)
  81. }
  82. if err := c.handleMessage(&Message{Type: MsgSync}); err != nil {
  83. t.Fatal(err)
  84. }
  85. messages := readAllMessages(t, conn.bytes())
  86. last := messages[len(messages)-1]
  87. if last.Type != MsgReadyForQuery || len(last.Data) != 1 || last.Data[0] != TxStatusFailed {
  88. t.Fatalf("Describe did not fail transaction: %v", last)
  89. }
  90. if err := c.handleMessage(&Message{Type: MsgQuery, Data: []byte("ROLLBACK\x00")}); err != nil {
  91. t.Fatal(err)
  92. }
  93. if c.txStatus != TxStatusIdle {
  94. t.Fatal("rollback did not recover")
  95. }
  96. }
  97. func TestParseRejectsMultipleCommands(t *testing.T) {
  98. c, conn := guardConnection(t)
  99. parse := NewMessageBuilder()
  100. parse.WriteString("q")
  101. parse.WriteString("SELECT 1; INSERT INTO t VALUES (1)")
  102. parse.WriteInt16(0)
  103. if err := c.handleMessage(&Message{Type: MsgParse, Data: parse.Bytes()}); err != nil {
  104. t.Fatal(err)
  105. }
  106. if _, ok := c.statements["q"]; ok {
  107. t.Fatal("multiple commands stored")
  108. }
  109. messages := readAllMessages(t, conn.bytes())
  110. if len(messages) != 1 || errorCode(messages[0]) != ErrCodeSyntaxError {
  111. t.Fatal(messages)
  112. }
  113. }