PostgreSQL中Pluggable storage for tables的实现方法是什么
这篇文章主要讲解了“PostgreSQL中Pluggable storage for tables的实现方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL中Pluggable storage for tables的实现方法是什么”吧!
PostgreSQL 12 beta 1 已于近期发布,此版本包含了众多新特性,其中可插拔表存储接口允许创建和使用不同的表存储方式,该特性的详细描述如下:
Pluggable storage for tables
PostgreSQL 12 引入了可插入表存储接口,允许创建和使用不同的表存储方法。可以使用 CREATE ACCESS METHOD 命令将新的访问方法添加到 PostgreSQL 集群,然后将其添加到 CREATE TABLE 上具有新 USING 子句的表中。可以通过创建新的表访问方法来定义表存储接口。在 PostgreSQL 12 中,默认使用的存储接口是堆访问方法,它目前是唯一的内置方法。
在创建/初始化relation时,指定access method,相应的源文件为relcache.c
relcache.c
1.在RelationBuildLocalRelation方法中,调用RelationInitTableAccessMethod初始化Table Access Method
RelationRelationBuildLocalRelation(constchar*relname,Oidrelnamespace,TupleDesctupDesc,Oidrelid,Oidaccessmtd,Oidrelfilenode,Oidreltablespace,boolshared_relation,boolmapped_relation,charrelpersistence,charrelkind){...if(relkind==RELKIND_RELATION||relkind==RELKIND_SEQUENCE||relkind==RELKIND_TOASTVALUE||relkind==RELKIND_MATVIEW)RelationInitTableAccessMethod(rel);...}/**Initializetableaccessmethodsupportforatablelikerelation*初始化表访问方法*/voidRelationInitTableAccessMethod(Relationrelation){HeapTupletuple;Form_pg_amaform;if(relation->rd_rel->relkind==RELKIND_SEQUENCE)//序列号{/**Sequencesarecurrentlyaccessedlikeheaptables,butitdoesn't*seemprudenttoshowthatinthecatalog.Sojustoverwriteit*here.*///设置accessmethodhandlerrelation->rd_amhandler=HEAP_TABLE_AM_HANDLER_OID;}elseif(IsCatalogRelation(relation))//系统表{/**Avoiddoingasyscachelookupforcatalogtables.*/Assert(relation->rd_rel->relam==HEAP_TABLE_AM_OID);relation->rd_amhandler=HEAP_TABLE_AM_HANDLER_OID;}else//其他{/**Lookupthetableaccessmethod,savetheOIDofitshandler*function.*/Assert(relation->rd_rel->relam!=InvalidOid);tuple=SearchSysCache1(AMOID,ObjectIdGetDatum(relation->rd_rel->relam));if(!HeapTupleIsValid(tuple))elog(ERROR,"cachelookupfailedforaccessmethod%u",relation->rd_rel->relam);aform=(Form_pg_am)GETSTRUCT(tuple);relation->rd_amhandler=aform->amhandler;ReleaseSysCache(tuple);}/**NowwecanfetchthetableAM'sAPIstruct*/InitTableAmRoutine(relation);}/**FillintheTableAmRoutineforarelation**relation'srd_amhandlermustbevalidalready.*/staticvoidInitTableAmRoutine(Relationrelation){relation->rd_tableam=GetTableAmRoutine(relation->rd_amhandler);}
2.在formrdesc方法中,设置relation的访问方法(relation->rd_tableam)
staticvoidformrdesc(constchar*relationName,OidrelationReltype,boolisshared,intnatts,constFormData_pg_attribute*attrs){.../**initializethetableamhandler*/relation->rd_rel->relam=HEAP_TABLE_AM_OID;relation->rd_tableam=GetHeapamTableAmRoutine();...}
GetHeapamTableAmRoutine方法在源文件heapam_handler.c中
heapam_handler.c
方法定义如下
...constTableAmRoutine*GetHeapamTableAmRoutine(void){return&heapam_methods;}Datumheap_tableam_handler(PG_FUNCTION_ARGS){PG_RETURN_POINTER(&heapam_methods);}
heapam_methods是TableAmRoutine结构体,定义了一套heap access method,函数指针指向heap_XXX函数.
/*------------------------------------------------------------------------*Definitionoftheheaptableaccessmethod.*------------------------------------------------------------------------*/staticconstTableAmRoutineheapam_methods={.type=T_TableAmRoutine,.slot_callbacks=heapam_slot_callbacks,.scan_begin=heap_beginscan,.scan_end=heap_endscan,.scan_rescan=heap_rescan,.scan_getnextslot=heap_getnextslot,.parallelscan_estimate=table_block_parallelscan_estimate,.parallelscan_initialize=table_block_parallelscan_initialize,.parallelscan_reinitialize=table_block_parallelscan_reinitialize,.index_fetch_begin=heapam_index_fetch_begin,.index_fetch_reset=heapam_index_fetch_reset,.index_fetch_end=heapam_index_fetch_end,.index_fetch_tuple=heapam_index_fetch_tuple,.tuple_insert=heapam_tuple_insert,.tuple_insert_speculative=heapam_tuple_insert_speculative,.tuple_complete_speculative=heapam_tuple_complete_speculative,.multi_insert=heap_multi_insert,.tuple_delete=heapam_tuple_delete,.tuple_update=heapam_tuple_update,.tuple_lock=heapam_tuple_lock,.finish_bulk_insert=heapam_finish_bulk_insert,.tuple_fetch_row_version=heapam_fetch_row_version,.tuple_get_latest_tid=heap_get_latest_tid,.tuple_tid_valid=heapam_tuple_tid_valid,.tuple_satisfies_snapshot=heapam_tuple_satisfies_snapshot,.compute_xid_horizon_for_tuples=heap_compute_xid_horizon_for_tuples,.relation_set_new_filenode=heapam_relation_set_new_filenode,.relation_nontransactional_truncate=heapam_relation_nontransactional_truncate,.relation_copy_data=heapam_relation_copy_data,.relation_copy_for_cluster=heapam_relation_copy_for_cluster,.relation_vacuum=heap_vacuum_rel,.scan_analyze_next_block=heapam_scan_analyze_next_block,.scan_analyze_next_tuple=heapam_scan_analyze_next_tuple,.index_build_range_scan=heapam_index_build_range_scan,.index_validate_scan=heapam_index_validate_scan,.relation_size=heapam_relation_size,.relation_estimate_size=heapam_estimate_rel_size,.scan_bitmap_next_block=heapam_scan_bitmap_next_block,.scan_bitmap_next_tuple=heapam_scan_bitmap_next_tuple,.scan_sample_next_block=heapam_scan_sample_next_block,.scan_sample_next_tuple=heapam_scan_sample_next_tuple};
TableAmRoutine在源文件tableam.h中定义
tableam.h
TableAmRoutine结构体封装了table access method,如需自定义storage接口,则需实现(部分)该结构体中定义的函数,在创建表时指定自定义的存储引擎,指向自定义的access method.
/**APIstructforatableAM.Notethismustbeallocatedina*server-lifetimemanner,typicallyasastaticconststruct,whichthengets*returnedbyFormData_pg_am.amhandler.**Inmostcasesit'snotappropriatetocallthecallbacksdirectly,usethe*table_*wrapperfunctionsinstead.**GetTableAmRoutine()assertsthatrequiredcallbacksarefilledin,remember*toupdatewhenaddingacallback.*/typedefstructTableAmRoutine{/*thismustbesettoT_TableAmRoutine*/NodeTagtype;/*------------------------------------------------------------------------*Slotrelatedcallbacks.*------------------------------------------------------------------------*//**ReturnslotimplementationsuitableforstoringatupleofthisAM.*/constTupleTableSlotOps*(*slot_callbacks)(Relationrel);/*------------------------------------------------------------------------*Tablescancallbacks.*------------------------------------------------------------------------*//**Startascanof`rel`.ThecallbackhastoreturnaTableScanDesc,*whichwilltypicallybeembeddedinalarger,AMspecific,struct.**Ifnkeys!=0,theresultsneedtobefilteredbythosescankeys.**pscan,ifnotNULL,willhavealreadybeeninitializedwith*parallelscan_initialize(),andhastobeforthesamerelation.Will*onlybesetcomingfromtable_beginscan_parallel().**`flags`isabitmaskindicatingthetypeofscan(ScanOptions's*SO_TYPE_*,currentlyonlyonemaybespecified),optionscontrolling*thescan'sbehaviour(ScanOptions'sSO_ALLOW_*,severalmaybe*specified,anAMmayignoreunsupportedones)andwhetherthesnapshot*needstobedeallocatedatscan_end(ScanOptions'sSO_TEMP_SNAPSHOT).*/TableScanDesc(*scan_begin)(Relationrel,Snapshotsnapshot,intnkeys,structScanKeyData*key,ParallelTableScanDescpscan,uint32flags);/**Releaseresourcesanddeallocatescan.IfTableScanDesc.temp_snap,*TableScanDesc.rs_snapshotneedstobeunregistered.*/void(*scan_end)(TableScanDescscan);/**Restartrelationscan.Ifset_paramsissettotrue,allow_{strat,*sync,pagemode}(seescan_begin)changesshouldbetakenintoaccount.*/void(*scan_rescan)(TableScanDescscan,structScanKeyData*key,boolset_params,boolallow_strat,boolallow_sync,boolallow_pagemode);/**Returnnexttuplefrom`scan`,storeinslot.*/bool(*scan_getnextslot)(TableScanDescscan,ScanDirectiondirection,TupleTableSlot*slot);/*------------------------------------------------------------------------*Paralleltablescanrelatedfunctions.*------------------------------------------------------------------------*//**Estimatethesizeofsharedmemoryneededforaparallelscanofthis*relation.Thesnapshotdoesnotneedtobeaccountedfor.*/Size(*parallelscan_estimate)(Relationrel);/**InitializeParallelTableScanDescforaparallelscanofthisrelation.*`pscan`willbesizedaccordingtoparallelscan_estimate()forthesame*relation.*/Size(*parallelscan_initialize)(Relationrel,ParallelTableScanDescpscan);/**Reinitialize`pscan`foranewscan.`rel`willbethesamerelationas*when`pscan`wasinitializedbyparallelscan_initialize.*/void(*parallelscan_reinitialize)(Relationrel,ParallelTableScanDescpscan);/*------------------------------------------------------------------------*IndexScanCallbacks*------------------------------------------------------------------------*//**Preparetofetchtuplesfromtherelation,asneededwhenfetching*tuplesforanindexscan.Thecallbackhastoreturnan*IndexFetchTableData,whichtheAMwilltypicallyembedinalarger*structurewithadditionalinformation.**Tuplesforanindexscancanthenbefetchedviaindex_fetch_tuple.*/structIndexFetchTableData*(*index_fetch_begin)(Relationrel);/**Resetindexfetch.Typicallythiswillreleasecrossindexfetch*resourcesheldinIndexFetchTableData.*/void(*index_fetch_reset)(structIndexFetchTableData*data);/**Releaseresourcesanddeallocateindexfetch.*/void(*index_fetch_end)(structIndexFetchTableData*data);/**Fetchtupleat`tid`into`slot`,afterdoingavisibilitytest*accordingto`snapshot`.Ifatuplewasfoundandpassedthevisibility*test,returntrue,falseotherwise.**NotethatAMsthatdonotnecessarilyupdateindexeswhenindexed*columnsdonotchange,needtoreturnthecurrent/correctversionof*thetuplethatisvisibletothesnapshot,evenifthetidpointstoan*olderversionofthetuple.***call_againisfalseonthefirstcalltoindex_fetch_tupleforatid.*Iftherepotentiallyisanothertuplematchingthetid,*call_again*needsbesettotruebyindex_fetch_tuple,signallingtothecaller*thatindex_fetch_tupleshouldbecalledagainforthesametid.***all_dead,ifall_deadisnotNULL,shouldbesettotrueby*index_fetch_tupleiffitisguaranteedthatnobackendneedstosee*thattuple.IndexAMscanusethatdoavoidreturningthattidin*futuresearches.*/bool(*index_fetch_tuple)(structIndexFetchTableData*scan,ItemPointertid,Snapshotsnapshot,TupleTableSlot*slot,bool*call_again,bool*all_dead);/*------------------------------------------------------------------------*Callbacksfornon-modifyingoperationsonindividualtuples*------------------------------------------------------------------------*//**Fetchtupleat`tid`into`slot`,afterdoingavisibilitytest*accordingto`snapshot`.Ifatuplewasfoundandpassedthevisibility*test,returnstrue,falseotherwise.*/bool(*tuple_fetch_row_version)(Relationrel,ItemPointertid,Snapshotsnapshot,TupleTableSlot*slot);/**Istidvalidforascanofthisrelation.*/bool(*tuple_tid_valid)(TableScanDescscan,ItemPointertid);/**Returnthelatestversionofthetupleat`tid`,byupdating`tid`to*pointatthenewestversion.*/void(*tuple_get_latest_tid)(TableScanDescscan,ItemPointertid);/**Doesthetuplein`slot`satisfy`snapshot`?Theslotneedstobeof*theappropriatetypefortheAM.*/bool(*tuple_satisfies_snapshot)(Relationrel,TupleTableSlot*slot,Snapshotsnapshot);/*seetable_compute_xid_horizon_for_tuples()*/TransactionId(*compute_xid_horizon_for_tuples)(Relationrel,ItemPointerData*items,intnitems);/*------------------------------------------------------------------------*Manipulationsofphysicaltuples.*------------------------------------------------------------------------*//*seetable_insert()forreferenceaboutparameters*/void(*tuple_insert)(Relationrel,TupleTableSlot*slot,CommandIdcid,intoptions,structBulkInsertStateData*bistate);/*seetable_insert_speculative()forreferenceaboutparameters*/void(*tuple_insert_speculative)(Relationrel,TupleTableSlot*slot,CommandIdcid,intoptions,structBulkInsertStateData*bistate,uint32specToken);/*seetable_complete_speculative()forreferenceaboutparameters*/void(*tuple_complete_speculative)(Relationrel,TupleTableSlot*slot,uint32specToken,boolsucceeded);/*seetable_multi_insert()forreferenceaboutparameters*/void(*multi_insert)(Relationrel,TupleTableSlot**slots,intnslots,CommandIdcid,intoptions,structBulkInsertStateData*bistate);/*seetable_delete()forreferenceaboutparameters*/TM_Result(*tuple_delete)(Relationrel,ItemPointertid,CommandIdcid,Snapshotsnapshot,Snapshotcrosscheck,boolwait,TM_FailureData*tmfd,boolchangingPart);/*seetable_update()forreferenceaboutparameters*/TM_Result(*tuple_update)(Relationrel,ItemPointerotid,TupleTableSlot*slot,CommandIdcid,Snapshotsnapshot,Snapshotcrosscheck,boolwait,TM_FailureData*tmfd,LockTupleMode*lockmode,bool*update_indexes);/*seetable_lock_tuple()forreferenceaboutparameters*/TM_Result(*tuple_lock)(Relationrel,ItemPointertid,Snapshotsnapshot,TupleTableSlot*slot,CommandIdcid,LockTupleModemode,LockWaitPolicywait_policy,uint8flags,TM_FailureData*tmfd);/**Performoperationsnecessarytocompleteinsertionsmadevia*tuple_insertandmulti_insertwithaBulkInsertStatespecified.This*mayforexamplebeusedtoflushtherelation,whenthe*TABLE_INSERT_SKIP_WALoptionwasused.**Typicallycallersoftuple_insertandmulti_insertwilljustpassall*theflagsthatapplytothem,andeachAMhastodecidewhichofthem*makesenseforit,andthenonlytakeactionsinfinish_bulk_insertfor*thoseflags,andignoreothers.**Optionalcallback.*/void(*finish_bulk_insert)(Relationrel,intoptions);/*------------------------------------------------------------------------*DDLrelatedfunctionality.*------------------------------------------------------------------------*//**Thiscallbackneedstocreateanewrelationfilenodefor`rel`,with*appropriatedurabilitybehaviourfor`persistence`.**Notethatonlythesubsetoftherelcachefilledby*RelationBuildLocalRelation()canberelieduponandthattherelation's*catalogentrieseitherwilleithernotyetexist(newrelation),or*willstillreferencetheoldrelfilenode.**Asoutput*freezeXid,*minmultimustbesettothevaluesappropriate*forpg_class.{relfrozenxid,relminmxid}.ForAMsthatdon'tneedthose*fieldstobefilledtheycanbesettoInvalidTransactionIdand*InvalidMultiXactId,respectively.**Seealsotable_relation_set_new_filenode().*/void(*relation_set_new_filenode)(Relationrel,constRelFileNode*newrnode,charpersistence,TransactionId*freezeXid,MultiXactId*minmulti);/**Thiscallbackneedstoremoveallcontentsfrom`rel`'scurrent*relfilenode.Noprovisionsfortransactionalbehaviourneedtobemade.*Oftenthiscanbeimplementedbytruncatingtheunderlyingstorageto*itsminimalsize.**Seealsotable_relation_nontransactional_truncate().*/void(*relation_nontransactional_truncate)(Relationrel);/**Seetable_relation_copy_data().**Thiscantypicallybeimplementedbydirectlycopyingtheunderlying*storage,unlessitcontainsreferencestothetablespaceinternally.*/void(*relation_copy_data)(Relationrel,constRelFileNode*newrnode);/*Seetable_relation_copy_for_cluster()*/void(*relation_copy_for_cluster)(RelationNewHeap,RelationOldHeap,RelationOldIndex,booluse_sort,TransactionIdOldestXmin,TransactionId*xid_cutoff,MultiXactId*multi_cutoff,double*num_tuples,double*tups_vacuumed,double*tups_recently_dead);/**ReacttoVACUUMcommandontherelation.TheVACUUMmightbeuser*triggeredorbyautovacuum.ThespecificactionsperformedbytheAM*willdependheavilyontheindividualAM.**Onentryatransactionisalreadyestablished,andtherelationis*lockedwithaShareUpdateExclusivelock.**NotethatneitherVACUUMFULL(andCLUSTER),norANALYZEgothrough*thisroutine,evenif(forANALYZE)itispartofthesameVACUUM*command.**Thereprobably,inthefuture,needstobeaseparatecallbackto*integratewithautovacuum'sscheduling.*/void(*relation_vacuum)(Relationonerel,structVacuumParams*params,BufferAccessStrategybstrategy);/**Preparetoanalyzeblock`blockno`of`scan`.Thescanhasbeenstarted*withtable_beginscan_analyze().Seealso*table_scan_analyze_next_block().**Thecallbackmayacquireresourceslikelocksthatarehelduntil*table_scan_analyze_next_tuple()returnsfalse.Ite.g.canmakesense*toholdalockuntilalltuplesonablockhavebeenanalyzedby*scan_analyze_next_tuple.**Thecallbackcanreturnfalseiftheblockisnotsuitablefor*sampling,e.g.becauseit'sametapagethatcouldnevercontaintuples.**XXX:Thisobviouslyisprimarilysuitedforblock-basedAMs.It'snot*clearwhatagoodinterfacefornonblockbasedAMswouldbe,sothere*isn'toneyet.*/bool(*scan_analyze_next_block)(TableScanDescscan,BlockNumberblockno,BufferAccessStrategybstrategy);/**Seetable_scan_analyze_next_tuple().**NoteveryAMmighthaveameaningfulconceptofdeadrows,inwhich*caseit'sOKtonotincrement*deadrows-butnotethatthatmay*influenceautovacuumscheduling(seecommentforrelation_vacuum*callback).*/bool(*scan_analyze_next_tuple)(TableScanDescscan,TransactionIdOldestXmin,double*liverows,double*deadrows,TupleTableSlot*slot);/*seetable_index_build_range_scanforreferenceaboutparameters*/double(*index_build_range_scan)(Relationheap_rel,Relationindex_rel,structIndexInfo*index_nfo,boolallow_sync,boolanyvisible,boolprogress,BlockNumberstart_blockno,BlockNumberend_blockno,IndexBuildCallbackcallback,void*callback_state,TableScanDescscan);/*seetable_index_validate_scanforreferenceaboutparameters*/void(*index_validate_scan)(Relationheap_rel,Relationindex_rel,structIndexInfo*index_info,Snapshotsnapshot,structValidateIndexState*state);/*------------------------------------------------------------------------*Miscellaneousfunctions.*------------------------------------------------------------------------*//**Seetable_relation_size().**NotethatcurrentlyafewcallersusetheMAIN_FORKNUMsizetofigure*outtherangeofpotentiallyinterestingblocks(brin,analyze).It's*probablethatwe'llneedtorevisetheinterfaceforthoseatsome*point.*/uint64(*relation_size)(Relationrel,ForkNumberforkNumber);/*------------------------------------------------------------------------*Plannerrelatedfunctions.*------------------------------------------------------------------------*//**Seetable_relation_estimate_size().**Whileblockoriented,itshouldn'tbetoohardforanAMthatdoesn't*doesn'tinternallyuseblockstoconvertintoausablerepresentation.**Thisdiffersfromtherelation_sizecallbackbyreturningsize*estimates(bothrelationsizeandtuplecount)forplanningpurposes,*ratherthanreturningacurrentlycorrectestimate.*/void(*relation_estimate_size)(Relationrel,int32*attr_widths,BlockNumber*pages,double*tuples,double*allvisfrac);/*------------------------------------------------------------------------*Executorrelatedfunctions.*------------------------------------------------------------------------*//**Preparetofetch/check/returntuplesfrom`tbmres->blockno`aspart*ofabitmaptablescan.`scan`wasstartedviatable_beginscan_bm().*Returnfalseiftherearenotuplestobefoundonthepage,true*otherwise.**Thiswilltypicallyreadandpinthetargetblock,anddothenecessary*worktoallowscan_bitmap_next_tuple()toreturntuples(e.g.itmight*makesensetoperformtuplevisibilitychecksatthistime).Forsome*AMsitwillmakemoresensetodoalltheworkreferencing`tbmres`*contentshere,forothersitmightbebettertodefermoreworkto*scan_bitmap_next_tuple.**If`tbmres->blockno`is-1,thisisalossyscanandallvisibletuples*onthepagehavetobereturned,otherwisethetuplesatoffsetsin*`tbmres->offsets`needtobereturned.**XXX:CurrentlythismayonlybeimplementediftheAMusesmd.casits*storagemanager,andusesItemPointer->ip_blkidinamannerthatmaps*blockidsdirectlytotheunderlyingstorage.nodeBitmapHeapscan.c*performsprefetchingdirectlyusingthatinterface.Thisprobably*needstoberectifiedatalaterpoint.**XXX:CurrentlythismayonlybeimplementediftheAMusesthe*visibilitymap,asnodeBitmapHeapscan.cunconditionallyaccessesitto*performprefetching.Thisprobablyneedstoberectifiedatalater*point.**Optionalcallback,buteitherbothscan_bitmap_next_blockand*scan_bitmap_next_tupleneedtoexist,orneither.*/bool(*scan_bitmap_next_block)(TableScanDescscan,structTBMIterateResult*tbmres);/**Fetchthenexttupleofabitmaptablescaninto`slot`andreturntrue*ifavisibletuplewasfound,falseotherwise.**ForsomeAMsitwillmakemoresensetodoalltheworkreferencing*`tbmres`contentsinscan_bitmap_next_block,forothersitmightbe*bettertodefermoreworktothiscallback.**Optionalcallback,buteitherbothscan_bitmap_next_blockand*scan_bitmap_next_tupleneedtoexist,orneither.*/bool(*scan_bitmap_next_tuple)(TableScanDescscan,structTBMIterateResult*tbmres,TupleTableSlot*slot);/**Preparetofetchtuplesfromthenextblockinasamplescan.Return*falseifthesamplescanisfinished,trueotherwise.`scan`was*startedviatable_beginscan_sampling().**Typicallythiswillfirstdeterminethetargetblockbycallthe*TsmRoutine'sNextSampleBlock()callbackifnotNULL,oralternatively*performasequentialscanoverallblocks.Thedeterminedblockis*thentypicallyreadandpinned.**AstheTsmRoutineinterfaceisblockbased,ablockneedstobepassed*toNextSampleBlock().Ifthat'snotappropriateforanAM,it*internallyneedstoperformmappingbetweentheinternalandablock*basedrepresentation.**Notethatit'snotacceptabletoholddeadlockproneresourcessuchas*lwlocksuntilscan_sample_next_tuple()hasexhaustedthetuplesonthe*block-thetupleislikelytobereturnedtoanupperquerynode,and*thenextcallcouldbeoffalongwhile.Holdingbufferpinsandsuch*isobviouslyOK.**Currentlyitisrequiredtoimplementthisinterface,asthere'sno*alternativeway(contrarye.g.tobitmapscans)toimplementsample*scans.IfinfeasibletoimplementtheAMmayraiseanerror.*/bool(*scan_sample_next_block)(TableScanDescscan,structSampleScanState*scanstate);/**Thiscallback,onlycalledafterscan_sample_next_blockhasreturned*true,shoulddeterminethenexttupletobereturnedfromtheselected*blockusingtheTsmRoutine'sNextSampleTuple()callback.**Thecallbackneedstoperformvisibilitychecks,andonlyreturn*visibletuples.ThatobviouslycanmeancallingNextSampletuple()*multipletimes.**TheTsmRoutineinterfaceassumesthatthere'samaximumoffsetona*givenpage,soifthatdoesn'tapplytoanAM,itneedstoemulatethat*assumptionsomehow.*/bool(*scan_sample_next_tuple)(TableScanDescscan,structSampleScanState*scanstate,TupleTableSlot*slot);}TableAmRoutine;
感谢各位的阅读,以上就是“PostgreSQL中Pluggable storage for tables的实现方法是什么”的内容了,经过本文的学习后,相信大家对PostgreSQL中Pluggable storage for tables的实现方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。