|  | 
| 15 | 15 | #include "utils/rel.h" | 
| 16 | 16 | #include "utils/builtins.h" | 
| 17 | 17 | #include "catalog/pg_class.h" | 
|  | 18 | +#include "catalog/pg_database.h" | 
| 18 | 19 | #include "commands/defrem.h" | 
| 19 | 20 | #include "commands/sequence.h" | 
| 20 | 21 | #include "access/table.h" | 
|  | 
| 23 | 24 | #include "catalog/namespace.h" | 
| 24 | 25 | #include "commands/event_trigger.h" | 
| 25 | 26 | #include "common/pg_tde_utils.h" | 
|  | 27 | +#include "storage/lmgr.h" | 
|  | 28 | +#include "tcop/utility.h" | 
|  | 29 | +#include "utils/fmgroids.h" | 
|  | 30 | +#include "utils/syscache.h" | 
| 26 | 31 | #include "pg_tde_event_capture.h" | 
| 27 | 32 | #include "pg_tde_guc.h" | 
|  | 33 | +#include "access/pg_tde_tdemap.h" | 
| 28 | 34 | #include "catalog/tde_principal_key.h" | 
| 29 | 35 | #include "miscadmin.h" | 
| 30 | 36 | #include "access/tableam.h" | 
|  | 
| 34 | 40 | static TdeCreateEvent tdeCurrentCreateEvent = {.tid = {.value = 0}}; | 
| 35 | 41 | static bool alterSetAccessMethod = false; | 
| 36 | 42 | 
 | 
|  | 43 | +static Oid	get_db_oid(const char *name); | 
| 37 | 44 | static void reset_current_tde_create_event(void); | 
| 38 | 45 | static Oid	get_tde_table_am_oid(void); | 
| 39 | 46 | 
 | 
