-
Notifications
You must be signed in to change notification settings - Fork 11
PG-1213 Add SQL function for checking if WAL record is encrypted #514
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: TDE_REL_17_STABLE
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
-- Function to check if a WAL record is encrypted | ||
CREATE FUNCTION pg_tde_is_wal_record_encrypted(lsn pg_lsn, tli integer DEFAULT 0) | ||
RETURNS BOOLEAN | ||
LANGUAGE C | ||
AS 'MODULE_PATHNAME'; | ||
REVOKE ALL ON FUNCTION pg_tde_is_wal_record_encrypted(pg_lsn, integer) FROM PUBLIC; | ||
|
||
-- Function to get WAL encryption ranges | ||
CREATE FUNCTION pg_tde_get_wal_encryption_ranges | ||
(OUT start_tli integer, | ||
OUT start_lsn pg_lsn, | ||
OUT end_tli integer, | ||
OUT end_lsn pg_lsn) | ||
RETURNS SETOF RECORD | ||
LANGUAGE C | ||
AS 'MODULE_PATHNAME'; | ||
REVOKE ALL ON FUNCTION pg_tde_get_wal_encryption_ranges() FROM PUBLIC; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
comment = 'pg_tde access method' | ||
default_version = '1.0' | ||
default_version = '2.0' | ||
module_pathname = '$libdir/pg_tde' | ||
relocatable = false |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -15,10 +15,12 @@ | |||||||||||||||||
#include "storage/shmem.h" | ||||||||||||||||||
#include "utils/builtins.h" | ||||||||||||||||||
#include "utils/percona.h" | ||||||||||||||||||
#include "utils/pg_lsn.h" | ||||||||||||||||||
|
||||||||||||||||||
#include "access/pg_tde_tdemap.h" | ||||||||||||||||||
#include "access/pg_tde_xlog.h" | ||||||||||||||||||
#include "access/pg_tde_xlog_smgr.h" | ||||||||||||||||||
#include "access/pg_tde_xlog_keys.h" | ||||||||||||||||||
#include "catalog/tde_global_space.h" | ||||||||||||||||||
#include "catalog/tde_principal_key.h" | ||||||||||||||||||
#include "encryption/enc_aes.h" | ||||||||||||||||||
|
@@ -33,6 +35,8 @@ | |||||||||||||||||
|
||||||||||||||||||
PG_MODULE_MAGIC; | ||||||||||||||||||
|
||||||||||||||||||
#define PG_TDE_LIST_WAL_KEYS_RANGES_COLS 4 | ||||||||||||||||||
|
||||||||||||||||||
static void pg_tde_init_data_dir(void); | ||||||||||||||||||
|
||||||||||||||||||
static shmem_startup_hook_type prev_shmem_startup_hook = NULL; | ||||||||||||||||||
|
@@ -41,6 +45,8 @@ static shmem_request_hook_type prev_shmem_request_hook = NULL; | |||||||||||||||||
PG_FUNCTION_INFO_V1(pg_tde_extension_initialize); | ||||||||||||||||||
PG_FUNCTION_INFO_V1(pg_tde_version); | ||||||||||||||||||
PG_FUNCTION_INFO_V1(pg_tdeam_handler); | ||||||||||||||||||
PG_FUNCTION_INFO_V1(pg_tde_is_wal_record_encrypted); | ||||||||||||||||||
PG_FUNCTION_INFO_V1(pg_tde_get_wal_encryption_ranges); | ||||||||||||||||||
|
||||||||||||||||||
static void | ||||||||||||||||||
tde_shmem_request(void) | ||||||||||||||||||
|
@@ -166,3 +172,100 @@ pg_tdeam_handler(PG_FUNCTION_ARGS) | |||||||||||||||||
{ | ||||||||||||||||||
PG_RETURN_POINTER(GetHeapamTableAmRoutine()); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/* | ||||||||||||||||||
* Returns true if the WAL record at the given LSN is encrypted. | ||||||||||||||||||
*/ | ||||||||||||||||||
Datum | ||||||||||||||||||
pg_tde_is_wal_record_encrypted(PG_FUNCTION_ARGS) | ||||||||||||||||||
{ | ||||||||||||||||||
XLogRecPtr lsn = PG_GETARG_LSN(0); | ||||||||||||||||||
int tli = PG_GETARG_INT32(1); | ||||||||||||||||||
WalLocation loc; | ||||||||||||||||||
WALKeyCacheRec *keys; | ||||||||||||||||||
|
||||||||||||||||||
if (tli == 0) | ||||||||||||||||||
tli = GetWALInsertionTimeLine(); | ||||||||||||||||||
|
||||||||||||||||||
/* Load all keys for the given timeline */ | ||||||||||||||||||
loc = (WalLocation) | ||||||||||||||||||
{ | ||||||||||||||||||
.tli = tli,.lsn = 0 | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
keys = pg_tde_fetch_wal_keys(loc); | ||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't fetch them, as they are most likely are already in the cache. This should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Writes may produce new WAL key, but writes don't update cache as allocations are forbidden there. So WAL reads have some logic how to check if new key was produced and refresh cache entries. After discussion with @dAdAbird we decided that doing it the same way in this function will make code too complex and we can just read keys from disk for now. postgres/contrib/pg_tde/src/access/pg_tde_xlog_smgr.c Lines 471 to 478 in 80e0bb0
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But Also, if the cache already has items, it doesn't return the first key in the cache, but the first item added to the cache in this read. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is also no longer true since #539 now the current key is also in the cache. So reading the cache should be enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Eah, but thinking on it, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahh damn, you're right. I didn't check how cached was populated 🤦 |
||||||||||||||||||
if (!keys) | ||||||||||||||||||
PG_RETURN_BOOL(false); | ||||||||||||||||||
|
||||||||||||||||||
loc.lsn = lsn; | ||||||||||||||||||
|
||||||||||||||||||
for (WALKeyCacheRec *curr_key = keys; curr_key != NULL; curr_key = curr_key->next) | ||||||||||||||||||
{ | ||||||||||||||||||
if (wal_location_cmp(loc, curr_key->start) >= 0 && | ||||||||||||||||||
wal_location_cmp(loc, curr_key->end) < 0) | ||||||||||||||||||
PG_RETURN_BOOL(curr_key->key.type == WAL_KEY_TYPE_ENCRYPTED); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
PG_RETURN_BOOL(false); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/* | ||||||||||||||||||
* Returns WAL encryption ranges. WAL records within the LSN range are encrypted. | ||||||||||||||||||
*/ | ||||||||||||||||||
Datum | ||||||||||||||||||
pg_tde_get_wal_encryption_ranges(PG_FUNCTION_ARGS) | ||||||||||||||||||
{ | ||||||||||||||||||
Tuplestorestate *tupstore; | ||||||||||||||||||
TupleDesc tupdesc; | ||||||||||||||||||
ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; | ||||||||||||||||||
MemoryContext per_query_ctx; | ||||||||||||||||||
MemoryContext oldcontext; | ||||||||||||||||||
WALKeyCacheRec *keys; | ||||||||||||||||||
WalLocation loc = {.tli = 0,.lsn = 0}; | ||||||||||||||||||
artemgavrilov marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
|
||||||||||||||||||
/* check to see if caller supports us returning a tuplestore */ | ||||||||||||||||||
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) | ||||||||||||||||||
ereport(ERROR, | ||||||||||||||||||
errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||||||||||||||||
errmsg("set-valued function called in context that cannot accept a set")); | ||||||||||||||||||
if (!(rsinfo->allowedModes & SFRM_Materialize)) | ||||||||||||||||||
ereport(ERROR, | ||||||||||||||||||
errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||||||||||||||||
errmsg("materialize mode required, but it is not allowed in this context")); | ||||||||||||||||||
|
||||||||||||||||||
/* Switch into long-lived context to construct returned data structures */ | ||||||||||||||||||
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; | ||||||||||||||||||
oldcontext = MemoryContextSwitchTo(per_query_ctx); | ||||||||||||||||||
|
||||||||||||||||||
/* Build a tuple descriptor for our result type */ | ||||||||||||||||||
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) | ||||||||||||||||||
elog(ERROR, "return type must be a row type"); | ||||||||||||||||||
|
||||||||||||||||||
tupstore = tuplestore_begin_heap(true, false, work_mem); | ||||||||||||||||||
rsinfo->returnMode = SFRM_Materialize; | ||||||||||||||||||
rsinfo->setResult = tupstore; | ||||||||||||||||||
rsinfo->setDesc = tupdesc; | ||||||||||||||||||
|
||||||||||||||||||
MemoryContextSwitchTo(oldcontext); | ||||||||||||||||||
|
||||||||||||||||||
keys = pg_tde_fetch_wal_keys(loc); | ||||||||||||||||||
|
||||||||||||||||||
for (WALKeyCacheRec *curr_key = keys; curr_key != NULL; curr_key = curr_key->next) | ||||||||||||||||||
{ | ||||||||||||||||||
Datum values[PG_TDE_LIST_WAL_KEYS_RANGES_COLS] = {0}; | ||||||||||||||||||
bool nulls[PG_TDE_LIST_WAL_KEYS_RANGES_COLS] = {0}; | ||||||||||||||||||
int i = 0; | ||||||||||||||||||
|
||||||||||||||||||
if (curr_key->key.type != WAL_KEY_TYPE_ENCRYPTED) | ||||||||||||||||||
continue; | ||||||||||||||||||
|
||||||||||||||||||
values[i++] = Int64GetDatum(curr_key->start.tli); | ||||||||||||||||||
values[i++] = Int64GetDatum(curr_key->start.lsn); | ||||||||||||||||||
values[i++] = Int64GetDatum(curr_key->end.tli); | ||||||||||||||||||
values[i++] = Int64GetDatum(curr_key->end.lsn); | ||||||||||||||||||
|
||||||||||||||||||
tuplestore_putvalues(tupstore, tupdesc, values, nulls); | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return (Datum) 0; | ||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.