Example #1
0
Datum
plhaskell_call_handler(PG_FUNCTION_ARGS)
{
    Oid fn_oid = fcinfo->flinfo->fn_oid;
    HeapTuple procTup = SearchSysCache1(PROCOID, ObjectIdGetDatum(fn_oid));

    bool isnull;
    Datum prosrcdatum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull);
    if (isnull)
        elog(ERROR, "null prosrc");

	Form_pg_proc procStruct = (Form_pg_proc) GETSTRUCT(procTup);

    Oid *argTypes;
    char **argNames;
    char *argModes;
    int argCount = get_func_arg_info(procTup, &argTypes, &argNames, &argModes);

    Datum *datum = malloc(sizeof(Datum));

    int r = plhaskell_test(
        TextDatumGetCString(prosrcdatum),
        procStruct->prorettype,
        datum,
        argTypes,
        argCount,
        fcinfo->arg,
        sizeof(Datum)
    );

    if (r < 0)
      elog(ERROR, (const char *) (*datum));

    return *datum;
}
/*
 * Create a new PLyProcedure structure
 */
static PLyProcedure *
PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
{
	char		procName[NAMEDATALEN + 256];
	Form_pg_proc procStruct;
	PLyProcedure *volatile proc;
	char	   *volatile procSource = NULL;
	Datum		prosrcdatum;
	bool		isnull;
	int			i,
				rv;

	procStruct = (Form_pg_proc) GETSTRUCT(procTup);
	rv = snprintf(procName, sizeof(procName),
				  "__plpython_procedure_%s_%u",
				  NameStr(procStruct->proname),
				  fn_oid);
	if (rv >= sizeof(procName) || rv < 0)
		elog(ERROR, "procedure name would overrun buffer");

	proc = PLy_malloc(sizeof(PLyProcedure));
	proc->proname = PLy_strdup(NameStr(procStruct->proname));
	proc->pyname = PLy_strdup(procName);
	proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
	proc->fn_tid = procTup->t_self;
	/* Remember if function is STABLE/IMMUTABLE */
	proc->fn_readonly =
		(procStruct->provolatile != PROVOLATILE_VOLATILE);
	PLy_typeinfo_init(&proc->result);
	for (i = 0; i < FUNC_MAX_ARGS; i++)
		PLy_typeinfo_init(&proc->args[i]);
	proc->nargs = 0;
	proc->code = proc->statics = NULL;
	proc->globals = NULL;
	proc->is_setof = procStruct->proretset;
	proc->setof = NULL;
	proc->src = NULL;
	proc->argnames = NULL;

	PG_TRY();
	{
		/*
		 * get information required for output conversion of the return value,
		 * but only if this isn't a trigger.
		 */
		if (!is_trigger)
		{
			HeapTuple	rvTypeTup;
			Form_pg_type rvTypeStruct;

			rvTypeTup = SearchSysCache1(TYPEOID,
								   ObjectIdGetDatum(procStruct->prorettype));
			if (!HeapTupleIsValid(rvTypeTup))
				elog(ERROR, "cache lookup failed for type %u",
					 procStruct->prorettype);
			rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);

			/* Disallow pseudotype result, except for void or record */
			if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
			{
				if (procStruct->prorettype == TRIGGEROID)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("trigger functions can only be called as triggers")));
				else if (procStruct->prorettype != VOIDOID &&
						 procStruct->prorettype != RECORDOID)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						  errmsg("PL/Python functions cannot return type %s",
								 format_type_be(procStruct->prorettype))));
			}

			if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE ||
				procStruct->prorettype == RECORDOID)
			{
				/*
				 * Tuple: set up later, during first call to
				 * PLy_function_handler
				 */
				proc->result.out.d.typoid = procStruct->prorettype;
				proc->result.out.d.typmod = -1;
				proc->result.is_rowtype = 2;
			}
			else
			{
				/* do the real work */
				PLy_output_datum_func(&proc->result, rvTypeTup);
			}

			ReleaseSysCache(rvTypeTup);
		}

		/*
		 * Now get information required for input conversion of the
		 * procedure's arguments.  Note that we ignore output arguments here.
		 * If the function returns record, those I/O functions will be set up
		 * when the function is first called.
		 */
		if (procStruct->pronargs)
		{
			Oid		   *types;
			char	  **names,
					   *modes;
			int			i,
						pos,
						total;

			/* extract argument type info from the pg_proc tuple */
			total = get_func_arg_info(procTup, &types, &names, &modes);

			/* count number of in+inout args into proc->nargs */
			if (modes == NULL)
				proc->nargs = total;
			else
			{
				/* proc->nargs was initialized to 0 above */
				for (i = 0; i < total; i++)
				{
					if (modes[i] != PROARGMODE_OUT &&
						modes[i] != PROARGMODE_TABLE)
						(proc->nargs)++;
				}
			}

			proc->argnames = (char **) PLy_malloc0(sizeof(char *) * proc->nargs);
			for (i = pos = 0; i < total; i++)
			{
				HeapTuple	argTypeTup;
				Form_pg_type argTypeStruct;

				if (modes &&
					(modes[i] == PROARGMODE_OUT ||
					 modes[i] == PROARGMODE_TABLE))
					continue;	/* skip OUT arguments */

				Assert(types[i] == procStruct->proargtypes.values[pos]);

				argTypeTup = SearchSysCache1(TYPEOID,
											 ObjectIdGetDatum(types[i]));
				if (!HeapTupleIsValid(argTypeTup))
					elog(ERROR, "cache lookup failed for type %u", types[i]);
				argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);

				/* check argument type is OK, set up I/O function info */
				switch (argTypeStruct->typtype)
				{
					case TYPTYPE_PSEUDO:
						/* Disallow pseudotype argument */
						ereport(ERROR,
								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						  errmsg("PL/Python functions cannot accept type %s",
								 format_type_be(types[i]))));
						break;
					case TYPTYPE_COMPOSITE:
						/* we'll set IO funcs at first call */
						proc->args[pos].is_rowtype = 2;
						break;
					default:
						PLy_input_datum_func(&(proc->args[pos]),
											 types[i],
											 argTypeTup);
						break;
				}

				/* get argument name */
				proc->argnames[pos] = names ? PLy_strdup(names[i]) : NULL;

				ReleaseSysCache(argTypeTup);

				pos++;
			}
		}

		/*
		 * get the text of the function.
		 */
		prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
									  Anum_pg_proc_prosrc, &isnull);
		if (isnull)
			elog(ERROR, "null prosrc");
		procSource = TextDatumGetCString(prosrcdatum);

		PLy_procedure_compile(proc, procSource);

		pfree(procSource);
		procSource = NULL;
	}
	PG_CATCH();
	{
		PLy_procedure_delete(proc);
		if (procSource)
			pfree(procSource);

		PG_RE_THROW();
	}
	PG_END_TRY();

	return proc;
}
Example #3
0
Datum
plpgsql_validator(PG_FUNCTION_ARGS)
{
	Oid			funcoid = PG_GETARG_OID(0);
	HeapTuple	tuple;
	Form_pg_proc proc;
	char		functyptype;
	int			numargs;
	Oid		   *argtypes;
	char	  **argnames;
	char	   *argmodes;
	bool		istrigger = false;
	int			i;

	if (!CheckFunctionValidatorAccess(fcinfo->flinfo->fn_oid, funcoid))
		PG_RETURN_VOID();

	/* Get the new function's pg_proc entry */
	tuple = SearchSysCache(PROCOID,
						   ObjectIdGetDatum(funcoid),
						   0, 0, 0);
	if (!HeapTupleIsValid(tuple))
		elog(ERROR, "cache lookup failed for function %u", funcoid);
	proc = (Form_pg_proc) GETSTRUCT(tuple);

	functyptype = get_typtype(proc->prorettype);

	/* Disallow pseudotype result */
	/* except for TRIGGER, RECORD, VOID, or polymorphic */
	if (functyptype == TYPTYPE_PSEUDO)
	{
		/* we assume OPAQUE with no arguments means a trigger */
		if (proc->prorettype == TRIGGEROID ||
			(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
			istrigger = true;
		else if (proc->prorettype != RECORDOID &&
				 proc->prorettype != VOIDOID &&
				 !IsPolymorphicType(proc->prorettype))
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("PL/pgSQL functions cannot return type %s",
							format_type_be(proc->prorettype))));
	}

	/* Disallow pseudotypes in arguments (either IN or OUT) */
	/* except for polymorphic */
	numargs = get_func_arg_info(tuple,
								&argtypes, &argnames, &argmodes);
	for (i = 0; i < numargs; i++)
	{
		if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
		{
			if (!IsPolymorphicType(argtypes[i]))
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("PL/pgSQL functions cannot accept type %s",
								format_type_be(argtypes[i]))));
		}
	}

	/* Postpone body checks if !check_function_bodies */
	if (check_function_bodies)
	{
		FunctionCallInfoData fake_fcinfo;
		FmgrInfo	flinfo;
		TriggerData trigdata;
		int			rc;

		/*
		 * Connect to SPI manager (is this needed for compilation?)
		 */
		if ((rc = SPI_connect()) != SPI_OK_CONNECT)
			elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc));

		/*
		 * Set up a fake fcinfo with just enough info to satisfy
		 * plpgsql_compile().
		 */
		MemSet(&fake_fcinfo, 0, sizeof(fake_fcinfo));
		MemSet(&flinfo, 0, sizeof(flinfo));
		fake_fcinfo.flinfo = &flinfo;
		flinfo.fn_oid = funcoid;
		flinfo.fn_mcxt = CurrentMemoryContext;
		if (istrigger)
		{
			MemSet(&trigdata, 0, sizeof(trigdata));
			trigdata.type = T_TriggerData;
			fake_fcinfo.context = (Node *) &trigdata;
		}

		/* Test-compile the function */
		plpgsql_compile(&fake_fcinfo, true);

		/*
		 * Disconnect from SPI manager
		 */
		if ((rc = SPI_finish()) != SPI_OK_FINISH)
			elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc));
	}

	ReleaseSysCache(tuple);

	PG_RETURN_VOID();
}
Example #4
0
Datum
exec_sql_using(PG_FUNCTION_ARGS) {
    HeapTuple procedureTuple = SearchSysCache1(PROCOID,
        ObjectIdGetDatum(fcinfo->flinfo->fn_oid));
    if (!HeapTupleIsValid(procedureTuple))
        ereport(ERROR,
            (errmsg("cache lookup failed for function %u",
            fcinfo->flinfo->fn_oid)));

    Oid* types = NULL;
    char** names = NULL;
    char* modes = NULL;
    int nargs = get_func_arg_info(procedureTuple, &types, &names, &modes);

    Oid resultTypeOid;
    TupleDesc tupleDesc;
    TypeFuncClass resultType = get_call_result_type(fcinfo, &resultTypeOid,
        &tupleDesc);
    bool returnTypeIsByValue;
    int16 returnTypeLen;
    get_typlenbyval(resultTypeOid, &returnTypeLen, &returnTypeIsByValue);

    if (resultType != TYPEFUNC_SCALAR && resultType != TYPEFUNC_COMPOSITE)
        ereport(ERROR, (
            errmsg("function \"%s\" has indeterminable result type",
                format_procedure(fcinfo->flinfo->fn_oid))
            ));
    bool returnVoid = resultTypeOid == VOIDOID;

    ReleaseSysCache(procedureTuple);

    if (nargs < 2)
        ereport(ERROR, (
            errmsg("function \"%s\" has less than 2 arguments",
                format_procedure(fcinfo->flinfo->fn_oid))
            ));
    else if (modes != NULL)
        for (int i = 0; i < nargs; i++) {
            if (modes[i] != PROARGMODE_IN)
                ereport(ERROR, (
                    errmsg("function \"%s\" has non-IN arguments",
                        format_procedure(fcinfo->flinfo->fn_oid))
                    ));
        }
    else if (PG_ARGISNULL(0))
        ereport(ERROR, (
            errmsg("function \"%s\" called with NULL as first argument",
                format_procedure(fcinfo->flinfo->fn_oid))
            ));

    char* stmt = NULL;
    if (types[0] == TEXTOID)
        stmt = DatumGetCString(
            DirectFunctionCall1(textout, PG_GETARG_DATUM(0)));
    else if (types[0] == VARCHAROID)
        stmt = DatumGetCString(
            DirectFunctionCall1(varcharout, PG_GETARG_DATUM(0)));
    else
        ereport(ERROR, (
            errmsg("function \"%s\" does not have a leading VARCHAR/TEXT "
                "argument",
                format_procedure(fcinfo->flinfo->fn_oid))
            ));

    char* nulls = NULL;
    for (int i = 1; i < nargs; i++)
        if (PG_ARGISNULL(i)) {
            if (nulls == NULL) {
                nulls = palloc0(sizeof(char) * (nargs - 1));
                memset(nulls, ' ', nargs - 1);
            }
            nulls[i - 1] = 'n';
        }

    SPI_connect();
    SPIPlanPtr plan = SPI_prepare(stmt, nargs - 1, &types[1]);
    if (plan == NULL)
        ereport(ERROR, (
            errmsg("function \"%s\" could not obtain execution plan for "
                "SQL statement",
                format_procedure(fcinfo->flinfo->fn_oid))
            ));

    int result = SPI_execute_plan(plan, &fcinfo->arg[1], nulls, false,
        returnVoid ? 0 : 1);

    Datum returnValue = 0;
    bool returnNull = false;
    if (!returnVoid) {
        if (result != SPI_OK_SELECT
            && result != SPI_OK_INSERT_RETURNING
            && result != SPI_OK_DELETE_RETURNING
            && result == SPI_OK_UPDATE_RETURNING)
            ereport(ERROR, (
                errmsg("function \"%s\" could not obtain result from query",
                    format_procedure(fcinfo->flinfo->fn_oid))
                ));
        else if (SPI_tuptable->tupdesc->natts != 1)
            ereport(ERROR, (
                errmsg("function \"%s\" retrieved more than one column from "
                    "query",
                    format_procedure(fcinfo->flinfo->fn_oid))
                ));
        else if (resultTypeOid != SPI_gettypeid(SPI_tuptable->tupdesc, 1))
            ereport(ERROR, (
                errmsg("function \"%s\" has different return type OID than "
                    "what query returned",
                    format_procedure(fcinfo->flinfo->fn_oid))
                ));

        /* It is important to copy the value into the upper executor context,
         * i.e., the memory context that was current when SPI_connect was
         * called */
        returnValue = SPI_getbinval(SPI_copytuple(SPI_tuptable->vals[0]),
            SPI_tuptable->tupdesc, 1, &returnNull);
    }

    SPI_freeplan(plan);
    if (nulls)
        pfree(nulls);
    SPI_finish();

    if (result < 0)
        ereport(ERROR, (
            errmsg("function \"%s\" encountered error %d during SQL execution",
                format_procedure(fcinfo->flinfo->fn_oid),
                result)
            ));

    if (returnVoid)
        PG_RETURN_VOID();
    else if (returnNull)
        PG_RETURN_NULL();
    else
        return returnValue;
}
Example #5
0
/*
 * Create a new PLyProcedure structure
 */
