Tom Lane <[EMAIL PROTECTED]> writes:
> That would be okay with me.  It might be a good idea to change the
> name completely (perhaps CopyTupleDesc() ?) as a means of catching
> places that aren't correctly updated.

Done, and done -- a revised patch is attached.

-Neil

Index: src/backend/access/common/tupdesc.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/access/common/tupdesc.c,v
retrieving revision 1.100
diff -c -r1.100 tupdesc.c
*** src/backend/access/common/tupdesc.c	25 Sep 2003 06:57:56 -0000	1.100
--- src/backend/access/common/tupdesc.c	20 Nov 2003 21:43:47 -0000
***************
*** 96,204 ****
  }
  
  /* ----------------------------------------------------------------
!  *		CreateTupleDescCopy
   *
!  *		This function creates a new TupleDesc by copying from an existing
!  *		TupleDesc
!  *
!  *		!!! Constraints are not copied !!!
   * ----------------------------------------------------------------
   */
  TupleDesc
! CreateTupleDescCopy(TupleDesc tupdesc)
  {
! 	TupleDesc	desc;
! 	int			i,
! 				size;
  
! 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
! 	desc->natts = tupdesc->natts;
! 	if (desc->natts > 0)
! 	{
! 		size = desc->natts * sizeof(Form_pg_attribute);
! 		desc->attrs = (Form_pg_attribute *) palloc(size);
! 		for (i = 0; i < desc->natts; i++)
! 		{
! 			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
! 			memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
! 			desc->attrs[i]->attnotnull = false;
! 			desc->attrs[i]->atthasdef = false;
! 		}
! 	}
! 	else
! 		desc->attrs = NULL;
! 	desc->constr = NULL;
! 	desc->tdhasoid = tupdesc->tdhasoid;
  
! 	return desc;
! }
! 
! /* ----------------------------------------------------------------
!  *		CreateTupleDescCopyConstr
!  *
!  *		This function creates a new TupleDesc by copying from an existing
!  *		TupleDesc (with Constraints)
!  * ----------------------------------------------------------------
!  */
! TupleDesc
! CreateTupleDescCopyConstr(TupleDesc tupdesc)
! {
! 	TupleDesc	desc;
! 	TupleConstr *constr = tupdesc->constr;
! 	int			i,
! 				size;
  
! 	desc = (TupleDesc) palloc(sizeof(struct tupleDesc));
! 	desc->natts = tupdesc->natts;
! 	if (desc->natts > 0)
! 	{
! 		size = desc->natts * sizeof(Form_pg_attribute);
! 		desc->attrs = (Form_pg_attribute *) palloc(size);
! 		for (i = 0; i < desc->natts; i++)
  		{
! 			desc->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
! 			memcpy(desc->attrs[i], tupdesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
  		}
  	}
  	else
! 		desc->attrs = NULL;
! 	if (constr)
  	{
! 		TupleConstr *cpy = (TupleConstr *) palloc(sizeof(TupleConstr));
  
! 		cpy->has_not_null = constr->has_not_null;
  
! 		if ((cpy->num_defval = constr->num_defval) > 0)
  		{
! 			cpy->defval = (AttrDefault *) palloc(cpy->num_defval * sizeof(AttrDefault));
! 			memcpy(cpy->defval, constr->defval, cpy->num_defval * sizeof(AttrDefault));
! 			for (i = cpy->num_defval - 1; i >= 0; i--)
  			{
  				if (constr->defval[i].adbin)
! 					cpy->defval[i].adbin = pstrdup(constr->defval[i].adbin);
  			}
  		}
  
! 		if ((cpy->num_check = constr->num_check) > 0)
  		{
! 			cpy->check = (ConstrCheck *) palloc(cpy->num_check * sizeof(ConstrCheck));
! 			memcpy(cpy->check, constr->check, cpy->num_check * sizeof(ConstrCheck));
! 			for (i = cpy->num_check - 1; i >= 0; i--)
  			{
  				if (constr->check[i].ccname)
! 					cpy->check[i].ccname = pstrdup(constr->check[i].ccname);
  				if (constr->check[i].ccbin)
! 					cpy->check[i].ccbin = pstrdup(constr->check[i].ccbin);
  			}
  		}
  
! 		desc->constr = cpy;
  	}
- 	else
- 		desc->constr = NULL;
  
! 	desc->tdhasoid = tupdesc->tdhasoid;
! 	return desc;
  }
  
  void
--- 96,183 ----
  }
  
  /* ----------------------------------------------------------------
!  *		CopyTupleDesc
   *
!  *		This function creates a new TupleDesc by copying from an
!  *		existing TupleDesc. Iff 'copyConstr' is true, the constraints
!  *		on the input TupleDesc are also copied.
   * ----------------------------------------------------------------
   */
  TupleDesc
! CopyTupleDesc(TupleDesc tupDesc, bool copyConstr)
  {
! 	TupleDesc	result;
! 	int			i;
  
! 	result = (TupleDesc) palloc(sizeof(*result));
! 	result->natts = tupDesc->natts;
! 	result->tdhasoid = tupDesc->tdhasoid;
! 	result->constr = NULL;
  
! 	if (result->natts > 0)
! 	{
! 		int size;
  
! 		size = result->natts * sizeof(Form_pg_attribute);
! 		result->attrs = (Form_pg_attribute *) palloc(size);
! 		for (i = 0; i < result->natts; i++)
  		{
! 			result->attrs[i] = (Form_pg_attribute) palloc(ATTRIBUTE_TUPLE_SIZE);
! 			memcpy(result->attrs[i], tupDesc->attrs[i], ATTRIBUTE_TUPLE_SIZE);
! 
! 			/*
! 			 * If we're not copying constraints, fix up the attributes
! 			 * to remove NOT NULL or DEFAULT indicators.
! 			 */
! 			if (!copyConstr)
! 			{
! 				result->attrs[i]->attnotnull = false;
! 				result->attrs[i]->atthasdef  = false;
! 			}
  		}
  	}
  	else
! 		result->attrs = NULL;
! 
! 	if (copyConstr && tupDesc->constr)
  	{
! 		TupleConstr *constr;
  
! 		constr = (TupleConstr *) palloc(sizeof(*constr));
! 		memcpy(constr, tupDesc->constr, sizeof(*constr));
! 		constr->defval = NULL;
! 		constr->check = NULL;
  
! 		if (constr->num_defval > 0)
  		{
! 			constr->defval = (AttrDefault *) palloc(constr->num_defval * sizeof(AttrDefault));
! 			memcpy(constr->defval, tupDesc->constr->defval,
! 				   constr->num_defval * sizeof(AttrDefault));
! 			for (i = constr->num_defval - 1; i >= 0; i--)
  			{
  				if (constr->defval[i].adbin)
! 					constr->defval[i].adbin = pstrdup(constr->defval[i].adbin);
  			}
  		}
  
! 		if (constr->num_check > 0)
  		{
! 			constr->check = (ConstrCheck *) palloc(constr->num_check * sizeof(ConstrCheck));
! 			memcpy(constr->check, tupDesc->constr->check,
! 				   constr->num_check * sizeof(ConstrCheck));
! 			for (i = constr->num_check - 1; i >= 0; i--)
  			{
  				if (constr->check[i].ccname)
! 					constr->check[i].ccname = pstrdup(constr->check[i].ccname);
  				if (constr->check[i].ccbin)
! 					constr->check[i].ccbin = pstrdup(constr->check[i].ccbin);
  			}
  		}
  
! 		result->constr = constr;
  	}
  
! 	return result;
  }
  
  void
***************
*** 607,613 ****
  	relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
  	relvar = makeRangeVarFromNameList(relname_list);
  	rel = relation_openrv(relvar, AccessShareLock);
! 	tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
  	relation_close(rel, AccessShareLock);
  
  	return tupdesc;
--- 586,592 ----
  	relname_list = stringToQualifiedNameList(relname, "RelationNameGetTupleDesc");
  	relvar = makeRangeVarFromNameList(relname_list);
  	rel = relation_openrv(relvar, AccessShareLock);
! 	tupdesc = CopyTupleDesc(RelationGetDescr(rel), false);
  	relation_close(rel, AccessShareLock);
  
  	return tupdesc;
***************
*** 644,650 ****
  			elog(ERROR, "invalid typrelid for complex type %u", typeoid);
  
  		rel = relation_open(relid, AccessShareLock);
! 		tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
  		natts = tupdesc->natts;
  		relation_close(rel, AccessShareLock);
  		/* XXX should we hold the lock to ensure table doesn't change? */
--- 623,629 ----
  			elog(ERROR, "invalid typrelid for complex type %u", typeoid);
  
  		rel = relation_open(relid, AccessShareLock);
! 		tupdesc = CopyTupleDesc(RelationGetDescr(rel), false);
  		natts = tupdesc->natts;
  		relation_close(rel, AccessShareLock);
  		/* XXX should we hold the lock to ensure table doesn't change? */
