2002-07-11 09:39:28 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* pg_conversion.c
|
|
|
|
* routines to support manipulation of the pg_conversion relation
|
|
|
|
*
|
|
|
|
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2002-11-02 19:41:22 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_conversion.c,v 1.8 2002/11/02 18:41:21 tgl Exp $
|
2002-07-11 09:39:28 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "access/heapam.h"
|
|
|
|
#include "catalog/catname.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"
|
2002-07-25 12:07:13 +02:00
|
|
|
#include "catalog/pg_class.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "catalog/pg_conversion.h"
|
|
|
|
#include "catalog/namespace.h"
|
|
|
|
#include "utils/builtins.h"
|
2002-07-25 12:07:13 +02:00
|
|
|
#include "utils/lsyscache.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "utils/syscache.h"
|
2002-07-25 12:07:13 +02:00
|
|
|
#include "utils/catcache.h"
|
2002-07-11 09:39:28 +02:00
|
|
|
#include "mb/pg_wchar.h"
|
|
|
|
#include "utils/fmgroids.h"
|
|
|
|
#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,
|
|
|
|
int32 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))
|
2002-07-11 09:39:28 +02:00
|
|
|
elog(ERROR, "conversion name \"%s\" already exists", conname);
|
|
|
|
|
|
|
|
if (def)
|
|
|
|
{
|
2002-09-04 22:31:48 +02:00
|
|
|
/*
|
|
|
|
* make sure there is no existing default <for encoding><to
|
|
|
|
* encoding> pair in this name space
|
|
|
|
*/
|
2002-07-11 09:39:28 +02:00
|
|
|
if (FindDefaultConversion(connamespace,
|
|
|
|
conforencoding,
|
|
|
|
contoencoding))
|
|
|
|
elog(ERROR, "default conversion for %s to %s already exists",
|
2002-11-02 03:33:03 +01:00
|
|
|
pg_encoding_to_char(conforencoding),
|
|
|
|
pg_encoding_to_char(contoencoding));
|
2002-07-11 09:39:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* open pg_conversion */
|
|
|
|
rel = heap_openr(ConversionRelationName, RowExclusiveLock);
|
|
|
|
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);
|
|
|
|
values[Anum_pg_conversion_conowner - 1] = Int32GetDatum(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);
|
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
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
myself.classId = RelationGetRelid(rel);
|
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 */
|
2002-07-25 12:07:13 +02:00
|
|
|
referenced.classId = RelOid_pg_proc;
|
|
|
|
referenced.objectId = conproc;
|
|
|
|
referenced.objectSubId = 0;
|
|
|
|
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
|
|
|
|
|
|
|
|
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))
|
2002-11-02 03:33:03 +01:00
|
|
|
elog(ERROR, "Conversion %u search from syscache failed",
|
|
|
|
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())
|
2002-07-11 09:39:28 +02:00
|
|
|
elog(ERROR, "permission denied");
|
|
|
|
|
2002-11-02 03:33:03 +01:00
|
|
|
ReleaseSysCache(tuple);
|
2002-07-25 12:07:13 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the deletion
|
|
|
|
*/
|
|
|
|
object.classId = get_system_catalog_relid(ConversionRelationName);
|
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;
|
|
|
|
TupleDesc tupDesc;
|
|
|
|
HeapTuple tuple;
|
|
|
|
HeapScanDesc scan;
|
|
|
|
ScanKeyData scanKeyData;
|
|
|
|
|
|
|
|
ScanKeyEntryInitialize(&scanKeyData,
|
|
|
|
0,
|
|
|
|
ObjectIdAttributeNumber,
|
|
|
|
F_OIDEQ,
|
|
|
|
ObjectIdGetDatum(conversionOid));
|
2002-07-11 09:39:28 +02:00
|
|
|
|
2002-07-25 12:07:13 +02:00
|
|
|
/* open pg_conversion */
|
|
|
|
rel = heap_openr(ConversionRelationName, RowExclusiveLock);
|
|
|
|
tupDesc = rel->rd_att;
|
|
|
|
|
|
|
|
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
|
2002-08-06 07:40:47 +02:00
|
|
|
elog(ERROR, "conversion %u does not exist", 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;
|
|
|
|
unsigned char *str;
|
|
|
|
unsigned 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 */
|
|
|
|
parsed_name = textToQualifiedNameList(conv_name, "convert_using");
|
|
|
|
convoid = FindConversionByName(parsed_name);
|
|
|
|
if (!OidIsValid(convoid))
|
|
|
|
elog(ERROR, "conversion %s not found", NameListToString(parsed_name));
|
|
|
|
|
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))
|
|
|
|
elog(ERROR, "Conversion %u search from syscache failed", 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
|
|
|
/*
|
2002-11-02 19:41:22 +01:00
|
|
|
* build text result structure. we cannot use textin() here, since
|
2002-09-04 22:31:48 +02:00
|
|
|
* textin assumes that input string encoding is same as database
|
|
|
|
* encoding.
|
|
|
|
*/
|
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);
|
|
|
|
}
|