1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-12 05:01:15 +03:00

Postgres95 1.01 Distribution - Virgin Sources

This commit is contained in:
Marc G. Fournier
1996-07-09 06:22:35 +00:00
commit d31084e9d1
868 changed files with 242656 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
#-------------------------------------------------------------------------
#
# Makefile.inc--
# Makefile for the traffic cop module
#
# Copyright (c) 1994, Regents of the University of California
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/tcop/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $
#
#-------------------------------------------------------------------------
VPATH:= $(VPATH):$(CURDIR)/tcop
SRCS_TCOP= aclchk.c dest.c fastpath.c postgres.c pquery.c utility.c
HEADERS+= dest.h fastpath.h pquery.h tcopdebug.h tcopprot.h utility.h

555
src/backend/tcop/aclchk.c Normal file
View File

@@ -0,0 +1,555 @@
/*-------------------------------------------------------------------------
*
* aclchk.c--
* Routines to check access control permissions.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/Attic/aclchk.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $
*
* NOTES
* See acl.h.
*
*-------------------------------------------------------------------------
*/
#include <string.h>
#include "utils/acl.h" /* where declarations for this file goes */
#include "access/heapam.h"
#include "access/htup.h"
#include "access/tupmacs.h"
#include "utils/builtins.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "catalog/indexing.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
#include "catalog/pg_group.h"
#include "catalog/pg_user.h"
#include "utils/syscache.h"
#include "parser/catalog_utils.h"
#include "fmgr.h"
/*
* Enable use of user relations in place of real system catalogs.
*/
/*#define ACLDEBUG*/
#ifdef ACLDEBUG
/*
* Fool the code below into thinking that "pgacls" is pg_class.
* relname and relowner are in the same place, happily.
*/
#undef Anum_pg_class_relacl
#define Anum_pg_class_relacl 3
#undef Natts_pg_class
#define Natts_pg_class 3
#undef Name_pg_class
#define Name_pg_class "pgacls"
#undef Name_pg_group
#define Name_pg_group "pggroup"
#endif
#ifdef ACLDEBUG_TRACE
static
dumpacl(Acl *acl)
{
register unsigned i;
AclItem *aip;
elog(DEBUG, "acl size = %d, # acls = %d",
ACL_SIZE(acl), ACL_NUM(acl));
aip = (AclItem *) ACL_DAT(acl);
for (i = 0; i < ACL_NUM(acl); ++i)
elog(DEBUG, " acl[%d]: %s", i, aclitemout(aip + i));
}
#endif
/*
*
*/
void
ChangeAcl(char *relname,
AclItem *mod_aip,
unsigned modechg)
{
register unsigned i;
Acl *old_acl = (Acl *) NULL, *new_acl;
Relation relation;
static ScanKeyData relkey[1] = {
{ 0, Anum_pg_class_relname, NameEqualRegProcedure }
};
HeapScanDesc hsdp;
HeapTuple htp;
Buffer buffer;
Datum values[Natts_pg_class];
char nulls[Natts_pg_class];
char replaces[Natts_pg_class];
ItemPointerData tmp_ipd;
Relation idescs[Num_pg_class_indices];
int free_old_acl = 0;
/*
* Find the pg_class tuple matching 'relname' and extract the ACL.
* If there's no ACL, create a default using the pg_class.relowner
* field.
*
* We can't use the syscache here, since we need to do a heap_replace
* on the tuple we find. Feh.
*/
relation = heap_openr(RelationRelationName);
if (!RelationIsValid(relation))
elog(WARN, "ChangeAcl: could not open '%s'??",
RelationRelationName);
fmgr_info(NameEqualRegProcedure, &relkey[0].sk_func, &relkey[0].sk_nargs);
relkey[0].sk_argument = NameGetDatum(relname);
hsdp = heap_beginscan(relation,
0,
NowTimeQual,
(unsigned) 1,
relkey);
htp = heap_getnext(hsdp, 0, &buffer);
if (!HeapTupleIsValid(htp)) {
heap_endscan(hsdp);
heap_close(relation);
elog(WARN, "ChangeAcl: class \"%s\" not found",
relname);
return;
}
if (!heap_attisnull(htp, Anum_pg_class_relacl))
old_acl = (Acl *) heap_getattr(htp, buffer,
Anum_pg_class_relacl,
RelationGetTupleDescriptor(relation),
(bool *) NULL);
if (!old_acl || ACL_NUM(old_acl) < 1) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "ChangeAcl: using default ACL");
#endif
/* old_acl = acldefault(((Form_pg_class) GETSTRUCT(htp))->relowner); */
old_acl = acldefault();
free_old_acl = 1;
}
#ifdef ACLDEBUG_TRACE
dumpacl(old_acl);
#endif
new_acl = aclinsert3(old_acl, mod_aip, modechg);
#ifdef ACLDEBUG_TRACE
dumpacl(new_acl);
#endif
for (i = 0; i < Natts_pg_class; ++i) {
replaces[i] = ' ';
nulls[i] = ' '; /* ignored if replaces[i] == ' ' anyway */
values[i] = (Datum)NULL;/* ignored if replaces[i] == ' ' anyway */
}
replaces[Anum_pg_class_relacl - 1] = 'r';
values[Anum_pg_class_relacl - 1] = (Datum)new_acl;
htp = heap_modifytuple(htp, buffer, relation, values, nulls, replaces);
/* XXX is this necessary? */
ItemPointerCopy(&htp->t_ctid, &tmp_ipd);
/* XXX handle index on pg_class? */
setheapoverride(true);
(void) heap_replace(relation, &tmp_ipd, htp);
setheapoverride(false);
heap_endscan(hsdp);
/* keep the catalog indices up to date */
CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices,
idescs);
CatalogIndexInsert(idescs, Num_pg_class_indices, relation, htp);
CatalogCloseIndices(Num_pg_class_indices, idescs);
heap_close(relation);
if (free_old_acl)
pfree(old_acl);
pfree(new_acl);
}
AclId
get_grosysid(char *groname)
{
HeapTuple htp;
AclId id = 0;
htp = SearchSysCacheTuple(GRONAME, PointerGetDatum(groname),
0,0,0);
if (HeapTupleIsValid(htp)) {
id = ((Form_pg_group) GETSTRUCT(htp))->grosysid;
} else {
elog(WARN, "non-existent group \"%s\"", groname);
}
return(id);
}
char*
get_groname(AclId grosysid)
{
HeapTuple htp;
char *name;
htp = SearchSysCacheTuple(GROSYSID, PointerGetDatum(grosysid),
0,0,0);
if (HeapTupleIsValid(htp)) {
name = (((Form_pg_group) GETSTRUCT(htp))->groname).data;
} else {
elog(NOTICE, "get_groname: group %d not found", grosysid);
}
return(name);
}
static int32
in_group(AclId uid, AclId gid)
{
Relation relation;
HeapTuple htp;
Acl *tmp;
unsigned i, num;
AclId *aidp;
int32 found = 0;
relation = heap_openr(GroupRelationName);
if (!RelationIsValid(relation)) {
elog(NOTICE, "in_group: could not open \"%s\"??",
GroupRelationName);
return(0);
}
htp = SearchSysCacheTuple(GROSYSID, ObjectIdGetDatum(gid),
0,0,0);
if (HeapTupleIsValid(htp) &&
!heap_attisnull(htp, Anum_pg_group_grolist)) {
tmp = (IdList *) heap_getattr(htp, InvalidBuffer,
Anum_pg_group_grolist,
RelationGetTupleDescriptor(relation),
(bool *) NULL);
/* XXX make me a function */
num = IDLIST_NUM(tmp);
aidp = IDLIST_DAT(tmp);
for (i = 0; i < num; ++i)
if (aidp[i] == uid) {
found = 1;
break;
}
} else {
elog(NOTICE, "in_group: group %d not found", gid);
}
heap_close(relation);
return(found);
}
/*
* aclcheck
* Returns 1 if the 'id' of type 'idtype' has ACL entries in 'acl' to satisfy
* any one of the requirements of 'mode'. Returns 0 otherwise.
*/
int32
aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode)
{
register unsigned i;
register AclItem *aip, *aidat;
unsigned num, found_group;
/* if no acl is found, use world default */
if (!acl) {
acl = acldefault();
}
num = ACL_NUM(acl);
aidat = ACL_DAT(acl);
/*
* We'll treat the empty ACL like that, too, although this is more
* like an error (i.e., you manually blew away your ACL array) --
* the system never creates an empty ACL.
*/
if (num < 1) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "aclcheck: zero-length ACL, returning 1");
#endif
return(1);
}
switch (idtype) {
case ACL_IDTYPE_UID:
for (i = 1, aip = aidat + 1; /* skip world entry */
i < num && aip->ai_idtype == ACL_IDTYPE_UID;
++i, ++aip) {
if (aip->ai_id == id) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "aclcheck: found %d/%d",
aip->ai_id, aip->ai_mode);
#endif
return((aip->ai_mode & mode) ? 1 : 0);
}
}
for (found_group = 0;
i < num && aip->ai_idtype == ACL_IDTYPE_GID;
++i, ++aip) {
if (in_group(id, aip->ai_id)) {
if (aip->ai_mode & mode)
++found_group;
else {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "aclcheck: found %d/%d",
aip->ai_id, aip->ai_mode);
#endif
return(0);
}
}
}
if (found_group) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG,"aclcheck: all groups ok");
#endif
return(1);
}
break;
case ACL_IDTYPE_GID:
for (i = 1, aip = aidat + 1; /* skip world entry and UIDs */
i < num && aip->ai_idtype == ACL_IDTYPE_UID;
++i, ++aip)
;
for (;
i < num && aip->ai_idtype == ACL_IDTYPE_GID;
++i, ++aip) {
if (aip->ai_id == id) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "aclcheck: found %d/%d",
aip->ai_id, aip->ai_mode);
#endif
return((aip->ai_mode & mode) ? 1 : 0);
}
}
break;
case ACL_IDTYPE_WORLD:
break;
default:
elog(WARN, "aclcheck: bogus ACL id type: %d", idtype);
break;
}
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "aclcheck: using world=%d", aidat->ai_mode);
#endif
return((aidat->ai_mode & mode) ? 1 : 0);
}
int32
pg_aclcheck(char *relname, char *usename, AclMode mode)
{
HeapTuple htp;
AclId id;
Acl *acl = (Acl *) NULL, *tmp;
int32 result;
Relation relation;
htp = SearchSysCacheTuple(USENAME, PointerGetDatum(usename),
0,0,0);
if (!HeapTupleIsValid(htp))
elog(WARN, "pg_aclcheck: user \"%-.*s\" not found",
NAMEDATALEN, usename);
id = (AclId) ((Form_pg_user) GETSTRUCT(htp))->usesysid;
/* for the 'pg_database' relation, check the usecreatedb
field before checking normal permissions */
if ( strcmp(DatabaseRelationName, relname) == 0 &&
(((Form_pg_user) GETSTRUCT(htp))->usecreatedb)) {
/* note that even though the user can now append to the
pg_database table, there is still additional permissions checking
in dbcommands.c */
if (mode & ACL_AP)
return (1);
}
/*
* Deny anyone permission to update a system catalog unless
* pg_user.usecatupd is set. (This is to let superusers protect
* themselves from themselves.)
*/
if (((mode & ACL_WR) || (mode & ACL_AP)) &&
IsSystemRelationName(relname) &&
!((Form_pg_user) GETSTRUCT(htp))->usecatupd) {
elog(DEBUG, "pg_aclcheck: catalog update to \"%-.*s\": permission denied",
NAMEDATALEN, relname);
return(0);
}
/*
* Otherwise, superusers bypass all permission-checking.
*/
if (((Form_pg_user) GETSTRUCT(htp))->usesuper) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "pg_aclcheck: \"%-.*s\" is superuser",
NAMEDATALEN, usename);
#endif
return(1);
}
#ifndef ACLDEBUG
htp = SearchSysCacheTuple(RELNAME, PointerGetDatum(relname),
0,0,0);
if (!HeapTupleIsValid(htp)) {
elog(WARN, "pg_aclcheck: class \"%-.*s\" not found",
NAMEDATALEN, relname);
return(1);
}
if (!heap_attisnull(htp, Anum_pg_class_relacl)) {
relation = heap_openr(RelationRelationName);
tmp = (Acl *) heap_getattr(htp, InvalidBuffer,
Anum_pg_class_relacl,
RelationGetTupleDescriptor(relation),
(bool *) NULL);
acl = makeacl(ACL_NUM(tmp));
memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
heap_close(relation);
} else {
/* if the acl is null, by default the owner can do whatever
he wants to with it */
Oid ownerId;
relation = heap_openr(RelationRelationName);
ownerId = (Oid)heap_getattr(htp, InvalidBuffer,
Anum_pg_class_relowner,
RelationGetTupleDescriptor(relation),
(bool*) NULL);
acl = aclownerdefault(ownerId);
}
#else
{ /* This is why the syscache is great... */
static ScanKeyData relkey[1] = {
{ 0, Anum_pg_class_relname, NameEqualRegProcedure }
};
HeapScanDesc hsdp;
relation = heap_openr(RelationRelationName);
if (!RelationIsValid(relation)) {
elog(NOTICE, "pg_checkacl: could not open \"%-.*s\"??",
RelationRelationName);
return(1);
}
fmgr_info(NameEqualRegProcedure,
&relkey[0].sk_func,
&relkey[0].sk_nargs);
relkey[0].sk_argument = NameGetDatum(relname);
hsdp = heap_beginscan(relation, 0, NowTimeQual, 1, relkey);
htp = heap_getnext(hsdp, 0, (Buffer *) 0);
if (HeapTupleIsValid(htp) &&
!heap_attisnull(htp, Anum_pg_class_relacl)) {
tmp = (Acl *) heap_getattr(htp, InvalidBuffer,
Anum_pg_class_relacl,
RelationGetTupleDescriptor(relation),
(bool *) NULL);
acl = makeacl(ACL_NUM(tmp));
memmove((char *) acl, (char *) tmp, ACL_SIZE(tmp));
}
heap_endscan(hsdp);
heap_close(relation);
}
#endif
result = aclcheck(acl, id, (AclIdType) ACL_IDTYPE_UID, mode);
if (acl)
pfree(acl);
return(result);
}
int32
pg_ownercheck(char *usename,
char *value,
int cacheid)
{
HeapTuple htp;
AclId user_id, owner_id;
htp = SearchSysCacheTuple(USENAME, PointerGetDatum(usename),
0,0,0);
if (!HeapTupleIsValid(htp))
elog(WARN, "pg_ownercheck: user \"%-.*s\" not found",
NAMEDATALEN, usename);
user_id = (AclId) ((Form_pg_user) GETSTRUCT(htp))->usesysid;
/*
* Superusers bypass all permission-checking.
*/
if (((Form_pg_user) GETSTRUCT(htp))->usesuper) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "pg_ownercheck: user \"%-.*s\" is superuser",
NAMEDATALEN, usename);
#endif
return(1);
}
htp = SearchSysCacheTuple(cacheid, PointerGetDatum(value),
0,0,0);
switch (cacheid) {
case OPROID:
if (!HeapTupleIsValid(htp))
elog(WARN, "pg_ownercheck: operator %d not found",
(int) value);
owner_id = ((OperatorTupleForm) GETSTRUCT(htp))->oprowner;
break;
case PRONAME:
if (!HeapTupleIsValid(htp))
elog(WARN, "pg_ownercheck: function \"%-.*s\" not found",
NAMEDATALEN, value);
owner_id = ((Form_pg_proc) GETSTRUCT(htp))->proowner;
break;
case RELNAME:
if (!HeapTupleIsValid(htp))
elog(WARN, "pg_ownercheck: class \"%-.*s\" not found",
NAMEDATALEN, value);
owner_id = ((Form_pg_class) GETSTRUCT(htp))->relowner;
break;
case TYPNAME:
if (!HeapTupleIsValid(htp))
elog(WARN, "pg_ownercheck: type \"%-.*s\" not found",
NAMEDATALEN, value);
owner_id = ((TypeTupleForm) GETSTRUCT(htp))->typowner;
break;
default:
elog(WARN, "pg_ownercheck: invalid cache id: %d",
cacheid);
break;
}
return(user_id == owner_id);
}
int32
pg_func_ownercheck(char *usename,
char *funcname,
int nargs,
Oid *arglist)
{
HeapTuple htp;
AclId user_id, owner_id;
htp = SearchSysCacheTuple(USENAME, PointerGetDatum(usename),
0,0,0);
if (!HeapTupleIsValid(htp))
elog(WARN, "pg_func_ownercheck: user \"%-.*s\" not found",
NAMEDATALEN, usename);
user_id = (AclId) ((Form_pg_user) GETSTRUCT(htp))->usesysid;
/*
* Superusers bypass all permission-checking.
*/
if (((Form_pg_user) GETSTRUCT(htp))->usesuper) {
#ifdef ACLDEBUG_TRACE
elog(DEBUG, "pg_ownercheck: user \"%-.*s\" is superuser",
NAMEDATALEN, usename);
#endif
return(1);
}
htp = SearchSysCacheTuple(PRONAME,
PointerGetDatum(funcname),
PointerGetDatum(nargs),
PointerGetDatum(arglist),
0);
if (!HeapTupleIsValid(htp))
func_error("pg_func_ownercheck", funcname, nargs, (int*)arglist);
owner_id = ((Form_pg_proc) GETSTRUCT(htp))->proowner;
return(user_id == owner_id);
}