| @@ -315,3 +322,159 @@ get_tde_table_am_oid(void) | 
| 315 | 322 | { | 
| 316 | 323 | 	return get_table_am_oid("tde_heap", false); | 
| 317 | 324 | } | 
|  | 325 | + | 
|  | 326 | +static ProcessUtility_hook_type next_ProcessUtility_hook = NULL; | 
|  | 327 | + | 
|  | 328 | +/* | 
|  | 329 | + * Handles utility commands which we cannot handle in the event trigger. | 
|  | 330 | + */ | 
|  | 331 | +static void | 
|  | 332 | +pg_tde_proccess_utility(PlannedStmt *pstmt, | 
|  | 333 | +						const char *queryString, | 
|  | 334 | +						bool readOnlyTree, | 
|  | 335 | +						ProcessUtilityContext context, | 
|  | 336 | +						ParamListInfo params, | 
|  | 337 | +						QueryEnvironment *queryEnv, | 
|  | 338 | +						DestReceiver *dest, | 
|  | 339 | +						QueryCompletion *qc) | 
|  | 340 | +{ | 
|  | 341 | +	Node	   *parsetree = pstmt->utilityStmt; | 
|  | 342 | + | 
|  | 343 | +	switch (nodeTag(parsetree)) | 
|  | 344 | +	{ | 
|  | 345 | +		case T_CreatedbStmt: | 
|  | 346 | + | 
|  | 347 | +			CreatedbStmt *stmt = castNode(CreatedbStmt, parsetree); | 
|  | 348 | +			ListCell   *option; | 
|  | 349 | +			char	   *dbtemplate = "template1"; | 
|  | 350 | +			char	   *strategy = "wal_log"; | 
|  | 351 | + | 
|  | 352 | +			foreach(option, stmt->options) | 
|  | 353 | +			{ | 
|  | 354 | +				DefElem    *defel = (DefElem *) lfirst(option); | 
|  | 355 | + | 
|  | 356 | +				if (strcmp(defel->defname, "template") == 0) | 
|  | 357 | +					dbtemplate = defGetString(defel); | 
|  | 358 | +				else if (strcmp(defel->defname, "strategy") == 0) | 
|  | 359 | +					strategy = defGetString(defel); | 
|  | 360 | +			} | 
|  | 361 | + | 
|  | 362 | +			if (pg_strcasecmp(strategy, "file_copy") == 0) | 
|  | 363 | +			{ | 
|  | 364 | +				Oid dbOid = get_db_oid(dbtemplate); | 
|  | 365 | + | 
|  | 366 | +				if (dbOid != InvalidOid) | 
|  | 367 | +				{ | 
|  | 368 | +					int			count = pg_tde_count_relations(dbOid); | 
|  | 369 | + | 
|  | 370 | +					if (count > 0) | 
|  | 371 | +						ereport(ERROR, | 
|  | 372 | +							errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | 
|  | 373 | +							errmsg("The FILE_COPY strategy cannot be used when there are encrypted objects in the template database: %d objects found", count), | 
|  | 374 | +							errhint("Use the WAL_LOG strategy instead.")); | 
|  | 375 | +				} | 
|  | 376 | +			} | 
|  | 377 | +			break; | 
|  | 378 | +		default: | 
|  | 379 | +			break; | 
|  | 380 | +	} | 
|  | 381 | + | 
|  | 382 | +	if (next_ProcessUtility_hook) | 
|  | 383 | +		(*next_ProcessUtility_hook) (pstmt, queryString, readOnlyTree, | 
|  | 384 | +									 context, params, queryEnv, | 
|  | 385 | +									 dest, qc); | 
|  | 386 | +	else | 
|  | 387 | +		standard_ProcessUtility(pstmt, queryString, readOnlyTree, | 
|  | 388 | +								context, params, queryEnv, | 
|  | 389 | +								dest, qc); | 
|  | 390 | +} | 
|  | 391 | + | 
|  | 392 | +void | 
|  | 393 | +TdeEventCaptureInit(void) | 
|  | 394 | +{ | 
|  | 395 | +	next_ProcessUtility_hook = ProcessUtility_hook; | 
|  | 396 | +	ProcessUtility_hook = pg_tde_proccess_utility; | 
|  | 397 | +} | 
|  | 398 | + | 
|  | 399 | +/* | 
|  | 400 | + * A stripped down version of get_db_info() from src/backend/commands/dbcommands.c | 
|  | 401 | + */ | 
|  | 402 | +static Oid | 
|  | 403 | +get_db_oid(const char *name) | 
|  | 404 | +{ | 
|  | 405 | +	Oid			resDbOid = InvalidOid; | 
|  | 406 | +	Relation	relation; | 
|  | 407 | + | 
|  | 408 | +	Assert(name); | 
|  | 409 | + | 
|  | 410 | +	relation = table_open(DatabaseRelationId, AccessShareLock); | 
|  | 411 | + | 
|  | 412 | +	/* | 
|  | 413 | +	 * Loop covers the rare case where the database is renamed before we can | 
|  | 414 | +	 * lock it.  We try again just in case we can find a new one of the same | 
|  | 415 | +	 * name. | 
|  | 416 | +	 */ | 
|  | 417 | +	for (;;) | 
|  | 418 | +	{ | 
|  | 419 | +		ScanKeyData scanKey; | 
|  | 420 | +		SysScanDesc scan; | 
|  | 421 | +		HeapTuple	tuple; | 
|  | 422 | +		Oid			dbOid; | 
|  | 423 | + | 
|  | 424 | +		/* | 
|  | 425 | +		 * there's no syscache for database-indexed-by-name, so must do it the | 
|  | 426 | +		 * hard way | 
|  | 427 | +		 */ | 
|  | 428 | +		ScanKeyInit(&scanKey, | 
|  | 429 | +					Anum_pg_database_datname, | 
|  | 430 | +					BTEqualStrategyNumber, F_NAMEEQ, | 
|  | 431 | +					CStringGetDatum(name)); | 
|  | 432 | + | 
|  | 433 | +		scan = systable_beginscan(relation, DatabaseNameIndexId, true, | 
|  | 434 | +								  NULL, 1, &scanKey); | 
|  | 435 | + | 
|  | 436 | +		tuple = systable_getnext(scan); | 
|  | 437 | + | 
|  | 438 | +		if (!HeapTupleIsValid(tuple)) | 
|  | 439 | +		{ | 
|  | 440 | +			/* definitely no database of that name */ | 
|  | 441 | +			systable_endscan(scan); | 
|  | 442 | +			break; | 
|  | 443 | +		} | 
|  | 444 | + | 
|  | 445 | +		dbOid = ((Form_pg_database) GETSTRUCT(tuple))->oid; | 
|  | 446 | + | 
|  | 447 | +		systable_endscan(scan); | 
|  | 448 | + | 
|  | 449 | +		/* | 
|  | 450 | +		 * Now that we have a database OID, we can try to lock the DB. | 
|  | 451 | +		 */ | 
|  | 452 | +		LockSharedObject(DatabaseRelationId, dbOid, 0, AccessExclusiveLock); | 
|  | 453 | + | 
|  | 454 | +		/* | 
|  | 455 | +		 * And now, re-fetch the tuple by OID.  If it's still there and still | 
|  | 456 | +		 * the same name, we win; else, drop the lock and loop back to try | 
|  | 457 | +		 * again. | 
|  | 458 | +		 */ | 
|  | 459 | +		tuple = SearchSysCache1(DATABASEOID, ObjectIdGetDatum(dbOid)); | 
|  | 460 | +		if (HeapTupleIsValid(tuple)) | 
|  | 461 | +		{ | 
|  | 462 | +			Form_pg_database dbform = (Form_pg_database) GETSTRUCT(tuple); | 
|  | 463 | + | 
|  | 464 | +			if (strcmp(name, NameStr(dbform->datname)) == 0) | 
|  | 465 | +			{ | 
|  | 466 | +				resDbOid = dbOid; | 
|  | 467 | +				ReleaseSysCache(tuple); | 
|  | 468 | +				break; | 
|  | 469 | +			} | 
|  | 470 | +			/* can only get here if it was just renamed */ | 
|  | 471 | +			ReleaseSysCache(tuple); | 
|  | 472 | +		} | 
|  | 473 | + | 
|  | 474 | +		UnlockSharedObject(DatabaseRelationId, dbOid, 0, AccessExclusiveLock); | 
|  | 475 | +	} | 
|  | 476 | + | 
|  | 477 | +	table_close(relation, AccessShareLock); | 
|  | 478 | + | 
|  | 479 | +	return resDbOid; | 
|  | 480 | +} | 
0 commit comments