mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc to all files copyright Regents of Berkeley. Man, that's a lot of files.
410 lines
8.5 KiB
C
410 lines
8.5 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* be-pqexec.c
|
|
* support for executing POSTGRES commands and functions from a
|
|
* user-defined function in a backend.
|
|
*
|
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/libpq/Attic/be-pqexec.c,v 1.30 2000/01/26 05:56:28 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* PQfn - call a POSTGRES function
|
|
* PQexec - execute a POSTGRES query
|
|
*
|
|
* NOTES
|
|
* These routines are compiled into the postgres backend.
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "libpq/libpq.h"
|
|
#include "tcop/fastpath.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "utils/builtins.h"
|
|
|
|
static char *strmake(char *str, int len);
|
|
|
|
/* ----------------------------------------------------------------
|
|
* PQ interface routines
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------
|
|
* PQfn - Send a function call to the POSTGRES backend.
|
|
*
|
|
* fnid : function id
|
|
* result_buf : pointer to result buffer (&int if integer)
|
|
* result_len : length of return value.
|
|
* result_is_int : If the result is an integer, this must be non-zero
|
|
* args : pointer to a NULL terminated arg array.
|
|
* (length, if integer, and result-pointer)
|
|
* nargs : # of arguments in args array.
|
|
*
|
|
* This code scavanged from HandleFunctionRequest() in tcop/fastpath.h
|
|
* ----------------
|
|
*/
|
|
char *
|
|
PQfn(int fnid,
|
|
int *result_buf, /* can't use void, dec compiler barfs */
|
|
int result_len,
|
|
int result_is_int,
|
|
PQArgBlock *args,
|
|
int nargs)
|
|
{
|
|
char *retval; /* XXX - should be datum, maybe ? */
|
|
char *arg[FUNC_MAX_ARGS];
|
|
bool isNull;
|
|
int i;
|
|
|
|
/* ----------------
|
|
* fill args[] array
|
|
* ----------------
|
|
*/
|
|
if (nargs > FUNC_MAX_ARGS)
|
|
elog(ERROR, "functions cannot have more than %d arguments",
|
|
FUNC_MAX_ARGS);
|
|
for (i = 0; i < nargs; i++)
|
|
{
|
|
if (args[i].len == VAR_LENGTH_ARG)
|
|
arg[i] = (char *) args[i].u.ptr;
|
|
else if (args[i].len > sizeof(int4))
|
|
elog(ERROR, "arg_length of argument %d too long", i);
|
|
else
|
|
arg[i] = (char *) args[i].u.integer;
|
|
}
|
|
|
|
/* ----------------
|
|
* call the postgres function manager
|
|
* ----------------
|
|
*/
|
|
retval = fmgr_array_args(fnid, nargs, arg, &isNull);
|
|
|
|
/* ----------------
|
|
* put the result in the buffer the user specified and
|
|
* return the proper code.
|
|
* ----------------
|
|
*/
|
|
if (isNull) /* void retval */
|
|
return "0";
|
|
|
|
if (result_is_int)
|
|
*result_buf = (int) retval;
|
|
else
|
|
memmove(result_buf, retval, result_len);
|
|
return "G";
|
|
}
|
|
|
|
/* ----------------
|
|
* PQexec - Send a query to the POSTGRES backend
|
|
*
|
|
* The return value is a string.
|
|
* If 0 or more tuples fetched from the backend, return "P portal-name".
|
|
* If a query is does not return tuples, return "C query-command".
|
|
* If there is an error: return "E error-message".
|
|
*
|
|
* Note: if we get a serious error or an elog(ERROR), then PQexec never
|
|
* returns because the system longjmp's back to the main loop.
|
|
* ----------------
|
|
*/
|
|
char *
|
|
PQexec(char *query)
|
|
{
|
|
PortalEntry *entry = NULL;
|
|
char *result = NULL;
|
|
|
|
/* ----------------
|
|
* create a new portal and put it on top of the portal stack.
|
|
* ----------------
|
|
*/
|
|
entry = (PortalEntry *) be_newportal();
|
|
be_portalpush(entry);
|
|
|
|
/* ----------------
|
|
* pg_exec_query_dest will put the query results in a portal which will
|
|
* end up on the top of the portal stack.
|
|
* ----------------
|
|
*/
|
|
pg_exec_query_dest(query, Local, FALSE);
|
|
|
|
/* ----------------
|
|
* pop the portal off the portal stack and return the
|
|
* result. Note if result is null, we return C.
|
|
* ----------------
|
|
*/
|
|
entry = (PortalEntry *) be_portalpop();
|
|
result = entry->result;
|
|
if (result == NULL)
|
|
{
|
|
char *PQE = "Cnull PQexec result";
|
|
|
|
result = pstrdup(PQE);
|
|
}
|
|
|
|
if (result[0] != 'P')
|
|
{
|
|
|
|
/*
|
|
* some successful command was executed, but it's not one where we
|
|
* return the portal name so here we should be sure to clear out
|
|
* the portal (since the caller has no handle on it)
|
|
*/
|
|
pbuf_close(entry->name);
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* pqtest support
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
|
|
/* ----------------
|
|
* pqtest_PQexec takes a text query and returns the number of
|
|
* tuples it returns. Note: there is no need to PQclear()
|
|
* here - the memory will go away at end transaction.
|
|
* ----------------
|
|
*/
|
|
int
|
|
pqtest_PQexec(char *q)
|
|
{
|
|
PortalBuffer *a;
|
|
char *res;
|
|
int t;
|
|
|
|
/* ----------------
|
|
* execute the postgres query
|
|
* ----------------
|
|
*/
|
|
res = PQexec(q);
|
|
|
|
/* ----------------
|
|
* return number of tuples in portal or 0 if command returns no tuples.
|
|
* ----------------
|
|
*/
|
|
t = 0;
|
|
switch (res[0])
|
|
{
|
|
case 'P':
|
|
a = PQparray(&res[1]);
|
|
if (a == NULL)
|
|
elog(ERROR, "pqtest_PQexec: PQparray could not find portal %s",
|
|
res);
|
|
|
|
t = PQntuples(a);
|
|
break;
|
|
case 'C':
|
|
break;
|
|
default:
|
|
elog(NOTICE, "pqtest_PQexec: PQexec(%s) returns %s", q, res);
|
|
break;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
/* ----------------
|
|
* utilities for pqtest_PQfn()
|
|
* ----------------
|
|
*/
|
|
static char *
|
|
strmake(char *str, int len)
|
|
{
|
|
char *newstr;
|
|
|
|
if (str == NULL)
|
|
return NULL;
|
|
if (len <= 0)
|
|
len = strlen(str);
|
|
|
|
newstr = (char *) palloc((unsigned) len + 1);
|
|
StrNCpy(newstr, str, len + 1);
|
|
newstr[len] = (char) 0;
|
|
return newstr;
|
|
}
|
|
|
|
#define SKIP 0
|
|
#define SCAN 1
|
|
|
|
static char spacestr[] = " ";
|
|
|
|
static int
|
|
strparse(char *s, char **fields, int *offsets, int maxfields)
|
|
{
|
|
int len = strlen(s);
|
|
char *cp = s,
|
|
*end = cp + len,
|
|
*ep;
|
|
int parsed = 0;
|
|
int mode = SKIP,
|
|
i = 0;
|
|
|
|
if (*(end - 1) == '\n')
|
|
end--;
|
|
|
|
for (i = 0; i < maxfields; i++)
|
|
fields[i] = spacestr;
|
|
|
|
i = 0;
|
|
while (!parsed)
|
|
{
|
|
if (mode == SKIP)
|
|
{
|
|
|
|
while ((cp < end) &&
|
|
(*cp == ' ' || *cp == '\t'))
|
|
cp++;
|
|
if (cp < end)
|
|
mode = SCAN;
|
|
else
|
|
parsed = 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
ep = cp;
|
|
while ((ep < end) && (*ep != ' ' && *ep != '\t'))
|
|
ep++;
|
|
|
|
if (ep < end)
|
|
mode = SKIP;
|
|
else
|
|
parsed = 1;
|
|
|
|
fields[i] = strmake(cp, ep - cp);
|
|
if (offsets != NULL)
|
|
offsets[i] = cp - s;
|
|
|
|
i++;
|
|
cp = ep;
|
|
if (i > maxfields)
|
|
parsed = 1;
|
|
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
/* ----------------
|
|
* pqtest_PQfn converts its string into a PQArgBlock and
|
|
* calls the specified function, which is assumed to return
|
|
* an integer value.
|
|
* ----------------
|
|
*/
|
|
int
|
|
pqtest_PQfn(char *q)
|
|
{
|
|
int k,
|
|
j,
|
|
i,
|
|
v,
|
|
f,
|
|
offsets;
|
|
char *fields[FUNC_MAX_ARGS];
|
|
PQArgBlock pqargs[FUNC_MAX_ARGS];
|
|
int res;
|
|
char *pqres;
|
|
|
|
/* ----------------
|
|
* parse q into fields
|
|
* ----------------
|
|
*/
|
|
i = strparse(q, fields, &offsets, FUNC_MAX_ARGS);
|
|
printf("pqtest_PQfn: strparse returns %d fields\n", i); /* debug */
|
|
if (i == 0)
|
|
return -1;
|
|
|
|
/* ----------------
|
|
* get the function id
|
|
* ----------------
|
|
*/
|
|
f = atoi(fields[0]);
|
|
printf("pqtest_PQfn: func is %d\n", f); /* debug */
|
|
if (f == 0)
|
|
return -1;
|
|
|
|
/* ----------------
|
|
* build a PQArgBlock
|
|
* ----------------
|
|
*/
|
|
for (j = 1; j < i && j < FUNC_MAX_ARGS; j++)
|
|
{
|
|
k = j - 1;
|
|
v = atoi(fields[j]);
|
|
if (v != 0 || (v == 0 && fields[j][0] == '0'))
|
|
{
|
|
pqargs[k].len = sizeof(int4);
|
|
pqargs[k].u.integer = v;
|
|
printf("pqtest_PQfn: arg %d is int %d\n", k, v); /* debug */
|
|
}
|
|
else
|
|
{
|
|
pqargs[k].len = VAR_LENGTH_ARG;
|
|
pqargs[k].u.ptr = (int *) textin(fields[j]);
|
|
printf("pqtest_PQfn: arg %d is text %s\n", k, fields[j]); /* debug */
|
|
}
|
|
}
|
|
|
|
/* ----------------
|
|
* call PQfn
|
|
* ----------------
|
|
*/
|
|
pqres = PQfn(f, &res, sizeof(int4), 1, pqargs, i - 1);
|
|
printf("pqtest_PQfn: pqres is %s\n", pqres); /* debug */
|
|
|
|
/* ----------------
|
|
* free memory used
|
|
* ----------------
|
|
*/
|
|
for (j = 0; j < i; j++)
|
|
{
|
|
pfree(fields[j]);
|
|
if (pqargs[j].len == VAR_LENGTH_ARG)
|
|
pfree(pqargs[j].u.ptr);
|
|
}
|
|
|
|
/* ----------------
|
|
* return result
|
|
* ----------------
|
|
*/
|
|
printf("pqtest_PQfn: res is %d\n", res); /* debugg */
|
|
return res;
|
|
}
|
|
|
|
/* ----------------
|
|
* pqtest looks at the first character of its test argument
|
|
* and decides which of pqtest_PQexec or pqtest_PQfn to call.
|
|
* ----------------
|
|
*/
|
|
int32
|
|
pqtest(struct varlena * vlena)
|
|
{
|
|
char *q;
|
|
|
|
/* ----------------
|
|
* get the query
|
|
* ----------------
|
|
*/
|
|
q = textout(vlena);
|
|
if (q == NULL)
|
|
return -1;
|
|
|
|
switch (q[0])
|
|
{
|
|
case '%':
|
|
return pqtest_PQfn(&q[1]);
|
|
break;
|
|
default:
|
|
return pqtest_PQexec(q);
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|