354
src/backend/tcop/dest.c Normal file
View File

@@ -0,0 +1,354 @@
/*-------------------------------------------------------------------------
*
* dest.c--
* support for various communication destinations - see lib/H/tcop/dest.h
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* BeginCommand - prepare destination for tuples of the given type
* EndCommand - tell destination that no more tuples will arrive
* NullCommand - tell dest that the last of a query sequence was processed
*
* NOTES
* These routines do the appropriate work before and after
* tuples are returned by a query to keep the backend and the
* "destination" portals synchronized.
*
*/
#include <stdio.h> /* for sprintf() */
#include "postgres.h"
#include "access/htup.h"
#include "libpq/libpq-be.h"
#include "access/printtup.h"
#include "utils/portal.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "executor/executor.h"
#include "tcop/dest.h"
#include "catalog/pg_type.h"
#include "utils/mcxt.h"
#include "commands/async.h"
/* ----------------
* output functions
* ----------------
*/
void
donothing(List *tuple, List *attrdesc)
{
}
void (*DestToFunction(CommandDest dest))()
{
switch (dest) {
case RemoteInternal:
return printtup_internal;
break;
case Remote:
return printtup;
break;
case Local:
return be_printtup;
break;
case Debug:
return debugtup;
break;
case None:
default:
return donothing;
break;
}
/*
* never gets here, but DECstation lint appears to be stupid...
*/
return donothing;
}
#define IS_INSERT_TAG(tag) (*tag == 'I' && *(tag+1) == 'N')
/* ----------------
* EndCommand - tell destination that no more tuples will arrive
* ----------------
*/
void
EndCommand(char *commandTag, CommandDest dest)
{
char buf[64];
switch (dest) {
case RemoteInternal:
case Remote:
/* ----------------
* tell the fe that the query is over
* ----------------
*/
pq_putnchar("C", 1);
/* pq_putint(0, 4); */
if (IS_INSERT_TAG(commandTag))
{
sprintf(buf, "%s %d", commandTag, GetAppendOid());
pq_putstr(buf);
}
else
pq_putstr(commandTag);
pq_flush();
break;
case Local:
case Debug:
break;
case CopyEnd:
pq_putnchar("Z", 1);
pq_flush();
break;
case None:
default:
break;
}
}
/*
* These are necessary to sync communications between fe/be processes doing
* COPY rel TO stdout
*
* or
*
* COPY rel FROM stdin
*
*/
void
SendCopyBegin()
{
pq_putnchar("B", 1);
/* pq_putint(0, 4); */
pq_flush();
}
void
ReceiveCopyBegin()
{
pq_putnchar("D", 1);
/* pq_putint(0, 4); */
pq_flush();
}
/* ----------------
* NullCommand - tell dest that the last of a query sequence was processed
*
* Necessary to implement the hacky FE/BE interface to handle
* multiple-return queries.
* ----------------
*/
void
NullCommand(CommandDest dest)
{
switch (dest) {
case RemoteInternal:
case Remote: {
#if 0
/* Do any asynchronous notification. If front end wants to poll,
it can send null queries to call this function.
*/
PQNotifyList *nPtr;
MemoryContext orig;
if (notifyContext == NULL) {
notifyContext = CreateGlobalMemory("notify");
}
orig = MemoryContextSwitchTo((MemoryContext)notifyContext);
for (nPtr = PQnotifies() ;
nPtr != NULL;
nPtr = (PQNotifyList *)SLGetSucc(&nPtr->Node)) {
pq_putnchar("A",1);
pq_putint(0, 4);
pq_putstr(nPtr->relname);
pq_putint(nPtr->be_pid,4);
PQremoveNotify(nPtr);
}
pq_flush();
PQcleanNotify(); /* garbage collect */
(void) MemoryContextSwitchTo(orig);
#endif
/* ----------------
* tell the fe that the last of the queries has finished
* ----------------
*/
/* pq_putnchar("I", 1); */
pq_putstr("I");
/* pq_putint(0, 4);*/
pq_flush();
}
break;
case Local:
case Debug:
case None:
default:
break;
}
}
/* ----------------
* BeginCommand - prepare destination for tuples of the given type
* ----------------
*/
void
BeginCommand(char *pname,
int operation,
TupleDesc tupdesc,
bool isIntoRel,
bool isIntoPortal,
char *tag,
CommandDest dest)
{
PortalEntry *entry;
AttributeTupleForm *attrs = tupdesc->attrs;
int natts = tupdesc->natts;
int i;
char *p;
switch (dest) {
case RemoteInternal:
case Remote:
/* ----------------
* if this is a "retrieve portal" query, just return
* because nothing needs to be sent to the fe.
* ----------------
*/
ResetAppendOid();
if (isIntoPortal)
return;
/* ----------------
* if portal name not specified for remote query,
* use the "blank" portal.
* ----------------
*/
if (pname == NULL)
pname = "blank";
/* ----------------
* send fe info on tuples we're about to send
* ----------------
*/
pq_flush();
pq_putnchar("P", 1); /* new portal.. */
pq_putstr(pname); /* portal name */
/* ----------------
* if this is a retrieve, then we send back the tuple
* descriptor of the tuples. "retrieve into" is an
* exception because no tuples are returned in that case.
* ----------------
*/
if (operation == CMD_SELECT && !isIntoRel) {
pq_putnchar("T", 1); /* type info to follow.. */
pq_putint(natts, 2); /* number of attributes in tuples */
for (i = 0; i < natts; ++i) {
pq_putstr(attrs[i]->attname.data);/* if 16 char name oops.. */
pq_putint((int) attrs[i]->atttypid, 4);
pq_putint(attrs[i]->attlen, 2);
}
}
pq_flush();
break;
case Local:
/* ----------------
* prepare local portal buffer for query results
* and setup result for PQexec()
* ----------------
*/
entry = be_currentportal();
if (pname != NULL)
pbuf_setportalinfo(entry, pname);
if (operation == CMD_SELECT && !isIntoRel) {
be_typeinit(entry, tupdesc, natts);
p = (char *) palloc(strlen(entry->name)+2);
p[0] = 'P';
strcpy(p+1,entry->name);
} else {
p = (char *) palloc(strlen(tag)+2);
p[0] = 'C';
strcpy(p+1,tag);
}
entry->result = p;
break;
case Debug:
/* ----------------
* show the return type of the tuples
* ----------------
*/
if (pname == NULL)
pname = "blank";
showatts(pname, tupdesc);
break;
case None:
default:
break;
}
}
static Oid AppendOid;
void
ResetAppendOid()
{
AppendOid = InvalidOid;
}
#define MULTI_TUPLE_APPEND -1
void
UpdateAppendOid(Oid newoid)
{
/*
* First update after AppendOid was reset (at command beginning).
*/
if (AppendOid == InvalidOid)
AppendOid = newoid;
/*
* Already detected a multiple tuple append, return a void oid ;)
*/
else if (AppendOid == MULTI_TUPLE_APPEND)
return;
/*
* Oid has been assigned once before, tag this as a multiple tuple
* append.
*/
else
AppendOid = MULTI_TUPLE_APPEND;
}
Oid
GetAppendOid()
{
if (AppendOid == MULTI_TUPLE_APPEND)
return InvalidOid;
return AppendOid;
}

