~vcs-imports/mammoth-replicator/trunk

« back to all changes in this revision

Viewing changes to src/backend/utils/adt/rowtypes.c

  • Committer: alvherre
  • Date: 2005-12-16 21:24:52 UTC
  • Revision ID: svn-v4:db760fc0-0f08-0410-9d63-cc6633f64896:trunk:1
Initial import of the REL8_0_3 sources from the Pgsql CVS repository.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * rowtypes.c
 
4
 *        I/O functions for generic composite types.
 
5
 *
 
6
 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
 
7
 * Portions Copyright (c) 1994, Regents of the University of California
 
8
 *
 
9
 *
 
10
 * IDENTIFICATION
 
11
 *        $PostgreSQL: pgsql/src/backend/utils/adt/rowtypes.c,v 1.8.4.2 2005-04-30 20:04:46 tgl Exp $
 
12
 *
 
13
 *-------------------------------------------------------------------------
 
14
 */
 
15
#include "postgres.h"
 
16
 
 
17
#include <ctype.h>
 
18
 
 
19
#include "access/heapam.h"
 
20
#include "access/htup.h"
 
21
#include "catalog/pg_type.h"
 
22
#include "lib/stringinfo.h"
 
23
#include "libpq/pqformat.h"
 
24
#include "utils/builtins.h"
 
25
#include "utils/lsyscache.h"
 
26
#include "utils/typcache.h"
 
27
 
 
28
 
 
29
/*
 
30
 * structure to cache metadata needed for record I/O
 
31
 */
 
32
typedef struct ColumnIOData
 
33
{
 
34
        Oid                     column_type;
 
35
        Oid                     typiofunc;
 
36
        Oid                     typioparam;
 
37
        FmgrInfo        proc;
 
38
} ColumnIOData;
 
39
 
 
40
typedef struct RecordIOData
 
41
{
 
42
        Oid                     record_type;
 
43
        int32           record_typmod;
 
44
        int                     ncolumns;
 
45
        ColumnIOData columns[1];        /* VARIABLE LENGTH ARRAY */
 
46
} RecordIOData;
 
47
 
 
48
 
 
49
/*
 
50
 * record_in            - input routine for any composite type.
 
51
 */
 
52
Datum
 
53
record_in(PG_FUNCTION_ARGS)
 
54
{
 
55
        char       *string = PG_GETARG_CSTRING(0);
 
56
        Oid                     tupType = PG_GETARG_OID(1);
 
57
        HeapTupleHeader result;
 
58
        int32           tupTypmod;
 
59
        TupleDesc       tupdesc;
 
60
        HeapTuple       tuple;
 
61
        RecordIOData *my_extra;
 
62
        bool            needComma = false;
 
63
        int                     ncolumns;
 
64
        int                     i;
 
65
        char       *ptr;
 
66
        Datum      *values;
 
67
        char       *nulls;
 
68
        StringInfoData buf;
 
69
 
 
70
        /*
 
71
         * Use the passed type unless it's RECORD; we can't support input of
 
72
         * anonymous types, mainly because there's no good way to figure out
 
73
         * which anonymous type is wanted.      Note that for RECORD, what we'll
 
74
         * probably actually get is RECORD's typelem, ie, zero.
 
75
         */
 
76
        if (tupType == InvalidOid || tupType == RECORDOID)
 
77
                ereport(ERROR,
 
78
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
79
                errmsg("input of anonymous composite types is not implemented")));
 
80
        tupTypmod = -1;                         /* for all non-anonymous types */
 
81
        tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
82
        ncolumns = tupdesc->natts;
 
83
 
 
84
        /*
 
85
         * We arrange to look up the needed I/O info just once per series of
 
86
         * calls, assuming the record type doesn't change underneath us.
 
87
         */
 
88
        my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
89
        if (my_extra == NULL ||
 
90
                my_extra->ncolumns != ncolumns)
 
91
        {
 
92
                fcinfo->flinfo->fn_extra =
 
93
                        MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
94
                                                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
95
                                                           + ncolumns * sizeof(ColumnIOData));
 
96
                my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
97
                my_extra->record_type = InvalidOid;
 
98
                my_extra->record_typmod = 0;
 
99
        }
 
