* tid.c
* Functions for the built-in type tuple id
*
- * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.27 2001/09/17 00:29:10 tgl Exp $
+ * src/backend/utils/adt/tid.c
*
* NOTES
* input routine largely stolen from boxin().
*
*-------------------------------------------------------------------------
*/
-
#include "postgres.h"
+#include <math.h>
+#include <limits.h>
+
#include "access/heapam.h"
+#include "access/sysattr.h"
+#include "catalog/namespace.h"
+#include "catalog/pg_type.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "parser/parsetree.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
+#include "utils/rel.h"
+#include "utils/snapmgr.h"
+#include "utils/tqual.h"
+#include "utils/varlena.h"
+
#define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X))
#define ItemPointerGetDatum(X) PointerGetDatum(X)
ItemPointer result;
BlockNumber blockNumber;
OffsetNumber offsetNumber;
+ char *badp;
+ int hold_offset;
for (i = 0, p = str; *p && i < NTIDARGS && *p != RDELIM; p++)
if (*p == DELIM || (*p == LDELIM && !i))
coord[i++] = p + 1;
if (i < NTIDARGS)
- elog(ERROR, "invalid tid format: '%s'", str);
-
- blockNumber = (BlockNumber) atoi(coord[0]);
- offsetNumber = (OffsetNumber) atoi(coord[1]);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "tid", str)));
+
+ errno = 0;
+ blockNumber = strtoul(coord[0], &badp, 10);
+ if (errno || *badp != DELIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "tid", str)));
+
+ hold_offset = strtol(coord[1], &badp, 10);
+ if (errno || *badp != RDELIM ||
+ hold_offset > USHRT_MAX || hold_offset < 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for type %s: \"%s\"",
+ "tid", str)));
+
+ offsetNumber = hold_offset;
result = (ItemPointer) palloc(sizeof(ItemPointerData));
tidout(PG_FUNCTION_ARGS)
{
ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
- BlockId blockId;
BlockNumber blockNumber;
OffsetNumber offsetNumber;
char buf[32];
- static char *invalidTid = "()";
- if (!ItemPointerIsValid(itemPtr))
- PG_RETURN_CSTRING(pstrdup(invalidTid));
+ blockNumber = BlockIdGetBlockNumber(&(itemPtr->ip_blkid));
+ offsetNumber = itemPtr->ip_posid;
- blockId = &(itemPtr->ip_blkid);
+ /* Perhaps someday we should output this as a record. */
+ snprintf(buf, sizeof(buf), "(%u,%u)", blockNumber, offsetNumber);
+
+ PG_RETURN_CSTRING(pstrdup(buf));
+}
+
+/*
+ * tidrecv - converts external binary format to tid
+ */
+Datum
+tidrecv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ ItemPointer result;
+ BlockNumber blockNumber;
+ OffsetNumber offsetNumber;
+
+ blockNumber = pq_getmsgint(buf, sizeof(blockNumber));
+ offsetNumber = pq_getmsgint(buf, sizeof(offsetNumber));
+
+ result = (ItemPointer) palloc(sizeof(ItemPointerData));
+ ItemPointerSet(result, blockNumber, offsetNumber);
+
+ PG_RETURN_ITEMPOINTER(result);
+}
+
+/*
+ * tidsend - converts tid to binary format
+ */
+Datum
+tidsend(PG_FUNCTION_ARGS)
+{
+ ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0);
+ BlockId blockId;
+ BlockNumber blockNumber;
+ OffsetNumber offsetNumber;
+ StringInfoData buf;
+
+ blockId = &(itemPtr->ip_blkid);
blockNumber = BlockIdGetBlockNumber(blockId);
offsetNumber = itemPtr->ip_posid;
- sprintf(buf, "(%d,%d)", (int) blockNumber, (int) offsetNumber);
-
- PG_RETURN_CSTRING(pstrdup(buf));
+ pq_begintypsend(&buf);
+ pq_sendint(&buf, blockNumber, sizeof(blockNumber));
+ pq_sendint(&buf, offsetNumber, sizeof(offsetNumber));
+ PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*****************************************************************************
ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
- PG_RETURN_BOOL(BlockIdGetBlockNumber(&(arg1->ip_blkid)) ==
- BlockIdGetBlockNumber(&(arg2->ip_blkid)) &&
- arg1->ip_posid == arg2->ip_posid);
+ PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0);
}
-#ifdef NOT_USED
Datum
tidne(PG_FUNCTION_ARGS)
{
ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
- PG_RETURN_BOOL(BlockIdGetBlockNumber(&(arg1->ip_blkid)) !=
- BlockIdGetBlockNumber(&(arg2->ip_blkid)) ||
- arg1->ip_posid != arg2->ip_posid);
+ PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) != 0);
+}
+
+Datum
+tidlt(PG_FUNCTION_ARGS)
+{
+ ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
+ ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
+
+ PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) < 0);
+}
+
+Datum
+tidle(PG_FUNCTION_ARGS)
+{
+ ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
+ ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
+
+ PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) <= 0);
+}
+
+Datum
+tidgt(PG_FUNCTION_ARGS)
+{
+ ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
+ ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
+
+ PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) > 0);
+}
+
+Datum
+tidge(PG_FUNCTION_ARGS)
+{
+ ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
+ ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
+
+ PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) >= 0);
+}
+
+Datum
+bttidcmp(PG_FUNCTION_ARGS)
+{
+ ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
+ ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
+
+ PG_RETURN_INT32(ItemPointerCompare(arg1, arg2));
+}
+
+Datum
+tidlarger(PG_FUNCTION_ARGS)
+{
+ ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
+ ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
+
+ PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) >= 0 ? arg1 : arg2);
+}
+
+Datum
+tidsmaller(PG_FUNCTION_ARGS)
+{
+ ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0);
+ ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1);
+
+ PG_RETURN_ITEMPOINTER(ItemPointerCompare(arg1, arg2) <= 0 ? arg1 : arg2);
}
-#endif
/*
* Functions to get latest tid of a specified tuple.
* Maybe these implementations should be moved to another place
*/
-static ItemPointerData Current_last_tid = { {0, 0}, 0};
+static ItemPointerData Current_last_tid = {{0, 0}, 0};
void
setLastTid(const ItemPointer tid)
Current_last_tid = *tid;
}
+/*
+ * Handle CTIDs of views.
+ * CTID should be defined in the view and it must
+ * correspond to the CTID of a base relation.
+ */
+static Datum
+currtid_for_view(Relation viewrel, ItemPointer tid)
+{
+ TupleDesc att = RelationGetDescr(viewrel);
+ RuleLock *rulelock;
+ RewriteRule *rewrite;
+ int i,
+ natts = att->natts,
+ tididx = -1;
+
+ for (i = 0; i < natts; i++)
+ {
+ if (strcmp(NameStr(att->attrs[i]->attname), "ctid") == 0)
+ {
+ if (att->attrs[i]->atttypid != TIDOID)
+ elog(ERROR, "ctid isn't of type TID");
+ tididx = i;
+ break;
+ }
+ }
+ if (tididx < 0)
+ elog(ERROR, "currtid cannot handle views with no CTID");
+ rulelock = viewrel->rd_rules;
+ if (!rulelock)
+ elog(ERROR, "the view has no rules");
+ for (i = 0; i < rulelock->numLocks; i++)
+ {
+ rewrite = rulelock->rules[i];
+ if (rewrite->event == CMD_SELECT)
+ {
+ Query *query;
+ TargetEntry *tle;
+
+ if (list_length(rewrite->actions) != 1)
+ elog(ERROR, "only one select rule is allowed in views");
+ query = (Query *) linitial(rewrite->actions);
+ tle = get_tle_by_resno(query->targetList, tididx + 1);
+ if (tle && tle->expr && IsA(tle->expr, Var))
+ {
+ Var *var = (Var *) tle->expr;
+ RangeTblEntry *rte;
+
+ if (!IS_SPECIAL_VARNO(var->varno) &&
+ var->varattno == SelfItemPointerAttributeNumber)
+ {
+ rte = rt_fetch(var->varno, query->rtable);
+ if (rte)
+ {
+ heap_close(viewrel, AccessShareLock);
+ return DirectFunctionCall2(currtid_byreloid, ObjectIdGetDatum(rte->relid), PointerGetDatum(tid));
+ }
+ }
+ }
+ break;
+ }
+ }
+ elog(ERROR, "currtid cannot handle this view");
+ return (Datum) 0;
+}
+
Datum
currtid_byreloid(PG_FUNCTION_ARGS)
{
ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
ItemPointer result;
Relation rel;
+ AclResult aclresult;
+ Snapshot snapshot;
result = (ItemPointer) palloc(sizeof(ItemPointerData));
- if (!reloid)
- {
- *result = Current_last_tid;
- PG_RETURN_ITEMPOINTER(result);
- }
- ItemPointerCopy(tid, result);
- if ((rel = heap_open(reloid, AccessShareLock)) != NULL)
+ if (!reloid)
{
- heap_get_latest_tid(rel, SnapshotNow, result);
- heap_close(rel, AccessShareLock);
+ *result = Current_last_tid;
+ PG_RETURN_ITEMPOINTER(result);
}
- else
- elog(ERROR, "Relation %u not found", reloid);
+
+ rel = heap_open(reloid, AccessShareLock);
+
+ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
+ ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_CLASS,
+ RelationGetRelationName(rel));
+
+ if (rel->rd_rel->relkind == RELKIND_VIEW)
+ return currtid_for_view(rel, tid);
+
+ ItemPointerCopy(tid, result);
+
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ heap_get_latest_tid(rel, snapshot, result);
+ UnregisterSnapshot(snapshot);
+
+ heap_close(rel, AccessShareLock);
PG_RETURN_ITEMPOINTER(result);
}
Datum
currtid_byrelname(PG_FUNCTION_ARGS)
{
- text *relname = PG_GETARG_TEXT_P(0);
+ text *relname = PG_GETARG_TEXT_PP(0);
ItemPointer tid = PG_GETARG_ITEMPOINTER(1);
ItemPointer result;
- char *str;
+ RangeVar *relrv;
Relation rel;
+ AclResult aclresult;
+ Snapshot snapshot;
+
+ relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
+ rel = heap_openrv(relrv, AccessShareLock);
+
+ aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(),
+ ACL_SELECT);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_CLASS,
+ RelationGetRelationName(rel));
- str = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(relname)));
+ if (rel->rd_rel->relkind == RELKIND_VIEW)
+ return currtid_for_view(rel, tid);
result = (ItemPointer) palloc(sizeof(ItemPointerData));
ItemPointerCopy(tid, result);
- if ((rel = heap_openr(str, AccessShareLock)) != NULL)
- {
- heap_get_latest_tid(rel, SnapshotNow, result);
- heap_close(rel, AccessShareLock);
- }
- else
- elog(ERROR, "Relation %s not found", str);
- pfree(str);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ heap_get_latest_tid(rel, snapshot, result);
+ UnregisterSnapshot(snapshot);
+
+ heap_close(rel, AccessShareLock);
PG_RETURN_ITEMPOINTER(result);
}