mirror of
https://github.com/postgres/postgres.git
synced 2025-08-06 18:42:54 +03:00
From: Massimo Dal Zotto <dz@cs.unitn.it>
Here is a tar file the new directories, which substitute the old ones in contrib. Please remove the old directories array, datetime, miscutil, string and userlock before unpacking the tar file in contrib. Note that as the modules are now installed in lib/modules I install all my sql code in lib/sql. In my opinion also the other contributors should follow these rules.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile--
|
||||
# Makefile for new string I/O functions.
|
||||
# Makefile --
|
||||
# Makefile for the user_locks module.
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@@ -15,36 +15,35 @@ INCLUDE_OPT = -I ./ \
|
||||
-I $(SRCDIR)/include \
|
||||
-I $(SRCDIR)/port/$(PORTNAME)
|
||||
|
||||
CFLAGS += $(INCLUDE_OPT)
|
||||
|
||||
ifeq ($(PORTNAME), linux)
|
||||
ifdef LINUX_ELF
|
||||
ifeq ($(CC), gcc)
|
||||
CFLAGS += -fPIC
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(PORTNAME), i386_solaris)
|
||||
CFLAGS+= -fPIC
|
||||
endif
|
||||
CFLAGS += $(INCLUDE_OPT) $(CFLAGS_SL)
|
||||
|
||||
MODNAME = user_locks
|
||||
|
||||
MODULE = $(MODNAME)$(DLSUFFIX)
|
||||
|
||||
MODDIR = $(LIBDIR)/modules
|
||||
|
||||
SQLDIR = $(LIBDIR)/sql
|
||||
|
||||
all: module sql
|
||||
|
||||
module: $(MODULE)
|
||||
|
||||
sql: $(MODNAME).sql
|
||||
|
||||
install: $(MODULE)
|
||||
cp -p $(MODULE) $(LIBDIR)/modules
|
||||
cd $(LIBDIR)/modules; strip $(MODULE)
|
||||
install: $(MODULE) $(MODDIR) $(SQLDIR)
|
||||
cp -p $(MODULE) $(MODDIR)/
|
||||
strip $(MODDIR)/$(MODULE)
|
||||
cp -p $(MODNAME).sql $(SQLDIR)/
|
||||
|
||||
$(MODDIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(SQLDIR):
|
||||
mkdir -p $@
|
||||
|
||||
%.sql: %.sql.in
|
||||
sed "s|MODULE_PATHNAME|$(LIBDIR)/modules/$(MODULE)|" < $< > $@
|
||||
sed "s|MODULE_PATHNAME|$(MODDIR)/$(MODULE)|" < $< > $@
|
||||
|
||||
.SUFFIXES: $(DLSUFFIX)
|
||||
|
||||
@@ -55,7 +54,7 @@ depend dep:
|
||||
$(CC) -MM $(INCLUDE_OPT) *.c >depend
|
||||
|
||||
clean:
|
||||
rm -f $(MODULE) $(MODNAME).sql
|
||||
rm -f *~ $(MODULE) $(MODNAME).sql
|
||||
|
||||
ifeq (depend,$(wildcard depend))
|
||||
include depend
|
||||
|
@@ -4,7 +4,10 @@
|
||||
* This loadable module, together with my user-lock.patch applied to the
|
||||
* backend, provides support for user-level long-term cooperative locks.
|
||||
*
|
||||
* Copyright (c) 1996, Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
* Copyright (c) 1998, Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
*
|
||||
* This file is distributed under the GNU General Public License
|
||||
* either version 2, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -14,46 +17,40 @@
|
||||
#include "postgres.h"
|
||||
#include "miscadmin.h"
|
||||
#include "storage/lock.h"
|
||||
#include "storage/lmgr.h"
|
||||
#include "storage/proc.h"
|
||||
#include "storage/block.h"
|
||||
#include "storage/multilev.h"
|
||||
#include "utils/elog.h"
|
||||
|
||||
#include "user_locks.h"
|
||||
|
||||
#define USER_LOCKS_TABLE_ID 0
|
||||
|
||||
extern Oid MyDatabaseId;
|
||||
|
||||
int
|
||||
user_lock(unsigned int id1, unsigned int id2, LOCKT lockt)
|
||||
user_lock(unsigned int id1, unsigned int id2, LOCKMODE lockmode)
|
||||
{
|
||||
LOCKTAG tag;
|
||||
|
||||
memset(&tag, 0, sizeof(LOCKTAG));
|
||||
tag.dbId = MyDatabaseId;
|
||||
tag.relId = 0;
|
||||
tag.dbId = MyDatabaseId;
|
||||
tag.tupleId.ip_blkid.bi_hi = id2 >> 16;
|
||||
tag.tupleId.ip_blkid.bi_lo = id2 & 0xffff;
|
||||
tag.tupleId.ip_posid = (unsigned short) (id1 & 0xffff);
|
||||
|
||||
return LockAcquire(USER_LOCKS_TABLE_ID, &tag, lockt);
|
||||
return LockAcquire(USER_LOCKMETHOD, &tag, lockmode);
|
||||
}
|
||||
|
||||
int
|
||||
user_unlock(unsigned int id1, unsigned int id2, LOCKT lockt)
|
||||
user_unlock(unsigned int id1, unsigned int id2, LOCKMODE lockmode)
|
||||
{
|
||||
LOCKTAG tag;
|
||||
|
||||
memset(&tag, 0, sizeof(LOCKTAG));
|
||||
tag.dbId = MyDatabaseId;
|
||||
tag.relId = 0;
|
||||
tag.dbId = MyDatabaseId;
|
||||
tag.tupleId.ip_blkid.bi_hi = id2 >> 16;
|
||||
tag.tupleId.ip_blkid.bi_lo = id2 & 0xffff;
|
||||
tag.tupleId.ip_posid = (unsigned short) (id1 & 0xffff);
|
||||
|
||||
return LockRelease(USER_LOCKS_TABLE_ID, &tag, lockt);
|
||||
return LockRelease(USER_LOCKMETHOD, &tag, lockmode);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -87,7 +84,7 @@ user_unlock_all()
|
||||
PROC *proc;
|
||||
SHMEM_OFFSET location;
|
||||
|
||||
ShmemPIDLookup(getpid(), &location);
|
||||
ShmemPIDLookup(MyProcPid, &location);
|
||||
if (location == INVALID_OFFSET)
|
||||
{
|
||||
elog(NOTICE, "UserUnlockAll: unable to get proc ptr");
|
||||
@@ -95,7 +92,15 @@ user_unlock_all()
|
||||
}
|
||||
|
||||
proc = (PROC *) MAKE_PTR(location);
|
||||
return LockReleaseAll(USER_LOCKS_TABLE_ID, &proc->lockQueue);
|
||||
return LockReleaseAll(USER_LOCKMETHOD, &proc->lockQueue);
|
||||
}
|
||||
|
||||
/* end of file */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-indent-level: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
*/
|
||||
|
@@ -2,29 +2,49 @@ User locks, by Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
|
||||
This loadable module, together with my user-lock.patch applied to the
|
||||
backend, provides support for user-level long-term cooperative locks.
|
||||
For example one can write:
|
||||
|
||||
For example one can write (this example is written in TclX):
|
||||
select some_fields, user_write_lock_oid(oid) from table where id='key';
|
||||
|
||||
set rec [sql "select ...,user_write_lock_oid(oid) from table where id=$id"]
|
||||
if {[keylget rec user_write_lock_oid] == 1} {
|
||||
# the write lock has been acquired with the record, start
|
||||
# a long editing session, then update the database and
|
||||
# release the lock.
|
||||
sql "update table set ... where id=$id"
|
||||
sql "select user_write_unlock_oid([keylget rec oid])"
|
||||
} else {
|
||||
# the record has been read but the write lock couldn't be acquired,
|
||||
# so it should not be modified by the application.
|
||||
messageBox "This record is in use by another user, retry later"
|
||||
}
|
||||
Now if the returned user_write_lock_oid field is 1 you have acquired an
|
||||
user lock on the oid of the selected tuple and can now do some long operation
|
||||
on it, like let the data being edited by the user.
|
||||
If it is 0 it means that the lock has been already acquired by some other
|
||||
process and you should not use that item until the other has finished.
|
||||
Note that in this case the query returns 0 immediately without waiting on
|
||||
the lock. This is good if the lock is held for long time.
|
||||
After you have finished your work on that item you can do:
|
||||
|
||||
update table set some_fields where id='key';
|
||||
select user_write_unlock_oid(oid) from table where id='key';
|
||||
|
||||
You can also ignore the failure and go ahead but this could produce conflicts
|
||||
or inconsistent data in your application. User locks require a cooperative
|
||||
behavior between users. User locks don't interfere with the normal locks
|
||||
used by postgres for transaction processing.
|
||||
|
||||
This could also be done by setting a flag in the record itself but in
|
||||
this case you have the overhead of the updates to the record and there
|
||||
may be some locks not released if the backend or the application crashes
|
||||
before resetting the flag.
|
||||
this case you have the overhead of the updates to the records and there
|
||||
could be some locks not released if the backend or the application crashes
|
||||
before resetting the lock flag.
|
||||
It could also be done with a begin/end block but in this case the entire
|
||||
table would be locked by postgres and it is not acceptable to do this for
|
||||
a long period because other transactions would block completely.
|
||||
Note that this type of locks are handled cooperatively by the application
|
||||
and do not interfere with the normal locks used by postgres. So an user
|
||||
could still modify an user-locked record if he wanted to ignore the lock.
|
||||
|
||||
The generic user locks use two values, group and id, to identify a lock,
|
||||
which correspond to ip_posid and ip_blkid of an ItemPointerData.
|
||||
Group is a 16 bit value while id is a 32 bit integer which can also
|
||||
contain an oid. The oid user lock function, which take an oid as argument,
|
||||
use a group equal to 0.
|
||||
|
||||
The meaning of group and id is defined by the application. The user
|
||||
lock code just takes two numbers and tells you if the corresponding
|
||||
entity has been succesfully locked. What this mean is up to you.
|
||||
|
||||
My succestion is that you use the group to identify an area of your
|
||||
application and the id to identify an object in this area.
|
||||
Or you can just lock the oid of the tuples which are by definition unique.
|
||||
|
||||
Note also that a process can acquire more than one lock on the same entity
|
||||
and it must release the lock the corresponding number of times. This can
|
||||
be done calling the unlock funtion until it returns 0.
|
||||
|
@@ -1,8 +1,8 @@
|
||||
#ifndef USER_LOCKS_H
|
||||
#define USER_LOCKS_H
|
||||
|
||||
int user_lock(unsigned int id1, unsigned int id2, LOCKT lockt);
|
||||
int user_unlock(unsigned int id1, unsigned int id2, LOCKT lockt);
|
||||
int user_lock(unsigned int id1, unsigned int id2, LOCKMODE lockmode);
|
||||
int user_unlock(unsigned int id1, unsigned int id2, LOCKMODE lockmode);
|
||||
int user_write_lock(unsigned int id1, unsigned int id2);
|
||||
int user_write_unlock(unsigned int id1, unsigned int id2);
|
||||
int user_write_lock_oid(Oid oid);
|
||||
@@ -10,3 +10,11 @@ int user_write_unlock_oid(Oid oid);
|
||||
int user_unlock_all(void);
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-indent-level: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
*/
|
||||
|
@@ -1,12 +1,19 @@
|
||||
-- SQL code to define the user locks functions
|
||||
-- user_locks.sql --
|
||||
--
|
||||
-- SQL code to define the user locks functions.
|
||||
--
|
||||
-- Copyright (c) 1998, Massimo Dal Zotto <dz@cs.unitn.it>
|
||||
--
|
||||
-- This file is distributed under the GNU General Public License
|
||||
-- either version 2, or (at your option) any later version.
|
||||
|
||||
-- select user_lock(group,id,type);
|
||||
-- select user_lock(group,id,mode);
|
||||
--
|
||||
create function user_lock(int4,int4,int4) returns int4
|
||||
as 'MODULE_PATHNAME'
|
||||
language 'c';
|
||||
|
||||
-- select user_unlock(group,id,type);
|
||||
-- select user_unlock(group,id,mode);
|
||||
--
|
||||
create function user_unlock(int4,int4,int4) returns int4
|
||||
as 'MODULE_PATHNAME'
|
||||
|
Reference in New Issue
Block a user