100
 
 
101
        if (my_extra->record_type != tupType ||
 
102
                my_extra->record_typmod != tupTypmod)
 
103
        {
 
104
                MemSet(my_extra, 0,
 
105
                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
106
                           + ncolumns * sizeof(ColumnIOData));
 
107
                my_extra->record_type = tupType;
 
108
                my_extra->record_typmod = tupTypmod;
 
109
                my_extra->ncolumns = ncolumns;
 
110
        }
 
111
 
 
112
        values = (Datum *) palloc(ncolumns * sizeof(Datum));
 
113
        nulls = (char *) palloc(ncolumns * sizeof(char));
 
114
 
 
115
        /*
 
116
         * Scan the string.  We use "buf" to accumulate the de-quoted data for
 
117
         * each column, which is then fed to the appropriate input converter.
 
118
         */
 
119
        ptr = string;
 
120
        /* Allow leading whitespace */
 
121
        while (*ptr && isspace((unsigned char) *ptr))
 
122
                ptr++;
 
123
        if (*ptr++ != '(')
 
124
                ereport(ERROR,
 
125
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
126
                                 errmsg("malformed record literal: \"%s\"", string),
 
127
                                 errdetail("Missing left parenthesis.")));
 
128
 
 
129
        initStringInfo(&buf);
 
130
 
 
131
        for (i = 0; i < ncolumns; i++)
 
132
        {
 
133
                ColumnIOData *column_info = &my_extra->columns[i];
 
134
                Oid                     column_type = tupdesc->attrs[i]->atttypid;
 
135
 
 
136
                /* Ignore dropped columns in datatype, but fill with nulls */
 
137
                if (tupdesc->attrs[i]->attisdropped)
 
138
                {
 
139
                        values[i] = (Datum) 0;
 
140
                        nulls[i] = 'n';
 
141
                        continue;
 
142
                }
 
143
 
 
144
                if (needComma)
 
145
                {
 
146
                        /* Skip comma that separates prior field from this one */
 
147
                        if (*ptr == ',')
 
148
                                ptr++;
 
149
                        else
 
150
                                /* *ptr must be ')' */
 
151
                                ereport(ERROR,
 
152
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
153
                                          errmsg("malformed record literal: \"%s\"", string),
 
154
                                                 errdetail("Too few columns.")));
 
155
                }
 
156
 
 
157
                /* Check for null: completely empty input means null */
 
158
                if (*ptr == ',' || *ptr == ')')
 
159
                {
 
160
                        values[i] = (Datum) 0;
 
161
                        nulls[i] = 'n';
 
162
                }
 
163
                else
 
164
                {
 
165
                        /* Extract string for this column */
 
166
                        bool            inquote = false;
 
167
 
 
168
                        buf.len = 0;
 
169
                        buf.data[0] = '\0';
 
170
                        while (inquote || !(*ptr == ',' || *ptr == ')'))
 
171
                        {
 
172
                                char            ch = *ptr++;
 
173
 
 
174
                                if (ch == '\0')
 
175
                                        ereport(ERROR,
 
176
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
177
                                                         errmsg("malformed record literal: \"%s\"",
 
178
                                                                        string),
 
179
                                                         errdetail("Unexpected end of input.")));
 
180
                                if (ch == '\\')
 
181
                                {
 
182
                                        if (*ptr == '\0')
 
183
                                                ereport(ERROR,
 
184
                                                   (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
185
                                                        errmsg("malformed record literal: \"%s\"",
 
186
                                                                   string),
 
187
                                                        errdetail("Unexpected end of input.")));
 
188
                                        appendStringInfoChar(&buf, *ptr++);
 
189
                                }
 
190
                                else if (ch == '\"')
 
191
                                {
 
192
                                        if (!inquote)
 
193
                                                inquote = true;
 
194
                                        else if (*ptr == '\"')
 
195
                                        {
 
196
                                                /* doubled quote within quote sequence */
 
197
                                                appendStringInfoChar(&buf, *ptr++);
 
198
                                        }
 
199
                                        else
 
200
                                                inquote = false;
 
201
                                }
 
202
                                else
 
203
                                        appendStringInfoChar(&buf, ch);
 
204
                        }
 
