/* * leadlag_common * common operation of lead() and lag() * For lead() forward is true, whereas for lag() it is false. * withoffset indicates we have an offset second argument. * withdefault indicates we have a default third argument. */ static Datum leadlag_common(FunctionCallInfo fcinfo, bool forward, bool withoffset, bool withdefault) { WindowObject winobj = PG_WINDOW_OBJECT(); int32 offset; bool const_offset; Datum result; bool isnull; bool isout; if (withoffset) { offset = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull)); if (isnull) PG_RETURN_NULL(); const_offset = get_fn_expr_arg_stable(fcinfo->flinfo, 1); } else { offset = 1; const_offset = true; } result = WinGetFuncArgInPartition(winobj, 0, (forward ? offset : -offset), WINDOW_SEEK_CURRENT, const_offset, &isnull, &isout); if (isout) { /* * target row is out of the partition; supply default value if * provided. otherwise it'll stay NULL */ if (withdefault) result = WinGetFuncArgCurrent(winobj, 2, &isnull); } if (isnull) PG_RETURN_NULL(); PG_RETURN_DATUM(result); }
Datum c_overpaid(PG_FUNCTION_ARGS) { HeapTupleHeader t = PG_GETARG_HEAPTUPLEHEADER(0); int32 limit = PG_GETARG_INT32(1); bool isnull; int32 salary; salary = DatumGetInt32(GetAttributeByName(t, "salary", &isnull)); if (isnull) PG_RETURN_BOOL(false); /* * Alternatively, we might prefer to do PG_RETURN_NULL() for null salary */ PG_RETURN_BOOL(salary > limit); }
/* * Compare two keys of the same index column */ int ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, GinNullCategory categorya, Datum b, GinNullCategory categoryb) { /* if not of same null category, sort by that first */ if (categorya != categoryb) return (categorya < categoryb) ? -1 : 1; /* all null items in same category are equal */ if (categorya != GIN_CAT_NORM_KEY) return 0; /* both not null, so safe to call the compareFn */ return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1], ginstate->supportCollation[attnum - 1], a, b)); }
Datum on_partitions_updated(PG_FUNCTION_ARGS) { Oid relid; PartRelationInfo *prel; /* Parent relation oid */ relid = DatumGetInt32(PG_GETARG_DATUM(0)); prel = get_pathman_relation_info(relid, NULL); if (prel != NULL) { LWLockAcquire(pmstate->load_config_lock, LW_EXCLUSIVE); remove_relation_info(relid); load_relations_hashtable(false); LWLockRelease(pmstate->load_config_lock); } PG_RETURN_NULL(); }
/* * CompareShardIntervals acts as a helper function to compare two shard intervals * by their minimum values, using the value's type comparison function. * * If a shard interval does not have min/max value, it's treated as being greater * than the other. */ int CompareShardIntervals(const void *leftElement, const void *rightElement, FmgrInfo *typeCompareFunction) { ShardInterval *leftShardInterval = *((ShardInterval **) leftElement); ShardInterval *rightShardInterval = *((ShardInterval **) rightElement); Datum leftDatum = 0; Datum rightDatum = 0; Datum comparisonDatum = 0; int comparisonResult = 0; Assert(typeCompareFunction != NULL); /* * Left element should be treated as the greater element in case it doesn't * have min or max values. */ if (!leftShardInterval->minValueExists || !leftShardInterval->maxValueExists) { comparisonResult = 1; return comparisonResult; } /* * Right element should be treated as the greater element in case it doesn't * have min or max values. */ if (!rightShardInterval->minValueExists || !rightShardInterval->maxValueExists) { comparisonResult = -1; return comparisonResult; } /* if both shard interval have min/max values, calculate the comparison result */ leftDatum = leftShardInterval->minValue; rightDatum = rightShardInterval->minValue; comparisonDatum = CompareCall2(typeCompareFunction, leftDatum, rightDatum); comparisonResult = DatumGetInt32(comparisonDatum); return comparisonResult; }
void GpRelationNode_GetValues( Datum *values, Oid *relfilenodeOid, int32 *segmentFileNum, int64 *createMirrorDataLossTrackingSessionNum, ItemPointer persistentTid, int64 *persistentSerialNum) { *relfilenodeOid = DatumGetObjectId(values[Anum_gp_relation_node_relfilenode_oid - 1]); *segmentFileNum = DatumGetInt32(values[Anum_gp_relation_node_segment_file_num - 1]); *createMirrorDataLossTrackingSessionNum = DatumGetInt64(values[Anum_gp_relation_node_create_mirror_data_loss_tracking_session_num - 1]); *persistentTid = *((ItemPointer) DatumGetPointer(values[Anum_gp_relation_node_persistent_tid - 1])); *persistentSerialNum = DatumGetInt64(values[Anum_gp_relation_node_persistent_serial_num - 1]); }
//cs3223 fnv1 32bit void hashMeth2(uint32 keyval, HashJoinTable hashtable) { uint32 hashkey = 8192*bitvector_size; long keyV; unsigned int hash = OFFSET32; int i=0; //printf("size of keyval: %d\n", sizeof(keyval)); int numOctal = sizeof(keyval); if (numOctal < 8) keyV = DatumGetInt32(keyval); else keyV = DatumGetInt64(keyval); for (i=0;i<numOctal;i++){ hash = hash ^ (keyV & 0x000000ff); hash = hash * PRIME32; keyV = keyV >> 8; } // Mapping method hash = hash % hashkey; setKbit(hashtable->bitvector, hash); }
int HdfsFreeFileInfo(FsysName protocol, hdfsFileInfo * info, int numEntries) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_FREEFILEINFO); fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_fileinfo = info; fsysUdf.fsys_fileinfonum = numEntries; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); Datum d = FunctionCallInvoke(&fcinfo); return DatumGetInt32(d); }
/* * Checks if range overlaps with existing partitions. * Returns TRUE if overlaps and FALSE otherwise. */ Datum check_overlap(PG_FUNCTION_ARGS) { int parent_oid = DatumGetInt32(PG_GETARG_DATUM(0)); Datum p1 = PG_GETARG_DATUM(1); Oid p1_type = get_fn_expr_argtype(fcinfo->flinfo, 1); Datum p2 = PG_GETARG_DATUM(2); Oid p2_type = get_fn_expr_argtype(fcinfo->flinfo, 2); PartRelationInfo *prel; RangeRelation *rangerel; RangeEntry *ranges; FmgrInfo cmp_func_1; FmgrInfo cmp_func_2; int i; bool byVal; prel = get_pathman_relation_info(parent_oid, NULL); rangerel = get_pathman_range_relation(parent_oid, NULL); if (!prel || !rangerel || prel->parttype != PT_RANGE) PG_RETURN_NULL(); /* comparison functions */ cmp_func_1 = *get_cmp_func(p1_type, prel->atttype); cmp_func_2 = *get_cmp_func(p2_type, prel->atttype); byVal = rangerel->by_val; ranges = (RangeEntry *) dsm_array_get_pointer(&rangerel->ranges); for (i=0; i<rangerel->ranges.length; i++) { int c1 = FunctionCall2(&cmp_func_1, p1, PATHMAN_GET_DATUM(ranges[i].max, byVal)); int c2 = FunctionCall2(&cmp_func_2, p2, PATHMAN_GET_DATUM(ranges[i].min, byVal)); if (c1 < 0 && c2 > 0) PG_RETURN_BOOL(true); } PG_RETURN_BOOL(false); }
long getDocumentCount() { int spiResultCode; Datum countDatum; bool isNull; long count; SPI_connect(); spiResultCode = SPI_execute("SELECT COUNT(*)::integer FROM CardVectors", 1, 1); if(spiResultCode == SPI_OK_SELECT) { countDatum = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isNull); count = DatumGetInt32(countDatum); } else { ereport(ERROR, (errmsg("Unable to query CardVectors table. Does it exist?"))); count = -1; } SPI_finish(); return count; }
/* * AuxiliaryProcKill() -- Cut-down version of ProcKill for auxiliary * processes (bgwriter, etc). The PGPROC and sema are not released, only * marked as not-in-use. */ static void AuxiliaryProcKill(int code, Datum arg) { int proctype = DatumGetInt32(arg); PGPROC *auxproc; Assert(proctype >= 0 && proctype < NUM_AUXILIARY_PROCS); auxproc = &AuxiliaryProcs[proctype]; Assert(MyProc == auxproc); /* Release any LW locks I am holding (see notes above) */ LWLockReleaseAll(); /* Update shared estimate of spins_per_delay */ update_spins_per_delay(); if (code == 0 || code == 1) { MyProc->postmasterResetRequired = false; } /* * If the parent process of this auxiliary process does not exist, * we want to set the proc array entry free here. The postmaster may * not own this process, so that it can't set the entry free. This * could happen to the filerep subprocesses when the filerep main * process dies unexpectedly. */ if (!ParentProcIsAlive()) { MyProc->pid = 0; MyProc->postmasterResetRequired = true; } /* PGPROC struct isn't mine anymore */ MyProc = NULL; lockHolderProcPtr = NULL; }
size_t append_datum(char* buf, Datum val, bool isnull, Oid typeoid) { HeapTuple typeTup; Form_pg_type typeStruct; FmgrInfo tmp_flinfo; char *str; size_t len; typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { elog(ERROR, "Cache lookup failed for %u", typeoid); } typeStruct = (Form_pg_type)GETSTRUCT(typeTup); if (typeStruct->typtype != 'b') { // Non-basic type elog(ERROR, "Don't support non-basic types (%s)", format_type_be(typeoid)); } fmgr_info_cxt(typeStruct->typoutput, &tmp_flinfo, CurTransactionContext); ReleaseSysCache(typeTup); if (!isnull) { if (typeoid == INT4OID) { *((int*)buf) = DatumGetInt32(val); return 4; } SPI_push(); str = OutputFunctionCall(&tmp_flinfo, val); SPI_pop(); len = strlen(str); strncpy(buf, str, len); return len; } return 0; }
static void fetch_restrict(HeapTuple *tuple, TupleDesc *tupdesc, restrict_columns_t *restrict_columns, restrict_t *rest) { Datum binval; bool isnull; int t; for(t=0; t<MAX_RULE_LENGTH;++t) rest->via[t] = -1; binval = SPI_getbinval(*tuple, *tupdesc, restrict_columns->target_id, &isnull); if (isnull) elog(ERROR, "target_id contains a null value"); rest->target_id = DatumGetInt32(binval); binval = SPI_getbinval(*tuple, *tupdesc, restrict_columns->to_cost, &isnull); if (isnull) elog(ERROR, "to_cost contains a null value"); rest->to_cost = DatumGetFloat8(binval); char *str = DatumGetCString(SPI_getvalue(*tuple, *tupdesc, restrict_columns->via_path)); //PGR_DBG("restriction: %f, %i, %s", rest->to_cost, rest->target_id, str); if (str != NULL) { char* pch = NULL; int ci = 0; pch = (char *)strtok (str," ,"); while (pch != NULL && ci < MAX_RULE_LENGTH) { rest->via[ci] = atoi(pch); //PGR_DBG(" rest->via[%i]=%i", ci, rest->via[ci]); ci++; pch = (char *)strtok (NULL, " ,"); } } }
void ParquetFetchSegFileInfo(AppendOnlyEntry *aoEntry, List *segfileinfos, Snapshot parquetMetaDataSnapshot) { Relation pg_parquetseg_rel; TupleDesc pg_parquetseg_dsc; HeapTuple tuple; SysScanDesc parquetscan; /* * Since this function is called for insert operation, * here we use RowExclusiveLock. */ pg_parquetseg_rel = heap_open(aoEntry->segrelid, RowExclusiveLock); pg_parquetseg_dsc = RelationGetDescr(pg_parquetseg_rel); parquetscan = systable_beginscan(pg_parquetseg_rel, InvalidOid, FALSE, parquetMetaDataSnapshot, 0, NULL); while (HeapTupleIsValid(tuple = systable_getnext(parquetscan))) { ListCell *lc; int segno = DatumGetInt32(fastgetattr(tuple, Anum_pg_parquetseg_segno, pg_parquetseg_dsc, NULL)); foreach (lc, segfileinfos) { ResultRelSegFileInfo *segfileinfo = (ResultRelSegFileInfo *)lfirst(lc); Assert(segfileinfo != NULL); if (segfileinfo->segno == segno) { segfileinfo->numfiles = 1; segfileinfo->tupcount = (int64)DatumGetFloat8(fastgetattr(tuple, Anum_pg_parquetseg_tupcount, pg_parquetseg_dsc, NULL)); segfileinfo->varblock = 0; segfileinfo->eof = (int64 *)palloc(sizeof(int64)); segfileinfo->eof[0] = (int64)DatumGetFloat8(fastgetattr(tuple, Anum_pg_parquetseg_eof, pg_parquetseg_dsc, NULL)); segfileinfo->uncompressed_eof = (int64 *)palloc(sizeof(int64)); segfileinfo->uncompressed_eof[0] = (int64)DatumGetFloat8(fastgetattr(tuple, Anum_pg_parquetseg_eofuncompressed, pg_parquetseg_dsc, NULL)); break; } } }
/* * CleanupProcSignalState * Remove current process from ProcSignalSlots * * This function is called via on_shmem_exit() during backend shutdown. */ static void CleanupProcSignalState(int status, Datum arg) { int pss_idx = DatumGetInt32(arg); volatile ProcSignalSlot *slot; slot = &ProcSignalSlots[pss_idx - 1]; Assert(slot == MyProcSignalSlot); /* sanity check */ if (slot->pss_pid != MyProcPid) { /* * don't ERROR here. We're exiting anyway, and don't want to get into * infinite loop trying to exit */ elog(LOG, "process %d releasing ProcSignal slot %d, but it contains %d", MyProcPid, pss_idx, (int) slot->pss_pid); return; /* XXX better to zero the slot anyway? */ } slot->pss_pid = 0; }
/* * AuxiliaryProcKill() -- Cut-down version of ProcKill for auxiliary * processes (bgwriter, etc). The PGPROC and sema are not released, only * marked as not-in-use. */ static void AuxiliaryProcKill(int code, Datum arg) { int proctype = DatumGetInt32(arg); PGPROC *auxproc PG_USED_FOR_ASSERTS_ONLY; PGPROC *proc; Assert(proctype >= 0 && proctype < NUM_AUXILIARY_PROCS); auxproc = &AuxiliaryProcs[proctype]; Assert(MyProc == auxproc); /* Release any LW locks I am holding (see notes above) */ LWLockReleaseAll(); /* * Reset MyLatch to the process local one. This is so that signal * handlers et al can continue using the latch after the shared latch * isn't ours anymore. After that clear MyProc and disown the shared * latch. */ SwitchBackToLocalLatch(); proc = MyProc; MyProc = NULL; DisownLatch(&proc->procLatch); SpinLockAcquire(ProcStructLock); /* Mark auxiliary proc no longer in use */ proc->pid = 0; /* Update shared estimate of spins_per_delay */ ProcGlobal->spins_per_delay = update_spins_per_delay(ProcGlobal->spins_per_delay); SpinLockRelease(ProcStructLock); }
static int get_nnode(PlxFn *plx_fn, FunctionCallInfo fcinfo) { PlxQuery *plx_q = plx_fn->hash_query; int err; SPIPlanPtr plan; Oid types[FUNC_MAX_ARGS]; Datum values[FUNC_MAX_ARGS]; char arg_nulls[FUNC_MAX_ARGS]; Datum val; bool isnull; int i; if ((err = SPI_connect()) != SPI_OK_CONNECT) plx_error(plx_fn, "SPI_connect: %s", SPI_result_code_string(err)); for (i = 0; i < plx_q->nargs; i++) { int idx = plx_q->plx_fn_arg_indexes[i]; types[i] = plx_fn->arg_types[idx]->oid; values[i] = PG_GETARG_DATUM(idx); } plan = SPI_prepare(plx_q->sql->data, plx_q->nargs, types); err = SPI_execute_plan(plan, values, arg_nulls, true, 0); if (err != SPI_OK_SELECT) plx_error(plx_fn, "query '%s' failed: %s", plx_q->sql->data, SPI_result_code_string(err)); val = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull); err = SPI_finish(); if (err != SPI_OK_FINISH) plx_error(plx_fn, "SPI_finish: %s", SPI_result_code_string(err)); return DatumGetInt32(val); }
/* * compare_keys */ static int compare_keys(KeyedAggState *state, KeyValue *kv, Datum incoming, bool incoming_null, Oid collation, bool *result_null) { TypeCacheEntry *type = state->key_type; FunctionCallInfoData cmp_fcinfo; int result; if (incoming_null || KV_KEY_IS_NULL(kv)) { *result_null = true; return 0; } InitFunctionCallInfoData(cmp_fcinfo, &type->cmp_proc_finfo, 2, collation, NULL, NULL); cmp_fcinfo.arg[0] = kv->key; cmp_fcinfo.argnull[0] = KV_KEY_IS_NULL(kv); cmp_fcinfo.arg[1] = incoming; cmp_fcinfo.argnull[1] = incoming_null; result = DatumGetInt32(FunctionCallInvoke(&cmp_fcinfo)); *result_null = cmp_fcinfo.isnull; return result; }
int checkMeth2(uint32 keyval, HashJoinTable hashtable){ uint32 hashkey = 8192*bitvector_size; long keyV; unsigned int hash = OFFSET32; int i=0; //printf("size of keyval: %d\n", sizeof(keyval)); int numOctal = sizeof(keyval); if (numOctal < 8) keyV = DatumGetInt32(keyval); else keyV = DatumGetInt64(keyval); for (i=0;i<numOctal;i++){ hash = hash ^ (keyV & 0x000000ff); hash = hash * PRIME32; keyV = keyV >> 8; } // Mapping method hash = hash % hashkey; if (checkKbit(hashtable->bitvector, hash) == 0){ //printf(" Filtered value: %d\n",GET_4_BYTES(keyval)); return 0; } return 1; }
Datum gin_numeric_cmp(PG_FUNCTION_ARGS) { Numeric a = (Numeric) PG_GETARG_POINTER(0); Numeric b = (Numeric) PG_GETARG_POINTER(1); int res = 0; if (NUMERIC_IS_LEFTMOST(a)) { res = (NUMERIC_IS_LEFTMOST(b)) ? 0 : -1; } else if (NUMERIC_IS_LEFTMOST(b)) { res = 1; } else { res = DatumGetInt32(DirectFunctionCall2(numeric_cmp, NumericGetDatum(a), NumericGetDatum(b))); } PG_RETURN_INT32(res); }
int HdfsDisconnect(FsysName protocol, hdfsFS fileSystem) { FunctionCallInfoData fcinfo; FileSystemUdfData fsysUdf; FmgrInfo *fsysFunc = FsysInterfaceGetFunc(protocol, FSYS_FUNC_DISCONNECT); #ifdef USE_ASSERT_CHECKING if (testmode_fault(gp_fsys_fault_inject_percent)) return -1; #endif fsysUdf.type = T_FileSystemFunctionData; fsysUdf.fsys_hdfs = fileSystem; InitFunctionCallInfoData(/* FunctionCallInfoData */ fcinfo, /* FmgrInfo */ fsysFunc, /* nArgs */ 0, /* Call Context */ (Node *) (&fsysUdf), /* ResultSetInfo */ NULL); Datum d = FunctionCallInvoke(&fcinfo); return DatumGetInt32(d); }
/* * For a newly inserted heap tid, check if an entry with this tid * already exists in a unique index. If it does, abort the inserting * transaction. */ static void _bt_validate_tid(Relation irel, ItemPointer h_tid) { MIRROREDLOCK_BUFMGR_DECLARE; BlockNumber blkno; BlockNumber num_pages; Buffer buf; Page page; BTPageOpaque opaque; IndexTuple itup; OffsetNumber maxoff, minoff, offnum; elog(DEBUG1, "validating tid (%d,%d) for index (%s)", ItemPointerGetBlockNumber(h_tid), ItemPointerGetOffsetNumber(h_tid), RelationGetRelationName(irel)); blkno = BTREE_METAPAGE + 1; num_pages = RelationGetNumberOfBlocks(irel); MIRROREDLOCK_BUFMGR_LOCK; for (; blkno < num_pages; blkno++) { buf = ReadBuffer(irel, blkno); page = BufferGetPage(buf); opaque = (BTPageOpaque) PageGetSpecialPointer(page); if (!PageIsNew(page)) _bt_checkpage(irel, buf); if (P_ISLEAF(opaque)) { minoff = P_FIRSTDATAKEY(opaque); maxoff = PageGetMaxOffsetNumber(page); for (offnum = minoff; offnum <= maxoff; offnum = OffsetNumberNext(offnum)) { itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); if (ItemPointerEquals(&itup->t_tid, h_tid)) { Form_pg_attribute key_att = RelationGetDescr(irel)->attrs[0]; Oid key = InvalidOid; bool isnull; if (key_att->atttypid == OIDOID) { key = DatumGetInt32( index_getattr(itup, 1, RelationGetDescr(irel), &isnull)); elog(ERROR, "found tid (%d,%d), %s (%d) already in index (%s)", ItemPointerGetBlockNumber(h_tid), ItemPointerGetOffsetNumber(h_tid), NameStr(key_att->attname), key, RelationGetRelationName(irel)); } else { elog(ERROR, "found tid (%d,%d) already in index (%s)", ItemPointerGetBlockNumber(h_tid), ItemPointerGetOffsetNumber(h_tid), RelationGetRelationName(irel)); } } } } ReleaseBuffer(buf); } MIRROREDLOCK_BUFMGR_UNLOCK; }
Datum gp_inject_fault(PG_FUNCTION_ARGS) { char *faultName = TextDatumGetCString(PG_GETARG_DATUM(0)); char *type = TextDatumGetCString(PG_GETARG_DATUM(1)); char *ddlStatement = TextDatumGetCString(PG_GETARG_DATUM(2)); char *databaseName = TextDatumGetCString(PG_GETARG_DATUM(3)); char *tableName = TextDatumGetCString(PG_GETARG_DATUM(4)); int numOccurrences = PG_GETARG_INT32(5); int sleepTimeSeconds = PG_GETARG_INT32(6); int dbid = PG_GETARG_INT32(7); StringInfo faultmsg = makeStringInfo(); /* Fast path if injecting fault in our postmaster. */ if (GpIdentity.dbid == dbid) { appendStringInfo(faultmsg, "%s\n%s\n%s\n%s\n%s\n%d\n%d\n", faultName, type, ddlStatement, databaseName, tableName, numOccurrences, sleepTimeSeconds); int offset = 0; char *response = processTransitionRequest_faultInject( faultmsg->data, &offset, faultmsg->len); if (!response) elog(ERROR, "failed to inject fault locally (dbid %d)", dbid); if (strncmp(response, "Success:", strlen("Success:")) != 0) elog(ERROR, "%s", response); elog(NOTICE, "%s", response); PG_RETURN_DATUM(true); } /* Obtain host and port of the requested dbid */ HeapTuple tuple; Relation rel = heap_open(GpSegmentConfigRelationId, AccessShareLock); ScanKeyData scankey; SysScanDesc sscan; ScanKeyInit(&scankey, Anum_gp_segment_configuration_dbid, BTEqualStrategyNumber, F_INT2EQ, Int16GetDatum((int16) dbid)); sscan = systable_beginscan(rel, GpSegmentConfigDbidIndexId, true, GetTransactionSnapshot(), 1, &scankey); tuple = systable_getnext(sscan); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cannot find dbid %d", dbid); bool isnull; Datum datum = heap_getattr(tuple, Anum_gp_segment_configuration_hostname, RelationGetDescr(rel), &isnull); char *hostname; if (!isnull) hostname = DatumGetCString(DirectFunctionCall1(textout, datum)); else elog(ERROR, "hostname is null for dbid %d", dbid); int port = DatumGetInt32(heap_getattr(tuple, Anum_gp_segment_configuration_port, RelationGetDescr(rel), &isnull)); systable_endscan(sscan); heap_close(rel, NoLock); struct addrinfo *addrList = NULL; struct addrinfo hint; int ret; /* Initialize hint structure */ MemSet(&hint, 0, sizeof(hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_family = AF_UNSPEC; char portStr[100]; if (snprintf(portStr, sizeof(portStr), "%d", port) >= sizeof(portStr)) elog(ERROR, "port number too long for dbid %d", dbid); /* Use pg_getaddrinfo_all() to resolve the address */ ret = pg_getaddrinfo_all(hostname, portStr, &hint, &addrList); if (ret || !addrList) { if (addrList) pg_freeaddrinfo_all(hint.ai_family, addrList); elog(ERROR, "could not translate host name \"%s\" to address: %s\n", hostname, gai_strerror(ret)); } PrimaryMirrorTransitionClientInfo client; client.receivedDataCallbackFn = transitionReceivedDataFn; client.errorLogFn = transitionErrorLogFn; client.checkForNeedToExitFn = checkForNeedToExitFn; transitionMsgErrors = makeStringInfo(); appendStringInfo(faultmsg, "%s\n%s\n%s\n%s\n%s\n%s\n%d\n%d\n", "faultInject", faultName, type, ddlStatement, databaseName, tableName, numOccurrences, sleepTimeSeconds); if (sendTransitionMessage(&client, addrList, faultmsg->data, faultmsg->len, 1 /* retries */, 60 /* timeout */) != TRANS_ERRCODE_SUCCESS) { pg_freeaddrinfo_all(hint.ai_family, addrList); ereport(ERROR, (errmsg("failed to inject %s fault in dbid %d", faultName, dbid), errdetail("%s", transitionMsgErrors->data))); } pg_freeaddrinfo_all(hint.ai_family, addrList); PG_RETURN_DATUM(BoolGetDatum(true)); }
/* ---------- * toast_fetch_datum_slice - * * Reconstruct a segment of a Datum from the chunks saved * in the toast relation * ---------- */ static struct varlena * toast_fetch_datum_slice(struct varlena * attr, int32 sliceoffset, int32 length) { Relation toastrel; Relation toastidx; ScanKeyData toastkey[3]; int nscankeys; SysScanDesc toastscan; HeapTuple ttup; TupleDesc toasttupDesc; struct varlena *result; struct varatt_external toast_pointer; int32 attrsize; int32 residx; int32 nextidx; int numchunks; int startchunk; int endchunk; int32 startoffset; int32 endoffset; int totalchunks; Pointer chunk; bool isnull; char *chunkdata; int32 chunksize; int32 chcpystrt; int32 chcpyend; Assert(VARATT_IS_EXTERNAL(attr)); /* Must copy to access aligned fields */ VARATT_EXTERNAL_GET_POINTER(toast_pointer, attr); /* * It's nonsense to fetch slices of a compressed datum -- this isn't lo_* * we can't return a compressed datum which is meaningful to toast later */ Assert(!VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer)); attrsize = toast_pointer.va_extsize; totalchunks = ((attrsize - 1) / TOAST_MAX_CHUNK_SIZE) + 1; if (sliceoffset >= attrsize) { sliceoffset = 0; length = 0; } if (((sliceoffset + length) > attrsize) || length < 0) length = attrsize - sliceoffset; result = (struct varlena *) palloc(length + VARHDRSZ); if (VARATT_EXTERNAL_IS_COMPRESSED(toast_pointer)) SET_VARSIZE_COMPRESSED(result, length + VARHDRSZ); else SET_VARSIZE(result, length + VARHDRSZ); if (length == 0) return result; /* Can save a lot of work at this point! */ startchunk = sliceoffset / TOAST_MAX_CHUNK_SIZE; endchunk = (sliceoffset + length - 1) / TOAST_MAX_CHUNK_SIZE; numchunks = (endchunk - startchunk) + 1; startoffset = sliceoffset % TOAST_MAX_CHUNK_SIZE; endoffset = (sliceoffset + length - 1) % TOAST_MAX_CHUNK_SIZE; /* * Open the toast relation and its index */ toastrel = heap_open(toast_pointer.va_toastrelid, AccessShareLock); toasttupDesc = toastrel->rd_att; toastidx = index_open(toastrel->rd_rel->reltoastidxid, AccessShareLock); /* * Setup a scan key to fetch from the index. This is either two keys or * three depending on the number of chunks. */ ScanKeyInit(&toastkey[0], (AttrNumber) 1, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(toast_pointer.va_valueid)); /* * Use equality condition for one chunk, a range condition otherwise: */ if (numchunks == 1) { ScanKeyInit(&toastkey[1], (AttrNumber) 2, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(startchunk)); nscankeys = 2; } else { ScanKeyInit(&toastkey[1], (AttrNumber) 2, BTGreaterEqualStrategyNumber, F_INT4GE, Int32GetDatum(startchunk)); ScanKeyInit(&toastkey[2], (AttrNumber) 2, BTLessEqualStrategyNumber, F_INT4LE, Int32GetDatum(endchunk)); nscankeys = 3; } /* * Read the chunks by index * * The index is on (valueid, chunkidx) so they will come in order */ nextidx = startchunk; toastscan = systable_beginscan_ordered(toastrel, toastidx, SnapshotToast, nscankeys, toastkey); while ((ttup = systable_getnext_ordered(toastscan, ForwardScanDirection)) != NULL) { /* * Have a chunk, extract the sequence number and the data */ residx = DatumGetInt32(fastgetattr(ttup, 2, toasttupDesc, &isnull)); Assert(!isnull); chunk = DatumGetPointer(fastgetattr(ttup, 3, toasttupDesc, &isnull)); Assert(!isnull); if (!VARATT_IS_EXTENDED(chunk)) { chunksize = VARSIZE(chunk) - VARHDRSZ; chunkdata = VARDATA(chunk); } else if (VARATT_IS_SHORT(chunk)) { /* could happen due to heap_form_tuple doing its thing */ chunksize = VARSIZE_SHORT(chunk) - VARHDRSZ_SHORT; chunkdata = VARDATA_SHORT(chunk); } else { /* should never happen */ elog(ERROR, "found toasted toast chunk for toast value %u in %s", toast_pointer.va_valueid, RelationGetRelationName(toastrel)); chunksize = 0; /* keep compiler quiet */ chunkdata = NULL; } /* * Some checks on the data we've found */ if ((residx != nextidx) || (residx > endchunk) || (residx < startchunk)) elog(ERROR, "unexpected chunk number %d (expected %d) for toast value %u in %s", residx, nextidx, toast_pointer.va_valueid, RelationGetRelationName(toastrel)); if (residx < totalchunks - 1) { if (chunksize != TOAST_MAX_CHUNK_SIZE) elog(ERROR, "unexpected chunk size %d (expected %d) in chunk %d of %d for toast value %u in %s when fetching slice", chunksize, (int) TOAST_MAX_CHUNK_SIZE, residx, totalchunks, toast_pointer.va_valueid, RelationGetRelationName(toastrel)); } else if (residx == totalchunks - 1) { if ((residx * TOAST_MAX_CHUNK_SIZE + chunksize) != attrsize) elog(ERROR, "unexpected chunk size %d (expected %d) in final chunk %d for toast value %u in %s when fetching slice", chunksize, (int) (attrsize - residx * TOAST_MAX_CHUNK_SIZE), residx, toast_pointer.va_valueid, RelationGetRelationName(toastrel)); } else elog(ERROR, "unexpected chunk number %d (out of range %d..%d) for toast value %u in %s", residx, 0, totalchunks - 1, toast_pointer.va_valueid, RelationGetRelationName(toastrel)); /* * Copy the data into proper place in our result */ chcpystrt = 0; chcpyend = chunksize - 1; if (residx == startchunk) chcpystrt = startoffset; if (residx == endchunk) chcpyend = endoffset; memcpy(VARDATA(result) + (residx * TOAST_MAX_CHUNK_SIZE - sliceoffset) + chcpystrt, chunkdata + chcpystrt, (chcpyend - chcpystrt) + 1); nextidx++; } /* * Final checks that we successfully fetched the datum */ if (nextidx != (endchunk + 1)) elog(ERROR, "missing chunk number %d for toast value %u in %s", nextidx, toast_pointer.va_valueid, RelationGetRelationName(toastrel)); /* * End scan and close relations */ systable_endscan_ordered(toastscan); index_close(toastidx, AccessShareLock); heap_close(toastrel, AccessShareLock); return result; }
static int32 gbt_byteacmp(const bytea *a, const bytea *b) { return (DatumGetInt32(DirectFunctionCall2(byteacmp, PointerGetDatum(a), PointerGetDatum(b)))); }
/*---------- * _bt_compare() -- Compare scankey to a particular tuple on the page. * * The passed scankey must be an insertion-type scankey (see nbtree/README), * but it can omit the rightmost column(s) of the index. * * keysz: number of key conditions to be checked (might be less than the * number of index columns!) * page/offnum: location of btree item to be compared to. * * This routine returns: * <0 if scankey < tuple at offnum; * 0 if scankey == tuple at offnum; * >0 if scankey > tuple at offnum. * NULLs in the keys are treated as sortable values. Therefore * "equality" does not necessarily mean that the item should be * returned to the caller as a matching key! * * CRUCIAL NOTE: on a non-leaf page, the first data key is assumed to be * "minus infinity": this routine will always claim it is less than the * scankey. The actual key value stored (if any, which there probably isn't) * does not matter. This convention allows us to implement the Lehman and * Yao convention that the first down-link pointer is before the first key. * See backend/access/nbtree/README for details. *---------- */ int32 _bt_compare(Relation rel, int keysz, ScanKey scankey, Page page, OffsetNumber offnum) { TupleDesc itupdesc = RelationGetDescr(rel); BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page); IndexTuple itup; int i; /* * Force result ">" if target item is first data item on an internal page * --- see NOTE above. */ if (!P_ISLEAF(opaque) && offnum == P_FIRSTDATAKEY(opaque)) return 1; itup = (IndexTuple) PageGetItem(page, PageGetItemId(page, offnum)); /* * The scan key is set up with the attribute number associated with each * term in the key. It is important that, if the index is multi-key, the * scan contain the first k key attributes, and that they be in order. If * you think about how multi-key ordering works, you'll understand why * this is. * * We don't test for violation of this condition here, however. The * initial setup for the index scan had better have gotten it right (see * _bt_first). */ for (i = 1; i <= keysz; i++) { Datum datum; bool isNull; int32 result; datum = index_getattr(itup, scankey->sk_attno, itupdesc, &isNull); /* see comments about NULLs handling in btbuild */ if (scankey->sk_flags & SK_ISNULL) /* key is NULL */ { if (isNull) result = 0; /* NULL "=" NULL */ else if (scankey->sk_flags & SK_BT_NULLS_FIRST) result = -1; /* NULL "<" NOT_NULL */ else result = 1; /* NULL ">" NOT_NULL */ } else if (isNull) /* key is NOT_NULL and item is NULL */ { if (scankey->sk_flags & SK_BT_NULLS_FIRST) result = 1; /* NOT_NULL ">" NULL */ else result = -1; /* NOT_NULL "<" NULL */ } else { /* * The sk_func needs to be passed the index value as left arg and * the sk_argument as right arg (they might be of different * types). Since it is convenient for callers to think of * _bt_compare as comparing the scankey to the index item, we have * to flip the sign of the comparison result. (Unless it's a DESC * column, in which case we *don't* flip the sign.) */ result = DatumGetInt32(FunctionCall2Coll(&scankey->sk_func, scankey->sk_collation, datum, scankey->sk_argument)); if (!(scankey->sk_flags & SK_BT_DESC)) result = -result; } /* if the keys are unequal, return the difference */ if (result != 0) return result; scankey++; } /* if we get here, the keys are equal */ return 0; }
/* * record_cmp() * Internal comparison function for records. * * Returns -1, 0 or 1 * * Do not assume that the two inputs are exactly the same record type; * for instance we might be comparing an anonymous ROW() construct against a * named composite type. We will compare as long as they have the same number * of non-dropped columns of the same types. */ static int record_cmp(FunctionCallInfo fcinfo) { HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0); HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1); int result = 0; Oid tupType1; Oid tupType2; int32 tupTypmod1; int32 tupTypmod2; TupleDesc tupdesc1; TupleDesc tupdesc2; HeapTupleData tuple1; HeapTupleData tuple2; int ncolumns1; int ncolumns2; RecordCompareData *my_extra; int ncols; Datum *values1; Datum *values2; bool *nulls1; bool *nulls2; int i1; int i2; int j; /* Extract type info from the tuples */ tupType1 = HeapTupleHeaderGetTypeId(record1); tupTypmod1 = HeapTupleHeaderGetTypMod(record1); tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1); ncolumns1 = tupdesc1->natts; tupType2 = HeapTupleHeaderGetTypeId(record2); tupTypmod2 = HeapTupleHeaderGetTypMod(record2); tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2); ncolumns2 = tupdesc2->natts; /* Build temporary HeapTuple control structures */ tuple1.t_len = HeapTupleHeaderGetDatumLength(record1); ItemPointerSetInvalid(&(tuple1.t_self)); tuple1.t_tableOid = InvalidOid; tuple1.t_data = record1; tuple2.t_len = HeapTupleHeaderGetDatumLength(record2); ItemPointerSetInvalid(&(tuple2.t_self)); tuple2.t_tableOid = InvalidOid; tuple2.t_data = record2; /* * We arrange to look up the needed comparison info just once per series * of calls, assuming the record types don't change underneath us. */ ncols = Max(ncolumns1, ncolumns2); my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns < ncols) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordCompareData) - sizeof(ColumnCompareData) + ncols * sizeof(ColumnCompareData)); my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra; my_extra->ncolumns = ncols; my_extra->record1_type = InvalidOid; my_extra->record1_typmod = 0; my_extra->record2_type = InvalidOid; my_extra->record2_typmod = 0; } if (my_extra->record1_type != tupType1 || my_extra->record1_typmod != tupTypmod1 || my_extra->record2_type != tupType2 || my_extra->record2_typmod != tupTypmod2) { MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData)); my_extra->record1_type = tupType1; my_extra->record1_typmod = tupTypmod1; my_extra->record2_type = tupType2; my_extra->record2_typmod = tupTypmod2; } /* Break down the tuples into fields */ values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum)); nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool)); heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1); values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum)); nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool)); heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2); /* * Scan corresponding columns, allowing for dropped columns in different * places in the two rows. i1 and i2 are physical column indexes, j is * the logical column index. */ i1 = i2 = j = 0; while (i1 < ncolumns1 || i2 < ncolumns2) { TypeCacheEntry *typentry; Oid collation; FunctionCallInfoData locfcinfo; int32 cmpresult; /* * Skip dropped columns */ if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped) { i1++; continue; } if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped) { i2++; continue; } if (i1 >= ncolumns1 || i2 >= ncolumns2) break; /* we'll deal with mismatch below loop */ /* * Have two matching columns, they must be same type */ if (tupdesc1->attrs[i1]->atttypid != tupdesc2->attrs[i2]->atttypid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot compare dissimilar column types %s and %s at record column %d", format_type_be(tupdesc1->attrs[i1]->atttypid), format_type_be(tupdesc2->attrs[i2]->atttypid), j + 1))); /* * If they're not same collation, we don't complain here, but the * comparison function might. */ collation = tupdesc1->attrs[i1]->attcollation; if (collation != tupdesc2->attrs[i2]->attcollation) collation = InvalidOid; /* * Lookup the comparison function if not done already */ typentry = my_extra->columns[j].typentry; if (typentry == NULL || typentry->type_id != tupdesc1->attrs[i1]->atttypid) { typentry = lookup_type_cache(tupdesc1->attrs[i1]->atttypid, TYPECACHE_CMP_PROC_FINFO); if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify a comparison function for type %s", format_type_be(typentry->type_id)))); my_extra->columns[j].typentry = typentry; } /* * We consider two NULLs equal; NULL > not-NULL. */ if (!nulls1[i1] || !nulls2[i2]) { if (nulls1[i1]) { /* arg1 is greater than arg2 */ result = 1; break; } if (nulls2[i2]) { /* arg1 is less than arg2 */ result = -1; break; } /* Compare the pair of elements */ InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2, collation, NULL, NULL); locfcinfo.arg[0] = values1[i1]; locfcinfo.arg[1] = values2[i2]; locfcinfo.argnull[0] = false; locfcinfo.argnull[1] = false; locfcinfo.isnull = false; cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo)); if (cmpresult < 0) { /* arg1 is less than arg2 */ result = -1; break; } else if (cmpresult > 0) { /* arg1 is greater than arg2 */ result = 1; break; } } /* equal, so continue to next column */ i1++, i2++, j++; } /* * If we didn't break out of the loop early, check for column count * mismatch. (We do not report such mismatch if we found unequal column * values; is that a feature or a bug?) */ if (result == 0) { if (i1 != ncolumns1 || i2 != ncolumns2) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot compare record types with different numbers of columns"))); } pfree(values1); pfree(nulls1); pfree(values2); pfree(nulls2); ReleaseTupleDesc(tupdesc1); ReleaseTupleDesc(tupdesc2); /* Avoid leaking memory when handed toasted input. */ PG_FREE_IF_COPY(record1, 0); PG_FREE_IF_COPY(record2, 1); return result; }
/*---------- * _bt_compare() -- Compare scankey to a particular tuple on the page. * * keysz: number of key conditions to be checked (might be less than the * total length of the scan key!) * page/offnum: location of btree item to be compared to. * * This routine returns: * <0 if scankey < tuple at offnum; * 0 if scankey == tuple at offnum; * >0 if scankey > tuple at offnum. * NULLs in the keys are treated as sortable values. Therefore * "equality" does not necessarily mean that the item should be * returned to the caller as a matching key! * * CRUCIAL NOTE: on a non-leaf page, the first data key is assumed to be * "minus infinity": this routine will always claim it is less than the * scankey. The actual key value stored (if any, which there probably isn't) * does not matter. This convention allows us to implement the Lehman and * Yao convention that the first down-link pointer is before the first key. * See backend/access/nbtree/README for details. *---------- */ int32 _bt_compare(Relation rel, int keysz, ScanKey scankey, Page page, OffsetNumber offnum) { TupleDesc itupdesc = RelationGetDescr(rel); BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page); BTItem btitem; IndexTuple itup; int i; /* * Force result ">" if target item is first data item on an internal * page --- see NOTE above. */ if (!P_ISLEAF(opaque) && offnum == P_FIRSTDATAKEY(opaque)) return 1; btitem = (BTItem) PageGetItem(page, PageGetItemId(page, offnum)); itup = &(btitem->bti_itup); /* * The scan key is set up with the attribute number associated with * each term in the key. It is important that, if the index is * multi-key, the scan contain the first k key attributes, and that * they be in order. If you think about how multi-key ordering works, * you'll understand why this is. * * We don't test for violation of this condition here, however. The * initial setup for the index scan had better have gotten it right * (see _bt_first). */ for (i = 0; i < keysz; i++) { ScanKey entry = &scankey[i]; Datum datum; bool isNull; int32 result; datum = index_getattr(itup, entry->sk_attno, itupdesc, &isNull); /* see comments about NULLs handling in btbuild */ if (entry->sk_flags & SK_ISNULL) /* key is NULL */ { if (isNull) result = 0; /* NULL "=" NULL */ else result = 1; /* NULL ">" NOT_NULL */ } else if (isNull) /* key is NOT_NULL and item is NULL */ { result = -1; /* NOT_NULL "<" NULL */ } else { result = DatumGetInt32(FunctionCall2(&entry->sk_func, entry->sk_argument, datum)); } /* if the keys are unequal, return the difference */ if (result != 0) return result; } /* if we get here, the keys are equal */ return 0; }
Datum /* have to return HeapTuple to Executor */ timetravel(PG_FUNCTION_ARGS) { TriggerData *trigdata = (TriggerData *) fcinfo->context; Trigger *trigger; /* to get trigger name */ int argc; char **args; /* arguments */ int attnum[MaxAttrNum]; /* fnumbers of start/stop columns */ Datum oldtimeon, oldtimeoff; Datum newtimeon, newtimeoff, newuser, nulltext; Datum *cvals; /* column values */ char *cnulls; /* column nulls */ char *relname; /* triggered relation name */ Relation rel; /* triggered relation */ HeapTuple trigtuple; HeapTuple newtuple = NULL; HeapTuple rettuple; TupleDesc tupdesc; /* tuple description */ int natts; /* # of attributes */ EPlan *plan; /* prepared plan */ char ident[2 * NAMEDATALEN]; bool isnull; /* to know is some column NULL or not */ bool isinsert = false; int ret; int i; /* * Some checks first... */ /* Called by trigger manager ? */ if (!CALLED_AS_TRIGGER(fcinfo)) elog(ERROR, "timetravel: not fired by trigger manager"); /* Should be called for ROW trigger */ if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) elog(ERROR, "timetravel: can't process STATEMENT events"); /* Should be called BEFORE */ if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) elog(ERROR, "timetravel: must be fired before event"); /* INSERT ? */ if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) isinsert = true; if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) newtuple = trigdata->tg_newtuple; trigtuple = trigdata->tg_trigtuple; rel = trigdata->tg_relation; relname = SPI_getrelname(rel); /* check if TT is OFF for this relation */ if (0 == findTTStatus(relname)) { /* OFF - nothing to do */ pfree(relname); return PointerGetDatum((newtuple != NULL) ? newtuple : trigtuple); } trigger = trigdata->tg_trigger; argc = trigger->tgnargs; if (argc != MinAttrNum && argc != MaxAttrNum) elog(ERROR, "timetravel (%s): invalid (!= %d or %d) number of arguments %d", relname, MinAttrNum, MaxAttrNum, trigger->tgnargs); args = trigger->tgargs; tupdesc = rel->rd_att; natts = tupdesc->natts; for (i = 0; i < MinAttrNum; i++) { attnum[i] = SPI_fnumber(tupdesc, args[i]); if (attnum[i] < 0) elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]); if (SPI_gettypeid(tupdesc, attnum[i]) != ABSTIMEOID) elog(ERROR, "timetravel (%s): attribute %s must be of abstime type", relname, args[i]); } for (; i < argc; i++) { attnum[i] = SPI_fnumber(tupdesc, args[i]); if (attnum[i] < 0) elog(ERROR, "timetravel (%s): there is no attribute %s", relname, args[i]); if (SPI_gettypeid(tupdesc, attnum[i]) != TEXTOID) elog(ERROR, "timetravel (%s): attribute %s must be of text type", relname, args[i]); } /* create fields containing name */ newuser = DirectFunctionCall1(textin, CStringGetDatum(GetUserNameFromId(GetUserId()))); nulltext = (Datum) NULL; if (isinsert) { /* INSERT */ int chnattrs = 0; int chattrs[MaxAttrNum]; Datum newvals[MaxAttrNum]; char newnulls[MaxAttrNum]; oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull); if (isnull) { newvals[chnattrs] = GetCurrentAbsoluteTime(); newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_on]; chnattrs++; } oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull); if (isnull) { if ((chnattrs == 0 && DatumGetInt32(oldtimeon) >= NOEND_ABSTIME) || (chnattrs > 0 && DatumGetInt32(newvals[a_time_on]) >= NOEND_ABSTIME)) elog(ERROR, "timetravel (%s): %s is infinity", relname, args[a_time_on]); newvals[chnattrs] = NOEND_ABSTIME; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_off]; chnattrs++; } else { if ((chnattrs == 0 && DatumGetInt32(oldtimeon) > DatumGetInt32(oldtimeoff)) || (chnattrs > 0 && DatumGetInt32(newvals[a_time_on]) > DatumGetInt32(oldtimeoff))) elog(ERROR, "timetravel (%s): %s gt %s", relname, args[a_time_on], args[a_time_off]); } pfree(relname); if (chnattrs <= 0) return PointerGetDatum(trigtuple); if (argc == MaxAttrNum) { /* clear update_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_upd_user]; chnattrs++; /* clear delete_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_del_user]; chnattrs++; /* set insert_user value */ newvals[chnattrs] = newuser; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_ins_user]; chnattrs++; } rettuple = SPI_modifytuple(rel, trigtuple, chnattrs, chattrs, newvals, newnulls); return PointerGetDatum(rettuple); /* end of INSERT */ } /* UPDATE/DELETE: */ oldtimeon = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_on], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]); oldtimeoff = SPI_getbinval(trigtuple, tupdesc, attnum[a_time_off], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]); /* * If DELETE/UPDATE of tuple with stop_date neq INFINITY then say * upper Executor to skip operation for this tuple */ if (newtuple != NULL) { /* UPDATE */ newtimeon = SPI_getbinval(newtuple, tupdesc, attnum[a_time_on], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_on]); newtimeoff = SPI_getbinval(newtuple, tupdesc, attnum[a_time_off], &isnull); if (isnull) elog(ERROR, "timetravel (%s): %s must be NOT NULL", relname, args[a_time_off]); if (oldtimeon != newtimeon || oldtimeoff != newtimeoff) elog(ERROR, "timetravel (%s): you can't change %s and/or %s columns (use set_timetravel)", relname, args[a_time_on], args[a_time_off]); } if (oldtimeoff != NOEND_ABSTIME) { /* current record is a deleted/updated * record */ pfree(relname); return PointerGetDatum(NULL); } newtimeoff = GetCurrentAbsoluteTime(); /* Connect to SPI manager */ if ((ret = SPI_connect()) < 0) elog(ERROR, "timetravel (%s): SPI_connect returned %d", relname, ret); /* Fetch tuple values and nulls */ cvals = (Datum *) palloc(natts * sizeof(Datum)); cnulls = (char *) palloc(natts * sizeof(char)); for (i = 0; i < natts; i++) { cvals[i] = SPI_getbinval(trigtuple, tupdesc, i + 1, &isnull); cnulls[i] = (isnull) ? 'n' : ' '; } /* change date column(s) */ cvals[attnum[a_time_off] - 1] = newtimeoff; /* stop_date eq current * date */ cnulls[attnum[a_time_off] - 1] = ' '; if (!newtuple) { /* DELETE */ if (argc == MaxAttrNum) { cvals[attnum[a_del_user] - 1] = newuser; /* set delete user */ cnulls[attnum[a_del_user] - 1] = ' '; } } /* * Construct ident string as TriggerName $ TriggeredRelationId and try * to find prepared execution plan. */ snprintf(ident, sizeof(ident), "%s$%u", trigger->tgname, rel->rd_id); plan = find_plan(ident, &Plans, &nPlans); /* if there is no plan ... */ if (plan->splan == NULL) { void *pplan; Oid *ctypes; char sql[8192]; char separ = ' '; /* allocate ctypes for preparation */ ctypes = (Oid *) palloc(natts * sizeof(Oid)); /* * Construct query: INSERT INTO _relation_ VALUES ($1, ...) */ snprintf(sql, sizeof(sql), "INSERT INTO %s VALUES (", relname); for (i = 1; i <= natts; i++) { ctypes[i - 1] = SPI_gettypeid(tupdesc, i); if (!(tupdesc->attrs[i - 1]->attisdropped)) /* skip dropped columns */ { snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), "%c$%d", separ, i); separ = ','; } } snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), ")"); elog(DEBUG4, "timetravel (%s) update: sql: %s", relname, sql); /* Prepare plan for query */ pplan = SPI_prepare(sql, natts, ctypes); if (pplan == NULL) elog(ERROR, "timetravel (%s): SPI_prepare returned %d", relname, SPI_result); /* * Remember that SPI_prepare places plan in current memory context * - so, we have to save plan in Top memory context for latter * use. */ pplan = SPI_saveplan(pplan); if (pplan == NULL) elog(ERROR, "timetravel (%s): SPI_saveplan returned %d", relname, SPI_result); plan->splan = pplan; } /* * Ok, execute prepared plan. */ ret = SPI_execp(plan->splan, cvals, cnulls, 0); if (ret < 0) elog(ERROR, "timetravel (%s): SPI_execp returned %d", relname, ret); /* Tuple to return to upper Executor ... */ if (newtuple) { /* UPDATE */ int chnattrs = 0; int chattrs[MaxAttrNum]; Datum newvals[MaxAttrNum]; char newnulls[MaxAttrNum]; newvals[chnattrs] = newtimeoff; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_on]; chnattrs++; newvals[chnattrs] = NOEND_ABSTIME; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_time_off]; chnattrs++; if (argc == MaxAttrNum) { /* set update_user value */ newvals[chnattrs] = newuser; newnulls[chnattrs] = ' '; chattrs[chnattrs] = attnum[a_upd_user]; chnattrs++; /* clear delete_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_del_user]; chnattrs++; /* set insert_user value */ newvals[chnattrs] = nulltext; newnulls[chnattrs] = 'n'; chattrs[chnattrs] = attnum[a_ins_user]; chnattrs++; } rettuple = SPI_modifytuple(rel, newtuple, chnattrs, chattrs, newvals, newnulls); /* * SPI_copytuple allocates tmptuple in upper executor context - * have to free allocation using SPI_pfree */ /* SPI_pfree(tmptuple); */ } else /* DELETE case */ rettuple = trigtuple; SPI_finish(); /* don't forget say Bye to SPI mgr */ pfree(relname); return PointerGetDatum(rettuple); }
/* * ntile * compute an exact numeric value with scale 0 (zero), * ranging from 1 (one) to n, per spec. */ Datum window_ntile(PG_FUNCTION_ARGS) { WindowObject winobj = PG_WINDOW_OBJECT(); ntile_context *context; context = (ntile_context *) WinGetPartitionLocalMemory(winobj, sizeof(ntile_context)); if (context->ntile == 0) { /* first call */ int64 total; int32 nbuckets; bool isnull; total = WinGetPartitionRowCount(winobj); nbuckets = DatumGetInt32(WinGetFuncArgCurrent(winobj, 0, &isnull)); /* * per spec: If NT is the null value, then the result is the null * value. */ if (isnull) PG_RETURN_NULL(); /* * per spec: If NT is less than or equal to 0 (zero), then an * exception condition is raised. */ if (nbuckets <= 0) ereport(ERROR, (errcode(ERRCODE_INVALID_ARGUMENT_FOR_NTILE), errmsg("argument of ntile must be greater than zero"))); context->ntile = 1; context->rows_per_bucket = 0; context->boundary = total / nbuckets; if (context->boundary <= 0) context->boundary = 1; else { /* * If the total number is not divisible, add 1 row to leading * buckets. */ context->remainder = total % nbuckets; if (context->remainder != 0) context->boundary++; } } context->rows_per_bucket++; if (context->boundary < context->rows_per_bucket) { /* ntile up */ if (context->remainder != 0 && context->ntile == context->remainder) { context->remainder = 0; context->boundary -= 1; } context->ntile += 1; context->rows_per_bucket = 1; } PG_RETURN_INT32(context->ntile); }