Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions test/advisory_lock_class.sh
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,40 @@ check "a duplicate key is still rejected" \
"$(grep -qiE 'duplicate key|unique constraint' <<<"$dup" && echo rejected || echo "NOT rejected: $(head -1 <<<"$dup")")" \
"rejected"

# ---- the row-identity lock (class 104) and its two settings (#1341) ----------------
#
# An UPDATE or DELETE serialises on the row it changes through an advisory lock in
# class 104 (PGCOLUMNAR_LOCKCLASS_ROW_IDENTITY). pgcolumnar.enable_row_update_lock
# turns that off for a session; pgcolumnar.row_lock_buckets is part of the lock tag,
# so it is fixed at server start. Neither was set or read by any test.
psql_run "CREATE TABLE rl (id int, v int) USING pgcolumnar;"
psql_run "INSERT INTO rl SELECT g, g FROM generate_series(1, 100) g;"
# row_locks_while_updating SETTING -> "<class-104 locks>|<holder's UPDATE running>", read
# in one query once the holder's UPDATE is running. WAITED FOR, NOT SLEPT ON (#1368
# review): read after a fixed sleep, a holder that had not reached its UPDATE yet made
# the off arm read 0 on a build that ignores the setting, because nothing was locked.
# The second field proves the UPDATE was running when the first was read.
row_locks_while_updating() {
env PATH="$PGC_BINDIR:$PATH" psql -h 127.0.0.1 -p "$PGC_PORT" -U postgres -d "$PGC_DB" -Atq \
-c "BEGIN; SET LOCAL pgcolumnar.enable_row_update_lock = $1; UPDATE rl SET v = v + 1 WHERE id = 7; SELECT pg_sleep(3); COMMIT;" \
>/dev/null 2>&1 &
local holder=$! got="" i
for i in $(seq 1 100); do
got="$(q "SELECT count(*) FILTER (WHERE locktype = 'advisory' AND objsubid = 104) || '|' ||
count(*) FILTER (WHERE locktype = 'relation' AND relation = 'rl'::regclass AND mode = 'RowExclusiveLock')
FROM pg_locks WHERE pid <> pg_backend_pid()")"
[ "${got#*|}" = 1 ] && break
sleep 0.1
done
echo "$got"
wait "$holder"
}
_rl_on="$(row_locks_while_updating on)"
check "an UPDATE holds a row-identity lock (class 104) while enable_row_update_lock is on" \
"$([ "${_rl_on%|*}" -ge 1 ] 2>/dev/null && echo "held|${_rl_on#*|}" || echo "none|${_rl_on#*|}")" "held|1"
check "and holds none with enable_row_update_lock off" "$(row_locks_while_updating off)" "0|1"
check "row_lock_buckets is fixed at server start, so a session cannot change it (55P02)" \
"$(env PATH="$PGC_BINDIR:$PATH" psql -h 127.0.0.1 -p "$PGC_PORT" -U postgres -d "$PGC_DB" -At -v VERBOSITY=sqlstate -c "SET pgcolumnar.row_lock_buckets = 7;" 2>&1 | sed -n 's/^.*ERROR:[[:space:]]*\([0-9A-Z]\{5\}\).*$/\1/p')|$(q "SELECT context || '|' || setting FROM pg_settings WHERE name = 'pgcolumnar.row_lock_buckets'")" \
"55P02|postmaster|1024"

pgc_summary
1 change: 1 addition & 0 deletions test/pytest/TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5379,6 +5379,7 @@ regardless.
| `test_the_lock_an_insert_takes_is_not_in_a_sql_reachable_class` | the lock is enabled, the insert's locks are visible, none is SQL-reachable, and the discovering session leaves nothing held |
| `test_a_user_advisory_lock_on_that_tag_does_not_block_an_insert` | a user session holding every addressable tag in class 2 does not block a columnar insert |
| `test_a_duplicate_key_is_still_rejected` | the lock still does its job, so removing the collision by removing the lock does not pass |
| `test_the_row_identity_lock_and_its_settings` | an UPDATE holds a class-104 row-identity lock with `enable_row_update_lock` on and none with it off; `row_lock_buckets` refuses `SET` (55P02) because it is fixed at server start (#1341) |
## 69. test_index_am_support.py: the index methods the page claims must work

`docs/features.md` said "`CREATE INDEX` builds btree and hash indexes over a
Expand Down
5 changes: 4 additions & 1 deletion test/pytest/expected_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -633,4 +633,7 @@ guard_tests 429
# test_ttl_expire.py, by collection over the cluster files on this tree.
# cluster_tests 534 -> 538 for #1341's test_import_export_privilege.py, the twin of
# import_export_privilege.sh: four tests, by collection over the cluster files on this tree.
cluster_tests 538
#
# cluster_tests 538 -> 539 for #1341's test_the_row_identity_lock_and_its_settings in
# test_advisory_lock_class.py, by collection over the cluster files on this tree.
cluster_tests 539
41 changes: 41 additions & 0 deletions test/pytest/test_advisory_lock_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,44 @@ def test_a_duplicate_key_is_still_rejected(pgc_conn, expect):
got = ("rejected" if "duplicate key" in msg or "unique constraint" in msg
else f"NOT rejected: {str(exc).splitlines()[0]}")
expect.text(got, "rejected", "a duplicate key is still rejected")


def test_the_row_identity_lock_and_its_settings(pgc_cluster, pgc_conn, expect):
"""An UPDATE serialises on its row through an advisory lock in class 104; the
session setting enable_row_update_lock turns that off, and row_lock_buckets is
part of the lock tag, so it is fixed at server start (#1341)."""
with pgc_conn.cursor() as cur:
cur.execute("CREATE TABLE rl (id int, v int) USING pgcolumnar")
cur.execute("INSERT INTO rl SELECT g, g FROM generate_series(1, 100) g")
cur.execute("SELECT current_schema()")
schema = cur.fetchone()[0]

def row_locks_while_updating(setting):
holder = psycopg.connect(pgc_cluster.dsn(), autocommit=False)
try:
with holder.cursor() as hc:
hc.execute(f'SET search_path TO "{schema}"')
hc.execute(f"SET LOCAL pgcolumnar.enable_row_update_lock = {setting}")
hc.execute("UPDATE rl SET v = v + 1 WHERE id = 7")
with pgc_conn.cursor() as cur:
cur.execute("SELECT count(*) FROM pg_locks WHERE locktype = 'advisory'"
" AND objsubid = 104 AND pid <> pg_backend_pid()")
return cur.fetchone()[0]
finally:
holder.rollback()
holder.close()

on = row_locks_while_updating("on")
expect.text("held" if on >= 1 else "none", "held",
"an UPDATE holds a row-identity lock (class 104) while enable_row_update_lock is on")
expect.num(row_locks_while_updating("off"), 0, "and holds none with enable_row_update_lock off")
with pgc_conn.cursor() as cur:
try:
cur.execute("SET pgcolumnar.row_lock_buckets = 7")
refused = "ok"
except psycopg.Error as e:
refused = e.sqlstate
cur.execute("SELECT context || '|' || setting FROM pg_settings WHERE name = 'pgcolumnar.row_lock_buckets'")
ctx = cur.fetchone()[0]
expect.text(f"{refused}|{ctx}", "55P02|postmaster|1024",
"row_lock_buckets is fixed at server start, so a session cannot change it (55P02)")
Loading