205
 
 
206
                        /*
 
207
                         * Convert the column value
 
208
                         */
 
209
                        if (column_info->column_type != column_type)
 
210
                        {
 
211
                                getTypeInputInfo(column_type,
 
212
                                                                 &column_info->typiofunc,
 
213
                                                                 &column_info->typioparam);
 
214
                                fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
 
215
                                                          fcinfo->flinfo->fn_mcxt);
 
216
                                column_info->column_type = column_type;
 
217
                        }
 
218
 
 
219
                        values[i] = FunctionCall3(&column_info->proc,
 
220
                                                                          CStringGetDatum(buf.data),
 
221
                                                           ObjectIdGetDatum(column_info->typioparam),
 
222
                                                        Int32GetDatum(tupdesc->attrs[i]->atttypmod));
 
223
                        nulls[i] = ' ';
 
224
                }
 
225
 
 
226
                /*
 
227
                 * Prep for next column
 
228
                 */
 
229
                needComma = true;
 
230
        }
 
231
 
 
232
        if (*ptr++ != ')')
 
233
                ereport(ERROR,
 
234
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
235
                                 errmsg("malformed record literal: \"%s\"", string),
 
236
                                 errdetail("Too many columns.")));
 
237
        /* Allow trailing whitespace */
 
238
        while (*ptr && isspace((unsigned char) *ptr))
 
239
                ptr++;
 
240
        if (*ptr)
 
241
                ereport(ERROR,
 
242
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
243
                                 errmsg("malformed record literal: \"%s\"", string),
 
244
                                 errdetail("Junk after right parenthesis.")));
 
245
 
 
246
        tuple = heap_formtuple(tupdesc, values, nulls);
 
247
 
 
248
        /*
 
249
         * We cannot return tuple->t_data because heap_formtuple allocates it
 
250
         * as part of a larger chunk, and our caller may expect to be able to
 
251
         * pfree our result.  So must copy the info into a new palloc chunk.
 
252
         */
 
253
        result = (HeapTupleHeader) palloc(tuple->t_len);
 
254
        memcpy(result, tuple->t_data, tuple->t_len);
 
255
 
 
256
        heap_freetuple(tuple);
 
257
        pfree(buf.data);
 
258
        pfree(values);
 
259
        pfree(nulls);
 
260
 
 
261
        PG_RETURN_HEAPTUPLEHEADER(result);
 
262
}
 
263
 
 
264
/*
 
265
 * record_out           - output routine for any composite type.
 
266
 */
 
267
Datum
 
268
record_out(PG_FUNCTION_ARGS)
 
269
{
 
270
        HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
 
271
        Oid                     tupType;
 
272
        int32           tupTypmod;
 
273
        TupleDesc       tupdesc;
 
274
        HeapTupleData tuple;
 
275
        RecordIOData *my_extra;
 
276
        bool            needComma = false;
 
277
        int                     ncolumns;
 
278
        int                     i;
 
279
        Datum      *values;
 
280
        char       *nulls;
 
281
        StringInfoData buf;
 
282
 
 
283
        /* Extract type info from the tuple itself */
 
284
        tupType = HeapTupleHeaderGetTypeId(rec);
 
285
        tupTypmod = HeapTupleHeaderGetTypMod(rec);
 
286
        tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
287
        ncolumns = tupdesc->natts;
 
288
 
 
289
        /* Build a temporary HeapTuple control structure */
 
290
        tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
 
291
        ItemPointerSetInvalid(&(tuple.t_self));
 
292
        tuple.t_tableOid = InvalidOid;
 
293
        tuple.t_data = rec;
 
294
 
 
295
        /*
 
296
         * We arrange to look up the needed I/O info just once per series of
 
297
         * calls, assuming the record type doesn't change underneath us.
 
298
         */
 
299
        my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
300
        if (my_extra == NULL ||
 
301
                my_extra->ncolumns != ncolumns)
 
302
        {
 
303
                fcinfo->flinfo->fn_extra =
 
304
                        MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
305
                                                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
306
                                                           + ncolumns * sizeof(ColumnIOData));
 
307
                my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
308
                my_extra->record_type = InvalidOid;
 
309
                my_extra->record_typmod = 0;
 
310
        }
 
