composite_primary_key_test.go 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package pgserver
  2. import (
  3. "strings"
  4. "testing"
  5. )
  6. // TestCompositePrimaryKeyWireBehavior drives a composite key through the
  7. // simple-query protocol: DDL, duplicate and NOT NULL SQLSTATEs, full-key reads,
  8. // PRAGMA table_info ordinals, and the information_schema key columns.
  9. func TestCompositePrimaryKeyWireBehavior(t *testing.T) {
  10. c, client := newDataConnection(t)
  11. query := func(sql string) []*Message {
  12. return runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte(sql), 0)})
  13. }
  14. if msgs := query("CREATE TABLE pairs (a TEXT, b TEXT, v TEXT, PRIMARY KEY (a, b))"); errorCode(msgs[0]) != "" {
  15. t.Fatalf("CREATE TABLE failed: %s", errorMessage(msgs[0]))
  16. }
  17. if msgs := query("INSERT INTO pairs (a, b, v) VALUES ('x', 'y', 'one')"); errorCode(msgs[0]) != "" {
  18. t.Fatalf("INSERT failed: %s", errorMessage(msgs[0]))
  19. }
  20. if code := errorCode(query("INSERT INTO pairs (a, b, v) VALUES ('x', 'y', 'two')")[0]); code != ErrCodeUniqueViolation {
  21. t.Fatalf("duplicate composite key SQLSTATE = %q, want 23505", code)
  22. }
  23. if code := errorCode(query("INSERT INTO pairs (a, v) VALUES ('x', 'missing b')")[0]); code != "23502" {
  24. t.Fatalf("missing key component SQLSTATE = %q, want 23502", code)
  25. }
  26. // Only the accepted row survived the two rejected inserts.
  27. selectRows := query("SELECT v FROM pairs WHERE a = 'x' AND b = 'y'")
  28. var dataRows []*Message
  29. for _, msg := range selectRows {
  30. if msg.Type == MsgDataRow {
  31. dataRows = append(dataRows, msg)
  32. }
  33. }
  34. if len(dataRows) != 1 {
  35. t.Fatalf("full-key select returned %d rows, want 1", len(dataRows))
  36. }
  37. if values := dataRowValues(t, dataRows[0]); len(values) != 1 || values[0] != "one" {
  38. t.Fatalf("full-key select values = %v, want [one]", values)
  39. }
  40. // PRAGMA table_info reports the pk ordinal, not a bare flag.
  41. var info [][]string
  42. for _, msg := range query("PRAGMA table_info(pairs)") {
  43. if msg.Type == MsgDataRow {
  44. info = append(info, dataRowValues(t, msg))
  45. }
  46. }
  47. if len(info) != 3 {
  48. t.Fatalf("table_info rows = %d, want 3", len(info))
  49. }
  50. wantPK := []string{"1", "2", "0"}
  51. for i, row := range info {
  52. if row[5] != wantPK[i] {
  53. t.Fatalf("table_info row %d pk = %s, want %s", i, row[5], wantPK[i])
  54. }
  55. }
  56. // KEY_COLUMN_USAGE lists one row per key column with its ordinal.
  57. var usage [][]string
  58. for _, msg := range query("SELECT column_name, ordinal_position FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_name = 'pairs'") {
  59. if msg.Type == MsgDataRow {
  60. usage = append(usage, dataRowValues(t, msg))
  61. }
  62. }
  63. if len(usage) != 2 {
  64. t.Fatalf("KEY_COLUMN_USAGE rows = %d, want 2", len(usage))
  65. }
  66. if usage[0][0] != "a" || usage[0][1] != "1" || usage[1][0] != "b" || usage[1][1] != "2" {
  67. t.Fatalf("KEY_COLUMN_USAGE = %v, want [[a 1] [b 2]]", usage)
  68. }
  69. }
  70. func TestCompositePrimaryKeyDDLRejections(t *testing.T) {
  71. c, client := newDataConnection(t)
  72. for _, sql := range []string{
  73. "CREATE TABLE t1 (a TEXT PRIMARY KEY, b TEXT PRIMARY KEY)",
  74. "CREATE TABLE t2 (a TEXT PRIMARY KEY, b TEXT, PRIMARY KEY (b))",
  75. "CREATE TABLE t3 (a TEXT, PRIMARY KEY (a, a))",
  76. "CREATE TABLE t4 (a TEXT, b TEXT GENERATED ALWAYS AS (a) STORED, PRIMARY KEY (b))",
  77. "CREATE TABLE t5 (a TEXT)",
  78. "ALTER TABLE t5 ADD COLUMN id INTEGER PRIMARY KEY",
  79. } {
  80. msgs := runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte(sql), 0)})
  81. if strings.HasPrefix(sql, "CREATE TABLE t5") {
  82. if code := errorCode(msgs[0]); code != "" {
  83. t.Fatalf("%s SQLSTATE = %q, want success", sql, code)
  84. }
  85. continue
  86. }
  87. if code := errorCode(msgs[0]); code != ErrCodeFeatureNotSupported {
  88. t.Fatalf("%s SQLSTATE = %q, want 0A000", sql, code)
  89. }
  90. }
  91. }
  92. // A key column that takes its value from a constant DEFAULT must resolve before
  93. // conflict matching, including over the wire.
  94. func TestCompositePrimaryKeyDefaultedKeyUpsertOverWire(t *testing.T) {
  95. c, client := newDataConnection(t)
  96. query := func(sql string) []*Message {
  97. return runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte(sql), 0)})
  98. }
  99. query("CREATE TABLE konst (a TEXT DEFAULT 'd', b TEXT DEFAULT 'e', v TEXT, PRIMARY KEY (a, b))")
  100. query("INSERT INTO konst (b, v) VALUES ('y', 'one')")
  101. if code := errorCode(query("INSERT INTO konst (b, v) VALUES ('y', 'two') ON CONFLICT (a, b) DO UPDATE SET v = excluded.v")[0]); code != "" {
  102. t.Fatalf("defaulted-key upsert SQLSTATE = %q, want success", code)
  103. }
  104. rows := query("SELECT v FROM konst WHERE a = 'd' AND b = 'y'")
  105. var dataRows []*Message
  106. for _, msg := range rows {
  107. if msg.Type == MsgDataRow {
  108. dataRows = append(dataRows, msg)
  109. }
  110. }
  111. if len(dataRows) != 1 {
  112. t.Fatalf("upsert read returned %d rows, want 1", len(dataRows))
  113. }
  114. if values := dataRowValues(t, dataRows[0]); len(values) != 1 || values[0] != "two" {
  115. t.Fatalf("upsert result = %v, want [two]", values)
  116. }
  117. // Assigning a key column is rejected, not applied as a ghost row.
  118. if code := errorCode(query("INSERT INTO konst (b, v) VALUES ('y', 'x') ON CONFLICT (a, b) DO UPDATE SET a = 'other'")[0]); code != ErrCodeFeatureNotSupported {
  119. t.Fatalf("key-assignment upsert SQLSTATE = %q, want 0A000", code)
  120. }
  121. }