78
src/backend/tcop/dest.h Normal file
View File

@@ -0,0 +1,78 @@
/*-------------------------------------------------------------------------
*
* dest.h--
* Whenever the backend is submitted a query, the results
* have to go someplace - either to the standard output,
* to a local portal buffer or to a remote portal buffer.
*
* - stdout is the destination only when we are running a
* backend without a postmaster and are returning results
* back to the user.
*
* - a local portal buffer is the destination when a backend
* executes a user-defined function which calls PQexec() or
* PQfn(). In this case, the results are collected into a
* PortalBuffer which the user's function may diddle with.
*
* - a remote portal buffer is the destination when we are
* running a backend with a frontend and the frontend executes
* PQexec() or PQfn(). In this case, the results are sent
* to the frontend via the pq_ functions.
*
* - None is the destination when the system executes
* a query internally. This is not used now but it may be
* useful for the parallel optimiser/executor.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: dest.h,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef DEST_H
#define DEST_H
#include "catalog/pg_attribute.h"
#include "access/tupdesc.h"
/* ----------------
* CommandDest is used to allow the results of calling
* pg_eval() to go to the right place.
* ----------------
*/
typedef enum {
None, /* results are discarded */
Debug, /* results go to debugging output */
Local, /* results go in local portal buffer */
Remote, /* results sent to frontend process */
CopyBegin, /* results sent to frontend process but are strings */
CopyEnd, /* results sent to frontend process but are strings */
RemoteInternal /* results sent to frontend process in internal
(binary) form */
} CommandDest;
/* AttrInfo* replaced with TupleDesc, now that TupleDesc also has within it
the number of attributes
typedef struct AttrInfo {
int numAttr;
AttributeTupleForm *attrs;
} AttrInfo;
*/
extern void donothing(List *tuple, List *attrdesc);
extern void (*DestToFunction(CommandDest dest))();
extern void EndCommand(char *commandTag, CommandDest dest);
extern void SendCopyBegin();
extern void ReceiveCopyBegin();
extern void NullCommand(CommandDest dest);
extern void BeginCommand(char *pname, int operation, TupleDesc attinfo,
bool isIntoRel, bool isIntoPortal, char *tag,
CommandDest dest);
extern void ResetAppendOid();
extern void UpdateAppendOid(Oid newoid);
extern Oid GetAppendOid();
#endif /* DEST_H */

