|
52 | 52 | #include"access/xact.h"
|
53 | 53 | #include"access/xlog.h"
|
54 | 54 | #include"catalog/catalog.h"
|
| 55 | +#include"catalog/pg_authid.h" |
| 56 | +#include"commands/dbcommands.h" |
55 | 57 | #include"miscadmin.h"
|
56 | 58 | #include"pgstat.h"
|
57 | 59 | #include"storage/proc.h"
|
@@ -2970,6 +2972,118 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared)
|
2970 | 2972 | return true;/* timed out, still conflicts */
|
2971 | 2973 | }
|
2972 | 2974 |
|
| 2975 | +/* |
| 2976 | + * Terminate existing connections to the specified database. This routine |
| 2977 | + * is used by the DROP DATABASE command when user has asked to forcefully |
| 2978 | + * drop the database. |
| 2979 | + * |
| 2980 | + * The current backend is always ignored; it is caller's responsibility to |
| 2981 | + * check whether the current backend uses the given DB, if it's important. |
| 2982 | + * |
| 2983 | + * It doesn't allow to terminate the connections even if there is a one |
| 2984 | + * backend with the prepared transaction in the target database. |
| 2985 | + */ |
| 2986 | +void |
| 2987 | +TerminateOtherDBBackends(OiddatabaseId) |
| 2988 | +{ |
| 2989 | +ProcArrayStruct*arrayP=procArray; |
| 2990 | +List*pids=NIL; |
| 2991 | +intnprepared=0; |
| 2992 | +inti; |
| 2993 | + |
| 2994 | +LWLockAcquire(ProcArrayLock,LW_SHARED); |
| 2995 | + |
| 2996 | +for (i=0;i<procArray->numProcs;i++) |
| 2997 | +{ |
| 2998 | +intpgprocno=arrayP->pgprocnos[i]; |
| 2999 | +PGPROC*proc=&allProcs[pgprocno]; |
| 3000 | + |
| 3001 | +if (proc->databaseId!=databaseId) |
| 3002 | +continue; |
| 3003 | +if (proc==MyProc) |
| 3004 | +continue; |
| 3005 | + |
| 3006 | +if (proc->pid!=0) |
| 3007 | +pids=lappend_int(pids,proc->pid); |
| 3008 | +else |
| 3009 | +nprepared++; |
| 3010 | +} |
| 3011 | + |
| 3012 | +LWLockRelease(ProcArrayLock); |
| 3013 | + |
| 3014 | +if (nprepared>0) |
| 3015 | +ereport(ERROR, |
| 3016 | +(errcode(ERRCODE_OBJECT_IN_USE), |
| 3017 | +errmsg("database \"%s\" is being used by prepared transaction", |
| 3018 | +get_database_name(databaseId)), |
| 3019 | +errdetail_plural("There is %d prepared transaction using the database.", |
| 3020 | +"There are %d prepared transactions using the database.", |
| 3021 | +nprepared, |
| 3022 | +nprepared))); |
| 3023 | + |
| 3024 | +if (pids) |
| 3025 | +{ |
| 3026 | +ListCell*lc; |
| 3027 | + |
| 3028 | +/* |
| 3029 | + * Check whether we have the necessary rights to terminate other |
| 3030 | + * sessions. We don't terminate any session untill we ensure that we |
| 3031 | + * have rights on all the sessions to be terminated. These checks are |
| 3032 | + * the same as we do in pg_terminate_backend. |
| 3033 | + * |
| 3034 | + * In this case we don't raise some warnings - like "PID %d is not a |
| 3035 | + * PostgreSQL server process", because for us already finished session |
| 3036 | + * is not a problem. |
| 3037 | + */ |
| 3038 | +foreach(lc,pids) |
| 3039 | +{ |
| 3040 | +intpid=lfirst_int(lc); |
| 3041 | +PGPROC*proc=BackendPidGetProc(pid); |
| 3042 | + |
| 3043 | +if (proc!=NULL) |
| 3044 | +{ |
| 3045 | +/* Only allow superusers to signal superuser-owned backends. */ |
| 3046 | +if (superuser_arg(proc->roleId)&& !superuser()) |
| 3047 | +ereport(ERROR, |
| 3048 | +(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
| 3049 | + (errmsg("must be a superuser to terminate superuser process")))); |
| 3050 | + |
| 3051 | +/* Users can signal backends they have role membership in. */ |
| 3052 | +if (!has_privs_of_role(GetUserId(),proc->roleId)&& |
| 3053 | +!has_privs_of_role(GetUserId(),DEFAULT_ROLE_SIGNAL_BACKENDID)) |
| 3054 | +ereport(ERROR, |
| 3055 | +(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
| 3056 | + (errmsg("must be a member of the role whose process is being terminated or member of pg_signal_backend")))); |
| 3057 | +} |
| 3058 | +} |
| 3059 | + |
| 3060 | +/* |
| 3061 | + * There's a race condition here: once we release the ProcArrayLock, |
| 3062 | + * it's possible for the session to exit before we issue kill. That |
| 3063 | + * race condition possibility seems too unlikely to worry about. See |
| 3064 | + * pg_signal_backend. |
| 3065 | + */ |
| 3066 | +foreach(lc,pids) |
| 3067 | +{ |
| 3068 | +intpid=lfirst_int(lc); |
| 3069 | +PGPROC*proc=BackendPidGetProc(pid); |
| 3070 | + |
| 3071 | +if (proc!=NULL) |
| 3072 | +{ |
| 3073 | +/* |
| 3074 | + * If we have setsid(), signal the backend's whole process |
| 3075 | + * group |
| 3076 | + */ |
| 3077 | +#ifdefHAVE_SETSID |
| 3078 | +(void)kill(-pid,SIGTERM); |
| 3079 | +#else |
| 3080 | +(void)kill(pid,SIGTERM); |
| 3081 | +#endif |
| 3082 | +} |
| 3083 | +} |
| 3084 | +} |
| 3085 | +} |
| 3086 | + |
2973 | 3087 | /*
|
2974 | 3088 | * ProcArraySetReplicationSlotXmin
|
2975 | 3089 | *
|
|