311
 
 
312
        if (my_extra->record_type != tupType ||
 
313
                my_extra->record_typmod != tupTypmod)
 
314
        {
 
315
                MemSet(my_extra, 0,
 
316
                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
317
                           + ncolumns * sizeof(ColumnIOData));
 
318
                my_extra->record_type = tupType;
 
319
                my_extra->record_typmod = tupTypmod;
 
320
                my_extra->ncolumns = ncolumns;
 
321
        }
 
322
 
 
323
        values = (Datum *) palloc(ncolumns * sizeof(Datum));
 
324
        nulls = (char *) palloc(ncolumns * sizeof(char));
 
325
 
 
326
        /* Break down the tuple into fields */
 
327
        heap_deformtuple(&tuple, tupdesc, values, nulls);
 
328
 
 
329
        /* And build the result string */
 
330
        initStringInfo(&buf);
 
331
 
 
332
        appendStringInfoChar(&buf, '(');
 
333
 
 
334
        for (i = 0; i < ncolumns; i++)
 
335
        {
 
336
                ColumnIOData *column_info = &my_extra->columns[i];
 
337
                Oid                     column_type = tupdesc->attrs[i]->atttypid;
 
338
                char       *value;
 
339
                char       *tmp;
 
340
                bool            nq;
 
341
 
 
342
                /* Ignore dropped columns in datatype */
 
343
                if (tupdesc->attrs[i]->attisdropped)
 
344
                        continue;
 
345
 
 
346
                if (needComma)
 
347
                        appendStringInfoChar(&buf, ',');
 
348
                needComma = true;
 
349
 
 
350
                if (nulls[i] == 'n')
 
351
                {
 
352
                        /* emit nothing... */
 
353
                        continue;
 
354
                }
 
355
 
 
356
                /*
 
357
                 * Convert the column value to text
 
358
                 */
 
359
                if (column_info->column_type != column_type)
 
360
                {
 
361
                        bool            typIsVarlena;
 
362
 
 
363
                        getTypeOutputInfo(column_type,
 
364
                                                          &column_info->typiofunc,
 
365
                                                          &column_info->typioparam,
 
366
                                                          &typIsVarlena);
 
367
                        fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
 
368
                                                  fcinfo->flinfo->fn_mcxt);
 
369
                        column_info->column_type = column_type;
 
370
                }
 
371
 
 
372
                value = DatumGetCString(FunctionCall3(&column_info->proc,
 
373
                                                                                          values[i],
 
374
                                                           ObjectIdGetDatum(column_info->typioparam),
 
375
                                                   Int32GetDatum(tupdesc->attrs[i]->atttypmod)));
 
376
 
 
377
                /* Detect whether we need double quotes for this value */
 
378
                nq = (value[0] == '\0');        /* force quotes for empty string */
 
379
                for (tmp = value; *tmp; tmp++)
 
380
                {
 
381
                        char            ch = *tmp;
 
382
 
 
383
                        if (ch == '"' || ch == '\\' ||
 
384
                                ch == '(' || ch == ')' || ch == ',' ||
 
385
                                isspace((unsigned char) ch))
 
386
                        {
 
387
                                nq = true;
 
388
                                break;
 
389
                        }
 
390
                }
 
391
 
 
392
                /* And emit the string */
 
393
                if (nq)
 
394
                        appendStringInfoChar(&buf, '"');
 
395
                for (tmp = value; *tmp; tmp++)
 
396
                {
 
397
                        char            ch = *tmp;
 
398
 
 
399
                        if (ch == '"' || ch == '\\')
 
400
                                appendStringInfoChar(&buf, ch);
 
401
                        appendStringInfoChar(&buf, ch);
 
402
                }
 
403
                if (nq)
 
404
                        appendStringInfoChar(&buf, '"');
 
405
        }
 
406
 
 
407
        appendStringInfoChar(&buf, ')');
 
408
 
 
409
        pfree(values);
 
410
        pfree(nulls);
 
411
 
 
412
        PG_RETURN_CSTRING(buf.data);
 
413
}
 
414
 
 
415
/*
 
416
 * record_recv          - binary input routine for any composite type.
 
417
 */
 
418
Datum
 
419
record_recv(PG_FUNCTION_ARGS)
 
