<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
- $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.22 2001/08/26 16:55:58 tgl Exp $
+ $Header: /cvsroot/pgsql/doc/src/sgml/catalogs.sgml,v 2.23 2001/09/06 02:07:41 tgl Exp $
-->
<chapter id="catalogs">
<entry></entry>
<entry>
The initial value of the transition state. This is a text
- field which will be cast to the type of aggtranstype.
+ field containing the initial value in its external string
+ representation. If the field is NULL, the transition state
+ value starts out NULL.
</entry>
</row>
</tbody>
<entry>typdefault</entry>
<entry><type>text</type></entry>
<entry></entry>
- <entry>???</entry>
+ <entry><para>
+ <structfield>typdefault</structfield> is NULL for types without a
+ default value. If it's not NULL, it contains the external string
+ representation of the type's default value.
+ </para></entry>
</row>
</tbody>
</tgroup>
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.175 2001/08/25 18:52:41 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.176 2001/09/06 02:07:42 tgl Exp $
*
*
* INTERFACE ROUTINES
static void
AddNewRelationType(char *typeName, Oid new_rel_oid, Oid new_type_oid)
{
-
/*
* The sizes are set to oid size because it makes implementing sets
* MUCH easier, and no one (we hope) uses these fields to figure out
* actually get is the oid of a tuple in the pg_proc catalog, so the
* size of the "set" is the size of an oid. Similarly, byval being
* true makes sets much easier, and it isn't used by anything else.
- *
- * XXX Note the assumption that OIDs are the same size as int4s.
*/
TypeCreate(typeName, /* type name */
new_type_oid, /* preassigned oid for type */
new_rel_oid, /* relation oid */
sizeof(Oid), /* internal size */
- sizeof(Oid), /* external size */
+ -1, /* external size */
'c', /* type-type (catalog) */
',', /* default array delimiter */
- "int4in", /* input procedure */
- "int4out", /* output procedure */
- "int4in", /* receive procedure */
- "int4out", /* send procedure */
+ "oidin", /* input procedure */
+ "oidout", /* output procedure */
+ "oidin", /* receive procedure */
+ "oidout", /* send procedure */
NULL, /* array element type - irrelevant */
- "-", /* default type value */
+ NULL, /* default type value - none */
true, /* passed by value */
- 'i', /* default alignment */
+ 'i', /* default alignment - same as for OID */
'p'); /* Not TOASTable */
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.62 2001/08/10 15:49:39 petere Exp $
+ * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.63 2001/09/06 02:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* initialize the scan key and begin a scan of pg_type
*/
- ScanKeyEntryInitialize(typeKey,
+ ScanKeyEntryInitialize(&typeKey[0],
0,
Anum_pg_type_typname,
F_NAMEEQ,
}
/*
- * XXX comment me
+ * validate size specifications: either positive (fixed-length) or
+ * -1 (variable-length).
*/
- if (externalSize == 0)
- externalSize = -1; /* variable length */
+ if (! (internalSize > 0 || internalSize == -1))
+ elog(ERROR, "TypeCreate: invalid type internal size %d",
+ internalSize);
+ if (! (externalSize > 0 || externalSize == -1))
+ elog(ERROR, "TypeCreate: invalid type external size %d",
+ externalSize);
+
+ if (internalSize != -1 && storage != 'p')
+ elog(ERROR, "TypeCreate: fixed size types must have storage PLAIN");
/*
* initialize arrays needed by FormHeapTuple
{
nulls[i] = ' ';
replaces[i] = 'r';
- values[i] = (Datum) NULL; /* redundant, but nice */
+ values[i] = (Datum) 0;
}
- /*
- * XXX
- *
- * Do this so that user-defined types have size -1 instead of zero if
- * they are variable-length - this is so that everything else in the
- * backend works.
- */
-
- if (internalSize == 0)
- internalSize = -1;
-
/*
* initialize the *values information
*/
/*
* initialize the default value for this type.
*/
- values[i] = DirectFunctionCall1(textin, /* 17 */
- CStringGetDatum(defaultTypeValue ? defaultTypeValue : "-"));
+ if (defaultTypeValue)
+ values[i] = DirectFunctionCall1(textin,
+ CStringGetDatum(defaultTypeValue));
+ else
+ nulls[i] = 'n';
+ i++; /* 17 */
/*
* open pg_type and begin a scan for the type name.
*/
pg_type_desc = heap_openr(TypeRelationName, RowExclusiveLock);
- ScanKeyEntryInitialize(typeKey,
+ ScanKeyEntryInitialize(&typeKey[0],
0,
Anum_pg_type_typname,
F_NAMEEQ,
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.58 2001/08/03 20:47:40 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.59 2001/09/06 02:07:42 tgl Exp $
*
* DESCRIPTION
* The "DefineFoo" routines take the parse tree and pick out the
void
DefineType(char *typeName, List *parameters)
{
- int16 internalLength = 0; /* int2 */
- int16 externalLength = 0; /* int2 */
+ int16 internalLength = -1; /* int2 */
+ int16 externalLength = -1; /* int2 */
char *elemName = NULL;
char *inputName = NULL;
char *outputName = NULL;
char *sendName = NULL;
char *receiveName = NULL;
- char *defaultValue = NULL; /* Datum */
+ char *defaultValue = NULL;
bool byValue = false;
char delimiter = DEFAULT_TYPDELIM;
char *shadow_type;
List *pl;
char alignment = 'i'; /* default alignment */
- char storage = 'p'; /* default storage in TOAST */
+ char storage = 'p'; /* default TOAST storage method */
/*
* Type names must be one character shorter than other names, allowing
* "_".
*/
if (strlen(typeName) > (NAMEDATALEN - 2))
- {
elog(ERROR, "DefineType: type names must be %d characters or less",
NAMEDATALEN - 2);
- }
foreach(pl, parameters)
{
if (outputName == NULL)
elog(ERROR, "Define: \"output\" unspecified");
- if (internalLength != -1 && storage != 'p')
- elog(ERROR, "Define: fixed size types must have storage PLAIN");
-
/*
* now have TypeCreate do all the real work.
*/
*/
shadow_type = makeArrayTypeName(typeName);
+ /* alignment must be 'i' or 'd' for arrays */
+ alignment = (alignment == 'd') ? 'd' : 'i';
+
TypeCreate(shadow_type, /* type name */
InvalidOid, /* preassigned type oid (not done here) */
InvalidOid, /* relation oid (n/a here) */
typeName, /* element type name */
NULL, /* never a default type value */
false, /* never passed by value */
- alignment, /* NB: must be 'i' or 'd' for arrays... */
+ alignment, /* see above */
'x'); /* ARRAY is always toastable */
pfree(shadow_type);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.42 2001/03/22 03:59:38 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.43 2001/09/06 02:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
case CMD_INSERT:
{
- Datum typedefault = get_typdefault(atttype);
- int typlen;
- Const *temp_const;
+ bool hasdefault;
+ Datum typedefault;
+ int16 typlen;
+ bool typbyval;
+ Const *def_const;
-#ifdef _DROP_COLUMN_HACK__
- if (COLUMN_IS_DROPPED(att_tup))
- typedefault = PointerGetDatum(NULL);
-#endif /* _DROP_COLUMN_HACK__ */
-
- if (typedefault == PointerGetDatum(NULL))
- typlen = 0;
- else
+ if (att_tup->attisset)
{
-
/*
- * Since this is an append or replace, the
- * size of any set attribute is the size of
- * the OID used to represent it.
+ * Set attributes are represented as OIDs no
+ * matter what the set element type is, and
+ * the element type's default is irrelevant too.
*/
- if (att_tup->attisset)
- typlen = get_typlen(OIDOID);
+ hasdefault = false;
+ typedefault = (Datum) 0;
+ typlen = sizeof(Oid);
+ typbyval = true;
+ }
+ else
+ {
+#ifdef _DROP_COLUMN_HACK__
+ if (COLUMN_IS_DROPPED(att_tup))
+ {
+ hasdefault = false;
+ typedefault = (Datum) 0;
+ }
else
- typlen = get_typlen(atttype);
+#endif /* _DROP_COLUMN_HACK__ */
+ hasdefault = get_typdefault(atttype,
+ &typedefault);
+
+ get_typlenbyval(atttype, &typlen, &typbyval);
}
- temp_const = makeConst(atttype,
- typlen,
- typedefault,
- (typedefault == PointerGetDatum(NULL)),
- false,
- false, /* not a set */
- false);
+ def_const = makeConst(atttype,
+ typlen,
+ typedefault,
+ !hasdefault,
+ typbyval,
+ false, /* not a set */
+ false);
new_tle = makeTargetEntry(makeResdom(attrno,
atttype,
-1,
pstrdup(attrname),
false),
- (Node *) temp_const);
+ (Node *) def_const);
break;
}
case CMD_UPDATE:
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.57 2001/08/21 16:36:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.58 2001/09/06 02:07:42 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
/*
* get_typdefault
*
- * Given a type OID, return the typdefault field associated with that
- * type, or Datum(NULL) if there is no typdefault. (This implies
- * that pass-by-value types can't have a default value that has
- * a representation of zero. Not worth fixing now.)
- * The result points to palloc'd storage for non-pass-by-value types.
+ * Given a type OID, return the type's default value, if any.
+ * Returns FALSE if there is no default (effectively, default is NULL).
+ * The result points to palloc'd storage for pass-by-reference types.
*/
-Datum
-get_typdefault(Oid typid)
+bool
+get_typdefault(Oid typid, Datum *defaultValue)
{
HeapTuple typeTuple;
Form_pg_type type;
- struct varlena *typDefault;
+ Oid typinput,
+ typelem;
+ Datum textDefaultVal;
bool isNull;
- int32 dataSize;
- int32 typLen;
- bool typByVal;
- Datum returnValue;
+ char *strDefaultVal;
typeTuple = SearchSysCache(TYPEOID,
ObjectIdGetDatum(typid),
type = (Form_pg_type) GETSTRUCT(typeTuple);
+ typinput = type->typinput;
+ typelem = type->typelem;
+
/*
- * First, see if there is a non-null typdefault field (usually there
- * isn't)
+ * typdefault is potentially null, so don't try to access it as a struct
+ * field. Must do it the hard way with SysCacheGetAttr.
*/
- typDefault = (struct varlena *)
- DatumGetPointer(SysCacheGetAttr(TYPEOID,
- typeTuple,
- Anum_pg_type_typdefault,
- &isNull));
+ textDefaultVal = SysCacheGetAttr(TYPEOID,
+ typeTuple,
+ Anum_pg_type_typdefault,
+ &isNull);
if (isNull)
{
ReleaseSysCache(typeTuple);
- return PointerGetDatum(NULL);
+ *defaultValue = (Datum) 0;
+ return false;
}
- /*
- * Otherwise, extract/copy the value.
- */
- dataSize = VARSIZE(typDefault) - VARHDRSZ;
- typLen = type->typlen;
- typByVal = type->typbyval;
+ /* Convert text datum to C string */
+ strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
+ textDefaultVal));
- if (typByVal)
- {
- if (dataSize == typLen)
- returnValue = fetch_att(VARDATA(typDefault), typByVal, typLen);
- else
- returnValue = PointerGetDatum(NULL);
- }
- else if (typLen < 0)
- {
- /* variable-size type */
- if (dataSize < 0)
- returnValue = PointerGetDatum(NULL);
- else
- {
- returnValue = PointerGetDatum(palloc(VARSIZE(typDefault)));
- memcpy((char *) DatumGetPointer(returnValue),
- (char *) typDefault,
- (int) VARSIZE(typDefault));
- }
- }
- else
- {
- /* fixed-size pass-by-ref type */
- if (dataSize != typLen)
- returnValue = PointerGetDatum(NULL);
- else
- {
- returnValue = PointerGetDatum(palloc(dataSize));
- memcpy((char *) DatumGetPointer(returnValue),
- VARDATA(typDefault),
- (int) dataSize);
- }
- }
+ /* Convert C string to a value of the given type */
+ *defaultValue = OidFunctionCall3(typinput,
+ CStringGetDatum(strDefaultVal),
+ ObjectIdGetDatum(typelem),
+ Int32GetDatum(-1));
+ pfree(strDefaultVal);
ReleaseSysCache(typeTuple);
- return returnValue;
+ return true;
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.227 2001/08/27 20:33:07 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/bin/pg_dump/pg_dump.c,v 1.228 2001/09/06 02:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
tinfo[i].typsend = strdup(PQgetvalue(res, i, i_typsend));
tinfo[i].typelem = strdup(PQgetvalue(res, i, i_typelem));
tinfo[i].typdelim = strdup(PQgetvalue(res, i, i_typdelim));
- tinfo[i].typdefault = strdup(PQgetvalue(res, i, i_typdefault));
+ if (PQgetisnull(res, i, i_typdefault))
+ tinfo[i].typdefault = NULL;
+ else
+ tinfo[i].typdefault = strdup(PQgetvalue(res, i, i_typdefault));
tinfo[i].typrelid = strdup(PQgetvalue(res, i, i_typrelid));
tinfo[i].typalign = strdup(PQgetvalue(res, i, i_typalign));
tinfo[i].typstorage = strdup(PQgetvalue(res, i, i_typstorage));
"CREATE TYPE %s "
"( internallength = %s, externallength = %s,",
fmtId(tinfo[i].typname, force_quotes),
- tinfo[i].typlen,
- tinfo[i].typprtlen);
+ (strcmp(tinfo[i].typlen, "-1") == 0) ?
+ "variable" : tinfo[i].typlen,
+ (strcmp(tinfo[i].typprtlen, "-1") == 0) ?
+ "variable" : tinfo[i].typprtlen);
/* cannot combine these because fmtId uses static result area */
appendPQExpBuffer(q, " input = %s,",
fmtId(tinfo[i].typinput, force_quotes));
fmtId(tinfo[i].typoutput, force_quotes));
appendPQExpBuffer(q, " send = %s,",
fmtId(tinfo[i].typsend, force_quotes));
- appendPQExpBuffer(q, " receive = %s, default = ",
+ appendPQExpBuffer(q, " receive = %s",
fmtId(tinfo[i].typreceive, force_quotes));
- formatStringLiteral(q, tinfo[i].typdefault, CONV_ALL);
+
+ if (tinfo[i].typdefault != NULL)
+ {
+ appendPQExpBuffer(q, ", default = ");
+ formatStringLiteral(q, tinfo[i].typdefault, CONV_ALL);
+ }
if (tinfo[i].isArray)
{
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pg_type.h,v 1.110 2001/08/25 18:52:43 tgl Exp $
+ * $Id: pg_type.h,v 1.111 2001/09/06 02:07:42 tgl Exp $
*
* NOTES
* the genbki.sh script reads this file and generates .bki
int4 typowner;
/*
- * typlen is the number of bytes we use to represent a value of this
- * type, e.g. 4 for an int4. But for a variable length type, typlen
- * is -1.
+ * For a fixed-size type, typlen is the number of bytes we use to
+ * represent a value of this type, e.g. 4 for an int4. But for a
+ * variable-length type, typlen is -1.
*/
int2 typlen;
+ /*
+ * typprtlen was once intended to be the length of the external
+ * representation of a datatype, with the same interpretation as for
+ * typlen. But it's currently unused.
+ */
int2 typprtlen;
/*
* anyway?)
*/
char typtype;
+
+ /*
+ * If typisdefined is false, the entry is only a placeholder (forward
+ * reference). We know the type name, but not yet anything else about it.
+ */
bool typisdefined;
- char typdelim;
+
+ char typdelim; /* delimiter for arrays of this type */
Oid typrelid; /* 0 if not a class type */
/*
* typelem != 0 and typlen < 0.
*/
Oid typelem;
+
+ /*
+ * I/O conversion procedures for the datatype.
+ */
regproc typinput;
regproc typoutput;
regproc typreceive;
*/
char typstorage;
+ /*
+ * typdefault is NULL if the type has no associated default value.
+ * If it's not NULL, it contains the external representation of the
+ * type's default value --- this default is used whenever no per-column
+ * default is specified for a column of the datatype.
+ */
text typdefault; /* VARIABLE LENGTH FIELD */
} FormData_pg_type;
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: lsyscache.h,v 1.35 2001/08/21 16:36:06 tgl Exp $
+ * $Id: lsyscache.h,v 1.36 2001/09/06 02:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern bool get_typbyval(Oid typid);
extern void get_typlenbyval(Oid typid, int16 *typlen, bool *typbyval);
extern char get_typstorage(Oid typid);
-extern Datum get_typdefault(Oid typid);
+extern bool get_typdefault(Oid typid, Datum *defaultValue);
extern int32 get_typavgwidth(Oid typid, int32 typmod);
extern int32 get_attavgwidth(Oid relid, AttrNumber attnum);
extern bool get_attstatsslot(HeapTuple statstuple,
output = int44out,
element = int4
);
+-- Test type-related default values (broken in releases before PG 7.2)
+CREATE TYPE int42 (
+ internallength = 4,
+ input = int4in,
+ output = int4out,
+ alignment = int4,
+ default = 42,
+ passedbyvalue
+);
+CREATE TYPE text_w_default (
+ internallength = variable,
+ input = textin,
+ output = textout,
+ alignment = int4,
+ default = 'zippo'
+);
+CREATE TABLE default_test (f1 text_w_default, f2 int42);
+INSERT INTO default_test DEFAULT VALUES;
+SELECT * FROM default_test;
+ f1 | f2
+-------+----
+ zippo | 42
+(1 row)
+
+DROP TABLE default_test;
element = int4
);
+-- Test type-related default values (broken in releases before PG 7.2)
+
+CREATE TYPE int42 (
+ internallength = 4,
+ input = int4in,
+ output = int4out,
+ alignment = int4,
+ default = 42,
+ passedbyvalue
+);
+
+CREATE TYPE text_w_default (
+ internallength = variable,
+ input = textin,
+ output = textout,
+ alignment = int4,
+ default = 'zippo'
+);
+
+CREATE TABLE default_test (f1 text_w_default, f2 int42);
+
+INSERT INTO default_test DEFAULT VALUES;
+
+SELECT * FROM default_test;
+
+DROP TABLE default_test;