📄 rowtypes.c
字号:
/*------------------------------------------------------------------------- * * rowtypes.c * I/O functions for generic composite types. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.13.2.1 2006/01/17 17:33:21 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <ctype.h>#include "access/heapam.h"#include "access/htup.h"#include "catalog/pg_type.h"#include "lib/stringinfo.h"#include "libpq/pqformat.h"#include "utils/builtins.h"#include "utils/lsyscache.h"#include "utils/typcache.h"/* * structure to cache metadata needed for record I/O */typedef struct ColumnIOData{ Oid column_type; Oid typiofunc; Oid typioparam; FmgrInfo proc;} ColumnIOData;typedef struct RecordIOData{ Oid record_type; int32 record_typmod; int ncolumns; ColumnIOData columns[1]; /* VARIABLE LENGTH ARRAY */} RecordIOData;/* * record_in - input routine for any composite type. */Datumrecord_in(PG_FUNCTION_ARGS){ char *string = PG_GETARG_CSTRING(0); Oid tupType = PG_GETARG_OID(1);#ifdef NOT_USED int32 typmod = PG_GETARG_INT32(2);#endif HeapTupleHeader result; int32 tupTypmod; TupleDesc tupdesc; HeapTuple tuple; RecordIOData *my_extra; bool needComma = false; int ncolumns; int i; char *ptr; Datum *values; char *nulls; StringInfoData buf; /* * Use the passed type unless it's RECORD; we can't support input of * anonymous types, mainly because there's no good way to figure out which * anonymous type is wanted. Note that for RECORD, what we'll probably * actually get is RECORD's typelem, ie, zero. */ if (tupType == InvalidOid || tupType == RECORDOID) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("input of anonymous composite types is not implemented"))); tupTypmod = -1; /* for all non-anonymous types */ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); tupdesc = CreateTupleDescCopy(tupdesc); ncolumns = tupdesc->natts; /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (char *) palloc(ncolumns * sizeof(char)); /* * Scan the string. We use "buf" to accumulate the de-quoted data for * each column, which is then fed to the appropriate input converter. */ ptr = string; /* Allow leading whitespace */ while (*ptr && isspace((unsigned char) *ptr)) ptr++; if (*ptr++ != '(') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed record literal: \"%s\"", string), errdetail("Missing left parenthesis."))); initStringInfo(&buf); for (i = 0; i < ncolumns; i++) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; /* Ignore dropped columns in datatype, but fill with nulls */ if (tupdesc->attrs[i]->attisdropped) { values[i] = (Datum) 0; nulls[i] = 'n'; continue; } if (needComma) { /* Skip comma that separates prior field from this one */ if (*ptr == ',') ptr++; else /* *ptr must be ')' */ ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed record literal: \"%s\"", string), errdetail("Too few columns."))); } /* Check for null: completely empty input means null */ if (*ptr == ',' || *ptr == ')') { values[i] = (Datum) 0; nulls[i] = 'n'; } else { /* Extract string for this column */ bool inquote = false; buf.len = 0; buf.data[0] = '\0'; while (inquote || !(*ptr == ',' || *ptr == ')')) { char ch = *ptr++; if (ch == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed record literal: \"%s\"", string), errdetail("Unexpected end of input."))); if (ch == '\\') { if (*ptr == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed record literal: \"%s\"", string), errdetail("Unexpected end of input."))); appendStringInfoChar(&buf, *ptr++); } else if (ch == '\"') { if (!inquote) inquote = true; else if (*ptr == '\"') { /* doubled quote within quote sequence */ appendStringInfoChar(&buf, *ptr++); } else inquote = false; } else appendStringInfoChar(&buf, ch); } /* * Convert the column value */ if (column_info->column_type != column_type) { getTypeInputInfo(column_type, &column_info->typiofunc, &column_info->typioparam); fmgr_info_cxt(column_info->typiofunc, &column_info->proc, fcinfo->flinfo->fn_mcxt); column_info->column_type = column_type; } values[i] = FunctionCall3(&column_info->proc, CStringGetDatum(buf.data), ObjectIdGetDatum(column_info->typioparam), Int32GetDatum(tupdesc->attrs[i]->atttypmod)); nulls[i] = ' '; } /* * Prep for next column */ needComma = true; } if (*ptr++ != ')') ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed record literal: \"%s\"", string), errdetail("Too many columns."))); /* Allow trailing whitespace */ while (*ptr && isspace((unsigned char) *ptr)) ptr++; if (*ptr) ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("malformed record literal: \"%s\"", string), errdetail("Junk after right parenthesis."))); tuple = heap_formtuple(tupdesc, values, nulls); /* * We cannot return tuple->t_data because heap_formtuple allocates it as * part of a larger chunk, and our caller may expect to be able to pfree * our result. So must copy the info into a new palloc chunk. */ result = (HeapTupleHeader) palloc(tuple->t_len); memcpy(result, tuple->t_data, tuple->t_len); heap_freetuple(tuple); pfree(buf.data); pfree(values); pfree(nulls); FreeTupleDesc(tupdesc); PG_RETURN_HEAPTUPLEHEADER(result);}/* * record_out - output routine for any composite type. */Datumrecord_out(PG_FUNCTION_ARGS){ HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0); Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tuple; RecordIOData *my_extra; bool needComma = false; int ncolumns; int i; Datum *values; char *nulls; StringInfoData buf; /* Extract type info from the tuple itself */ tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); tupdesc = CreateTupleDescCopy(tupdesc); ncolumns = tupdesc->natts; /* Build a temporary HeapTuple control structure */ tuple.t_len = HeapTupleHeaderGetDatumLength(rec); ItemPointerSetInvalid(&(tuple.t_self)); tuple.t_tableOid = InvalidOid; tuple.t_data = rec; /* * We arrange to look up the needed I/O info just once per series of * calls, assuming the record type doesn't change underneath us. */ my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; if (my_extra == NULL || my_extra->ncolumns != ncolumns) { fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra; my_extra->record_type = InvalidOid; my_extra->record_typmod = 0; } if (my_extra->record_type != tupType || my_extra->record_typmod != tupTypmod) { MemSet(my_extra, 0, sizeof(RecordIOData) - sizeof(ColumnIOData) + ncolumns * sizeof(ColumnIOData)); my_extra->record_type = tupType; my_extra->record_typmod = tupTypmod; my_extra->ncolumns = ncolumns; } values = (Datum *) palloc(ncolumns * sizeof(Datum)); nulls = (char *) palloc(ncolumns * sizeof(char)); /* Break down the tuple into fields */ heap_deformtuple(&tuple, tupdesc, values, nulls); /* And build the result string */ initStringInfo(&buf); appendStringInfoChar(&buf, '('); for (i = 0; i < ncolumns; i++) { ColumnIOData *column_info = &my_extra->columns[i]; Oid column_type = tupdesc->attrs[i]->atttypid; char *value; char *tmp; bool nq; /* Ignore dropped columns in datatype */ if (tupdesc->attrs[i]->attisdropped) continue; if (needComma) appendStringInfoChar(&buf, ','); needComma = true; if (nulls[i] == 'n') { /* emit nothing... */ continue; } /* * Convert the column value to text */ if (column_info->column_type != column_type) { bool typIsVarlena;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -