text: /*-------------------------------------------------------------------------
   *
   * pg_conversion.c
   *	  routines to support manipulation of the pg_conversion relation
   *
   * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
   * Portions Copyright (c) 1994, Regents of the University of California
   *
   *
   * IDENTIFICATION
   *	  $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.47 2009/01/01 17:23:37 momjian Exp $
   *
   *-------------------------------------------------------------------------
   */
  #include "postgres.h"
  
  #include "access/heapam.h"
  #include "access/sysattr.h"
  #include "catalog/dependency.h"
  #include "catalog/indexing.h"
  #include "catalog/pg_conversion.h"
  #include "catalog/pg_conversion_fn.h"
  #include "catalog/pg_namespace.h"
  #include "catalog/pg_proc.h"
  #include "mb/pg_wchar.h"
  #include "miscadmin.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
  #include "utils/fmgroids.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
  
  /*
   * ConversionCreate
   *
   * Add a new tuple to pg_conversion.
   */
  Oid
  ConversionCreate(const char *conname, Oid connamespace,
  				 Oid conowner,
  				 int32 conforencoding, int32 contoencoding,
  				 Oid conproc, bool def)
  {
  	int			i;
  	Relation	rel;
  	TupleDesc	tupDesc;
  	HeapTuple	tup;
  	bool		nulls[Natts_pg_conversion];
  	Datum		values[Natts_pg_conversion];
  	NameData	cname;
  	Oid			oid;
  	ObjectAddress myself,
  				referenced;
  
  	/* sanity checks */
  	if (!conname)
  		elog(ERROR, "no conversion name supplied");
  
  	/* make sure there is no existing conversion of same name */
  	if (SearchSysCacheExists(CONNAMENSP,
  							 PointerGetDatum(conname),
  							 ObjectIdGetDatum(connamespace),
  							 0, 0))
  		ereport(ERROR,
  				(errcode(ERRCODE_DUPLICATE_OBJECT),
  				 errmsg("conversion \"%s\" already exists", conname)));
  
  	if (def)
  	{
  		/*
  		 * make sure there is no existing default <for encoding><to encoding>
  		 * pair in this name space
  		 */
  		if (FindDefaultConversion(connamespace,
  								  conforencoding,
  								  contoencoding))
  			ereport(ERROR,
  					(errcode(ERRCODE_DUPLICATE_OBJECT),
  					 errmsg("default conversion for %s to %s already exists",
  							pg_encoding_to_char(conforencoding),
  							pg_encoding_to_char(contoencoding))));
  	}
  
  	/* open pg_conversion */
  	rel = heap_open(ConversionRelationId, RowExclusiveLock);
  	tupDesc = rel->rd_att;
  
  	/* initialize nulls and values */
  	for (i = 0; i < Natts_pg_conversion; i++)
  	{
  		nulls[i] = false;
  		values[i] = (Datum) NULL;
  	}
  
  	/* form a tuple */
  	namestrcpy(&cname, conname);
  	values[Anum_pg_conversion_conname - 1] = NameGetDatum(&cname);
  	values[Anum_pg_conversion_connamespace - 1] = ObjectIdGetDatum(connamespace);
  	values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(conowner);
  	values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding);
  	values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding);
  	values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc);
  	values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def);
  
  	tup = heap_form_tuple(tupDesc, values, nulls);
  
  	/* insert a new tuple */
  	oid = simple_heap_insert(rel, tup);
  	Assert(OidIsValid(oid));
  
  	/* update the index if any */
  	CatalogUpdateIndexes(rel, tup);
  
  	myself.classId = ConversionRelationId;
  	myself.objectId = HeapTupleGetOid(tup);
  	myself.objectSubId = 0;
  
  	/* create dependency on conversion procedure */
  	referenced.classId = ProcedureRelationId;
  	referenced.objectId = conproc;
  	referenced.objectSubId = 0;
  	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  
  	/* create dependency on namespace */
  	referenced.classId = NamespaceRelationId;
  	referenced.objectId = connamespace;
  	referenced.objectSubId = 0;
  	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  
  	/* create dependency on owner */
  	recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
  							conowner);
  
  	heap_freetuple(tup);
  	heap_close(rel, RowExclusiveLock);
  
  	return oid;
  }
  
  /*
   * RemoveConversionById
   *
   * Remove a tuple from pg_conversion by Oid. This function is solely
   * called inside catalog/dependency.c
   */
  void
  RemoveConversionById(Oid conversionOid)
  {
  	Relation	rel;
  	HeapTuple	tuple;
  	HeapScanDesc scan;
  	ScanKeyData scanKeyData;
  
  	ScanKeyInit(&scanKeyData,
  				ObjectIdAttributeNumber,
  				BTEqualStrategyNumber, F_OIDEQ,
  				ObjectIdGetDatum(conversionOid));
  
  	/* open pg_conversion */
  	rel = heap_open(ConversionRelationId, RowExclusiveLock);
  
  	scan = heap_beginscan(rel, SnapshotNow,
  						  1, &scanKeyData);
  
  	/* search for the target tuple */
  	if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
  		simple_heap_delete(rel, &tuple->t_self);
  	else
  		elog(ERROR, "could not find tuple for conversion %u", conversionOid);
  	heap_endscan(scan);
  	heap_close(rel, RowExclusiveLock);
  }
  
  /*
   * FindDefaultConversion
   *
   * Find "default" conversion proc by for_encoding and to_encoding in the
   * given namespace.
   *
   * If found, returns the procedure's oid, otherwise InvalidOid.  Note that
   * you get the procedure's OID not the conversion's OID!
   */
  Oid
  FindDefaultConversion(Oid name_space, int32 for_encoding, int32 to_encoding)
  {
  	CatCList   *catlist;
  	HeapTuple	tuple;
  	Form_pg_conversion body;
  	Oid			proc = InvalidOid;
  	int			i;
  
  	catlist = SearchSysCacheList(CONDEFAULT, 3,
  								 ObjectIdGetDatum(name_space),
  								 Int32GetDatum(for_encoding),
  								 Int32GetDatum(to_encoding),
  								 0);
  
  	for (i = 0; i < catlist->n_members; i++)
  	{
  		tuple = &catlist->members[i]->tuple;
  		body = (Form_pg_conversion) GETSTRUCT(tuple);
  		if (body->condefault)
  		{
  			proc = body->conproc;
  			break;
  		}
  	}
  	ReleaseSysCacheList(catlist);
  	return proc;
  }
  
  /*
   * FindConversion
   *
   * Find conversion by namespace and conversion name.
   * Returns conversion OID.
   */
  Oid
  FindConversion(const char *conname, Oid connamespace)
  {
  	HeapTuple	tuple;
  	Oid			procoid;
  	Oid			conoid;
  	AclResult	aclresult;
  
  	/* search pg_conversion by connamespace and conversion name */
  	tuple = SearchSysCache(CONNAMENSP,
  						   PointerGetDatum(conname),
  						   ObjectIdGetDatum(connamespace),
  						   0, 0);
  	if (!HeapTupleIsValid(tuple))
  		return InvalidOid;
  
  	procoid = ((Form_pg_conversion) GETSTRUCT(tuple))->conproc;
  	conoid = HeapTupleGetOid(tuple);
  
  	ReleaseSysCache(tuple);
  
  	/* Check we have execute rights for the function */
  	aclresult = pg_proc_aclcheck(procoid, GetUserId(), ACL_EXECUTE);
  	if (aclresult != ACLCHECK_OK)
  		return InvalidOid;
  
  	return conoid;
  }
  