/* * Create partitions and return an OID of the partition that contain value */ Oid create_partitions(Oid relid, Datum value, Oid value_type, bool *crashed) { int ret; RangeEntry *ranges; Datum vals[2]; Oid oids[] = {OIDOID, value_type}; bool nulls[] = {false, false}; char *sql; bool found; int pos; PartRelationInfo *prel; RangeRelation *rangerel; FmgrInfo cmp_func; char *schema; *crashed = false; schema = get_extension_schema(); prel = get_pathman_relation_info(relid, NULL); rangerel = get_pathman_range_relation(relid, NULL); ranges = dsm_array_get_pointer(&rangerel->ranges); /* Comparison function */ cmp_func = *get_cmp_func(value_type, prel->atttype); vals[0] = ObjectIdGetDatum(relid); vals[1] = value; /* Perform PL procedure */ sql = psprintf("SELECT %s.append_partitions_on_demand_internal($1, $2)", schema); PG_TRY(); { ret = SPI_execute_with_args(sql, 2, oids, vals, nulls, false, 0); if (ret > 0) { /* Update relation info */ free_dsm_array(&rangerel->ranges); free_dsm_array(&prel->children); load_check_constraints(relid, GetCatalogSnapshot(relid)); } } PG_CATCH(); { elog(WARNING, "Attempt to create new partitions failed"); if (crashed != NULL) *crashed = true; return 0; } PG_END_TRY(); /* Repeat binary search */ ranges = dsm_array_get_pointer(&rangerel->ranges); pos = range_binary_search(rangerel, &cmp_func, value, &found); if (found) return ranges[pos].child_oid; return 0; }
Datum variant_cast_out(PG_FUNCTION_ARGS) { Oid targettypid = get_fn_expr_rettype(fcinfo->flinfo); VariantInt vi; Datum out; if( PG_ARGISNULL(0) ) PG_RETURN_NULL(); /* No reason to format type name, so use IOFunc_input instead of IOFunc_output */ vi = make_variant_int(PG_GETARG_VARIANT(0), fcinfo, IOFunc_input); /* If original was NULL then we MUST return NULL */ if( vi->isnull ) PG_RETURN_NULL(); /* If our types match exactly we don't need to cast */ if( vi->typid == targettypid ) PG_RETURN_DATUM(vi->data); /* Keep cruft localized to just here */ { bool do_pop; int ret; bool isnull; MemoryContext cctx = CurrentMemoryContext; HeapTuple tup; StringInfoData cmdd; StringInfo cmd = &cmdd; char *nulls = " "; do_pop = _SPI_conn(); initStringInfo(cmd); appendStringInfo( cmd, "SELECT $1::%s", format_type_be(targettypid) ); /* command, nargs, Oid *argument_types, *values, *nulls, read_only, count */ if( (ret = SPI_execute_with_args( cmd->data, 1, &vi->typid, &vi->data, nulls, true, 0 )) != SPI_OK_SELECT ) elog( ERROR, "SPI_execute_with_args returned %s", SPI_result_code_string(ret)); /* * Make a copy of result tuple in previous memory context. Copying the * entire tuple is wasteful; it would be better to only copy the actual * attribute; but in this case the difference isn't very large. */ MemoryContextSwitchTo(cctx); tup = heap_copytuple(SPI_tuptable->vals[0]); out = heap_getattr(tup, 1, SPI_tuptable->tupdesc, &isnull); // getTypeOutputInfo(typoid, &foutoid, &typisvarlena); /* Remember this frees everything palloc'd since our connect/push call */ _SPI_disc(do_pop); } /* End cruft */ PG_RETURN_DATUM(out); }
long getTokenFrequency(char *token) { bool isNull; text *tokenTextP; Datum tokenDatum, countDatum; Datum *args; Oid *argTypes; int spiResultCode; long count; SPI_connect(); tokenTextP = cstring_to_text(token); tokenDatum = PointerGetDatum(tokenTextP); argTypes = palloc(sizeof(Oid)); argTypes[0] = TEXTOID; args = (Datum *) palloc(sizeof(Datum)); args[0] = tokenDatum; count = -1; spiResultCode = SPI_execute_with_args( "SELECT count::integer FROM TokenDictionaryMaterialized WHERE token = $1", 1, argTypes, args, NULL, true, 1 ); if(spiResultCode == SPI_OK_SELECT) { if(SPI_processed > 0) { countDatum = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isNull); count = DatumGetInt32(countDatum); } else { ereport(ERROR, (errmsg("Token '%s' is not in TokenDictionaryMaterialized", token))); } } else { ereport(ERROR, (errmsg("Unable to query TokenDictionaryMaterialized table. Does it exist?"))); } pfree(args); pfree(argTypes); pfree(tokenTextP); SPI_finish(); return count; }
void execute_with_args(int expected, const char *src, int nargs, Oid argtypes[], Datum values[], const bool nulls[]) { int ret; int i; char c_nulls[FUNC_MAX_ARGS]; for (i = 0; i < nargs; i++) c_nulls[i] = (nulls[i] ? 'n' : ' '); ret = SPI_execute_with_args(src, nargs, argtypes, values, c_nulls, false, 0); if EXEC_FAILED(ret, expected) elog(ERROR, "query failed: (sql=%s, code=%d, expected=%d)", src, ret, expected); }
/* * variant_cmp_int: Compare two variants */ static int variant_cmp_int(FunctionCallInfo fcinfo) { Variant l, r; VariantInt li; VariantInt ri; int out; Assert(fcinfo->flinfo->fn_strict); /* Must not be callable on NULL input */ l = PG_GETARG_VARIANT(0); r = PG_GETARG_VARIANT(1); /* * Presumably if both inputs are binary equal then they are in fact equal. * The only problem is two variants storing NULL would be binary equal but * can't be treated as-such. Given that issue, it doesn't seem worth trying * to optimize this. * * Note that we're not trying to play tricks with not detoasting or * un-packing, unlike variant_image_eq(). */ #ifdef NOT_USED if(VARSIZE(l) == VARSIZE(r) && memcmp(l, r, VARSIZE(l)) == 0) return 0; #endif /* * We don't care about IO function but must specify something * * TODO: Improve caching so it will handle more than just one type :( */ li = make_variant_int(l, fcinfo, IOFunc_input); ri = make_variant_int(r, fcinfo, IOFunc_input); /* * We need to special-case IS DISTINCT, because it considers NULL to be the * same as NULL. */ if(fcinfo->flinfo->fn_expr && IsA(fcinfo->flinfo->fn_expr, DistinctExpr)) { if( li->isnull && ri->isnull ) return 0; else if( li->isnull || ri->isnull ) return -1; } else if(li->isnull || ri->isnull ) PG_RETURN_NULL(); /* TODO: If both variants are of the same type try comparing directly */ /* TODO: Support Transform_null_equals */ /* Do comparison via SPI */ /* TODO: cache this */ { bool do_pop; int ret; char *cmd; Oid types[2]; Datum values[2]; bool nulls[2]; do_pop = _SPI_conn(); cmd = "SELECT CASE WHEN $1 = $2 THEN 0 WHEN $1 < $2 THEN -1 ELSE 1 END::int"; types[0] = li->typid; values[0] = li->data; nulls[0] = li->isnull; types[1] = ri->typid; values[1] = ri->data; nulls[1] = ri->isnull; if( (ret = SPI_execute_with_args( cmd, 2, types, values, nulls, true, /* read-only */ 0 /* count */ )) != SPI_OK_SELECT ) elog( ERROR, "SPI_execute_with_args returned %s", SPI_result_code_string(ret)); /* Note 0 vs 1 based numbering */ Assert(SPI_tuptable->tupdesc->attrs[0]->atttypid == INT4OID); /* Don't need to copy the tuple because int is pass by value */ out = DatumGetInt32( heap_getattr(SPI_tuptable->vals[0], 1, SPI_tuptable->tupdesc, &fcinfo->isnull) ); _SPI_disc(do_pop); } return out; }
/* * variant_get_variant_name: Return the name of a named variant */ char * variant_get_variant_name(int typmod, Oid org_typid, bool ignore_storage) { Datum values[2]; MemoryContext cctx = CurrentMemoryContext; bool do_pop = _SPI_conn(); bool isnull; Oid types[2] = {INT4OID, REGTYPEOID}; char *cmd; int nargs; int ret; Datum result; char *out; values[0] = Int32GetDatum(typmod); /* * There's a race condition here; someone could be attempting to remove an * allowed type from this registered variant or even remove it entirely. We * could avoid that by taking a share/keyshare lock here and taking the * appropriate blocking lock when modifying the registration record. Doing * that would probably be quite bad though; not only are type IO and typmod * IO routines assumed to be non-volatile, taking such a lock would end up * generating a lot of lock updates to the registration rows. * * Since the whole purpose of registration is to handle the issue of someone * attempting to drop a type that has made it into a variant in a table * column, which we can't completely handle anyway, I don't think it's worth * it to lock the rows. */ if(ignore_storage) { cmd = "SELECT variant_name, variant_enabled, storage_allowed FROM variant._registered WHERE variant_typmod = $1"; nargs = 1; } else { cmd = "SELECT variant_name, variant_enabled, storage_allowed, allowed_types @> array[ $2 ] FROM variant._registered WHERE variant_typmod = $1"; nargs = 2; values[1] = ObjectIdGetDatum(org_typid); } /* command, nargs, Oid *argument_types, *values, *nulls, read_only, count */ if( (ret = SPI_execute_with_args( cmd, nargs, types, values, " ", true, 0 )) != SPI_OK_SELECT ) elog( ERROR, "SPI_execute_with_args(%s) returned %s", cmd, SPI_result_code_string(ret)); Assert( SPI_tuptable ); if ( SPI_processed > 1 ) ereport(ERROR, ( errmsg( "Got %u records for variant typmod %i", SPI_processed, typmod ), errhint( "This means _variant._registered is corrupted" ) ) ); if ( SPI_processed < 1 ) elog( ERROR, "invalid typmod %i", typmod ); /* Note 0 vs 1 based numbering */ Assert(SPI_tuptable->tupdesc->attrs[0]->atttypid == VARCHAROID); Assert(SPI_tuptable->tupdesc->attrs[1]->atttypid == BOOLOID); Assert(SPI_tuptable->tupdesc->attrs[2]->atttypid == BOOLOID); result = heap_getattr( SPI_tuptable->vals[0], 1, SPI_tuptable->tupdesc, &isnull ); if( isnull ) ereport( ERROR, ( errmsg( "Found NULL variant_name for typmod %i", typmod ), errhint( "This should never happen; is _variant._registered corrupted?" ) ) ); MemoryContextSwitchTo(cctx); out = text_to_cstring(DatumGetTextP(result)); result = heap_getattr( SPI_tuptable->vals[0], 2, SPI_tuptable->tupdesc, &isnull ); if( !DatumGetBool(result) ) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "variant.variant(%s) is disabled", out ) ) ); /* * If storage is allowed, then throw an error if we don't know what our * original type is, or if that type is not listed as allowed. */ if(!ignore_storage) { result = heap_getattr( SPI_tuptable->vals[0], 3, SPI_tuptable->tupdesc, &isnull ); if( DatumGetBool(result) ) { if( org_typid == InvalidOid) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "Unable to determine original type" ) ) ); result = heap_getattr( SPI_tuptable->vals[0], 4, SPI_tuptable->tupdesc, &isnull ); if( !DatumGetBool(result) ) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "type %s is not allowed in variant.variant(%s)", format_type_be(org_typid), out ), errhint( "you can permanently allow a type to be used by calling variant.allow_type()" ) ) ); } } _SPI_disc(do_pop); /* pfree's all SPI stuff */ return out; }
Datum variant_typmod_in(PG_FUNCTION_ARGS) { ArrayType *arr = PG_GETARG_ARRAYTYPE_P(0); Datum *elem_values; int arr_nelem; char *inputCString; Datum inputDatum; Datum out; Assert(fcinfo->flinfo->fn_strict); /* Must be strict */ deconstruct_array(arr, CSTRINGOID, -2, false, 'c', /* elmlen, elmbyval, elmalign */ &elem_values, NULL, &arr_nelem); /* elements, nulls, number_of_elements */ /* TODO: Sanity check array */ /* PointerGetDatum is equivalent to TextGetDatum, which doesn't exist */ inputCString = DatumGetCString(elem_values[0]); inputDatum = PointerGetDatum( cstring_to_text( inputCString ) ); /* TODO: cache this stuff */ /* Keep cruft localized to just here */ { bool do_pop = _SPI_conn(); bool isnull; int ret; Oid type = TEXTOID; /* This should arguably be FOR KEY SHARE. See comment in variant_get_variant_name() */ char *cmd = "SELECT variant_typmod, variant_enabled FROM variant._registered WHERE lower(variant_name) = lower($1)"; /* command, nargs, Oid *argument_types, *values, *nulls, read_only, count */ if( (ret = SPI_execute_with_args( cmd, 1, &type, &inputDatum, " ", true, 0 )) != SPI_OK_SELECT ) elog( ERROR, "SPI_execute_with_args(%s) returned %s", cmd, SPI_result_code_string(ret)); Assert( SPI_tuptable ); if ( SPI_processed > 1 ) ereport(ERROR, ( errmsg( "Got %u records for variant.variant(%s)", SPI_processed, inputCString ), errhint( "This means _variant._registered is corrupted" ) ) ); if ( SPI_processed < 1 ) elog( ERROR, "variant.variant(%s) is not registered", inputCString ); /* Note 0 vs 1 based numbering */ Assert(SPI_tuptable->tupdesc->attrs[0]->atttypid == INT4OID); Assert(SPI_tuptable->tupdesc->attrs[1]->atttypid == BOOLOID); out = heap_getattr( SPI_tuptable->vals[0], 2, SPI_tuptable->tupdesc, &isnull ); if( !DatumGetBool(out) ) ereport( ERROR, ( errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "variant.variant(%s) is disabled", inputCString ) ) ); /* Don't need to copy the tuple because int is pass by value */ out = heap_getattr( SPI_tuptable->vals[0], 1, SPI_tuptable->tupdesc, &isnull ); if( isnull ) ereport( ERROR, ( errmsg( "Found NULL variant_typmod for variant.variant(%s)", inputCString ), errhint( "This should never happen; is _variant._registered corrupted?" ) ) ); _SPI_disc(do_pop); } PG_RETURN_INT32(out); }
/* * import_stats_from_file * load data from file or stdin into work table, and delete unnecessary * records. */ static void import_stats_from_file(char *filename, char *nspname, char *relname, char *attname) { StringInfoData buf; List *parsetree_list; uint64 processed; Datum values[3]; Oid argtypes[3] = { CSTRINGOID, CSTRINGOID, CSTRINGOID }; char nulls[3] = { 'n', 'n', 'n' }; int nargs; int ret; /* for debug use */ elog(DEBUG3, "%s() f=%s n=%s r=%s a=%s", __FUNCTION__, filename ? filename : "(null)", nspname ? nspname : "(null)", relname ? relname : "(null)", attname ? attname : "(null)"); /* * Construct COPY statement. NULL for filename indicates that source is * stdin. */ initStringInfo(&buf); appendStringInfoString(&buf, "COPY dbms_stats_work_stats FROM "); if (filename == NULL) appendStringInfoString(&buf, "stdin"); else appendLiteral(&buf, filename); appendStringInfoString(&buf, " (FORMAT 'binary')"); /* Execute COPY FROM command. */ parsetree_list = pg_parse_query(buf.data); #if PG_VERSION_NUM >= 90300 DoCopy((CopyStmt *)linitial(parsetree_list), buf.data, &processed); #else processed = DoCopy((CopyStmt *)linitial(parsetree_list), buf.data); #endif if (processed == 0) elog(ERROR, "no data to be imported"); /* * Delete the statistics other than the specified object's statistic from * the temp table. We can skip DELETEing staging data when schemaname is * NULL, because it means database-wise import. */ if (nspname == NULL) return; resetStringInfo(&buf); appendStringInfoString(&buf, "DELETE FROM dbms_stats_work_stats " " WHERE nspname <> $1::text "); values[0] = CStringGetDatum(nspname); nulls[0] = 't'; nargs = 1; if (relname != NULL) { values[1] = CStringGetDatum(relname); nulls[1] = 't'; nargs++; appendStringInfoString(&buf, " OR (relname <> $2::text) "); if (attname != NULL) { values[2] = CStringGetDatum(attname); nulls[2] = 't'; nargs++; appendStringInfoString(&buf, " OR (attname <> $3::text) "); } } ret = SPI_execute_with_args(buf.data, nargs, argtypes, values, nulls, false, 0); if (ret != SPI_OK_DELETE) elog(ERROR, "pg_dbms_stats: SPI_execute_with_args => %d", ret); }