/* ------------------------------------------------------------------------ * * xact_handling.c * Transaction-specific locks and other functions * * Copyright (c) 2016, Postgres Professional * * ------------------------------------------------------------------------ */ #include "xact_handling.h" #include "postgres.h" #include "access/xact.h" #include "catalog/catalog.h" #include "miscadmin.h" #include "storage/lmgr.h" static inline void SetLocktagRelationOid(LOCKTAG *tag, Oid relid); static inline bool do_we_hold_the_lock(Oid relid, LOCKMODE lockmode); /* * Lock certain partitioned relation to disable concurrent access. */ bool xact_lock_partitioned_rel(Oid relid, bool nowait) { if (nowait) { if (ConditionalLockRelationOid(relid, ShareUpdateExclusiveLock)) return true; return false; } else LockRelationOid(relid, ShareUpdateExclusiveLock); return true; } /* * Unlock partitioned relation. */ void xact_unlock_partitioned_rel(Oid relid) { UnlockRelationOid(relid, ShareUpdateExclusiveLock); } /* * Lock relation exclusively (SELECTs are possible). */ bool xact_lock_rel_exclusive(Oid relid, bool nowait) { if (nowait) { if (ConditionalLockRelationOid(relid, ExclusiveLock)) return true; return false; } else LockRelationOid(relid, ExclusiveLock); return true; } /* * Unlock relation (exclusive lock). */ void xact_unlock_rel_exclusive(Oid relid) { UnlockRelationOid(relid, ExclusiveLock); } /* * Check whether we already hold a lock that * might conflict with partition spawning BGW. */ bool xact_bgw_conflicting_lock_exists(Oid relid) { LOCKMODE lockmode; /* Try each lock >= ShareUpdateExclusiveLock */ for (lockmode = ShareUpdateExclusiveLock; lockmode <= AccessExclusiveLock; lockmode++) { if (do_we_hold_the_lock(relid, lockmode)) return true; } return false; } /* * Check if current transaction's level is READ COMMITTED. */ bool xact_is_level_read_committed(void) { if (XactIsoLevel <= XACT_READ_COMMITTED) return true; return false; } /* * Check if 'stmt' is BEGIN\ROLLBACK etc transaction statement. */ bool xact_is_transaction_stmt(Node *stmt) { if (!stmt) return false; if (IsA(stmt, TransactionStmt)) return true; return false; } /* * Check if 'stmt' is SET TRANSACTION statement. */ bool xact_is_set_transaction_stmt(Node *stmt) { if (!stmt) return false; if (IsA(stmt, VariableSetStmt)) { VariableSetStmt *var_set_stmt = (VariableSetStmt *) stmt; /* special case for SET TRANSACTION ... */ if (var_set_stmt->kind == VAR_SET_MULTI) return true; } return false; } /* * Do we hold the specified lock? */ static inline bool do_we_hold_the_lock(Oid relid, LOCKMODE lockmode) { LOCKTAG tag; /* Create a tag for lock */ SetLocktagRelationOid(&tag, relid); /* If lock is alredy held, release it one time (decrement) */ switch (LockAcquire(&tag, lockmode, false, true)) { case LOCKACQUIRE_ALREADY_HELD: LockRelease(&tag, lockmode, false); return true; case LOCKACQUIRE_OK: LockRelease(&tag, lockmode, false); return false; default: return false; } } /* * SetLocktagRelationOid * Set up a locktag for a relation, given only relation OID */ static inline void SetLocktagRelationOid(LOCKTAG *tag, Oid relid) { Oid dbid; if (IsSharedRelation(relid)) dbid = InvalidOid; else dbid = MyDatabaseId; SET_LOCKTAG_RELATION(*tag, dbid, relid); }