Datum levenshtein(PG_FUNCTION_ARGS) { char *str_s; char *str_s0; char *str_t; int cols = 0; int rows = 0; int *u_cells; int *l_cells; int *tmp; int i; int j; /* * Fetch the arguments. str_s is referred to as the "source" cols = length * of source + 1 to allow for the initialization column str_t is referred * to as the "target", rows = length of target + 1 rows = length of target * + 1 to allow for the initialization row */ str_s = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(0)))); str_t = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(PG_GETARG_TEXT_P(1)))); cols = strlen(str_s) + 1; rows = strlen(str_t) + 1; /* * Restrict the length of the strings being compared to something * reasonable because we will have to perform rows * cols calculations. If * longer strings need to be compared, increase MAX_LEVENSHTEIN_STRLEN to * suit (but within your tolerance for speed and memory usage). */ if ((cols > MAX_LEVENSHTEIN_STRLEN + 1) || (rows > MAX_LEVENSHTEIN_STRLEN + 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("argument exceeds max length: %d", MAX_LEVENSHTEIN_STRLEN))); /* * If either rows or cols is 0, the answer is the other value. This makes * sense since it would take that many insertions the build a matching * string */ if (cols == 0) PG_RETURN_INT32(rows); if (rows == 0) PG_RETURN_INT32(cols); /* * Allocate two vectors of integers. One will be used for the "upper" row, * the other for the "lower" row. Initialize the "upper" row to 0..cols */ u_cells = palloc(sizeof(int) * cols); for (i = 0; i < cols; i++) u_cells[i] = i; l_cells = palloc(sizeof(int) * cols); /* * Use str_s0 to "rewind" the pointer to str_s in the nested for loop * below */ str_s0 = str_s; /* * Loop through the rows, starting at row 1. Row 0 is used for the initial * "upper" row. */ for (j = 1; j < rows; j++) { /* * We'll always start with col 1, and initialize lower row col 0 to j */ l_cells[0] = j; for (i = 1; i < cols; i++) { int c = 0; int c1 = 0; int c2 = 0; int c3 = 0; /* * The "cost" value is 0 if the character at the current col * position in the source string, matches the character at the * current row position in the target string; cost is 1 otherwise. */ c = (*str_s != *str_t); /* * c1 is upper right cell plus 1 */ c1 = u_cells[i] + 1; /* * c2 is lower left cell plus 1 */ c2 = l_cells[i - 1] + 1; /* * c3 is cell diagonally above to the left plus "cost" */ c3 = u_cells[i - 1] + c; /* * The lower right cell is set to the minimum of c1, c2, c3 */ l_cells[i] = (c1 < c2 ? c1 : c2) < c3 ? (c1 < c2 ? c1 : c2) : c3; /* * Increment the pointer to str_s */ str_s++; } /* * Lower row now becomes the upper row, and the upper row gets reused * as the new lower row. */ tmp = u_cells; u_cells = l_cells; l_cells = tmp; /* * Increment the pointer to str_t */ str_t++; /* * Rewind the pointer to str_s */ str_s = str_s0; } /* * Because the final value (at position row, col) was swapped from the * lower row to the upper row, that's where we'll find it. */ PG_RETURN_INT32(u_cells[cols - 1]); }
Datum lwgeom_cmp(PG_FUNCTION_ARGS) { GSERIALIZED *geom1 = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); GSERIALIZED *geom2 = (GSERIALIZED *) PG_DETOAST_DATUM(PG_GETARG_DATUM(1)); GBOX box1; GBOX box2; POSTGIS_DEBUG(2, "lwgeom_cmp called"); if (gserialized_get_srid(geom1) != gserialized_get_srid(geom2)) { elog(BTREE_SRID_MISMATCH_SEVERITY, "Operation on two GEOMETRIES with different SRIDs\n"); PG_FREE_IF_COPY(geom1, 0); PG_FREE_IF_COPY(geom2, 1); PG_RETURN_NULL(); } gserialized_get_gbox_p(geom1, &box1); gserialized_get_gbox_p(geom2, &box2); PG_FREE_IF_COPY(geom1, 0); PG_FREE_IF_COPY(geom2, 1); if ( ! FPeq(box1.xmin , box2.xmin) ) { if (box1.xmin < box2.xmin) { PG_RETURN_INT32(-1); } PG_RETURN_INT32(1); } if ( ! FPeq(box1.ymin , box2.ymin) ) { if (box1.ymin < box2.ymin) { PG_RETURN_INT32(-1); } PG_RETURN_INT32(1); } if ( ! FPeq(box1.xmax , box2.xmax) ) { if (box1.xmax < box2.xmax) { PG_RETURN_INT32(-1); } PG_RETURN_INT32(1); } if ( ! FPeq(box1.ymax , box2.ymax) ) { if (box1.ymax < box2.ymax) { PG_RETURN_INT32(-1); } PG_RETURN_INT32(1); } PG_RETURN_INT32(0); }
/* * This function accepts an array, and returns one item for each entry in the * array */ Datum int_enum(PG_FUNCTION_ARGS) { PGARRAY *p = (PGARRAY *) PG_GETARG_POINTER(0); CTX *pc; ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (!rsi || !IsA(rsi, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("int_enum called in context that cannot accept a set"))); if (!p) { elog(WARNING, "no data sent"); PG_RETURN_NULL(); } if (!fcinfo->flinfo->fn_extra) { /* Allocate working state */ MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); pc = (CTX *) palloc(sizeof(CTX)); /* Don't copy attribute if you don't need to */ if (VARATT_IS_EXTENDED(p)) { /* Toasted!!! */ pc->p = (PGARRAY *) PG_DETOAST_DATUM_COPY(p); pc->flags = TOASTED; } else { /* Untoasted */ pc->p = p; pc->flags = 0; } /* Now that we have a detoasted array, verify dimensions */ /* We'll treat a zero-D array as empty, below */ if (pc->p->a.ndim > 1) elog(ERROR, "int_enum only accepts 1-D arrays"); pc->num = 0; fcinfo->flinfo->fn_extra = (void *) pc; MemoryContextSwitchTo(oldcontext); } else /* use existing working state */ pc = (CTX *) fcinfo->flinfo->fn_extra; /* Are we done yet? */ if (pc->p->a.ndim < 1 || pc->num >= pc->p->items) { /* We are done */ if (pc->flags & TOASTED) pfree(pc->p); pfree(pc); fcinfo->flinfo->fn_extra = NULL; rsi->isDone = ExprEndResult; } else { /* nope, return the next value */ int val = pc->p->array[pc->num++]; rsi->isDone = ExprMultipleResult; PG_RETURN_INT32(val); } PG_RETURN_NULL(); }
Datum hstore_cmp(PG_FUNCTION_ARGS) { HStore *hs1 = PG_GETARG_HS(0); HStore *hs2 = PG_GETARG_HS(1); int hcount1 = HS_COUNT(hs1); int hcount2 = HS_COUNT(hs2); int res = 0; if (hcount1 == 0 || hcount2 == 0) { /* * if either operand is empty, and the other is nonempty, the nonempty * one is larger. If both are empty they are equal. */ if (hcount1 > 0) res = 1; else if (hcount2 > 0) res = -1; } else { /* here we know both operands are nonempty */ char *str1 = STRPTR(hs1); char *str2 = STRPTR(hs2); HEntry *ent1 = ARRPTR(hs1); HEntry *ent2 = ARRPTR(hs2); size_t len1 = HSE_ENDPOS(ent1[2 * hcount1 - 1]); size_t len2 = HSE_ENDPOS(ent2[2 * hcount2 - 1]); res = memcmp(str1, str2, Min(len1, len2)); if (res == 0) { if (len1 > len2) res = 1; else if (len1 < len2) res = -1; else if (hcount1 > hcount2) res = 1; else if (hcount2 > hcount1) res = -1; else { int count = hcount1 * 2; int i; for (i = 0; i < count; ++i) if (HSE_ENDPOS(ent1[i]) != HSE_ENDPOS(ent2[i]) || HSE_ISNULL(ent1[i]) != HSE_ISNULL(ent2[i])) break; if (i < count) { if (HSE_ENDPOS(ent1[i]) < HSE_ENDPOS(ent2[i])) res = -1; else if (HSE_ENDPOS(ent1[i]) > HSE_ENDPOS(ent2[i])) res = 1; else if (HSE_ISNULL(ent1[i])) res = 1; else if (HSE_ISNULL(ent2[i])) res = -1; } } } else { res = (res > 0) ? 1 : -1; } } /* * this is a btree support function; this is one of the few places where * memory needs to be explicitly freed. */ PG_FREE_IF_COPY(hs1, 0); PG_FREE_IF_COPY(hs2, 1); PG_RETURN_INT32(res); }
Datum gtin_cmp (PG_FUNCTION_ARGS) { PG_RETURN_INT32( gtin_str_cmp( PG_ARGS ) ); }
Datum levenshtein_fast(PG_FUNCTION_ARGS) { inline int max(int a, int b) { return a > b ? a : b; } inline int min(int a, int b) { return a < b ? a : b; } text *s1 = PG_GETARG_TEXT_P(0); text *s2 = PG_GETARG_TEXT_P(1); int maxd = PG_GETARG_INT32(2); char c1, c2; int i, j, temp; int n1 = VARSIZE(s1) - VARHDRSZ; int n2 = VARSIZE(s2) - VARHDRSZ; int f1[n2 + 10], f2[n2 + 10]; int f1_start, f1_end, f2_start, f2_end; memset(f1, 0, sizeof(f1)); for (i = 0; i <= min(maxd, n2); i++) f1[i] = i; f1_start = 0; f1_end = min(maxd, n2); for (i = 1; i <= n1; i++) { memset(f2, 0, sizeof(f2)); f2_start = -1; f2_end = 0; for (j = f1_start; j <= min(f1_end + 1, n2); j++) { temp = maxd + 1; if (j <= f1_end) temp = min(temp, f1[j - f1_start] + 1); //f[i - 1][j] + 1; if (f2_start != -1) temp = min(temp, f2[j - 1 - f2_start] + 1); //f[i][j - 1] + 1; // f[i - 1][j - 1] + 1/0; if (j > f1_start) { c1 = VARDATA(s1)[i - 1]; c2 = VARDATA(s2)[j - 1]; if (c1 >= 'a' && c1 <= 'z') c1 = c1 - 'a' + 'A'; if (c2 >= 'a' && c2 <= 'z') c2 = c2 - 'a' + 'A'; if (c1 == c2) temp = min(temp, f1[j - 1 - f1_start]); else temp = min(temp, f1[j - 1 - f1_start] + 1); } if (f2_start != -1 || temp <= maxd) { if (f2_start == -1) f2_start = j; f2[j - f2_start] = temp; f2_end = j; } } if (f2_start == -1) PG_RETURN_INT32(maxd + 1); while (f2[f2_end - f2_start] > maxd) f2_end--; f1_start = f2_start; f1_end = f2_end; memcpy(f1, f2, sizeof(f1)); } if (n2 > f2_end) PG_RETURN_INT32(maxd + 1); else PG_RETURN_INT32(f2[n2 - f2_start]); }
Datum base36_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); PG_RETURN_INT32(base36_from_str(str)); }
Datum wc_words(PG_FUNCTION_ARGS) { PG_RETURN_INT32(53); }
/* * lo_export - * exports an (inversion) large object. */ Datum be_lo_export(PG_FUNCTION_ARGS) { Oid lobjId = PG_GETARG_OID(0); text *filename = PG_GETARG_TEXT_PP(1); int fd; int nbytes, tmp; char buf[BUFSIZE]; char fnamebuf[MAXPGPATH]; LargeObjectDesc *lobj; mode_t oumask; CreateFSContext(); /* * open the inversion object (no need to test for failure) */ lobj = inv_open(lobjId, INV_READ, fscxt); /* * open the file to be written to * * Note: we reduce backend's normal 077 umask to the slightly friendlier * 022. This code used to drop it all the way to 0, but creating * world-writable export files doesn't seem wise. */ text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); oumask = umask(S_IWGRP | S_IWOTH); PG_TRY(); { fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); } PG_CATCH(); { umask(oumask); PG_RE_THROW(); } PG_END_TRY(); umask(oumask); if (fd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create server file \"%s\": %m", fnamebuf))); /* * read in from the inversion file and write to the filesystem */ while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) { tmp = write(fd, buf, nbytes); if (tmp != nbytes) ereport(ERROR, (errcode_for_file_access(), errmsg("could not write server file \"%s\": %m", fnamebuf))); } CloseTransientFile(fd); inv_close(lobj); PG_RETURN_INT32(1); }
Datum svec_dimension(PG_FUNCTION_ARGS) { SvecType *svec = PG_GETARG_SVECTYPE_P(0); PG_RETURN_INT32(svec->dimension); }
Datum wc_chars(PG_FUNCTION_ARGS) { PG_RETURN_INT32(31); }
Datum svec_l2_cmp(PG_FUNCTION_ARGS) { SvecType *svec1 = PG_GETARG_SVECTYPE_P(0); SvecType *svec2 = PG_GETARG_SVECTYPE_P(1); PG_RETURN_INT32(svec_l2_cmp_internal(svec1,svec2)); }
Datum float8arr_hash(PG_FUNCTION_ARGS) { ArrayType *array = PG_GETARG_ARRAYTYPE_P(0); PG_RETURN_INT32(float8arr_hash_internal(array)); }
/* * master_apply_delete_command takes in a delete command, finds shards that * match the criteria defined in the delete command, drops the found shards from * the worker nodes, and updates the corresponding metadata on the master node. * This function drops a shard if and only if all rows in the shard satisfy * the conditions in the delete command. Note that this function only accepts * conditions on the partition key and if no condition is provided then all * shards are deleted. * * We mark shard placements that we couldn't drop as to be deleted later. If a * shard satisfies the given conditions, we delete it from shard metadata table * even though related shard placements are not deleted. */ Datum master_apply_delete_command(PG_FUNCTION_ARGS) { text *queryText = PG_GETARG_TEXT_P(0); char *queryString = text_to_cstring(queryText); char *relationName = NULL; char *schemaName = NULL; Oid relationId = InvalidOid; List *shardIntervalList = NIL; List *deletableShardIntervalList = NIL; List *queryTreeList = NIL; Query *deleteQuery = NULL; Node *whereClause = NULL; Node *deleteCriteria = NULL; Node *queryTreeNode = NULL; DeleteStmt *deleteStatement = NULL; int droppedShardCount = 0; LOCKMODE lockMode = 0; char partitionMethod = 0; bool failOK = false; #if (PG_VERSION_NUM >= 100000) RawStmt *rawStmt = (RawStmt *) ParseTreeRawStmt(queryString); queryTreeNode = rawStmt->stmt; #else queryTreeNode = ParseTreeNode(queryString); #endif EnsureCoordinator(); CheckCitusVersion(ERROR); if (!IsA(queryTreeNode, DeleteStmt)) { ereport(ERROR, (errmsg("query \"%s\" is not a delete statement", queryString))); } deleteStatement = (DeleteStmt *) queryTreeNode; schemaName = deleteStatement->relation->schemaname; relationName = deleteStatement->relation->relname; /* * We take an exclusive lock while dropping shards to prevent concurrent * writes. We don't want to block SELECTs, which means queries might fail * if they access a shard that has just been dropped. */ lockMode = ExclusiveLock; relationId = RangeVarGetRelid(deleteStatement->relation, lockMode, failOK); /* schema-prefix if it is not specified already */ if (schemaName == NULL) { Oid schemaId = get_rel_namespace(relationId); schemaName = get_namespace_name(schemaId); } CheckDistributedTable(relationId); EnsureTablePermissions(relationId, ACL_DELETE); #if (PG_VERSION_NUM >= 100000) queryTreeList = pg_analyze_and_rewrite(rawStmt, queryString, NULL, 0, NULL); #else queryTreeList = pg_analyze_and_rewrite(queryTreeNode, queryString, NULL, 0); #endif deleteQuery = (Query *) linitial(queryTreeList); CheckTableCount(deleteQuery); /* get where clause and flatten it */ whereClause = (Node *) deleteQuery->jointree->quals; deleteCriteria = eval_const_expressions(NULL, whereClause); partitionMethod = PartitionMethod(relationId); if (partitionMethod == DISTRIBUTE_BY_HASH) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot delete from hash distributed table with this " "command"), errdetail("Delete statements on hash-partitioned tables " "are not supported with master_apply_delete_command."), errhint("Use master_modify_multiple_shards command instead."))); } else if (partitionMethod == DISTRIBUTE_BY_NONE) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot delete from distributed table"), errdetail("Delete statements on reference tables " "are not supported."))); } CheckDeleteCriteria(deleteCriteria); CheckPartitionColumn(relationId, deleteCriteria); shardIntervalList = LoadShardIntervalList(relationId); /* drop all shards if where clause is not present */ if (deleteCriteria == NULL) { deletableShardIntervalList = shardIntervalList; ereport(DEBUG2, (errmsg("dropping all shards for \"%s\"", relationName))); } else { deletableShardIntervalList = ShardsMatchingDeleteCriteria(relationId, shardIntervalList, deleteCriteria); } droppedShardCount = DropShards(relationId, schemaName, relationName, deletableShardIntervalList); PG_RETURN_INT32(droppedShardCount); }
Datum ltree_cmp(PG_FUNCTION_ARGS) { RUNCMP PG_RETURN_INT32(res); }
static Datum kmeans_impl(PG_FUNCTION_ARGS, bool initial_mean_supplied) { WindowObject winobj = PG_WINDOW_OBJECT(); kmeans_context *context; int64 curpos, rowcount; rowcount = WinGetPartitionRowCount(winobj); context = (kmeans_context *) WinGetPartitionLocalMemory(winobj, sizeof(kmeans_context) + sizeof(int) * rowcount); if (!context->isdone) { int dim, k, N; Datum arg; bool isnull, isout; myvector inputs, mean, maxlist, minlist; int *r; int i, a; ArrayType *x; arg = WinGetFuncArgCurrent(winobj, 0, &isnull); if (!isnull) x = DatumGetArrayTypeP( WinGetFuncArgCurrent(winobj, 0, &isnull)); KMEANS_CHECK_V(x, ARR_DIMS(x)[0], isnull); dim = ARR_DIMS(x)[0]; k = DatumGetInt32(WinGetFuncArgCurrent(winobj, 1, &isnull)); /* * Since window function ignores STRICT mark, * return NULL simply. */ if (isnull || k <= 0) { context->isdone = true; context->isnull = true; PG_RETURN_NULL(); } N = (int) WinGetPartitionRowCount(winobj); inputs = (myvector) palloc(SIZEOF_V(dim) * N); maxlist = (myvector) palloc(SIZEOF_V(dim)); minlist = (myvector) palloc(SIZEOF_V(dim)); for (i = 0; i < N; i++) { x = DatumGetArrayTypeP( WinGetFuncArgInPartition(winobj, 0, i, WINDOW_SEEK_HEAD, false, &isnull, &isout)); KMEANS_CHECK_V(x, dim, isnull); memcpy(&inputs[i * dim], ARR_DATA_PTR(x), SIZEOF_V(dim)); /* update min/max for later use of init mean */ for (a = 0; a < dim; a++) { if (i == 0 || maxlist[a] < inputs[i * dim + a]) maxlist[a] = inputs[i * dim + a]; if (i == 0 || minlist[a] > inputs[i * dim + a]) minlist[a] = inputs[i * dim + a]; } } /* * initial mean vectors. need improve how to define them. */ mean = (myvector) palloc(SIZEOF_V(dim) * k); /* only the result is stored in the partition local memory */ r = context->result; if (initial_mean_supplied) { ArrayType *init = DatumGetArrayTypeP( WinGetFuncArgCurrent(winobj, 2, &isnull)); /* * we can accept 1d or 2d array as mean vectors. */ if (isnull || ARR_HASNULL(init) || !((ARR_NDIM(init) == 2 && ARR_DIMS(init)[0] == k && ARR_DIMS(init)[1] == dim) || (ARR_NDIM(init) == 1 && ARR_DIMS(init)[0] == k * dim))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("initial mean vector must be 2d without NULL element"))); memcpy(mean, ARR_DATA_PTR(init), SIZEOF_V(dim) * k); } else { initialize_mean(inputs, dim, N, k, mean, r); kmeans_debug(mean, dim, k); } /* run it! */ calc_kmeans(inputs, dim, N, k, mean, r); context->isdone = true; } if (context->isnull) PG_RETURN_NULL(); curpos = WinGetCurrentPosition(winobj); PG_RETURN_INT32(context->result[curpos]); }
/* * Import data into GPDB. */ Datum demoprot_import(PG_FUNCTION_ARGS) { extprotocol_t *myData; char *data; int datlen; size_t nread = 0; /* Must be called via the external table format manager */ if (!CALLED_AS_EXTPROTOCOL(fcinfo)) elog(ERROR, "extprotocol_import: not called by external protocol manager"); /* Get our internal description of the protocol */ myData = (extprotocol_t *) EXTPROTOCOL_GET_USER_CTX(fcinfo); if(EXTPROTOCOL_IS_LAST_CALL(fcinfo)) { /* we're done receiving data. close our connection */ if(myData && myData->file) if(fclose(myData->file)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", myData->filename))); PG_RETURN_INT32(0); } if (myData == NULL) { /* first call. do any desired init */ const char *p_name = "demoprot"; DemoUri *parsed_url; char *url = EXTPROTOCOL_GET_URL(fcinfo); myData = palloc(sizeof(extprotocol_t)); myData->url = pstrdup(url); parsed_url = ParseDemoUri(myData->url); myData->filename = pstrdup(parsed_url->path); if(strcasecmp(parsed_url->protocol, p_name) != 0) elog(ERROR, "internal error: demoprot called with a different protocol (%s)", parsed_url->protocol); FreeDemoUri(parsed_url); /* open the destination file (or connect to remote server in other cases) */ myData->file = fopen(myData->filename, "r"); if (myData->file == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("demoprot_import: could not open file \"%s\" for reading: %m", myData->filename), errOmitLocation(true))); EXTPROTOCOL_SET_USER_CTX(fcinfo, myData); } /* ======================================================================= * DO THE IMPORT * ======================================================================= */ data = EXTPROTOCOL_GET_DATABUF(fcinfo); datlen = EXTPROTOCOL_GET_DATALEN(fcinfo); if(datlen > 0) { nread = fread(data, 1, datlen, myData->file); if (ferror(myData->file)) ereport(ERROR, (errcode_for_file_access(), errmsg("demoprot_import: could not write to file \"%s\": %m", myData->filename))); } PG_RETURN_INT32((int)nread); }
/* * Export data out of GPDB. * invoked by GPDB, be careful with C++ exceptions. */ Datum s3_export(PG_FUNCTION_ARGS) { PG_RETURN_INT32(0); }
/* * Datum a is a value from extract_query method and for BTLess* * strategy it is a left-most value. So, use original datum from QueryInfo * to decide to stop scanning or not. Datum b is always from index. */ static Datum gin_btree_compare_prefix(FunctionCallInfo fcinfo) { Datum a = PG_GETARG_DATUM(0); Datum b = PG_GETARG_DATUM(1); QueryInfo *data = (QueryInfo *) PG_GETARG_POINTER(3); int32 res, cmp; cmp = DatumGetInt32(CallerFInfoFunctionCall2( data->typecmp, fcinfo->flinfo, PG_GET_COLLATION(), (data->strategy == BTLessStrategyNumber || data->strategy == BTLessEqualStrategyNumber) ? data->datum : a, b)); switch (data->strategy) { case BTLessStrategyNumber: /* If original datum > indexed one then return match */ if (cmp > 0) res = 0; else res = 1; break; case BTLessEqualStrategyNumber: /* The same except equality */ if (cmp >= 0) res = 0; else res = 1; break; case BTEqualStrategyNumber: if (cmp != 0) res = 1; else res = 0; break; case BTGreaterEqualStrategyNumber: /* If original datum <= indexed one then return match */ if (cmp <= 0) res = 0; else res = 1; break; case BTGreaterStrategyNumber: /* If original datum <= indexed one then return match */ /* If original datum == indexed one then continue scan */ if (cmp < 0) res = 0; else if (cmp == 0) res = -1; else res = 1; break; default: elog(ERROR, "unrecognized strategy number: %d", data->strategy); res = 0; } PG_RETURN_INT32(res); }
/* * Import data into GPDB. * invoked by GPDB, be careful with C++ exceptions. */ Datum s3_import(PG_FUNCTION_ARGS) { S3ExtBase *myData; char *data; int data_len; size_t nread = 0; /* Must be called via the external table format manager */ if (!CALLED_AS_EXTPROTOCOL(fcinfo)) elog(ERROR, "extprotocol_import: not called by external protocol manager"); /* Get our internal description of the protocol */ myData = (S3ExtBase *)EXTPROTOCOL_GET_USER_CTX(fcinfo); if (EXTPROTOCOL_IS_LAST_CALL(fcinfo)) { if (myData) { thread_cleanup(); if (!myData->Destroy()) { ereport(ERROR, (0, errmsg("Failed to cleanup S3 extention"))); } delete myData; } /* * Cleanup function for the XML library. */ xmlCleanupParser(); PG_RETURN_INT32(0); } if (myData == NULL) { /* first call. do any desired init */ curl_global_init(CURL_GLOBAL_ALL); thread_setup(); const char *p_name = "s3"; char *url_with_options = EXTPROTOCOL_GET_URL(fcinfo); char *url = truncate_options(url_with_options); char *config_path = get_opt_s3(url_with_options, "config"); if (!config_path) { // no config path in url, use default value // data_folder/gpseg0/s3/s3.conf config_path = strdup("s3/s3.conf"); } bool result = InitConfig(config_path, ""); if (!result) { free(config_path); ereport(ERROR, (0, errmsg("Can't find config file, please check"))); } else { ClearConfig(); free(config_path); } InitLog(); if (s3ext_accessid == "") { ereport(ERROR, (0, errmsg("ERROR: access id is empty"))); } if (s3ext_secret == "") { ereport(ERROR, (0, errmsg("ERROR: secret is empty"))); } if ((s3ext_segnum == -1) || (s3ext_segid == -1)) { ereport(ERROR, (0, errmsg("ERROR: segment id is invalid"))); } myData = CreateExtWrapper(url); if (!myData || !myData->Init(s3ext_segid, s3ext_segnum, s3ext_chunksize)) { if (myData) delete myData; ereport(ERROR, (0, errmsg("Failed to init S3 extension, segid = " "%d, segnum = %d, please check your " "configurations and net connection", s3ext_segid, s3ext_segnum))); } EXTPROTOCOL_SET_USER_CTX(fcinfo, myData); free(url); } /* ======================================================================= * DO THE IMPORT * ======================================================================= */ data = EXTPROTOCOL_GET_DATABUF(fcinfo); data_len = EXTPROTOCOL_GET_DATALEN(fcinfo); uint64_t readlen = 0; if (data_len > 0) { readlen = data_len; if (!myData->TransferData(data, readlen)) ereport(ERROR, (0, errmsg("s3_import: could not read data"))); nread = (size_t)readlen; // S3DEBUG("read %d data from S3", nread); } PG_RETURN_INT32((int)nread); }
/* * lo_export - * exports an (inversion) large object. */ Datum lo_export(PG_FUNCTION_ARGS) { Oid lobjId = PG_GETARG_OID(0); text *filename = PG_GETARG_TEXT_PP(1); int fd; int nbytes, tmp; char buf[BUFSIZE]; char fnamebuf[MAXPGPATH]; LargeObjectDesc *lobj; mode_t oumask; #ifndef ALLOW_DANGEROUS_LO_FUNCTIONS if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to use server-side lo_export()"), errhint("Anyone can use the client-side lo_export() provided by libpq."))); #endif CreateFSContext(); /* * open the inversion object (no need to test for failure) */ lobj = inv_open(lobjId, INV_READ, fscxt); /* * open the file to be written to * * Note: we reduce backend's normal 077 umask to the slightly friendlier * 022. This code used to drop it all the way to 0, but creating * world-writable export files doesn't seem wise. */ text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf)); oumask = umask(S_IWGRP | S_IWOTH); fd = OpenTransientFile(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); umask(oumask); if (fd < 0) ereport(ERROR, (errcode_for_file_access(), errmsg("could not create server file \"%s\": %m", fnamebuf))); /* * read in from the inversion file and write to the filesystem */ while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0) { tmp = write(fd, buf, nbytes); if (tmp != nbytes) ereport(ERROR, (errcode_for_file_access(), errmsg("could not write server file \"%s\": %m", fnamebuf))); } CloseTransientFile(fd); inv_close(lobj); PG_RETURN_INT32(1); }
Datum adaptive_length(PG_FUNCTION_ARGS) { PG_RETURN_INT32(VARSIZE((AdaptiveCounter)PG_GETARG_BYTEA_P(0))); }
/* * 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); }
Datum adaptive_get_item_size(PG_FUNCTION_ARGS) { /* return the error rate of the counter */ PG_RETURN_INT32(((AdaptiveCounter)PG_GETARG_BYTEA_P(0))->itemSize); }
Datum btrecordcmp(PG_FUNCTION_ARGS) { PG_RETURN_INT32(record_cmp(fcinfo)); }
/** * An interface to re-weigh an existing session on the master and all backends. * Input: * session id - what session is statement on? * command count - what is the command count of statement. * weight - int, what should be the new priority of this statement. * Output: * number of backends whose weights were changed by this call. */ Datum gp_adjust_priority_int(PG_FUNCTION_ARGS) { int32 session_id = PG_GETARG_INT32(0); int32 command_count = PG_GETARG_INT32(1); int32 wt = PG_GETARG_INT32(2); int numfound = 0; StatementId sid; if (!gp_enable_resqueue_priority) elog(ERROR, "Query prioritization is disabled."); if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("only superuser can re-prioritize a query after it has begun execution")))); if (Gp_role == GP_ROLE_UTILITY) elog(ERROR, "Query prioritization does not work in utility mode."); if (wt <= 0) elog(ERROR, "Weight of statement must be greater than 0."); init(&sid, session_id, command_count); if (Gp_role == GP_ROLE_DISPATCH) { int i = 0; CdbPgResults cdb_pgresults = {NULL, 0}; char cmd[255]; /* * Make sure the session exists before dispatching */ for (i = 0; i < backoffSingleton->numEntries; i++) { BackoffBackendSharedEntry *se = getBackoffEntryRW(i); if (equalStatementId(&se->statementId, &sid)) { if (gp_debug_resqueue_priority) { elog(LOG, "changing weight of (%d:%d) from %d to %d", se->statementId.sessionId, se->statementId.commandCount, se->weight, wt); } se->weight = wt; numfound++; } } if (numfound == 0) elog(ERROR, "Did not find any backend entries for session %d, command count %d.", session_id, command_count); /* * Ok, it exists, dispatch the command to the segDBs. */ sprintf(cmd, "select gp_adjust_priority(%d,%d,%d)", session_id, command_count, wt); CdbDispatchCommand(cmd, DF_WITH_SNAPSHOT, &cdb_pgresults); for (i = 0; i < cdb_pgresults.numResults; i++) { struct pg_result *pgresult = cdb_pgresults.pg_results[i]; if (PQresultStatus(pgresult) != PGRES_TUPLES_OK) { cdbdisp_clearCdbPgResults(&cdb_pgresults); elog(ERROR, "gp_adjust_priority: resultStatus not tuples_Ok"); } else { int j; for (j = 0; j < PQntuples(pgresult); j++) { int retvalue = 0; retvalue = atoi(PQgetvalue(pgresult, j, 0)); numfound += retvalue; } } } cdbdisp_clearCdbPgResults(&cdb_pgresults); } else /* Gp_role == EXECUTE */ { /* * Find number of backends working on behalf of this session and * distribute the weight evenly. */ int i = 0; Assert(Gp_role == GP_ROLE_EXECUTE); for (i = 0; i < backoffSingleton->numEntries; i++) { BackoffBackendSharedEntry *se = getBackoffEntryRW(i); if (equalStatementId(&se->statementId, &sid)) { if (gp_debug_resqueue_priority) { elog(LOG, "changing weight of (%d:%d) from %d to %d", se->statementId.sessionId, se->statementId.commandCount, se->weight, wt); } se->weight = wt; numfound++; } } if (gp_debug_resqueue_priority && numfound == 0) { elog(LOG, "did not find any matching backends on segments"); } } PG_RETURN_INT32(numfound); }
/* * SQL-callable function to scan through an index and summarize all ranges * that are not currently summarized. */ Datum brin_summarize_new_values(PG_FUNCTION_ARGS) { Oid indexoid = PG_GETARG_OID(0); Oid heapoid; Relation indexRel; Relation heapRel; double numSummarized = 0; if (RecoveryInProgress()) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("recovery is in progress"), errhint("BRIN control functions cannot be executed during recovery."))); /* * We must lock table before index to avoid deadlocks. However, if the * passed indexoid isn't an index then IndexGetRelation() will fail. * Rather than emitting a not-very-helpful error message, postpone * complaining, expecting that the is-it-an-index test below will fail. */ heapoid = IndexGetRelation(indexoid, true); if (OidIsValid(heapoid)) heapRel = heap_open(heapoid, ShareUpdateExclusiveLock); else heapRel = NULL; indexRel = index_open(indexoid, ShareUpdateExclusiveLock); /* Must be a BRIN index */ if (indexRel->rd_rel->relkind != RELKIND_INDEX || indexRel->rd_rel->relam != BRIN_AM_OID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a BRIN index", RelationGetRelationName(indexRel)))); /* User must own the index (comparable to privileges needed for VACUUM) */ if (!pg_class_ownercheck(indexoid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(indexRel)); /* * Since we did the IndexGetRelation call above without any lock, it's * barely possible that a race against an index drop/recreation could have * netted us the wrong table. Recheck. */ if (heapRel == NULL || heapoid != IndexGetRelation(indexoid, false)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_TABLE), errmsg("could not open parent table of index %s", RelationGetRelationName(indexRel)))); /* OK, do it */ brinsummarize(indexRel, heapRel, &numSummarized, NULL); relation_close(indexRel, ShareUpdateExclusiveLock); relation_close(heapRel, ShareUpdateExclusiveLock); PG_RETURN_INT32((int32) numSummarized); }
/* * array_position_common * Common code for array_position and array_position_start * * These are separate wrappers for the sake of opr_sanity regression test. * They are not strict so we have to test for null inputs explicitly. */ static Datum array_position_common(FunctionCallInfo fcinfo) { ArrayType *array; Oid collation = PG_GET_COLLATION(); Oid element_type; Datum searched_element, value; bool isnull; int position, position_min; bool found = false; TypeCacheEntry *typentry; ArrayMetaState *my_extra; bool null_search; ArrayIterator array_iterator; if (PG_ARGISNULL(0)) PG_RETURN_NULL(); array = PG_GETARG_ARRAYTYPE_P(0); element_type = ARR_ELEMTYPE(array); /* * We refuse to search for elements in multi-dimensional arrays, since we * have no good way to report the element's location in the array. */ if (ARR_NDIM(array) > 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("searching for elements in multidimensional arrays is not supported"))); if (PG_ARGISNULL(1)) { /* fast return when the array doesn't have nulls */ if (!array_contains_nulls(array)) PG_RETURN_NULL(); searched_element = (Datum) 0; null_search = true; } else { searched_element = PG_GETARG_DATUM(1); null_search = false; } position = (ARR_LBOUND(array))[0] - 1; /* figure out where to start */ if (PG_NARGS() == 3) { if (PG_ARGISNULL(2)) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("initial position must not be null"))); position_min = PG_GETARG_INT32(2); } else position_min = (ARR_LBOUND(array))[0]; /* * We arrange to look up type info for array_create_iterator only once per * series of calls, assuming the element type doesn't change underneath * us. */ my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; if (my_extra == NULL) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(ArrayMetaState)); my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra; my_extra->element_type = ~element_type; } if (my_extra->element_type != element_type) { get_typlenbyvalalign(element_type, &my_extra->typlen, &my_extra->typbyval, &my_extra->typalign); typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO); if (!OidIsValid(typentry->eq_opr_finfo.fn_oid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify an equality operator for type %s", format_type_be(element_type)))); my_extra->element_type = element_type; fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc, fcinfo->flinfo->fn_mcxt); } /* Examine each array element until we find a match. */ array_iterator = array_create_iterator(array, 0, my_extra); while (array_iterate(array_iterator, &value, &isnull)) { position++; /* skip initial elements if caller requested so */ if (position < position_min) continue; /* * Can't look at the array element's value if it's null; but if we * search for null, we have a hit and are done. */ if (isnull || null_search) { if (isnull && null_search) { found = true; break; } else continue; } /* not nulls, so run the operator */ if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation, searched_element, value))) { found = true; break; } } array_free_iterator(array_iterator); /* Avoid leaking memory when handed toasted input */ PG_FREE_IF_COPY(array, 0); if (!found) PG_RETURN_NULL(); PG_RETURN_INT32(position); }
Datum pg_backend_pid(PG_FUNCTION_ARGS) { PG_RETURN_INT32(MyProcPid); }
/** * @fn Datum repack_apply(PG_FUNCTION_ARGS) * @brief Apply operations in log table into temp table. * * repack_apply(sql_peek, sql_insert, sql_delete, sql_update, sql_pop, count) * * @param sql_peek SQL to pop tuple from log table. * @param sql_insert SQL to insert into temp table. * @param sql_delete SQL to delete from temp table. * @param sql_update SQL to update temp table. * @param sql_pop SQL to bulk-delete tuples from log table. * @param count Max number of operations, or no count iff <=0. * @retval Number of performed operations. */ Datum repack_apply(PG_FUNCTION_ARGS) { #define DEFAULT_PEEK_COUNT 1000 const char *sql_peek = PG_GETARG_CSTRING(0); const char *sql_insert = PG_GETARG_CSTRING(1); const char *sql_delete = PG_GETARG_CSTRING(2); const char *sql_update = PG_GETARG_CSTRING(3); /* sql_pop, the fourth arg, will be used in the loop below */ int32 count = PG_GETARG_INT32(5); SPIPlanPtr plan_peek = NULL; SPIPlanPtr plan_insert = NULL; SPIPlanPtr plan_delete = NULL; SPIPlanPtr plan_update = NULL; uint32 n, i; Oid argtypes_peek[1] = { INT4OID }; Datum values_peek[1]; const char nulls_peek[1] = { 0 }; StringInfoData sql_pop; initStringInfo(&sql_pop); /* authority check */ must_be_superuser("repack_apply"); /* connect to SPI manager */ repack_init(); /* peek tuple in log */ plan_peek = repack_prepare(sql_peek, 1, argtypes_peek); for (n = 0;;) { int ntuples; SPITupleTable *tuptable; TupleDesc desc; Oid argtypes[3]; /* id, pk, row */ Datum values[3]; /* id, pk, row */ bool nulls[3]; /* id, pk, row */ /* peek tuple in log */ if (count <= 0) values_peek[0] = Int32GetDatum(DEFAULT_PEEK_COUNT); else values_peek[0] = Int32GetDatum(Min(count - n, DEFAULT_PEEK_COUNT)); execute_plan(SPI_OK_SELECT, plan_peek, values_peek, nulls_peek); if (SPI_processed <= 0) break; /* copy tuptable because we will call other sqls. */ ntuples = SPI_processed; tuptable = SPI_tuptable; desc = tuptable->tupdesc; argtypes[0] = SPI_gettypeid(desc, 1); /* id */ argtypes[1] = SPI_gettypeid(desc, 2); /* pk */ argtypes[2] = SPI_gettypeid(desc, 3); /* row */ resetStringInfo(&sql_pop); appendStringInfoString(&sql_pop, PG_GETARG_CSTRING(4)); for (i = 0; i < ntuples; i++, n++) { HeapTuple tuple; char *pkid; tuple = tuptable->vals[i]; values[0] = SPI_getbinval(tuple, desc, 1, &nulls[0]); values[1] = SPI_getbinval(tuple, desc, 2, &nulls[1]); values[2] = SPI_getbinval(tuple, desc, 3, &nulls[2]); pkid = SPI_getvalue(tuple, desc, 1); Assert(pkid != NULL); if (nulls[1]) { /* INSERT */ if (plan_insert == NULL) plan_insert = repack_prepare(sql_insert, 1, &argtypes[2]); execute_plan(SPI_OK_INSERT, plan_insert, &values[2], (nulls[2] ? "n" : " ")); } else if (nulls[2]) { /* DELETE */ if (plan_delete == NULL) plan_delete = repack_prepare(sql_delete, 1, &argtypes[1]); execute_plan(SPI_OK_DELETE, plan_delete, &values[1], (nulls[1] ? "n" : " ")); } else { /* UPDATE */ if (plan_update == NULL) plan_update = repack_prepare(sql_update, 2, &argtypes[1]); execute_plan(SPI_OK_UPDATE, plan_update, &values[1], (nulls[1] ? "n" : " ")); } /* Add the primary key ID of each row from the log * table we have processed so far to this * DELETE ... IN (...) query string, so we * can delete all the rows we have processed at-once. */ if (i == 0) appendStringInfoString(&sql_pop, pkid); else appendStringInfo(&sql_pop, ",%s", pkid); pfree(pkid); } /* i must be > 0 (and hence we must have some rows to delete) * since SPI_processed > 0 */ Assert(i > 0); appendStringInfoString(&sql_pop, ");"); /* Bulk delete of processed rows from the log table */ execute(SPI_OK_DELETE, sql_pop.data); SPI_freetuptable(tuptable); } SPI_finish(); PG_RETURN_INT32(n); }