| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /*------------------------------------------------------------------------- |
| * |
| * tid.c |
| * Functions for the built-in type tuple id |
| * |
| * Portions Copyright (c) 2006-2009, Greenplum inc |
| * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * $PostgreSQL: pgsql/src/backend/utils/adt/tid.c,v 1.63 2009/01/01 17:23:50 momjian Exp $ |
| * |
| * 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/tqual.h" |
| |
| #define PG_GETARG_ITEMPOINTER(n) DatumGetItemPointer(PG_GETARG_DATUM(n)) |
| #define PG_RETURN_ITEMPOINTER(x) return ItemPointerGetDatum(x) |
| |
| #define LDELIM '(' |
| #define RDELIM ')' |
| #define DELIM ',' |
| #define NTIDARGS 2 |
| |
| /* ---------------------------------------------------------------- |
| * tidin |
| * ---------------------------------------------------------------- |
| */ |
| Datum |
| tidin(PG_FUNCTION_ARGS) |
| { |
| char *str = PG_GETARG_CSTRING(0); |
| char *p, |
| *coord[NTIDARGS]; |
| int i; |
| 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) |
| ereport(ERROR, |
| (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
| errmsg("invalid input syntax for type tid: \"%s\"", |
| 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 tid: \"%s\"", |
| 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 tid: \"%s\"", |
| str))); |
| |
| offsetNumber = hold_offset; |
| |
| result = (ItemPointer) palloc(sizeof(ItemPointerData)); |
| |
| ItemPointerSet(result, blockNumber, offsetNumber); |
| |
| PG_RETURN_ITEMPOINTER(result); |
| } |
| |
| /* ---------------------------------------------------------------- |
| * tidout |
| * ---------------------------------------------------------------- |
| */ |
| Datum |
| tidout(PG_FUNCTION_ARGS) |
| { |
| ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); |
| BlockNumber blockNumber; |
| OffsetNumber offsetNumber; |
| char buf[32]; |
| |
| blockNumber = BlockIdGetBlockNumber(&(itemPtr->ip_blkid)); |
| offsetNumber = itemPtr->ip_posid; |
| |
| /* 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; |
| |
| pq_begintypsend(&buf); |
| pq_sendint(&buf, blockNumber, sizeof(blockNumber)); |
| pq_sendint(&buf, offsetNumber, sizeof(offsetNumber)); |
| PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); |
| } |
| |
| |
| /* ---------------------------------------------------------------- |
| * Conversion operators. |
| * ---------------------------------------------------------------- |
| */ |
| |
| Datum |
| tidtoi8(PG_FUNCTION_ARGS) /*CDB*/ |
| { |
| ItemPointer itemPtr = PG_GETARG_ITEMPOINTER(0); |
| BlockNumber blockNumber; |
| OffsetNumber offsetNumber; |
| |
| blockNumber = BlockIdGetBlockNumber(&(itemPtr->ip_blkid)); |
| offsetNumber = itemPtr->ip_posid; |
| |
| PG_RETURN_INT64(((int64)blockNumber << 16) + offsetNumber); |
| } |
| |
| |
| /***************************************************************************** |
| * PUBLIC ROUTINES * |
| *****************************************************************************/ |
| |
| Datum |
| tideq(PG_FUNCTION_ARGS) |
| { |
| ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); |
| ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); |
| |
| PG_RETURN_BOOL(ItemPointerCompare(arg1, arg2) == 0); |
| } |
| |
| Datum |
| tidne(PG_FUNCTION_ARGS) |
| { |
| ItemPointer arg1 = PG_GETARG_ITEMPOINTER(0); |
| ItemPointer arg2 = PG_GETARG_ITEMPOINTER(1); |
| |
| 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); |
| } |
| |
| |
| /* |
| * 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}; |
| |
| 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 (var->varno > 0 && var->varno < INNER && |
| 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; |
| } |
| |
| |
| /* |
| * This function originates from PostgreSQL, |
| * is currently not supported by GPDB - MPP-7886. |
| * The problem is that calling function |
| * heapam.c::heap_get_latest_tid below fails to return |
| * the current number of blocks for the examined relation |
| */ |
| |
| Datum |
| currtid_byreloid(PG_FUNCTION_ARGS) |
| { |
| Oid reloid = PG_GETARG_OID(0); |
| ItemPointer tid = PG_GETARG_ITEMPOINTER(1); |
| ItemPointer result; |
| Relation rel; |
| AclResult aclresult; |
| |
| /* |
| * Immediately inform client that the function is not supported |
| */ |
| |
| elog(ERROR, "Function currtid is not supported by GPDB"); |
| |
| |
| result = (ItemPointer) palloc(sizeof(ItemPointerData)); |
| if (!reloid) |
| { |
| *result = Current_last_tid; |
| PG_RETURN_ITEMPOINTER(result); |
| } |
| |
| 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); |
| heap_get_latest_tid(rel, SnapshotNow, result); |
| |
| heap_close(rel, AccessShareLock); |
| |
| PG_RETURN_ITEMPOINTER(result); |
| } |
| |
| |
| /* |
| * This function originates from PostgreSQL, |
| * is currently not supported by GPDB - MPP-7886. |
| * The problem is that calling function |
| * heapam.c::heap_get_latest_tid below fails to return |
| * the current number of blocks for the examined relation |
| */ |
| |
| Datum |
| currtid_byrelname(PG_FUNCTION_ARGS) |
| { |
| text *relname = PG_GETARG_TEXT_P(0); |
| ItemPointer tid = PG_GETARG_ITEMPOINTER(1); |
| ItemPointer result; |
| RangeVar *relrv; |
| Relation rel; |
| AclResult aclresult; |
| |
| /* |
| * Immediately inform client that the function is not supported |
| */ |
| |
| elog(ERROR, "Function currtid2 is not supported by GPDB"); |
| |
| 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)); |
| |
| if (rel->rd_rel->relkind == RELKIND_VIEW) |
| return currtid_for_view(rel, tid); |
| |
| result = (ItemPointer) palloc(sizeof(ItemPointerData)); |
| ItemPointerCopy(tid, result); |
| |
| heap_get_latest_tid(rel, SnapshotNow, result); |
| |
| heap_close(rel, AccessShareLock); |
| |
| PG_RETURN_ITEMPOINTER(result); |
| } |