From 5f4745adf4fb2a1f933b25d7a2bc72b39fa9edfd Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 30 Mar 2002 01:02:42 +0000 Subject: [PATCH] Further cleanups for relations in schemas: teach nextval and other sequence functions how to cope with qualified names. Same code is also used for int4notin, currtid_byrelname, pgstattuple. Also, move TOAST tables into special pg_toast namespace. --- contrib/pgstattuple/README.pgstattuple | 2 +- contrib/pgstattuple/pgstattuple.c | 15 +- contrib/pgstattuple/pgstattuple.sql.in | 4 +- src/backend/catalog/namespace.c | 21 ++- src/backend/commands/command.c | 12 +- src/backend/commands/sequence.c | 170 +++++++-------------- src/backend/parser/parse_type.c | 11 +- src/backend/utils/adt/not_in.c | 36 +++-- src/backend/utils/adt/tid.c | 34 ++--- src/backend/utils/adt/varlena.c | 118 +++++++++++++- src/include/catalog/namespace.h | 4 +- src/include/catalog/pg_namespace.h | 8 +- src/include/utils/builtins.h | 3 +- src/test/regress/output/constraints.source | 1 - 14 files changed, 261 insertions(+), 178 deletions(-) diff --git a/contrib/pgstattuple/README.pgstattuple b/contrib/pgstattuple/README.pgstattuple index 6a191e9cce..7237b9a4dc 100644 --- a/contrib/pgstattuple/README.pgstattuple +++ b/contrib/pgstattuple/README.pgstattuple @@ -33,7 +33,7 @@ NOTICE: physical length: 0.08MB live tuples: 20 (0.00MB, 1.17%) dead tuples: 32 pgstattuple can be called as a function: - pgstattuple(NAME) RETURNS FLOAT8 + pgstattuple(TEXT) RETURNS FLOAT8 The argument is the table name. pgstattuple returns the percentage of the "dead" tuples of a table. diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c index beda282172..39bd5d1f95 100644 --- a/contrib/pgstattuple/pgstattuple.c +++ b/contrib/pgstattuple/pgstattuple.c @@ -1,5 +1,5 @@ /* - * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.4 2002/03/06 06:09:10 momjian Exp $ + * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.5 2002/03/30 01:02:41 tgl Exp $ * * Copyright (c) 2001 Tatsuo Ishii * @@ -27,6 +27,9 @@ #include "fmgr.h" #include "access/heapam.h" #include "access/transam.h" +#include "catalog/namespace.h" +#include "utils/builtins.h" + PG_FUNCTION_INFO_V1(pgstattuple); @@ -43,8 +46,8 @@ extern Datum pgstattuple(PG_FUNCTION_ARGS); Datum pgstattuple(PG_FUNCTION_ARGS) { - Name p = PG_GETARG_NAME(0); - + text *relname = PG_GETARG_TEXT_P(0); + RangeVar *relrv; Relation rel; HeapScanDesc scan; HeapTuple tuple; @@ -59,11 +62,13 @@ pgstattuple(PG_FUNCTION_ARGS) uint64 dead_tuple_count = 0; double tuple_percent; double dead_tuple_percent; - uint64 free_space = 0; /* free/reusable space in bytes */ double free_percent; /* free/reusable space in % */ - rel = heap_openr(NameStr(*p), AccessShareLock); + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname, + "pgstattuple")); + rel = heap_openrv(relrv, AccessShareLock); + nblocks = RelationGetNumberOfBlocks(rel); scan = heap_beginscan(rel, false, SnapshotAny, 0, NULL); diff --git a/contrib/pgstattuple/pgstattuple.sql.in b/contrib/pgstattuple/pgstattuple.sql.in index 7a7706fa19..b49a13483f 100644 --- a/contrib/pgstattuple/pgstattuple.sql.in +++ b/contrib/pgstattuple/pgstattuple.sql.in @@ -1,4 +1,4 @@ -DROP FUNCTION pgstattuple(NAME); -CREATE FUNCTION pgstattuple(NAME) RETURNS FLOAT8 +DROP FUNCTION pgstattuple(text); +CREATE FUNCTION pgstattuple(text) RETURNS float8 AS 'MODULE_PATHNAME', 'pgstattuple' LANGUAGE 'c' WITH (isstrict); diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index a1ea6ed24f..e245ae5a3e 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.2 2002/03/29 19:06:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/namespace.c,v 1.3 2002/03/30 01:02:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -81,6 +81,10 @@ RangeVarGetRelid(const RangeVar *relation, bool failOK) * RangeVarGetCreationNamespace * Given a RangeVar describing a to-be-created relation, * choose which namespace to create it in. + * + * Note: calling this may result in a CommandCounterIncrement operation. + * That will happen on the first request for a temp table in any particular + * backend run; we will need to either create or clean out the temp schema. */ Oid RangeVarGetCreationNamespace(const RangeVar *newRelation) @@ -126,6 +130,21 @@ RelnameGetRelid(const char *relname) return get_relname_relid(relname, PG_CATALOG_NAMESPACE); } +/* + * TypenameGetTypid + * Try to resolve an unqualified datatype name. + * Returns OID if type found in search path, else InvalidOid. + */ +Oid +TypenameGetTypid(const char *typname) +{ + /* XXX wrong, should use namespace search */ + return GetSysCacheOid(TYPENAMENSP, + PointerGetDatum(typname), + ObjectIdGetDatum(PG_CATALOG_NAMESPACE), + 0, 0); +} + /* * QualifiedNameGetCreationNamespace * Given a possibly-qualified name for an object (in List-of-Values diff --git a/src/backend/commands/command.c b/src/backend/commands/command.c index 9180c539ff..2c029fa2ae 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/command.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.167 2002/03/29 19:06:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.168 2002/03/30 01:02:41 tgl Exp $ * * NOTES * The PerformAddAttribute() code, like most of the relation @@ -1755,13 +1755,13 @@ AlterTableCreateToastTable(Oid relOid, bool silent) tupdesc->attrs[2]->attstorage = 'p'; /* - * Note: the toast relation is considered a "normal" relation even if - * its master relation is a temp table. There cannot be any naming - * collision, and the toast rel will be destroyed when its master is, - * so there's no need to handle the toast rel as temp. + * Note: the toast relation is placed in the regular pg_toast namespace + * even if its master relation is a temp table. There cannot be any + * naming collision, and the toast rel will be destroyed when its master + * is, so there's no need to handle the toast rel as temp. */ toast_relid = heap_create_with_catalog(toast_relname, - RelationGetNamespace(rel), + PG_TOAST_NAMESPACE, tupdesc, RELKIND_TOASTVALUE, false, false, true); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 54ca1d19a1..a7678c5ce3 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,15 +8,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.75 2002/03/29 19:06:07 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.76 2002/03/30 01:02:41 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include - #include "access/heapam.h" +#include "catalog/namespace.h" #include "catalog/pg_type.h" #include "commands/creatinh.h" #include "commands/sequence.h" @@ -24,9 +23,6 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/int8.h" -#ifdef MULTIBYTE -#include "mb/pg_wchar.h" -#endif #define SEQ_MAGIC 0x1717 @@ -57,7 +53,6 @@ typedef struct sequence_magic typedef struct SeqTableData { - char *name; Oid relid; Relation rel; /* NULL if rel is not open in cur xact */ int64 cached; @@ -70,12 +65,11 @@ typedef SeqTableData *SeqTable; static SeqTable seqtab = NULL; -static char *get_seq_name(text *seqin); -static SeqTable init_sequence(char *caller, char *name); +static SeqTable init_sequence(char *caller, RangeVar *relation); static Form_pg_sequence read_info(char *caller, SeqTable elm, Buffer *buf); static void init_params(CreateSeqStmt *seq, Form_pg_sequence new); static int64 get_param(DefElem *def); -static void do_setval(char *seqname, int64 next, bool iscalled); +static void do_setval(RangeVar *sequence, int64 next, bool iscalled); /* * DefineSequence @@ -285,7 +279,7 @@ Datum nextval(PG_FUNCTION_ARGS) { text *seqin = PG_GETARG_TEXT_P(0); - char *seqname = get_seq_name(seqin); + RangeVar *sequence; SeqTable elm; Buffer buf; Page page; @@ -302,14 +296,15 @@ nextval(PG_FUNCTION_ARGS) rescnt = 0; bool logit = false; + sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, + "nextval")); + /* open and AccessShareLock sequence */ - elm = init_sequence("nextval", seqname); + elm = init_sequence("nextval", sequence); if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK) elog(ERROR, "%s.nextval: you don't have permissions to set sequence %s", - seqname, seqname); - - pfree(seqname); + sequence->relname, sequence->relname); if (elm->last != elm->cached) /* some numbers were cached */ { @@ -379,7 +374,7 @@ nextval(PG_FUNCTION_ARGS) break; /* stop fetching */ if (!seq->is_cycled) elog(ERROR, "%s.nextval: reached MAXVALUE (" INT64_FORMAT ")", - elm->name, maxv); + sequence->relname, maxv); next = minv; } else @@ -395,7 +390,7 @@ nextval(PG_FUNCTION_ARGS) break; /* stop fetching */ if (!seq->is_cycled) elog(ERROR, "%s.nextval: reached MINVALUE (" INT64_FORMAT ")", - elm->name, minv); + sequence->relname, minv); next = maxv; } else @@ -456,7 +451,7 @@ nextval(PG_FUNCTION_ARGS) LockBuffer(buf, BUFFER_LOCK_UNLOCK); if (WriteBuffer(buf) == STATUS_ERROR) - elog(ERROR, "%s.nextval: WriteBuffer failed", elm->name); + elog(ERROR, "%s.nextval: WriteBuffer failed", sequence->relname); PG_RETURN_INT64(result); } @@ -465,25 +460,26 @@ Datum currval(PG_FUNCTION_ARGS) { text *seqin = PG_GETARG_TEXT_P(0); - char *seqname = get_seq_name(seqin); + RangeVar *sequence; SeqTable elm; int64 result; + sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, + "currval")); + /* open and AccessShareLock sequence */ - elm = init_sequence("currval", seqname); + elm = init_sequence("currval", sequence); if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_SELECT) != ACLCHECK_OK) elog(ERROR, "%s.currval: you don't have permissions to read sequence %s", - seqname, seqname); + sequence->relname, sequence->relname); if (elm->increment == 0) /* nextval/read_info were not called */ elog(ERROR, "%s.currval is not yet defined in this session", - seqname); + sequence->relname); result = elm->last; - pfree(seqname); - PG_RETURN_INT64(result); } @@ -501,25 +497,25 @@ currval(PG_FUNCTION_ARGS) * sequence. */ static void -do_setval(char *seqname, int64 next, bool iscalled) +do_setval(RangeVar *sequence, int64 next, bool iscalled) { SeqTable elm; Buffer buf; Form_pg_sequence seq; /* open and AccessShareLock sequence */ - elm = init_sequence("setval", seqname); + elm = init_sequence("setval", sequence); if (pg_class_aclcheck(elm->relid, GetUserId(), ACL_UPDATE) != ACLCHECK_OK) elog(ERROR, "%s.setval: you don't have permissions to set sequence %s", - seqname, seqname); + sequence->relname, sequence->relname); /* lock page' buffer and read tuple */ seq = read_info("setval", elm, &buf); if ((next < seq->min_value) || (next > seq->max_value)) elog(ERROR, "%s.setval: value " INT64_FORMAT " is out of bounds (" INT64_FORMAT "," INT64_FORMAT ")", - seqname, next, seq->min_value, seq->max_value); + sequence->relname, next, seq->min_value, seq->max_value); /* save info in local cache */ elm->last = next; /* last returned number */ @@ -562,9 +558,7 @@ do_setval(char *seqname, int64 next, bool iscalled) LockBuffer(buf, BUFFER_LOCK_UNLOCK); if (WriteBuffer(buf) == STATUS_ERROR) - elog(ERROR, "%s.setval: WriteBuffer failed", seqname); - - pfree(seqname); + elog(ERROR, "%s.setval: WriteBuffer failed", sequence->relname); } /* @@ -576,9 +570,12 @@ setval(PG_FUNCTION_ARGS) { text *seqin = PG_GETARG_TEXT_P(0); int64 next = PG_GETARG_INT64(1); - char *seqname = get_seq_name(seqin); + RangeVar *sequence; - do_setval(seqname, next, true); + sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, + "setval")); + + do_setval(sequence, next, true); PG_RETURN_INT64(next); } @@ -593,67 +590,16 @@ setval_and_iscalled(PG_FUNCTION_ARGS) text *seqin = PG_GETARG_TEXT_P(0); int64 next = PG_GETARG_INT64(1); bool iscalled = PG_GETARG_BOOL(2); - char *seqname = get_seq_name(seqin); + RangeVar *sequence; - do_setval(seqname, next, iscalled); + sequence = makeRangeVarFromNameList(textToQualifiedNameList(seqin, + "setval")); + + do_setval(sequence, next, iscalled); PG_RETURN_INT64(next); } -/* - * Given a 'text' parameter to a sequence function, extract the actual - * sequence name. We downcase the name if it's not double-quoted, - * and truncate it if it's too long. - * - * This is a kluge, really --- should be able to write nextval(seqrel). - */ -static char * -get_seq_name(text *seqin) -{ - char *rawname = DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(seqin))); - int rawlen = strlen(rawname); - char *seqname; - - if (rawlen >= 2 && - rawname[0] == '\"' && rawname[rawlen - 1] == '\"') - { - /* strip off quotes, keep case */ - rawname[rawlen - 1] = '\0'; - seqname = pstrdup(rawname + 1); - pfree(rawname); - } - else - { - seqname = rawname; - - /* - * It's important that this match the identifier downcasing code - * used by backend/parser/scan.l. - */ - for (; *rawname; rawname++) - { - if (isupper((unsigned char) *rawname)) - *rawname = tolower((unsigned char) *rawname); - } - } - - /* Truncate name if it's overlength; again, should match scan.l */ - if (strlen(seqname) >= NAMEDATALEN) - { -#ifdef MULTIBYTE - int len; - - len = pg_mbcliplen(seqname, rawlen, NAMEDATALEN - 1); - seqname[len] = '\0'; -#else - seqname[NAMEDATALEN - 1] = '\0'; -#endif - } - - return seqname; -} - static Form_pg_sequence read_info(char *caller, SeqTable elm, Buffer *buf) { @@ -665,11 +611,12 @@ read_info(char *caller, SeqTable elm, Buffer *buf) if (elm->rel->rd_nblocks > 1) elog(ERROR, "%s.%s: invalid number of blocks in sequence", - elm->name, caller); + RelationGetRelationName(elm->rel), caller); *buf = ReadBuffer(elm->rel, 0); if (!BufferIsValid(*buf)) - elog(ERROR, "%s.%s: ReadBuffer failed", elm->name, caller); + elog(ERROR, "%s.%s: ReadBuffer failed", + RelationGetRelationName(elm->rel), caller); LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE); @@ -677,7 +624,8 @@ read_info(char *caller, SeqTable elm, Buffer *buf) sm = (sequence_magic *) PageGetSpecialPointer(page); if (sm->magic != SEQ_MAGIC) - elog(ERROR, "%s.%s: bad magic (%08X)", elm->name, caller, sm->magic); + elog(ERROR, "%s.%s: bad magic (%08X)", + RelationGetRelationName(elm->rel), caller, sm->magic); lp = PageGetItemId(page, FirstOffsetNumber); Assert(ItemIdIsUsed(lp)); @@ -692,16 +640,17 @@ read_info(char *caller, SeqTable elm, Buffer *buf) static SeqTable -init_sequence(char *caller, char *name) +init_sequence(char *caller, RangeVar *relation) { + Oid relid = RangeVarGetRelid(relation, false); SeqTable elm, prev = (SeqTable) NULL; Relation seqrel; - - /* Look to see if we already have a seqtable entry for name */ + + /* Look to see if we already have a seqtable entry for relation */ for (elm = seqtab; elm != (SeqTable) NULL; elm = elm->next) { - if (strcmp(elm->name, name) == 0) + if (elm->relid == relid) break; prev = elm; } @@ -711,24 +660,22 @@ init_sequence(char *caller, char *name) return elm; /* Else open and check it */ - seqrel = heap_openr(name, AccessShareLock); + seqrel = heap_open(relid, AccessShareLock); if (seqrel->rd_rel->relkind != RELKIND_SEQUENCE) - elog(ERROR, "%s.%s: %s is not a sequence", name, caller, name); + elog(ERROR, "%s.%s: %s is not a sequence", + relation->relname, caller, relation->relname); + /* + * If elm exists but elm->rel is NULL, the seqtable entry is left over + * from a previous xact -- update the entry and reuse it. + * + * NOTE: seqtable entries remain in the list for the life of a backend. + * If the sequence itself is deleted then the entry becomes wasted memory, + * but it's small enough that this should not matter. + */ if (elm != (SeqTable) NULL) { - /* - * We are using a seqtable entry left over from a previous xact; - * must check for relid change. - */ elm->rel = seqrel; - if (RelationGetRelid(seqrel) != elm->relid) - { - elog(WARNING, "%s.%s: sequence was re-created", - name, caller); - elm->relid = RelationGetRelid(seqrel); - elm->cached = elm->last = elm->increment = 0; - } } else { @@ -739,11 +686,8 @@ init_sequence(char *caller, char *name) elm = (SeqTable) malloc(sizeof(SeqTableData)); if (elm == NULL) elog(ERROR, "Memory exhausted in init_sequence"); - elm->name = strdup(name); - if (elm->name == NULL) - elog(ERROR, "Memory exhausted in init_sequence"); elm->rel = seqrel; - elm->relid = RelationGetRelid(seqrel); + elm->relid = relid; elm->cached = elm->last = elm->increment = 0; elm->next = (SeqTable) NULL; diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 22db859ed2..a6ba966195 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,14 +8,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.38 2002/03/29 19:06:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.39 2002/03/30 01:02:41 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "catalog/namespace.h" -#include "catalog/pg_namespace.h" #include "catalog/pg_type.h" #include "lib/stringinfo.h" #include "miscadmin.h" @@ -132,6 +131,7 @@ LookupTypeName(const TypeName *typename) if (schemaname) { + /* Look in specific schema only */ Oid namespaceId; namespaceId = GetSysCacheOid(NAMESPACENAME, @@ -147,11 +147,8 @@ LookupTypeName(const TypeName *typename) } else { - /* XXX wrong, should use namespace search */ - restype = GetSysCacheOid(TYPENAMENSP, - PointerGetDatum(typname), - ObjectIdGetDatum(PG_CATALOG_NAMESPACE), - 0, 0); + /* Unqualified type name, so search the search path */ + restype = TypenameGetTypid(typname); } } diff --git a/src/backend/utils/adt/not_in.c b/src/backend/utils/adt/not_in.c index 8c3ca1b73b..970b1fc9c2 100644 --- a/src/backend/utils/adt/not_in.c +++ b/src/backend/utils/adt/not_in.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.27 2001/10/25 05:49:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/not_in.c,v 1.28 2002/03/30 01:02:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,7 +25,9 @@ */ #include "postgres.h" + #include "access/heapam.h" +#include "catalog/namespace.h" #include "utils/builtins.h" static int my_varattno(Relation rd, char *a); @@ -39,43 +41,39 @@ int4notin(PG_FUNCTION_ARGS) { int32 not_in_arg = PG_GETARG_INT32(0); text *relation_and_attr = PG_GETARG_TEXT_P(1); + List *names; + int nnames; + RangeVar *relrv; + char *attribute; Relation relation_to_scan; int32 integer_value; HeapTuple current_tuple; HeapScanDesc scan_descriptor; bool isNull, retval; - int attrid, - strlength; - char *relation, - *attribute; - char my_copy[NAMEDATALEN * 2 + 2]; + int attrid; Datum value; - /* make a null-terminated copy of text */ - strlength = VARSIZE(relation_and_attr) - VARHDRSZ; - if (strlength >= sizeof(my_copy)) - strlength = sizeof(my_copy) - 1; - memcpy(my_copy, VARDATA(relation_and_attr), strlength); - my_copy[strlength] = '\0'; + /* Parse the argument */ - relation = (char *) strtok(my_copy, "."); - attribute = (char *) strtok(NULL, "."); - if (attribute == NULL) + names = textToQualifiedNameList(relation_and_attr, "int4notin"); + nnames = length(names); + if (nnames < 2) elog(ERROR, "int4notin: must provide relationname.attributename"); + attribute = strVal(nth(nnames-1, names)); + names = ltruncate(nnames-1, names); + relrv = makeRangeVarFromNameList(names); /* Open the relation and get a relation descriptor */ - relation_to_scan = heap_openr(relation, AccessShareLock); + relation_to_scan = heap_openrv(relrv, AccessShareLock); /* Find the column to search */ attrid = my_varattno(relation_to_scan, attribute); if (attrid < 0) - { elog(ERROR, "int4notin: unknown attribute %s for relation %s", - attribute, relation); - } + attribute, RelationGetRelationName(relation_to_scan)); scan_descriptor = heap_beginscan(relation_to_scan, false, SnapshotNow, 0, (ScanKey) NULL); diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c index 623d3178e0..9f3dcf8fa1 100644 --- a/src/backend/utils/adt/tid.c +++ b/src/backend/utils/adt/tid.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.28 2001/10/25 05:49:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/tid.c,v 1.29 2002/03/30 01:02:41 tgl Exp $ * * NOTES * input routine largely stolen from boxin(). @@ -19,6 +19,7 @@ #include "postgres.h" #include "access/heapam.h" +#include "catalog/namespace.h" #include "utils/builtins.h" #define DatumGetItemPointer(X) ((ItemPointer) DatumGetPointer(X)) @@ -146,14 +147,13 @@ currtid_byreloid(PG_FUNCTION_ARGS) *result = Current_last_tid; PG_RETURN_ITEMPOINTER(result); } + + rel = heap_open(reloid, AccessShareLock); + ItemPointerCopy(tid, result); - if ((rel = heap_open(reloid, AccessShareLock)) != NULL) - { - heap_get_latest_tid(rel, SnapshotNow, result); - heap_close(rel, AccessShareLock); - } - else - elog(ERROR, "Relation %u not found", reloid); + heap_get_latest_tid(rel, SnapshotNow, result); + + heap_close(rel, AccessShareLock); PG_RETURN_ITEMPOINTER(result); } @@ -164,23 +164,19 @@ currtid_byrelname(PG_FUNCTION_ARGS) text *relname = PG_GETARG_TEXT_P(0); ItemPointer tid = PG_GETARG_ITEMPOINTER(1); ItemPointer result; - char *str; + RangeVar *relrv; Relation rel; - str = DatumGetCString(DirectFunctionCall1(textout, - PointerGetDatum(relname))); + relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname, + "currtid_byrelname")); + rel = heap_openrv(relrv, AccessShareLock); result = (ItemPointer) palloc(sizeof(ItemPointerData)); ItemPointerCopy(tid, result); - if ((rel = heap_openr(str, AccessShareLock)) != NULL) - { - heap_get_latest_tid(rel, SnapshotNow, result); - heap_close(rel, AccessShareLock); - } - else - elog(ERROR, "Relation %s not found", str); - pfree(str); + heap_get_latest_tid(rel, SnapshotNow, result); + + heap_close(rel, AccessShareLock); PG_RETURN_ITEMPOINTER(result); } diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index b10ec7d685..ce3e8dc254 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.79 2002/03/05 05:33:19 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/varlena.c,v 1.80 2002/03/30 01:02:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1039,6 +1039,122 @@ name_text(PG_FUNCTION_ARGS) } +/* + * textToQualifiedNameList - convert a text object to list of names + * + * This implements the input parsing needed by nextval() and other + * functions that take a text parameter representing a qualified name. + * We split the name at dots, downcase if not double-quoted, and + * truncate names if they're too long. + * + * This is a kluge, really, and exists only for historical reasons. + * A better notation for such functions would be nextval(relname). + */ +List * +textToQualifiedNameList(text *textval, const char *caller) +{ + char *rawname; + char *nextp; + List *result = NIL; + + /* Convert to C string (handles possible detoasting). */ + /* Note we rely on being able to modify rawname below. */ + rawname = DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(textval))); + nextp = rawname; + + do + { + char *curname; + char *endp; + char *cp; + int curlen; + + if (*nextp == '\"') + { + /* Quoted name --- collapse quote-quote pairs, no downcasing */ + curname = nextp + 1; + for (;;) + { + endp = strchr(nextp + 1, '\"'); + if (endp == NULL) + elog(ERROR, "%s: invalid quoted name: mismatched quotes", + caller); + if (endp[1] != '\"') + break; /* found end of quoted name */ + /* Collapse adjacent quotes into one quote, and look again */ + memmove(endp, endp+1, strlen(endp)); + nextp = endp; + } + *endp = '\0'; + nextp = endp + 1; + if (*nextp) + { + if (*nextp != '.') + elog(ERROR, "%s: invalid name syntax", + caller); + nextp++; + if (*nextp == '\0') + elog(ERROR, "%s: invalid name syntax", + caller); + } + } + else + { + /* Unquoted name --- extends to next dot */ + if (*nextp == '\0') /* empty name not okay here */ + elog(ERROR, "%s: invalid name syntax", + caller); + curname = nextp; + endp = strchr(nextp, '.'); + if (endp) + { + *endp = '\0'; + nextp = endp + 1; + if (*nextp == '\0') + elog(ERROR, "%s: invalid name syntax", + caller); + } + else + nextp = nextp + strlen(nextp); + /* + * It's important that this match the identifier downcasing code + * used by backend/parser/scan.l. + */ + for (cp = curname; *cp; cp++) + { + if (isupper((unsigned char) *cp)) + *cp = tolower((unsigned char) *cp); + } + } + + /* Truncate name if it's overlength; again, should match scan.l */ + curlen = strlen(curname); + if (curlen >= NAMEDATALEN) + { +#ifdef MULTIBYTE + curlen = pg_mbcliplen(curname, curlen, NAMEDATALEN - 1); + curname[curlen] = '\0'; +#else + curname[NAMEDATALEN - 1] = '\0'; +#endif + } + + /* + * Finished isolating current name --- add it to list + */ + result = lappend(result, makeString(pstrdup(curname))); + /* + * Loop back if we found a dot + */ + } while (*nextp); + + pfree(rawname); + + return result; +} + + /***************************************************************************** * Comparison Functions used for bytea * diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index f6f359813c..242050de49 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: namespace.h,v 1.2 2002/03/29 19:06:18 tgl Exp $ + * $Id: namespace.h,v 1.3 2002/03/30 01:02:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,8 @@ extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation); extern Oid RelnameGetRelid(const char *relname); +extern Oid TypenameGetTypid(const char *typname); + extern Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p); extern RangeVar *makeRangeVarFromNameList(List *names); diff --git a/src/include/catalog/pg_namespace.h b/src/include/catalog/pg_namespace.h index 8a57cf7977..607b74132e 100644 --- a/src/include/catalog/pg_namespace.h +++ b/src/include/catalog/pg_namespace.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_namespace.h,v 1.1 2002/03/22 21:34:44 tgl Exp $ + * $Id: pg_namespace.h,v 1.2 2002/03/30 01:02:42 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -69,6 +69,12 @@ typedef FormData_pg_namespace *Form_pg_namespace; DATA(insert OID = 11 ( "pg_catalog" PGUID "{=r}" )); DESCR("System catalog namespace"); #define PG_CATALOG_NAMESPACE 11 +DATA(insert OID = 99 ( "pg_toast" PGUID "{=r}" )); +DESCR("Reserved namespace for TOAST tables"); +#define PG_TOAST_NAMESPACE 99 +DATA(insert OID = 2071 ( "pg_public" PGUID "{=rw}" )); +DESCR("Standard public namespace"); +#define PG_PUBLIC_NAMESPACE 2071 /* diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index e1343bf4fc..7bd111cd54 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.173 2002/03/22 02:56:37 tgl Exp $ + * $Id: builtins.h,v 1.174 2002/03/30 01:02:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -410,6 +410,7 @@ extern Datum text_substr(PG_FUNCTION_ARGS); extern Datum name_text(PG_FUNCTION_ARGS); extern Datum text_name(PG_FUNCTION_ARGS); extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2); +extern List *textToQualifiedNameList(text *textval, const char *caller); extern Datum byteain(PG_FUNCTION_ARGS); extern Datum byteaout(PG_FUNCTION_ARGS); diff --git a/src/test/regress/output/constraints.source b/src/test/regress/output/constraints.source index 0e12a36816..8986118fd4 100644 --- a/src/test/regress/output/constraints.source +++ b/src/test/regress/output/constraints.source @@ -216,7 +216,6 @@ INSERT INTO tmp VALUES (null, 'Y', null); INSERT INTO tmp VALUES (5, '!check failed', null); INSERT INTO tmp VALUES (null, 'try again', null); INSERT INTO INSERT_TBL(y) select yd from tmp; -WARNING: insert_seq.nextval: sequence was re-created SELECT '' AS three, * FROM INSERT_TBL; three | x | y | z -------+---+---------------+----