353
src/backend/tcop/fastpath.c Normal file
View File

@@ -0,0 +1,353 @@
/*-------------------------------------------------------------------------
*
* fastpath.c--
* routines to handle function requests from the frontend
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/fastpath.c,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $
*
* NOTES
* This cruft is the server side of PQfn.
*
* - jolly 07/11/95:
*
* no longer rely on return sizes provided by the frontend. Always
* use the true lengths for the catalogs. Assume that the frontend
* has allocated enough space to handle the result value returned.
*
* trust that the user knows what he is doing with the args. If the
* sys catalog says it is a varlena, assume that the user is only sending
* down VARDATA and that the argsize is the VARSIZE. If the arg is
* fixed len, assume that the argsize given by the user is correct.
*
* if the function returns by value, then only send 4 bytes value
* back to the frontend. If the return returns by reference,
* send down only the data portion and set the return size appropriately.
*
* OLD COMMENTS FOLLOW
*
* The VAR_LENGTH_{ARGS,RESULT} stuff is limited to MAX_STRING_LENGTH
* (see src/backend/tmp/fastpath.h) for no obvious reason. Since its
* primary use (for us) is for Inversion path names, it should probably
* be increased to 256 (MAXPATHLEN for Inversion, hidden in pg_type
* as well as utils/adt/filename.c).
*
* Quoth PMA on 08/15/93:
*
* This code has been almost completely rewritten with an eye to
* keeping it as compatible as possible with the previous (broken)
* implementation.
*
* The previous implementation would assume (1) that any value of
* length <= 4 bytes was passed-by-value, and that any other value
* was a struct varlena (by-reference). There was NO way to pass a
* fixed-length by-reference argument (like char16) or a struct
* varlena of size <= 4 bytes.
*
* The new implementation checks the catalogs to determine whether
* a value is by-value (type "0" is null-delimited character string,
* as it is for, e.g., the parser). The only other item obtained
* from the catalogs is whether or not the value should be placed in
* a struct varlena or not. Otherwise, the size given by the
* frontend is assumed to be correct (probably a bad decision, but
* we do strange things in the name of compatibility).
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "tcop/tcopdebug.h"
#include "utils/palloc.h"
#include "fmgr.h"
#include "utils/elog.h"
#include "utils/builtins.h" /* for oideq */
#include "tcop/fastpath.h"
#include "libpq/libpq.h"
#include "access/xact.h" /* for TransactionId/CommandId protos */
#include "utils/syscache.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
/* ----------------
* SendFunctionResult
* ----------------
*/
static void
SendFunctionResult(Oid fid, /* function id */
char *retval, /* actual return value */
bool retbyval,
int retlen /* the length according to the catalogs */
)
{
pq_putnchar("V", 1);
if (retlen != 0) {
pq_putnchar("G", 1);
if (retbyval) { /* by-value */
pq_putint(retlen, 4);
pq_putint((int)retval, retlen);
} else { /* by-reference ... */
if (retlen < 0) { /* ... varlena */
pq_putint(VARSIZE(retval) - VARHDRSZ, 4);
pq_putnchar(VARDATA(retval), VARSIZE(retval) - VARHDRSZ);
} else { /* ... fixed */
pq_putint(retlen, 4);
pq_putnchar(retval, retlen);
}
}
}
pq_putnchar("0", 1);
pq_flush();
}
/*
* This structure saves enough state so that one can avoid having to
* do catalog lookups over and over again. (Each RPC can require up
* to MAXFMGRARGS+2 lookups, which is quite tedious.)
*
* The previous incarnation of this code just assumed that any argument
* of size <= 4 was by value; this is not correct. There is no cheap
* way to determine function argument length etc.; one must simply pay
* the price of catalog lookups.
*/
struct fp_info {
Oid funcid;
int nargs;
bool argbyval[MAXFMGRARGS];
int32 arglen[MAXFMGRARGS]; /* signed (for varlena) */
bool retbyval;
int32 retlen; /* signed (for varlena) */
TransactionId xid;
CommandId cid;
};
/*
* We implement one-back caching here. If we need to do more, we can.
* Most routines in tight loops (like PQfswrite -> F_LOWRITE) will do
* the same thing repeatedly.
*/
static struct fp_info last_fp = { InvalidOid };
/*
* valid_fp_info
*
* RETURNS:
* 1 if the state in 'fip' is valid
* 0 otherwise
*
* "valid" means:
* The saved state was either uninitialized, for another function,
* or from a previous command. (Commands can do updates, which
* may invalidate catalog entries for subsequent commands. This
* is overly pessimistic but since there is no smarter invalidation
* scheme...).
*/
static int
valid_fp_info(Oid func_id, struct fp_info *fip)
{
Assert(OidIsValid(func_id));
Assert(fip != (struct fp_info *) NULL);
return(OidIsValid(fip->funcid) &&
oideq(func_id, fip->funcid) &&
TransactionIdIsCurrentTransactionId(fip->xid) &&
CommandIdIsCurrentCommandId(fip->cid));
}
/*
* update_fp_info
*
* Performs catalog lookups to load a struct fp_info 'fip' for the
* function 'func_id'.
*
* RETURNS:
* The correct information in 'fip'. Sets 'fip->funcid' to
* InvalidOid if an exception occurs.
*/
static void
update_fp_info(Oid func_id, struct fp_info *fip)
{
Oid *argtypes; /* an oid8 */
Oid rettype;
HeapTuple func_htp, type_htp;
TypeTupleForm tp;
Form_pg_proc pp;
int i;
Assert(OidIsValid(func_id));
Assert(fip != (struct fp_info *) NULL);
/*
* Since the validity of this structure is determined by whether
* the funcid is OK, we clear the funcid here. It must not be
* set to the correct value until we are about to return with
* a good struct fp_info, since we can be interrupted (i.e., with
* an elog(WARN, ...)) at any time.
*/
memset((char *) fip, 0, (int) sizeof(struct fp_info));
fip->funcid = InvalidOid;
func_htp = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(func_id),
0,0,0);
if (!HeapTupleIsValid(func_htp)) {
elog(WARN, "update_fp_info: cache lookup for function %d failed",
func_id);
}
pp = (Form_pg_proc) GETSTRUCT(func_htp);
fip->nargs = pp->pronargs;
rettype = pp->prorettype;
argtypes = pp->proargtypes;
for (i = 0; i < fip->nargs; ++i) {
if (OidIsValid(argtypes[i])) {
type_htp = SearchSysCacheTuple(TYPOID,
ObjectIdGetDatum(argtypes[i]),
0,0,0);
if (!HeapTupleIsValid(type_htp)) {
elog(WARN, "update_fp_info: bad argument type %d for %d",
argtypes[i], func_id);
}
tp = (TypeTupleForm) GETSTRUCT(type_htp);
fip->argbyval[i] = tp->typbyval;
fip->arglen[i] = tp->typlen;
} /* else it had better be VAR_LENGTH_ARG */
}
if (OidIsValid(rettype)) {
type_htp = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(rettype),
0,0,0);
if (!HeapTupleIsValid(type_htp)) {
elog(WARN, "update_fp_info: bad return type %d for %d",
rettype, func_id);
}
tp = (TypeTupleForm) GETSTRUCT(type_htp);
fip->retbyval = tp->typbyval;
fip->retlen = tp->typlen;
} /* else it had better by VAR_LENGTH_RESULT */
fip->xid = GetCurrentTransactionId();
fip->cid = GetCurrentCommandId();
/*
* This must be last!
*/
fip->funcid = func_id;
}
/*
* HandleFunctionRequest
*
* Server side of PQfn (fastpath function calls from the frontend).
* This corresponds to the libpq protocol symbol "F".
*
* RETURNS:
* nothing of significance.
* All errors result in elog(WARN,...).
*/
int
HandleFunctionRequest()
{
Oid fid;
int argsize;
int nargs;
char *arg[8];
char *retval;
int i;
uint32 palloced;
char *p;
struct fp_info *fip;
fid = (Oid) pq_getint(4); /* function oid */
nargs = pq_getint(4); /* # of arguments */
/*
* This is where the one-back caching is done.
* If you want to save more state, make this a loop around an array.
*/
fip = &last_fp;
if (!valid_fp_info(fid, fip)) {
update_fp_info(fid, fip);
}
if (fip->nargs != nargs) {
elog(WARN, "HandleFunctionRequest: actual arguments (%d) != registered arguments (%d)",
nargs, fip->nargs);
}
/*
* Copy arguments into arg vector. If we palloc() an argument, we need
* to remember, so that we pfree() it after the call.
*/
palloced = 0x0;
for (i = 0; i < 8; ++i) {
if (i >= nargs) {
arg[i] = (char *) NULL;
} else {
argsize = pq_getint(4);
Assert(argsize > 0);
if (fip->argbyval[i]) { /* by-value */
Assert(argsize <= 4);
arg[i] = (char *) pq_getint(argsize);
} else { /* by-reference ... */
if (fip->arglen[i] < 0) { /* ... varlena */
if (!(p = palloc(argsize + VARHDRSZ))) {
elog(WARN, "HandleFunctionRequest: palloc failed");
}
VARSIZE(p) = argsize + VARHDRSZ;
pq_getnchar(VARDATA(p), 0, argsize);
} else { /* ... fixed */
/* XXX cross our fingers and trust "argsize" */
if (!(p = palloc(argsize))) {
elog(WARN, "HandleFunctionRequest: palloc failed");
}
pq_getnchar(p, 0, argsize);
}
palloced |= (1 << i);
arg[i] = p;
}
}
}
#ifndef NO_FASTPATH
retval = fmgr(fid,
arg[0], arg[1], arg[2], arg[3],
arg[4], arg[5], arg[6], arg[7]);
#else
retval = NULL;
#endif /* NO_FASTPATH */
/* free palloc'ed arguments */
for (i = 0; i < nargs; ++i) {
if (palloced & (1 << i))
pfree(arg[i]);
}
/*
* If this is an ordinary query (not a retrieve portal p ...), then
* we return the data to the user. If the return value was palloc'ed,
* then it must also be freed.
*/
#ifndef NO_FASTPATH
SendFunctionResult(fid, retval, fip->retbyval, fip->retlen);
#else
SendFunctionResult(fid, retval, fip->retbyval, 0);
#endif /* NO_FASTPATH */
if (!fip->retbyval)
pfree(retval);
return(0);
}