static PLyProcedure *
PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
{
	char		procName[NAMEDATALEN + 256];
	Form_pg_proc procStruct;
	PLyProcedure *volatile proc;
	MemoryContext cxt;
	MemoryContext oldcxt;
	int			rv;
	char	   *ptr;

	procStruct = (Form_pg_proc) GETSTRUCT(procTup);
	rv = snprintf(procName, sizeof(procName),
				  "__plpython_procedure_%s_%u",
				  NameStr(procStruct->proname),
				  fn_oid);
	if (rv >= sizeof(procName) || rv < 0)
		elog(ERROR, "procedure name would overrun buffer");

	/* Replace any not-legal-in-Python-names characters with '_' */
	for (ptr = procName; *ptr; ptr++)
	{
		if (!((*ptr >= 'A' && *ptr <= 'Z') ||
			  (*ptr >= 'a' && *ptr <= 'z') ||
			  (*ptr >= '0' && *ptr <= '9')))
			*ptr = '_';
	}

	/* Create long-lived context that all procedure info will live in */
	cxt = AllocSetContextCreateExtended(TopMemoryContext,
										procName,
										MEMCONTEXT_COPY_NAME,
										ALLOCSET_DEFAULT_SIZES);

	oldcxt = MemoryContextSwitchTo(cxt);

	proc = (PLyProcedure *) palloc0(sizeof(PLyProcedure));
	proc->mcxt = cxt;

	PG_TRY();
	{
		Datum		protrftypes_datum;
		Datum		prosrcdatum;
		bool		isnull;
		char	   *procSource;
		int			i;

		proc->proname = pstrdup(NameStr(procStruct->proname));
		proc->pyname = pstrdup(procName);
		proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
		proc->fn_tid = procTup->t_self;
		proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
		proc->is_setof = procStruct->proretset;
		proc->is_procedure = (procStruct->prorettype == InvalidOid);
		proc->src = NULL;
		proc->argnames = NULL;
		proc->args = NULL;
		proc->nargs = 0;
		proc->langid = procStruct->prolang;
		protrftypes_datum = SysCacheGetAttr(PROCOID, procTup,
											Anum_pg_proc_protrftypes,
											&isnull);
		proc->trftypes = isnull ? NIL : oid_array_to_list(protrftypes_datum);
		proc->code = NULL;
		proc->statics = NULL;
		proc->globals = NULL;
		proc->calldepth = 0;
		proc->argstack = NULL;

		/*
		 * get information required for output conversion of the return value,
		 * but only if this isn't a trigger or procedure.
		 */
		if (!is_trigger && procStruct->prorettype)
		{
			Oid			rettype = procStruct->prorettype;
			HeapTuple	rvTypeTup;
			Form_pg_type rvTypeStruct;

			rvTypeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rettype));
			if (!HeapTupleIsValid(rvTypeTup))
				elog(ERROR, "cache lookup failed for type %u", rettype);
			rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);

			/* Disallow pseudotype result, except for void or record */
			if (rvTypeStruct->typtype == TYPTYPE_PSEUDO)
			{
				if (rettype == VOIDOID ||
					rettype == RECORDOID)
					 /* okay */ ;
				else if (rettype == TRIGGEROID || rettype == EVTTRIGGEROID)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("trigger functions can only be called as triggers")));
				else
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("PL/Python functions cannot return type %s",
									format_type_be(rettype))));
			}

			/* set up output function for procedure result */
			PLy_output_setup_func(&proc->result, proc->mcxt,
								  rettype, -1, proc);

			ReleaseSysCache(rvTypeTup);
		}
		else
		{
			/*
			 * In a trigger function, we use proc->result and proc->result_in
			 * for converting tuples, but we don't yet have enough info to set
			 * them up.  PLy_exec_trigger will deal with it.
			 */
			proc->result.typoid = InvalidOid;
			proc->result_in.typoid = InvalidOid;
		}

		/*
		 * Now get information required for input conversion of the
		 * procedure's arguments.  Note that we ignore output arguments here.
		 * If the function returns record, those I/O functions will be set up
		 * when the function is first called.
		 */
		if (procStruct->pronargs)
		{
			Oid		   *types;
			char	  **names,
					   *modes;
			int			pos,
						total;

			/* extract argument type info from the pg_proc tuple */
			total = get_func_arg_info(procTup, &types, &names, &modes);

			/* count number of in+inout args into proc->nargs */
			if (modes == NULL)
				proc->nargs = total;
			else
			{
				/* proc->nargs was initialized to 0 above */
				for (i = 0; i < total; i++)
				{
					if (modes[i] != PROARGMODE_OUT &&
						modes[i] != PROARGMODE_TABLE)
						(proc->nargs)++;
				}
			}

			/* Allocate arrays for per-input-argument data */
			proc->argnames = (char **) palloc0(sizeof(char *) * proc->nargs);
			proc->args = (PLyDatumToOb *) palloc0(sizeof(PLyDatumToOb) * proc->nargs);

			for (i = pos = 0; i < total; i++)
			{
				HeapTuple	argTypeTup;
				Form_pg_type argTypeStruct;

				if (modes &&
					(modes[i] == PROARGMODE_OUT ||
					 modes[i] == PROARGMODE_TABLE))
					continue;	/* skip OUT arguments */

				Assert(types[i] == procStruct->proargtypes.values[pos]);

				argTypeTup = SearchSysCache1(TYPEOID,
											 ObjectIdGetDatum(types[i]));
				if (!HeapTupleIsValid(argTypeTup))
					elog(ERROR, "cache lookup failed for type %u", types[i]);
				argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);

				/* disallow pseudotype arguments */
				if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("PL/Python functions cannot accept type %s",
									format_type_be(types[i]))));

				/* set up I/O function info */
				PLy_input_setup_func(&proc->args[pos], proc->mcxt,
									 types[i], -1,	/* typmod not known */
									 proc);

				/* get argument name */
				proc->argnames[pos] = names ? pstrdup(names[i]) : NULL;

				ReleaseSysCache(argTypeTup);

				pos++;
			}
		}

		/*
		 * get the text of the function.
		 */
		prosrcdatum = SysCacheGetAttr(PROCOID, procTup,
									  Anum_pg_proc_prosrc, &isnull);
		if (isnull)
			elog(ERROR, "null prosrc");
		procSource = TextDatumGetCString(prosrcdatum);

		PLy_procedure_compile(proc, procSource);

		pfree(procSource);
	}
	PG_CATCH();
	{
		MemoryContextSwitchTo(oldcxt);
		PLy_procedure_delete(proc);
		PG_RE_THROW();
	}
	PG_END_TRY();

	MemoryContextSwitchTo(oldcxt);
	return proc;
}
Example #6
0
/*
 * Internal handler function
 */
