Hi

út 18. 12. 2018 v 16:11 odesílatel Filip Rembiałkowski <
filip.rembialkow...@gmail.com> napsal:

> Hi,
>
> I propose a simple patch (works-for-me), which adds --force (-f)
> option to dropdb utility.
>
> Pros:  This seems to be a desired option for many sysadmins, as this
> thread proves:
> https://dba.stackexchange.com/questions/11893/force-drop-db-while-others-may-be-connected
> Cons: another possible foot-gun for the unwary.
>
> Obviously this patch needs some more work (see TODO note inside).
>
> Please share opinions if this makes sense at all, and has any chance
> going upstream.
>
> The regression tests is simplistic, please help with an example of
> multi-session test so I can follow.
>

Still one my customer use a patch that implement FORCE on SQL level. It is
necessary under higher load when is not easy to synchronize clients.

Regards

Pavel

>
>
> Thanks,
> Filip
>
diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c
index 68c43620ef..008cac25cc 100644
--- a/src/backend/commands/dbcommands.c
+++ b/src/backend/commands/dbcommands.c
@@ -496,7 +496,7 @@ createdb(ParseState *pstate, const CreatedbStmt *stmt)
 		 * to wait 5 sec. We try to raise warning after 1 minute and and raise
 		 * a error after 5 minutes.
 		 */
-		if (!CountOtherDBBackends(src_dboid, &notherbackends, &npreparedxacts, true))
+		if (!CountOtherDBBackends(src_dboid, &notherbackends, &npreparedxacts, true, false))
 			break;
 
 		if (loops++ % 12 == 0)
@@ -798,7 +798,7 @@ createdb_failure_callback(int code, Datum arg)
  * DROP DATABASE
  */
 void
-dropdb(const char *dbname, bool missing_ok)
+dropdb(const char *dbname, bool missing_ok, bool force)
 {
 	Oid			db_id;
 	bool		db_istemplate;
@@ -806,6 +806,7 @@ dropdb(const char *dbname, bool missing_ok)
 	HeapTuple	tup;
 	int			notherbackends;
 	int			npreparedxacts;
+	int			loops = 0;
 	int			nslots,
 				nslots_active;
 	int			nsubscriptions;
@@ -889,12 +890,33 @@ dropdb(const char *dbname, bool missing_ok)
 	 *
 	 * As in CREATE DATABASE, check this after other error conditions.
 	 */
-	if (CountOtherDBBackends(db_id, &notherbackends, &npreparedxacts, false))
-		ereport(ERROR,
-				(errcode(ERRCODE_OBJECT_IN_USE),
-				 errmsg("database \"%s\" is being accessed by other users",
-						dbname),
-				 errdetail_busy_db(notherbackends, npreparedxacts)));
+	for (;;)
+	{
+		/*
+		 * CountOtherDBBackends check usage of database by other backends and try
+		 * to wait 5 sec. We try to raise warning after 1 minute and and raise
+		 * a error after 5 minutes.
+		 */
+		if (!CountOtherDBBackends(db_id, &notherbackends, &npreparedxacts, true, force))
+			break;
+
+		if (force && loops++ % 12 == 0)
+			ereport(WARNING,
+					(errcode(ERRCODE_OBJECT_IN_USE),
+				errmsg("source database \"%s\" is being accessed by other users",
+					   dbname),
+					 errdetail_busy_db(notherbackends, npreparedxacts)));
+
+		/* without "force" flag raise exception immediately, or after 5 minutes */
+		if (!force || loops % 60 == 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_OBJECT_IN_USE),
+				errmsg("source database \"%s\" is being accessed by other users",
+					   dbname),
+					 errdetail_busy_db(notherbackends, npreparedxacts)));
+
+		CHECK_FOR_INTERRUPTS();
+	}
 
 	/*
 	 * Check if there are subscriptions defined in the target database.
@@ -1053,7 +1075,7 @@ RenameDatabase(const char *oldname, const char *newname)
 	 *
 	 * As in CREATE DATABASE, check this after other error conditions.
 	 */