Index: src/backend/commands/cluster.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/commands/cluster.c,v
retrieving revision 1.118
diff -c -r1.118 cluster.c
*** src/backend/commands/cluster.c	12 Nov 2003 21:15:49 -0000	1.118
--- src/backend/commands/cluster.c	20 Nov 2003 21:19:36 -0000
***************
*** 494,500 ****
  	 * Need to make a copy of the tuple descriptor, since
  	 * heap_create_with_catalog modifies it.
  	 */
! 	tupdesc = CreateTupleDescCopyConstr(OldHeapDesc);
  
  	OIDNewHeap = heap_create_with_catalog(NewName,
  										  RelationGetNamespace(OldHeap),
--- 494,500 ----
  	 * Need to make a copy of the tuple descriptor, since
  	 * heap_create_with_catalog modifies it.
  	 */
! 	tupdesc = CopyTupleDesc(OldHeapDesc, true);
  
  	OIDNewHeap = heap_create_with_catalog(NewName,
  										  RelationGetNamespace(OldHeap),
Index: src/backend/commands/portalcmds.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/commands/portalcmds.c,v
retrieving revision 1.24
diff -c -r1.24 portalcmds.c
*** src/backend/commands/portalcmds.c	24 Aug 2003 21:02:43 -0000	1.24
--- src/backend/commands/portalcmds.c	20 Nov 2003 21:27:24 -0000
***************
*** 295,301 ****
  	 */
  	oldcxt = MemoryContextSwitchTo(portal->holdContext);
  
! 	portal->tupDesc = CreateTupleDescCopy(portal->tupDesc);
  
  	MemoryContextSwitchTo(oldcxt);
  
--- 295,301 ----
  	 */
  	oldcxt = MemoryContextSwitchTo(portal->holdContext);
  
! 	portal->tupDesc = CopyTupleDesc(portal->tupDesc, false);
  
  	MemoryContextSwitchTo(oldcxt);
  
Index: src/backend/executor/execMain.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/execMain.c,v
retrieving revision 1.221
diff -c -r1.221 execMain.c
*** src/backend/executor/execMain.c	6 Nov 2003 22:08:14 -0000	1.221
--- src/backend/executor/execMain.c	20 Nov 2003 21:29:16 -0000
***************
*** 804,810 ****
  		/*
  		 * have to copy tupType to get rid of constraints
  		 */
! 		tupdesc = CreateTupleDescCopy(tupType);
  
  		intoRelationId = heap_create_with_catalog(intoName,
  												  namespaceId,
--- 804,810 ----
  		/*
  		 * have to copy tupType to get rid of constraints
  		 */
! 		tupdesc = CopyTupleDesc(tupType, false);
  
  		intoRelationId = heap_create_with_catalog(intoName,
  												  namespaceId,
Index: src/backend/executor/execQual.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/execQual.c,v
retrieving revision 1.150
diff -c -r1.150 execQual.c
*** src/backend/executor/execQual.c	13 Oct 2003 22:47:15 -0000	1.150
--- src/backend/executor/execQual.c	20 Nov 2003 21:29:58 -0000
***************
*** 1074,1080 ****
  						ereport(ERROR,
  								(errcode(ERRCODE_DATATYPE_MISMATCH),
  								 errmsg("function returning row did not return a valid tuple slot")));
! 					tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
  					returnsTuple = true;
  				}
  				else
--- 1074,1080 ----
  						ereport(ERROR,
  								(errcode(ERRCODE_DATATYPE_MISMATCH),
  								 errmsg("function returning row did not return a valid tuple slot")));
! 					tupdesc = CopyTupleDesc(slot->ttc_tupleDescriptor, false);
  					returnsTuple = true;
  				}
  				else
Index: src/backend/executor/functions.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/functions.c,v
retrieving revision 1.75
diff -c -r1.75 functions.c
*** src/backend/executor/functions.c	25 Sep 2003 18:58:35 -0000	1.75
--- src/backend/executor/functions.c	20 Nov 2003 21:30:17 -0000
***************
*** 389,395 ****
  	 */
  	if (funcSlot->ttc_tupleDescriptor == NULL)
  	{
! 		resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor);
  		ExecSetSlotDescriptor(funcSlot, resultTd, true);
  		ExecSetSlotDescriptorIsNew(funcSlot, true);
  	}
--- 389,395 ----
  	 */
  	if (funcSlot->ttc_tupleDescriptor == NULL)
  	{
! 		resultTd = CopyTupleDesc(resultSlot->ttc_tupleDescriptor, false);
  		ExecSetSlotDescriptor(funcSlot, resultTd, true);
  		ExecSetSlotDescriptorIsNew(funcSlot, true);
  	}
Index: src/backend/executor/nodeFunctionscan.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/nodeFunctionscan.c,v
retrieving revision 1.22
diff -c -r1.22 nodeFunctionscan.c
*** src/backend/executor/nodeFunctionscan.c	25 Sep 2003 23:02:12 -0000	1.22
--- src/backend/executor/nodeFunctionscan.c	20 Nov 2003 21:30:35 -0000
***************
*** 205,211 ****
  			elog(ERROR, "invalid typrelid for complex type %u",
  				 funcrettype);
  		rel = relation_open(funcrelid, AccessShareLock);
! 		tupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
  		relation_close(rel, AccessShareLock);
  	}
  	else if (functyptype == 'b' || functyptype == 'd')