420
{
 
421
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
 
422
        Oid                     tupType = PG_GETARG_OID(1);
 
423
        HeapTupleHeader result;
 
424
        int32           tupTypmod;
 
425
        TupleDesc       tupdesc;
 
426
        HeapTuple       tuple;
 
427
        RecordIOData *my_extra;
 
428
        int                     ncolumns;
 
429
        int                     usercols;
 
430
        int                     validcols;
 
431
        int                     i;
 
432
        Datum      *values;
 
433
        char       *nulls;
 
434
 
 
435
        /*
 
436
         * Use the passed type unless it's RECORD; we can't support input of
 
437
         * anonymous types, mainly because there's no good way to figure out
 
438
         * which anonymous type is wanted.      Note that for RECORD, what we'll
 
439
         * probably actually get is RECORD's typelem, ie, zero.
 
440
         */
 
441
        if (tupType == InvalidOid || tupType == RECORDOID)
 
442
                ereport(ERROR,
 
443
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
444
                errmsg("input of anonymous composite types is not implemented")));
 
445
        tupTypmod = -1;                         /* for all non-anonymous types */
 
446
        tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
447
        ncolumns = tupdesc->natts;
 
448
 
 
449
        /*
 
450
         * We arrange to look up the needed I/O info just once per series of
 
451
         * calls, assuming the record type doesn't change underneath us.
 
452
         */
 
453
        my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
454
        if (my_extra == NULL ||
 
455
                my_extra->ncolumns != ncolumns)
 
456
        {
 
457
                fcinfo->flinfo->fn_extra =
 
458
                        MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
459
                                                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
460
                                                           + ncolumns * sizeof(ColumnIOData));
 
461
                my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
462
                my_extra->record_type = InvalidOid;
 
463
                my_extra->record_typmod = 0;
 
464
        }
 
465
 
 
466
        if (my_extra->record_type != tupType ||
 
467
                my_extra->record_typmod != tupTypmod)
 
468
        {
 
469
                MemSet(my_extra, 0,
 
470
                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
471
                           + ncolumns * sizeof(ColumnIOData));
 
472
                my_extra->record_type = tupType;
 
473
                my_extra->record_typmod = tupTypmod;
 
474
                my_extra->ncolumns = ncolumns;
 
475
        }
 
476
 
 
477
        values = (Datum *) palloc(ncolumns * sizeof(Datum));
 
478
        nulls = (char *) palloc(ncolumns * sizeof(char));
 
479
 
 
480
        /* Fetch number of columns user thinks it has */
 
481
        usercols = pq_getmsgint(buf, 4);
 
482
 
 
483
        /* Need to scan to count nondeleted columns */
 
484
        validcols = 0;
 
485
        for (i = 0; i < ncolumns; i++)
 
486
        {
 
487
                if (!tupdesc->attrs[i]->attisdropped)
 
488
                        validcols++;
 
489
        }
 
490
        if (usercols != validcols)
 
491
                ereport(ERROR,
 
492
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
 
493
                                 errmsg("wrong number of columns: %d, expected %d",
 
494
                                                usercols, validcols)));
 
495
 
 
496
        /* Process each column */
 
497
        for (i = 0; i < ncolumns; i++)
 
498
        {
 
499
                ColumnIOData *column_info = &my_extra->columns[i];
 
500
                Oid                     column_type = tupdesc->attrs[i]->atttypid;
 
501
                Oid                     coltypoid;
 
502
                int                     itemlen;
 
503
 
 
504
                /* Ignore dropped columns in datatype, but fill with nulls */
 
505
                if (tupdesc->attrs[i]->attisdropped)
 
506
                {
 
507
                        values[i] = (Datum) 0;
 
508
                        nulls[i] = 'n';
 
509
                        continue;
 
510
                }
 
511
 
 
512
                /* Verify column datatype */
 
513
                coltypoid = pq_getmsgint(buf, sizeof(Oid));
 
514
                if (coltypoid != column_type)
 
515
                        ereport(ERROR,
 
516
                                        (errcode(ERRCODE_DATATYPE_MISMATCH),
 
517
                                         errmsg("wrong data type: %u, expected %u",
 
518
                                                        coltypoid, column_type)));
 
519
 
 
520
                /* Get and check the item length */
 
521
                itemlen = pq_getmsgint(buf, 4);
 
522
                if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
 
523
                        ereport(ERROR,
 
524
                                        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
525
                                         errmsg("insufficient data left in message")));
 
