diff --git a/src/common/backend/catalog/builtin_funcs.ini b/src/common/backend/catalog/builtin_funcs.ini index bd59d4584..69f0830c8 100755 --- a/src/common/backend/catalog/builtin_funcs.ini +++ b/src/common/backend/catalog/builtin_funcs.ini @@ -7527,6 +7527,53 @@ "percentile_of_value", 1, AddBuiltinFunc(_0(9990), _1("percentile_of_value"), _2(3), _3(false), _4(false), _5(aggregate_dummy), _6(701), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(true), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(3, 701, 701, 701), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("aggregate_dummy"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) ), + AddFuncGroup( + "pgfadvise", 1, + AddBuiltinFunc(_0(2372), _1("pgfadvise"), _2(3), _3(false), _4(true), _5(pgfadvise), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(3,2205,25,23), _21(7,2205,25,23,25,20,20,20), _22(7,'i','i','i','o','o','o','o'), _23(7,"relname","fork","action","relpath","os_page_size","rel_os_pages","os_pages_free"), _24(NULL), _25("pgfadvise"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("Predeclare an access pattern for file data"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfadvise_dontneed", 1, + AddBuiltinFunc(_0(2536), _1("pgfadvise_dontneed"), _2(1), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1,2205), _21(5,2205,25,20,20,20), _22(5,'i','o','o','o','o'), _23(5,"relname","relpath","os_page_size","rel_os_pages","os_pages_free"), _24(NULL), _25("SELECT pg_catalog.pgfadvise($1, 'main', 20)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfadvise_normal", 1, + AddBuiltinFunc(_0(2537), _1("pgfadvise_normal"), _2(1), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1,2205), _21(5,2205,25,20,20,20), _22(5,'i','o','o','o','o'), _23(5,"relname","relpath","os_page_size","rel_os_pages","os_pages_free"), _24(NULL), _25("SELECT pg_catalog.pgfadvise($1, 'main', 30)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfadvise_random", 1, + AddBuiltinFunc(_0(2542), _1("pgfadvise_random"), _2(1), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1,2205), _21(5,2205,25,20,20,20), _22(5,'i','o','o','o','o'), _23(5,"relname","relpath","os_page_size","rel_os_pages","os_pages_free"), _24(NULL), _25("SELECT pg_catalog.pgfadvise($1, 'main', 50)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfadvise_sequential", 1, + AddBuiltinFunc(_0(2538), _1("pgfadvise_sequential"), _2(1), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1,2205), _21(5,2205,25,20,20,20), _22(5,'i','o','o','o','o'), _23(5,"relname","relpath","os_page_size","rel_os_pages","os_pages_free"), _24(NULL), _25("SELECT pg_catalog.pgfadvise($1, 'main', 40)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfadvise_willneed", 1, + AddBuiltinFunc(_0(2535), _1("pgfadvise_willneed"), _2(1), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1,2205), _21(5,2205,25,20,20,20), _22(5,'i','o','o','o','o'), _23(5,"relname","relpath","os_page_size","rel_os_pages","os_pages_free"), _24(NULL), _25("SELECT pg_catalog.pgfadvise($1, 'main', 10)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfadvise_loader", 2, + AddBuiltinFunc(_0(2373), _1("pgfadvise_loader"), _2(6), _3(false), _4(true), _5(pgfadvise_loader), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(6,2205,25,23,16,16,1562), _21(11,2205,25,23,16,16,1562,25,20,20,20,20), _22(11,'i','i','i','i','i','i','o','o','o','o','o'), _23(11,"relname","fork","segment","load","unload","databit","relpath","os_page_size","os_pages_free","pages_loaded","pages_unloaded"), _24(NULL), _25("pgfadvise_loader"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("Restore cache from the snapshot, options to load/unload each block to/from cache"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), + AddBuiltinFunc(_0(2539), _1("pgfadvise_loader"), _2(5), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(5,2205,23,16,16,1562), _21(10,2205,23,16,16,1562,25,20,20,20,20), _22(10,'i','i','i','i','i','o','o','o','o','o'), _23(10,"relname","segment","load","unload","databit","relpath","os_page_size","os_pages_free","pages_loaded","pages_unloaded"), _24(NULL), _25("SELECT pg_catalog.pgfadvise_loader($1, 'main', $2, $3, $4, $5)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfincore", 3, + AddBuiltinFunc(_0(2374), _1("pgfincore"), _2(3), _3(false), _4(true), _5(pgfincore), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(3,2205,25,16), _21(13,2205,25,16,25,23,20,20,20,20,20,1562,20,20), _22(13,'i','i','i','o','o','o','o','o','o','o','o','o','o'), _23(13,"relname","fork","getdatabit","relpath","segment","os_page_size","rel_os_pages","pages_mem","group_mem","os_pages_free","databit","pages_dirty","group_dirty"), _24(NULL), _25("pgfincore"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("Utility to inspect and get a snapshot of the system cache"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), + AddBuiltinFunc(_0(2540), _1("pgfincore"), _2(2), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(2,2205,16), _21(12,2205,16,25,23,20,20,20,20,20,1562,20,20), _22(12,'i','i','o','o','o','o','o','o','o','o','o','o'), _23(12,"relname","getdatabit","relpath","segment","os_page_size","rel_os_pages","pages_mem","group_mem","os_pages_free","databit","pages_dirty","group_dirty"), _24(NULL), _25("SELECT * from pg_catalog.pgfincore($1, 'main', $2)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), + AddBuiltinFunc(_0(2541), _1("pgfincore"), _2(1), _3(false), _4(true), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(1), _11(1), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1,2205), _21(11,2205,25,23,20,20,20,20,20,1562,20,20), _22(11,'i','o','o','o','o','o','o','o','o','o','o'), _23(11,"relname","relpath","segment","os_page_size","rel_os_pages","pages_mem","group_mem","os_pages_free","databit","pages_dirty","group_dirty"), _24(NULL), _25("SELECT * from pg_catalog.pgfincore($1, 'main', false)"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33(NULL), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgfincore_drawer", 1, + AddBuiltinFunc(_0(2543), _1("pgfincore_drawer"), _2(1), _3(false), _4(false), _5(pgfincore_drawer), _6(2275), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(0), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('i'), _19(0), _20(1,1562), _21(2,1562,2275), _22(2,'i','o'), _23(2,"input","drawer"), _24(NULL), _25("pgfincore_drawer"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("A naive drawing function to visualize page cache per object"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgsysconf_pretty", 1, + AddBuiltinFunc(_0(2534), _1("pgsysconf_pretty"), _2(0), _3(false), _4(false), _5(NULL), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(SQLlanguageId), _10(0), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(0), _21(3,25,25,25), _22(3,'o','o','o'), _23(3,"os_page_size","os_pages_free","os_total_pages"), _24(NULL), _25("select pg_catalog.pg_size_pretty(os_page_size) as os_page_size, pg_catalog.pg_size_pretty(os_pages_free * os_page_size) as os_pages_free, pg_catalog.pg_size_pretty(os_total_pages * os_page_size) as os_total_pages from pg_catalog.pgsysconf()"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("Pgsysconf() with human readable output"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), + AddFuncGroup( + "pgsysconf", 1, + AddBuiltinFunc(_0(2371), _1("pgsysconf"), _2(0), _3(false), _4(false), _5(pgsysconf), _6(2249), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(0), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(0), _21(3,20,20,20), _22(3,'o','o','o'), _23(3,"os_page_size","os_pages_free","os_total_pages"), _24(NULL), _25("pgsysconf"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("Get system configuration information at run time, man 3 sysconf for details"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)) + ), AddFuncGroup( "pg_advisory_lock", 3, AddBuiltinFunc(_0(2880), _1("pg_advisory_lock"), _2(1), _3(true), _4(false), _5(pg_advisory_lock_int8), _6(2278), _7(PG_CATALOG_NAMESPACE), _8(BOOTSTRAP_SUPERUSERID), _9(INTERNALlanguageId), _10(1), _11(0), _12(0), _13(0), _14(false), _15(false), _16(false), _17(false), _18('v'), _19(0), _20(1, 20), _21(NULL), _22(NULL), _23(NULL), _24(NULL), _25("pg_advisory_lock_int8"), _26(NULL), _27(NULL), _28(NULL), _29(0), _30(false), _31(NULL), _32(false), _33("obtain exclusive advisory lock"), _34('f'), _35(NULL), _36(0), _37(false), _38(NULL), _39(NULL), _40(0)), diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index 3c47f83c5..05817c537 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -59,7 +59,7 @@ bool open_join_children = true; bool will_shutdown = false; /* hard-wired binary version number */ -const uint32 GRAND_VERSION_NUM = 92612; +const uint32 GRAND_VERSION_NUM = 92613; const uint32 PREDPUSH_SAME_LEVEL_VERSION_NUM = 92522; const uint32 UPSERT_WHERE_VERSION_NUM = 92514; diff --git a/src/common/backend/utils/misc/Makefile b/src/common/backend/utils/misc/Makefile index 597657d6c..d553acf7d 100644 --- a/src/common/backend/utils/misc/Makefile +++ b/src/common/backend/utils/misc/Makefile @@ -21,7 +21,7 @@ ifneq "$(MAKECMDGOALS)" "clean" endif endif endif -OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \ +OBJS = guc.o help_config.o pg_rusage.o pgfincore.o ps_status.o superuser.o tzparser.o \ rbtree.o anls_opt.o sec_rls_utils.o elf_parser.o pg_controldata.o oidrbtree.o # This location might depend on the installation directories. Therefore diff --git a/src/common/backend/utils/misc/pgfincore.cpp b/src/common/backend/utils/misc/pgfincore.cpp new file mode 100644 index 000000000..b3efc2788 --- /dev/null +++ b/src/common/backend/utils/misc/pgfincore.cpp @@ -0,0 +1,1312 @@ +/* ------------------------------------------------------------------------- + * + * pgfincore.cpp + * This file let you see and mainpulate objects in the FS page cache + * + * Portions Copyright (c) 2022, Huawei Technologies Co.,Ltd. + * Portions Copyright (c) 2009-2011, Cédric Villemain + * + * IDENTIFICATION + * src/common/backend/utils/misc/pgfincore.cpp + * + * ------------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include +#include "postgres.h" +#include "access/heapam.h" +#include "catalog/catalog.h" +#include "catalog/namespace.h" +#include "catalog/pg_partition_fn.h" +#include "catalog/pg_partition.h" +#include "utils/builtins.h" +#include "utils/rel.h" +#include "utils/rel_gs.h" +#include "utils/varbit.h" +#include "utils/relcache.h" +#include "funcapi.h" +#include "catalog/pg_type.h" +#include "storage/smgr/fd.h" +#include "securec.h" +#include "nodes/pg_list.h" +#include "storage/lock/lock.h" +#include "utils/relcache.h" + +#define PGSYSCONF_COLS 3 +#define PGFADVISE_COLS 4 +#define PGFADVISE_LOADER_COLS 5 +#define PGFINCORE_COLS 10 + +#define PGF_WILLNEED 10 +#define PGF_DONTNEED 20 +#define PGF_NORMAL 30 +#define PGF_SEQUENTIAL 40 +#define PGF_RANDOM 50 + +#define FINCORE_PRESENT 0x1 +#define FINCORE_DIRTY 0x2 +#define FINCORE_BITS 1 + +/* + * pgfadvise_fctx structure is needed + * to keep track of relation path, segment number, ... + */ +typedef struct { + int advice; /* the posix_fadvise advice */ + TupleDesc tupd; /* the tuple descriptor */ + Relation rel; /* the relation */ + unsigned int segcount; /* the segment current number */ + char *relationpath; /* the relation path */ + bool isPartitionTable; /* partition table ?*/ + bool isSubPartitionTable; /*subPartition table ?*/ + ListCell *partitionCell; + ListCell *subPartitionCell; + List *partitionIdList; + List *subPartitionIdList; + text *forkName; + List *indexoidlist; + ListCell *indexCell; + bool isFirstIndexOid; +} pgfadvise_fctx; + +/* + * pgfadvise structure is needed + * to return values + */ +typedef struct { + size_t pageSize; /* os page size */ + size_t pagesFree; /* free page cache */ + size_t filesize; /* the filesize */ +} pgfadviseStruct; + +/* + * pgfloader structure is needed + * to return values + */ +typedef struct { + size_t pageSize; /* os page size */ + size_t pagesFree; /* free page cache */ + size_t pagesLoaded; /* pages loaded */ + size_t pagesUnloaded; /* pages unloaded */ +} pgfloaderStruct; + +/* + * pgfincore_fctx structure is needed + * to keep track of relation path, segment number, ... + */ +typedef struct { + bool getvector; /* output varbit data ? */ + TupleDesc tupd; /* the tuple descriptor */ + Relation rel; /* the relation */ + unsigned int segcount; /* the segment current number */ + char *relationpath; /* the relation path */ + bool isPartitionTable; /* partition table ?*/ + bool isSubPartitionTable; /*subPartition table ?*/ + ListCell *partitionCell; + ListCell *subPartitionCell; + List *partitionIdList; + List *subPartitionIdList; + text *forkName; + List *indexoidlist; + ListCell *indexCell; + bool isFirstIndexOid; +} pgfincore_fctx; + +/* + * pgfadvise_loader_struct structure is needed + * to keep track of relation path, segment number, ... + */ +typedef struct { + size_t pageSize; /* os page size */ + size_t pagesFree; /* free page cache */ + size_t rel_os_pages; + size_t pages_mem; + size_t group_mem; + size_t pages_dirty; + size_t group_dirty; + VarBit *databit; +} pgfincoreStruct; + +Datum pgsysconf(PG_FUNCTION_ARGS); + +extern Datum pgfadvise(PG_FUNCTION_ARGS); +static bool pgfadvise_file(char *filename, int advice, pgfadviseStruct *pgfdv); + +extern Datum pgfadvise_loader(PG_FUNCTION_ARGS); +static bool pgfadvise_loader_file(char *filename, bool willneed, + bool dontneed, VarBit *databit, pgfloaderStruct *pgfloader); + +extern Datum pgfincore(PG_FUNCTION_ARGS); +static bool pgfincore_file(char *filename, pgfincoreStruct *pgfncr); + +extern Datum pgfincore_drawer(PG_FUNCTION_ARGS); + +#define relpathpg(rel, forkName) \ + relpathbackend((rel)->rd_node, (rel)->rd_backend, (forkname_to_number(text_to_cstring(forkName)))) + +/* + * pgsysconf + * just output the actual system value for + * _SC_PAGESIZE --> Page Size + * _SC_AVPHYS_PAGES --> Free page in memory + * _SC_PHYS_PAGES --> Total memory + * + */ +Datum pgsysconf(PG_FUNCTION_ARGS) +{ + HeapTuple tuple; + TupleDesc tupdesc; + Datum values[PGSYSCONF_COLS]; + bool nulls[PGSYSCONF_COLS]; + + /* initialize nulls array to build the tuple */ + int ret = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); + securec_check(ret, "\0", "\0"); + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, (errmsg("pgsysconf: return type must be a row type"))); + + /* Page size */ + values[0] = Int64GetDatum(sysconf(_SC_PAGESIZE)); + + /* free page in memory */ + values[1] = Int64GetDatum(sysconf(_SC_AVPHYS_PAGES)); + + /* total memory */ + values[2] = Int64GetDatum(sysconf(_SC_PHYS_PAGES)); + + /* Build and return the result tuple. */ + tuple = heap_form_tuple(tupdesc, values, nulls); + PG_RETURN_DATUM( HeapTupleGetDatum(tuple) ); +} + +#if defined(USE_POSIX_FADVISE) + +static bool pgfadvise_file(char *filename, int advice, pgfadviseStruct *pgfdv) +{ + /* + * We use the AllocateFile(2) provided by PostgreSQL. We're going to + * close it ourselves even if PostgreSQL close it anyway at transaction + * end. + */ + FILE *fp; + int fd; + struct stat st; + int adviceFlag; + + /* + * OS Page size and Free pages + */ + pgfdv->pageSize = sysconf(_SC_PAGESIZE); + + /* + * Fopen and fstat file + * fd will be provided to posix_fadvise + * if there is no file, just return 1, it is expected to leave the SRF + */ + fp = AllocateFile(filename, "rb"); + if (fp == NULL) + return false; + + fd = fileno(fp); + if (fstat(fd, &st) == -1) { + FreeFile(fp); + ereport(ERROR, (errmsg("pgfadvise: Can not stat object file : %s", filename))); + return false; + } + + /* + * the file size is used in the SRF to output the number of pages used by + * the segment + */ + pgfdv->filesize = st.st_size; + ereport(DEBUG1, + (errmsg("pgfadvise: working on %s of %lld bytes", filename,(long long int) pgfdv->filesize))); + /* FADVISE_WILLNEED */ + if (advice == PGF_WILLNEED) { + adviceFlag = POSIX_FADV_WILLNEED; + ereport(DEBUG1, (errmsg("pgfadvise: setting advice POSIX_FADV_WILLNEED"))); + } + /* FADVISE_DONTNEED */ + else if (advice == PGF_DONTNEED) { + adviceFlag = POSIX_FADV_DONTNEED; + ereport(DEBUG1, (errmsg("pgfadvise: setting advice POSIX_FADV_DONTNEED"))); + } + /* POSIX_FADV_NORMAL */ + else if (advice == PGF_NORMAL) { + adviceFlag = POSIX_FADV_NORMAL; + ereport(DEBUG1, (errmsg("pgfadvise: setting advice POSIX_FADV_NORMAL"))); + } + /* POSIX_FADV_SEQUENTIAL */ + else if (advice == PGF_SEQUENTIAL) { + adviceFlag = POSIX_FADV_SEQUENTIAL; + ereport(DEBUG1, (errmsg("pgfadvise: setting advice POSIX_FADV_SEQUENTIAL"))); + } + /* POSIX_FADV_RANDOM */ + else if (advice == PGF_RANDOM) { + adviceFlag = POSIX_FADV_RANDOM; + ereport(DEBUG1, (errmsg("pgfadvise: setting advice POSIX_FADV_RANDOM"))); + } else { + ereport(ERROR, (errmsg("pgfadvise: invalid advice: %d", advice))); + return false; + } + + /* + * Call posix_fadvise with the relevant advice on the file descriptor + */ + posix_fadvise(fd, 0, 0, adviceFlag); + + /* close the file */ + FreeFile(fp); + + /* + * OS things : Pages free + */ + pgfdv->pagesFree = sysconf(_SC_AVPHYS_PAGES); + + return true; +} +#else +static bool pgfadvise_file(char *filename, int advice, pgfadviseStruct *pgfdv) +{ + ereport(ERROR, (errmsg("POSIX_FADVISE UNSUPPORTED on your platform"))); + return false; +} +#endif + +/* + * pgfadvise is a function that handle the process to have a sharelock + * on the relation and to walk the segments. + * for each segment it call the posix_fadvise with the required flag + * parameter + */ +Datum pgfadvise(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + pgfadvise_fctx *fctx; + + /* our structure use to return values */ + pgfadviseStruct *pgfdv; + + /* our return value, true for success */ + bool result; + + /* The file we are working on */ + char filename[MAXPGPATH]; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) { + MemoryContext oldcontext; + + Oid relOid = PG_GETARG_OID(0); + text *forkName = PG_GETARG_TEXT_P(1); + int advice = PG_GETARG_INT32(2); + + /* + * Postgresql stuff to return a tuple + */ + TupleDesc tupdesc; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + fctx = (pgfadvise_fctx*)palloc(sizeof(pgfadvise_fctx)); + + fctx->forkName = (text*)palloc(VARSIZE(forkName)); + SET_VARSIZE(fctx->forkName, VARSIZE(forkName)); + errno_t ret = memcpy_s((void*)VARDATA(fctx->forkName), VARSIZE(forkName) - VARHDRSZ, (void*)VARDATA(forkName), VARSIZE(forkName) - VARHDRSZ); + securec_check(ret, "\0", "\0"); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, (errmsg("pgfadvise: return type must be a row type"))); + + /* provide the tuple descriptor to the fonction structure */ + fctx->tupd = tupdesc; + + /* open the current relation, accessShareLock */ + // TODO use try_relation_open instead ? + fctx->rel = relation_open(relOid, AccessShareLock); + + if (RelationIsColStore(fctx->rel)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support feature"), + errdetail("column-store relation doesn't support pgfadvise yet"))); + } + + if (RelationIsSegmentTable(fctx->rel)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support feature"), + errdetail("segment-page tables doesn't support pgfadvise yet"))); + } + + if (RelationIsSubPartitioned(fctx->rel)) { + fctx->isSubPartitionTable = true; + fctx->isPartitionTable = false; + } else if (RELATION_IS_PARTITIONED(fctx->rel)) { + fctx->isPartitionTable = true; + fctx->isSubPartitionTable = false; + } else { + fctx->isPartitionTable = false; + fctx->isSubPartitionTable = false; + } + fctx->partitionCell = NULL; + fctx->subPartitionCell = NULL; + fctx->indexCell = NULL; + fctx->partitionIdList = NULL; + fctx->subPartitionIdList = NULL; + fctx->isFirstIndexOid = true;; + if (!RelationIsIndex(fctx->rel)) { + fctx->indexoidlist = RelationGetIndexList(fctx->rel); + } else { + fctx->indexoidlist = NULL; + } + + + /* Here we keep track of current action in all calls */ + fctx->advice = advice; + + if (!(fctx->isPartitionTable || fctx->isSubPartitionTable)) { + /* we get the common part of the filename of each segment of a relation */ + fctx->relationpath = relpathpg(fctx->rel, forkName); + + /* segcount is used to get the next segment of the current relation */ + fctx->segcount = 0; + } else if (fctx->isSubPartitionTable) { + fctx->subPartitionIdList = RelationGetSubPartitionList(fctx->rel, AccessShareLock); + fctx->subPartitionCell = list_head(fctx->subPartitionIdList); + Partition subPartition = (Partition)lfirst(fctx->subPartitionCell); + Relation subPartionRel = SubPartitionGetRelation(fctx->rel, subPartition, AccessShareLock); + fctx->relationpath = relpathpg(subPartionRel, forkName); + fctx->segcount = 0; + releaseDummyRelation(&subPartionRel); + } else if (fctx->isPartitionTable) { + fctx->partitionIdList = relationGetPartitionList(fctx->rel, AccessShareLock); + fctx->partitionCell = list_head(fctx->partitionIdList); + Partition partition = (Partition)lfirst(fctx->partitionCell); + Relation partitionRel = partitionGetRelation(fctx->rel, partition); + fctx->relationpath = relpathpg(partitionRel, forkName); + fctx->segcount = 0; + releaseDummyRelation(&partitionRel); + } + + + /* And finally we keep track of our initialization */ + ereport(DEBUG1, (errmsg("pgfadvise: init done for %s, in fork %s", + fctx->relationpath, text_to_cstring(forkName)))); + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + /* After the first call, we recover our context */ + funcctx = SRF_PERCALL_SETUP(); + fctx = (pgfadvise_fctx *) funcctx->user_fctx; + + /* + * If we are still looking the first segment + * relationpath should not be suffixed + */ + if (fctx->segcount == 0){ + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s.%u", fctx->relationpath, fctx->segcount); + securec_check_ss(rc, "\0", "\0"); + } + + FILE *fp = AllocateFile(filename, "rb"); + if (fp == NULL) { + if (fctx->isPartitionTable || fctx->isSubPartitionTable) { + if (fctx->isSubPartitionTable && lnext(fctx->subPartitionCell)) { + fctx->subPartitionCell = lnext(fctx->subPartitionCell); + Partition subPartition = (Partition)lfirst(fctx->subPartitionCell); + Relation subPartionRel = SubPartitionGetRelation(fctx->rel,subPartition,AccessShareLock); + fctx->relationpath = relpathpg(subPartionRel, fctx->forkName); + fctx->segcount = 0; + releaseDummyRelation(&subPartionRel); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else if (fctx->isPartitionTable && lnext(fctx->partitionCell)) { + fctx->partitionCell = lnext(fctx->partitionCell); + Partition partition = (Partition)lfirst(fctx->partitionCell); + Relation partitionRel = partitionGetRelation(fctx->rel,partition); + fctx->relationpath = relpathpg(partitionRel, fctx->forkName); + fctx->segcount = 0; + releaseDummyRelation(&partitionRel); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + if (fctx->indexoidlist != NULL) { + if (fctx->isFirstIndexOid) { + fctx->indexCell = list_head(fctx->indexoidlist); + fctx->isFirstIndexOid=false; + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + if (lnext(fctx->indexCell)) { + fctx->indexCell = lnext(fctx->indexCell); + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } + } + } + } + } else { + //process index + if (fctx->indexoidlist != NULL) { + if (fctx->isFirstIndexOid) { + fctx->indexCell = list_head(fctx->indexoidlist); + fctx->isFirstIndexOid=false; + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + if (lnext(fctx->indexCell)) { + fctx->indexCell = lnext(fctx->indexCell); + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } + } + } + } + } + + ereport(DEBUG1, (errmsg("pgfadvise: about to work with %s, current advice : %d", + filename, fctx->advice))); + /* + * Call posix_fadvise with the advice, returning the structure + */ + pgfdv = (pgfadviseStruct *) palloc(sizeof(pgfadviseStruct)); + result = pgfadvise_file(filename, fctx->advice, pgfdv); + + /* + * When we have work with all segments of the current relation + * We exit from the SRF + * Else we build and return the tuple for this segment + */ + if (!result) { + ereport(DEBUG1, (errmsg("pgfadvise: closing %s", fctx->relationpath))); + if (fctx->isPartitionTable) { + releasePartitionList(fctx->rel, &(fctx->partitionIdList), AccessShareLock); + } else if (fctx->isSubPartitionTable) { + releasePartitionList(fctx->rel, &(fctx->subPartitionIdList), AccessShareLock); + } + relation_close(fctx->rel, AccessShareLock); + list_free(fctx->indexoidlist); + pfree(fctx); + SRF_RETURN_DONE(funcctx); + } else { + /* + * Postgresql stuff to return a tuple + */ + HeapTuple tuple; + Datum values[PGFADVISE_COLS]; + bool nulls[PGFADVISE_COLS]; + + /* initialize nulls array to build the tuple */ + int ret = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); + securec_check(ret, "\0", "\0"); + /* prepare the number of the next segment */ + fctx->segcount++; + + /* Filename */ + values[0] = CStringGetTextDatum( filename ); + /* os page size */ + values[1] = Int64GetDatum( (int64) pgfdv->pageSize ); + /* number of pages used by segment */ + values[2] = Int64GetDatum( (int64) ((pgfdv->filesize+pgfdv->pageSize-1)/pgfdv->pageSize) ); + /* free page cache */ + values[3] = Int64GetDatum( (int64) pgfdv->pagesFree ); + /* Build the result tuple. */ + tuple = heap_form_tuple(fctx->tupd, values, nulls); + + /* Ok, return results, and go for next call */ + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } +} + +#if defined(USE_POSIX_FADVISE) + +static bool pgfadvise_loader_file(char *filename, + bool willneed, bool dontneed, VarBit *databit, + pgfloaderStruct *pgfloader) +{ + bits8 *sp; + int bitlen; + bits8 x; + int i, k; + + /* + * We use the AllocateFile(2) provided by PostgreSQL. We're going to + * close it ourselves even if PostgreSQL close it anyway at transaction + * end. + */ + FILE *fp; + int fd; + struct stat st; + + /* + * OS things : Page size + */ + pgfloader->pageSize = sysconf(_SC_PAGESIZE); + + /* + * we count the action we perform + * both are theorical : we don't know if the page was or not in memory + * when we call posix_fadvise + */ + pgfloader->pagesLoaded = 0; + pgfloader->pagesUnloaded = 0; + + /* + * Fopen and fstat file + * fd will be provided to posix_fadvise + * if there is no file, just return 1, it is expected to leave the SRF + */ + fp = AllocateFile(filename, "rb"); + if (fp == NULL) + return false; + + fd = fileno(fp); + if (fstat(fd, &st) == -1) { + FreeFile(fp); + ereport(ERROR, (errmsg("pgfadvise_loader: Can not stat object file: %s", filename))); + return false; + } + + ereport(DEBUG1, (errmsg("pgfadvise_loader: working on %s", filename))); + + bitlen = VARBITLEN(databit); + sp = VARBITS(databit); + for (i = 0; i < bitlen - BITS_PER_BYTE; i += BITS_PER_BYTE, sp++) { + x = *sp; + /* Is this bit set ? */ + for (k = 0; k < BITS_PER_BYTE; k++) { + if (IS_HIGHBIT_SET(x)) { + if (willneed) { + (void) posix_fadvise(fd, + ((i+k) * pgfloader->pageSize), + pgfloader->pageSize, + POSIX_FADV_WILLNEED); + pgfloader->pagesLoaded++; + } + } else if (dontneed) { + (void) posix_fadvise(fd, + ((i+k) * pgfloader->pageSize), + pgfloader->pageSize, + POSIX_FADV_DONTNEED); + pgfloader->pagesUnloaded++; + } + + x <<= 1; + } + } + /* + * XXX this copy/paste of code to finnish to walk the bits is not pretty + */ + if (i < bitlen) + { + /* print the last partial byte */ + x = *sp; + for (k = i; k < bitlen; k++) { + if (IS_HIGHBIT_SET(x)) { + if (willneed) { + (void) posix_fadvise(fd, + (k * pgfloader->pageSize), + pgfloader->pageSize, + POSIX_FADV_WILLNEED); + pgfloader->pagesLoaded++; + } + } else if (dontneed) { + (void) posix_fadvise(fd, + (k * pgfloader->pageSize), + pgfloader->pageSize, + POSIX_FADV_DONTNEED); + pgfloader->pagesUnloaded++; + } + x <<= 1; + } + } + FreeFile(fp); + + /* + * OS things : Pages free + */ + pgfloader->pagesFree = sysconf(_SC_AVPHYS_PAGES); + + return true; +} +#else +static bool pgfadvise_loader_file(char *filename, + bool willneed, bool dontneed, VarBit *databit, + pgfloaderStruct *pgfloader) +{ + ereport(ERROR, (errmsg("POSIX_FADVISE UNSUPPORTED on your platform"))); + return false; +} +#endif + +/* + * + * pgfadv_loader to handle work with varbit map of buffer cache. + * it is actually used for loading/unloading block to/from buffer cache + * + */ +Datum pgfadvise_loader(PG_FUNCTION_ARGS) +{ + Oid relOid = PG_GETARG_OID(0); + text *forkName = PG_GETARG_TEXT_P(1); + int segmentNumber = PG_GETARG_INT32(2); + /* if this variable is set to false, no page will be set to willneed */ + bool willneed = PG_GETARG_BOOL(3); + /* if this variable is set to false, no page will be set to dontneed */ + bool dontneed = PG_GETARG_BOOL(4); + /* + * if the variable willneed and dontneed is set to true, the pages + * corresponding to 1 will be set WILLNEED, and the pages corresponding + * to 0 will be set DONTNEED. + */ + VarBit *databit; + + /* our structure use to return values */ + pgfloaderStruct *pgfloader; + + Relation rel; + char *relationpath; + char filename[MAXPGPATH]; + + /* our return value, true for success */ + bool result; + + /* + * Postgresql stuff to return a tuple + */ + HeapTuple tuple; + TupleDesc tupdesc; + Datum values[PGFADVISE_LOADER_COLS]; + bool nulls[PGFADVISE_LOADER_COLS]; + + if (PG_ARGISNULL(5)) + ereport(ERROR, (errmsg("pgfadvise_loader: databit argument shouldn't be NULL"))); + + databit = PG_GETARG_VARBIT_P(5); + + /* initialize nulls array to build the tuple */ + int ret = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); + securec_check(ret, "\0", "\0"); + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, (errmsg("return type must be a row type"))); + + /* open the current relation in accessShareLock */ + rel = relation_open(relOid, AccessShareLock); + + if (RelationIsColStore(rel)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support feature"), + errdetail("column-store relation doesn't support pgfadvise_loader yet"))); + } + + if (RelationIsSegmentTable(rel)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support feature"), + errdetail("segment-page tables doesn't support pgfadvise_loader yet"))); + } + + /* we get the common part of the filename of each segment of a relation */ + relationpath = relpathpg(rel, forkName); + + /* + * If we are looking the first segment, + * relationpath should not be suffixed + */ + if (segmentNumber == 0){ + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s.%u", relationpath, (int) segmentNumber); + securec_check_ss(rc, "\0", "\0"); + } + + /* + * We don't need the relation anymore + * the only purpose was to get a consistent filename + * (if file disappear, an error is logged) + */ + relation_close(rel, AccessShareLock); + + /* + * Call pgfadvise_loader with the varbit + */ + pgfloader = (pgfloaderStruct *) palloc(sizeof(pgfloaderStruct)); + result = pgfadvise_loader_file(filename, + willneed, dontneed, databit, + pgfloader); + if (!result) + ereport(ERROR, (errmsg("Can't read file %s, fork(%s)", + filename, text_to_cstring(forkName)))); + /* Filename */ + values[0] = CStringGetTextDatum( filename ); + /* os page size */ + values[1] = Int64GetDatum( pgfloader->pageSize ); + /* free page cache */ + values[2] = Int64GetDatum( pgfloader->pagesFree ); + /* pages loaded */ + values[3] = Int64GetDatum( pgfloader->pagesLoaded ); + /* pages unloaded */ + values[4] = Int64GetDatum( pgfloader->pagesUnloaded ); + + /* Build and return the result tuple. */ + tuple = heap_form_tuple(tupdesc, values, nulls); + PG_RETURN_DATUM( HeapTupleGetDatum(tuple) ); +} + +/* + * pgfincore_file handle the mmaping, mincore process (and access file, etc.) + */ +static bool pgfincore_file(char *filename, pgfincoreStruct *pgfncr) +{ + int flag=1; + int flag_dirty=1; + + int len, bitlen; + bits8 *r; + bits8 x = 0; + register size_t pageIndex; + + /* + * We use the AllocateFile(2) provided by PostgreSQL. We're going to + * close it ourselves even if PostgreSQL close it anyway at transaction + * end. + */ + FILE *fp; + int fd; + struct stat st; + + void *pa = (char *) 0; + + unsigned char *vec = (unsigned char *) 0; + + /* + * OS Page size + */ + pgfncr->pageSize = sysconf(_SC_PAGESIZE); + + /* + * Initialize counters + */ + pgfncr->pages_mem = 0; + pgfncr->group_mem = 0; + pgfncr->pages_dirty = 0; + pgfncr->group_dirty = 0; + pgfncr->rel_os_pages = 0; + + /* + * Fopen and fstat file + * fd will be provided to posix_fadvise + * if there is no file, just return 1, it is expected to leave the SRF + */ + fp = AllocateFile(filename, "rb"); + if (fp == NULL) + return false; + + fd = fileno(fp); + + if (fstat(fd, &st) == -1) { + FreeFile(fp); + ereport(ERROR, (errmsg("Can not stat object file : %s", filename))); + return false; + } + + /* + * if file ok + * then process + */ + if (st.st_size != 0) { + /* number of pages in the current file */ + pgfncr->rel_os_pages = (st.st_size+pgfncr->pageSize-1)/pgfncr->pageSize; + + pa = mmap(NULL, st.st_size, PROT_NONE, MAP_SHARED, fd, 0); + if (pa == MAP_FAILED) { + int save_errno = errno; + FreeFile(fp); + ereport(ERROR, (errmsg("Can not mmap object file : %s, errno = %i,%s\nThis error can happen if there is not enought space in memory to do the projection.", + filename, save_errno, strerror(save_errno)))); + return false; + } + + /* Prepare our vector containing all blocks information */ + vec = (unsigned char * ) calloc(1, (st.st_size+pgfncr->pageSize-1)/pgfncr->pageSize); + if ((void *)0 == vec) { + munmap(pa, st.st_size); + FreeFile(fp); + ereport(ERROR, (errmsg("Can not calloc object file : %s", filename))); + return false; + } + + /* Affect vec with mincore */ + if (mincore(pa, st.st_size, vec) != 0) { + int save_errno = errno; + munmap(pa, st.st_size); + ereport(ERROR, (errmsg("mincore(%p, %lld, %p): %s\n", + pa, (long long int)st.st_size, vec, strerror(save_errno)))); + free(vec); + FreeFile(fp); + return false; + } + + /* + * prepare the bit string + */ + bitlen = FINCORE_BITS * ((st.st_size+pgfncr->pageSize-1)/pgfncr->pageSize); + len = VARBITTOTALLEN(bitlen); + /* + * set to 0 so that *r is always initialised and string is zero-padded + * XXX: do we need to free that ? + */ + pgfncr->databit = (VarBit *) palloc0(len); + SET_VARSIZE(pgfncr->databit, len); + VARBITLEN(pgfncr->databit) = bitlen; + + r = VARBITS(pgfncr->databit); + x = HIGHBIT; + + /* handle the results */ + for (pageIndex = 0; pageIndex < pgfncr->rel_os_pages; pageIndex++) { + // block in memory + if (vec[pageIndex] & FINCORE_PRESENT) { + pgfncr->pages_mem++; + *r |= x; + if (FINCORE_BITS > 1) { + if (vec[pageIndex] & FINCORE_DIRTY) { + pgfncr->pages_dirty++; + *r |= (x >> 1); + /* we flag to detect contigous blocks in the same state */ + if (flag_dirty) + pgfncr->group_dirty++; + flag_dirty = 0; + } else { + flag_dirty = 1; + } + } + ereport(DEBUG5, (errmsg("in memory blocks : %lld / %lld", + (long long int) pageIndex, (long long int) pgfncr->rel_os_pages))); + /* we flag to detect contigous blocks in the same state */ + if (flag) + pgfncr->group_mem++; + flag = 0; + } else { + flag=1; + } + + x >>= FINCORE_BITS; + if (x == 0) { + x = HIGHBIT; + r++; + } + } + } + ereport(DEBUG1, (errmsg("pgfincore %s: %lld of %lld block in linux cache, %lld groups", + filename, (long long int) pgfncr->pages_mem, (long long int) pgfncr->rel_os_pages, (long long int) pgfncr->group_mem))); + + /* + * free and close + */ + free(vec); + munmap(pa, st.st_size); + FreeFile(fp); + + /* + * OS things : Pages free + */ + pgfncr->pagesFree = sysconf(_SC_AVPHYS_PAGES); + + return true; +} + +/* + * pgfincore is a function that handle the process to have a sharelock + * on the relation and to walk the segments. + * for each segment it call the appropriate function depending on 'action' + * parameter + */ +Datum pgfincore(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + pgfincore_fctx *fctx; + + /* our structure use to return values */ + pgfincoreStruct *pgfncr; + + /* our return value, true for success */ + bool result; + + /* The file we are working on */ + char filename[MAXPGPATH]; + + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) { + MemoryContext oldcontext; + + Oid relOid = PG_GETARG_OID(0); + text *forkName = PG_GETARG_TEXT_P(1); + bool getvector = PG_GETARG_BOOL(2); + + /* + * Postgresql stuff to return a tuple + */ + TupleDesc tupdesc; + + /* create a function context for cross-call persistence */ + funcctx = SRF_FIRSTCALL_INIT(); + + /* + * switch to memory context appropriate for multiple function calls + */ + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + /* allocate memory for user context */ + fctx = (pgfincore_fctx *) palloc(sizeof(pgfincore_fctx)); + + fctx->forkName = (text*)palloc(VARSIZE(forkName)); + SET_VARSIZE(fctx->forkName, VARSIZE(forkName)); + errno_t ret = memcpy_s((void*)VARDATA(fctx->forkName), VARSIZE(forkName) - VARHDRSZ, (void*)VARDATA(forkName), VARSIZE(forkName) - VARHDRSZ); + securec_check(ret, "\0", "\0"); + + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + ereport(ERROR, (errmsg("pgfadvise: return type must be a row type"))); + + /* provide the tuple descriptor to the fonction structure */ + fctx->tupd = tupdesc; + + /* are we going to grab and output the varbit data (can be large) */ + fctx->getvector = getvector; + + /* open the current relation, accessShareLock */ + // TODO use try_relation_open instead ? + fctx->rel = relation_open(relOid, AccessShareLock); + + if (RelationIsColStore(fctx->rel)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support feature"), + errdetail("column-store relation doesn't support pgfincore yet"))); + } + + if (RelationIsSegmentTable(fctx->rel)) { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("Un-support feature"), + errdetail("segment-page tables doesn't support pgfincore yet"))); + } + + if (RelationIsSubPartitioned(fctx->rel)) { + fctx->isSubPartitionTable = true; + fctx->isPartitionTable = false; + } else if (RELATION_IS_PARTITIONED(fctx->rel)) { + fctx->isPartitionTable = true; + fctx->isSubPartitionTable = false; + } else { + fctx->isPartitionTable = false; + fctx->isSubPartitionTable = false; + } + fctx->partitionCell = NULL; + fctx->subPartitionCell = NULL; + fctx->partitionIdList = NULL; + fctx->subPartitionIdList = NULL; + fctx->indexCell = NULL; + + fctx->isFirstIndexOid = true;; + if (!RelationIsIndex(fctx->rel)) { + fctx->indexoidlist = RelationGetIndexList(fctx->rel); + } else { + fctx->indexoidlist = NULL; + } + + if (!(fctx->isPartitionTable || fctx->isSubPartitionTable)) { + /* we get the common part of the filename of each segment of a relation */ + fctx->relationpath = relpathpg(fctx->rel, forkName); + + /* segcount is used to get the next segment of the current relation */ + fctx->segcount = 0; + } else if (fctx->isSubPartitionTable) { + fctx->subPartitionIdList = RelationGetSubPartitionList(fctx->rel, AccessShareLock); + fctx->subPartitionCell = list_head(fctx->subPartitionIdList); + Partition subPartition = (Partition)lfirst(fctx->subPartitionCell); + Relation subPartionRel = SubPartitionGetRelation(fctx->rel, subPartition, AccessShareLock); + fctx->relationpath = relpathpg(subPartionRel, forkName); + fctx->segcount = 0; + releaseDummyRelation(&subPartionRel); + } else if (fctx->isPartitionTable) { + fctx->partitionIdList = relationGetPartitionList(fctx->rel, AccessShareLock); + fctx->partitionCell = list_head(fctx->partitionIdList); + Partition partition = (Partition)lfirst(fctx->partitionCell); + Relation partitionRel = partitionGetRelation(fctx->rel, partition); + fctx->relationpath = relpathpg(partitionRel, forkName); + fctx->segcount = 0; + releaseDummyRelation(&partitionRel); + } + + /* And finally we keep track of our initialization */ + ereport(DEBUG1, (errmsg("pgfincore: init done for %s, in fork %s", + fctx->relationpath, text_to_cstring(forkName)))); + funcctx->user_fctx = fctx; + MemoryContextSwitchTo(oldcontext); + } + + /* After the first call, we recover our context */ + funcctx = SRF_PERCALL_SETUP(); + fctx = (pgfincore_fctx*) funcctx->user_fctx; + + /* + * If we are still looking the first segment + * relationpath should not be suffixed + */ + if (fctx->segcount == 0) { + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s.%u", fctx->relationpath, fctx->segcount); + securec_check_ss(rc, "\0", "\0"); + } + + FILE *fp = AllocateFile(filename, "rb"); + if (fp == NULL) { + if (fctx->isPartitionTable || fctx->isSubPartitionTable) { + if (fctx->isSubPartitionTable && lnext(fctx->subPartitionCell)) { + fctx->subPartitionCell = lnext(fctx->subPartitionCell); + Partition subPartition = (Partition)lfirst(fctx->subPartitionCell); + Relation subPartionRel = SubPartitionGetRelation(fctx->rel,subPartition,AccessShareLock); + fctx->relationpath = relpathpg(subPartionRel, fctx->forkName); + fctx->segcount = 0; + releaseDummyRelation(&subPartionRel); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else if (fctx->isPartitionTable && lnext(fctx->partitionCell)) { + fctx->partitionCell = lnext(fctx->partitionCell); + Partition partition = (Partition)lfirst(fctx->partitionCell); + Relation partitionRel = partitionGetRelation(fctx->rel,partition); + fctx->relationpath = relpathpg(partitionRel, fctx->forkName); + fctx->segcount = 0; + releaseDummyRelation(&partitionRel); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + if (fctx->indexoidlist != NULL) { + if (fctx->isFirstIndexOid) { + fctx->indexCell = list_head(fctx->indexoidlist); + fctx->isFirstIndexOid=false; + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + if (lnext(fctx->indexCell)) { + fctx->indexCell = lnext(fctx->indexCell); + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } + } + } + } + } else { + //process index + if (fctx->indexoidlist != NULL) { + if (fctx->isFirstIndexOid) { + fctx->indexCell = list_head(fctx->indexoidlist); + fctx->isFirstIndexOid=false; + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } else { + if (lnext(fctx->indexCell)) { + fctx->indexCell = lnext(fctx->indexCell); + Oid indexId = lfirst_oid(fctx->indexCell); + Relation currentIndex = index_open(indexId, AccessShareLock); + fctx->relationpath = relpathpg(currentIndex, fctx->forkName); + fctx->segcount = 0; + index_close(currentIndex, NoLock); + errno_t rc = snprintf_s(filename, MAXPGPATH, MAXPGPATH-1, "%s", fctx->relationpath); + securec_check_ss(rc, "\0", "\0"); + } + } + } + } + } + + ereport(DEBUG1, (errmsg("pgfincore: about to work with %s", filename))); + + /* + * Call pgfincore with the advice, returning the structure + */ + pgfncr = (pgfincoreStruct *) palloc(sizeof(pgfincoreStruct)); + result = pgfincore_file(filename, pgfncr); + + /* + * When we have work with all segment of the current relation, test success + * We exit from the SRF + */ + if (!result) { + ereport(DEBUG1, (errmsg("pgfincore: closing %s", fctx->relationpath))); + + if (fctx->isPartitionTable) { + releasePartitionList(fctx->rel, &(fctx->partitionIdList), AccessShareLock); + } else if (fctx->isSubPartitionTable) { + releasePartitionList(fctx->rel, &(fctx->subPartitionIdList), AccessShareLock); + } + relation_close(fctx->rel, AccessShareLock); + list_free(fctx->indexoidlist); + pfree(fctx); + SRF_RETURN_DONE(funcctx); + } else { + /* + * Postgresql stuff to return a tuple + */ + HeapTuple tuple; + Datum values[PGFINCORE_COLS]; + bool nulls[PGFINCORE_COLS]; + + /* initialize nulls array to build the tuple */ + int ret = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls)); + securec_check(ret, "\0", "\0"); + + /* Filename */ + values[0] = CStringGetTextDatum(filename); + /* Segment Number */ + values[1] = Int32GetDatum(fctx->segcount); + /* os page size */ + values[2] = Int64GetDatum(pgfncr->pageSize); + /* number of pages used by segment */ + values[3] = Int64GetDatum(pgfncr->rel_os_pages); + /* number of pages in OS cache */ + values[4] = Int64GetDatum(pgfncr->pages_mem); + /* number of group of contigous page in os cache */ + values[5] = Int64GetDatum(pgfncr->group_mem); + /* free page cache */ + values[6] = Int64GetDatum(pgfncr->pagesFree); + /* the map of the file with bit set for in os cache page */ + if (fctx->getvector && pgfncr->rel_os_pages) { + values[7] = VarBitPGetDatum(pgfncr->databit); + } else { + nulls[7] = true; + values[7] = (Datum) NULL; + } + /* number of pages dirty in OS cache */ + values[8] = Int64GetDatum(pgfncr->pages_dirty); + /* number of group of contigous dirty pages in os cache */ + values[9] = Int64GetDatum(pgfncr->group_dirty); + /* Build the result tuple. */ + tuple = heap_form_tuple(fctx->tupd, values, nulls); + + /* prepare the number of the next segment */ + fctx->segcount++; + + /* Ok, return results, and go for next call */ + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple)); + } +} + +/* + * pgfincore_drawer A very naive renderer. (for testing) + */ +Datum pgfincore_drawer(PG_FUNCTION_ARGS) +{ + char *result,*r; + int len,i,k; + VarBit *databit; + bits8 *sp; + bits8 x; + + if (PG_ARGISNULL(0)) + ereport(ERROR, (errmsg("pgfincore_drawer: databit argument shouldn't be NULL"))); + + databit = PG_GETARG_VARBIT_P(0); + + len = VARBITLEN(databit); + result = (char *) palloc((len/FINCORE_BITS) + 1); + sp = VARBITS(databit); + r = result; + + for (i = 0; i <= len - BITS_PER_BYTE; i += BITS_PER_BYTE, sp++) { + x = *sp; + /* Is this bit set ? */ + for (k = 0; k < (BITS_PER_BYTE/FINCORE_BITS); k++) { + char out = ' '; + if (IS_HIGHBIT_SET(x)) + out = '.' ; + x <<= 1; + if (FINCORE_BITS > 1) { + if (IS_HIGHBIT_SET(x)) + out = '*'; + x <<= 1; + } + *r++ = out; + } + } + if (i < len) { + /* print the last partial byte */ + x = *sp; + for (k = i; k < (len/FINCORE_BITS); k++) { + char out = ' '; + if (IS_HIGHBIT_SET(x)) + out = '.' ; + x <<= 1; + if (FINCORE_BITS > 1) { + if (IS_HIGHBIT_SET(x)) + out = '*'; + x <<= 1; + } + *r++ = out; + } + } + + *r = '\0'; + PG_RETURN_CSTRING(result); +} diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_613.sql b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_613.sql new file mode 100644 index 000000000..88521bb7a --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_maindb/rollback-post_catalog_maindb_92_613.sql @@ -0,0 +1,16 @@ +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf_pretty() CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf() CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_willneed(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_dontneed(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_normal(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_sequential(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_random(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise(regclass, text, int) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, text, int, bool, bool, varbit) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, int, bool, bool, varbit) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, bool) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, text, bool) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore_drawer(varbit) CASCADE; + + diff --git a/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_maindb_92_613.sql b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_maindb_92_613.sql new file mode 100644 index 000000000..88521bb7a --- /dev/null +++ b/src/include/catalog/upgrade_sql/rollback_catalog_otherdb/rollback-post_catalog_maindb_92_613.sql @@ -0,0 +1,16 @@ +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf_pretty() CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf() CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_willneed(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_dontneed(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_normal(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_sequential(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_random(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise(regclass, text, int) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, text, int, bool, bool, varbit) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, int, bool, bool, varbit) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, bool) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, text, bool) CASCADE; +DROP FUNCTION IF EXISTS pg_catalog.pgfincore_drawer(varbit) CASCADE; + + diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_613.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_613.sql new file mode 100644 index 000000000..b4ad240f1 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_maindb/upgrade-post_catalog_maindb_92_613.sql @@ -0,0 +1,206 @@ +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf() CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2371; +CREATE FUNCTION +pg_catalog.pgsysconf(OUT os_page_size bigint, + OUT os_pages_free bigint, + OUT os_total_pages bigint) +RETURNS record LANGUAGE INTERNAL VOLATILE +AS 'pgfincore'; +COMMENT ON FUNCTION pg_catalog.pgsysconf() +IS 'Get system configuration information at run time, man 3 sysconf for details'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf_pretty() CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2534; +CREATE FUNCTION +pg_catalog.pgsysconf_pretty(OUT os_page_size text, + OUT os_pages_free text, + OUT os_total_pages text) +RETURNS record LANGUAGE SQL VOLATILE +AS ' +select pg_catalog.pg_size_pretty(os_page_size) as os_page_size, + pg_catalog.pg_size_pretty(os_pages_free * os_page_size) as os_pages_free, + pg_catalog.pg_size_pretty(os_total_pages * os_page_size) as os_total_pages +from pgsysconf()' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise(regclass, text, int) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2372; +CREATE OR REPLACE FUNCTION +pg_catalog.pgfadvise(IN regclass, IN text, IN int, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE INTERNAL VOLATILE +AS 'pgfadvise' +; +COMMENT ON FUNCTION pg_catalog.pgfadvise(regclass, text, int) +IS 'Predeclare an access pattern for file data'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_willneed(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2535; +CREATE FUNCTION +pg_catalog.pgfadvise_willneed(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 10)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_dontneed(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2536; +CREATE FUNCTION +pg_catalog.pgfadvise_dontneed(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 20)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_normal(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2537; +CREATE FUNCTION +pg_catalog.pgfadvise_normal(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 30)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_sequential(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2538; +CREATE FUNCTION +pg_catalog.pgfadvise_sequential(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 40)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_random(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2542; +CREATE FUNCTION +pg_catalog.pgfadvise_random(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 50)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, text, int, bool, bool, varbit) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2373; +CREATE FUNCTION +pg_catalog.pgfadvise_loader(IN regclass, IN text, IN int, IN bool, IN bool, IN varbit, + OUT relpath text, + OUT os_page_size bigint, + OUT os_pages_free bigint, + OUT pages_loaded bigint, + OUT pages_unloaded bigint) +RETURNS setof record LANGUAGE INTERNAL VOLATILE +AS 'pgfadvise_loader' +; +COMMENT ON FUNCTION pg_catalog.pgfadvise_loader(regclass, text, int, bool, bool, varbit) +IS 'Restore cache from the snapshot, options to load/unload each block to/from cache'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, int, bool, bool, varbit) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2539; +CREATE FUNCTION +pg_catalog.pgfadvise_loader(IN regclass, IN int, IN bool, IN bool, IN varbit, + OUT relpath text, + OUT os_page_size bigint, + OUT os_pages_free bigint, + OUT pages_loaded bigint, + OUT pages_unloaded bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise_loader($1, ''main'', $2, $3, $4, $5)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, text, bool) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2374; +CREATE FUNCTION +pg_catalog.pgfincore(IN regclass, IN text, IN bool, + OUT relpath text, + OUT segment int, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT pages_mem bigint, + OUT group_mem bigint, + OUT os_pages_free bigint, + OUT databit varbit, + OUT pages_dirty bigint, + OUT group_dirty bigint) +RETURNS setof record LANGUAGE INTERNAL VOLATILE +AS 'pgfincore' +; +COMMENT ON FUNCTION pg_catalog.pgfincore(regclass, text, bool) +IS 'Utility to inspect and get a snapshot of the system cache'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2541; +CREATE FUNCTION +pg_catalog.pgfincore(IN regclass, + OUT relpath text, + OUT segment int, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT pages_mem bigint, + OUT group_mem bigint, + OUT os_pages_free bigint, + OUT databit varbit, + OUT pages_dirty bigint, + OUT group_dirty bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT * from pg_catalog.pgfincore($1, ''main'', false)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, bool) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2540; +CREATE FUNCTION +pg_catalog.pgfincore(IN regclass, IN bool, + OUT relpath text, + OUT segment int, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT pages_mem bigint, + OUT group_mem bigint, + OUT os_pages_free bigint, + OUT databit varbit, + OUT pages_dirty bigint, + OUT group_dirty bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT * from pg_catalog.pgfincore($1, ''main'', $2)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore_drawer(varbit) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2543; +CREATE FUNCTION +pg_catalog.pgfincore_drawer(IN varbit, + OUT drawer cstring) +RETURNS cstring LANGUAGE INTERNAL IMMUTABLE +AS 'pgfincore_drawer' +; +COMMENT ON FUNCTION pg_catalog.pgfincore_drawer(varbit) +IS 'A naive drawing function to visualize page cache per object'; diff --git a/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_613.sql b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_613.sql new file mode 100644 index 000000000..b4ad240f1 --- /dev/null +++ b/src/include/catalog/upgrade_sql/upgrade_catalog_otherdb/upgrade-post_catalog_otherdb_92_613.sql @@ -0,0 +1,206 @@ +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf() CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2371; +CREATE FUNCTION +pg_catalog.pgsysconf(OUT os_page_size bigint, + OUT os_pages_free bigint, + OUT os_total_pages bigint) +RETURNS record LANGUAGE INTERNAL VOLATILE +AS 'pgfincore'; +COMMENT ON FUNCTION pg_catalog.pgsysconf() +IS 'Get system configuration information at run time, man 3 sysconf for details'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgsysconf_pretty() CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2534; +CREATE FUNCTION +pg_catalog.pgsysconf_pretty(OUT os_page_size text, + OUT os_pages_free text, + OUT os_total_pages text) +RETURNS record LANGUAGE SQL VOLATILE +AS ' +select pg_catalog.pg_size_pretty(os_page_size) as os_page_size, + pg_catalog.pg_size_pretty(os_pages_free * os_page_size) as os_pages_free, + pg_catalog.pg_size_pretty(os_total_pages * os_page_size) as os_total_pages +from pgsysconf()' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise(regclass, text, int) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2372; +CREATE OR REPLACE FUNCTION +pg_catalog.pgfadvise(IN regclass, IN text, IN int, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE INTERNAL VOLATILE +AS 'pgfadvise' +; +COMMENT ON FUNCTION pg_catalog.pgfadvise(regclass, text, int) +IS 'Predeclare an access pattern for file data'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_willneed(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2535; +CREATE FUNCTION +pg_catalog.pgfadvise_willneed(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 10)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_dontneed(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2536; +CREATE FUNCTION +pg_catalog.pgfadvise_dontneed(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 20)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_normal(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2537; +CREATE FUNCTION +pg_catalog.pgfadvise_normal(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 30)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_sequential(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2538; +CREATE FUNCTION +pg_catalog.pgfadvise_sequential(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 40)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_random(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2542; +CREATE FUNCTION +pg_catalog.pgfadvise_random(IN regclass, + OUT relpath text, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT os_pages_free bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise($1, ''main'', 50)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, text, int, bool, bool, varbit) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2373; +CREATE FUNCTION +pg_catalog.pgfadvise_loader(IN regclass, IN text, IN int, IN bool, IN bool, IN varbit, + OUT relpath text, + OUT os_page_size bigint, + OUT os_pages_free bigint, + OUT pages_loaded bigint, + OUT pages_unloaded bigint) +RETURNS setof record LANGUAGE INTERNAL VOLATILE +AS 'pgfadvise_loader' +; +COMMENT ON FUNCTION pg_catalog.pgfadvise_loader(regclass, text, int, bool, bool, varbit) +IS 'Restore cache from the snapshot, options to load/unload each block to/from cache'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfadvise_loader(regclass, int, bool, bool, varbit) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2539; +CREATE FUNCTION +pg_catalog.pgfadvise_loader(IN regclass, IN int, IN bool, IN bool, IN varbit, + OUT relpath text, + OUT os_page_size bigint, + OUT os_pages_free bigint, + OUT pages_loaded bigint, + OUT pages_unloaded bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT pg_catalog.pgfadvise_loader($1, ''main'', $2, $3, $4, $5)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, text, bool) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2374; +CREATE FUNCTION +pg_catalog.pgfincore(IN regclass, IN text, IN bool, + OUT relpath text, + OUT segment int, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT pages_mem bigint, + OUT group_mem bigint, + OUT os_pages_free bigint, + OUT databit varbit, + OUT pages_dirty bigint, + OUT group_dirty bigint) +RETURNS setof record LANGUAGE INTERNAL VOLATILE +AS 'pgfincore' +; +COMMENT ON FUNCTION pg_catalog.pgfincore(regclass, text, bool) +IS 'Utility to inspect and get a snapshot of the system cache'; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2541; +CREATE FUNCTION +pg_catalog.pgfincore(IN regclass, + OUT relpath text, + OUT segment int, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT pages_mem bigint, + OUT group_mem bigint, + OUT os_pages_free bigint, + OUT databit varbit, + OUT pages_dirty bigint, + OUT group_dirty bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT * from pg_catalog.pgfincore($1, ''main'', false)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore(regclass, bool) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2540; +CREATE FUNCTION +pg_catalog.pgfincore(IN regclass, IN bool, + OUT relpath text, + OUT segment int, + OUT os_page_size bigint, + OUT rel_os_pages bigint, + OUT pages_mem bigint, + OUT group_mem bigint, + OUT os_pages_free bigint, + OUT databit varbit, + OUT pages_dirty bigint, + OUT group_dirty bigint) +RETURNS setof record LANGUAGE SQL VOLATILE +AS 'SELECT * from pg_catalog.pgfincore($1, ''main'', $2)' +; + + +DROP FUNCTION IF EXISTS pg_catalog.pgfincore_drawer(varbit) CASCADE; +SET LOCAL inplace_upgrade_next_system_object_oids=IUO_PROC, 2543; +CREATE FUNCTION +pg_catalog.pgfincore_drawer(IN varbit, + OUT drawer cstring) +RETURNS cstring LANGUAGE INTERNAL IMMUTABLE +AS 'pgfincore_drawer' +; +COMMENT ON FUNCTION pg_catalog.pgfincore_drawer(varbit) +IS 'A naive drawing function to visualize page cache per object'; diff --git a/src/test/regress/expected/pgfincore.out b/src/test/regress/expected/pgfincore.out new file mode 100644 index 000000000..2c6fec9fc --- /dev/null +++ b/src/test/regress/expected/pgfincore.out @@ -0,0 +1,122 @@ +-- +-- test SYSCONF +-- +select true from pgsysconf(); + bool +------ + t +(1 row) + +select true from pgsysconf_pretty(); + bool +------ + t +(1 row) + +-- +-- make a temp table to use below +-- +CREATE TEMP TABLE test AS SELECT generate_series(1,256) as a; +-- +-- this is not perfect testing but it is hard to predict what the OS will do +-- for *sure* +-- +-- +-- test fadvise_loader +-- +select true from pgfadvise_loader('test', 0, true, true, B'1010'); + bool +------ + t +(1 row) + +select true from pgfadvise_loader('test', 0, true, false, B'1010'); + bool +------ + t +(1 row) + +select true from pgfadvise_loader('test', 0, false, true, B'1010'); + bool +------ + t +(1 row) + +select true from pgfadvise_loader('test', 0, false, false, B'1010'); + bool +------ + t +(1 row) + +-- must not fail on empty databit input +select true from pgfadvise_loader('test', 0, false, false, B''); + bool +------ + t +(1 row) + +-- ERROR on NULL databit input +select true from pgfadvise_loader('test', 0, false, false, NULL); +ERROR: pgfadvise_loader: databit argument shouldn't be NULL +CONTEXT: referenced column: pgfadvise_loader +SQL function "pgfadvise_loader" statement 1 +-- +-- test pgfincore +-- +select true from pgfincore('test', true); + bool +------ + t +(1 row) + +select true from pgfincore('test'); + bool +------ + t +(1 row) + +-- +-- test DONTNEED, WILLNEED +-- +select true from pgfadvise_willneed('test'); + bool +------ + t +(1 row) + +select true from pgfadvise_dontneed('test'); + bool +------ + t +(1 row) + +-- +-- test PGFADVISE flags +-- +select true from pgfadvise_sequential('test'); + bool +------ + t +(1 row) + +select true from pgfadvise_random('test'); + bool +------ + t +(1 row) + +select true from pgfadvise_normal('test'); + bool +------ + t +(1 row) + +-- +-- tests drawers +-- +select true from pgfincore('test','main',true); + bool +------ + t +(1 row) + diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 79c49f338..cccf21017 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -929,3 +929,4 @@ test: composite_datum_record mysql_function b_comments test: join_test_alias alter_ctable_compress test: ignore/ignore_type_transform ignore/ignore_not_null_constraints ignore/ignore_unique_constraints ignore/ignore_no_matched_partition +test: pgfincore diff --git a/src/test/regress/sql/pgfincore.sql b/src/test/regress/sql/pgfincore.sql new file mode 100644 index 000000000..192845760 --- /dev/null +++ b/src/test/regress/sql/pgfincore.sql @@ -0,0 +1,51 @@ +-- +-- test SYSCONF +-- +select true from pgsysconf(); +select true from pgsysconf_pretty(); + +-- +-- make a temp table to use below +-- +CREATE TEMP TABLE test AS SELECT generate_series(1,256) as a; + +-- +-- this is not perfect testing but it is hard to predict what the OS will do +-- for *sure* +-- + +-- +-- test fadvise_loader +-- +select true from pgfadvise_loader('test', 0, true, true, B'1010'); +select true from pgfadvise_loader('test', 0, true, false, B'1010'); +select true from pgfadvise_loader('test', 0, false, true, B'1010'); +select true from pgfadvise_loader('test', 0, false, false, B'1010'); +-- must not fail on empty databit input +select true from pgfadvise_loader('test', 0, false, false, B''); +-- ERROR on NULL databit input +select true from pgfadvise_loader('test', 0, false, false, NULL); + +-- +-- test pgfincore +-- +select true from pgfincore('test', true); +select true from pgfincore('test'); + +-- +-- test DONTNEED, WILLNEED +-- +select true from pgfadvise_willneed('test'); +select true from pgfadvise_dontneed('test'); + +-- +-- test PGFADVISE flags +-- +select true from pgfadvise_sequential('test'); +select true from pgfadvise_random('test'); +select true from pgfadvise_normal('test'); + +-- +-- tests drawers +-- +select true from pgfincore('test','main',true);