| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- package pgserver
- import (
- "strings"
- "testing"
- )
- // TestCompositePrimaryKeyWireBehavior drives a composite key through the
- // simple-query protocol: DDL, duplicate and NOT NULL SQLSTATEs, full-key reads,
- // PRAGMA table_info ordinals, and the information_schema key columns.
- func TestCompositePrimaryKeyWireBehavior(t *testing.T) {
- c, client := newDataConnection(t)
- query := func(sql string) []*Message {
- return runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte(sql), 0)})
- }
- if msgs := query("CREATE TABLE pairs (a TEXT, b TEXT, v TEXT, PRIMARY KEY (a, b))"); errorCode(msgs[0]) != "" {
- t.Fatalf("CREATE TABLE failed: %s", errorMessage(msgs[0]))
- }
- if msgs := query("INSERT INTO pairs (a, b, v) VALUES ('x', 'y', 'one')"); errorCode(msgs[0]) != "" {
- t.Fatalf("INSERT failed: %s", errorMessage(msgs[0]))
- }
- if code := errorCode(query("INSERT INTO pairs (a, b, v) VALUES ('x', 'y', 'two')")[0]); code != ErrCodeUniqueViolation {
- t.Fatalf("duplicate composite key SQLSTATE = %q, want 23505", code)
- }
- if code := errorCode(query("INSERT INTO pairs (a, v) VALUES ('x', 'missing b')")[0]); code != "23502" {
- t.Fatalf("missing key component SQLSTATE = %q, want 23502", code)
- }
- // Only the accepted row survived the two rejected inserts.
- selectRows := query("SELECT v FROM pairs WHERE a = 'x' AND b = 'y'")
- var dataRows []*Message
- for _, msg := range selectRows {
- if msg.Type == MsgDataRow {
- dataRows = append(dataRows, msg)
- }
- }
- if len(dataRows) != 1 {
- t.Fatalf("full-key select returned %d rows, want 1", len(dataRows))
- }
- if values := dataRowValues(t, dataRows[0]); len(values) != 1 || values[0] != "one" {
- t.Fatalf("full-key select values = %v, want [one]", values)
- }
- // PRAGMA table_info reports the pk ordinal, not a bare flag.
- var info [][]string
- for _, msg := range query("PRAGMA table_info(pairs)") {
- if msg.Type == MsgDataRow {
- info = append(info, dataRowValues(t, msg))
- }
- }
- if len(info) != 3 {
- t.Fatalf("table_info rows = %d, want 3", len(info))
- }
- wantPK := []string{"1", "2", "0"}
- for i, row := range info {
- if row[5] != wantPK[i] {
- t.Fatalf("table_info row %d pk = %s, want %s", i, row[5], wantPK[i])
- }
- }
- // KEY_COLUMN_USAGE lists one row per key column with its ordinal.
- var usage [][]string
- for _, msg := range query("SELECT column_name, ordinal_position FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE table_name = 'pairs'") {
- if msg.Type == MsgDataRow {
- usage = append(usage, dataRowValues(t, msg))
- }
- }
- if len(usage) != 2 {
- t.Fatalf("KEY_COLUMN_USAGE rows = %d, want 2", len(usage))
- }
- if usage[0][0] != "a" || usage[0][1] != "1" || usage[1][0] != "b" || usage[1][1] != "2" {
- t.Fatalf("KEY_COLUMN_USAGE = %v, want [[a 1] [b 2]]", usage)
- }
- }
- func TestCompositePrimaryKeyDDLRejections(t *testing.T) {
- c, client := newDataConnection(t)
- for _, sql := range []string{
- "CREATE TABLE t1 (a TEXT PRIMARY KEY, b TEXT PRIMARY KEY)",
- "CREATE TABLE t2 (a TEXT PRIMARY KEY, b TEXT, PRIMARY KEY (b))",
- "CREATE TABLE t3 (a TEXT, PRIMARY KEY (a, a))",
- "CREATE TABLE t4 (a TEXT, b TEXT GENERATED ALWAYS AS (a) STORED, PRIMARY KEY (b))",
- "CREATE TABLE t5 (a TEXT)",
- "ALTER TABLE t5 ADD COLUMN id INTEGER PRIMARY KEY",
- } {
- msgs := runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte(sql), 0)})
- if strings.HasPrefix(sql, "CREATE TABLE t5") {
- if code := errorCode(msgs[0]); code != "" {
- t.Fatalf("%s SQLSTATE = %q, want success", sql, code)
- }
- continue
- }
- if code := errorCode(msgs[0]); code != ErrCodeFeatureNotSupported {
- t.Fatalf("%s SQLSTATE = %q, want 0A000", sql, code)
- }
- }
- }
- // A key column that takes its value from a constant DEFAULT must resolve before
- // conflict matching, including over the wire.
- func TestCompositePrimaryKeyDefaultedKeyUpsertOverWire(t *testing.T) {
- c, client := newDataConnection(t)
- query := func(sql string) []*Message {
- return runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte(sql), 0)})
- }
- query("CREATE TABLE konst (a TEXT DEFAULT 'd', b TEXT DEFAULT 'e', v TEXT, PRIMARY KEY (a, b))")
- query("INSERT INTO konst (b, v) VALUES ('y', 'one')")
- if code := errorCode(query("INSERT INTO konst (b, v) VALUES ('y', 'two') ON CONFLICT (a, b) DO UPDATE SET v = excluded.v")[0]); code != "" {
- t.Fatalf("defaulted-key upsert SQLSTATE = %q, want success", code)
- }
- rows := query("SELECT v FROM konst WHERE a = 'd' AND b = 'y'")
- var dataRows []*Message
- for _, msg := range rows {
- if msg.Type == MsgDataRow {
- dataRows = append(dataRows, msg)
- }
- }
- if len(dataRows) != 1 {
- t.Fatalf("upsert read returned %d rows, want 1", len(dataRows))
- }
- if values := dataRowValues(t, dataRows[0]); len(values) != 1 || values[0] != "two" {
- t.Fatalf("upsert result = %v, want [two]", values)
- }
- // Assigning a key column is rejected, not applied as a ghost row.
- if code := errorCode(query("INSERT INTO konst (b, v) VALUES ('y', 'x') ON CONFLICT (a, b) DO UPDATE SET a = 'other'")[0]); code != ErrCodeFeatureNotSupported {
- t.Fatalf("key-assignment upsert SQLSTATE = %q, want 0A000", code)
- }
- }
|