526
 
 
527
                if (itemlen == -1)
 
528
                {
 
529
                        /* -1 length means NULL */
 
530
                        values[i] = (Datum) 0;
 
531
                        nulls[i] = 'n';
 
532
                }
 
533
                else
 
534
                {
 
535
                        /*
 
536
                         * Rather than copying data around, we just set up a phony
 
537
                         * StringInfo pointing to the correct portion of the input
 
538
                         * buffer. We assume we can scribble on the input buffer so as
 
539
                         * to maintain the convention that StringInfos have a trailing
 
540
                         * null.
 
541
                         */
 
542
                        StringInfoData item_buf;
 
543
                        char            csave;
 
544
 
 
545
                        item_buf.data = &buf->data[buf->cursor];
 
546
                        item_buf.maxlen = itemlen + 1;
 
547
                        item_buf.len = itemlen;
 
548
                        item_buf.cursor = 0;
 
549
 
 
550
                        buf->cursor += itemlen;
 
551
 
 
552
                        csave = buf->data[buf->cursor];
 
553
                        buf->data[buf->cursor] = '\0';
 
554
 
 
555
                        /* Now call the column's receiveproc */
 
556
                        if (column_info->column_type != column_type)
 
557
                        {
 
558
                                getTypeBinaryInputInfo(column_type,
 
559
                                                                           &column_info->typiofunc,
 
560
                                                                           &column_info->typioparam);
 
561
                                fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
 
562
                                                          fcinfo->flinfo->fn_mcxt);
 
563
                                column_info->column_type = column_type;
 
564
                        }
 
565
 
 
566
                        values[i] = FunctionCall2(&column_info->proc,
 
567
                                                                          PointerGetDatum(&item_buf),
 
568
                                                          ObjectIdGetDatum(column_info->typioparam));
 
569
 
 
570
                        nulls[i] = ' ';
 
571
 
 
572
                        /* Trouble if it didn't eat the whole buffer */
 
573
                        if (item_buf.cursor != itemlen)
 
574
                                ereport(ERROR,
 
575
                                                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
576
                                         errmsg("improper binary format in record column %d",
 
577
                                                        i + 1)));
 
578
 
 
579
                        buf->data[buf->cursor] = csave;
 
580
                }
 
581
        }
 
582
 
 
583
        tuple = heap_formtuple(tupdesc, values, nulls);
 
584
 
 
585
        /*
 
586
         * We cannot return tuple->t_data because heap_formtuple allocates it
 
587
         * as part of a larger chunk, and our caller may expect to be able to
 
588
         * pfree our result.  So must copy the info into a new palloc chunk.
 
589
         */
 
590
        result = (HeapTupleHeader) palloc(tuple->t_len);
 
591
        memcpy(result, tuple->t_data, tuple->t_len);
 
592
 
 
593
        heap_freetuple(tuple);
 
594
        pfree(values);
 
595
        pfree(nulls);
 
596
 
 
597
        PG_RETURN_HEAPTUPLEHEADER(result);
 
598
}
 
599
 
 
600
/*
 
601
 * record_send          - binary output routine for any composite type.
 
602
 */
 
603
Datum
 
604
record_send(PG_FUNCTION_ARGS)
 
