1 /*------------------------------------------------------------------------- 2 * 3 * tid.c 4 * Functions for the built-in type tuple id 5 * 6 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group 7 * Portions Copyright (c) 1994, Regents of the University of California 8 * 9 * 10 * IDENTIFICATION 11 * src/backend/utils/adt/tid.c 12 * 13 * NOTES 14 * input routine largely stolen from boxin(). 15 * 16 *------------------------------------------------------------------------- 17 */ 18 #include "postgres.h" 19 20 #include <math.h> 21 #include <limits.h> 22 23 #include "access/heapam.h" 24 #include "access/sysattr.h" 25 #include "catalog/namespace.h" 26 #include "catalog/pg_type.h" 27 #include "libpq/pqformat.h" 28 #include "miscadmin.h" 29 #include "parser/parsetree.h" 30 #include "utils/acl.h" 31 #include "utils/builtins.h" 32 #include "utils/rel.h" 33 #include "utils/snapmgr.h" 34 #include "utils/tqual.h" 35 #include "utils/varlena.h" 36 37 38 #define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X)) 39 #define ItemPointerGetDatum(X) PointerGetDatum(X) 40 #define PG_GETARG_ITEMPOINTER(n) DatumGetItemPointer(PG_GETARG_DATUM(n)) 41 #define PG_RETURN_ITEMPOINTER(x) return ItemPointerGetDatum(x) 42 43 #define LDELIM '(' 44 #define RDELIM ')' 45 #define DELIM ',' 46 #define NTIDARGS 2 47 48 /* ---------------------------------------------------------------- 49 * tidin 50 * ---------------------------------------------------------------- 51 */ 52 Datum 53 tidin(PG_FUNCTION_ARGS) 54 { 55 char *str = PG_GETARG_CSTRING(0); 56 char *p, 57 *coord[NTIDARGS]; 58 int i; 59 ItemPointer result; 60 BlockNumber blockNumber; 61 OffsetNumber offsetNumber; 62 char *badp; 63 int hold_offset; 64 65 for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++) 66 if (*p == DELIM || (*p == LDELIM && !i)) 67 coord[i++] = p + 1; 68 69 if (i < NTIDARGS) 70 ereport(ERROR, 71 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 72 errmsg("invalid input syntax for type %s: \"%s\"", 73 "tid", str))); 74 75 errno = 0; 76 blockNumber = strtoul(coord[0], &badp, 10); 77 if (errno || *badp != DELIM) 78 ereport(ERROR, 79 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 80 errmsg("invalid input syntax for type %s: \"%s\"", 81 "tid", str))); 82 83 hold_offset = strtol(coord[1], &badp, 10); 84 if (errno || *badp != RDELIM || 85 hold_offset > USHRT_MAX || hold_offset < 0) 86 ereport(ERROR, 87 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), 88 errmsg("invalid input syntax for type %s: \"%s\"", 89 "tid", str))); 90 91 offsetNumber = hold_offset; 92 93 result = (ItemPointer) palloc(sizeof(ItemPointerData)); 94 95 ItemPointerSet(result, blockNumber, offsetNumber); 96 97 PG_RETURN_ITEMPOINTER(result); 98 } 99 100 /* ---------------------------------------------------------------- 101 * tidout 102 * ---------------------------------------------------------------- 103 */ 104 Datum 105 tidout(PG_FUNCTION_ARGS) 106 { 107 ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); 108 BlockNumber blockNumber; 109 OffsetNumber offsetNumber; 110 char buf[32]; 111 112 blockNumber = ItemPointerGetBlockNumberNoCheck(itemPtr); 113 offsetNumber = ItemPointerGetOffsetNumberNoCheck(itemPtr); 114 115 /* Perhaps someday we should output this as a record. */ 116 snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber); 117 118 PG_RETURN_CSTRING(pstrdup(buf)); 119 } 120 121 /* 122 * tidrecv - converts external binary format to tid 123 */ 124 Datum 125 tidrecv(PG_FUNCTION_ARGS) 126 { 127 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); 128 ItemPointer result; 129 BlockNumber blockNumber; 130 OffsetNumber offsetNumber; 131 132 blockNumber = pq_getmsgint(buf, sizeof(blockNumber)); 133 offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber)); 134 135 result = (ItemPointer) palloc(sizeof(ItemPointerData)); 136 137 ItemPointerSet(result, blockNumber, offsetNumber); 138 139 PG_RETURN_ITEMPOINTER(result); 140 } 141 142 /* 143 * tidsend - converts tid to binary format 144 */ 145 Datum 146 tidsend(PG_FUNCTION_ARGS) 147 { 148 ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); 149 StringInfoData buf; 150 151 pq_begintypsend(&buf); 152 pq_sendint32(&buf, ItemPointerGetBlockNumberNoCheck(itemPtr)); 153 pq_sendint16(&buf, ItemPointerGetOffsetNumberNoCheck(itemPtr)); 154 PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); 155 } 156 157 /***************************************************************************** 158 * PUBLIC ROUTINES * 159 *****************************************************************************/ 160 161 Datum 162 tideq(PG_FUNCTION_ARGS) 163 { 164 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 165 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 166 167 PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0); 168 } 169 170 Datum 171 tidne(PG_FUNCTION_ARGS) 172 { 173 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 174 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 175 176 PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0); 177 } 178 179 Datum 180 tidlt(PG_FUNCTION_ARGS) 181 { 182 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 183 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 184 185 PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0); 186 } 187 188 Datum 189 tidle(PG_FUNCTION_ARGS) 190 { 191 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 192 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 193 194 PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0); 195 } 196 197 Datum 198 tidgt(PG_FUNCTION_ARGS) 199 { 200 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 201 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 202 203 PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0); 204 } 205 206 Datum 207 tidge(PG_FUNCTION_ARGS) 208 { 209 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 210 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 211 212 PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0); 213 } 214 215 Datum 216 bttidcmp(PG_FUNCTION_ARGS) 217 { 218 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 219 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 220 221 PG_RETURN_INT32(ItemPointerCompare(arg1, arg2)); 222 } 223 224 Datum 225 tidlarger(PG_FUNCTION_ARGS) 226 { 227 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 228 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 229 230 PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2); 231 } 232 233 Datum 234 tidsmaller(PG_FUNCTION_ARGS) 235 { 236 ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); 237 ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); 238 239 PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2); 240 } 241 242 243 /* 244 * Functions to get latest tid of a specified tuple. 245 * 246 * Maybe these implementations should be moved to another place 247 */ 248 249 static ItemPointerData Current_last_tid = {{0, 0}, 0}; 250 251 void 252 setLastTid(const ItemPointer tid) 253 { 254 Current_last_tid = *tid; 255 } 256 257 /* 258 * Handle CTIDs of views. 259 * CTID should be defined in the view and it must 260 * correspond to the CTID of a base relation. 261 */ 262 static Datum 263 currtid_for_view(Relation viewrel, ItemPointer tid) 264 { 265 TupleDesc att = RelationGetDescr(viewrel); 266 RuleLock *rulelock; 267 RewriteRule *rewrite; 268 int i, 269 natts = att->natts, 270 tididx = -1; 271 272 for (i = 0; i < natts; i++) 273 { 274 Form_pg_attribute attr = TupleDescAttr(att, i); 275 276 if (strcmp(NameStr(attr->attname), "ctid") == 0) 277 { 278 if (attr->atttypid != TIDOID) 279 elog(ERROR, "ctid isn't of type TID"); 280 tididx = i; 281 break; 282 } 283 } 284 if (tididx < 0) 285 elog(ERROR, "currtid cannot handle views with no CTID"); 286 rulelock = viewrel->rd_rules; 287 if (!rulelock) 288 elog(ERROR, "the view has no rules"); 289 for (i = 0; i < rulelock->numLocks; i++) 290 { 291 rewrite = rulelock->rules[i]; 292 if (rewrite->event == CMD_SELECT) 293 { 294 Query *query; 295 TargetEntry *tle; 296 297 if (list_length(rewrite->actions) != 1) 298 elog(ERROR, "only one select rule is allowed in views"); 299 query = (Query *) linitial(rewrite->actions); 300 tle = get_tle_by_resno(query->targetList, tididx + 1); 301 if (tle && tle->expr && IsA(tle->expr, Var)) 302 { 303 Var *var = (Var *) tle->expr; 304 RangeTblEntry *rte; 305 306 if (!IS_SPECIAL_VARNO(var->varno) && 307 var->varattno == SelfItemPointerAttributeNumber) 308 { 309 rte = rt_fetch(var->varno, query->rtable); 310 if (rte) 311 { 312 Datum result; 313 314 result = DirectFunctionCall2(currtid_byreloid, 315 ObjectIdGetDatum(rte->relid), 316 PointerGetDatum(tid)); 317 heap_close(viewrel, AccessShareLock); 318 return result; 319 } 320 } 321 } 322 break; 323 } 324 } 325 elog(ERROR, "currtid cannot handle this view"); 326 return (Datum) 0; 327 } 328 329 Datum 330 currtid_byreloid(PG_FUNCTION_ARGS) 331 { 332 Oid reloid = PG_GETARG_OID(0); 333 ItemPointer tid = PG_GETARG_ITEMPOINTER(1); 334 ItemPointer result; 335 Relation rel; 336 AclResult aclresult; 337 Snapshot snapshot; 338 339 result = (ItemPointer) palloc(sizeof(ItemPointerData)); 340 if (!reloid) 341 { 342 *result = Current_last_tid; 343 PG_RETURN_ITEMPOINTER(result); 344 } 345 346 rel = heap_open(reloid, AccessShareLock); 347 348 aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), 349 ACL_SELECT); 350 if (aclresult != ACLCHECK_OK) 351 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), 352 RelationGetRelationName(rel)); 353 354 if (rel->rd_rel->relkind == RELKIND_VIEW) 355 return currtid_for_view(rel, tid); 356 357 ItemPointerCopy(tid, result); 358 359 snapshot = RegisterSnapshot(GetLatestSnapshot()); 360 heap_get_latest_tid(rel, snapshot, result); 361 UnregisterSnapshot(snapshot); 362 363 heap_close(rel, AccessShareLock); 364 365 PG_RETURN_ITEMPOINTER(result); 366 } 367 368 Datum 369 currtid_byrelname(PG_FUNCTION_ARGS) 370 { 371 text *relname = PG_GETARG_TEXT_PP(0); 372 ItemPointer tid = PG_GETARG_ITEMPOINTER(1); 373 ItemPointer result; 374 RangeVar *relrv; 375 Relation rel; 376 AclResult aclresult; 377 Snapshot snapshot; 378 379 relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); 380 rel = heap_openrv(relrv, AccessShareLock); 381 382 aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), 383 ACL_SELECT); 384 if (aclresult != ACLCHECK_OK) 385 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), 386 RelationGetRelationName(rel)); 387 388 if (rel->rd_rel->relkind == RELKIND_VIEW) 389 return currtid_for_view(rel, tid); 390 391 result = (ItemPointer) palloc(sizeof(ItemPointerData)); 392 ItemPointerCopy(tid, result); 393 394 snapshot = RegisterSnapshot(GetLatestSnapshot()); 395 heap_get_latest_tid(rel, snapshot, result); 396 UnregisterSnapshot(snapshot); 397 398 heap_close(rel, AccessShareLock); 399 400 PG_RETURN_ITEMPOINTER(result); 401 } 402