static Datum
handler_internal(Oid function_oid, FunctionCallInfo fcinfo, bool execute)
{
	HeapTuple	proctuple;
	Form_pg_proc pg_proc_entry;
	char	   *sourcecode;
	Datum		prosrcdatum;
	bool		isnull;
	const char **xslt_params;
	int			i;
	Oid		   *argtypes;
	char	  **argnames;
	char	   *argmodes;
	int			numargs;
	xmlDocPtr	ssdoc;
	xmlDocPtr	xmldoc;
	xmlDocPtr	resdoc;
	xsltStylesheetPtr stylesheet;
	int			reslen;
	xmlChar	   *resstr;
	Datum		resdatum;

	if (CALLED_AS_TRIGGER(fcinfo))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("trigger functions not supported")));

	proctuple = SearchSysCache(PROCOID, ObjectIdGetDatum(function_oid), 0, 0, 0);
	if (!HeapTupleIsValid(proctuple))
		elog(ERROR, "cache lookup failed for function %u", function_oid);

	prosrcdatum = SysCacheGetAttr(PROCOID, proctuple, Anum_pg_proc_prosrc, &isnull);
	if (isnull)
		elog(ERROR, "null prosrc");

	sourcecode = pstrdup(DatumGetCString(DirectFunctionCall1(textout,
															 prosrcdatum)));
	/* allow one blank line at the start */
	if (sourcecode[0] == '\n')
		sourcecode++;

	numargs = get_func_arg_info(proctuple,
								&argtypes, &argnames, &argmodes);

	if (numargs < 1)
		ereport(ERROR,
				(errmsg("XSLT function must have at least one argument")));

				if (argtypes[0] != XMLOID)
		ereport(ERROR,
				(errmsg("first argument of XSLT function must have type XML")));

