OSDN Git Service

Commit old versions into main branch again.
[pg-rex/syncrep.git] / src / backend / commands / typecmds.c
1 /*-------------------------------------------------------------------------
2  *
3  * typecmds.c
4  *        Routines for SQL commands that manipulate types (and domains).
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.3 2002/05/03 00:32:16 tgl Exp $
12  *
13  * DESCRIPTION
14  *        The "DefineFoo" routines take the parse tree and pick out the
15  *        appropriate arguments/flags, passing the results to the
16  *        corresponding "FooDefine" routines (in src/catalog) that do
17  *        the actual catalog-munging.  These routines also verify permission
18  *        of the user to execute the command.
19  *
20  * NOTES
21  *        These things must be defined and committed in the following order:
22  *              "create function":
23  *                              input/output, recv/send procedures
24  *              "create type":
25  *                              type
26  *              "create operator":
27  *                              operators
28  *
29  *
30  *-------------------------------------------------------------------------
31  */
32 #include "postgres.h"
33
34 #include "access/heapam.h"
35 #include "catalog/catname.h"
36 #include "catalog/heap.h"
37 #include "catalog/namespace.h"
38 #include "catalog/pg_type.h"
39 #include "commands/comment.h"
40 #include "commands/defrem.h"
41 #include "miscadmin.h"
42 #include "parser/parse.h"
43 #include "parser/parse_func.h"
44 #include "parser/parse_type.h"
45 #include "utils/acl.h"
46 #include "utils/builtins.h"
47 #include "utils/fmgroids.h"
48 #include "utils/lsyscache.h"
49 #include "utils/syscache.h"
50
51
52 static Oid findTypeIOFunction(List *procname, bool isOutput);
53
54
55 /*
56  * DefineType
57  *              Registers a new type.
58  */
59 void
60 DefineType(List *names, List *parameters)
61 {
62         char       *typeName;
63         Oid                     typeNamespace;
64         AclResult       aclresult;
65         int16           internalLength = -1;    /* int2 */
66         int16           externalLength = -1;    /* int2 */
67         Oid                     elemType = InvalidOid;
68         List       *inputName = NIL;
69         List       *outputName = NIL;
70         List       *sendName = NIL;
71         List       *receiveName = NIL;
72         char       *defaultValue = NULL;
73         bool            byValue = false;
74         char            delimiter = DEFAULT_TYPDELIM;
75         char            alignment = 'i';        /* default alignment */
76         char            storage = 'p';  /* default TOAST storage method */
77         Oid                     inputOid;
78         Oid                     outputOid;
79         Oid                     sendOid;
80         Oid                     receiveOid;
81         char       *shadow_type;
82         List       *pl;
83         Oid                     typoid;
84
85         /* Convert list of names to a name and namespace */
86         typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);
87
88         /* Check we have creation rights in target namespace */
89         aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
90         if (aclresult != ACLCHECK_OK)
91                 aclcheck_error(aclresult, get_namespace_name(typeNamespace));
92
93         /*
94          * Type names must be one character shorter than other names, allowing
95          * room to create the corresponding array type name with prepended
96          * "_".
97          */
98         if (strlen(typeName) > (NAMEDATALEN - 2))
99                 elog(ERROR, "DefineType: type names must be %d characters or less",
100                          NAMEDATALEN - 2);
101
102         foreach(pl, parameters)
103         {
104                 DefElem    *defel = (DefElem *) lfirst(pl);
105
106                 if (strcasecmp(defel->defname, "internallength") == 0)
107                         internalLength = defGetTypeLength(defel);
108                 else if (strcasecmp(defel->defname, "externallength") == 0)
109                         externalLength = defGetTypeLength(defel);
110                 else if (strcasecmp(defel->defname, "input") == 0)
111                         inputName = defGetQualifiedName(defel);
112                 else if (strcasecmp(defel->defname, "output") == 0)
113                         outputName = defGetQualifiedName(defel);
114                 else if (strcasecmp(defel->defname, "send") == 0)
115                         sendName = defGetQualifiedName(defel);
116                 else if (strcasecmp(defel->defname, "receive") == 0)
117                         receiveName = defGetQualifiedName(defel);
118                 else if (strcasecmp(defel->defname, "delimiter") == 0)
119                 {
120                         char       *p = defGetString(defel);
121
122                         delimiter = p[0];
123                 }
124                 else if (strcasecmp(defel->defname, "element") == 0)
125                         elemType = typenameTypeId(defGetTypeName(defel));
126                 else if (strcasecmp(defel->defname, "default") == 0)
127                         defaultValue = defGetString(defel);
128                 else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
129                         byValue = true;
130                 else if (strcasecmp(defel->defname, "alignment") == 0)
131                 {
132                         char       *a = defGetString(defel);
133
134                         /*
135                          * Note: if argument was an unquoted identifier, parser will
136                          * have applied translations to it, so be prepared to
137                          * recognize translated type names as well as the nominal
138                          * form.
139                          */
140                         if (strcasecmp(a, "double") == 0 ||
141                                 strcasecmp(a, "float8") == 0 ||
142                                 strcasecmp(a, "pg_catalog.float8") == 0)
143                                 alignment = 'd';
144                         else if (strcasecmp(a, "int4") == 0 ||
145                                          strcasecmp(a, "pg_catalog.int4") == 0)
146                                 alignment = 'i';
147                         else if (strcasecmp(a, "int2") == 0 ||
148                                          strcasecmp(a, "pg_catalog.int2") == 0)
149                                 alignment = 's';
150                         else if (strcasecmp(a, "char") == 0 ||
151                                          strcasecmp(a, "pg_catalog.bpchar") == 0)
152                                 alignment = 'c';
153                         else
154                                 elog(ERROR, "DefineType: \"%s\" alignment not recognized",
155                                          a);
156                 }
157                 else if (strcasecmp(defel->defname, "storage") == 0)
158                 {
159                         char       *a = defGetString(defel);
160
161                         if (strcasecmp(a, "plain") == 0)
162                                 storage = 'p';
163                         else if (strcasecmp(a, "external") == 0)
164                                 storage = 'e';
165                         else if (strcasecmp(a, "extended") == 0)
166                                 storage = 'x';
167                         else if (strcasecmp(a, "main") == 0)
168                                 storage = 'm';
169                         else
170                                 elog(ERROR, "DefineType: \"%s\" storage not recognized",
171                                          a);
172                 }
173                 else
174                 {
175                         elog(WARNING, "DefineType: attribute \"%s\" not recognized",
176                                  defel->defname);
177                 }
178         }
179
180         /*
181          * make sure we have our required definitions
182          */
183         if (inputName == NIL)
184                 elog(ERROR, "Define: \"input\" unspecified");
185         if (outputName == NIL)
186                 elog(ERROR, "Define: \"output\" unspecified");
187
188         /* Convert I/O proc names to OIDs */
189         inputOid = findTypeIOFunction(inputName, false);
190         outputOid = findTypeIOFunction(outputName, true);
191         if (sendName)
192                 sendOid = findTypeIOFunction(sendName, true);
193         else
194                 sendOid = outputOid;
195         if (receiveName)
196                 receiveOid = findTypeIOFunction(receiveName, false);
197         else
198                 receiveOid = inputOid;
199
200         /*
201          * now have TypeCreate do all the real work.
202          */
203         typoid =
204                 TypeCreate(typeName,            /* type name */
205                                    typeNamespace,       /* namespace */
206                                    InvalidOid,          /* preassigned type oid (not done here) */
207                                    InvalidOid,          /* relation oid (n/a here) */
208                                    internalLength,      /* internal size */
209                                    externalLength,      /* external size */
210                                    'b',                         /* type-type (base type) */
211                                    delimiter,           /* array element delimiter */
212                                    inputOid,            /* input procedure */
213                                    outputOid,           /* output procedure */
214                                    receiveOid,          /* receive procedure */
215                                    sendOid,                     /* send procedure */
216                                    elemType,            /* element type ID */
217                                    InvalidOid,          /* base type ID (only for domains) */
218                                    defaultValue,        /* default type value */
219                                    NULL,                        /* no binary form available */
220                                    byValue,                     /* passed by value */
221                                    alignment,           /* required alignment */
222                                    storage,                     /* TOAST strategy */
223                                    -1,                          /* typMod (Domains only) */
224                                    0,                           /* Array Dimensions of typbasetype */
225                                    false);                      /* Type NOT NULL */
226
227         /*
228          * When we create a base type (as opposed to a complex type) we need
229          * to have an array entry for it in pg_type as well.
230          */
231         shadow_type = makeArrayTypeName(typeName);
232
233         /* alignment must be 'i' or 'd' for arrays */
234         alignment = (alignment == 'd') ? 'd' : 'i';
235
236         TypeCreate(shadow_type,         /* type name */
237                            typeNamespace,       /* namespace */
238                            InvalidOid,          /* preassigned type oid (not done here) */
239                            InvalidOid,          /* relation oid (n/a here) */
240                            -1,                          /* internal size */
241                            -1,                          /* external size */
242                            'b',                         /* type-type (base type) */
243                            DEFAULT_TYPDELIM,    /* array element delimiter */
244                            F_ARRAY_IN,          /* input procedure */
245                            F_ARRAY_OUT,         /* output procedure */
246                            F_ARRAY_IN,          /* receive procedure */
247                            F_ARRAY_OUT,         /* send procedure */
248                            typoid,                      /* element type ID */
249                            InvalidOid,          /* base type ID */
250                            NULL,                        /* never a default type value */
251                            NULL,                        /* binary default isn't sent either */
252                            false,                       /* never passed by value */
253                            alignment,           /* see above */
254                            'x',                         /* ARRAY is always toastable */
255                            -1,                          /* typMod (Domains only) */
256                            0,                           /* Array dimensions of typbasetype */
257                            false);                      /* Type NOT NULL */
258
259         pfree(shadow_type);
260 }
261
262
263 /*
264  *      RemoveType
265  *              Removes a datatype.
266  *
267  * NOTE: since this tries to remove the associated array type too, it'll
268  * only work on scalar types.
269  */
270 void
271 RemoveType(List *names)
272 {
273         TypeName   *typename;
274         Relation        relation;
275         Oid                     typeoid;
276         HeapTuple       tup;
277
278         /* Make a TypeName so we can use standard type lookup machinery */
279         typename = makeNode(TypeName);
280         typename->names = names;
281         typename->typmod = -1;
282         typename->arrayBounds = NIL;
283
284         relation = heap_openr(TypeRelationName, RowExclusiveLock);
285
286         /* Use LookupTypeName here so that shell types can be removed. */
287         typeoid = LookupTypeName(typename);
288         if (!OidIsValid(typeoid))
289                 elog(ERROR, "Type \"%s\" does not exist",
290                          TypeNameToString(typename));
291
292         tup = SearchSysCache(TYPEOID,
293                                                  ObjectIdGetDatum(typeoid),
294                                                  0, 0, 0);
295         if (!HeapTupleIsValid(tup))
296                 elog(ERROR, "Type \"%s\" does not exist",
297                          TypeNameToString(typename));
298
299         /* Permission check: must own type or its namespace */
300         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
301                 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
302                                                                  GetUserId()))
303                 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
304
305         /* Delete any comments associated with this type */
306         DeleteComments(typeoid, RelationGetRelid(relation));
307
308         /* Remove the type tuple from pg_type */
309         simple_heap_delete(relation, &tup->t_self);
310
311         ReleaseSysCache(tup);
312
313         /* Now, delete the "array of" that type */
314         typename->arrayBounds = makeList1(makeInteger(1));
315
316         typeoid = LookupTypeName(typename);
317         if (!OidIsValid(typeoid))
318                 elog(ERROR, "Type \"%s\" does not exist",
319                          TypeNameToString(typename));
320
321         tup = SearchSysCache(TYPEOID,
322                                                  ObjectIdGetDatum(typeoid),
323                                                  0, 0, 0);
324         if (!HeapTupleIsValid(tup))
325                 elog(ERROR, "Type \"%s\" does not exist",
326                          TypeNameToString(typename));
327
328         DeleteComments(typeoid, RelationGetRelid(relation));
329
330         simple_heap_delete(relation, &tup->t_self);
331
332         ReleaseSysCache(tup);
333
334         heap_close(relation, RowExclusiveLock);
335 }
336
337
338 /*
339  * DefineDomain
340  *              Registers a new domain.
341  */
342 void
343 DefineDomain(CreateDomainStmt *stmt)
344 {
345         char       *domainName;
346         Oid                     domainNamespace;
347         AclResult       aclresult;
348         int16           internalLength;
349         int16           externalLength;
350         Oid                     inputProcedure;
351         Oid                     outputProcedure;
352         Oid                     receiveProcedure;
353         Oid                     sendProcedure;
354         bool            byValue;
355         char            delimiter;
356         char            alignment;
357         char            storage;
358         char            typtype;
359         Datum           datum;
360         bool            isnull;
361         char       *defaultValue = NULL;
362         char       *defaultValueBin = NULL;
363         bool            typNotNull = false;
364         Oid                     basetypelem;
365         int32           typNDims = length(stmt->typename->arrayBounds);
366         HeapTuple       typeTup;
367         List       *schema = stmt->constraints;
368         List       *listptr;
369
370         /* Convert list of names to a name and namespace */
371         domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
372                                                                                                                 &domainName);
373
374         /* Check we have creation rights in target namespace */
375         aclresult = pg_namespace_aclcheck(domainNamespace, GetUserId(),
376                                                                           ACL_CREATE);
377         if (aclresult != ACLCHECK_OK)
378                 aclcheck_error(aclresult, get_namespace_name(domainNamespace));
379
380         /*
381          * Domainnames, unlike typenames don't need to account for the '_'
382          * prefix.  So they can be one character longer.
383          */
384         if (strlen(domainName) > (NAMEDATALEN - 1))
385                 elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
386                          NAMEDATALEN - 1);
387
388         /*
389          * Look up the base type.
390          */
391         typeTup = typenameType(stmt->typename);
392
393         /*
394          * What we really don't want is domains of domains.  This could cause all sorts
395          * of neat issues if we allow that.
396          *
397          * With testing, we may determine complex types should be allowed
398          */
399         typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype;
400         if (typtype != 'b')
401                 elog(ERROR, "DefineDomain: %s is not a basetype",
402                          TypeNameToString(stmt->typename));
403
404         /* passed by value */
405         byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
406
407         /* Required Alignment */
408         alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
409
410         /* TOAST Strategy */
411         storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
412
413         /* Storage Length */
414         internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen;
415
416         /* External Length (unused) */
417         externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen;
418
419         /* Array element Delimiter */
420         delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim;
421
422         /* I/O Functions */
423         inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput;
424         outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
425         receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive;
426         sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend;
427
428         /* Inherited default value */
429         datum = SysCacheGetAttr(TYPEOID, typeTup,
430                                                         Anum_pg_type_typdefault, &isnull);
431         if (!isnull)
432                 defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
433
434         /* Inherited default binary value */
435         datum = SysCacheGetAttr(TYPEOID, typeTup,
436                                                         Anum_pg_type_typdefaultbin, &isnull);
437         if (!isnull)
438                 defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum));
439
440         /*
441          * Pull out the typelem name of the parent OID.
442          *
443          * This is what enables us to make a domain of an array
444          */
445         basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
446
447         /*
448          * Run through constraints manually to avoid the additional
449          * processing conducted by DefineRelation() and friends.
450          *
451          * Besides, we don't want any constraints to be cooked.  We'll
452          * do that when the table is created via MergeDomainAttributes().
453          */
454         foreach(listptr, schema)
455         {
456                 Constraint *colDef = lfirst(listptr);
457                 bool nullDefined = false;
458                 Node       *expr;
459                 ParseState *pstate;
460
461                 switch (colDef->contype)
462                 {
463                         /*
464                          * The inherited default value may be overridden by the user
465                          * with the DEFAULT <expr> statement.
466                          *
467                          * We have to search the entire constraint tree returned as we
468                          * don't want to cook or fiddle too much.
469                          */
470                         case CONSTR_DEFAULT:
471                                 /* Create a dummy ParseState for transformExpr */
472                                 pstate = make_parsestate(NULL);
473                                 /*
474                                  * Cook the colDef->raw_expr into an expression.
475                                  * Note: Name is strictly for error message
476                                  */
477                                 expr = cookDefault(pstate, colDef->raw_expr,
478                                                                    typeTup->t_data->t_oid,
479                                                                    stmt->typename->typmod,
480                                                                    domainName);
481                                 /*
482                                  * Expression must be stored as a nodeToString result,
483                                  * but we also require a valid textual representation
484                                  * (mainly to make life easier for pg_dump).
485                                  */
486                                 defaultValue = deparse_expression(expr,
487                                                                 deparse_context_for(domainName,
488                                                                                                         InvalidOid),
489                                                                                                    false);
490                                 defaultValueBin = nodeToString(expr);
491                                 break;
492
493                         /*
494                          * Find the NULL constraint.
495                          */
496                         case CONSTR_NOTNULL:
497                                 if (nullDefined) {
498                                         elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
499                                 } else {
500                                         typNotNull = true;
501                                         nullDefined = true;
502                                 }
503                                 break;
504
505                         case CONSTR_NULL:
506                                 if (nullDefined) {
507                                         elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint");
508                                 } else {
509                                         typNotNull = false;
510                                         nullDefined = true;
511                                 }
512                                 break;
513
514                         case CONSTR_UNIQUE:
515                                 elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported");
516                                 break;
517
518                         case CONSTR_PRIMARY:
519                                 elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported");
520                                 break;
521
522                         case CONSTR_CHECK:
523                                 elog(ERROR, "DefineDomain: CHECK Constraints not supported");
524                                 break;
525
526                         case CONSTR_ATTR_DEFERRABLE:
527                         case CONSTR_ATTR_NOT_DEFERRABLE:
528                         case CONSTR_ATTR_DEFERRED:
529                         case CONSTR_ATTR_IMMEDIATE:
530                                 elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported");
531                                 break;
532
533                         default:
534                                 elog(ERROR, "DefineDomain: unrecognized constraint node type");
535                                 break;
536                 }
537         }
538
539         /*
540          * Have TypeCreate do all the real work.
541          */
542         TypeCreate(domainName,                  /* type name */
543                            domainNamespace,             /* namespace */
544                            InvalidOid,                  /* preassigned type oid (not done here) */
545                            InvalidOid,                  /* relation oid (n/a here) */
546                            internalLength,              /* internal size */
547                            externalLength,              /* external size */
548                            'd',                                 /* type-type (domain type) */
549                            delimiter,                   /* array element delimiter */
550                            inputProcedure,              /* input procedure */
551                            outputProcedure,             /* output procedure */
552                            receiveProcedure,    /* receive procedure */
553                            sendProcedure,               /* send procedure */
554                            basetypelem,                 /* element type ID */
555                            typeTup->t_data->t_oid,      /* base type ID */
556                            defaultValue,                /* default type value (text) */
557                            defaultValueBin,             /* default type value (binary) */
558                            byValue,                             /* passed by value */
559                            alignment,                   /* required alignment */
560                            storage,                             /* TOAST strategy */
561                            stmt->typename->typmod, /* typeMod value */
562                            typNDims,                    /* Array dimensions for base type */
563                            typNotNull);                 /* Type NOT NULL */
564
565         /*
566          * Now we can clean up.
567          */
568         ReleaseSysCache(typeTup);
569 }
570
571
572 /*
573  *      RemoveDomain
574  *              Removes a domain.
575  */
576 void
577 RemoveDomain(List *names, int behavior)
578 {
579         TypeName   *typename;
580         Relation        relation;
581         Oid                     typeoid;
582         HeapTuple       tup;
583         char            typtype;
584
585         /* CASCADE unsupported */
586         if (behavior == CASCADE)
587                 elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
588
589         /* Make a TypeName so we can use standard type lookup machinery */
590         typename = makeNode(TypeName);
591         typename->names = names;
592         typename->typmod = -1;
593         typename->arrayBounds = NIL;
594
595         relation = heap_openr(TypeRelationName, RowExclusiveLock);
596
597         typeoid = typenameTypeId(typename);
598
599         tup = SearchSysCache(TYPEOID,
600                                                  ObjectIdGetDatum(typeoid),
601                                                  0, 0, 0);
602         if (!HeapTupleIsValid(tup))
603                 elog(ERROR, "RemoveDomain: type '%s' does not exist",
604                          TypeNameToString(typename));
605
606         /* Permission check: must own type or its namespace */
607         if (!pg_type_ownercheck(typeoid, GetUserId()) &&
608                 !pg_namespace_ownercheck(((Form_pg_type) GETSTRUCT(tup))->typnamespace,
609                                                                  GetUserId()))
610                 aclcheck_error(ACLCHECK_NOT_OWNER, TypeNameToString(typename));
611
612         /* Check that this is actually a domain */
613         typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
614
615         if (typtype != 'd')
616                 elog(ERROR, "%s is not a domain",
617                          TypeNameToString(typename));
618
619         /* Delete any comments associated with this type */
620         DeleteComments(typeoid, RelationGetRelid(relation));
621
622         /* Remove the type tuple from pg_type */
623         simple_heap_delete(relation, &tup->t_self);
624
625         ReleaseSysCache(tup);
626
627         /* At present, domains don't have associated array types */
628
629         heap_close(relation, RowExclusiveLock);
630 }
631
632
633 /*
634  * Find a suitable I/O function for a type.
635  */
636 static Oid
637 findTypeIOFunction(List *procname, bool isOutput)
638 {
639         Oid                     argList[FUNC_MAX_ARGS];
640         int                     nargs;
641         Oid                     procOid;
642
643         /*
644          * First look for a 1-argument func with all argtypes 0. This is
645          * valid for all kinds of procedure.
646          */
647         MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid));
648
649         procOid = LookupFuncName(procname, 1, argList);
650
651         if (!OidIsValid(procOid))
652         {
653                 /*
654                  * Alternatively, input procedures may take 3 args (data
655                  * value, element OID, atttypmod); the pg_proc argtype
656                  * signature is 0,OIDOID,INT4OID.  Output procedures may
657                  * take 2 args (data value, element OID).
658                  */
659                 if (isOutput)
660                 {
661                         /* output proc */
662                         nargs = 2;
663                         argList[1] = OIDOID;
664                 }
665                 else
666                 {
667                         /* input proc */
668                         nargs = 3;
669                         argList[1] = OIDOID;
670                         argList[2] = INT4OID;
671                 }
672                 procOid = LookupFuncName(procname, nargs, argList);
673
674                 if (!OidIsValid(procOid))
675                         func_error("TypeCreate", procname, 1, argList, NULL);
676         }
677
678         return procOid;
679 }