2002-07-11 09:39:28 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_conversion.c
|
|
|
|
* routines to support manipulation of the pg_conversion relation
|
|
|
|
*
|
2006-03-05 16:59:11 +01:00
|
|
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
2002-07-11 09:39:28 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2006-08-31 19:31:33 +02:00
|
|
|
* $PostgreSQL: pgsql/src/backend/catalog/pg_conversion.c,v 1.33 2006/08/31 17:31:33 tgl Exp $
|
2002-07-11 09:39:28 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/heapam.h"
|
2002-07-25 12:07:13 +02:00
|
|
|
#include "catalog/dependency.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "catalog/indexing.h"
|
2006-07-11 19:26:59 +02:00
|
|
|
#include "catalog/namespace.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "catalog/pg_conversion.h"
|
2006-05-30 15:36:30 +02:00
|
|
|
#include "catalog/pg_namespace.h"
|
2005-04-14 03:38:22 +02:00
|
|
|
#include "catalog/pg_proc.h"
|
2006-07-11 19:26:59 +02:00
|
|
|
#include "mb/pg_wchar.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/fmgroids.h"
|
2006-07-11 19:26:59 +02:00
|
|
|
#include "utils/syscache.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "utils/acl.h"
|
|
|
|
#include "miscadmin.h"
|
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
/*
|
2002-07-11 09:39:28 +02:00
|
|
|
* ConversionCreate
|
2002-07-25 12:07:13 +02:00
|
|
|
*
|
2002-11-02 03:33:03 +01:00
|
|
|
* Add a new tuple to pg_conversion.
|
2002-07-11 09:39:28 +02:00
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid
|
|
|
|
ConversionCreate(const char *conname, Oid connamespace,
|
2005-06-28 07:09:14 +02:00
|
|
|
Oid conowner,
|
2002-11-02 03:33:03 +01:00
|
|
|
int32 conforencoding, int32 contoencoding,
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid conproc, bool def)
|
2002-07-11 09:39:28 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
int i;
|
2002-07-11 09:39:28 +02:00
|
|
|
Relation rel;
|
|
|
|
TupleDesc tupDesc;
|
|
|
|
HeapTuple tup;
|
|
|
|
char nulls[Natts_pg_conversion];
|
|
|
|
Datum values[Natts_pg_conversion];
|
|
|
|
NameData cname;
|
|
|
|
Oid oid;
|
2002-09-04 22:31:48 +02:00
|
|
|
ObjectAddress myself,
|
|
|
|
referenced;
|
2002-07-11 09:39:28 +02:00
|
|
|
|
|
|
|
/* sanity checks */
|
|
|
|
if (!conname)
|
|
|
|
elog(ERROR, "no conversion name supplied");
|
|
|
|
|
|
|
|
/* make sure there is no existing conversion of same name */
|
2002-11-02 03:33:03 +01:00
|
|
|
if (SearchSysCacheExists(CONNAMENSP,
|
2002-09-04 22:31:48 +02:00
|
|
|
PointerGetDatum(conname),
|
|
|
|
ObjectIdGetDatum(connamespace),
|
|
|
|
0, 0))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
|
|
errmsg("conversion \"%s\" already exists", conname)));
|
2002-07-11 09:39:28 +02:00
|
|
|
|
|
|
|
if (def)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* make sure there is no existing default <for encoding><to encoding>
|
|
|
|
* pair in this name space
|
2002-09-04 22:31:48 +02:00
|
|
|
*/
|
2002-07-11 09:39:28 +02:00
|
|
|
if (FindDefaultConversion(connamespace,
|
|
|
|
conforencoding,
|
|
|
|
contoencoding))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
2005-10-15 04:49:52 +02:00
|
|
|
errmsg("default conversion for %s to %s already exists",
|
|
|
|
pg_encoding_to_char(conforencoding),
|
|
|
|
pg_encoding_to_char(contoencoding))));
|
2002-07-11 09:39:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* open pg_conversion */
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(ConversionRelationId, RowExclusiveLock);
|
2002-07-11 09:39:28 +02:00
|
|
|
tupDesc = rel->rd_att;
|
|
|
|
|
|
|
|
/* initialize nulls and values */
|
|
|
|
for (i = 0; i < Natts_pg_conversion; i++)
|
|
|
|
{
|
|
|
|
nulls[i] = ' ';
|
|
|
|
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);
|
2005-06-28 07:09:14 +02:00
|
|
|
values[Anum_pg_conversion_conowner - 1] = ObjectIdGetDatum(conowner);
|
2002-07-11 09:39:28 +02:00
|
|
|
values[Anum_pg_conversion_conforencoding - 1] = Int32GetDatum(conforencoding);
|
|
|
|
values[Anum_pg_conversion_contoencoding - 1] = Int32GetDatum(contoencoding);
|
|
|
|
values[Anum_pg_conversion_conproc - 1] = ObjectIdGetDatum(conproc);
|
2002-11-02 03:33:03 +01:00
|
|
|
values[Anum_pg_conversion_condefault - 1] = BoolGetDatum(def);
|
2002-07-11 09:39:28 +02:00
|
|
|
|
|
|
|
tup = heap_formtuple(tupDesc, values, nulls);
|
|
|
|
|
|
|
|
/* insert a new tuple */
|
|
|
|
oid = simple_heap_insert(rel, tup);
|
|
|
|
Assert(OidIsValid(oid));
|
|
|
|
|
|
|
|
/* update the index if any */
|
2002-08-05 05:29:17 +02:00
|
|
|
CatalogUpdateIndexes(rel, tup);
|
2002-07-11 09:39:28 +02:00
|
|
|
|
2005-04-14 22:03:27 +02:00
|
|
|
myself.classId = ConversionRelationId;
|
2002-07-25 12:07:13 +02:00
|
|
|
myself.objectId = HeapTupleGetOid(tup);
|
|
|
|
myself.objectSubId = 0;
|
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
/* create dependency on conversion procedure */
|
2005-04-14 03:38:22 +02:00
|
|
|
referenced.classId = ProcedureRelationId;
|
2002-07-25 12:07:13 +02:00
|
|
|
referenced.objectId = conproc;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
2006-05-30 15:36:30 +02:00
|
|
|
/* create dependency on namespace */
|
|
|
|
referenced.classId = NamespaceRelationId;
|
|
|
|
referenced.objectId = connamespace;
|
2006-08-31 19:31:33 +02:00
|
|
|
referenced.objectSubId = 0;
|
2006-05-30 15:36:30 +02:00
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
2006-08-31 19:31:33 +02:00
|
|
|
/* create dependency on owner */
|
|
|
|
recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup),
|
|
|
|
conowner);
|
|
|
|
|
2002-07-25 12:07:13 +02:00
|
|
|
heap_freetuple(tup);
|
2002-07-11 09:39:28 +02:00
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
|
|
|
|
return oid;
|
|
|
|
}
|
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
/*
|
2002-07-11 09:39:28 +02:00
|
|
|
* ConversionDrop
|
2002-07-25 12:07:13 +02:00
|
|
|
*
|
2002-11-02 03:33:03 +01:00
|
|
|
* Drop a conversion after doing permission checks.
|
2002-07-11 09:39:28 +02:00
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
void
|
2002-11-02 03:33:03 +01:00
|
|
|
ConversionDrop(Oid conversionOid, DropBehavior behavior)
|
2002-07-11 09:39:28 +02:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2002-09-04 22:31:48 +02:00
|
|
|
ObjectAddress object;
|
2002-07-11 09:39:28 +02:00
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
tuple = SearchSysCache(CONOID,
|
|
|
|
ObjectIdGetDatum(conversionOid),
|
|
|
|
0, 0, 0);
|
2002-07-11 09:39:28 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "cache lookup failed for conversion %u", conversionOid);
|
2002-07-11 09:39:28 +02:00
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
if (!superuser() &&
|
|
|
|
((Form_pg_conversion) GETSTRUCT(tuple))->conowner != GetUserId())
|
2003-08-01 02:15:26 +02:00
|
|
|
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION,
|
2005-10-15 04:49:52 +02:00
|
|
|
NameStr(((Form_pg_conversion) GETSTRUCT(tuple))->conname));
|
2002-07-11 09:39:28 +02:00
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2002-07-25 12:07:13 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the deletion
|
|
|
|
*/
|
2005-04-14 22:03:27 +02:00
|
|
|
object.classId = ConversionRelationId;
|
2002-11-02 03:33:03 +01:00
|
|
|
object.objectId = conversionOid;
|
2002-07-25 12:07:13 +02:00
|
|
|
object.objectSubId = 0;
|
|
|
|
|
|
|
|
performDeletion(&object, behavior);
|
|
|
|
}
|
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
/*
|
2002-07-25 12:07:13 +02:00
|
|
|
* RemoveConversionById
|
|
|
|
*
|
2002-11-02 03:33:03 +01:00
|
|
|
* Remove a tuple from pg_conversion by Oid. This function is solely
|
2002-07-25 12:07:13 +02:00
|
|
|
* called inside catalog/dependency.c
|
2002-11-02 03:33:03 +01:00
|
|
|
*/
|
2002-07-25 12:07:13 +02:00
|
|
|
void
|
|
|
|
RemoveConversionById(Oid conversionOid)
|
|
|
|
{
|
|
|
|
Relation rel;
|
|
|
|
HeapTuple tuple;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
ScanKeyData scanKeyData;
|
|
|
|
|
2003-11-12 22:15:59 +01:00
|
|
|
ScanKeyInit(&scanKeyData,
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
BTEqualStrategyNumber, F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(conversionOid));
|
2002-07-11 09:39:28 +02:00
|
|
|
|
2002-07-25 12:07:13 +02:00
|
|
|
/* open pg_conversion */
|
2005-04-14 22:03:27 +02:00
|
|
|
rel = heap_open(ConversionRelationId, RowExclusiveLock);
|
2002-07-25 12:07:13 +02:00
|
|
|
|
|
|
|
scan = heap_beginscan(rel, SnapshotNow,
|
2002-09-04 22:31:48 +02:00
|
|
|
1, &scanKeyData);
|
2002-07-25 12:07:13 +02:00
|
|
|
|
|
|
|
/* search for the target tuple */
|
|
|
|
if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
|
|
|
|
simple_heap_delete(rel, &tuple->t_self);
|
|
|
|
else
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "could not find tuple for conversion %u", conversionOid);
|
2002-07-11 09:39:28 +02:00
|
|
|
heap_endscan(scan);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
}
|
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
/*
|
2002-07-11 09:39:28 +02:00
|
|
|
* FindDefaultConversion
|
|
|
|
*
|
2002-11-02 03:33:03 +01:00
|
|
|
* 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!
|
2002-07-11 09:39:28 +02:00
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid
|
2002-11-02 03:33:03 +01:00
|
|
|
FindDefaultConversion(Oid name_space, int32 for_encoding, int32 to_encoding)
|
2002-07-25 12:07:13 +02:00
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
CatCList *catlist;
|
2002-07-25 12:07:13 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_conversion body;
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid proc = InvalidOid;
|
|
|
|
int i;
|
2002-07-25 12:07:13 +02:00
|
|
|
|
|
|
|
catlist = SearchSysCacheList(CONDEFAULT, 3,
|
2002-09-04 22:31:48 +02:00
|
|
|
ObjectIdGetDatum(name_space),
|
|
|
|
Int32GetDatum(for_encoding),
|
|
|
|
Int32GetDatum(to_encoding),
|
|
|
|
0);
|
2002-07-25 12:07:13 +02:00
|
|
|
|
|
|
|
for (i = 0; i < catlist->n_members; i++)
|
|
|
|
{
|
|
|
|
tuple = &catlist->members[i]->tuple;
|
2002-09-04 22:31:48 +02:00
|
|
|
body = (Form_pg_conversion) GETSTRUCT(tuple);
|
2002-11-02 03:33:03 +01:00
|
|
|
if (body->condefault)
|
2002-07-25 12:07:13 +02:00
|
|
|
{
|
|
|
|
proc = body->conproc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ReleaseSysCacheList(catlist);
|
|
|
|
return proc;
|
|
|
|
}
|
2002-07-11 09:39:28 +02:00
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
/*
|
|
|
|
* FindConversion
|
2002-07-11 09:39:28 +02:00
|
|
|
*
|
2002-08-06 07:40:47 +02:00
|
|
|
* Find conversion by namespace and conversion name.
|
2002-11-02 03:33:03 +01:00
|
|
|
* Returns conversion OID.
|
2002-07-11 09:39:28 +02:00
|
|
|
*/
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid
|
|
|
|
FindConversion(const char *conname, Oid connamespace)
|
2002-07-11 09:39:28 +02:00
|
|
|
{
|
|
|
|
HeapTuple tuple;
|
2002-09-04 22:31:48 +02:00
|
|
|
Oid procoid;
|
|
|
|
Oid conoid;
|
2002-07-11 09:39:28 +02:00
|
|
|
AclResult aclresult;
|
|
|
|
|
2002-08-06 07:40:47 +02:00
|
|
|
/* search pg_conversion by connamespace and conversion name */
|
2002-11-02 03:33:03 +01:00
|
|
|
tuple = SearchSysCache(CONNAMENSP,
|
2002-08-06 07:40:47 +02:00
|
|
|
PointerGetDatum(conname),
|
|
|
|
ObjectIdGetDatum(connamespace),
|
2002-09-04 22:31:48 +02:00
|
|
|
0, 0);
|
2002-07-11 09:39:28 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
|
|
|
return InvalidOid;
|
2002-11-02 03:33:03 +01:00
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
procoid = ((Form_pg_conversion) GETSTRUCT(tuple))->conproc;
|
2002-08-06 07:40:47 +02:00
|
|
|
conoid = HeapTupleGetOid(tuple);
|
2002-07-11 09:39:28 +02:00
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
|
|
|
/* Check we have execute rights for the function */
|
|
|
|
aclresult = pg_proc_aclcheck(procoid, GetUserId(), ACL_EXECUTE);
|
|
|
|
if (aclresult != ACLCHECK_OK)
|
|
|
|
return InvalidOid;
|
|
|
|
|
2002-08-06 07:40:47 +02:00
|
|
|
return conoid;
|
2002-07-11 09:39:28 +02:00
|
|
|
}
|
|
|
|
|
2002-08-06 07:40:47 +02:00
|
|
|
/*
|
|
|
|
* Execute SQL99's CONVERT function.
|
|
|
|
*
|
|
|
|
* CONVERT <left paren> <character value expression>
|
|
|
|
* USING <form-of-use conversion name> <right paren>
|
|
|
|
*
|
2002-11-02 19:41:22 +01:00
|
|
|
* TEXT convert_using(TEXT string, TEXT conversion_name)
|
2002-08-06 07:40:47 +02:00
|
|
|
*/
|
|
|
|
Datum
|
2002-11-02 19:41:22 +01:00
|
|
|
pg_convert_using(PG_FUNCTION_ARGS)
|
2002-08-06 07:40:47 +02:00
|
|
|
{
|
|
|
|
text *string = PG_GETARG_TEXT_P(0);
|
2002-11-02 19:41:22 +01:00
|
|
|
text *conv_name = PG_GETARG_TEXT_P(1);
|
|
|
|
text *retval;
|
|
|
|
List *parsed_name;
|
|
|
|
Oid convoid;
|
2002-08-06 07:40:47 +02:00
|
|
|
HeapTuple tuple;
|
|
|
|
Form_pg_conversion body;
|
2005-09-24 19:53:28 +02:00
|
|
|
char *str;
|
|
|
|
char *result;
|
2002-09-04 22:31:48 +02:00
|
|
|
int len;
|
2002-08-06 07:40:47 +02:00
|
|
|
|
2002-11-02 19:41:22 +01:00
|
|
|
/* Convert input string to null-terminated form */
|
2002-08-06 07:40:47 +02:00
|
|
|
len = VARSIZE(string) - VARHDRSZ;
|
|
|
|
str = palloc(len + 1);
|
|
|
|
memcpy(str, VARDATA(string), len);
|
|
|
|
*(str + len) = '\0';
|
|
|
|
|
2002-11-02 19:41:22 +01:00
|
|
|
/* Look up the conversion name */
|
2005-05-27 02:57:49 +02:00
|
|
|
parsed_name = textToQualifiedNameList(conv_name);
|
2002-11-02 19:41:22 +01:00
|
|
|
convoid = FindConversionByName(parsed_name);
|
|
|
|
if (!OidIsValid(convoid))
|
2003-07-21 03:59:11 +02:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
|
|
errmsg("conversion \"%s\" does not exist",
|
|
|
|
NameListToString(parsed_name))));
|
2002-11-02 19:41:22 +01:00
|
|
|
|
2002-08-06 07:40:47 +02:00
|
|
|
tuple = SearchSysCache(CONOID,
|
|
|
|
ObjectIdGetDatum(convoid),
|
2002-09-04 22:31:48 +02:00
|
|
|
0, 0, 0);
|
2002-08-06 07:40:47 +02:00
|
|
|
if (!HeapTupleIsValid(tuple))
|
2003-07-21 03:59:11 +02:00
|
|
|
elog(ERROR, "cache lookup failed for conversion %u", convoid);
|
2002-11-02 19:41:22 +01:00
|
|
|
body = (Form_pg_conversion) GETSTRUCT(tuple);
|
2002-08-06 07:40:47 +02:00
|
|
|
|
2002-11-02 19:41:22 +01:00
|
|
|
/* Temporary result area should be more than big enough */
|
2002-08-06 07:40:47 +02:00
|
|
|
result = palloc(len * 4 + 1);
|
|
|
|
|
|
|
|
OidFunctionCall5(body->conproc,
|
|
|
|
Int32GetDatum(body->conforencoding),
|
|
|
|
Int32GetDatum(body->contoencoding),
|
|
|
|
CStringGetDatum(str),
|
|
|
|
CStringGetDatum(result),
|
|
|
|
Int32GetDatum(len));
|
|
|
|
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
|
2002-09-04 22:31:48 +02:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* build text result structure. we cannot use textin() here, since textin
|
|
|
|
* assumes that input string encoding is same as database encoding.
|
2002-09-04 22:31:48 +02:00
|
|
|
*/
|
2002-08-06 07:40:47 +02:00
|
|
|
len = strlen(result) + VARHDRSZ;
|
|
|
|
retval = palloc(len);
|
|
|
|
VARATT_SIZEP(retval) = len;
|
|
|
|
memcpy(VARDATA(retval), result, len - VARHDRSZ);
|
|
|
|
|
|
|
|
pfree(result);
|
|
|
|
pfree(str);
|
|
|
|
|
|
|
|
PG_RETURN_TEXT_P(retval);
|
|
|
|
}
|