-	if (CountOtherDBBackends(db_id, &notherbackends, &npreparedxacts, false))
+	if (CountOtherDBBackends(db_id, &notherbackends, &npreparedxacts, false, false))
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_IN_USE),
 				 errmsg("database \"%s\" is being accessed by other users",
@@ -1183,7 +1205,7 @@ movedb(const char *dbname, const char *tblspcname)
 	 *
 	 * As in CREATE DATABASE, check this after other error conditions.
 	 */
-	if (CountOtherDBBackends(db_id, &notherbackends, &npreparedxacts, false))
+	if (CountOtherDBBackends(db_id, &notherbackends, &npreparedxacts, false, false))
 		ereport(ERROR,
 				(errcode(ERRCODE_OBJECT_IN_USE),
 				 errmsg("database \"%s\" is being accessed by other users",
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 8c8384cd6b..e147e594ab 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3755,6 +3755,7 @@ _copyDropdbStmt(const DropdbStmt *from)
 
 	COPY_STRING_FIELD(dbname);
 	COPY_SCALAR_FIELD(missing_ok);
+	COPY_SCALAR_FIELD(force);
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 68d38fcba1..ba7bee4bb0 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1655,6 +1655,7 @@ _equalDropdbStmt(const DropdbStmt *a, const DropdbStmt *b)
 {
 	COMPARE_STRING_FIELD(dbname);
 	COMPARE_SCALAR_FIELD(missing_ok);
+	COMPARE_SCALAR_FIELD(force);
 
 	return true;
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7d0de99baf..358e878c70 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9817,12 +9817,30 @@ DropdbStmt: DROP DATABASE database_name
 					n->dbname = $3;
 					n->missing_ok = FALSE;
 					$$ = (Node *)n;
+					n->force = false;
 				}
 			| DROP DATABASE IF_P EXISTS database_name
 				{
 					DropdbStmt *n = makeNode(DropdbStmt);
 					n->dbname = $5;
 					n->missing_ok = TRUE;
+					n->force = false;
+					$$ = (Node *)n;
+				}
+			| DROP DATABASE database_name FORCE
+				{
+					DropdbStmt *n = makeNode(DropdbStmt);
+					n->dbname = $3;
+					n->missing_ok = false;
+					n->force = true;
+					$$ = (Node *)n;
+				}
+			| DROP DATABASE IF_P EXISTS database_name FORCE
+				{
+					DropdbStmt *n = makeNode(DropdbStmt);
+					n->dbname = $5;
+					n->missing_ok = true;
+					n->force = true;
 					$$ = (Node *)n;
 				}
 		;
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 8690a97dee..3080a81f7f 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -2895,10 +2895,13 @@ CountUserBackends(Oid roleid)
  * continue (in simple implementation based only on PGPROC entries). In this case we should
  * not calculate this process safely, becase createdb holds a lock.
  *
+ * A option "force_terminate" enforce termination other sessions, that uses database. When we
+ * try to drop database, we should to calculate with all attached process.
  *
  */
 bool
-CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared, bool is_createdb_cmd)
+CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared,
+						    bool is_createdb_cmd, bool force_terminate)
 {
 	ProcArrayStruct *arrayP = procArray;
 
@@ -2943,6 +2946,18 @@ CountOtherDBBackends(Oid databaseId, int *nbackends, int *nprepared, bool is_cre
 				if ((pgxact->vacuumFlags & PROC_IS_AUTOVACUUM) &&
 					nautovacs < MAXAUTOVACPIDS)
 					autovac_pids[nautovacs++] = proc->pid;
+				else
+				{
+					if (force_terminate)
+					{
+						/* try to terminate backend */
+#ifdef HAVE_SETSID
+							kill(-(proc->pid), SIGTERM);
+#else
+							kill(proc->pid, SIGTERM)
+#endif
+					}
+				}
 			}
 		}
 
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 82a707af7b..2304dce9d9 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -610,7 +610,7 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
 				/* no event triggers for global objects */
 				PreventTransactionChain(isTopLevel, "DROP DATABASE");
-				dropdb(stmt->dbname, stmt->missing_ok);
+				dropdb(stmt->dbname, stmt->missing_ok, stmt->force);
 			}
 			break;
 
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index f42c8cdbe3..e267dc0a28 100644
--- a/src/include/commands/dbcommands.h
+++ b/src/include/commands/dbcommands.h
@@ -20,7 +20,7 @@
 #include "nodes/parsenodes.h"
 
 extern Oid	createdb(ParseState *pstate, const CreatedbStmt *stmt);
-extern void dropdb(const char *dbname, bool missing_ok);
+extern void dropdb(const char *dbname, bool missing_ok, bool force);
 extern ObjectAddress RenameDatabase(const char *oldname, const char *newname);
 extern Oid	AlterDatabase(ParseState *pstate, AlterDatabaseStmt *stmt, bool isTopLevel);
 extern Oid	AlterDatabaseSet(AlterDatabaseSetStmt *stmt);
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ecb6cd0249..d3147ab6f9 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -3053,6 +3053,7 @@ typedef struct DropdbStmt
 	NodeTag		type;
 	char	   *dbname;			/* database to drop */
 	bool		missing_ok;		/* skip error if db is missing? */
+	bool		force;			/* terminate all other sessions in db */
 } DropdbStmt;
 
 /* ----------------------
diff --git a/src/include/storage/procarray.h b/src/include/storage/procarray.h
index 298492afa1..f6561d11c7 100644
--- a/src/include/storage/procarray.h
+++ b/src/include/storage/procarray.h
@@ -113,7 +113,7 @@ extern void CancelDBBackends(Oid databaseid, ProcSignalReason sigmode, bool conf
 extern int	CountUserBackends(Oid roleid);
 extern bool CountOtherDBBackends(Oid databaseId,
 					 int *nbackends, int *nprepared,
-					 bool is_createdb_cmd);
+					 bool is_createdb_cmd, bool force_terminate);
 
 extern void XidCacheRemoveRunningXids(TransactionId xid,
 						  int nxids, const TransactionId *xids,

Reply via email to