605
{
 
606
        HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
 
607
        Oid                     tupType;
 
608
        int32           tupTypmod;
 
609
        TupleDesc       tupdesc;
 
610
        HeapTupleData tuple;
 
611
        RecordIOData *my_extra;
 
612
        int                     ncolumns;
 
613
        int                     validcols;
 
614
        int                     i;
 
615
        Datum      *values;
 
616
        char       *nulls;
 
617
        StringInfoData buf;
 
618
 
 
619
        /* Extract type info from the tuple itself */
 
620
        tupType = HeapTupleHeaderGetTypeId(rec);
 
621
        tupTypmod = HeapTupleHeaderGetTypMod(rec);
 
622
        tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
 
623
        ncolumns = tupdesc->natts;
 
624
 
 
625
        /* Build a temporary HeapTuple control structure */
 
626
        tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
 
627
        ItemPointerSetInvalid(&(tuple.t_self));
 
628
        tuple.t_tableOid = InvalidOid;
 
629
        tuple.t_data = rec;
 
630
 
 
631
        /*
 
632
         * We arrange to look up the needed I/O info just once per series of
 
633
         * calls, assuming the record type doesn't change underneath us.
 
634
         */
 
635
        my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
636
        if (my_extra == NULL ||
 
637
                my_extra->ncolumns != ncolumns)
 
638
        {
 
639
                fcinfo->flinfo->fn_extra =
 
640
                        MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
641
                                                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
642
                                                           + ncolumns * sizeof(ColumnIOData));
 
643
                my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
 
644
                my_extra->record_type = InvalidOid;
 
645
                my_extra->record_typmod = 0;
 
646
        }
 
647
 
 
648
        if (my_extra->record_type != tupType ||
 
649
                my_extra->record_typmod != tupTypmod)
 
650
        {
 
651
                MemSet(my_extra, 0,
 
652
                           sizeof(RecordIOData) - sizeof(ColumnIOData)
 
653
                           + ncolumns * sizeof(ColumnIOData));
 
654
                my_extra->record_type = tupType;
 
655
                my_extra->record_typmod = tupTypmod;
 
656
                my_extra->ncolumns = ncolumns;
 
657
        }
 
658
 
 
659
        values = (Datum *) palloc(ncolumns * sizeof(Datum));
 
660
        nulls = (char *) palloc(ncolumns * sizeof(char));
 
661
 
 
662
        /* Break down the tuple into fields */
 
663
        heap_deformtuple(&tuple, tupdesc, values, nulls);
 
664
 
 
665
        /* And build the result string */
 
666
        pq_begintypsend(&buf);
 
667
 
 
668
        /* Need to scan to count nondeleted columns */
 
669
        validcols = 0;
 
670
        for (i = 0; i < ncolumns; i++)
 
671
        {
 
672
                if (!tupdesc->attrs[i]->attisdropped)
 
673
                        validcols++;
 
674
        }
 
675
        pq_sendint(&buf, validcols, 4);
 
676
 
 
677
        for (i = 0; i < ncolumns; i++)
 
678
        {
 
679
                ColumnIOData *column_info = &my_extra->columns[i];
 
680
                Oid                     column_type = tupdesc->attrs[i]->atttypid;
 
681
                bytea      *outputbytes;
 
682
 
 
683
                /* Ignore dropped columns in datatype */
 
684
                if (tupdesc->attrs[i]->attisdropped)
 
685
                        continue;
 
686
 
 
687
                pq_sendint(&buf, column_type, sizeof(Oid));
 
688
 
 
689
                if (nulls[i] == 'n')
 
690
                {
 
691
                        /* emit -1 data length to signify a NULL */
 
692
                        pq_sendint(&buf, -1, 4);
 
693
                        continue;
 
694
                }
 
695
 
 
696
                /*
 
697
                 * Convert the column value to binary
 
698
                 */
 
699
                if (column_info->column_type != column_type)
 
700
                {
 
701
                        bool            typIsVarlena;
 
702
 
 
703
                        getTypeBinaryOutputInfo(column_type,
 
704
                                                                        &column_info->typiofunc,
 
705
                                                                        &column_info->typioparam,
 
706
                                                                        &typIsVarlena);
 
707
                        fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
 
708
                                                  fcinfo->flinfo->fn_mcxt);
 
709
                        column_info->column_type = column_type;
 
710
                }
 
711
 
 
712
                outputbytes = DatumGetByteaP(FunctionCall2(&column_info->proc,
 
713
                                                                                                   values[i],
 
714
                                                         ObjectIdGetDatum(column_info->typioparam)));
 
715
 
 
716
                /* We assume the result will not have been toasted */
 
717
                pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
 
718
                pq_sendbytes(&buf, VARDATA(outputbytes),
 
719
                                         VARSIZE(outputbytes) - VARHDRSZ);
 
720
                pfree(outputbytes);
 
721
        }
 
722
 
 
723
        pfree(values);
 
724
        pfree(nulls);
 
725
 
 
726
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 
727
}