--- 205,211 ----
  			elog(ERROR, "invalid typrelid for complex type %u",
  				 funcrettype);
  		rel = relation_open(funcrelid, AccessShareLock);
! 		tupdesc = CopyTupleDesc(RelationGetDescr(rel), false);
  		relation_close(rel, AccessShareLock);
  	}
  	else if (functyptype == 'b' || functyptype == 'd')
Index: src/backend/executor/spi.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/executor/spi.c,v
retrieving revision 1.107
diff -c -r1.107 spi.c
*** src/backend/executor/spi.c	1 Oct 2003 21:30:52 -0000	1.107
--- src/backend/executor/spi.c	20 Nov 2003 21:31:30 -0000
***************
*** 386,392 ****
  		oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
  	}
  
! 	ctupdesc = CreateTupleDescCopy(tupdesc);
  
  	if (oldcxt)
  		MemoryContextSwitchTo(oldcxt);
--- 386,392 ----
  		oldcxt = MemoryContextSwitchTo(_SPI_current->savedcxt);
  	}
  
! 	ctupdesc = CopyTupleDesc(tupdesc, false);
  
  	if (oldcxt)
  		MemoryContextSwitchTo(oldcxt);
***************
*** 416,422 ****
  	}
  
  	ctuple = heap_copytuple(tuple);
! 	ctupdesc = CreateTupleDescCopy(tupdesc);
  
  	cslot = MakeTupleTableSlot();
  	ExecSetSlotDescriptor(cslot, ctupdesc, true);
--- 416,422 ----
  	}
  
  	ctuple = heap_copytuple(tuple);
! 	ctupdesc = CopyTupleDesc(tupdesc, false);
  
  	cslot = MakeTupleTableSlot();
  	ExecSetSlotDescriptor(cslot, ctupdesc, true);
***************
*** 950,956 ****
  	tuptable->tuptabcxt = tuptabcxt;
  	tuptable->alloced = tuptable->free = 128;
  	tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
! 	tuptable->tupdesc = CreateTupleDescCopy(typeinfo);
  
  	MemoryContextSwitchTo(oldcxt);
  }
--- 950,956 ----
  	tuptable->tuptabcxt = tuptabcxt;
  	tuptable->alloced = tuptable->free = 128;
  	tuptable->vals = (HeapTuple *) palloc(tuptable->alloced * sizeof(HeapTuple));
! 	tuptable->tupdesc = CopyTupleDesc(typeinfo, false);
  
  	MemoryContextSwitchTo(oldcxt);
  }
Index: src/backend/tcop/utility.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/tcop/utility.c,v
retrieving revision 1.208
diff -c -r1.208 utility.c
*** src/backend/tcop/utility.c	2 Oct 2003 06:34:04 -0000	1.208
--- src/backend/tcop/utility.c	20 Nov 2003 21:31:51 -0000
***************
*** 1119,1125 ****
  				portal = GetPortalByName(stmt->portalname);
  				if (!PortalIsValid(portal))
  					return NULL;	/* not our business to raise error */
! 				return CreateTupleDescCopy(portal->tupDesc);
  			}
  
  		case T_ExecuteStmt:
--- 1119,1125 ----
  				portal = GetPortalByName(stmt->portalname);
  				if (!PortalIsValid(portal))
  					return NULL;	/* not our business to raise error */
! 				return CopyTupleDesc(portal->tupDesc, false);
  			}
  
  		case T_ExecuteStmt:
Index: src/backend/utils/cache/catcache.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/utils/cache/catcache.c,v
retrieving revision 1.110
diff -c -r1.110 catcache.c
*** src/backend/utils/cache/catcache.c	12 Nov 2003 21:15:55 -0000	1.110
--- src/backend/utils/cache/catcache.c	20 Nov 2003 21:37:56 -0000
***************
*** 913,919 ****
  	/*
  	 * copy the relcache's tuple descriptor to permanent cache storage
  	 */