View File

@@ -0,0 +1,31 @@
/*-------------------------------------------------------------------------
*
* fastpath.h--
*
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: fastpath.h,v 1.1.1.1 1996/07/09 06:21:59 scrappy Exp $
*
* NOTES
* This information pulled out of tcop/fastpath.c and put
* here so that the PQfn() in be-pqexec.c could access it.
* -cim 2/26/91
*
*-------------------------------------------------------------------------
*/
#ifndef FASTPATH_H
#define FASTPATH_H
/* ----------------
* fastpath #defines
* ----------------
*/
#define VAR_LENGTH_RESULT (-1)
#define VAR_LENGTH_ARG (-5)
#define MAX_STRING_LENGTH 256
extern int HandleFunctionRequest(void);
#endif /* FASTPATH_H */

1500
src/backend/tcop/postgres.c Normal file

File diff suppressed because it is too large Load Diff

362
src/backend/tcop/pquery.c Normal file
View File

@@ -0,0 +1,362 @@
/*-------------------------------------------------------------------------
*
* pquery.c--
* POSTGRES process query command code
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "tcop/tcopdebug.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "utils/mcxt.h"
#include "miscadmin.h"
#include "utils/portal.h"
#include "nodes/pg_list.h"
#include "nodes/primnodes.h"
#include "nodes/plannodes.h"
#include "nodes/execnodes.h"
#include "nodes/memnodes.h"
#include "tcop/dest.h"
#include "executor/execdefs.h"
#include "executor/execdesc.h"
#include "executor/executor.h"
#include "tcop/pquery.h"
#include "commands/command.h"
static char* CreateOperationTag(int operationType);
/* ----------------------------------------------------------------
* CreateQueryDesc
* ----------------------------------------------------------------
*/
QueryDesc *
CreateQueryDesc(Query *parsetree,
Plan *plantree,
CommandDest dest)
{
QueryDesc *qd = (QueryDesc *)palloc(sizeof(QueryDesc));
qd->operation = parsetree->commandType; /* operation */
qd->parsetree = parsetree; /* parse tree */
qd->plantree = plantree; /* plan */
qd->dest = dest; /* output dest */
return qd;
}
/* ----------------------------------------------------------------
* CreateExecutorState
*
* Note: this may someday take parameters -cim 9/18/89
* ----------------------------------------------------------------
*/
EState *
CreateExecutorState()
{
EState *state;
extern int NBuffers;
long *refcount;
/* ----------------
* create a new executor state
* ----------------
*/
state = makeNode(EState);
/* ----------------
* initialize the Executor State structure
* ----------------
*/
state->es_direction = EXEC_FRWD;
state->es_range_table = NIL;
state->es_into_relation_descriptor = NULL;
state->es_result_relation_info = NULL;
state->es_param_list_info = NULL;
state->es_BaseId = 0;
state->es_tupleTable = NULL;
state->es_junkFilter = NULL;
refcount = (long *) palloc(NBuffers * sizeof(long));
memset((char *) refcount, 0, NBuffers * sizeof(long));
state->es_refcount = (int *) refcount;
/* ----------------
* return the executor state structure
* ----------------
*/
return state;
}
/* ----------------------------------------------------------------
* CreateOperationTag
*
* utility to get a string representation of the
* query operation.
* ----------------------------------------------------------------
*/
static char*
CreateOperationTag(int operationType)
{
char* tag;
switch (operationType) {
case CMD_SELECT:
tag = "SELECT";
break;
case CMD_INSERT:
tag = "INSERT";
break;
case CMD_DELETE:
tag = "DELETE";
break;
case CMD_UPDATE:
tag = "UPDATE";
break;
default:
elog(DEBUG, "CreateOperationTag: unknown operation type %d",
operationType);
tag = NULL;
break;
}
return tag;
}
/* ----------------
* ProcessPortal
* ----------------
*/
void
ProcessPortal(char* portalName,
Query *parseTree,
Plan *plan,
EState *state,
TupleDesc attinfo,
CommandDest dest)
{
Portal portal;
MemoryContext portalContext;
/* ----------------
* convert the current blank portal into the user-specified
* portal and initialize the state and query descriptor.
* ----------------
*/
if (PortalNameIsSpecial(portalName))
elog(WARN,
"The portal name %s is reserved for internal use",
portalName);
portal = BlankPortalAssignName(portalName);
PortalSetQuery(portal,
CreateQueryDesc(parseTree, plan, dest),
attinfo,
state,
PortalCleanup);
/* ----------------
* now create a new blank portal and switch to it.
* Otherwise, the new named portal will be cleaned.
*
* Note: portals will only be supported within a BEGIN...END
* block in the near future. Later, someone will fix it to
* do what is possible across transaction boundries. -hirohama
* ----------------
*/
portalContext = (MemoryContext)
PortalGetHeapMemory(GetPortalByName(NULL));
MemoryContextSwitchTo(portalContext);
StartPortalAllocMode(DefaultAllocMode, 0);
}
/* ----------------------------------------------------------------
* ProcessQueryDesc
*
* Read the comments for ProcessQuery() below...
* ----------------------------------------------------------------
*/
void
ProcessQueryDesc(QueryDesc *queryDesc)
{
Query *parseTree;
Plan *plan;
int operation;
char* tag;
EState *state;
TupleDesc attinfo;
bool isRetrieveIntoPortal;
bool isRetrieveIntoRelation;
char* intoName;
CommandDest dest;
/* ----------------
* get info from the query desc
* ----------------
*/
parseTree = queryDesc->parsetree;
plan = queryDesc->plantree;
operation = queryDesc->operation;
tag = CreateOperationTag(operation);
dest = queryDesc->dest;
/* ----------------
* initialize portal/into relation status
* ----------------
*/
isRetrieveIntoPortal = false;
isRetrieveIntoRelation = false;
if (operation == CMD_SELECT) {
if (parseTree->isPortal) {
isRetrieveIntoPortal = true;
intoName = parseTree->into;
if (parseTree->isBinary) {
/*
* For internal format portals, we change Remote
* (externalized form) to RemoteInternal (internalized
* form)
*/
dest = queryDesc->dest = RemoteInternal;
}
} else if (parseTree->into != NULL) {
/* select into table */
isRetrieveIntoRelation = true;
}
}
/* ----------------
* when performing a retrieve into, we override the normal
* communication destination during the processing of the
* the query. This only affects the tuple-output function
* - the correct destination will still see BeginCommand()
* and EndCommand() messages.
* ----------------
*/
if (isRetrieveIntoRelation)
queryDesc->dest = (int) None;
/* ----------------
* create a default executor state..
* ----------------
*/
state = CreateExecutorState();
/* ----------------
* call ExecStart to prepare the plan for execution
* ----------------
*/
attinfo = ExecutorStart(queryDesc, state);
/* ----------------
* report the query's result type information
* back to the front end or to whatever destination
* we're dealing with.
* ----------------
*/
BeginCommand(NULL,
operation,
attinfo,
isRetrieveIntoRelation,
isRetrieveIntoPortal,
tag,
dest);
/* ----------------
* Named portals do not do a "fetch all" initially, so now
* we return since ExecMain has been called with EXEC_START
* to initialize the query plan.
*
* Note: ProcessPortal transforms the current "blank" portal
* into a named portal and creates a new blank portal so
* everything we allocated in the current "blank" memory
* context will be preserved across queries. -cim 2/22/91
* ----------------
*/
if (isRetrieveIntoPortal) {
PortalExecutorHeapMemory = NULL;
ProcessPortal(intoName,
parseTree,
plan,
state,
attinfo,
dest);
EndCommand(tag, dest);
return;
}
/* ----------------
* Now we get to the important call to ExecutorRun() where we
* actually run the plan..
* ----------------
*/
ExecutorRun(queryDesc, state, EXEC_RUN, 0);
/* ----------------
* now, we close down all the scans and free allocated resources...
* with ExecutorEnd()
* ----------------
*/
ExecutorEnd(queryDesc, state);
/* ----------------
* Notify the destination of end of processing.
* ----------------
*/
EndCommand(tag, dest);
}
/* ----------------------------------------------------------------
* ProcessQuery
*
* Execute a plan, the non-parallel version
* ----------------------------------------------------------------
*/
void
ProcessQuery(Query *parsetree,
Plan *plan,
char *argv[],
Oid *typev,
int nargs,
CommandDest dest)
{
QueryDesc *queryDesc;
extern int dontExecute; /* from postgres.c */
extern void print_plan (Plan* p, Query* parsetree); /* from print.c */
queryDesc = CreateQueryDesc(parsetree, plan, dest);
if (dontExecute) {
/* don't execute it, just show the query plan */
print_plan(plan, parsetree);
} else
ProcessQueryDesc(queryDesc);
}

