From 169cf5a38e42fce00a238367e5579d791f2fc4ff Mon Sep 17 00:00:00 2001 From: Arseny Kositsin Date: Wed, 19 Feb 2025 15:18:25 +0300 Subject: [PATCH 1/7] [PGPRO-12159] Added pageinspect functions for rum. This commit adds three functions for low-level exploration of the index's rum pages: 1) rum_metapage_info() -- is used to examine the information posted on the meta page (flags: {meta}). 2) rum_page_opaque_info() -- is used to examine information that is placed in the opaque area of the index page (any index page). 3) rum_leaf_data_page_items() -- is used to examine the information that is placed on the leaf pages of the posting tree (flags: {leaf, data}). To extract information, all these functions need to pass the index name and the page number. Tags: rum --- Makefile | 2 +- rum_init.sql | 38 +++ src/rum_debug_funcs.c | 670 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 709 insertions(+), 1 deletion(-) create mode 100644 src/rum_debug_funcs.c diff --git a/Makefile b/Makefile index a8d510019d..68f70a9dd2 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ OBJS = src/rumsort.o src/rum_ts_utils.o src/rumtsquery.o \ src/rumbtree.o src/rumbulk.o src/rumdatapage.o \ src/rumentrypage.o src/rumget.o src/ruminsert.o \ src/rumscan.o src/rumutil.o src/rumvacuum.o src/rumvalidate.o \ - src/btree_rum.o src/rum_arr_utils.o $(WIN32RES) + src/btree_rum.o src/rum_arr_utils.o src/rum_debug_funcs.o $(WIN32RES) DATA_updates = rum--1.0--1.1.sql rum--1.1--1.2.sql \ rum--1.2--1.3.sql diff --git a/rum_init.sql b/rum_init.sql index 621c4d2b9f..7dcf3ef8ca 100644 --- a/rum_init.sql +++ b/rum_init.sql @@ -1724,3 +1724,41 @@ RETURNS float4 AS 'MODULE_PATHNAME', 'rum_ts_score_td' LANGUAGE C IMMUTABLE STRICT; +/*--------------------RUM debug functions-----------------------*/ + +CREATE FUNCTION rum_metapage_info( + IN rel_name text, + IN blk_num int8, + OUT pending_head bigint, + OUT pending_tail bigint, + OUT tail_free_size int4, + OUT n_pending_pages bigint, + OUT n_pending_tuples bigint, + OUT n_total_pages bigint, + OUT n_entry_pages bigint, + OUT n_data_pages bigint, + OUT n_entries bigint, + OUT version bigint) +AS 'MODULE_PATHNAME', 'rum_metapage_info' +LANGUAGE C STRICT PARALLEL SAFE; + +CREATE FUNCTION rum_page_opaque_info( + IN rel_name text, + IN blk_num int8, + OUT leftlink bigint, + OUT rightlink bigint, + OUT maxoff int4, + OUT freespace int4, + OUT flags text[]) +AS 'MODULE_PATHNAME', 'rum_page_opaque_info' +LANGUAGE C STRICT PARALLEL SAFE; + +CREATE FUNCTION rum_leaf_data_page_items( + IN rel_name text, + IN blk_num int8, + OUT tuple_id tid, + OUT add_info_is_null bool, + OUT addInfo varchar) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'rum_leaf_data_page_items' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/src/rum_debug_funcs.c b/src/rum_debug_funcs.c new file mode 100644 index 0000000000..4e37fbf640 --- /dev/null +++ b/src/rum_debug_funcs.c @@ -0,0 +1,670 @@ +/* + * rum_debug_funcs.c + * Functions to investigate the content of RUM indexes + * + * Copyright (c) 2025, Postgres Professional + * + * IDENTIFICATION + * contrib/rum/rum_debug_funcs.c + * + * LIST OF ISSUES: + * 1) Strangely, the index version is returned in the + * rum_metapage_info() function. + * + * 2) Using obsolete macros in the addInfoGetText() function. + * + * 3) I/O functions were not available for all types in + * in the addInfoGetText() function. + * + * 4) The unclear logic of choosing the attribute number + * that the addInfo corresponds to. + */ + +#include "postgres.h" +#include "fmgr.h" +#include "funcapi.h" +#include "catalog/namespace.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "access/relation.h" +#include "utils/varlena.h" +#include "rum.h" + +PG_FUNCTION_INFO_V1(rum_metapage_info); +PG_FUNCTION_INFO_V1(rum_page_opaque_info); +PG_FUNCTION_INFO_V1(rum_leaf_data_page_items); + +static Page get_page_from_raw(bytea *raw_page); +static Datum addInfoGetText(Datum addInfo, Oid atttypid); +static Relation get_rel_from_name(text *relname); +static bytea *get_rel_raw_page(Relation rel, BlockNumber blkno); + +/* + * The rum_metapage_info() function is used to retrieve + * information stored on the meta page of the rum index. + * To scan, need the index name and the page number. + * (for the meta page blkno = 0). + */ +Datum +rum_metapage_info(PG_FUNCTION_ARGS) +{ + /* Reading input arguments */ + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); + + Relation rel; /* needed to initialize the RumState + structure */ + + bytea *raw_page; /* the raw page obtained from rel */ + RumPageOpaque opaq; /* data from the opaque area of the page */ + RumMetaPageData *metadata; /* data stored on the meta page */ + Page page; /* the page to be scanned */ + + TupleDesc tupdesc; /* description of the result tuple */ + HeapTuple resultTuple; /* for the results */ + Datum values[10]; /* return values */ + bool nulls[10]; /* true if the corresponding value is NULL */ + + /* Only the superuser can use this */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + /* Getting rel by name and raw page by number */ + rel = get_rel_from_name(relname); + raw_page = get_rel_raw_page(rel, blkno); + relation_close(rel, AccessShareLock); + + /* Getting a copy of the page from the raw page */ + page = get_page_from_raw(raw_page); + + /* If the page is new, the function should return NULL */ + if (PageIsNew(page)) + PG_RETURN_NULL(); + + /* Checking the size of the opaque area of the page */ + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(RumPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid RUM metapage"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(RumPageOpaqueData)), + (int) PageGetSpecialSize(page)))); + + /* Getting a page description from an opaque area */ + opaq = RumPageGetOpaque(page); + + /* Checking the flags */ + if (opaq->flags != RUM_META) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a RUM metapage"), + errdetail("Flags %04X, expected %04X", + opaq->flags, RUM_META))); + + /* 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"); + + /* Getting information from the meta page */ + metadata = RumPageGetMeta(page); + + memset(nulls, 0, sizeof(nulls)); + + /* + * Writing data from metadata to values. + * + * The first five values are obsolete because the + * pending list was removed from the rum index. + */ + values[0] = Int64GetDatum(metadata->head); + values[1] = Int64GetDatum(metadata->tail); + values[2] = Int32GetDatum(metadata->tailFreeSize); + values[3] = Int64GetDatum(metadata->nPendingPages); + values[4] = Int64GetDatum(metadata->nPendingHeapTuples); + values[5] = Int64GetDatum(metadata->nTotalPages); + values[6] = Int64GetDatum(metadata->nEntryPages); + values[7] = Int64GetDatum(metadata->nDataPages); + values[8] = Int64GetDatum(metadata->nEntries); + values[9] = Int64GetDatum(metadata->rumVersion); + + /* Build and return the result tuple */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + /* Returning the result */ + return HeapTupleGetDatum(resultTuple); +} + +/* + * The rum_page_opaque_info() function is used to retrieve + * information stored in the opaque area of the index rum + * page. To scan, need the index name and the page number. + */ +Datum +rum_page_opaque_info(PG_FUNCTION_ARGS) +{ + /* Reading input arguments */ + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); + + Relation rel; /* needed to initialize the RumState + structure */ + + bytea *raw_page; /* the raw page obtained from rel */ + RumPageOpaque opaq; /* data from the opaque area of the page */ + Page page; /* the page to be scanned */ + + HeapTuple resultTuple; /* for the results */ + TupleDesc tupdesc; /* description of the result tuple */ + + Datum values[5]; /* return values */ + bool nulls[5]; /* true if the corresponding value is NULL */ + Datum flags[16]; /* array with flags in text format */ + int nflags = 0; /* index in the array of flags */ + uint16 flagbits; /* flags in the opaque area of the page */ + + /* Only the superuser can use this */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use raw page functions"))); + + /* Getting rel by name and raw page by number */ + rel = get_rel_from_name(relname); + raw_page = get_rel_raw_page(rel, blkno); + relation_close(rel, AccessShareLock); + + /* Getting a copy of the page from the raw page */ + page = get_page_from_raw(raw_page); + + /* If the page is new, the function should return NULL */ + if (PageIsNew(page)) + PG_RETURN_NULL(); + + /* Checking the size of the opaque area of the page */ + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(RumPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid RUM data leaf page"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(RumPageOpaqueData)), + (int) PageGetSpecialSize(page)))); + + /* Getting a page description from an opaque area */ + opaq = RumPageGetOpaque(page); + + /* 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"); + + /* Convert the flags bitmask to an array of human-readable names */ + flagbits = opaq->flags; + if (flagbits & RUM_DATA) + flags[nflags++] = CStringGetTextDatum("data"); + if (flagbits & RUM_LEAF) + flags[nflags++] = CStringGetTextDatum("leaf"); + if (flagbits & RUM_DELETED) + flags[nflags++] = CStringGetTextDatum("deleted"); + if (flagbits & RUM_META) + flags[nflags++] = CStringGetTextDatum("meta"); + if (flagbits & RUM_LIST) + flags[nflags++] = CStringGetTextDatum("list"); + if (flagbits & RUM_LIST_FULLROW) + flags[nflags++] = CStringGetTextDatum("list_fullrow"); + flagbits &= ~(RUM_DATA | RUM_LEAF | RUM_DELETED | RUM_META | RUM_LIST | + RUM_LIST_FULLROW); + if (flagbits) + { + /* any flags we don't recognize are printed in hex */ + flags[nflags++] = DirectFunctionCall1(to_hex32, Int32GetDatum(flagbits)); + } + + memset(nulls, 0, sizeof(nulls)); + + /* + * Writing data from metadata to values. + */ + values[0] = Int64GetDatum(opaq->leftlink); + values[1] = Int64GetDatum(opaq->rightlink); + values[2] = Int32GetDatum(opaq->maxoff); + values[3] = Int32GetDatum(opaq->freespace); + values[4] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID)); + + /* Build and return the result tuple. */ + resultTuple = heap_form_tuple(tupdesc, values, nulls); + + /* Returning the result */ + return HeapTupleGetDatum(resultTuple); +} + +/* + * A structure that stores information between calls to the + * rum_leaf_data_page_items() function. This information is + * necessary to scan the page. + */ +typedef struct rum_leafpage_items_state +{ + /* Number of RumItem structures per {leaf, data} page */ + int maxoff; + + /* Pointer to the current RumItem */ + Pointer item_ptr; + + /* A pointer to the RumState structure, which is needed to scan the page */ + RumState *rum_state_ptr; + + /* A pointer to the description of the attribute, which is addInfo */ + Form_pg_attribute addInfo_att_ptr; +} rum_leafpage_items_state; + +/* + * The function rum_leaf_data_page_items() is designed to read + * information from the {leaf, data} pages of the rum index. + * The pages scanned by this function are the pages of the + * Posting Tree. The information on the page is stored in RumItem + * structures. The function returns tuple_id, add_info_is_null, + * addinfo. To scan, need the index name and the page number. + * It is an SRF function, i.e. it returns one value per call. + */ +Datum +rum_leaf_data_page_items(PG_FUNCTION_ARGS) +{ + /* Reading input arguments */ + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); + + /* + * The context of the function calls and the pointer + * to the long-lived inter_call_data structure + */ + FuncCallContext *fctx; + rum_leafpage_items_state *inter_call_data; + + /* Only the superuser can use this */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use this function"))); + + /* + * In the case of the first function call, it is necessary + * to get the page by its number and create a Runstate + * structure for scanning the page. + */ + if (SRF_IS_FIRSTCALL()) + { + + Relation rel; /* needed to initialize the RumState + structure */ + bytea *raw_page; /* The raw page obtained from rel */ + + TupleDesc tupdesc; /* description of the result tuple */ + MemoryContext oldmctx; /* the old function memory context */ + Page page; /* the page to be scanned */ + RumPageOpaque opaq; /* data from the opaque area of the page */ + + /* + * Initializing the FuncCallContext structure and switching the memory + * context to the one needed for structures that must be saved during + * multiple calls + */ + fctx = SRF_FIRSTCALL_INIT(); + oldmctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + /* Getting rel by name and raw page by number */ + rel = get_rel_from_name(relname); + raw_page = get_rel_raw_page(rel, blkno); + + /* Allocating memory for a long-lived structure */ + inter_call_data = palloc(sizeof(rum_leafpage_items_state)); + + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); + + /* + * It is convenient to save a pointer to an attribute + * of addInfo in a long-lived structure for shorter + * access in the future. + */ + inter_call_data->addInfo_att_ptr = inter_call_data->rum_state_ptr-> + addAttrs[inter_call_data->rum_state_ptr->attrnAddToColumn - 1]; + + relation_close(rel, AccessShareLock); + + /* Getting a copy of the page from the raw page */ + page = get_page_from_raw(raw_page); + + /* If the page is new, the function should return NULL */ + if (PageIsNew(page)) + { + MemoryContextSwitchTo(oldmctx); + PG_RETURN_NULL(); + } + + /* Checking the size of the opaque area of the page */ + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(RumPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid RUM page"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(RumPageOpaqueData)), + (int) PageGetSpecialSize(page)))); + + /* Getting a page description from an opaque area */ + opaq = RumPageGetOpaque(page); + + /* Checking the flags */ + if (opaq->flags != (RUM_DATA | RUM_LEAF)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a RUM {data, leaf} page"), + errdetail("Flags %04X, expected %04X", + opaq->flags, (RUM_DATA | RUM_LEAF)))); + + /* 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"); + + /* Needed to for subsequent recording tupledesc in fctx */ + BlessTupleDesc(tupdesc); + + /* + * Write to the long-lived structure the number of RumItem + * structures on the page and a pointer to the data on the page. + */ + inter_call_data->maxoff = opaq->maxoff; + inter_call_data->item_ptr = RumDataPageGetData(page); + + /* + * Save a pointer to a long-lived structure and + * tuple descriptor for our result type in fctx. + */ + fctx->user_fctx = inter_call_data; + fctx->tuple_desc = tupdesc; + + /* Switching to the old memory context */ + MemoryContextSwitchTo(oldmctx); + } + + /* Preparing to use the FuncCallContext */ + fctx = SRF_PERCALL_SETUP(); + + /* In the current call, we are reading data from the previous one */ + inter_call_data = fctx->user_fctx; + + /* Go through the page */ + if((fctx->call_cntr + 1) <= inter_call_data->maxoff) + { + RumItem rum_item; /* to read data from a page */ + Datum values[3]; /* return values */ + bool nulls[3]; /* true if the corresponding value is NULL */ + + /* For the results */ + HeapTuple resultTuple; + Datum result; + + memset(nulls, 0, sizeof(nulls)); + + /* Reading information from the page in rum_item */ + inter_call_data->item_ptr = rumDataPageLeafRead(inter_call_data->item_ptr, + inter_call_data->rum_state_ptr->attrnAddToColumn, + &rum_item, false, inter_call_data->rum_state_ptr); + + /* Writing data from rum_item to values */ + values[0] = ItemPointerGetDatum(&(rum_item.iptr)); + values[1] = BoolGetDatum(rum_item.addInfoIsNull); + + /* + * If addInfo is not NULL, you need to return it as text. + * If addInfo is NULL, then you need to specify this in + * the corresponding value of the nulls array. + * + * You don't have to worry about freeing up memory in the + * addInfoGetText() function, because the memory context + * in which the current SRF function is called is temporary + * and it will be cleaned up between calls. + */ + if(!rum_item.addInfoIsNull) + values[2] = addInfoGetText(rum_item.addInfo, + inter_call_data->addInfo_att_ptr->atttypid); + else nulls[2] = true; + + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); + } + + /* Completing the function */ + SRF_RETURN_DONE(fctx); +} + +/* + * A copy of the get_page_from_raw() + * function from pageinspect. + * + * Get a palloc'd, maxalign'ed page image + * from the result of get_rel_raw_page() + */ +static Page +get_page_from_raw(bytea *raw_page) +{ + Page page; + int raw_page_size; + + raw_page_size = VARSIZE_ANY_EXHDR(raw_page); + + if (raw_page_size != BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid page size"), + errdetail("Expected %d bytes, got %d.", + BLCKSZ, raw_page_size))); + + page = palloc(raw_page_size); + + memcpy(page, VARDATA_ANY(raw_page), raw_page_size); + + return page; +} + +/* + * An auxiliary function that is used to convert additional + * information into text. This is a universal way to return any type. + * + * The following types of data are checked: + * int2, int4, int8, float4, float8, money, oid, timestamp, + * timestamptz, time, timetz, date, interval, macaddr, inet, + * cidr, text, varchar, char, bytea, bit, varbit, numeric. + * + * All types accepted by rum must be checked, but + * perhaps some types are missing or some are superfluous. + */ +static Datum +addInfoGetText(Datum addInfo, Oid atttypid) +{ + char *str_addInfo = NULL; + + /* addInfo cannot be NULL */ + Assert(DatumGetPointer(addInfo) != NULL); + + /* + * Form a string depending on the type of addInfo. + * + * FIXME The macros used below are taken from the pg_type_d file.h, + * and it says not to use them in the new code, then it's not + * clear how to determine the attribute type. In addition, it + * was not possible to find conversion functions for several + * types below. + */ + switch (atttypid) + { + case INT2OID: + str_addInfo = OidOutputFunctionCall(F_INT2OUT, addInfo); + break; + + case INT4OID: + str_addInfo = OidOutputFunctionCall(F_INT4OUT, addInfo); + break; + + case INT8OID: + str_addInfo = OidOutputFunctionCall(F_INT8OUT, addInfo); + break; + + case FLOAT4OID: + str_addInfo = OidOutputFunctionCall(F_FLOAT4OUT, addInfo); + break; + + case FLOAT8OID: + str_addInfo = OidOutputFunctionCall(F_FLOAT8OUT, addInfo); + break; + + /*case MONEYOID:*/ + /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ + /* break;*/ + + case OIDOID: + str_addInfo = OidOutputFunctionCall(F_OIDOUT, addInfo); + break; + + case TIMESTAMPOID: + str_addInfo = OidOutputFunctionCall(F_TIMESTAMP_OUT, addInfo); + break; + + case TIMESTAMPTZOID: + str_addInfo = OidOutputFunctionCall(F_TIMESTAMPTZ_OUT, addInfo); + break; + + case TIMEOID: + str_addInfo = OidOutputFunctionCall(F_TIME_OUT, addInfo); + break; + + case TIMETZOID: + str_addInfo = OidOutputFunctionCall(F_TIMETZ_OUT, addInfo); + break; + + case DATEOID: + str_addInfo = OidOutputFunctionCall(F_DATE_OUT, addInfo); + break; + + case INTERVALOID: + str_addInfo = OidOutputFunctionCall(F_INTERVAL_OUT, addInfo); + break; + + case MACADDROID: + str_addInfo = OidOutputFunctionCall(F_MACADDR_OUT, addInfo); + break; + + case INETOID: + str_addInfo = OidOutputFunctionCall(F_INET_OUT, addInfo); + break; + + case CIDROID: + str_addInfo = OidOutputFunctionCall(F_CIDR_OUT, addInfo); + break; + + case TEXTOID: + str_addInfo = OidOutputFunctionCall(F_CIDR_OUT, addInfo); + break; + + /*case VARCHAROID:*/ + /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ + /* break;*/ + /**/ + /*case CHAROID:*/ + /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ + /* break;*/ + /**/ + /*case BYTEAOID:*/ + /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ + /* break;*/ + + case BITOID: + str_addInfo = OidOutputFunctionCall(F_BIT_OUT, addInfo); + break; + + case VARBITOID: + str_addInfo = OidOutputFunctionCall(F_VARBIT_OUT, addInfo); + break; + + case NUMERICOID: + str_addInfo = OidOutputFunctionCall(F_NUMERIC_OUT, addInfo); + break; + } + + return CStringGetTextDatum(str_addInfo); +} + +/* + * This function and get_rel_raw_page() are derived from the separation + * of the get_raw_page_internal() function, which was copied from the pageinspect code. + * It is needed in order to call the initRumState() function if necessary. + */ +static Relation +get_rel_from_name(text *relname) +{ + RangeVar *relrv; + Relation rel; + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from relation \"%s\"", + RelationGetRelationName(rel)), + errdetail_relkind_not_supported(rel->rd_rel->relkind))); + + /* + * Reject attempts to read non-local temporary relations; we would be + * likely to get wrong data since we have no visibility into the owning + * session's local buffers. + */ + if (RELATION_IS_OTHER_TEMP(rel)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot access temporary tables of other sessions"))); + + return rel; +} + +/* + * This function and get_rel_from_name() are derived from the separation + * of the get_raw_page_internal() function, which was copied from the pageinspect code. + * It is needed in order to call the initRumState() function if necessary. + */ +static bytea * +get_rel_raw_page(Relation rel, BlockNumber blkno) +{ + bytea *raw_page; + char *raw_page_data; + Buffer buf; + + if (blkno >= RelationGetNumberOfBlocksInFork(rel, MAIN_FORKNUM)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("block number %u is out of range for relation \"%s\"", + blkno, RelationGetRelationName(rel)))); + + /* Initialize buffer to copy to */ + raw_page = (bytea *) palloc(BLCKSZ + VARHDRSZ); + SET_VARSIZE(raw_page, BLCKSZ + VARHDRSZ); + raw_page_data = VARDATA(raw_page); + + /* Take a verbatim copy of the page */ + buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_NORMAL, NULL); + LockBuffer(buf, BUFFER_LOCK_SHARE); + + memcpy(raw_page_data, BufferGetPage(buf), BLCKSZ); + + LockBuffer(buf, BUFFER_LOCK_UNLOCK); + ReleaseBuffer(buf); + + return raw_page; +} From d3121f311e7ee27e4ed353c73e2bcb2c393e1750 Mon Sep 17 00:00:00 2001 From: Arseny Kositsin Date: Fri, 21 Feb 2025 10:43:50 +0300 Subject: [PATCH 2/7] [PGPRO-12159] Added three missing pageinspect functions for rum. 1) rum_internal_data_page_items() - it is intended for viewing information that is located on the internal pages of the posting tree (flags {data}). 2) rum_leaf_entry_page_items() - it is intended for viewing information that is located on the leaf pages of the entry tree (flags {leaf}). 3) rum_internal_entry_page_items() - it is intended for viewing information that is located on the internal pages of the entry tree (flags {}). To extract information, all these functions need to pass the index name and the page number. Tags: rum --- rum_init.sql | 44 +- src/rum_debug_funcs.c | 1414 ++++++++++++++++++++++++++++++++++------- 2 files changed, 1215 insertions(+), 243 deletions(-) diff --git a/rum_init.sql b/rum_init.sql index 7dcf3ef8ca..edfc54968f 100644 --- a/rum_init.sql +++ b/rum_init.sql @@ -6,7 +6,6 @@ LANGUAGE C; /* * RUM access method */ - CREATE ACCESS METHOD rum TYPE INDEX HANDLER rumhandler; /* @@ -1738,7 +1737,7 @@ CREATE FUNCTION rum_metapage_info( OUT n_entry_pages bigint, OUT n_data_pages bigint, OUT n_entries bigint, - OUT version bigint) + OUT version varchar) AS 'MODULE_PATHNAME', 'rum_metapage_info' LANGUAGE C STRICT PARALLEL SAFE; @@ -1756,9 +1755,48 @@ LANGUAGE C STRICT PARALLEL SAFE; CREATE FUNCTION rum_leaf_data_page_items( IN rel_name text, IN blk_num int8, + OUT is_high_key bool, OUT tuple_id tid, OUT add_info_is_null bool, - OUT addInfo varchar) + OUT add_info varchar) RETURNS SETOF record AS 'MODULE_PATHNAME', 'rum_leaf_data_page_items' LANGUAGE C STRICT PARALLEL SAFE; + +CREATE FUNCTION rum_internal_data_page_items( + IN rel_name text, + IN blk_num int8, + OUT is_high_key bool, + OUT block_number int8, + OUT tuple_id tid, + OUT add_info_is_null bool, + OUT add_info varchar) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'rum_internal_data_page_items' +LANGUAGE C STRICT PARALLEL SAFE; + +CREATE FUNCTION rum_leaf_entry_page_items( + IN rel_name text, + IN blk_num int8, + OUT key varchar, + OUT attrnum int8, + OUT category varchar, + OUT tuple_id tid, + OUT add_info_is_null bool, + OUT add_info varchar, + OUT is_postring_tree bool, + OUT postring_tree_root int8) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'rum_leaf_entry_page_items' +LANGUAGE C STRICT PARALLEL SAFE; + +CREATE FUNCTION rum_internal_entry_page_items( + IN rel_name text, + IN blk_num int8, + OUT key varchar, + OUT attrnum int8, + OUT category varchar, + OUT down_link int8) +RETURNS SETOF record +AS 'MODULE_PATHNAME', 'rum_internal_entry_page_items' +LANGUAGE C STRICT PARALLEL SAFE; diff --git a/src/rum_debug_funcs.c b/src/rum_debug_funcs.c index 4e37fbf640..80b4acee98 100644 --- a/src/rum_debug_funcs.c +++ b/src/rum_debug_funcs.c @@ -8,16 +8,13 @@ * contrib/rum/rum_debug_funcs.c * * LIST OF ISSUES: - * 1) Strangely, the index version is returned in the - * rum_metapage_info() function. * - * 2) Using obsolete macros in the addInfoGetText() function. + * 1) Using obsolete macros in the get_datum_text_by_oid() function. * - * 3) I/O functions were not available for all types in - * in the addInfoGetText() function. + * 2) I/O functions were not available for all types in + * in the get_datum_text_by_oid() function. * - * 4) The unclear logic of choosing the attribute number - * that the addInfo corresponds to. + * 3) SIGSEGV in case of bytea output as additional information. */ #include "postgres.h" @@ -34,11 +31,89 @@ PG_FUNCTION_INFO_V1(rum_metapage_info); PG_FUNCTION_INFO_V1(rum_page_opaque_info); PG_FUNCTION_INFO_V1(rum_leaf_data_page_items); +PG_FUNCTION_INFO_V1(rum_internal_data_page_items); +PG_FUNCTION_INFO_V1(rum_leaf_entry_page_items); +PG_FUNCTION_INFO_V1(rum_internal_entry_page_items); + +/* + * A structure that stores information between calls to the + * rum_leaf_data_page_items(), rum_internal_data_page_items(), + * rum_leaf_entry_page_items(), rum_internal_entry_page_items() + * functions. This information is necessary to scan the page. + */ +typedef struct rum_page_items_state +{ + /* Pointer to the beginning of the scanned page */ + Page page; + + /* + * The number of scanned items per page. + * + * On the {leaf, data} page, this is the number of + * RumItem structures that are in the compressed posting list. + * + * On the {data} page, this is the number of PostingItem structures. + * + * On the {leaf} page, this is the number of IndexTuple, each of + * which contains a compressed posting list. In this case, the size + * of the Posting list is determined using RumGetNPosting(itup). + * + * On the {} page, this is the number of IndexTuple. + */ + int maxoff; + + /* Current IndexTuple on the page */ + IndexTuple cur_itup; + + /* The number of the current IndexTuple on the page */ + int cur_tuple_num; + + /* Pointer to the current scanning item */ + Pointer item_ptr; + + /* The number of the current element in the current IndexTuple */ + int cur_tuple_item; + + /* The attribute number of the current tuple key */ + OffsetNumber cur_tuple_key_attnum; + + /* The current tuple key */ + Datum cur_tuple_key; + + /* Current tuple key category (see rum.h) */ + RumNullCategory cur_tuple_key_category; + + /* The number of the child page that is stored in the current IndexTuple */ + BlockNumber cur_tuple_down_link; + + /* It is necessary to scan the posting list */ + RumItem cur_rum_item; + + /* + * The Oid of the additional information in the index. + * It is read from the RumConfig structure. + */ + Oid add_info_oid; + + /* + * If the current IndexTuple is scanned, then + * you need to move on to the next one. + */ + bool need_new_tuple; + + /* A pointer to the RumState structure, which is needed to scan the page */ + RumState *rum_state_ptr; +} rum_page_items_state; static Page get_page_from_raw(bytea *raw_page); -static Datum addInfoGetText(Datum addInfo, Oid atttypid); +static Datum get_datum_text_by_oid(Datum addInfo, Oid atttypid); static Relation get_rel_from_name(text *relname); static bytea *get_rel_raw_page(Relation rel, BlockNumber blkno); +static void get_new_index_tuple(rum_page_items_state *inter_call_data); +static Oid get_cur_attr_oid(rum_page_items_state *inter_call_data); +static Datum category_get_datum_text(RumNullCategory category); +static Oid find_add_info_oid(RumState *rum_state_ptr); +static OffsetNumber find_add_info_atrr_num(RumState *rum_state_ptr); /* * The rum_metapage_info() function is used to retrieve @@ -50,21 +125,27 @@ Datum rum_metapage_info(PG_FUNCTION_ARGS) { /* Reading input arguments */ - text *relname = PG_GETARG_TEXT_PP(0); - uint32 blkno = PG_GETARG_UINT32(1); + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); + + Relation rel; /* needed to initialize the RumState structure */ - Relation rel; /* needed to initialize the RumState - structure */ + bytea *raw_page; /* the raw page obtained from rel */ + RumPageOpaque opaq; /* data from the opaque area of the page */ + RumMetaPageData *metadata; /* data stored on the meta page */ + Page page; /* the page to be scanned */ - bytea *raw_page; /* the raw page obtained from rel */ - RumPageOpaque opaq; /* data from the opaque area of the page */ - RumMetaPageData *metadata; /* data stored on the meta page */ - Page page; /* the page to be scanned */ + TupleDesc tupdesc; /* description of the result tuple */ + HeapTuple resultTuple; /* for the results */ + Datum values[10]; /* return values */ + bool nulls[10]; /* true if the corresponding value is NULL */ - TupleDesc tupdesc; /* description of the result tuple */ - HeapTuple resultTuple; /* for the results */ - Datum values[10]; /* return values */ - bool nulls[10]; /* true if the corresponding value is NULL */ + /* + * To output the index version. + * If you change the index version, you + * may need to increase the buffer size. + */ + char version_buf[20]; /* Only the superuser can use this */ if (!superuser()) @@ -128,7 +209,8 @@ rum_metapage_info(PG_FUNCTION_ARGS) values[6] = Int64GetDatum(metadata->nEntryPages); values[7] = Int64GetDatum(metadata->nDataPages); values[8] = Int64GetDatum(metadata->nEntries); - values[9] = Int64GetDatum(metadata->rumVersion); + snprintf(version_buf, sizeof(version_buf), "0x%X", metadata->rumVersion); + values[9] = CStringGetTextDatum(version_buf); /* Build and return the result tuple */ resultTuple = heap_form_tuple(tupdesc, values, nulls); @@ -146,24 +228,23 @@ Datum rum_page_opaque_info(PG_FUNCTION_ARGS) { /* Reading input arguments */ - text *relname = PG_GETARG_TEXT_PP(0); - uint32 blkno = PG_GETARG_UINT32(1); + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); - Relation rel; /* needed to initialize the RumState - structure */ + Relation rel; /* needed to initialize the RumState structure */ - bytea *raw_page; /* the raw page obtained from rel */ - RumPageOpaque opaq; /* data from the opaque area of the page */ - Page page; /* the page to be scanned */ + bytea *raw_page; /* the raw page obtained from rel */ + RumPageOpaque opaq; /* data from the opaque area of the page */ + Page page; /* the page to be scanned */ - HeapTuple resultTuple; /* for the results */ - TupleDesc tupdesc; /* description of the result tuple */ + HeapTuple resultTuple; /* for the results */ + TupleDesc tupdesc; /* description of the result tuple */ - Datum values[5]; /* return values */ - bool nulls[5]; /* true if the corresponding value is NULL */ - Datum flags[16]; /* array with flags in text format */ - int nflags = 0; /* index in the array of flags */ - uint16 flagbits; /* flags in the opaque area of the page */ + Datum values[5]; /* return values */ + bool nulls[5]; /* true if the corresponding value is NULL */ + Datum flags[16]; /* array with flags in text format */ + int nflags = 0; /* index in the array of flags */ + uint16 flagbits; /* flags in the opaque area of the page */ /* Only the superuser can use this */ if (!superuser()) @@ -239,48 +320,35 @@ rum_page_opaque_info(PG_FUNCTION_ARGS) return HeapTupleGetDatum(resultTuple); } -/* - * A structure that stores information between calls to the - * rum_leaf_data_page_items() function. This information is - * necessary to scan the page. - */ -typedef struct rum_leafpage_items_state -{ - /* Number of RumItem structures per {leaf, data} page */ - int maxoff; - - /* Pointer to the current RumItem */ - Pointer item_ptr; - - /* A pointer to the RumState structure, which is needed to scan the page */ - RumState *rum_state_ptr; - - /* A pointer to the description of the attribute, which is addInfo */ - Form_pg_attribute addInfo_att_ptr; -} rum_leafpage_items_state; - /* - * The function rum_leaf_data_page_items() is designed to read - * information from the {leaf, data} pages of the rum index. - * The pages scanned by this function are the pages of the - * Posting Tree. The information on the page is stored in RumItem - * structures. The function returns tuple_id, add_info_is_null, - * addinfo. To scan, need the index name and the page number. - * It is an SRF function, i.e. it returns one value per call. + * The function rum_leaf_data_page_items() is designed + * to view information that is located on the leaf + * pages of the index's rum posting tree. On these pages, + * information is stored in a set of compressed posting lists + * (similar to entry tree leaf pages), but these posting lists + * are not in IndexTuples, but are located between the PageHeader + * and pd_lower. The space between pd_lower and pd_upper is not used. + * + * The function returns the bool variable is_high_key (true if + * the current returned tuple is the high key of the current page) + * and the information contained in the corresponding structure of + * RumItem: tid, the bool variable add_info_is_null and add_info. + * + * It is a SRF function, i.e. it returns one tuple per call. */ Datum rum_leaf_data_page_items(PG_FUNCTION_ARGS) { /* Reading input arguments */ - text *relname = PG_GETARG_TEXT_PP(0); - uint32 blkno = PG_GETARG_UINT32(1); + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); /* * The context of the function calls and the pointer * to the long-lived inter_call_data structure */ FuncCallContext *fctx; - rum_leafpage_items_state *inter_call_data; + rum_page_items_state *inter_call_data; /* Only the superuser can use this */ if (!superuser()) @@ -295,15 +363,13 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) */ if (SRF_IS_FIRSTCALL()) { - - Relation rel; /* needed to initialize the RumState - structure */ - bytea *raw_page; /* The raw page obtained from rel */ + Relation rel; /* needed to initialize the RumState structure */ + bytea *raw_page; /* The raw page obtained from rel */ - TupleDesc tupdesc; /* description of the result tuple */ - MemoryContext oldmctx; /* the old function memory context */ - Page page; /* the page to be scanned */ - RumPageOpaque opaq; /* data from the opaque area of the page */ + TupleDesc tupdesc; /* description of the result tuple */ + MemoryContext oldmctx; /* the old function memory context */ + Page page; /* the page to be scanned */ + RumPageOpaque opaq; /* data from the opaque area of the page */ /* * Initializing the FuncCallContext structure and switching the memory @@ -318,20 +384,12 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) raw_page = get_rel_raw_page(rel, blkno); /* Allocating memory for a long-lived structure */ - inter_call_data = palloc(sizeof(rum_leafpage_items_state)); + inter_call_data = palloc(sizeof(rum_page_items_state)); /* Initializing the RumState structure */ inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); initRumState(inter_call_data->rum_state_ptr, rel); - /* - * It is convenient to save a pointer to an attribute - * of addInfo in a long-lived structure for shorter - * access in the future. - */ - inter_call_data->addInfo_att_ptr = inter_call_data->rum_state_ptr-> - addAttrs[inter_call_data->rum_state_ptr->attrnAddToColumn - 1]; - relation_close(rel, AccessShareLock); /* Getting a copy of the page from the raw page */ @@ -375,8 +433,16 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) * Write to the long-lived structure the number of RumItem * structures on the page and a pointer to the data on the page. */ + inter_call_data->page = page; inter_call_data->maxoff = opaq->maxoff; inter_call_data->item_ptr = RumDataPageGetData(page); + inter_call_data->add_info_oid = find_add_info_oid(inter_call_data->rum_state_ptr); + + /* + * It is necessary for the correct reading of the + * tid (see the function rumDataPageLeafRead()) + */ + memset(&(inter_call_data->cur_rum_item), 0, sizeof(RumItem)); /* * Save a pointer to a long-lived structure and @@ -395,42 +461,84 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) /* In the current call, we are reading data from the previous one */ inter_call_data = fctx->user_fctx; - /* Go through the page */ - if((fctx->call_cntr + 1) <= inter_call_data->maxoff) + /* + * Go through the page. It should be noted that fctx->call_cntr + * on the first call is 0. The very first function call is intended + * to return the high key of the scanned page (this is done because + * the high key is not taken into account in inter_call_data->maxoff). + */ + if(fctx->call_cntr <= inter_call_data->maxoff) { - RumItem rum_item; /* to read data from a page */ - Datum values[3]; /* return values */ - bool nulls[3]; /* true if the corresponding value is NULL */ + RumItem *high_key_ptr; + RumItem *rum_item_ptr; /* to read data from a page */ + Datum values[4]; /* return values */ + bool nulls[4]; /* true if the corresponding value is NULL */ /* For the results */ - HeapTuple resultTuple; - Datum result; + HeapTuple resultTuple; + Datum result; memset(nulls, 0, sizeof(nulls)); + rum_item_ptr = &(inter_call_data->cur_rum_item); + + /* + * The high key of the current page is the RumItem structure and + * it is located immediately after the PageHeader. On the first + * call, we return the information it stores. + */ + if (fctx->call_cntr == 0) + { + high_key_ptr = RumDataPageGetRightBound(inter_call_data->page); + values[0] = BoolGetDatum(true); + values[1] = ItemPointerGetDatum(&(high_key_ptr->iptr)); + values[2] = BoolGetDatum(high_key_ptr->addInfoIsNull); + + /* Returning add info */ + if(!high_key_ptr->addInfoIsNull && inter_call_data->add_info_oid != 0) + values[3] = get_datum_text_by_oid(high_key_ptr->addInfo, + inter_call_data->add_info_oid); + else nulls[3] = true; + + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); + } - /* Reading information from the page in rum_item */ + /* + * Reading information from the page in rum_item. + * + * TODO: The fact is that being on the posting tree page, we don't know which + * index attribute this posting tree was built for, so we don't know the + * attribute number of the additional information. But the rumDataPageLeafRead() + * function requires it to read information from the page. Here we use the auxiliary + * function find_add_info_atr_num(), which simply iterates through the array with + * attributes that are additional information and selects the attribute number for + * which the additional information attribute is not NULL. This approach is incorrect + * because there may not be additional information for the attribute on the page, + * but we hope that in this case add_info_is_null will have the value true and the + * additional information will not be read. + * + * This problem can be solved by asking the user for the attribute number of + * additional information, because going through the index from top to bottom, + * he saw it next to the link to the posting tree root. + */ inter_call_data->item_ptr = rumDataPageLeafRead(inter_call_data->item_ptr, - inter_call_data->rum_state_ptr->attrnAddToColumn, - &rum_item, false, inter_call_data->rum_state_ptr); + /* inter_call_data->cur_tuple_key_attnum, */ + find_add_info_atrr_num(inter_call_data->rum_state_ptr), + rum_item_ptr, false, inter_call_data->rum_state_ptr); /* Writing data from rum_item to values */ - values[0] = ItemPointerGetDatum(&(rum_item.iptr)); - values[1] = BoolGetDatum(rum_item.addInfoIsNull); + values[0] = false; + values[1] = ItemPointerGetDatum(&(rum_item_ptr->iptr)); + values[2] = BoolGetDatum(rum_item_ptr->addInfoIsNull); - /* - * If addInfo is not NULL, you need to return it as text. - * If addInfo is NULL, then you need to specify this in - * the corresponding value of the nulls array. - * - * You don't have to worry about freeing up memory in the - * addInfoGetText() function, because the memory context - * in which the current SRF function is called is temporary - * and it will be cleaned up between calls. - */ - if(!rum_item.addInfoIsNull) - values[2] = addInfoGetText(rum_item.addInfo, - inter_call_data->addInfo_att_ptr->atttypid); - else nulls[2] = true; + /* Returning add info */ + if(!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0) + values[3] = get_datum_text_by_oid(rum_item_ptr->addInfo, inter_call_data->add_info_oid); + else nulls[3] = true; /* Forming the returned tuple */ resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); @@ -445,180 +553,956 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) } /* - * A copy of the get_page_from_raw() - * function from pageinspect. + * The function rum_internal_data_page_items() is designed + * to view information that is located on the internal + * pages of the index's rum posting tree. On these pages, + * information is stored in an array of PostingItem structures, + * which are located immediately after the PageHeader. Each + * PostingItem structure contains the child page number and + * RumItem in which iptr is the high key of the child page. * - * Get a palloc'd, maxalign'ed page image - * from the result of get_rel_raw_page() + * The function returns the bool variable is_high_key (true if + * the current returned tuple is the high key of the current page); + * the child page number; and the information contained in the + * corresponding structure of RumItem: tid, the bool variable + * add_info_is_null and add_info. + * + * It is a SRF function, i.e. it returns one tuple per call. */ -static Page -get_page_from_raw(bytea *raw_page) +Datum +rum_internal_data_page_items(PG_FUNCTION_ARGS) { - Page page; - int raw_page_size; + /* Reading input arguments */ + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); - raw_page_size = VARSIZE_ANY_EXHDR(raw_page); + /* + * The context of the function calls and the pointer + * to the long-lived inter_call_data structure + */ + FuncCallContext *fctx; + rum_page_items_state *inter_call_data; - if (raw_page_size != BLCKSZ) + /* Only the superuser can use this */ + if (!superuser()) ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("invalid page size"), - errdetail("Expected %d bytes, got %d.", - BLCKSZ, raw_page_size))); + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use this function"))); - page = palloc(raw_page_size); + /* + * In the case of the first function call, it is necessary + * to get the page by its number and create a RumState + * structure for scanning the page. + */ + if (SRF_IS_FIRSTCALL()) + { + Relation rel; /* needed to initialize the RumState structure */ + bytea *raw_page; /* The raw page obtained from rel */ - memcpy(page, VARDATA_ANY(raw_page), raw_page_size); + TupleDesc tupdesc; /* description of the result tuple */ + MemoryContext oldmctx; /* the old function memory context */ + Page page; /* the page to be scanned */ + RumPageOpaque opaq; /* data from the opaque area of the page */ - return page; -} + /* + * Initializing the FuncCallContext structure and switching the memory + * context to the one needed for structures that must be saved during + * multiple calls. + */ + fctx = SRF_FIRSTCALL_INIT(); + oldmctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); -/* - * An auxiliary function that is used to convert additional - * information into text. This is a universal way to return any type. - * - * The following types of data are checked: - * int2, int4, int8, float4, float8, money, oid, timestamp, - * timestamptz, time, timetz, date, interval, macaddr, inet, - * cidr, text, varchar, char, bytea, bit, varbit, numeric. - * - * All types accepted by rum must be checked, but - * perhaps some types are missing or some are superfluous. - */ -static Datum -addInfoGetText(Datum addInfo, Oid atttypid) -{ - char *str_addInfo = NULL; + /* Getting rel by name and raw page by number */ + rel = get_rel_from_name(relname); + raw_page = get_rel_raw_page(rel, blkno); - /* addInfo cannot be NULL */ - Assert(DatumGetPointer(addInfo) != NULL); + /* Allocating memory for a long-lived structure */ + inter_call_data = palloc(sizeof(rum_page_items_state)); - /* - * Form a string depending on the type of addInfo. - * - * FIXME The macros used below are taken from the pg_type_d file.h, - * and it says not to use them in the new code, then it's not - * clear how to determine the attribute type. In addition, it - * was not possible to find conversion functions for several - * types below. - */ - switch (atttypid) - { - case INT2OID: - str_addInfo = OidOutputFunctionCall(F_INT2OUT, addInfo); - break; + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); - case INT4OID: - str_addInfo = OidOutputFunctionCall(F_INT4OUT, addInfo); - break; + relation_close(rel, AccessShareLock); - case INT8OID: - str_addInfo = OidOutputFunctionCall(F_INT8OUT, addInfo); - break; + /* Getting a copy of the page from the raw page */ + page = get_page_from_raw(raw_page); - case FLOAT4OID: - str_addInfo = OidOutputFunctionCall(F_FLOAT4OUT, addInfo); - break; + /* If the page is new, the function should return NULL */ + if (PageIsNew(page)) + { + MemoryContextSwitchTo(oldmctx); + PG_RETURN_NULL(); + } - case FLOAT8OID: - str_addInfo = OidOutputFunctionCall(F_FLOAT8OUT, addInfo); - break; + /* Checking the size of the opaque area of the page */ + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(RumPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid RUM page"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(RumPageOpaqueData)), + (int) PageGetSpecialSize(page)))); - /*case MONEYOID:*/ - /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ - /* break;*/ + /* Getting a page description from an opaque area */ + opaq = RumPageGetOpaque(page); - case OIDOID: - str_addInfo = OidOutputFunctionCall(F_OIDOUT, addInfo); - break; + /* Checking the flags */ + if (opaq->flags != (RUM_DATA & ~RUM_LEAF)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a RUM {data} page"), + errdetail("Flags %04X, expected %04X", + opaq->flags, (RUM_DATA & ~RUM_LEAF)))); - case TIMESTAMPOID: - str_addInfo = OidOutputFunctionCall(F_TIMESTAMP_OUT, addInfo); - break; + /* 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"); - case TIMESTAMPTZOID: - str_addInfo = OidOutputFunctionCall(F_TIMESTAMPTZ_OUT, addInfo); - break; + /* Needed to for subsequent recording tupledesc in fctx */ + BlessTupleDesc(tupdesc); - case TIMEOID: - str_addInfo = OidOutputFunctionCall(F_TIME_OUT, addInfo); - break; + /* + * Write to the long-lived structure the number of RumItem + * structures on the page and a pointer to the data on the page. + */ + inter_call_data->page = page; + inter_call_data->maxoff = opaq->maxoff; + inter_call_data->item_ptr = RumDataPageGetData(page); + inter_call_data->add_info_oid = find_add_info_oid(inter_call_data->rum_state_ptr); - case TIMETZOID: - str_addInfo = OidOutputFunctionCall(F_TIMETZ_OUT, addInfo); - break; + /* + * Save a pointer to a long-lived structure and + * tuple descriptor for our result type in fctx. + */ + fctx->user_fctx = inter_call_data; + fctx->tuple_desc = tupdesc; - case DATEOID: - str_addInfo = OidOutputFunctionCall(F_DATE_OUT, addInfo); - break; + /* Switching to the old memory context */ + MemoryContextSwitchTo(oldmctx); + } - case INTERVALOID: - str_addInfo = OidOutputFunctionCall(F_INTERVAL_OUT, addInfo); - break; + /* Preparing to use the FuncCallContext */ + fctx = SRF_PERCALL_SETUP(); - case MACADDROID: - str_addInfo = OidOutputFunctionCall(F_MACADDR_OUT, addInfo); - break; + /* In the current call, we are reading data from the previous one */ + inter_call_data = fctx->user_fctx; - case INETOID: - str_addInfo = OidOutputFunctionCall(F_INET_OUT, addInfo); - break; + /* + * Go through the page. It should be noted that fctx->call_cntr + * on the first call is 0. The very first function call is intended + * to return the high key of the scanned page (this is done because + * the high key is not taken into account in inter_call_data->maxoff). + */ + if(fctx->call_cntr <= inter_call_data->maxoff) + { + RumItem *high_key_ptr; + PostingItem *posting_item_ptr; /* to read data from a page */ + Datum values[5]; /* returned values */ + bool nulls[5]; /* true if the corresponding returned value is NULL */ - case CIDROID: - str_addInfo = OidOutputFunctionCall(F_CIDR_OUT, addInfo); - break; + /* For the results */ + HeapTuple resultTuple; + Datum result; - case TEXTOID: - str_addInfo = OidOutputFunctionCall(F_CIDR_OUT, addInfo); - break; + memset(nulls, 0, sizeof(nulls)); + + /* + * The high key of the current page is the RumItem structure and + * it is located immediately after the PageHeader. On the first + * call, we return the information it stores. + */ + if (fctx->call_cntr == 0) + { + high_key_ptr = RumDataPageGetRightBound(inter_call_data->page); + values[0] = BoolGetDatum(true); + nulls[1] = true; + values[2] = ItemPointerGetDatum(&(high_key_ptr->iptr)); + values[3] = BoolGetDatum(high_key_ptr->addInfoIsNull); + + /* Returning add info */ + if(!high_key_ptr->addInfoIsNull && inter_call_data->add_info_oid != 0) + values[4] = get_datum_text_by_oid(high_key_ptr->addInfo, + inter_call_data->add_info_oid); + else nulls[4] = true; + + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); + } - /*case VARCHAROID:*/ - /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ - /* break;*/ - /**/ - /*case CHAROID:*/ - /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ - /* break;*/ - /**/ - /*case BYTEAOID:*/ - /* str_addInfo = OidOutputFunctionCall(, addInfo);*/ - /* break;*/ + /* Reading information from the page */ + posting_item_ptr = (PostingItem *) inter_call_data->item_ptr; + inter_call_data->item_ptr += sizeof(PostingItem); - case BITOID: - str_addInfo = OidOutputFunctionCall(F_BIT_OUT, addInfo); - break; + /* Writing data from posting_item_ptr to values */ + values[0] = BoolGetDatum(false); + values[1] = UInt32GetDatum(PostingItemGetBlockNumber(posting_item_ptr)); + values[2] = ItemPointerGetDatum(&(posting_item_ptr->item.iptr)); + values[3] = BoolGetDatum(posting_item_ptr->item.addInfoIsNull); - case VARBITOID: - str_addInfo = OidOutputFunctionCall(F_VARBIT_OUT, addInfo); - break; + /* Returning add info */ + if(!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != 0) + values[4] = get_datum_text_by_oid(posting_item_ptr->item.addInfo, + inter_call_data->add_info_oid); + else nulls[4] = true; - case NUMERICOID: - str_addInfo = OidOutputFunctionCall(F_NUMERIC_OUT, addInfo); - break; + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); } - return CStringGetTextDatum(str_addInfo); + /* Completing the function */ + SRF_RETURN_DONE(fctx); } /* - * This function and get_rel_raw_page() are derived from the separation - * of the get_raw_page_internal() function, which was copied from the pageinspect code. - * It is needed in order to call the initRumState() function if necessary. + * The function rum_leaf_entry_page_items() is designed + * to view information that is located on the leaf + * pages of the index's rum entry tree. On these pages, + * information is stored in compressed posting lists (which + * consist of RumItem structures), which conteins in the IndexTuples. + * + * The function returns the key; the key attribute number; + * the key category (see rum.h); and the information contained + * in the corresponding structure of RumItem: tid, the bool variable + * add_info_is_null and add_info. Also, in the case of the posting list, + * the bool variable is_postring_true = false and the page number of + * the root of posting tree is NULL. + * + * If posting tree is placed instead of posting list, the function + * returns the bool variable is_postring_true = true and the page + * number on which the root of the posting tree is located. The rest + * of the returned values contain NULL (except for the key itself). + * + * It is a SRF function, i.e. it returns one tuple per call. */ -static Relation -get_rel_from_name(text *relname) +Datum +rum_leaf_entry_page_items(PG_FUNCTION_ARGS) { - RangeVar *relrv; - Relation rel; + /* Reading input arguments */ + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); - relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); - rel = relation_openrv(relrv, AccessShareLock); + /* + * The context of the function calls and the pointer + * to the long-lived inter_call_data structure. + */ + FuncCallContext *fctx; + rum_page_items_state *inter_call_data; - if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) + /* Only the superuser can use this */ + if (!superuser()) ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot get raw page from relation \"%s\"", - RelationGetRelationName(rel)), + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use this function"))); + + /* + * In the case of the first function call, it is necessary + * to get the page by its number and create a RumState + * structure for scanning the page. + */ + if (SRF_IS_FIRSTCALL()) + { + Relation rel; /* needed to initialize the RumState structure */ + bytea *raw_page; /* The raw page obtained from rel */ + + TupleDesc tupdesc; /* description of the result tuple */ + MemoryContext oldmctx; /* the old function memory context */ + Page page; /* the page to be scanned */ + RumPageOpaque opaq; /* data from the opaque area of the page */ + + /* + * Initializing the FuncCallContext structure and switching the memory + * context to the one needed for structures that must be saved during + * multiple calls + */ + fctx = SRF_FIRSTCALL_INIT(); + oldmctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + /* Getting rel by name and raw page by number */ + rel = get_rel_from_name(relname); + raw_page = get_rel_raw_page(rel, blkno); + + /* Allocating memory for a long-lived structure */ + inter_call_data = palloc(sizeof(rum_page_items_state)); + + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); + + relation_close(rel, AccessShareLock); + + /* Getting a copy of the page from the raw page */ + page = get_page_from_raw(raw_page); + + /* If the page is new, the function should return NULL */ + if (PageIsNew(page)) + { + MemoryContextSwitchTo(oldmctx); + PG_RETURN_NULL(); + } + + /* Checking the size of the opaque area of the page */ + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(RumPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid RUM page"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(RumPageOpaqueData)), + (int) PageGetSpecialSize(page)))); + + /* Getting a page description from an opaque area */ + opaq = RumPageGetOpaque(page); + + /* Checking the flags */ + if (opaq->flags != RUM_LEAF) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a RUM {leaf} page"), + errdetail("Flags %04X, expected %04X", + opaq->flags, RUM_LEAF))); + + /* 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"); + + /* Needed to for subsequent recording tupledesc in fctx */ + BlessTupleDesc(tupdesc); + + /* + * We save all the necessary information for + * scanning the page in a long-lived structure. + */ + inter_call_data->page = page; + inter_call_data->maxoff = PageGetMaxOffsetNumber(page); + inter_call_data->need_new_tuple = true; + inter_call_data->cur_tuple_num = FirstOffsetNumber; + inter_call_data->add_info_oid = find_add_info_oid(inter_call_data->rum_state_ptr); + + /* + * Save a pointer to a long-lived structure and + * tuple descriptor for our result type in fctx. + */ + fctx->user_fctx = inter_call_data; + fctx->tuple_desc = tupdesc; + + /* Switching to the old memory context */ + MemoryContextSwitchTo(oldmctx); + } + + /* Preparing to use the FuncCallContext */ + fctx = SRF_PERCALL_SETUP(); + + /* In the current call, we are reading data from the previous one */ + inter_call_data = fctx->user_fctx; + + /* Go through the page */ + if(inter_call_data->cur_tuple_num <= inter_call_data->maxoff) + { + RumItem *rum_item_ptr; /* to read data from a page */ + Oid key_oid; /* to convert key to text */ + + Datum values[8]; /* returned values */ + bool nulls[8]; /* true if the corresponding returned value is NULL */ + + /* For the results */ + HeapTuple resultTuple; + Datum result; + + memset(nulls, 0, sizeof(nulls)); + rum_item_ptr = &(inter_call_data->cur_rum_item); + + /* + * The function reads information from compressed Posting lists, + * each of which is located in the corresponding IndexTuple. + * Therefore, first, if the previous IndexTuple has ended, + * the new one is read. After that, the current IndexTuple is + * scanned until it runs out. The IndexTuple themselves are read + * until they end on the page. + */ + if(inter_call_data->need_new_tuple) + { + /* Read the new IndexTuple */ + get_new_index_tuple(inter_call_data); + + /* + * Every time you read a new IndexTuple, you need to reset the + * tid for the rumDataPageLeafRead() function to work correctly. + */ + memset(rum_item_ptr, 0, sizeof(RumItem)); + + /* Getting the posting list */ + inter_call_data->item_ptr = RumGetPosting(inter_call_data->cur_itup); + inter_call_data->cur_tuple_item = 1; + inter_call_data->need_new_tuple = false; + inter_call_data->cur_tuple_num++; + + /* Getting key and key attribute number */ + inter_call_data->cur_tuple_key_attnum = rumtuple_get_attrnum(inter_call_data->rum_state_ptr, + inter_call_data->cur_itup); + inter_call_data->cur_tuple_key = rumtuple_get_key(inter_call_data->rum_state_ptr, + inter_call_data->cur_itup, + &(inter_call_data->cur_tuple_key_category)); + + /* The case when there is a posting tree instead of a compressed posting list */ + if(RumIsPostingTree(inter_call_data->cur_itup)) + { + /* Returning the key value */ + key_oid = get_cur_attr_oid(inter_call_data); + if(inter_call_data->cur_tuple_key_category == RUM_CAT_NORM_KEY) + values[0] = get_datum_text_by_oid(inter_call_data->cur_tuple_key, key_oid); + else nulls[0] = true; + + /* Returning the key attribute number */ + values[1] = UInt16GetDatum(inter_call_data->cur_tuple_key_attnum); + + /* Returning the key category */ + values[2] = category_get_datum_text(inter_call_data->cur_tuple_key_category); + + /* Everything stored in the RumItem structure has a NULL value */ + nulls[3] = true; + nulls[4] = true; + nulls[5] = true; + + /* Returning the root of the posting tree */ + values[6] = true; + values[7] = UInt32GetDatum(RumGetPostingTree(inter_call_data->cur_itup)); + + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* The next call will require a new IndexTuple */ + inter_call_data->need_new_tuple = true; + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); + } + } + + /* Reading the RumItem structures from the IndexTuple */ + inter_call_data->item_ptr = rumDataPageLeafRead(inter_call_data->item_ptr, + inter_call_data->cur_tuple_key_attnum, + rum_item_ptr, false, inter_call_data->rum_state_ptr); + + /* Returning the key value */ + key_oid = get_cur_attr_oid(inter_call_data); + if(inter_call_data->cur_tuple_key_category == RUM_CAT_NORM_KEY) + values[0] = get_datum_text_by_oid(inter_call_data->cur_tuple_key, key_oid); + else nulls[0] = true; + + /* Returning the key attribute number */ + values[1] = UInt16GetDatum(inter_call_data->cur_tuple_key_attnum); + + /* Returning the key category */ + values[2] = category_get_datum_text(inter_call_data->cur_tuple_key_category); + + /* Writing data from rum_item to values */ + values[3] = ItemPointerGetDatum(&(rum_item_ptr->iptr)); + values[4] = BoolGetDatum(rum_item_ptr->addInfoIsNull); + + + /* Returning add info */ + if(!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0) + values[5] = get_datum_text_by_oid(rum_item_ptr->addInfo, inter_call_data->add_info_oid); + else nulls[5] = true; + + /* The current IndexTuple does not contain a posting tree */ + values[6] = BoolGetDatum(false); + nulls[7] = true; + + /* + * If the current IndexTuple has ended, i.e. we have scanned all + * its RumItems, then we need to enable the need_new_tuple flag + * so that the next time the function is called, we can read + * a new IndexTuple from the page. + */ + inter_call_data->cur_tuple_item++; + if(inter_call_data->cur_tuple_item > RumGetNPosting(inter_call_data->cur_itup)) + inter_call_data->need_new_tuple = true; + + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); + } + + /* Completing the function */ + SRF_RETURN_DONE(fctx); +} + +/* + * The function rum_internal_entry_page_items() is designed + * to view information that is located on the internal + * pages of the index's rum entry tree. On these pages, + * information is stored in the IndexTuples. + * + * The function returns the key, the key attribute number, + * the key category (see rum.h), and the child page number. + * + * It is a SRF function, i.e. it returns one tuple per call. + */ +Datum +rum_internal_entry_page_items(PG_FUNCTION_ARGS) +{ + /* Reading input arguments */ + text *relname = PG_GETARG_TEXT_PP(0); + uint32 blkno = PG_GETARG_UINT32(1); + + /* + * The context of the function calls and the pointer + * to the long-lived inter_call_data structure. + */ + FuncCallContext *fctx; + rum_page_items_state *inter_call_data; + + /* Only the superuser can use this */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to use this function"))); + + /* + * In the case of the first function call, it is necessary + * to get the page by its number and create a RumState + * structure for scanning the page. + */ + if (SRF_IS_FIRSTCALL()) + { + Relation rel; /* needed to initialize the RumState structure */ + bytea *raw_page; /* the raw page obtained from rel */ + + TupleDesc tupdesc; /* description of the result tuple */ + MemoryContext oldmctx; /* the old function memory context */ + Page page; /* the page to be scanned */ + RumPageOpaque opaq; /* data from the opaque area of the page */ + + /* + * Initializing the FuncCallContext structure and switching the memory + * context to the one needed for structures that must be saved during + * multiple calls. + */ + fctx = SRF_FIRSTCALL_INIT(); + oldmctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx); + + /* Getting rel by name and raw page by number */ + rel = get_rel_from_name(relname); + raw_page = get_rel_raw_page(rel, blkno); + + /* Allocating memory for a long-lived structure */ + inter_call_data = palloc(sizeof(rum_page_items_state)); + + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); + + relation_close(rel, AccessShareLock); + + /* Getting a copy of the page from the raw page */ + page = get_page_from_raw(raw_page); + + /* If the page is new, the function should return NULL */ + if (PageIsNew(page)) + { + MemoryContextSwitchTo(oldmctx); + PG_RETURN_NULL(); + } + + /* Checking the size of the opaque area of the page */ + if (PageGetSpecialSize(page) != MAXALIGN(sizeof(RumPageOpaqueData))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a valid RUM page"), + errdetail("Expected special size %d, got %d.", + (int) MAXALIGN(sizeof(RumPageOpaqueData)), + (int) PageGetSpecialSize(page)))); + + /* Getting a page description from an opaque area */ + opaq = RumPageGetOpaque(page); + + /* Checking the flags */ + if (opaq->flags != 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("input page is not a RUM {} page"), + errdetail("Flags %04X, expected %04X", + opaq->flags, 0))); + + /* 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"); + + /* Needed to for subsequent recording tupledesc in fctx */ + BlessTupleDesc(tupdesc); + + /* + * We save all the necessary information for + * scanning the page in a long-lived structure. + */ + inter_call_data->page = page; + inter_call_data->maxoff = PageGetMaxOffsetNumber(page); + inter_call_data->cur_tuple_num = FirstOffsetNumber; + + /* + * Save a pointer to a long-lived structure and + * tuple descriptor for our result type in fctx. + */ + fctx->user_fctx = inter_call_data; + fctx->tuple_desc = tupdesc; + + /* Switching to the old memory context */ + MemoryContextSwitchTo(oldmctx); + } + + /* Preparing to use the FuncCallContext */ + fctx = SRF_PERCALL_SETUP(); + + /* In the current call, we are reading data from the previous one */ + inter_call_data = fctx->user_fctx; + + /* Go through the page */ + if(inter_call_data->cur_tuple_num <= inter_call_data->maxoff) + { + /* returned values */ + Datum values[4]; + + /* true if the corresponding returned value is NULL */ + bool nulls[4]; + + /* For the results */ + HeapTuple resultTuple; + Datum result; + + /* To convert a key to text */ + Oid key_oid; + + memset(nulls, 0, sizeof(nulls)); + + /* Read the new IndexTuple and get the values that are stored in it */ + get_new_index_tuple(inter_call_data); + + /* Scanning the IndexTuple that we received earlier */ + inter_call_data->cur_tuple_key_attnum = rumtuple_get_attrnum(inter_call_data->rum_state_ptr, + inter_call_data->cur_itup); + inter_call_data->cur_tuple_key = rumtuple_get_key(inter_call_data->rum_state_ptr, + inter_call_data->cur_itup, + &(inter_call_data->cur_tuple_key_category)); + inter_call_data->cur_tuple_down_link = RumGetDownlink(inter_call_data->cur_itup); + + /* + * On the rightmost page, in the last IndexTuple, there is a + * high key, which is assumed to be equal to +inf. + */ + if (RumPageRightMost(inter_call_data->page) && + inter_call_data->cur_tuple_num == inter_call_data->maxoff) + { + values[0] = CStringGetTextDatum("+inf"); + nulls[1] = true; + nulls[2] = true; + values[3] = UInt32GetDatum(inter_call_data->cur_tuple_down_link); + + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* Increase the counter before the next call */ + inter_call_data->cur_tuple_num++; + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); + } + + /* Increase the counter before the next call */ + inter_call_data->cur_tuple_num++; + + /* Getting the key attribute number */ + key_oid = get_cur_attr_oid(inter_call_data); + + /* Filling in the returned values */ + if (inter_call_data->cur_tuple_key_category == RUM_CAT_NORM_KEY) + values[0] = get_datum_text_by_oid(inter_call_data->cur_tuple_key, key_oid); + else nulls[0] = true; + + values[1] = UInt16GetDatum(inter_call_data->cur_tuple_key_attnum); + values[2] = category_get_datum_text(inter_call_data->cur_tuple_key_category); + values[3] = UInt32GetDatum(inter_call_data->cur_tuple_down_link); + + /* Forming the returned tuple */ + resultTuple = heap_form_tuple(fctx->tuple_desc, values, nulls); + result = HeapTupleGetDatum(resultTuple); + + /* Returning the result of the current call */ + SRF_RETURN_NEXT(fctx, result); + } + + /* Completing the function */ + SRF_RETURN_DONE(fctx); +} + +/* + * This function returns the key category as text. + */ +static Datum +category_get_datum_text(RumNullCategory category) +{ + char category_arr[][20] = {"RUM_CAT_NORM_KEY", + "RUM_CAT_NULL_KEY", + "RUM_CAT_EMPTY_ITEM", + "RUM_CAT_NULL_ITEM", + "RUM_CAT_EMPTY_QUERY"}; + + switch(category) + { + case RUM_CAT_NORM_KEY: + return CStringGetTextDatum(category_arr[0]); + + case RUM_CAT_NULL_KEY: + return CStringGetTextDatum(category_arr[1]); + + case RUM_CAT_EMPTY_ITEM: + return CStringGetTextDatum(category_arr[2]); + + case RUM_CAT_NULL_ITEM: + return CStringGetTextDatum(category_arr[3]); + + case RUM_CAT_EMPTY_QUERY: + return CStringGetTextDatum(category_arr[4]); + } + + /* In case of an error, return 0 */ + return (Datum) 0; +} + +/* + * This function places a pointer to a new IndexTuple + * in the rum_page_items_state structure. + */ +static void +get_new_index_tuple(rum_page_items_state *inter_call_data) +{ + inter_call_data->cur_itup = (IndexTuple) PageGetItem(inter_call_data->page, + PageGetItemId(inter_call_data->page, inter_call_data->cur_tuple_num)); +} + +/* + * This function gets the attribute number of the + * current key from the RumState structure. + */ +static Oid +get_cur_attr_oid(rum_page_items_state *inter_call_data) +{ + Oid result; + TupleDesc orig_tuple_desc; + OffsetNumber attnum; + FormData_pg_attribute *attrs; + + attnum = inter_call_data->cur_tuple_key_attnum; + orig_tuple_desc = inter_call_data->rum_state_ptr->origTupdesc; + attrs = orig_tuple_desc->attrs; + result = (attrs[attnum - 1]).atttypid; + + return result; +} + +/* + * A copy of the get_page_from_raw() + * function from pageinspect. + * + * Get a palloc'd, maxalign'ed page image + * from the result of get_rel_raw_page() + */ +static Page +get_page_from_raw(bytea *raw_page) +{ + Page page; + int raw_page_size; + + raw_page_size = VARSIZE_ANY_EXHDR(raw_page); + + if (raw_page_size != BLCKSZ) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid page size"), + errdetail("Expected %d bytes, got %d.", + BLCKSZ, raw_page_size))); + + page = palloc(raw_page_size); + + memcpy(page, VARDATA_ANY(raw_page), raw_page_size); + + return page; +} + +/* + * An auxiliary function that is used to convert information + * (being a Datum) into text. This is a universal way to + * return any type. + * + * The following types of data are checked: + * int2, int4, int8, float4, float8, money, oid, timestamp, + * timestamptz, time, timetz, date, interval, macaddr, inet, + * cidr, text, varchar, char, bytea, bit, varbit, numeric. + * + * TODO: All types accepted by rum must be checked, but + * perhaps some types are missing or some are superfluous. + */ +static Datum +get_datum_text_by_oid(Datum info, Oid info_oid) +{ + char *str_info = NULL; + + /* info cannot be NULL */ + Assert(DatumGetPointer(info) != NULL); + + /* + * Form a string depending on the type of info. + * + * FIXME: The macros used below are taken from the + * pg_type_d file.h, and it says not to use them + * in the new code. + */ + switch (info_oid) + { + case INT2OID: + str_info = OidOutputFunctionCall(F_INT2OUT, info); + break; + + case INT4OID: + str_info = OidOutputFunctionCall(F_INT4OUT, info); + break; + + case INT8OID: + str_info = OidOutputFunctionCall(F_INT8OUT, info); + break; + + case FLOAT4OID: + str_info = OidOutputFunctionCall(F_FLOAT4OUT, info); + break; + + case FLOAT8OID: + str_info = OidOutputFunctionCall(F_FLOAT8OUT, info); + break; + + /* + * TODO: The oid of the function for displaying this + * type as text could not be found. + */ + case MONEYOID: + /* str_addInfo = OidOutputFunctionCall(, addInfo); */ + /* break; */ + return CStringGetTextDatum("MONEYOID is not supported"); + + case OIDOID: + str_info = OidOutputFunctionCall(F_OIDOUT, info); + break; + + case TIMESTAMPOID: + str_info = OidOutputFunctionCall(F_TIMESTAMP_OUT, info); + break; + + case TIMESTAMPTZOID: + str_info = OidOutputFunctionCall(F_TIMESTAMPTZ_OUT, info); + break; + + case TIMEOID: + str_info = OidOutputFunctionCall(F_TIME_OUT, info); + break; + + case TIMETZOID: + str_info = OidOutputFunctionCall(F_TIMETZ_OUT, info); + break; + + case DATEOID: + str_info = OidOutputFunctionCall(F_DATE_OUT, info); + break; + + case INTERVALOID: + str_info = OidOutputFunctionCall(F_INTERVAL_OUT, info); + break; + + case MACADDROID: + str_info = OidOutputFunctionCall(F_MACADDR_OUT, info); + break; + + case INETOID: + str_info = OidOutputFunctionCall(F_INET_OUT, info); + break; + + case CIDROID: + str_info = OidOutputFunctionCall(F_CIDR_OUT, info); + break; + + case TEXTOID: + return info; + + case VARCHAROID: + str_info = OidOutputFunctionCall(F_VARCHAROUT, info); + break; + + case CHAROID: + str_info = OidOutputFunctionCall(F_CHAROUT, info); + break; + + /* + * TODO: For some reason, the rum index created for a single tsv + * field contains additional information as bytea. In addition, + * if additional information in this format is extracted from + * posting tree pages, it cannot be displayed correctly as text. + * If the additional information was extracted from the entry + * tree pages, then it is displayed correctly. + */ + case BYTEAOID: + /* str_info = OidOutputFunctionCall(F_BYTEAOUT, info); */ + /* break; */ + return CStringGetTextDatum("BYTEAOID is not supported"); + + case BITOID: + str_info = OidOutputFunctionCall(F_BIT_OUT, info); + break; + + case VARBITOID: + str_info = OidOutputFunctionCall(F_VARBIT_OUT, info); + break; + + case NUMERICOID: + str_info = OidOutputFunctionCall(F_NUMERIC_OUT, info); + break; + + default: + return CStringGetTextDatum("unsupported type"); + } + + return CStringGetTextDatum(str_info); +} + +/* + * This function and get_rel_raw_page() are derived from the separation + * of the get_raw_page_internal() function, which was copied from the pageinspect code. + * It is needed in order to call the initRumState() function if necessary. + */ +static Relation +get_rel_from_name(text *relname) +{ + RangeVar *relrv; + Relation rel; + + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); + rel = relation_openrv(relrv, AccessShareLock); + + if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from relation \"%s\"", + RelationGetRelationName(rel)), errdetail_relkind_not_supported(rel->rd_rel->relkind))); /* @@ -668,3 +1552,53 @@ get_rel_raw_page(Relation rel, BlockNumber blkno) return raw_page; } + +/* + * This function looks through the addAttrs array and extracts + * the Oid of additional information for an attribute for + * which it is not NULL. + * + * TODO: The logic of the function assumes that there cannot + * be several types of additional information in the index, + * otherwise it will not work. + */ +static Oid +find_add_info_oid(RumState *rum_state_ptr) +{ + Oid add_info_oid = 0; + + /* Number of index attributes */ + int num_attrs = rum_state_ptr->origTupdesc->natts; + + /* + * For each of the attributes, we read the + * oid of additional information. + */ + for (int i = 0; i < num_attrs; i++) + if ((rum_state_ptr->addAttrs)[i] != NULL) + add_info_oid = ((rum_state_ptr->addAttrs)[i])->atttypid; + + return add_info_oid; +} + +/* + * This is an auxiliary function to get the attribute number + * for additional information. It is used in the rum_leaf_data_page_items() + * function to call the rumDataPageLeafRead() function. + */ +static OffsetNumber +find_add_info_atrr_num(RumState *rum_state_ptr) +{ + OffsetNumber add_info_attr_num = 0; + + /* Number of index attributes */ + int num_attrs = rum_state_ptr->origTupdesc->natts; + + /* Go through the addAttrs array */ + for (int i = 0; i < num_attrs; i++) + if ((rum_state_ptr->addAttrs)[i] != NULL) + add_info_attr_num = i; + + /* Need to add 1 because the attributes are numbered from 1 */ + return add_info_attr_num + 1; +} From bbe410b779b624eb2895837fc28f0d3622335eae Mon Sep 17 00:00:00 2001 From: Arseny Kositsyn Date: Mon, 21 Apr 2025 22:01:45 +0300 Subject: [PATCH 3/7] [PGPRO-12159] Added the output of tsv lexemes positions. If you create an index with the operator class rum_tsvector_ops, the positions of the lexemes will be saved as additional information. The positions are stored in compressed form in bytea. There is a problem that is related to the fact that in the posting tree, additional information for the senior keys is stored in a different way, which is why it has not yet been possible to output it. For all other cases, the output of additional information works correctly. Tags: rum --- src/rum.h | 6 ++ src/rum_debug_funcs.c | 206 +++++++++++++++++++++++++++++++++++------- src/rum_ts_utils.c | 8 +- 3 files changed, 182 insertions(+), 38 deletions(-) diff --git a/src/rum.h b/src/rum.h index 2139774d08..188e3da241 100644 --- a/src/rum.h +++ b/src/rum.h @@ -21,6 +21,7 @@ #include "storage/bufmgr.h" #include "utils/datum.h" #include "utils/memutils.h" +#include "tsearch/ts_type.h" #include "rumsort.h" @@ -836,6 +837,8 @@ extern RumItem *rumGetBAEntry(BuildAccumulator *accum, #define RUM_ADDINFO_JOIN 10 #define RUMNProcs 10 +#define LOWERMASK 0x1F + extern PGDLLEXPORT Datum rum_extract_tsvector(PG_FUNCTION_ARGS); extern PGDLLEXPORT Datum rum_extract_tsquery(PG_FUNCTION_ARGS); extern PGDLLEXPORT Datum rum_tsvector_config(PG_FUNCTION_ARGS); @@ -847,6 +850,9 @@ extern PGDLLEXPORT Datum rum_ts_distance_td(PG_FUNCTION_ARGS); extern PGDLLEXPORT Datum tsquery_to_distance_query(PG_FUNCTION_ARGS); +extern char* decompress_pos(char *ptr, WordEntryPos *pos); +extern unsigned int count_pos(char *ptr, int len); + /* rum_arr_utils.c */ typedef enum SimilarityType { diff --git a/src/rum_debug_funcs.c b/src/rum_debug_funcs.c index 80b4acee98..96138709c5 100644 --- a/src/rum_debug_funcs.c +++ b/src/rum_debug_funcs.c @@ -27,6 +27,7 @@ #include "access/relation.h" #include "utils/varlena.h" #include "rum.h" +#include "tsearch/ts_type.h" PG_FUNCTION_INFO_V1(rum_metapage_info); PG_FUNCTION_INFO_V1(rum_page_opaque_info); @@ -115,6 +116,8 @@ static Datum category_get_datum_text(RumNullCategory category); static Oid find_add_info_oid(RumState *rum_state_ptr); static OffsetNumber find_add_info_atrr_num(RumState *rum_state_ptr); +static Datum get_positions_to_text_datum(Datum add_info); + /* * The rum_metapage_info() function is used to retrieve * information stored on the meta page of the rum index. @@ -386,12 +389,6 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) /* Allocating memory for a long-lived structure */ inter_call_data = palloc(sizeof(rum_page_items_state)); - /* Initializing the RumState structure */ - inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); - initRumState(inter_call_data->rum_state_ptr, rel); - - relation_close(rel, AccessShareLock); - /* Getting a copy of the page from the raw page */ page = get_page_from_raw(raw_page); @@ -422,6 +419,12 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) errdetail("Flags %04X, expected %04X", opaq->flags, (RUM_DATA | RUM_LEAF)))); + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); + + relation_close(rel, AccessShareLock); + /* 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"); @@ -494,9 +497,24 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) values[2] = BoolGetDatum(high_key_ptr->addInfoIsNull); /* Returning add info */ - if(!high_key_ptr->addInfoIsNull && inter_call_data->add_info_oid != 0) + if(!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid != BYTEAOID) + { values[3] = get_datum_text_by_oid(high_key_ptr->addInfo, inter_call_data->add_info_oid); + } + + /* + * In this case, we are dealing with the positions + * of tokens and they need to be decoded. + */ + else if (!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid == BYTEAOID) + { + /* values[3] = get_positions_to_text_datum(high_key_ptr->addInfo); */ + values[3] = CStringGetTextDatum("high key positions in posting tree is not supported"); + } + else nulls[3] = true; /* Forming the returned tuple */ @@ -536,8 +554,23 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) values[2] = BoolGetDatum(rum_item_ptr->addInfoIsNull); /* Returning add info */ - if(!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0) - values[3] = get_datum_text_by_oid(rum_item_ptr->addInfo, inter_call_data->add_info_oid); + if(!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid != BYTEAOID) + { + values[3] = get_datum_text_by_oid(rum_item_ptr->addInfo, + inter_call_data->add_info_oid); + } + + /* + * In this case, we are dealing with the positions + * of tokens and they need to be decoded. + */ + else if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid == BYTEAOID) + { + values[3] = get_positions_to_text_datum(rum_item_ptr->addInfo); + } + else nulls[3] = true; /* Forming the returned tuple */ @@ -619,12 +652,6 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) /* Allocating memory for a long-lived structure */ inter_call_data = palloc(sizeof(rum_page_items_state)); - /* Initializing the RumState structure */ - inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); - initRumState(inter_call_data->rum_state_ptr, rel); - - relation_close(rel, AccessShareLock); - /* Getting a copy of the page from the raw page */ page = get_page_from_raw(raw_page); @@ -655,6 +682,12 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) errdetail("Flags %04X, expected %04X", opaq->flags, (RUM_DATA & ~RUM_LEAF)))); + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); + + relation_close(rel, AccessShareLock); + /* 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"); @@ -721,9 +754,24 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) values[3] = BoolGetDatum(high_key_ptr->addInfoIsNull); /* Returning add info */ - if(!high_key_ptr->addInfoIsNull && inter_call_data->add_info_oid != 0) + if(!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid != BYTEAOID) + { values[4] = get_datum_text_by_oid(high_key_ptr->addInfo, inter_call_data->add_info_oid); + } + + /* + * In this case, we are dealing with the positions + * of tokens and they need to be decoded. + */ + else if (!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid == BYTEAOID) + { + /* values[4] = get_positions_to_text_datum(high_key_ptr->addInfo); */ + values[4] = CStringGetTextDatum("high key positions in posting tree is not supported"); + } + else nulls[4] = true; /* Forming the returned tuple */ @@ -745,9 +793,24 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) values[3] = BoolGetDatum(posting_item_ptr->item.addInfoIsNull); /* Returning add info */ - if(!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != 0) + if(!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid != BYTEAOID) + { values[4] = get_datum_text_by_oid(posting_item_ptr->item.addInfo, inter_call_data->add_info_oid); + } + + /* + * In this case, we are dealing with the positions + * of tokens and they need to be decoded. + */ + else if (!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid == BYTEAOID) + { + /* values[4] = get_positions_to_text_datum(posting_item_ptr->item.addInfo); */ + values[4] = CStringGetTextDatum("high key positions in posting tree is not supported"); + } + else nulls[4] = true; /* Forming the returned tuple */ @@ -833,12 +896,6 @@ rum_leaf_entry_page_items(PG_FUNCTION_ARGS) /* Allocating memory for a long-lived structure */ inter_call_data = palloc(sizeof(rum_page_items_state)); - /* Initializing the RumState structure */ - inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); - initRumState(inter_call_data->rum_state_ptr, rel); - - relation_close(rel, AccessShareLock); - /* Getting a copy of the page from the raw page */ page = get_page_from_raw(raw_page); @@ -869,6 +926,12 @@ rum_leaf_entry_page_items(PG_FUNCTION_ARGS) errdetail("Flags %04X, expected %04X", opaq->flags, RUM_LEAF))); + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); + + relation_close(rel, AccessShareLock); + /* 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"); @@ -1008,10 +1071,23 @@ rum_leaf_entry_page_items(PG_FUNCTION_ARGS) values[3] = ItemPointerGetDatum(&(rum_item_ptr->iptr)); values[4] = BoolGetDatum(rum_item_ptr->addInfoIsNull); - /* Returning add info */ - if(!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0) + if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 && + inter_call_data->add_info_oid != BYTEAOID) + { values[5] = get_datum_text_by_oid(rum_item_ptr->addInfo, inter_call_data->add_info_oid); + } + + /* + * In this case, we are dealing with the positions + * of tokens and they need to be decoded. + */ + else if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + && inter_call_data->add_info_oid == BYTEAOID) + { + values[5] = get_positions_to_text_datum(rum_item_ptr->addInfo); + } + else nulls[5] = true; /* The current IndexTuple does not contain a posting tree */ @@ -1101,12 +1177,6 @@ rum_internal_entry_page_items(PG_FUNCTION_ARGS) /* Allocating memory for a long-lived structure */ inter_call_data = palloc(sizeof(rum_page_items_state)); - /* Initializing the RumState structure */ - inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); - initRumState(inter_call_data->rum_state_ptr, rel); - - relation_close(rel, AccessShareLock); - /* Getting a copy of the page from the raw page */ page = get_page_from_raw(raw_page); @@ -1137,6 +1207,12 @@ rum_internal_entry_page_items(PG_FUNCTION_ARGS) errdetail("Flags %04X, expected %04X", opaq->flags, 0))); + /* Initializing the RumState structure */ + inter_call_data->rum_state_ptr = palloc(sizeof(RumState)); + initRumState(inter_call_data->rum_state_ptr, rel); + + relation_close(rel, AccessShareLock); + /* 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"); @@ -1355,7 +1431,7 @@ get_page_from_raw(bytea *raw_page) * TODO: All types accepted by rum must be checked, but * perhaps some types are missing or some are superfluous. */ -static Datum +static Datum get_datum_text_by_oid(Datum info, Oid info_oid) { char *str_info = NULL; @@ -1602,3 +1678,69 @@ find_add_info_atrr_num(RumState *rum_state_ptr) /* Need to add 1 because the attributes are numbered from 1 */ return add_info_attr_num + 1; } + +#define POS_STR_BUF_LENGHT 1024 +#define POS_MAX_VAL_LENGHT 6 + +/* + * A function for extracting the positions of tokens from additional + * information. Returns a string in which the positions of the tokens + * are recorded. The memory that the string occupies must be cleared later. + */ +static Datum +get_positions_to_text_datum(Datum add_info) +{ + bytea *positions; + char *ptrt; + WordEntryPos position = 0; + int32 npos; + + Datum res; + char *positions_str; + char *positions_str_cur_ptr; + int cur_max_str_lenght; + + positions = DatumGetByteaP(add_info); + ptrt = (char *) VARDATA_ANY(positions); + npos = count_pos(VARDATA_ANY(positions), + VARSIZE_ANY_EXHDR(positions)); + + /* Initialize the string */ + positions_str = (char*) palloc(POS_STR_BUF_LENGHT * sizeof(char)); + positions_str[0] = '\0'; + cur_max_str_lenght = POS_STR_BUF_LENGHT; + positions_str_cur_ptr = positions_str; + + /* Extract the positions of the tokens and put them in the string */ + for (int i = 0; i < npos; i++) + { + /* At each iteration decode the position */ + ptrt = decompress_pos(ptrt, &position); + + /* Write this position in the string */ + sprintf(positions_str_cur_ptr, "%d,", position); + + /* Moving the pointer forward */ + positions_str_cur_ptr += strlen(positions_str_cur_ptr); + + /* + * Check that there is not too little left to the + * end of the line and, if necessary, overspend + * the memory. + */ + if (cur_max_str_lenght - (positions_str_cur_ptr - positions_str) <= POS_MAX_VAL_LENGHT) + { + cur_max_str_lenght += POS_STR_BUF_LENGHT; + positions_str = (char*) repalloc(positions_str, cur_max_str_lenght * sizeof(char)); + positions_str_cur_ptr = positions_str + strlen(positions_str); + } + } + + /* Delete the last comma if there has been at least one iteration of the loop */ + if (npos > 0) + positions_str[strlen(positions_str) - 1] = '\0'; + + res = CStringGetTextDatum(positions_str); + pfree(positions_str); + return res; +} diff --git a/src/rum_ts_utils.c b/src/rum_ts_utils.c index d3b9c5478a..042f0f2d81 100644 --- a/src/rum_ts_utils.c +++ b/src/rum_ts_utils.c @@ -16,7 +16,6 @@ #include "catalog/pg_type.h" #include "funcapi.h" #include "miscadmin.h" -#include "tsearch/ts_type.h" #include "tsearch/ts_utils.h" #include "utils/array.h" #include "utils/builtins.h" @@ -80,8 +79,6 @@ PG_FUNCTION_INFO_V1(rum_ts_join_pos); PG_FUNCTION_INFO_V1(tsquery_to_distance_query); -static unsigned int count_pos(char *ptr, int len); -static char *decompress_pos(char *ptr, WordEntryPos *pos); static Datum build_tsvector_entry(TSVector vector, WordEntry *we); static Datum build_tsvector_hash_entry(TSVector vector, WordEntry *we); static Datum build_tsquery_entry(TSQuery query, QueryOperand *operand); @@ -964,7 +961,6 @@ rum_tsquery_timestamp_consistent(PG_FUNCTION_ARGS) } #define SIXTHBIT 0x20 -#define LOWERMASK 0x1F static unsigned int compress_pos(char *target, WordEntryPos *pos, int npos) @@ -999,7 +995,7 @@ compress_pos(char *target, WordEntryPos *pos, int npos) return ptr - target; } -static char * +extern char * decompress_pos(char *ptr, WordEntryPos *pos) { int i; @@ -1027,7 +1023,7 @@ decompress_pos(char *ptr, WordEntryPos *pos) } } -static unsigned int +extern unsigned int count_pos(char *ptr, int len) { int count = 0, From a0c5c9791f831d2e71b40777bbf852d58ae29b1f Mon Sep 17 00:00:00 2001 From: Arseny Kositsyn Date: Mon, 28 Apr 2025 17:08:00 +0300 Subject: [PATCH 4/7] [PGPRO-12159] Added the output of weights. If the index is created with the appropriate class of operators, then in addition to the positions of the lexemes, weights (A, B, C, D) are also stored in the additional information. Their output has been added. In addition, Asserts have been added to the find_add_info_atr_num() and find_add_info_oid() functions, which check that there is only one (or zero) type of additional information in the index. Tags: rum --- src/rum_debug_funcs.c | 141 ++++++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 68 deletions(-) diff --git a/src/rum_debug_funcs.c b/src/rum_debug_funcs.c index 96138709c5..0b48a74d21 100644 --- a/src/rum_debug_funcs.c +++ b/src/rum_debug_funcs.c @@ -14,10 +14,12 @@ * 2) I/O functions were not available for all types in * in the get_datum_text_by_oid() function. * - * 3) SIGSEGV in case of bytea output as additional information. + * 3) The output of lexeme positions in the high keys of the posting + * tree is not supported. */ #include "postgres.h" +#include "miscadmin.h" #include "fmgr.h" #include "funcapi.h" #include "catalog/namespace.h" @@ -115,8 +117,8 @@ static Oid get_cur_attr_oid(rum_page_items_state *inter_call_data); static Datum category_get_datum_text(RumNullCategory category); static Oid find_add_info_oid(RumState *rum_state_ptr); static OffsetNumber find_add_info_atrr_num(RumState *rum_state_ptr); - static Datum get_positions_to_text_datum(Datum add_info); +static char pos_get_weight(WordEntryPos position); /* * The rum_metapage_info() function is used to retrieve @@ -472,7 +474,7 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) */ if(fctx->call_cntr <= inter_call_data->maxoff) { - RumItem *high_key_ptr; + RumItem *high_key_ptr; /* to read high key from a page */ RumItem *rum_item_ptr; /* to read data from a page */ Datum values[4]; /* return values */ bool nulls[4]; /* true if the corresponding value is NULL */ @@ -497,7 +499,7 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) values[2] = BoolGetDatum(high_key_ptr->addInfoIsNull); /* Returning add info */ - if(!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + if(!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid != BYTEAOID) { values[3] = get_datum_text_by_oid(high_key_ptr->addInfo, @@ -506,12 +508,11 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) /* * In this case, we are dealing with the positions - * of tokens and they need to be decoded. + * of lexemes and they need to be decoded. */ - else if (!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + else if (!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid == BYTEAOID) { - /* values[3] = get_positions_to_text_datum(high_key_ptr->addInfo); */ values[3] = CStringGetTextDatum("high key positions in posting tree is not supported"); } @@ -525,26 +526,8 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) SRF_RETURN_NEXT(fctx, result); } - /* - * Reading information from the page in rum_item. - * - * TODO: The fact is that being on the posting tree page, we don't know which - * index attribute this posting tree was built for, so we don't know the - * attribute number of the additional information. But the rumDataPageLeafRead() - * function requires it to read information from the page. Here we use the auxiliary - * function find_add_info_atr_num(), which simply iterates through the array with - * attributes that are additional information and selects the attribute number for - * which the additional information attribute is not NULL. This approach is incorrect - * because there may not be additional information for the attribute on the page, - * but we hope that in this case add_info_is_null will have the value true and the - * additional information will not be read. - * - * This problem can be solved by asking the user for the attribute number of - * additional information, because going through the index from top to bottom, - * he saw it next to the link to the posting tree root. - */ + /* Reading information from the page in rum_item */ inter_call_data->item_ptr = rumDataPageLeafRead(inter_call_data->item_ptr, - /* inter_call_data->cur_tuple_key_attnum, */ find_add_info_atrr_num(inter_call_data->rum_state_ptr), rum_item_ptr, false, inter_call_data->rum_state_ptr); @@ -554,7 +537,7 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) values[2] = BoolGetDatum(rum_item_ptr->addInfoIsNull); /* Returning add info */ - if(!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + if(!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid != BYTEAOID) { values[3] = get_datum_text_by_oid(rum_item_ptr->addInfo, @@ -563,9 +546,9 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) /* * In this case, we are dealing with the positions - * of tokens and they need to be decoded. + * of lexemes and they need to be decoded. */ - else if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + else if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid == BYTEAOID) { values[3] = get_positions_to_text_datum(rum_item_ptr->addInfo); @@ -729,7 +712,7 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) */ if(fctx->call_cntr <= inter_call_data->maxoff) { - RumItem *high_key_ptr; + RumItem *high_key_ptr; /* to read high key from a page */ PostingItem *posting_item_ptr; /* to read data from a page */ Datum values[5]; /* returned values */ bool nulls[5]; /* true if the corresponding returned value is NULL */ @@ -754,7 +737,7 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) values[3] = BoolGetDatum(high_key_ptr->addInfoIsNull); /* Returning add info */ - if(!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + if(!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid != BYTEAOID) { values[4] = get_datum_text_by_oid(high_key_ptr->addInfo, @@ -763,12 +746,11 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) /* * In this case, we are dealing with the positions - * of tokens and they need to be decoded. + * of lexemes and they need to be decoded. */ - else if (!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + else if (!(high_key_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid == BYTEAOID) { - /* values[4] = get_positions_to_text_datum(high_key_ptr->addInfo); */ values[4] = CStringGetTextDatum("high key positions in posting tree is not supported"); } @@ -793,7 +775,7 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) values[3] = BoolGetDatum(posting_item_ptr->item.addInfoIsNull); /* Returning add info */ - if(!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != 0 + if(!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid != BYTEAOID) { values[4] = get_datum_text_by_oid(posting_item_ptr->item.addInfo, @@ -802,12 +784,11 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) /* * In this case, we are dealing with the positions - * of tokens and they need to be decoded. + * of lexemes and they need to be decoded. */ - else if (!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != 0 + else if (!posting_item_ptr->item.addInfoIsNull && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid == BYTEAOID) { - /* values[4] = get_positions_to_text_datum(posting_item_ptr->item.addInfo); */ values[4] = CStringGetTextDatum("high key positions in posting tree is not supported"); } @@ -1072,7 +1053,7 @@ rum_leaf_entry_page_items(PG_FUNCTION_ARGS) values[4] = BoolGetDatum(rum_item_ptr->addInfoIsNull); /* Returning add info */ - if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 && + if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid != BYTEAOID) { values[5] = get_datum_text_by_oid(rum_item_ptr->addInfo, inter_call_data->add_info_oid); @@ -1080,9 +1061,9 @@ rum_leaf_entry_page_items(PG_FUNCTION_ARGS) /* * In this case, we are dealing with the positions - * of tokens and they need to be decoded. + * of lexemes and they need to be decoded. */ - else if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != 0 + else if (!(rum_item_ptr->addInfoIsNull) && inter_call_data->add_info_oid != InvalidOid && inter_call_data->add_info_oid == BYTEAOID) { values[5] = get_positions_to_text_datum(rum_item_ptr->addInfo); @@ -1427,22 +1408,16 @@ get_page_from_raw(bytea *raw_page) * int2, int4, int8, float4, float8, money, oid, timestamp, * timestamptz, time, timetz, date, interval, macaddr, inet, * cidr, text, varchar, char, bytea, bit, varbit, numeric. - * - * TODO: All types accepted by rum must be checked, but - * perhaps some types are missing or some are superfluous. */ static Datum get_datum_text_by_oid(Datum info, Oid info_oid) { char *str_info = NULL; - /* info cannot be NULL */ - Assert(DatumGetPointer(info) != NULL); - /* * Form a string depending on the type of info. * - * FIXME: The macros used below are taken from the + * TODO: The macros used below are taken from the * pg_type_d file.h, and it says not to use them * in the new code. */ @@ -1528,18 +1503,9 @@ get_datum_text_by_oid(Datum info, Oid info_oid) str_info = OidOutputFunctionCall(F_CHAROUT, info); break; - /* - * TODO: For some reason, the rum index created for a single tsv - * field contains additional information as bytea. In addition, - * if additional information in this format is extracted from - * posting tree pages, it cannot be displayed correctly as text. - * If the additional information was extracted from the entry - * tree pages, then it is displayed correctly. - */ case BYTEAOID: - /* str_info = OidOutputFunctionCall(F_BYTEAOUT, info); */ - /* break; */ - return CStringGetTextDatum("BYTEAOID is not supported"); + str_info = OidOutputFunctionCall(F_BYTEAOUT, info); + break; case BITOID: str_info = OidOutputFunctionCall(F_BIT_OUT, info); @@ -1634,14 +1600,14 @@ get_rel_raw_page(Relation rel, BlockNumber blkno) * the Oid of additional information for an attribute for * which it is not NULL. * - * TODO: The logic of the function assumes that there cannot + * The logic of the function assumes that there cannot * be several types of additional information in the index, * otherwise it will not work. */ static Oid find_add_info_oid(RumState *rum_state_ptr) { - Oid add_info_oid = 0; + Oid add_info_oid = InvalidOid; /* Number of index attributes */ int num_attrs = rum_state_ptr->origTupdesc->natts; @@ -1651,8 +1617,13 @@ find_add_info_oid(RumState *rum_state_ptr) * oid of additional information. */ for (int i = 0; i < num_attrs; i++) + { if ((rum_state_ptr->addAttrs)[i] != NULL) + { + Assert(add_info_oid == InvalidOid); add_info_oid = ((rum_state_ptr->addAttrs)[i])->atttypid; + } + } return add_info_oid; } @@ -1661,19 +1632,28 @@ find_add_info_oid(RumState *rum_state_ptr) * This is an auxiliary function to get the attribute number * for additional information. It is used in the rum_leaf_data_page_items() * function to call the rumDataPageLeafRead() function. + * + * The logic of the function assumes that there cannot + * be several types of additional information in the index, + * otherwise it will not work. */ static OffsetNumber find_add_info_atrr_num(RumState *rum_state_ptr) { - OffsetNumber add_info_attr_num = 0; + OffsetNumber add_info_attr_num = InvalidOffsetNumber; /* Number of index attributes */ int num_attrs = rum_state_ptr->origTupdesc->natts; /* Go through the addAttrs array */ - for (int i = 0; i < num_attrs; i++) + for (int i = 0; i < num_attrs; i++) + { if ((rum_state_ptr->addAttrs)[i] != NULL) + { + Assert(add_info_attr_num == InvalidOffsetNumber); add_info_attr_num = i; + } + } /* Need to add 1 because the attributes are numbered from 1 */ return add_info_attr_num + 1; @@ -1683,8 +1663,8 @@ find_add_info_atrr_num(RumState *rum_state_ptr) #define POS_MAX_VAL_LENGHT 6 /* - * A function for extracting the positions of tokens from additional - * information. Returns a string in which the positions of the tokens + * A function for extracting the positions of lexemes from additional + * information. Returns a string in which the positions of the lexemes * are recorded. The memory that the string occupies must be cleared later. */ static Datum @@ -1711,14 +1691,17 @@ get_positions_to_text_datum(Datum add_info) cur_max_str_lenght = POS_STR_BUF_LENGHT; positions_str_cur_ptr = positions_str; - /* Extract the positions of the tokens and put them in the string */ + /* Extract the positions of the lexemes and put them in the string */ for (int i = 0; i < npos; i++) { /* At each iteration decode the position */ ptrt = decompress_pos(ptrt, &position); - /* Write this position in the string */ - sprintf(positions_str_cur_ptr, "%d,", position); + /* Write this position and weight in the string */ + if(pos_get_weight(position) == 'D') + sprintf(positions_str_cur_ptr, "%d,", WEP_GETPOS(position)); + else + sprintf(positions_str_cur_ptr, "%d%c,", WEP_GETPOS(position), pos_get_weight(position)); /* Moving the pointer forward */ positions_str_cur_ptr += strlen(positions_str_cur_ptr); @@ -1744,3 +1727,25 @@ get_positions_to_text_datum(Datum add_info) pfree(positions_str); return res; } + +/* + * The function extracts the weight and + * returns the corresponding letter. + */ +static char +pos_get_weight(WordEntryPos position) +{ + char res = 'D'; + + switch(WEP_GETWEIGHT(position)) + { + case 3: + return 'A'; + case 2: + return 'B'; + case 1: + return 'C'; + } + + return res; +} From 05b292a7c54622b3404d169c2a59933982d44635 Mon Sep 17 00:00:00 2001 From: Arseny Kositsyn Date: Wed, 14 May 2025 11:26:45 +0300 Subject: [PATCH 5/7] [PGPRO-12159] Fixed test crashes on version 15 of PostgreSQL. The crashes were due to the fact that the construct_array_builtin() function is not defined on versions below 16. Tags: rum --- src/rum_debug_funcs.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/rum_debug_funcs.c b/src/rum_debug_funcs.c index 0b48a74d21..424b18c9b8 100644 --- a/src/rum_debug_funcs.c +++ b/src/rum_debug_funcs.c @@ -316,7 +316,13 @@ rum_page_opaque_info(PG_FUNCTION_ARGS) values[1] = Int64GetDatum(opaq->rightlink); values[2] = Int32GetDatum(opaq->maxoff); values[3] = Int32GetDatum(opaq->freespace); - values[4] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID)); + +#if PG_VERSION_NUM >= 160000 + values[4] = ItemPointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID)); +#else + values[4] = PointerGetDatum(construct_array(flags, nflags, + TEXTOID, -1, false, TYPALIGN_INT)); +#endif /* Build and return the result tuple. */ resultTuple = heap_form_tuple(tupdesc, values, nulls); @@ -495,7 +501,13 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) { high_key_ptr = RumDataPageGetRightBound(inter_call_data->page); values[0] = BoolGetDatum(true); + +#if PG_VERSION_NUM >= 160000 values[1] = ItemPointerGetDatum(&(high_key_ptr->iptr)); +#else + values[1] = PointerGetDatum(&(high_key_ptr->iptr)); +#endif + values[2] = BoolGetDatum(high_key_ptr->addInfoIsNull); /* Returning add info */ @@ -533,7 +545,13 @@ rum_leaf_data_page_items(PG_FUNCTION_ARGS) /* Writing data from rum_item to values */ values[0] = false; + +#if PG_VERSION_NUM >= 160000 values[1] = ItemPointerGetDatum(&(rum_item_ptr->iptr)); +#else + values[1] = PointerGetDatum(&(rum_item_ptr->iptr)); +#endif + values[2] = BoolGetDatum(rum_item_ptr->addInfoIsNull); /* Returning add info */ @@ -733,7 +751,13 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) high_key_ptr = RumDataPageGetRightBound(inter_call_data->page); values[0] = BoolGetDatum(true); nulls[1] = true; + +#if PG_VERSION_NUM >= 160000 values[2] = ItemPointerGetDatum(&(high_key_ptr->iptr)); +#else + values[2] = PointerGetDatum(&(high_key_ptr->iptr)); +#endif + values[3] = BoolGetDatum(high_key_ptr->addInfoIsNull); /* Returning add info */ @@ -771,7 +795,13 @@ rum_internal_data_page_items(PG_FUNCTION_ARGS) /* Writing data from posting_item_ptr to values */ values[0] = BoolGetDatum(false); values[1] = UInt32GetDatum(PostingItemGetBlockNumber(posting_item_ptr)); + +#if PG_VERSION_NUM >= 160000 values[2] = ItemPointerGetDatum(&(posting_item_ptr->item.iptr)); +#else + values[2] = PointerGetDatum(&(posting_item_ptr->item.iptr)); +#endif + values[3] = BoolGetDatum(posting_item_ptr->item.addInfoIsNull); /* Returning add info */ @@ -1049,7 +1079,12 @@ rum_leaf_entry_page_items(PG_FUNCTION_ARGS) values[2] = category_get_datum_text(inter_call_data->cur_tuple_key_category); /* Writing data from rum_item to values */ +#if PG_VERSION_NUM >= 160000 values[3] = ItemPointerGetDatum(&(rum_item_ptr->iptr)); +#else + values[3] = PointerGetDatum(&(rum_item_ptr->iptr)); +#endif + values[4] = BoolGetDatum(rum_item_ptr->addInfoIsNull); /* Returning add info */ From 1fa1b30b705280d6bd9db61942ad5c891e0cdddd Mon Sep 17 00:00:00 2001 From: Arseny Kositsyn Date: Wed, 14 May 2025 15:12:18 +0300 Subject: [PATCH 6/7] [PGPRO-12159] Fixed test crashes on version 14 of PostgreSQL. The crashes were due to the fact that the errdetail_relkind_not_supported() function is not defined on versions below 15. Tags: rum --- src/rum_debug_funcs.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/rum_debug_funcs.c b/src/rum_debug_funcs.c index 424b18c9b8..8516a9b624 100644 --- a/src/rum_debug_funcs.c +++ b/src/rum_debug_funcs.c @@ -1575,12 +1575,41 @@ get_rel_from_name(text *relname) relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); rel = relation_openrv(relrv, AccessShareLock); +#if PG_VERSION_NUM >= 150000 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot get raw page from relation \"%s\"", RelationGetRelationName(rel)), errdetail_relkind_not_supported(rel->rd_rel->relkind))); +#else + /* Check that this relation has storage */ + if (rel->rd_rel->relkind == RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from view \"%s\"", + RelationGetRelationName(rel)))); + if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from composite type \"%s\"", + RelationGetRelationName(rel)))); + if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from foreign table \"%s\"", + RelationGetRelationName(rel)))); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from partitioned table \"%s\"", + RelationGetRelationName(rel)))); + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot get raw page from partitioned index \"%s\"", + RelationGetRelationName(rel)))); +#endif /* * Reject attempts to read non-local temporary relations; we would be From 63ecfd572eaf91c5707e58cf880f7b132d8a3ad5 Mon Sep 17 00:00:00 2001 From: Arseny Kositsyn Date: Wed, 14 May 2025 18:44:32 +0300 Subject: [PATCH 7/7] [PGPRO-12159] Fixed test crashes on version 13 of PostgreSQL. The crashes were due to the fact that the MONEYOID is not defined on versions below 14. Tags: rum --- src/rum_debug_funcs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rum_debug_funcs.c b/src/rum_debug_funcs.c index 8516a9b624..df24aea98a 100644 --- a/src/rum_debug_funcs.c +++ b/src/rum_debug_funcs.c @@ -318,7 +318,7 @@ rum_page_opaque_info(PG_FUNCTION_ARGS) values[3] = Int32GetDatum(opaq->freespace); #if PG_VERSION_NUM >= 160000 - values[4] = ItemPointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID)); + values[4] = PointerGetDatum(construct_array_builtin(flags, nflags, TEXTOID)); #else values[4] = PointerGetDatum(construct_array(flags, nflags, TEXTOID, -1, false, TYPALIGN_INT)); @@ -1478,6 +1478,7 @@ get_datum_text_by_oid(Datum info, Oid info_oid) str_info = OidOutputFunctionCall(F_FLOAT8OUT, info); break; +#if PG_VERSION_NUM >= 140000 /* * TODO: The oid of the function for displaying this * type as text could not be found. @@ -1486,6 +1487,7 @@ get_datum_text_by_oid(Datum info, Oid info_oid) /* str_addInfo = OidOutputFunctionCall(, addInfo); */ /* break; */ return CStringGetTextDatum("MONEYOID is not supported"); +#endif case OIDOID: str_info = OidOutputFunctionCall(F_OIDOUT, info);