! 	tupdesc = CreateTupleDescCopyConstr(RelationGetDescr(relation));
  
  	/*
  	 * get the relation's OID and relisshared flag, too
--- 913,919 ----
  	/*
  	 * copy the relcache's tuple descriptor to permanent cache storage
  	 */
! 	tupdesc = CopyTupleDesc(RelationGetDescr(relation), true);
  
  	/*
  	 * get the relation's OID and relisshared flag, too
Index: src/backend/utils/cache/relcache.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/backend/utils/cache/relcache.c,v
retrieving revision 1.192
diff -c -r1.192 relcache.c
*** src/backend/utils/cache/relcache.c	12 Nov 2003 21:15:56 -0000	1.192
--- src/backend/utils/cache/relcache.c	20 Nov 2003 21:32:12 -0000
***************
*** 2132,2138 ****
  	 * multiple system catalogs.  We can copy attnotnull constraints here,
  	 * however.
  	 */
! 	rel->rd_att = CreateTupleDescCopy(tupDesc);
  	has_not_null = false;
  	for (i = 0; i < natts; i++)
  	{
--- 2132,2138 ----
  	 * multiple system catalogs.  We can copy attnotnull constraints here,
  	 * however.
  	 */
! 	rel->rd_att = CopyTupleDesc(tupDesc, false);
  	has_not_null = false;
  	for (i = 0; i < natts; i++)
  	{
Index: src/include/access/tupdesc.h
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/include/access/tupdesc.h,v
retrieving revision 1.41
diff -c -r1.41 tupdesc.h
*** src/include/access/tupdesc.h	11 Aug 2003 23:04:50 -0000	1.41
--- src/include/access/tupdesc.h	20 Nov 2003 21:18:21 -0000
***************
*** 60,68 ****
  extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
  				Form_pg_attribute *attrs);
  
! extern TupleDesc CreateTupleDescCopy(TupleDesc tupdesc);
! 
! extern TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc);
  
  extern void FreeTupleDesc(TupleDesc tupdesc);
  
--- 60,66 ----
  extern TupleDesc CreateTupleDesc(int natts, bool hasoid,
  				Form_pg_attribute *attrs);
  
! extern TupleDesc CopyTupleDesc(TupleDesc tupdesc, bool copyConstr);
  
  extern void FreeTupleDesc(TupleDesc tupdesc);
  
Index: src/pl/plpgsql/src/pl_comp.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/pl_comp.c,v
retrieving revision 1.69
diff -c -r1.69 pl_comp.c
*** src/pl/plpgsql/src/pl_comp.c	30 Sep 2003 00:59:51 -0000	1.69
--- src/pl/plpgsql/src/pl_comp.c	20 Nov 2003 21:33:44 -0000
***************
*** 1494,1500 ****
  	 * memory context ...
  	 */
  	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
! 	row->rowtupdesc = CreateTupleDescCopy(RelationGetDescr(rel));
  	MemoryContextSwitchTo(oldcxt);
  
  	row->nfields = classStruct->relnatts;
--- 1494,1500 ----
  	 * memory context ...
  	 */
  	oldcxt = MemoryContextSwitchTo(TopMemoryContext);
! 	row->rowtupdesc = CopyTupleDesc(RelationGetDescr(rel), false);
  	MemoryContextSwitchTo(oldcxt);
  
  	row->nfields = classStruct->relnatts;
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.93
diff -c -r1.93 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c	1 Oct 2003 21:47:42 -0000	1.93
--- src/pl/plpgsql/src/pl_exec.c	20 Nov 2003 21:34:54 -0000
***************
*** 352,358 ****
  				MemoryContext oldcxt;
  
  				oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt);
! 				rsi->setDesc = CreateTupleDescCopy(estate.rettupdesc);
  				MemoryContextSwitchTo(oldcxt);
  			}
  		}
--- 352,358 ----
  				MemoryContext oldcxt;
  
  				oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt);
! 				rsi->setDesc = CopyTupleDesc(estate.rettupdesc, false);
  				MemoryContextSwitchTo(oldcxt);
  			}
  		}
***************
*** 3390,3396 ****
  
  		if (tupdesc)
  		{
! 			rec->tupdesc = CreateTupleDescCopy(tupdesc);
  			rec->freetupdesc = true;
  		}
  		else
--- 3390,3396 ----
  
  		if (tupdesc)
  		{
! 			rec->tupdesc = CopyTupleDesc(tupdesc, false);
  			rec->freetupdesc = true;
  		}
  		else
---------------------------(end of broadcast)---------------------------
TIP 5: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faqs/FAQ.html

Reply via email to