LCOV - differential code coverage report
Current view: top level - src/backend/executor - execReplication.c (source / functions) Coverage Total Hit UNC UBC GNC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 82.2 % 241 198 3 40 13 185
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 11 11 3 8
Baseline: 16@8cea358b128 Branches: 64.0 % 197 126 1 70 2 124
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed [..60] days: 100.0 % 3 3 3
(240..) days: 81.9 % 238 195 3 40 13 182
Function coverage date bins:
(240..) days: 100.0 % 11 11 3 8
Branch coverage date bins:
[..60] days: 100.0 % 2 2 2
(240..) days: 63.6 % 195 124 1 70 2 122

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * execReplication.c
                                  4                 :                :  *    miscellaneous executor routines for logical replication
                                  5                 :                :  *
                                  6                 :                :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
                                  7                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                  8                 :                :  *
                                  9                 :                :  * IDENTIFICATION
                                 10                 :                :  *    src/backend/executor/execReplication.c
                                 11                 :                :  *
                                 12                 :                :  *-------------------------------------------------------------------------
                                 13                 :                :  */
                                 14                 :                : 
                                 15                 :                : #include "postgres.h"
                                 16                 :                : 
                                 17                 :                : #include "access/genam.h"
                                 18                 :                : #include "access/relscan.h"
                                 19                 :                : #include "access/tableam.h"
                                 20                 :                : #include "access/transam.h"
                                 21                 :                : #include "access/xact.h"
                                 22                 :                : #include "catalog/pg_am_d.h"
                                 23                 :                : #include "commands/trigger.h"
                                 24                 :                : #include "executor/executor.h"
                                 25                 :                : #include "executor/nodeModifyTable.h"
                                 26                 :                : #include "replication/logicalrelation.h"
                                 27                 :                : #include "storage/lmgr.h"
                                 28                 :                : #include "utils/builtins.h"
                                 29                 :                : #include "utils/lsyscache.h"
                                 30                 :                : #include "utils/rel.h"
                                 31                 :                : #include "utils/snapmgr.h"
                                 32                 :                : #include "utils/syscache.h"
                                 33                 :                : #include "utils/typcache.h"
                                 34                 :                : 
                                 35                 :                : 
                                 36                 :                : static bool tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2,
                                 37                 :                :                          TypeCacheEntry **eq);
                                 38                 :                : 
                                 39                 :                : /*
                                 40                 :                :  * Returns the fixed strategy number, if any, of the equality operator for the
                                 41                 :                :  * given index access method, otherwise, InvalidStrategy.
                                 42                 :                :  *
                                 43                 :                :  * Currently, only Btree and Hash indexes are supported. The other index access
                                 44                 :                :  * methods don't have a fixed strategy for equality operation - instead, the
                                 45                 :                :  * support routines of each operator class interpret the strategy numbers
                                 46                 :                :  * according to the operator class's definition.
                                 47                 :                :  */
                                 48                 :                : StrategyNumber
  275 akapila@postgresql.o       49                 :GNC      144227 : get_equal_strategy_number_for_am(Oid am)
                                 50                 :                : {
                                 51                 :                :     int         ret;
                                 52                 :                : 
                                 53      [ +  +  - ]:         144227 :     switch (am)
                                 54                 :                :     {
                                 55                 :         144217 :         case BTREE_AM_OID:
                                 56                 :         144217 :             ret = BTEqualStrategyNumber;
                                 57                 :         144217 :             break;
                                 58                 :             10 :         case HASH_AM_OID:
                                 59                 :             10 :             ret = HTEqualStrategyNumber;
                                 60                 :             10 :             break;
  275 akapila@postgresql.o       61                 :UNC           0 :         default:
                                 62                 :                :             /* XXX: Only Btree and Hash indexes are supported */
                                 63                 :              0 :             ret = InvalidStrategy;
                                 64                 :              0 :             break;
                                 65                 :                :     }
                                 66                 :                : 
  275 akapila@postgresql.o       67                 :GNC      144227 :     return ret;
                                 68                 :                : }
                                 69                 :                : 
                                 70                 :                : /*
                                 71                 :                :  * Return the appropriate strategy number which corresponds to the equality
                                 72                 :                :  * operator.
                                 73                 :                :  */
                                 74                 :                : static StrategyNumber
                                 75                 :          72110 : get_equal_strategy_number(Oid opclass)
                                 76                 :                : {
                                 77                 :          72110 :     Oid         am = get_opclass_method(opclass);
                                 78                 :                : 
                                 79                 :          72110 :     return get_equal_strategy_number_for_am(am);
                                 80                 :                : }
                                 81                 :                : 
                                 82                 :                : /*
                                 83                 :                :  * Setup a ScanKey for a search in the relation 'rel' for a tuple 'key' that
                                 84                 :                :  * is setup to match 'rel' (*NOT* idxrel!).
                                 85                 :                :  *
                                 86                 :                :  * Returns how many columns to use for the index scan.
                                 87                 :                :  *
                                 88                 :                :  * This is not generic routine, idxrel must be PK, RI, or an index that can be
                                 89                 :                :  * used for REPLICA IDENTITY FULL table. See FindUsableIndexForReplicaIdentityFull()
                                 90                 :                :  * for details.
                                 91                 :                :  *
                                 92                 :                :  * By definition, replication identity of a rel meets all limitations associated
                                 93                 :                :  * with that. Note that any other index could also meet these limitations.
                                 94                 :                :  */
                                 95                 :                : static int
 2642 peter_e@gmx.net            96                 :CBC       72099 : build_replindex_scan_key(ScanKey skey, Relation rel, Relation idxrel,
                                 97                 :                :                          TupleTableSlot *searchslot)
                                 98                 :                : {
                                 99                 :                :     int         index_attoff;
  396 akapila@postgresql.o      100                 :          72099 :     int         skey_attoff = 0;
                                101                 :                :     Datum       indclassDatum;
                                102                 :                :     oidvector  *opclass;
 2642 peter_e@gmx.net           103                 :          72099 :     int2vector *indkey = &idxrel->rd_index->indkey;
                                104                 :                : 
  386 dgustafsson@postgres      105                 :          72099 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, idxrel->rd_indextuple,
                                106                 :                :                                            Anum_pg_index_indclass);
 2642 peter_e@gmx.net           107                 :          72099 :     opclass = (oidvector *) DatumGetPointer(indclassDatum);
                                108                 :                : 
                                109                 :                :     /* Build scankey for every non-expression attribute in the index. */
  396 akapila@postgresql.o      110         [ +  + ]:         144211 :     for (index_attoff = 0; index_attoff < IndexRelationGetNumberOfKeyAttributes(idxrel);
                                111                 :          72112 :          index_attoff++)
                                112                 :                :     {
                                113                 :                :         Oid         operator;
                                114                 :                :         Oid         optype;
                                115                 :                :         Oid         opfamily;
                                116                 :                :         RegProcedure regop;
                                117                 :          72112 :         int         table_attno = indkey->values[index_attoff];
                                118                 :                :         StrategyNumber eq_strategy;
                                119                 :                : 
                                120         [ +  + ]:          72112 :         if (!AttributeNumberIsValid(table_attno))
                                121                 :                :         {
                                122                 :                :             /*
                                123                 :                :              * XXX: Currently, we don't support expressions in the scan key,
                                124                 :                :              * see code below.
                                125                 :                :              */
                                126                 :              2 :             continue;
                                127                 :                :         }
                                128                 :                : 
                                129                 :                :         /*
                                130                 :                :          * Load the operator info.  We need this to get the equality operator
                                131                 :                :          * function for the scan key.
                                132                 :                :          */
                                133                 :          72110 :         optype = get_opclass_input_type(opclass->values[index_attoff]);
                                134                 :          72110 :         opfamily = get_opclass_family(opclass->values[index_attoff]);
  275 akapila@postgresql.o      135                 :GNC       72110 :         eq_strategy = get_equal_strategy_number(opclass->values[index_attoff]);
                                136                 :                : 
 2642 peter_e@gmx.net           137                 :CBC       72110 :         operator = get_opfamily_member(opfamily, optype,
                                138                 :                :                                        optype,
                                139                 :                :                                        eq_strategy);
                                140                 :                : 
                                141         [ -  + ]:          72110 :         if (!OidIsValid(operator))
 2456 tgl@sss.pgh.pa.us         142         [ #  # ]:UBC           0 :             elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
                                143                 :                :                  eq_strategy, optype, optype, opfamily);
                                144                 :                : 
 2642 peter_e@gmx.net           145                 :CBC       72110 :         regop = get_opcode(operator);
                                146                 :                : 
                                147                 :                :         /* Initialize the scankey. */
  396 akapila@postgresql.o      148                 :          72110 :         ScanKeyInit(&skey[skey_attoff],
                                149                 :          72110 :                     index_attoff + 1,
                                150                 :                :                     eq_strategy,
                                151                 :                :                     regop,
                                152                 :          72110 :                     searchslot->tts_values[table_attno - 1]);
                                153                 :                : 
                                154                 :          72110 :         skey[skey_attoff].sk_collation = idxrel->rd_indcollation[index_attoff];
                                155                 :                : 
                                156                 :                :         /* Check for null value. */
                                157         [ +  + ]:          72110 :         if (searchslot->tts_isnull[table_attno - 1])
                                158                 :              1 :             skey[skey_attoff].sk_flags |= (SK_ISNULL | SK_SEARCHNULL);
                                159                 :                : 
                                160                 :          72110 :         skey_attoff++;
                                161                 :                :     }
                                162                 :                : 
                                163                 :                :     /* There must always be at least one attribute for the index scan. */
                                164         [ -  + ]:          72099 :     Assert(skey_attoff > 0);
                                165                 :                : 
                                166                 :          72099 :     return skey_attoff;
                                167                 :                : }
                                168                 :                : 
                                169                 :                : /*
                                170                 :                :  * Search the relation 'rel' for tuple using the index.
                                171                 :                :  *
                                172                 :                :  * If a matching tuple is found, lock it with lockmode, fill the slot with its
                                173                 :                :  * contents, and return true.  Return false otherwise.
                                174                 :                :  */
                                175                 :                : bool
 2642 peter_e@gmx.net           176                 :          72099 : RelationFindReplTupleByIndex(Relation rel, Oid idxoid,
                                177                 :                :                              LockTupleMode lockmode,
                                178                 :                :                              TupleTableSlot *searchslot,
                                179                 :                :                              TupleTableSlot *outslot)
                                180                 :                : {
                                181                 :                :     ScanKeyData skey[INDEX_MAX_KEYS];
                                182                 :                :     int         skey_attoff;
                                183                 :                :     IndexScanDesc scan;
                                184                 :                :     SnapshotData snap;
                                185                 :                :     TransactionId xwait;
                                186                 :                :     Relation    idxrel;
                                187                 :                :     bool        found;
  396 akapila@postgresql.o      188                 :          72099 :     TypeCacheEntry **eq = NULL;
                                189                 :                :     bool        isIdxSafeToSkipDuplicates;
                                190                 :                : 
                                191                 :                :     /* Open the index. */
 2642 peter_e@gmx.net           192                 :          72099 :     idxrel = index_open(idxoid, RowExclusiveLock);
                                193                 :                : 
  396 akapila@postgresql.o      194                 :          72099 :     isIdxSafeToSkipDuplicates = (GetRelationIdentityOrPK(rel) == idxoid);
                                195                 :                : 
 2642 peter_e@gmx.net           196                 :          72099 :     InitDirtySnapshot(snap);
                                197                 :                : 
                                198                 :                :     /* Build scan key. */
  396 akapila@postgresql.o      199                 :          72099 :     skey_attoff = build_replindex_scan_key(skey, rel, idxrel, searchslot);
                                200                 :                : 
                                201                 :                :     /* Start an index scan. */
                                202                 :          72099 :     scan = index_beginscan(rel, idxrel, &snap, skey_attoff, 0);
                                203                 :                : 
 2642 peter_e@gmx.net           204                 :UBC           0 : retry:
 2642 peter_e@gmx.net           205                 :CBC       72099 :     found = false;
                                206                 :                : 
  396 akapila@postgresql.o      207                 :          72099 :     index_rescan(scan, skey, skey_attoff, NULL, 0);
                                208                 :                : 
                                209                 :                :     /* Try to find the tuple */
                                210         [ +  + ]:          72099 :     while (index_getnext_slot(scan, ForwardScanDirection, outslot))
                                211                 :                :     {
                                212                 :                :         /*
                                213                 :                :          * Avoid expensive equality check if the index is primary key or
                                214                 :                :          * replica identity index.
                                215                 :                :          */
                                216         [ +  + ]:          72091 :         if (!isIdxSafeToSkipDuplicates)
                                217                 :                :         {
                                218         [ +  - ]:             15 :             if (eq == NULL)
                                219                 :             15 :                 eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
                                220                 :                : 
                                221         [ -  + ]:             15 :             if (!tuples_equal(outslot, searchslot, eq))
  396 akapila@postgresql.o      222                 :UBC           0 :                 continue;
                                223                 :                :         }
                                224                 :                : 
 2642 peter_e@gmx.net           225                 :CBC       72091 :         ExecMaterializeSlot(outslot);
                                226                 :                : 
                                227                 :         144182 :         xwait = TransactionIdIsValid(snap.xmin) ?
                                228         [ -  + ]:          72091 :             snap.xmin : snap.xmax;
                                229                 :                : 
                                230                 :                :         /*
                                231                 :                :          * If the tuple is locked, wait for locking transaction to finish and
                                232                 :                :          * retry.
                                233                 :                :          */
                                234         [ -  + ]:          72091 :         if (TransactionIdIsValid(xwait))
                                235                 :                :         {
 2642 peter_e@gmx.net           236                 :UBC           0 :             XactLockTableWait(xwait, NULL, NULL, XLTW_None);
                                237                 :              0 :             goto retry;
                                238                 :                :         }
                                239                 :                : 
                                240                 :                :         /* Found our tuple and it's not locked */
  396 akapila@postgresql.o      241                 :CBC       72091 :         found = true;
                                242                 :          72091 :         break;
                                243                 :                :     }
                                244                 :                : 
                                245                 :                :     /* Found tuple, try to lock it in the lockmode. */
 2642 peter_e@gmx.net           246         [ +  + ]:          72099 :     if (found)
                                247                 :                :     {
                                248                 :                :         TM_FailureData tmfd;
                                249                 :                :         TM_Result   res;
                                250                 :                : 
                                251                 :          72091 :         PushActiveSnapshot(GetLatestSnapshot());
                                252                 :                : 
 1788 andres@anarazel.de        253                 :          72091 :         res = table_tuple_lock(rel, &(outslot->tts_tid), GetLatestSnapshot(),
                                254                 :                :                                outslot,
                                255                 :                :                                GetCurrentCommandId(false),
                                256                 :                :                                lockmode,
                                257                 :                :                                LockWaitBlock,
                                258                 :                :                                0 /* don't follow updates */ ,
                                259                 :                :                                &tmfd);
                                260                 :                : 
 2642 peter_e@gmx.net           261                 :          72091 :         PopActiveSnapshot();
                                262                 :                : 
                                263   [ +  -  -  -  :          72091 :         switch (res)
                                                 - ]
                                264                 :                :         {
 1849 andres@anarazel.de        265                 :          72091 :             case TM_Ok:
 2642 peter_e@gmx.net           266                 :          72091 :                 break;
 1849 andres@anarazel.de        267                 :UBC           0 :             case TM_Updated:
                                268                 :                :                 /* XXX: Improve handling here */
                                269         [ #  # ]:              0 :                 if (ItemPointerIndicatesMovedPartitions(&tmfd.ctid))
 2199                           270         [ #  # ]:              0 :                     ereport(LOG,
                                271                 :                :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                272                 :                :                              errmsg("tuple to be locked was already moved to another partition due to concurrent update, retrying")));
                                273                 :                :                 else
                                274         [ #  # ]:              0 :                     ereport(LOG,
                                275                 :                :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                276                 :                :                              errmsg("concurrent update, retrying")));
 2642 peter_e@gmx.net           277                 :              0 :                 goto retry;
 1849 andres@anarazel.de        278                 :              0 :             case TM_Deleted:
                                279                 :                :                 /* XXX: Improve handling here */
                                280         [ #  # ]:              0 :                 ereport(LOG,
                                281                 :                :                         (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                282                 :                :                          errmsg("concurrent delete, retrying")));
                                283                 :              0 :                 goto retry;
                                284                 :              0 :             case TM_Invisible:
 2642 peter_e@gmx.net           285         [ #  # ]:              0 :                 elog(ERROR, "attempted to lock invisible tuple");
                                286                 :                :                 break;
                                287                 :              0 :             default:
 1788 andres@anarazel.de        288         [ #  # ]:              0 :                 elog(ERROR, "unexpected table_tuple_lock status: %u", res);
                                289                 :                :                 break;
                                290                 :                :         }
                                291                 :                :     }
                                292                 :                : 
 2642 peter_e@gmx.net           293                 :CBC       72099 :     index_endscan(scan);
                                294                 :                : 
                                295                 :                :     /* Don't release lock until commit. */
                                296                 :          72099 :     index_close(idxrel, NoLock);
                                297                 :                : 
                                298                 :          72099 :     return found;
                                299                 :                : }
                                300                 :                : 
                                301                 :                : /*
                                302                 :                :  * Compare the tuples in the slots by checking if they have equal values.
                                303                 :                :  */
                                304                 :                : static bool
 1464 noah@leadboat.com         305                 :         105282 : tuples_equal(TupleTableSlot *slot1, TupleTableSlot *slot2,
                                306                 :                :              TypeCacheEntry **eq)
                                307                 :                : {
                                308                 :                :     int         attrnum;
                                309                 :                : 
 1861 andres@anarazel.de        310         [ -  + ]:         105282 :     Assert(slot1->tts_tupleDescriptor->natts ==
                                311                 :                :            slot2->tts_tupleDescriptor->natts);
                                312                 :                : 
                                313                 :         105282 :     slot_getallattrs(slot1);
                                314                 :         105282 :     slot_getallattrs(slot2);
                                315                 :                : 
                                316                 :                :     /* Check equality of the attributes. */
                                317         [ +  + ]:         105473 :     for (attrnum = 0; attrnum < slot1->tts_tupleDescriptor->natts; attrnum++)
                                318                 :                :     {
                                319                 :                :         Form_pg_attribute att;
                                320                 :                :         TypeCacheEntry *typentry;
                                321                 :                : 
  390 akapila@postgresql.o      322                 :         105312 :         att = TupleDescAttr(slot1->tts_tupleDescriptor, attrnum);
                                323                 :                : 
                                324                 :                :         /*
                                325                 :                :          * Ignore dropped and generated columns as the publisher doesn't send
                                326                 :                :          * those
                                327                 :                :          */
  388                           328   [ +  +  +  + ]:         105312 :         if (att->attisdropped || att->attgenerated)
  390                           329                 :              2 :             continue;
                                330                 :                : 
                                331                 :                :         /*
                                332                 :                :          * If one value is NULL and other is not, then they are certainly not
                                333                 :                :          * equal
                                334                 :                :          */
 1861 andres@anarazel.de        335         [ -  + ]:         105310 :         if (slot1->tts_isnull[attrnum] != slot2->tts_isnull[attrnum])
 2642 peter_e@gmx.net           336                 :UBC           0 :             return false;
                                337                 :                : 
                                338                 :                :         /*
                                339                 :                :          * If both are NULL, they can be considered equal.
                                340                 :                :          */
 1861 andres@anarazel.de        341   [ +  +  -  + ]:CBC      105310 :         if (slot1->tts_isnull[attrnum] || slot2->tts_isnull[attrnum])
 2642 peter_e@gmx.net           342                 :              1 :             continue;
                                343                 :                : 
 1464 noah@leadboat.com         344                 :         105309 :         typentry = eq[attrnum];
                                345         [ +  + ]:         105309 :         if (typentry == NULL)
                                346                 :                :         {
                                347                 :            188 :             typentry = lookup_type_cache(att->atttypid,
                                348                 :                :                                          TYPECACHE_EQ_OPR_FINFO);
                                349         [ -  + ]:            188 :             if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
 1464 noah@leadboat.com         350         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                351                 :                :                         (errcode(ERRCODE_UNDEFINED_FUNCTION),
                                352                 :                :                          errmsg("could not identify an equality operator for type %s",
                                353                 :                :                                 format_type_be(att->atttypid))));
 1464 noah@leadboat.com         354                 :CBC         188 :             eq[attrnum] = typentry;
                                355                 :                :         }
                                356                 :                : 
 1850 peter@eisentraut.org      357         [ +  + ]:         105309 :         if (!DatumGetBool(FunctionCall2Coll(&typentry->eq_opr_finfo,
                                358                 :                :                                             att->attcollation,
 1789 tgl@sss.pgh.pa.us         359                 :         105309 :                                             slot1->tts_values[attrnum],
                                360                 :         105309 :                                             slot2->tts_values[attrnum])))
 2642 peter_e@gmx.net           361                 :         105121 :             return false;
                                362                 :                :     }
                                363                 :                : 
                                364                 :            161 :     return true;
                                365                 :                : }
                                366                 :                : 
                                367                 :                : /*
                                368                 :                :  * Search the relation 'rel' for tuple using the sequential scan.
                                369                 :                :  *
                                370                 :                :  * If a matching tuple is found, lock it with lockmode, fill the slot with its
                                371                 :                :  * contents, and return true.  Return false otherwise.
                                372                 :                :  *
                                373                 :                :  * Note that this stops on the first matching tuple.
                                374                 :                :  *
                                375                 :                :  * This can obviously be quite slow on tables that have more than few rows.
                                376                 :                :  */
                                377                 :                : bool
                                378                 :            146 : RelationFindReplTupleSeq(Relation rel, LockTupleMode lockmode,
                                379                 :                :                          TupleTableSlot *searchslot, TupleTableSlot *outslot)
                                380                 :                : {
                                381                 :                :     TupleTableSlot *scanslot;
                                382                 :                :     TableScanDesc scan;
                                383                 :                :     SnapshotData snap;
                                384                 :                :     TypeCacheEntry **eq;
                                385                 :                :     TransactionId xwait;
                                386                 :                :     bool        found;
 1861 andres@anarazel.de        387                 :            146 :     TupleDesc   desc PG_USED_FOR_ASSERTS_ONLY = RelationGetDescr(rel);
                                388                 :                : 
 2642 peter_e@gmx.net           389         [ -  + ]:            146 :     Assert(equalTupleDescs(desc, outslot->tts_tupleDescriptor));
                                390                 :                : 
 1464 noah@leadboat.com         391                 :            146 :     eq = palloc0(sizeof(*eq) * outslot->tts_tupleDescriptor->natts);
                                392                 :                : 
                                393                 :                :     /* Start a heap scan. */
 2642 peter_e@gmx.net           394                 :            146 :     InitDirtySnapshot(snap);
 1861 andres@anarazel.de        395                 :            146 :     scan = table_beginscan(rel, &snap, 0, NULL);
                                396                 :            146 :     scanslot = table_slot_create(rel, NULL);
                                397                 :                : 
 2642 peter_e@gmx.net           398                 :UBC           0 : retry:
 2642 peter_e@gmx.net           399                 :CBC         146 :     found = false;
                                400                 :                : 
 1861 andres@anarazel.de        401                 :            146 :     table_rescan(scan, NULL);
                                402                 :                : 
                                403                 :                :     /* Try to find the tuple */
                                404         [ +  - ]:         105267 :     while (table_scan_getnextslot(scan, ForwardScanDirection, scanslot))
                                405                 :                :     {
 1464 noah@leadboat.com         406         [ +  + ]:         105267 :         if (!tuples_equal(scanslot, searchslot, eq))
 2642 peter_e@gmx.net           407                 :         105121 :             continue;
                                408                 :                : 
                                409                 :            146 :         found = true;
 1861 andres@anarazel.de        410                 :            146 :         ExecCopySlot(outslot, scanslot);
                                411                 :                : 
 2642 peter_e@gmx.net           412                 :            292 :         xwait = TransactionIdIsValid(snap.xmin) ?
                                413         [ -  + ]:            146 :             snap.xmin : snap.xmax;
                                414                 :                : 
                                415                 :                :         /*
                                416                 :                :          * If the tuple is locked, wait for locking transaction to finish and
                                417                 :                :          * retry.
                                418                 :                :          */
                                419         [ -  + ]:            146 :         if (TransactionIdIsValid(xwait))
                                420                 :                :         {
 2642 peter_e@gmx.net           421                 :UBC           0 :             XactLockTableWait(xwait, NULL, NULL, XLTW_None);
                                422                 :              0 :             goto retry;
                                423                 :                :         }
                                424                 :                : 
                                425                 :                :         /* Found our tuple and it's not locked */
 1532 alvherre@alvh.no-ip.      426                 :CBC         146 :         break;
                                427                 :                :     }
                                428                 :                : 
                                429                 :                :     /* Found tuple, try to lock it in the lockmode. */
 2642 peter_e@gmx.net           430         [ +  - ]:            146 :     if (found)
                                431                 :                :     {
                                432                 :                :         TM_FailureData tmfd;
                                433                 :                :         TM_Result   res;
                                434                 :                : 
                                435                 :            146 :         PushActiveSnapshot(GetLatestSnapshot());
                                436                 :                : 
 1788 andres@anarazel.de        437                 :            146 :         res = table_tuple_lock(rel, &(outslot->tts_tid), GetLatestSnapshot(),
                                438                 :                :                                outslot,
                                439                 :                :                                GetCurrentCommandId(false),
                                440                 :                :                                lockmode,
                                441                 :                :                                LockWaitBlock,
                                442                 :                :                                0 /* don't follow updates */ ,
                                443                 :                :                                &tmfd);
                                444                 :                : 
 2642 peter_e@gmx.net           445                 :            146 :         PopActiveSnapshot();
                                446                 :                : 
                                447   [ +  -  -  -  :            146 :         switch (res)
                                                 - ]
                                448                 :                :         {
 1849 andres@anarazel.de        449                 :            146 :             case TM_Ok:
 2642 peter_e@gmx.net           450                 :            146 :                 break;
 1849 andres@anarazel.de        451                 :UBC           0 :             case TM_Updated:
                                452                 :                :                 /* XXX: Improve handling here */
                                453         [ #  # ]:              0 :                 if (ItemPointerIndicatesMovedPartitions(&tmfd.ctid))
 2199                           454         [ #  # ]:              0 :                     ereport(LOG,
                                455                 :                :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                456                 :                :                              errmsg("tuple to be locked was already moved to another partition due to concurrent update, retrying")));
                                457                 :                :                 else
                                458         [ #  # ]:              0 :                     ereport(LOG,
                                459                 :                :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                460                 :                :                              errmsg("concurrent update, retrying")));
 2642 peter_e@gmx.net           461                 :              0 :                 goto retry;
 1849 andres@anarazel.de        462                 :              0 :             case TM_Deleted:
                                463                 :                :                 /* XXX: Improve handling here */
                                464         [ #  # ]:              0 :                 ereport(LOG,
                                465                 :                :                         (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                466                 :                :                          errmsg("concurrent delete, retrying")));
                                467                 :              0 :                 goto retry;
                                468                 :              0 :             case TM_Invisible:
 2642 peter_e@gmx.net           469         [ #  # ]:              0 :                 elog(ERROR, "attempted to lock invisible tuple");
                                470                 :                :                 break;
                                471                 :              0 :             default:
 1788 andres@anarazel.de        472         [ #  # ]:              0 :                 elog(ERROR, "unexpected table_tuple_lock status: %u", res);
                                473                 :                :                 break;
                                474                 :                :         }
                                475                 :                :     }
                                476                 :                : 
 1861 andres@anarazel.de        477                 :CBC         146 :     table_endscan(scan);
                                478                 :            146 :     ExecDropSingleTupleTableSlot(scanslot);
                                479                 :                : 
 2642 peter_e@gmx.net           480                 :            146 :     return found;
                                481                 :                : }
                                482                 :                : 
                                483                 :                : /*
                                484                 :                :  * Insert tuple represented in the slot to the relation, update the indexes,
                                485                 :                :  * and execute any constraints and per-row triggers.
                                486                 :                :  *
                                487                 :                :  * Caller is responsible for opening the indexes.
                                488                 :                :  */
                                489                 :                : void
 1278 heikki.linnakangas@i      490                 :          75743 : ExecSimpleRelationInsert(ResultRelInfo *resultRelInfo,
                                491                 :                :                          EState *estate, TupleTableSlot *slot)
                                492                 :                : {
 2524 bruce@momjian.us          493                 :          75743 :     bool        skip_tuple = false;
                                494                 :          75743 :     Relation    rel = resultRelInfo->ri_RelationDesc;
                                495                 :                : 
                                496                 :                :     /* For now we support only tables. */
 2642 peter_e@gmx.net           497         [ -  + ]:          75743 :     Assert(rel->rd_rel->relkind == RELKIND_RELATION);
                                498                 :                : 
                                499                 :          75743 :     CheckCmdReplicaIdentity(rel, CMD_INSERT);
                                500                 :                : 
                                501                 :                :     /* BEFORE ROW INSERT Triggers */
                                502         [ +  + ]:          75743 :     if (resultRelInfo->ri_TrigDesc &&
                                503         [ +  + ]:             19 :         resultRelInfo->ri_TrigDesc->trig_insert_before_row)
                                504                 :                :     {
 1874 andres@anarazel.de        505         [ +  + ]:              3 :         if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
 1789 tgl@sss.pgh.pa.us         506                 :              1 :             skip_tuple = true;  /* "do nothing" */
                                507                 :                :     }
                                508                 :                : 
 2642 peter_e@gmx.net           509         [ +  + ]:          75743 :     if (!skip_tuple)
                                510                 :                :     {
                                511                 :          75742 :         List       *recheckIndexes = NIL;
                                512                 :                : 
                                513                 :                :         /* Compute stored generated columns */
 1842 peter@eisentraut.org      514         [ +  + ]:          75742 :         if (rel->rd_att->constr &&
                                515         [ +  + ]:          45478 :             rel->rd_att->constr->has_generated_stored)
 1278 heikki.linnakangas@i      516                 :              4 :             ExecComputeStoredGenerated(resultRelInfo, estate, slot,
                                517                 :                :                                        CMD_INSERT);
                                518                 :                : 
                                519                 :                :         /* Check the constraints of the tuple */
 2642 peter_e@gmx.net           520         [ +  + ]:          75742 :         if (rel->rd_att->constr)
 2134 alvherre@alvh.no-ip.      521                 :          45478 :             ExecConstraints(resultRelInfo, slot, estate);
 1306 tgl@sss.pgh.pa.us         522         [ +  + ]:          75742 :         if (rel->rd_rel->relispartition)
 2134 alvherre@alvh.no-ip.      523                 :             60 :             ExecPartitionCheck(resultRelInfo, slot, estate, true);
                                524                 :                : 
                                525                 :                :         /* OK, store the tuple and create index entries for it */
    3 akorotkov@postgresql      526                 :          75742 :         simple_table_tuple_insert(resultRelInfo->ri_RelationDesc, slot);
                                527                 :                : 
                                528         [ +  + ]:          75742 :         if (resultRelInfo->ri_NumIndices > 0)
 1278 heikki.linnakangas@i      529                 :          55423 :             recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
                                530                 :                :                                                    slot, estate, false, false,
                                531                 :                :                                                    NULL, NIL, false);
                                532                 :                : 
                                533                 :                :         /* AFTER ROW INSERT Triggers */
 1874 andres@anarazel.de        534                 :          75734 :         ExecARInsertTriggers(estate, resultRelInfo, slot,
                                535                 :                :                              recheckIndexes, NULL);
                                536                 :                : 
                                537                 :                :         /*
                                538                 :                :          * XXX we should in theory pass a TransitionCaptureState object to the
                                539                 :                :          * above to capture transition tuples, but after statement triggers
                                540                 :                :          * don't actually get fired by replication yet anyway
                                541                 :                :          */
                                542                 :                : 
 2642 peter_e@gmx.net           543                 :          75734 :         list_free(recheckIndexes);
                                544                 :                :     }
                                545                 :          75735 : }
                                546                 :                : 
                                547                 :                : /*
                                548                 :                :  * Find the searchslot tuple and update it with data in the slot,
                                549                 :                :  * update the indexes, and execute any constraints and per-row triggers.
                                550                 :                :  *
                                551                 :                :  * Caller is responsible for opening the indexes.
                                552                 :                :  */
                                553                 :                : void
 1278 heikki.linnakangas@i      554                 :          31925 : ExecSimpleRelationUpdate(ResultRelInfo *resultRelInfo,
                                555                 :                :                          EState *estate, EPQState *epqstate,
                                556                 :                :                          TupleTableSlot *searchslot, TupleTableSlot *slot)
                                557                 :                : {
 2524 bruce@momjian.us          558                 :          31925 :     bool        skip_tuple = false;
                                559                 :          31925 :     Relation    rel = resultRelInfo->ri_RelationDesc;
 1849 andres@anarazel.de        560                 :          31925 :     ItemPointer tid = &(searchslot->tts_tid);
                                561                 :                : 
                                562                 :                :     /* For now we support only tables. */
 2642 peter_e@gmx.net           563         [ -  + ]:          31925 :     Assert(rel->rd_rel->relkind == RELKIND_RELATION);
                                564                 :                : 
                                565                 :          31925 :     CheckCmdReplicaIdentity(rel, CMD_UPDATE);
                                566                 :                : 
                                567                 :                :     /* BEFORE ROW UPDATE Triggers */
                                568         [ +  + ]:          31925 :     if (resultRelInfo->ri_TrigDesc &&
                                569         [ +  + ]:             10 :         resultRelInfo->ri_TrigDesc->trig_update_before_row)
                                570                 :                :     {
 1874 andres@anarazel.de        571         [ +  + ]:              3 :         if (!ExecBRUpdateTriggers(estate, epqstate, resultRelInfo,
                                572                 :                :                                   tid, NULL, slot, NULL, NULL))
 1789 tgl@sss.pgh.pa.us         573                 :              2 :             skip_tuple = true;  /* "do nothing" */
                                574                 :                :     }
                                575                 :                : 
 2642 peter_e@gmx.net           576         [ +  + ]:          31925 :     if (!skip_tuple)
                                577                 :                :     {
                                578                 :          31923 :         List       *recheckIndexes = NIL;
                                579                 :                :         TU_UpdateIndexes update_indexes;
                                580                 :                : 
                                581                 :                :         /* Compute stored generated columns */
 1842 peter@eisentraut.org      582         [ +  + ]:          31923 :         if (rel->rd_att->constr &&
                                583         [ +  + ]:          31880 :             rel->rd_att->constr->has_generated_stored)
 1278 heikki.linnakangas@i      584                 :              3 :             ExecComputeStoredGenerated(resultRelInfo, estate, slot,
                                585                 :                :                                        CMD_UPDATE);
                                586                 :                : 
                                587                 :                :         /* Check the constraints of the tuple */
 2642 peter_e@gmx.net           588         [ +  + ]:          31923 :         if (rel->rd_att->constr)
 2134 alvherre@alvh.no-ip.      589                 :          31880 :             ExecConstraints(resultRelInfo, slot, estate);
 1306 tgl@sss.pgh.pa.us         590         [ +  + ]:          31923 :         if (rel->rd_rel->relispartition)
 2134 alvherre@alvh.no-ip.      591                 :             11 :             ExecPartitionCheck(resultRelInfo, slot, estate, true);
                                592                 :                : 
 1788 andres@anarazel.de        593                 :          31923 :         simple_table_tuple_update(rel, tid, slot, estate->es_snapshot,
                                594                 :                :                                   &update_indexes);
                                595                 :                : 
  391 tomas.vondra@postgre      596   [ +  +  +  + ]:          31923 :         if (resultRelInfo->ri_NumIndices > 0 && (update_indexes != TU_None))
 1278 heikki.linnakangas@i      597                 :          20297 :             recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
                                598                 :                :                                                    slot, estate, true, false,
                                599                 :                :                                                    NULL, NIL,
                                600                 :                :                                                    (update_indexes == TU_Summarizing));
                                601                 :                : 
                                602                 :                :         /* AFTER ROW UPDATE Triggers */
 2642 peter_e@gmx.net           603                 :          31923 :         ExecARUpdateTriggers(estate, resultRelInfo,
                                604                 :                :                              NULL, NULL,
                                605                 :                :                              tid, NULL, slot,
                                606                 :                :                              recheckIndexes, NULL, false);
                                607                 :                : 
                                608                 :          31923 :         list_free(recheckIndexes);
                                609                 :                :     }
                                610                 :          31925 : }
                                611                 :                : 
                                612                 :                : /*
                                613                 :                :  * Find the searchslot tuple and delete it, and execute any constraints
                                614                 :                :  * and per-row triggers.
                                615                 :                :  *
                                616                 :                :  * Caller is responsible for opening the indexes.
                                617                 :                :  */
                                618                 :                : void
 1278 heikki.linnakangas@i      619                 :          40311 : ExecSimpleRelationDelete(ResultRelInfo *resultRelInfo,
                                620                 :                :                          EState *estate, EPQState *epqstate,
                                621                 :                :                          TupleTableSlot *searchslot)
                                622                 :                : {
 2524 bruce@momjian.us          623                 :          40311 :     bool        skip_tuple = false;
                                624                 :          40311 :     Relation    rel = resultRelInfo->ri_RelationDesc;
 1849 andres@anarazel.de        625                 :          40311 :     ItemPointer tid = &searchslot->tts_tid;
                                626                 :                : 
 2642 peter_e@gmx.net           627                 :          40311 :     CheckCmdReplicaIdentity(rel, CMD_DELETE);
                                628                 :                : 
                                629                 :                :     /* BEFORE ROW DELETE Triggers */
                                630         [ +  + ]:          40311 :     if (resultRelInfo->ri_TrigDesc &&
 2376 rhaas@postgresql.org      631         [ -  + ]:             10 :         resultRelInfo->ri_TrigDesc->trig_delete_before_row)
                                632                 :                :     {
 2642 peter_e@gmx.net           633                 :UBC           0 :         skip_tuple = !ExecBRDeleteTriggers(estate, epqstate, resultRelInfo,
  398 dean.a.rasheed@gmail      634                 :              0 :                                            tid, NULL, NULL, NULL, NULL);
                                635                 :                :     }
                                636                 :                : 
 2642 peter_e@gmx.net           637         [ +  - ]:CBC       40311 :     if (!skip_tuple)
                                638                 :                :     {
                                639                 :                :         /* OK, delete the tuple */
    3 akorotkov@postgresql      640                 :          40311 :         simple_table_tuple_delete(rel, tid, estate->es_snapshot);
                                641                 :                : 
                                642                 :                :         /* AFTER ROW DELETE Triggers */
 2642 peter_e@gmx.net           643                 :          40311 :         ExecARDeleteTriggers(estate, resultRelInfo,
                                644                 :                :                              tid, NULL, NULL, false);
                                645                 :                :     }
                                646                 :          40311 : }
                                647                 :                : 
                                648                 :                : /*
                                649                 :                :  * Check if command can be executed with current replica identity.
                                650                 :                :  */
                                651                 :                : void
                                652                 :         209955 : CheckCmdReplicaIdentity(Relation rel, CmdType cmd)
                                653                 :                : {
                                654                 :                :     PublicationDesc pubdesc;
                                655                 :                : 
                                656                 :                :     /*
                                657                 :                :      * Skip checking the replica identity for partitioned tables, because the
                                658                 :                :      * operations are actually performed on the leaf partitions.
                                659                 :                :      */
  607 akapila@postgresql.o      660         [ +  + ]:         209955 :     if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
                                661                 :         199138 :         return;
                                662                 :                : 
                                663                 :                :     /* We only need to do checks for UPDATE and DELETE. */
 2642 peter_e@gmx.net           664   [ +  +  +  + ]:         207315 :     if (cmd != CMD_UPDATE && cmd != CMD_DELETE)
                                665                 :         121422 :         return;
                                666                 :                : 
                                667                 :                :     /*
                                668                 :                :      * It is only safe to execute UPDATE/DELETE when all columns, referenced
                                669                 :                :      * in the row filters from publications which the relation is in, are
                                670                 :                :      * valid - i.e. when all referenced columns are part of REPLICA IDENTITY
                                671                 :                :      * or the table does not publish UPDATEs or DELETEs.
                                672                 :                :      *
                                673                 :                :      * XXX We could optimize it by first checking whether any of the
                                674                 :                :      * publications have a row filter for this relation. If not and relation
                                675                 :                :      * has replica identity then we can avoid building the descriptor but as
                                676                 :                :      * this happens only one time it doesn't seem worth the additional
                                677                 :                :      * complexity.
                                678                 :                :      */
  782 akapila@postgresql.o      679                 :          85893 :     RelationBuildPublicationDesc(rel, &pubdesc);
                                680   [ +  +  +  + ]:          85893 :     if (cmd == CMD_UPDATE && !pubdesc.rf_valid_for_update)
                                681         [ +  - ]:             30 :         ereport(ERROR,
                                682                 :                :                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                                683                 :                :                  errmsg("cannot update table \"%s\"",
                                684                 :                :                         RelationGetRelationName(rel)),
                                685                 :                :                  errdetail("Column used in the publication WHERE expression is not part of the replica identity.")));
  750 tomas.vondra@postgre      686   [ +  +  +  + ]:          85863 :     else if (cmd == CMD_UPDATE && !pubdesc.cols_valid_for_update)
                                687         [ +  - ]:             54 :         ereport(ERROR,
                                688                 :                :                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                                689                 :                :                  errmsg("cannot update table \"%s\"",
                                690                 :                :                         RelationGetRelationName(rel)),
                                691                 :                :                  errdetail("Column list used by the publication does not cover the replica identity.")));
  782 akapila@postgresql.o      692   [ +  +  -  + ]:          85809 :     else if (cmd == CMD_DELETE && !pubdesc.rf_valid_for_delete)
  782 akapila@postgresql.o      693         [ #  # ]:UBC           0 :         ereport(ERROR,
                                694                 :                :                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                                695                 :                :                  errmsg("cannot delete from table \"%s\"",
                                696                 :                :                         RelationGetRelationName(rel)),
                                697                 :                :                  errdetail("Column used in the publication WHERE expression is not part of the replica identity.")));
  750 tomas.vondra@postgre      698   [ +  +  -  + ]:CBC       85809 :     else if (cmd == CMD_DELETE && !pubdesc.cols_valid_for_delete)
  750 tomas.vondra@postgre      699         [ #  # ]:UBC           0 :         ereport(ERROR,
                                700                 :                :                 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                                701                 :                :                  errmsg("cannot delete from table \"%s\"",
                                702                 :                :                         RelationGetRelationName(rel)),
                                703                 :                :                  errdetail("Column list used by the publication does not cover the replica identity.")));
                                704                 :                : 
                                705                 :                :     /* If relation has replica identity we are always good. */
  782 akapila@postgresql.o      706         [ +  + ]:CBC       85809 :     if (OidIsValid(RelationGetReplicaIndex(rel)))
 2642 peter_e@gmx.net           707                 :          74870 :         return;
                                708                 :                : 
                                709                 :                :     /* REPLICA IDENTITY FULL is also good for UPDATE/DELETE. */
  750 tomas.vondra@postgre      710         [ +  + ]:          10939 :     if (rel->rd_rel->relreplident == REPLICA_IDENTITY_FULL)
                                711                 :            206 :         return;
                                712                 :                : 
                                713                 :                :     /*
                                714                 :                :      * This is UPDATE/DELETE and there is no replica identity.
                                715                 :                :      *
                                716                 :                :      * Check if the table publishes UPDATES or DELETES.
                                717                 :                :      */
  782 akapila@postgresql.o      718   [ +  +  +  + ]:          10733 :     if (cmd == CMD_UPDATE && pubdesc.pubactions.pubupdate)
 2642 peter_e@gmx.net           719         [ +  - ]:             48 :         ereport(ERROR,
                                720                 :                :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                721                 :                :                  errmsg("cannot update table \"%s\" because it does not have a replica identity and publishes updates",
                                722                 :                :                         RelationGetRelationName(rel)),
                                723                 :                :                  errhint("To enable updating the table, set REPLICA IDENTITY using ALTER TABLE.")));
  782 akapila@postgresql.o      724   [ +  +  -  + ]:          10685 :     else if (cmd == CMD_DELETE && pubdesc.pubactions.pubdelete)
 2642 peter_e@gmx.net           725         [ #  # ]:UBC           0 :         ereport(ERROR,
                                726                 :                :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                727                 :                :                  errmsg("cannot delete from table \"%s\" because it does not have a replica identity and publishes deletes",
                                728                 :                :                         RelationGetRelationName(rel)),
                                729                 :                :                  errhint("To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.")));
                                730                 :                : }
                                731                 :                : 
                                732                 :                : 
                                733                 :                : /*
                                734                 :                :  * Check if we support writing into specific relkind.
                                735                 :                :  *
                                736                 :                :  * The nspname and relname are only needed for error reporting.
                                737                 :                :  */
                                738                 :                : void
 2525 peter_e@gmx.net           739                 :CBC         810 : CheckSubscriptionRelkind(char relkind, const char *nspname,
                                740                 :                :                          const char *relname)
                                741                 :                : {
  738 tomas.vondra@postgre      742   [ +  +  -  + ]:            810 :     if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
 2525 peter_e@gmx.net           743         [ #  # ]:UBC           0 :         ereport(ERROR,
                                744                 :                :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                745                 :                :                  errmsg("cannot use relation \"%s.%s\" as logical replication target",
                                746                 :                :                         nspname, relname),
                                747                 :                :                  errdetail_relkind_not_supported(relkind)));
 2525 peter_e@gmx.net           748                 :CBC         810 : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622