Пример #1
0
/*
 * 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;
}
Пример #2
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;
}
Пример #4
0
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);
}
Пример #5
0
/*
 * 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;
}
Пример #6
0
/*
 * 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;
}
Пример #7
0
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);
}
Пример #8
0
/*
 * 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);
}