mod common;
use common::pgwire_harness::TestServer;
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn strict_insert_duplicate_pk_raises_unique_violation() {
let server = TestServer::start().await;
server
.exec(
"CREATE COLLECTION t \
(id STRING NOT NULL PRIMARY KEY, n INT) WITH (engine='document_strict')",
)
.await
.unwrap();
server
.exec("INSERT INTO t (id, n) VALUES ('dup', 1)")
.await
.unwrap();
match server
.client
.simple_query("INSERT INTO t (id, n) VALUES ('dup', 2)")
.await
{
Ok(_) => panic!("expected unique_violation, got success"),
Err(e) => {
let db_err = e.as_db_error().expect("expected DbError");
assert_eq!(
db_err.code().code(),
"23505",
"expected SQLSTATE 23505, got {}: {}",
db_err.code().code(),
db_err.message()
);
let msg = db_err.message().to_lowercase();
assert!(
msg.contains("dup"),
"error message should name the conflicting PK, got: {}",
db_err.message()
);
}
}
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn strict_insert_duplicate_pk_preserves_original_row() {
let server = TestServer::start().await;
server
.exec(
"CREATE COLLECTION t \
(id STRING NOT NULL PRIMARY KEY, n INT) WITH (engine='document_strict')",
)
.await
.unwrap();
server
.exec("INSERT INTO t (id, n) VALUES ('dup', 1)")
.await
.unwrap();
let _ = server
.client
.simple_query("INSERT INTO t (id, n) VALUES ('dup', 2)")
.await;
let rows = server
.query_text("SELECT n FROM t WHERE id = 'dup'")
.await
.unwrap();
assert_eq!(rows.len(), 1, "expected exactly one row, got {rows:?}");
assert_eq!(
rows[0], "1",
"duplicate-PK INSERT must not overwrite the original row, got: {}",
rows[0]
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn strict_insert_on_conflict_do_nothing_is_noop() {
let server = TestServer::start().await;
server
.exec(
"CREATE COLLECTION t \
(id STRING NOT NULL PRIMARY KEY, n INT) WITH (engine='document_strict')",
)
.await
.unwrap();
server
.exec("INSERT INTO t (id, n) VALUES ('dup', 1)")
.await
.unwrap();
server
.exec("INSERT INTO t (id, n) VALUES ('dup', 2) ON CONFLICT DO NOTHING")
.await
.unwrap();
let rows = server
.query_text("SELECT n FROM t WHERE id = 'dup'")
.await
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(
rows[0], "1",
"ON CONFLICT DO NOTHING must leave the original row intact, got: {}",
rows[0]
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn strict_insert_on_conflict_do_update_overwrites() {
let server = TestServer::start().await;
server
.exec(
"CREATE COLLECTION t \
(id STRING NOT NULL PRIMARY KEY, n INT) WITH (engine='document_strict')",
)
.await
.unwrap();
server
.exec("INSERT INTO t (id, n) VALUES ('dup', 1)")
.await
.unwrap();
server
.exec(
"INSERT INTO t (id, n) VALUES ('dup', 2) \
ON CONFLICT (id) DO UPDATE SET n = EXCLUDED.n",
)
.await
.unwrap();
let rows = server
.query_text("SELECT n FROM t WHERE id = 'dup'")
.await
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0], "2", "got: {}", rows[0]);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn strict_upsert_keyword_overwrites() {
let server = TestServer::start().await;
server
.exec(
"CREATE COLLECTION t \
(id STRING NOT NULL PRIMARY KEY, n INT) WITH (engine='document_strict')",
)
.await
.unwrap();
server
.exec("INSERT INTO t (id, n) VALUES ('dup', 1)")
.await
.unwrap();
server
.exec("UPSERT INTO t (id, n) VALUES ('dup', 2)")
.await
.unwrap();
let rows = server
.query_text("SELECT n FROM t WHERE id = 'dup'")
.await
.unwrap();
assert_eq!(rows.len(), 1);
assert_eq!(rows[0], "2", "got: {}", rows[0]);
}