36
src/backend/tcop/pquery.h Normal file
View File

@@ -0,0 +1,36 @@
/*-------------------------------------------------------------------------
*
* pquery.h--
* prototypes for pquery.c.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pquery.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PQUERY_H
#define PQUERY_H
#include "executor/execdesc.h"
#include "tcop/dest.h"
/* moved to execdesc.h
extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
CommandDest dest);
*/
extern EState *CreateExecutorState();
extern void ProcessPortal(char *portalName, Query *parseTree,
Plan *plan, EState *state, TupleDesc attinfo,
CommandDest dest);
extern void ProcessQueryDesc(QueryDesc *queryDesc);
extern void ProcessQuery(Query *parsetree, Plan *plan, char *argv[],
Oid *typev, int nargs, CommandDest dest);
#endif /* pqueryIncluded */

View File

@@ -0,0 +1,43 @@
/*-------------------------------------------------------------------------
*
* tcopdebug.h--
* #defines governing debugging behaviour in the traffic cop
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: tcopdebug.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef TCOPDEBUG_H
#define TCOPDEBUG_H
/* ----------------------------------------------------------------
* debugging defines.
*
* If you want certain debugging behaviour, then #define
* the variable to 1, else #undef it. -cim 10/26/89
* ----------------------------------------------------------------
*/
/* ----------------
* TCOP_SHOWSTATS controls whether or not buffer and
* access method statistics are shown for each query. -cim 2/9/89
* ----------------
*/
#undef TCOP_SHOWSTATS
/* ----------------
* TCOP_DONTUSENEWLINE controls the default setting of
* the UseNewLine variable in postgres.c
* ----------------
*/
#undef TCOP_DONTUSENEWLINE
/* ----------------------------------------------------------------
* #defines controlled by above definitions
* ----------------------------------------------------------------
*/
#endif /* TCOPDEBUG_H */

View File