#if 0
	xsltSetGenericErrorFunc(NULL, xmlGenericError);
#endif

	ssdoc = xmlParseDoc((xmlChar *) sourcecode); /* XXX use backend's xml_parse here() */
	stylesheet = xsltParseStylesheetDoc(ssdoc); /* XXX check error handling */
	if (!stylesheet)
		ereport(ERROR,
				(errmsg("could not parse stylesheet")));

	pg_proc_entry = (Form_pg_proc) GETSTRUCT(proctuple);

	{
		char	   *method;

		method = (char *) stylesheet->method;
		/*
		 * TODO: This is strictly speaking not correct because the
		 * default output method may be "html", but that can only
		 * detected at run time, so punt for now.
		 */
		if (!method)
			method = "xml";

		if (strcmp(method, "xml") == 0 && pg_proc_entry->prorettype != XMLOID)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("XSLT stylesheet has output method \"xml\" but return type of function is not xml")));
		else if ((strcmp(method, "html") == 0 || strcmp(method, "text") == 0)
				 && pg_proc_entry->prorettype != TEXTOID && pg_proc_entry->prorettype != VARCHAROID)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("XSLT stylesheet has output method \"%s\" but return type of function is not text or varchar", method)));
	}

	/* validation stops here */
	if (!execute)
	{
		ReleaseSysCache(proctuple);
		PG_RETURN_VOID();
	}

	/* execution begins here */

	xslt_params = palloc(((numargs - 1) * 2 + 1) * sizeof(*xslt_params));
	for (i = 0; i < numargs-1; i++)
	{
		xslt_params[i*2] = argnames[i+1];
		xslt_params[i*2+1] = type_to_cstring(PG_GETARG_DATUM(i+1),
											 argtypes[i+1]);
	}
	xslt_params[i*2] = NULL;

	{
		xmltype *arg0 = PG_GETARG_XML_P(0);
		// XXX this ought to use xml_parse()
		xmldoc = xmlParseMemory((char *) VARDATA(arg0), VARSIZE(arg0) - VARHDRSZ);
	}

	resdoc = xsltApplyStylesheet(stylesheet, xmldoc, xslt_params);
	if (!resdoc)
		elog(ERROR, "xsltApplyStylesheet() failed");

	xmlFreeDoc(xmldoc);

	if (xsltSaveResultToString(&resstr, &reslen, resdoc, stylesheet) != 0)
		elog(ERROR, "result serialization failed");

	xsltFreeStylesheet(stylesheet);
	xmlFreeDoc(resdoc);

	xsltCleanupGlobals();
	xmlCleanupParser();

	resdatum = cstring_to_type(resstr ? (char *) resstr : "", pg_proc_entry->prorettype);
	ReleaseSysCache(proctuple);
	PG_RETURN_DATUM(resdatum);
}
Example #7
0
int
get_pgfunc(lua_State *L)
{
	Lua_pgfunc *lf;
	Pgfunc_options opt;
	MemoryContext m;
	const char* reg_name = NULL;
	HeapTuple proctup;
	Form_pg_proc proc;
	int luasrc = 0;
	Oid funcid = 0;

	BEGINLUA;

	opt.only_internal = true;
	opt.throwable = true;

	if (lua_gettop(L) == 2){
		luaL_checktype(L, 2, LUA_TTABLE);
		parse_options(L, &opt);
	}else if (lua_gettop(L) != 1){
		return luaL_error(L, "pgfunc(text): wrong arguments");
	}
	if(lua_type(L, 1) == LUA_TSTRING){
		reg_name = luaL_checkstring(L, 1);
		m = MemoryContextSwitchTo(tmpcontext);
		PG_TRY();
		{
			funcid = DatumGetObjectId(DirectFunctionCall1(regprocedurein, CStringGetDatum(reg_name)));
		}
		PG_CATCH();{}
		PG_END_TRY();
		MemoryContextSwitchTo(m);
		MemoryContextReset(tmpcontext);
	}else if (lua_type(L, 1) == LUA_TNUMBER){
		funcid = luaL_checkinteger(L, 1);
	}

	if (!OidIsValid(funcid)){
		if (reg_name)
			return luaL_error(L,"failed to register %s", reg_name);
		return luaL_error(L,"failed to register function with oid %d", funcid);
	}

	proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
	if (!HeapTupleIsValid(proctup)){
		return luaL_error(L,"cache lookup failed for function %d", funcid);
	}

	proc = (Form_pg_proc) GETSTRUCT(proctup);

	luasrc = ((proc->prolang == get_pllua_oid())
		  || (proc->prolang == get_plluau_oid()));
	if ( opt.only_internal
			&&(proc->prolang != INTERNALlanguageId)
			&&(!luasrc) ){
		ReleaseSysCache(proctup);
		return luaL_error(L, "supported only SQL/internal functions");
	}


	lf = (Lua_pgfunc *)lua_newuserdata(L, sizeof(Lua_pgfunc));

	/*make it g/collected*/
	luaP_getfield(L, pg_func_type_name);
	lua_setmetatable(L, -2);


	lf->prorettype = proc->prorettype;
	lf->funcid = funcid;
	lf->options = opt;

	{
		Oid *argtypes;
		char **argnames;
		char *argmodes;
		int argc;
		MemoryContext cur = CurrentMemoryContext;

		MemoryContextSwitchTo(tmpcontext);

		argc = get_func_arg_info(proctup,
					 &argtypes, &argnames, &argmodes);

		MemoryContextSwitchTo(get_common_ctx());

		lf->numargs = argc;
		lf->argtypes = (Oid*)palloc(argc * sizeof(Oid));
		memcpy(lf->argtypes, argtypes, argc * sizeof(Oid));
		MemoryContextSwitchTo(cur);
		MemoryContextReset(tmpcontext);
	}

	if (luasrc){
		bool isnull;
		text *t;
		const char *s;
		luaL_Buffer b;
		int pcall_result;
		Datum prosrc;

		if((lf->numargs != 1)
				|| (lf->argtypes[0] != INTERNALOID)
				|| (lf->prorettype != INTERNALOID)){
			luaL_error(L, "pgfunc accepts only 'internal' pllua/u functions with internal argument");
		}

		prosrc = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosrc, &isnull);
		if (isnull) elog(ERROR, "[pgfunc]: null lua prosrc");
		luaL_buffinit(L, &b);

		luaL_addstring(&b,"do ");
		t = DatumGetTextP(prosrc);
		luaL_addlstring(&b, VARDATA(t), VARSIZE(t) - VARHDRSZ);
		luaL_addstring(&b, " end");
		luaL_pushresult(&b);
		s = lua_tostring(L, -1);

		ReleaseSysCache(proctup);
		clean_pgfuncinfo(lf);

		if (luaL_loadbuffer(L, s, strlen(s), "pgfunc chunk"))
			luaL_error(L, "compile");

		lua_remove(L, -2); /*delete source element*/

		pcall_result = lua_pcall(L, 0, 1, 0);
		lua_remove(L, -2); /*delete chunk*/
		if(pcall_result == 0){
			ENDLUAV(1);
			return 1;
		}

		if( pcall_result == LUA_ERRRUN)
			luaL_error(L,"%s %s","Runtime error:",lua_tostring(L, -1));
		else if(pcall_result == LUA_ERRMEM)
			luaL_error(L,"%s %s","Memory error:",lua_tostring(L, -1));
		else if(pcall_result == LUA_ERRERR)
			luaL_error(L,"%s %s","Error:",lua_tostring(L, -1));

		return luaL_error(L, "pgfunc unknown error");
	}

	if(proc->proretset) {
		lua_pushcclosure(L, pgfunc_rows, 1);
	} else {
		fmgr_info(funcid, &lf->fi);
		lua_pushcclosure(L, pg_callable_func, 1);
	}



	ReleaseSysCache(proctup);

	ENDLUAV(1);
	return 1;
}