@@ -0,0 +1,40 @@
/*-------------------------------------------------------------------------
*
* tcopprot.h--
* prototypes for postgres.c.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: tcopprot.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
* function prototypes without having to include tcop.h which single
* handedly includes the whole f*cking tree -- mer 5 Nov. 1991
*
*-------------------------------------------------------------------------
*/
#ifndef TCOPPROT_H
#define TCOPPROT_H
#include "tcop/dest.h"
#include "nodes/pg_list.h"
#include "parser/parse_query.h"
#ifndef BOOTSTRAP_INCLUDE
extern List *pg_plan(char *query_string, Oid *typev, int nargs,
QueryTreeList **queryListP, CommandDest dest);
extern void pg_eval(char *query_string, char *argv[], Oid *typev, int nargs);
extern void pg_eval_dest(char *query_string, char *argv[], Oid *typev,
int nargs, CommandDest dest);
#endif /* BOOTSTRAP_HEADER */
extern void handle_warn();
extern void quickdie();
extern void die();
extern int PostgresMain(int argc, char *argv[]);
extern void ResetUsage();
extern void ShowUsage();
#endif /* tcopprotIncluded */

646
src/backend/tcop/utility.c Normal file
View File

@@ -0,0 +1,646 @@
/*-------------------------------------------------------------------------
*
* utility.c--
* Contains functions which control the execution of the POSTGRES utility
* commands. At one time acted as an interface between the Lisp and C
* systems.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "parser/dbcommands.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/pg_type.h"
#include "commands/async.h"
#include "commands/cluster.h"
#include "commands/command.h"
#include "commands/copy.h"
#include "commands/creatinh.h"
#include "commands/defrem.h"
#include "commands/purge.h"
#include "commands/rename.h"
#include "commands/view.h"
#include "commands/version.h"
#include "commands/vacuum.h"
#include "commands/recipe.h"
#include "commands/explain.h"
#include "nodes/parsenodes.h"
#include "parse.h"
#include "utils/elog.h"
#include "utils/builtins.h"
#include "utils/acl.h"
#include "utils/palloc.h"
#include "rewrite/rewriteRemove.h"
#include "rewrite/rewriteDefine.h"
#include "tcop/tcopdebug.h"
#include "tcop/dest.h"
#ifndef NO_SECURITY
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/syscache.h"
#endif
/* ----------------
* CHECK_IF_ABORTED() is used to avoid doing unnecessary
* processing within an aborted transaction block.
* ----------------
*/
#define CHECK_IF_ABORTED() \
if (IsAbortedTransactionBlockState()) { \
elog(NOTICE, "(transaction aborted): %s", \
"queries ignored until END"); \
commandTag = "*ABORT STATE*"; \
break; \
} \
/* ----------------
* general utility function invoker
* ----------------
*/
void
ProcessUtility(Node *parsetree,
CommandDest dest)
{
char *commandTag = NULL;
char *relname;
char *relationName;
char *userName;
userName = GetPgUserName();
switch (nodeTag(parsetree)) {
/* ********************************
* transactions
* ********************************
*/
case T_TransactionStmt:
{
TransactionStmt *stmt = (TransactionStmt *)parsetree;
switch (stmt->command) {
case BEGIN_TRANS:
commandTag = "BEGIN";
CHECK_IF_ABORTED();
BeginTransactionBlock();
break;
case END_TRANS:
commandTag = "END";
EndTransactionBlock();
break;
case ABORT_TRANS:
commandTag = "ABORT";
UserAbortTransactionBlock();
break;
}
}
break;
/* ********************************
* portal manipulation
* ********************************
*/
case T_ClosePortalStmt:
{
ClosePortalStmt *stmt = (ClosePortalStmt *)parsetree;
commandTag = "CLOSE";
CHECK_IF_ABORTED();
PerformPortalClose(stmt->portalname, dest);
}
break;
case T_FetchStmt:
{
FetchStmt *stmt = (FetchStmt *)parsetree;
char *portalName = stmt->portalname;
bool forward;
int count;
commandTag = "FETCH";
CHECK_IF_ABORTED();
forward = (bool)(stmt->direction == FORWARD);
/* parser ensures that count is >= 0 and
'fetch ALL' -> 0 */
count = stmt->howMany;
PerformPortalFetch(portalName, forward, count, commandTag, dest);
}
break;
/* ********************************
* relation and attribute manipulation
* ********************************
*/
case T_CreateStmt:
commandTag = "CREATE";
CHECK_IF_ABORTED();
DefineRelation((CreateStmt *)parsetree);
break;
case T_DestroyStmt:
{
DestroyStmt *stmt = (DestroyStmt *)parsetree;
List *arg;
List *args = stmt->relNames;
commandTag = "DROP";
CHECK_IF_ABORTED();
foreach (arg, args) {
relname = strVal(lfirst(arg));
if (IsSystemRelationName(relname))
elog(WARN, "class \"%-.*s\" is a system catalog",
NAMEDATALEN, relname);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(WARN, "you do not own class \"%-.*s\"",
NAMEDATALEN, relname);
#endif
}
foreach (arg, args) {
relname = strVal(lfirst(arg));
RemoveRelation(relname);
}
}
break;
case T_PurgeStmt:
{
PurgeStmt *stmt = (PurgeStmt *)parsetree;
commandTag = "PURGE";
CHECK_IF_ABORTED();
RelationPurge(stmt->relname,
stmt->beforeDate, /* absolute time string */
stmt->afterDate); /* relative time string */
}
break;
case T_CopyStmt:
{
CopyStmt *stmt = (CopyStmt *)parsetree;
char *filename;
char *delim;
bool isBinary;
bool isFrom;
bool pipe = false;
commandTag = "COPY";
CHECK_IF_ABORTED();
relname = stmt->relname;
isBinary = stmt->binary;
isFrom = (bool)(stmt->direction == FROM);
filename = stmt->filename;
delim = stmt->delimiter;
#ifndef NO_SECURITY
if (isFrom) {
if (!pg_aclcheck(relname, userName, ACL_RD))
elog(WARN, "%s %s", relname, ACL_NO_PRIV_WARNING);
} else {
if (!pg_aclcheck(relname, userName, ACL_WR))
elog(WARN, "%s %s", relname, ACL_NO_PRIV_WARNING);
}
#endif
/* Free up file descriptors - going to do a read... */
closeOneVfd();
/*
* use stdin/stdout if filename is null.
*/
if (filename == NULL)
pipe = true;
if (pipe && IsUnderPostmaster) dest = CopyEnd;
DoCopy(relname, isBinary, isFrom, pipe, filename, delim);
}
break;
case T_AddAttrStmt:
{
AddAttrStmt *stmt = (AddAttrStmt *)parsetree;
commandTag = "ADD";
CHECK_IF_ABORTED();
/* owner checking done in PerformAddAttribute (now recursive) */
PerformAddAttribute(stmt->relname,
userName,
stmt->inh,
stmt->colDef);
}
break;
/*
* schema
*/
case T_RenameStmt:
{
RenameStmt *stmt = (RenameStmt *)parsetree;
commandTag = "RENAME";
CHECK_IF_ABORTED();
relname = stmt->relname;
if (IsSystemRelationName(relname))
elog(WARN, "class \"%s\" is a system catalog",
relname);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(WARN, "you do not own class \"%s\"",
relname);
#endif
/* ----------------
* XXX using len == 3 to tell the difference
* between "rename rel to newrel" and
* "rename att in rel to newatt" will not
* work soon because "rename type/operator/rule"
* stuff is being added. - cim 10/24/90
* ----------------
* [another piece of amuzing but useless anecdote -- ay]
*/
if (stmt->column == NULL) {
/* ----------------
* rename relation
*
* Note: we also rename the "type" tuple
* corresponding to the relation.
* ----------------
*/
renamerel(relname, /* old name */
stmt->newname); /* new name */
TypeRename(relname, /* old name */
stmt->newname); /* new name */
} else {
/* ----------------
* rename attribute
* ----------------
*/
renameatt(relname, /* relname */
stmt->column, /* old att name */
stmt->newname, /* new att name */
userName,
stmt->inh); /* recursive? */
}
}
break;
case T_ChangeACLStmt:
{
ChangeACLStmt *stmt = (ChangeACLStmt *)parsetree;
List *i;
AclItem *aip;
unsigned modechg;
commandTag = "CHANGE";
CHECK_IF_ABORTED();
aip = stmt->aclitem;
modechg = stmt->modechg;
#ifndef NO_SECURITY
foreach (i, stmt->relNames) {
relname = strVal(lfirst(i));
if (!pg_ownercheck(userName, relname, RELNAME))
elog(WARN, "you do not own class \"%-.*s\"",
NAMEDATALEN, relname);
}
#endif
foreach (i, stmt->relNames) {
relname = strVal(lfirst(i));
ChangeAcl(relname, aip, modechg);
}
}
break;
/* ********************************
* object creation / destruction
* ********************************
*/
case T_DefineStmt:
{
DefineStmt *stmt = (DefineStmt *)parsetree;
commandTag = "CREATE";
CHECK_IF_ABORTED();
switch(stmt->defType) {
case OPERATOR:
DefineOperator(stmt->defname, /* operator name */
stmt->definition); /* rest */
break;
case P_TYPE:
{
DefineType (stmt->defname, stmt->definition);
}
break;
case AGGREGATE:
DefineAggregate(stmt->defname, /*aggregate name */
stmt->definition); /* rest */
break;
}
}
break;
case T_ViewStmt: /* VIEW */
{
ViewStmt *stmt = (ViewStmt *)parsetree;
commandTag = "CREATE";
CHECK_IF_ABORTED();
DefineView (stmt->viewname, stmt->query); /* retrieve parsetree */
}
break;
case T_ProcedureStmt: /* FUNCTION */
commandTag = "CREATE";
CHECK_IF_ABORTED();
DefineFunction((ProcedureStmt *)parsetree, dest); /* everything */
break;
case T_IndexStmt:
{
IndexStmt *stmt = (IndexStmt *)parsetree;
commandTag = "CREATE";
CHECK_IF_ABORTED();
/* XXX no support for ARCHIVE indices, yet */
DefineIndex(stmt->relname, /* relation name */
stmt->idxname, /* index name */
stmt->accessMethod, /* am name */
stmt->indexParams, /* parameters */
stmt->withClause,
(Expr*)stmt->whereClause,
stmt->rangetable);
}
break;
case T_RuleStmt:
{
RuleStmt *stmt = (RuleStmt *)parsetree;
#ifndef NO_SECURITY
relname = stmt->object->relname;
if (!pg_aclcheck(relname, userName, ACL_RU))
elog(WARN, "%s %s", relname, ACL_NO_PRIV_WARNING);
#endif
commandTag = "CREATE";
CHECK_IF_ABORTED();
DefineQueryRewrite(stmt);
}
break;
case T_ExtendStmt:
{
ExtendStmt *stmt = (ExtendStmt *)parsetree;
commandTag = "EXTEND";
CHECK_IF_ABORTED();
ExtendIndex(stmt->idxname, /* index name */
(Expr*)stmt->whereClause, /* where */
stmt->rangetable);
}
break;
case T_RemoveStmt:
{
RemoveStmt *stmt = (RemoveStmt *)parsetree;
commandTag = "DROP";
CHECK_IF_ABORTED();
switch(stmt->removeType) {
case AGGREGATE:
RemoveAggregate(stmt->name);
break;
case INDEX:
relname = stmt->name;
if (IsSystemRelationName(relname))
elog(WARN, "class \"%s\" is a system catalog index",
relname);
#ifndef NO_SECURITY
if (!pg_ownercheck(userName, relname, RELNAME))
elog(WARN, "you do not own class \"%s\"",
relname);
#endif
RemoveIndex(relname);
break;
case RULE:
{
char *rulename = stmt->name;
#ifndef NO_SECURITY
relationName = RewriteGetRuleEventRel(rulename);
if (!pg_aclcheck(relationName, userName, ACL_RU))
elog(WARN, "%s %s", relationName, ACL_NO_PRIV_WARNING);
#endif
RemoveRewriteRule(rulename);
}
break;
case P_TYPE:
#ifndef NO_SECURITY
/* XXX moved to remove.c */
#endif
RemoveType(stmt->name);
break;
case VIEW:
{
char *viewName = stmt->name;
char *ruleName;
extern char *RewriteGetRuleEventRel();
#ifndef NO_SECURITY
ruleName = MakeRetrieveViewRuleName(viewName);
relationName = RewriteGetRuleEventRel(ruleName);
if (!pg_ownercheck(userName, relationName, RELNAME))
elog(WARN, "%s %s", relationName, ACL_NO_PRIV_WARNING);
pfree(ruleName);
#endif
RemoveView(viewName);
}
break;
}
break;
}
break;
case T_RemoveFuncStmt:
{
RemoveFuncStmt *stmt = (RemoveFuncStmt *)parsetree;
commandTag = "DROP";
CHECK_IF_ABORTED();
RemoveFunction(stmt->funcname,
length(stmt->args),
stmt->args);
}
break;
case T_RemoveOperStmt:
{
RemoveOperStmt *stmt = (RemoveOperStmt *)parsetree;
char* type1 = (char*) NULL;
char *type2 = (char*) NULL;
commandTag = "DROP";
CHECK_IF_ABORTED();
if (lfirst(stmt->args)!=NULL)
type1 = strVal(lfirst(stmt->args));
if (lsecond(stmt->args)!=NULL)
type2 = strVal(lsecond(stmt->args));
RemoveOperator(stmt->opname, type1, type2);
}
break;
case T_VersionStmt:
{
elog(WARN, "CREATE VERSION is not currently implemented");
}
break;
case T_CreatedbStmt:
{
CreatedbStmt *stmt = (CreatedbStmt *)parsetree;
commandTag = "CREATEDB";
CHECK_IF_ABORTED();
createdb(stmt->dbname);
}
break;
case T_DestroydbStmt:
{
DestroydbStmt *stmt = (DestroydbStmt *)parsetree;
commandTag = "DESTROYDB";
CHECK_IF_ABORTED();
destroydb(stmt->dbname);
}
break;
/* Query-level asynchronous notification */
case T_NotifyStmt:
{
NotifyStmt *stmt = (NotifyStmt *)parsetree;
commandTag = "NOTIFY";
CHECK_IF_ABORTED();
Async_Notify(stmt->relname);
}
break;
case T_ListenStmt:
{
ListenStmt *stmt = (ListenStmt *)parsetree;
commandTag = "LISTEN";
CHECK_IF_ABORTED();
Async_Listen(stmt->relname,MasterPid);
}
break;
/* ********************************
* dynamic loader
* ********************************
*/
case T_LoadStmt:
{
LoadStmt *stmt = (LoadStmt *)parsetree;
FILE *fp, *fopen();
char *filename;
commandTag = "LOAD";
CHECK_IF_ABORTED();
filename = stmt->filename;
closeAllVfds();
if ((fp = fopen(filename, "r")) == NULL)
elog(WARN, "LOAD: could not open file %s", filename);
fclose(fp);
load_file(filename);
}
break;
case T_ClusterStmt:
{
ClusterStmt *stmt = (ClusterStmt *)parsetree;
commandTag = "CLUSTER";
CHECK_IF_ABORTED();
cluster(stmt->relname, stmt->indexname);
}
break;
case T_VacuumStmt:
commandTag = "VACUUM";
CHECK_IF_ABORTED();
vacuum(((VacuumStmt *) parsetree)->vacrel);
break;
case T_ExplainStmt:
{
ExplainStmt *stmt = (ExplainStmt *)parsetree;
commandTag = "EXPLAIN";
CHECK_IF_ABORTED();
ExplainQuery(stmt->query, stmt->options, dest);
}
break;
/* ********************************
Tioga-related statements
*********************************/
case T_RecipeStmt:
{
RecipeStmt* stmt = (RecipeStmt*)parsetree;
commandTag="EXECUTE RECIPE";
CHECK_IF_ABORTED();
beginRecipe(stmt);
}
break;
/* ********************************
* default
* ********************************
*/
default:
elog(WARN, "ProcessUtility: command #%d unsupported",
nodeTag(parsetree));
break;
}
/* ----------------
* tell fe/be or whatever that we're done.
* ----------------
*/
EndCommand(commandTag, dest);
}

View File

@@ -0,0 +1,18 @@
/*-------------------------------------------------------------------------
*
* utility.h--
* prototypes for utility.c.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: utility.h,v 1.1.1.1 1996/07/09 06:22:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef UTILITY_H
#define UTILITY_H
extern void ProcessUtility(Node *parsetree, CommandDest dest);
#endif /* UTILITY_H */