00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "postgres.h"
00016
00017 #include <ctype.h>
00018
00019 #include "access/htup_details.h"
00020 #include "catalog/pg_type.h"
00021 #include "libpq/pqformat.h"
00022 #include "utils/builtins.h"
00023 #include "utils/lsyscache.h"
00024 #include "utils/typcache.h"
00025
00026
00027
00028
00029
00030 typedef struct ColumnIOData
00031 {
00032 Oid column_type;
00033 Oid typiofunc;
00034 Oid typioparam;
00035 bool typisvarlena;
00036 FmgrInfo proc;
00037 } ColumnIOData;
00038
00039 typedef struct RecordIOData
00040 {
00041 Oid record_type;
00042 int32 record_typmod;
00043 int ncolumns;
00044 ColumnIOData columns[1];
00045 } RecordIOData;
00046
00047
00048
00049
00050 typedef struct ColumnCompareData
00051 {
00052 TypeCacheEntry *typentry;
00053 } ColumnCompareData;
00054
00055 typedef struct RecordCompareData
00056 {
00057 int ncolumns;
00058 Oid record1_type;
00059 int32 record1_typmod;
00060 Oid record2_type;
00061 int32 record2_typmod;
00062 ColumnCompareData columns[1];
00063 } RecordCompareData;
00064
00065
00066
00067
00068
00069 Datum
00070 record_in(PG_FUNCTION_ARGS)
00071 {
00072 char *string = PG_GETARG_CSTRING(0);
00073 Oid tupType = PG_GETARG_OID(1);
00074
00075 #ifdef NOT_USED
00076 int32 typmod = PG_GETARG_INT32(2);
00077 #endif
00078 HeapTupleHeader result;
00079 int32 tupTypmod;
00080 TupleDesc tupdesc;
00081 HeapTuple tuple;
00082 RecordIOData *my_extra;
00083 bool needComma = false;
00084 int ncolumns;
00085 int i;
00086 char *ptr;
00087 Datum *values;
00088 bool *nulls;
00089 StringInfoData buf;
00090
00091
00092
00093
00094
00095
00096
00097 if (tupType == InvalidOid || tupType == RECORDOID)
00098 ereport(ERROR,
00099 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00100 errmsg("input of anonymous composite types is not implemented")));
00101 tupTypmod = -1;
00102
00103
00104
00105
00106
00107
00108 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
00109 ncolumns = tupdesc->natts;
00110
00111
00112
00113
00114
00115 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00116 if (my_extra == NULL ||
00117 my_extra->ncolumns != ncolumns)
00118 {
00119 fcinfo->flinfo->fn_extra =
00120 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00121 sizeof(RecordIOData) - sizeof(ColumnIOData)
00122 + ncolumns * sizeof(ColumnIOData));
00123 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00124 my_extra->record_type = InvalidOid;
00125 my_extra->record_typmod = 0;
00126 }
00127
00128 if (my_extra->record_type != tupType ||
00129 my_extra->record_typmod != tupTypmod)
00130 {
00131 MemSet(my_extra, 0,
00132 sizeof(RecordIOData) - sizeof(ColumnIOData)
00133 + ncolumns * sizeof(ColumnIOData));
00134 my_extra->record_type = tupType;
00135 my_extra->record_typmod = tupTypmod;
00136 my_extra->ncolumns = ncolumns;
00137 }
00138
00139 values = (Datum *) palloc(ncolumns * sizeof(Datum));
00140 nulls = (bool *) palloc(ncolumns * sizeof(bool));
00141
00142
00143
00144
00145
00146 ptr = string;
00147
00148 while (*ptr && isspace((unsigned char) *ptr))
00149 ptr++;
00150 if (*ptr++ != '(')
00151 ereport(ERROR,
00152 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00153 errmsg("malformed record literal: \"%s\"", string),
00154 errdetail("Missing left parenthesis.")));
00155
00156 initStringInfo(&buf);
00157
00158 for (i = 0; i < ncolumns; i++)
00159 {
00160 ColumnIOData *column_info = &my_extra->columns[i];
00161 Oid column_type = tupdesc->attrs[i]->atttypid;
00162 char *column_data;
00163
00164
00165 if (tupdesc->attrs[i]->attisdropped)
00166 {
00167 values[i] = (Datum) 0;
00168 nulls[i] = true;
00169 continue;
00170 }
00171
00172 if (needComma)
00173 {
00174
00175 if (*ptr == ',')
00176 ptr++;
00177 else
00178
00179 ereport(ERROR,
00180 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00181 errmsg("malformed record literal: \"%s\"", string),
00182 errdetail("Too few columns.")));
00183 }
00184
00185
00186 if (*ptr == ',' || *ptr == ')')
00187 {
00188 column_data = NULL;
00189 nulls[i] = true;
00190 }
00191 else
00192 {
00193
00194 bool inquote = false;
00195
00196 resetStringInfo(&buf);
00197 while (inquote || !(*ptr == ',' || *ptr == ')'))
00198 {
00199 char ch = *ptr++;
00200
00201 if (ch == '\0')
00202 ereport(ERROR,
00203 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00204 errmsg("malformed record literal: \"%s\"",
00205 string),
00206 errdetail("Unexpected end of input.")));
00207 if (ch == '\\')
00208 {
00209 if (*ptr == '\0')
00210 ereport(ERROR,
00211 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00212 errmsg("malformed record literal: \"%s\"",
00213 string),
00214 errdetail("Unexpected end of input.")));
00215 appendStringInfoChar(&buf, *ptr++);
00216 }
00217 else if (ch == '\"')
00218 {
00219 if (!inquote)
00220 inquote = true;
00221 else if (*ptr == '\"')
00222 {
00223
00224 appendStringInfoChar(&buf, *ptr++);
00225 }
00226 else
00227 inquote = false;
00228 }
00229 else
00230 appendStringInfoChar(&buf, ch);
00231 }
00232
00233 column_data = buf.data;
00234 nulls[i] = false;
00235 }
00236
00237
00238
00239
00240 if (column_info->column_type != column_type)
00241 {
00242 getTypeInputInfo(column_type,
00243 &column_info->typiofunc,
00244 &column_info->typioparam);
00245 fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
00246 fcinfo->flinfo->fn_mcxt);
00247 column_info->column_type = column_type;
00248 }
00249
00250 values[i] = InputFunctionCall(&column_info->proc,
00251 column_data,
00252 column_info->typioparam,
00253 tupdesc->attrs[i]->atttypmod);
00254
00255
00256
00257
00258 needComma = true;
00259 }
00260
00261 if (*ptr++ != ')')
00262 ereport(ERROR,
00263 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00264 errmsg("malformed record literal: \"%s\"", string),
00265 errdetail("Too many columns.")));
00266
00267 while (*ptr && isspace((unsigned char) *ptr))
00268 ptr++;
00269 if (*ptr)
00270 ereport(ERROR,
00271 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
00272 errmsg("malformed record literal: \"%s\"", string),
00273 errdetail("Junk after right parenthesis.")));
00274
00275 tuple = heap_form_tuple(tupdesc, values, nulls);
00276
00277
00278
00279
00280
00281
00282 result = (HeapTupleHeader) palloc(tuple->t_len);
00283 memcpy(result, tuple->t_data, tuple->t_len);
00284
00285 heap_freetuple(tuple);
00286 pfree(buf.data);
00287 pfree(values);
00288 pfree(nulls);
00289 ReleaseTupleDesc(tupdesc);
00290
00291 PG_RETURN_HEAPTUPLEHEADER(result);
00292 }
00293
00294
00295
00296
00297 Datum
00298 record_out(PG_FUNCTION_ARGS)
00299 {
00300 HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
00301 Oid tupType;
00302 int32 tupTypmod;
00303 TupleDesc tupdesc;
00304 HeapTupleData tuple;
00305 RecordIOData *my_extra;
00306 bool needComma = false;
00307 int ncolumns;
00308 int i;
00309 Datum *values;
00310 bool *nulls;
00311 StringInfoData buf;
00312
00313
00314 tupType = HeapTupleHeaderGetTypeId(rec);
00315 tupTypmod = HeapTupleHeaderGetTypMod(rec);
00316 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
00317 ncolumns = tupdesc->natts;
00318
00319
00320 tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
00321 ItemPointerSetInvalid(&(tuple.t_self));
00322 tuple.t_tableOid = InvalidOid;
00323 tuple.t_data = rec;
00324
00325
00326
00327
00328
00329 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00330 if (my_extra == NULL ||
00331 my_extra->ncolumns != ncolumns)
00332 {
00333 fcinfo->flinfo->fn_extra =
00334 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00335 sizeof(RecordIOData) - sizeof(ColumnIOData)
00336 + ncolumns * sizeof(ColumnIOData));
00337 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00338 my_extra->record_type = InvalidOid;
00339 my_extra->record_typmod = 0;
00340 }
00341
00342 if (my_extra->record_type != tupType ||
00343 my_extra->record_typmod != tupTypmod)
00344 {
00345 MemSet(my_extra, 0,
00346 sizeof(RecordIOData) - sizeof(ColumnIOData)
00347 + ncolumns * sizeof(ColumnIOData));
00348 my_extra->record_type = tupType;
00349 my_extra->record_typmod = tupTypmod;
00350 my_extra->ncolumns = ncolumns;
00351 }
00352
00353 values = (Datum *) palloc(ncolumns * sizeof(Datum));
00354 nulls = (bool *) palloc(ncolumns * sizeof(bool));
00355
00356
00357 heap_deform_tuple(&tuple, tupdesc, values, nulls);
00358
00359
00360 initStringInfo(&buf);
00361
00362 appendStringInfoChar(&buf, '(');
00363
00364 for (i = 0; i < ncolumns; i++)
00365 {
00366 ColumnIOData *column_info = &my_extra->columns[i];
00367 Oid column_type = tupdesc->attrs[i]->atttypid;
00368 Datum attr;
00369 char *value;
00370 char *tmp;
00371 bool nq;
00372
00373
00374 if (tupdesc->attrs[i]->attisdropped)
00375 continue;
00376
00377 if (needComma)
00378 appendStringInfoChar(&buf, ',');
00379 needComma = true;
00380
00381 if (nulls[i])
00382 {
00383
00384 continue;
00385 }
00386
00387
00388
00389
00390 if (column_info->column_type != column_type)
00391 {
00392 getTypeOutputInfo(column_type,
00393 &column_info->typiofunc,
00394 &column_info->typisvarlena);
00395 fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
00396 fcinfo->flinfo->fn_mcxt);
00397 column_info->column_type = column_type;
00398 }
00399
00400
00401
00402
00403
00404 if (column_info->typisvarlena)
00405 attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
00406 else
00407 attr = values[i];
00408
00409 value = OutputFunctionCall(&column_info->proc, attr);
00410
00411
00412 nq = (value[0] == '\0');
00413 for (tmp = value; *tmp; tmp++)
00414 {
00415 char ch = *tmp;
00416
00417 if (ch == '"' || ch == '\\' ||
00418 ch == '(' || ch == ')' || ch == ',' ||
00419 isspace((unsigned char) ch))
00420 {
00421 nq = true;
00422 break;
00423 }
00424 }
00425
00426
00427 if (nq)
00428 appendStringInfoCharMacro(&buf, '"');
00429 for (tmp = value; *tmp; tmp++)
00430 {
00431 char ch = *tmp;
00432
00433 if (ch == '"' || ch == '\\')
00434 appendStringInfoCharMacro(&buf, ch);
00435 appendStringInfoCharMacro(&buf, ch);
00436 }
00437 if (nq)
00438 appendStringInfoCharMacro(&buf, '"');
00439
00440 pfree(value);
00441
00442
00443 if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
00444 pfree(DatumGetPointer(attr));
00445 }
00446
00447 appendStringInfoChar(&buf, ')');
00448
00449 pfree(values);
00450 pfree(nulls);
00451 ReleaseTupleDesc(tupdesc);
00452
00453 PG_RETURN_CSTRING(buf.data);
00454 }
00455
00456
00457
00458
00459 Datum
00460 record_recv(PG_FUNCTION_ARGS)
00461 {
00462 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
00463 Oid tupType = PG_GETARG_OID(1);
00464
00465 #ifdef NOT_USED
00466 int32 typmod = PG_GETARG_INT32(2);
00467 #endif
00468 HeapTupleHeader result;
00469 int32 tupTypmod;
00470 TupleDesc tupdesc;
00471 HeapTuple tuple;
00472 RecordIOData *my_extra;
00473 int ncolumns;
00474 int usercols;
00475 int validcols;
00476 int i;
00477 Datum *values;
00478 bool *nulls;
00479
00480
00481
00482
00483
00484
00485
00486 if (tupType == InvalidOid || tupType == RECORDOID)
00487 ereport(ERROR,
00488 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00489 errmsg("input of anonymous composite types is not implemented")));
00490 tupTypmod = -1;
00491 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
00492 ncolumns = tupdesc->natts;
00493
00494
00495
00496
00497
00498 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00499 if (my_extra == NULL ||
00500 my_extra->ncolumns != ncolumns)
00501 {
00502 fcinfo->flinfo->fn_extra =
00503 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00504 sizeof(RecordIOData) - sizeof(ColumnIOData)
00505 + ncolumns * sizeof(ColumnIOData));
00506 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00507 my_extra->record_type = InvalidOid;
00508 my_extra->record_typmod = 0;
00509 }
00510
00511 if (my_extra->record_type != tupType ||
00512 my_extra->record_typmod != tupTypmod)
00513 {
00514 MemSet(my_extra, 0,
00515 sizeof(RecordIOData) - sizeof(ColumnIOData)
00516 + ncolumns * sizeof(ColumnIOData));
00517 my_extra->record_type = tupType;
00518 my_extra->record_typmod = tupTypmod;
00519 my_extra->ncolumns = ncolumns;
00520 }
00521
00522 values = (Datum *) palloc(ncolumns * sizeof(Datum));
00523 nulls = (bool *) palloc(ncolumns * sizeof(bool));
00524
00525
00526 usercols = pq_getmsgint(buf, 4);
00527
00528
00529 validcols = 0;
00530 for (i = 0; i < ncolumns; i++)
00531 {
00532 if (!tupdesc->attrs[i]->attisdropped)
00533 validcols++;
00534 }
00535 if (usercols != validcols)
00536 ereport(ERROR,
00537 (errcode(ERRCODE_DATATYPE_MISMATCH),
00538 errmsg("wrong number of columns: %d, expected %d",
00539 usercols, validcols)));
00540
00541
00542 for (i = 0; i < ncolumns; i++)
00543 {
00544 ColumnIOData *column_info = &my_extra->columns[i];
00545 Oid column_type = tupdesc->attrs[i]->atttypid;
00546 Oid coltypoid;
00547 int itemlen;
00548 StringInfoData item_buf;
00549 StringInfo bufptr;
00550 char csave;
00551
00552
00553 if (tupdesc->attrs[i]->attisdropped)
00554 {
00555 values[i] = (Datum) 0;
00556 nulls[i] = true;
00557 continue;
00558 }
00559
00560
00561 coltypoid = pq_getmsgint(buf, sizeof(Oid));
00562 if (coltypoid != column_type)
00563 ereport(ERROR,
00564 (errcode(ERRCODE_DATATYPE_MISMATCH),
00565 errmsg("wrong data type: %u, expected %u",
00566 coltypoid, column_type)));
00567
00568
00569 itemlen = pq_getmsgint(buf, 4);
00570 if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
00571 ereport(ERROR,
00572 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
00573 errmsg("insufficient data left in message")));
00574
00575 if (itemlen == -1)
00576 {
00577
00578 bufptr = NULL;
00579 nulls[i] = true;
00580 csave = 0;
00581 }
00582 else
00583 {
00584
00585
00586
00587
00588
00589
00590 item_buf.data = &buf->data[buf->cursor];
00591 item_buf.maxlen = itemlen + 1;
00592 item_buf.len = itemlen;
00593 item_buf.cursor = 0;
00594
00595 buf->cursor += itemlen;
00596
00597 csave = buf->data[buf->cursor];
00598 buf->data[buf->cursor] = '\0';
00599
00600 bufptr = &item_buf;
00601 nulls[i] = false;
00602 }
00603
00604
00605 if (column_info->column_type != column_type)
00606 {
00607 getTypeBinaryInputInfo(column_type,
00608 &column_info->typiofunc,
00609 &column_info->typioparam);
00610 fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
00611 fcinfo->flinfo->fn_mcxt);
00612 column_info->column_type = column_type;
00613 }
00614
00615 values[i] = ReceiveFunctionCall(&column_info->proc,
00616 bufptr,
00617 column_info->typioparam,
00618 tupdesc->attrs[i]->atttypmod);
00619
00620 if (bufptr)
00621 {
00622
00623 if (item_buf.cursor != itemlen)
00624 ereport(ERROR,
00625 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
00626 errmsg("improper binary format in record column %d",
00627 i + 1)));
00628
00629 buf->data[buf->cursor] = csave;
00630 }
00631 }
00632
00633 tuple = heap_form_tuple(tupdesc, values, nulls);
00634
00635
00636
00637
00638
00639
00640 result = (HeapTupleHeader) palloc(tuple->t_len);
00641 memcpy(result, tuple->t_data, tuple->t_len);
00642
00643 heap_freetuple(tuple);
00644 pfree(values);
00645 pfree(nulls);
00646 ReleaseTupleDesc(tupdesc);
00647
00648 PG_RETURN_HEAPTUPLEHEADER(result);
00649 }
00650
00651
00652
00653
00654 Datum
00655 record_send(PG_FUNCTION_ARGS)
00656 {
00657 HeapTupleHeader rec = PG_GETARG_HEAPTUPLEHEADER(0);
00658 Oid tupType;
00659 int32 tupTypmod;
00660 TupleDesc tupdesc;
00661 HeapTupleData tuple;
00662 RecordIOData *my_extra;
00663 int ncolumns;
00664 int validcols;
00665 int i;
00666 Datum *values;
00667 bool *nulls;
00668 StringInfoData buf;
00669
00670
00671 tupType = HeapTupleHeaderGetTypeId(rec);
00672 tupTypmod = HeapTupleHeaderGetTypMod(rec);
00673 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
00674 ncolumns = tupdesc->natts;
00675
00676
00677 tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
00678 ItemPointerSetInvalid(&(tuple.t_self));
00679 tuple.t_tableOid = InvalidOid;
00680 tuple.t_data = rec;
00681
00682
00683
00684
00685
00686 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00687 if (my_extra == NULL ||
00688 my_extra->ncolumns != ncolumns)
00689 {
00690 fcinfo->flinfo->fn_extra =
00691 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00692 sizeof(RecordIOData) - sizeof(ColumnIOData)
00693 + ncolumns * sizeof(ColumnIOData));
00694 my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
00695 my_extra->record_type = InvalidOid;
00696 my_extra->record_typmod = 0;
00697 }
00698
00699 if (my_extra->record_type != tupType ||
00700 my_extra->record_typmod != tupTypmod)
00701 {
00702 MemSet(my_extra, 0,
00703 sizeof(RecordIOData) - sizeof(ColumnIOData)
00704 + ncolumns * sizeof(ColumnIOData));
00705 my_extra->record_type = tupType;
00706 my_extra->record_typmod = tupTypmod;
00707 my_extra->ncolumns = ncolumns;
00708 }
00709
00710 values = (Datum *) palloc(ncolumns * sizeof(Datum));
00711 nulls = (bool *) palloc(ncolumns * sizeof(bool));
00712
00713
00714 heap_deform_tuple(&tuple, tupdesc, values, nulls);
00715
00716
00717 pq_begintypsend(&buf);
00718
00719
00720 validcols = 0;
00721 for (i = 0; i < ncolumns; i++)
00722 {
00723 if (!tupdesc->attrs[i]->attisdropped)
00724 validcols++;
00725 }
00726 pq_sendint(&buf, validcols, 4);
00727
00728 for (i = 0; i < ncolumns; i++)
00729 {
00730 ColumnIOData *column_info = &my_extra->columns[i];
00731 Oid column_type = tupdesc->attrs[i]->atttypid;
00732 Datum attr;
00733 bytea *outputbytes;
00734
00735
00736 if (tupdesc->attrs[i]->attisdropped)
00737 continue;
00738
00739 pq_sendint(&buf, column_type, sizeof(Oid));
00740
00741 if (nulls[i])
00742 {
00743
00744 pq_sendint(&buf, -1, 4);
00745 continue;
00746 }
00747
00748
00749
00750
00751 if (column_info->column_type != column_type)
00752 {
00753 getTypeBinaryOutputInfo(column_type,
00754 &column_info->typiofunc,
00755 &column_info->typisvarlena);
00756 fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
00757 fcinfo->flinfo->fn_mcxt);
00758 column_info->column_type = column_type;
00759 }
00760
00761
00762
00763
00764
00765 if (column_info->typisvarlena)
00766 attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
00767 else
00768 attr = values[i];
00769
00770 outputbytes = SendFunctionCall(&column_info->proc, attr);
00771
00772
00773 pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
00774 pq_sendbytes(&buf, VARDATA(outputbytes),
00775 VARSIZE(outputbytes) - VARHDRSZ);
00776
00777 pfree(outputbytes);
00778
00779
00780 if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
00781 pfree(DatumGetPointer(attr));
00782 }
00783
00784 pfree(values);
00785 pfree(nulls);
00786 ReleaseTupleDesc(tupdesc);
00787
00788 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
00789 }
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803 static int
00804 record_cmp(FunctionCallInfo fcinfo)
00805 {
00806 HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
00807 HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
00808 int result = 0;
00809 Oid tupType1;
00810 Oid tupType2;
00811 int32 tupTypmod1;
00812 int32 tupTypmod2;
00813 TupleDesc tupdesc1;
00814 TupleDesc tupdesc2;
00815 HeapTupleData tuple1;
00816 HeapTupleData tuple2;
00817 int ncolumns1;
00818 int ncolumns2;
00819 RecordCompareData *my_extra;
00820 int ncols;
00821 Datum *values1;
00822 Datum *values2;
00823 bool *nulls1;
00824 bool *nulls2;
00825 int i1;
00826 int i2;
00827 int j;
00828
00829
00830 tupType1 = HeapTupleHeaderGetTypeId(record1);
00831 tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
00832 tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
00833 ncolumns1 = tupdesc1->natts;
00834 tupType2 = HeapTupleHeaderGetTypeId(record2);
00835 tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
00836 tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
00837 ncolumns2 = tupdesc2->natts;
00838
00839
00840 tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
00841 ItemPointerSetInvalid(&(tuple1.t_self));
00842 tuple1.t_tableOid = InvalidOid;
00843 tuple1.t_data = record1;
00844 tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
00845 ItemPointerSetInvalid(&(tuple2.t_self));
00846 tuple2.t_tableOid = InvalidOid;
00847 tuple2.t_data = record2;
00848
00849
00850
00851
00852
00853 ncols = Max(ncolumns1, ncolumns2);
00854 my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
00855 if (my_extra == NULL ||
00856 my_extra->ncolumns < ncols)
00857 {
00858 fcinfo->flinfo->fn_extra =
00859 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
00860 sizeof(RecordCompareData) - sizeof(ColumnCompareData)
00861 + ncols * sizeof(ColumnCompareData));
00862 my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
00863 my_extra->ncolumns = ncols;
00864 my_extra->record1_type = InvalidOid;
00865 my_extra->record1_typmod = 0;
00866 my_extra->record2_type = InvalidOid;
00867 my_extra->record2_typmod = 0;
00868 }
00869
00870 if (my_extra->record1_type != tupType1 ||
00871 my_extra->record1_typmod != tupTypmod1 ||
00872 my_extra->record2_type != tupType2 ||
00873 my_extra->record2_typmod != tupTypmod2)
00874 {
00875 MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
00876 my_extra->record1_type = tupType1;
00877 my_extra->record1_typmod = tupTypmod1;
00878 my_extra->record2_type = tupType2;
00879 my_extra->record2_typmod = tupTypmod2;
00880 }
00881
00882
00883 values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
00884 nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
00885 heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
00886 values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
00887 nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
00888 heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
00889
00890
00891
00892
00893
00894
00895 i1 = i2 = j = 0;
00896 while (i1 < ncolumns1 || i2 < ncolumns2)
00897 {
00898 TypeCacheEntry *typentry;
00899 Oid collation;
00900 FunctionCallInfoData locfcinfo;
00901 int32 cmpresult;
00902
00903
00904
00905
00906 if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
00907 {
00908 i1++;
00909 continue;
00910 }
00911 if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
00912 {
00913 i2++;
00914 continue;
00915 }
00916 if (i1 >= ncolumns1 || i2 >= ncolumns2)
00917 break;
00918
00919
00920
00921
00922 if (tupdesc1->attrs[i1]->atttypid !=
00923 tupdesc2->attrs[i2]->atttypid)
00924 ereport(ERROR,
00925 (errcode(ERRCODE_DATATYPE_MISMATCH),
00926 errmsg("cannot compare dissimilar column types %s and %s at record column %d",
00927 format_type_be(tupdesc1->attrs[i1]->atttypid),
00928 format_type_be(tupdesc2->attrs[i2]->atttypid),
00929 j + 1)));
00930
00931
00932
00933
00934
00935 collation = tupdesc1->attrs[i1]->attcollation;
00936 if (collation != tupdesc2->attrs[i2]->attcollation)
00937 collation = InvalidOid;
00938
00939
00940
00941
00942 typentry = my_extra->columns[j].typentry;
00943 if (typentry == NULL ||
00944 typentry->type_id != tupdesc1->attrs[i1]->atttypid)
00945 {
00946 typentry = lookup_type_cache(tupdesc1->attrs[i1]->atttypid,
00947 TYPECACHE_CMP_PROC_FINFO);
00948 if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
00949 ereport(ERROR,
00950 (errcode(ERRCODE_UNDEFINED_FUNCTION),
00951 errmsg("could not identify a comparison function for type %s",
00952 format_type_be(typentry->type_id))));
00953 my_extra->columns[j].typentry = typentry;
00954 }
00955
00956
00957
00958
00959 if (!nulls1[i1] || !nulls2[i2])
00960 {
00961 if (nulls1[i1])
00962 {
00963
00964 result = 1;
00965 break;
00966 }
00967 if (nulls2[i2])
00968 {
00969
00970 result = -1;
00971 break;
00972 }
00973
00974
00975 InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
00976 collation, NULL, NULL);
00977 locfcinfo.arg[0] = values1[i1];
00978 locfcinfo.arg[1] = values2[i2];
00979 locfcinfo.argnull[0] = false;
00980 locfcinfo.argnull[1] = false;
00981 locfcinfo.isnull = false;
00982 cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
00983
00984 if (cmpresult < 0)
00985 {
00986
00987 result = -1;
00988 break;
00989 }
00990 else if (cmpresult > 0)
00991 {
00992
00993 result = 1;
00994 break;
00995 }
00996 }
00997
00998
00999 i1++, i2++, j++;
01000 }
01001
01002
01003
01004
01005
01006
01007 if (result == 0)
01008 {
01009 if (i1 != ncolumns1 || i2 != ncolumns2)
01010 ereport(ERROR,
01011 (errcode(ERRCODE_DATATYPE_MISMATCH),
01012 errmsg("cannot compare record types with different numbers of columns")));
01013 }
01014
01015 pfree(values1);
01016 pfree(nulls1);
01017 pfree(values2);
01018 pfree(nulls2);
01019 ReleaseTupleDesc(tupdesc1);
01020 ReleaseTupleDesc(tupdesc2);
01021
01022
01023 PG_FREE_IF_COPY(record1, 0);
01024 PG_FREE_IF_COPY(record2, 1);
01025
01026 return result;
01027 }
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038 Datum
01039 record_eq(PG_FUNCTION_ARGS)
01040 {
01041 HeapTupleHeader record1 = PG_GETARG_HEAPTUPLEHEADER(0);
01042 HeapTupleHeader record2 = PG_GETARG_HEAPTUPLEHEADER(1);
01043 bool result = true;
01044 Oid tupType1;
01045 Oid tupType2;
01046 int32 tupTypmod1;
01047 int32 tupTypmod2;
01048 TupleDesc tupdesc1;
01049 TupleDesc tupdesc2;
01050 HeapTupleData tuple1;
01051 HeapTupleData tuple2;
01052 int ncolumns1;
01053 int ncolumns2;
01054 RecordCompareData *my_extra;
01055 int ncols;
01056 Datum *values1;
01057 Datum *values2;
01058 bool *nulls1;
01059 bool *nulls2;
01060 int i1;
01061 int i2;
01062 int j;
01063
01064
01065 tupType1 = HeapTupleHeaderGetTypeId(record1);
01066 tupTypmod1 = HeapTupleHeaderGetTypMod(record1);
01067 tupdesc1 = lookup_rowtype_tupdesc(tupType1, tupTypmod1);
01068 ncolumns1 = tupdesc1->natts;
01069 tupType2 = HeapTupleHeaderGetTypeId(record2);
01070 tupTypmod2 = HeapTupleHeaderGetTypMod(record2);
01071 tupdesc2 = lookup_rowtype_tupdesc(tupType2, tupTypmod2);
01072 ncolumns2 = tupdesc2->natts;
01073
01074
01075 tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
01076 ItemPointerSetInvalid(&(tuple1.t_self));
01077 tuple1.t_tableOid = InvalidOid;
01078 tuple1.t_data = record1;
01079 tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
01080 ItemPointerSetInvalid(&(tuple2.t_self));
01081 tuple2.t_tableOid = InvalidOid;
01082 tuple2.t_data = record2;
01083
01084
01085
01086
01087
01088 ncols = Max(ncolumns1, ncolumns2);
01089 my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
01090 if (my_extra == NULL ||
01091 my_extra->ncolumns < ncols)
01092 {
01093 fcinfo->flinfo->fn_extra =
01094 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
01095 sizeof(RecordCompareData) - sizeof(ColumnCompareData)
01096 + ncols * sizeof(ColumnCompareData));
01097 my_extra = (RecordCompareData *) fcinfo->flinfo->fn_extra;
01098 my_extra->ncolumns = ncols;
01099 my_extra->record1_type = InvalidOid;
01100 my_extra->record1_typmod = 0;
01101 my_extra->record2_type = InvalidOid;
01102 my_extra->record2_typmod = 0;
01103 }
01104
01105 if (my_extra->record1_type != tupType1 ||
01106 my_extra->record1_typmod != tupTypmod1 ||
01107 my_extra->record2_type != tupType2 ||
01108 my_extra->record2_typmod != tupTypmod2)
01109 {
01110 MemSet(my_extra->columns, 0, ncols * sizeof(ColumnCompareData));
01111 my_extra->record1_type = tupType1;
01112 my_extra->record1_typmod = tupTypmod1;
01113 my_extra->record2_type = tupType2;
01114 my_extra->record2_typmod = tupTypmod2;
01115 }
01116
01117
01118 values1 = (Datum *) palloc(ncolumns1 * sizeof(Datum));
01119 nulls1 = (bool *) palloc(ncolumns1 * sizeof(bool));
01120 heap_deform_tuple(&tuple1, tupdesc1, values1, nulls1);
01121 values2 = (Datum *) palloc(ncolumns2 * sizeof(Datum));
01122 nulls2 = (bool *) palloc(ncolumns2 * sizeof(bool));
01123 heap_deform_tuple(&tuple2, tupdesc2, values2, nulls2);
01124
01125
01126
01127
01128
01129
01130 i1 = i2 = j = 0;
01131 while (i1 < ncolumns1 || i2 < ncolumns2)
01132 {
01133 TypeCacheEntry *typentry;
01134 Oid collation;
01135 FunctionCallInfoData locfcinfo;
01136 bool oprresult;
01137
01138
01139
01140
01141 if (i1 < ncolumns1 && tupdesc1->attrs[i1]->attisdropped)
01142 {
01143 i1++;
01144 continue;
01145 }
01146 if (i2 < ncolumns2 && tupdesc2->attrs[i2]->attisdropped)
01147 {
01148 i2++;
01149 continue;
01150 }
01151 if (i1 >= ncolumns1 || i2 >= ncolumns2)
01152 break;
01153
01154
01155
01156
01157 if (tupdesc1->attrs[i1]->atttypid !=
01158 tupdesc2->attrs[i2]->atttypid)
01159 ereport(ERROR,
01160 (errcode(ERRCODE_DATATYPE_MISMATCH),
01161 errmsg("cannot compare dissimilar column types %s and %s at record column %d",
01162 format_type_be(tupdesc1->attrs[i1]->atttypid),
01163 format_type_be(tupdesc2->attrs[i2]->atttypid),
01164 j + 1)));
01165
01166
01167
01168
01169
01170 collation = tupdesc1->attrs[i1]->attcollation;
01171 if (collation != tupdesc2->attrs[i2]->attcollation)
01172 collation = InvalidOid;
01173
01174
01175
01176
01177 typentry = my_extra->columns[j].typentry;
01178 if (typentry == NULL ||
01179 typentry->type_id != tupdesc1->attrs[i1]->atttypid)
01180 {
01181 typentry = lookup_type_cache(tupdesc1->attrs[i1]->atttypid,
01182 TYPECACHE_EQ_OPR_FINFO);
01183 if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
01184 ereport(ERROR,
01185 (errcode(ERRCODE_UNDEFINED_FUNCTION),
01186 errmsg("could not identify an equality operator for type %s",
01187 format_type_be(typentry->type_id))));
01188 my_extra->columns[j].typentry = typentry;
01189 }
01190
01191
01192
01193
01194 if (!nulls1[i1] || !nulls2[i2])
01195 {
01196 if (nulls1[i1] || nulls2[i2])
01197 {
01198 result = false;
01199 break;
01200 }
01201
01202
01203 InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
01204 collation, NULL, NULL);
01205 locfcinfo.arg[0] = values1[i1];
01206 locfcinfo.arg[1] = values2[i2];
01207 locfcinfo.argnull[0] = false;
01208 locfcinfo.argnull[1] = false;
01209 locfcinfo.isnull = false;
01210 oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
01211 if (!oprresult)
01212 {
01213 result = false;
01214 break;
01215 }
01216 }
01217
01218
01219 i1++, i2++, j++;
01220 }
01221
01222
01223
01224
01225
01226
01227 if (result)
01228 {
01229 if (i1 != ncolumns1 || i2 != ncolumns2)
01230 ereport(ERROR,
01231 (errcode(ERRCODE_DATATYPE_MISMATCH),
01232 errmsg("cannot compare record types with different numbers of columns")));
01233 }
01234
01235 pfree(values1);
01236 pfree(nulls1);
01237 pfree(values2);
01238 pfree(nulls2);
01239 ReleaseTupleDesc(tupdesc1);
01240 ReleaseTupleDesc(tupdesc2);
01241
01242
01243 PG_FREE_IF_COPY(record1, 0);
01244 PG_FREE_IF_COPY(record2, 1);
01245
01246 PG_RETURN_BOOL(result);
01247 }
01248
01249 Datum
01250 record_ne(PG_FUNCTION_ARGS)
01251 {
01252 PG_RETURN_BOOL(!DatumGetBool(record_eq(fcinfo)));
01253 }
01254
01255 Datum
01256 record_lt(PG_FUNCTION_ARGS)
01257 {
01258 PG_RETURN_BOOL(record_cmp(fcinfo) < 0);
01259 }
01260
01261 Datum
01262 record_gt(PG_FUNCTION_ARGS)
01263 {
01264 PG_RETURN_BOOL(record_cmp(fcinfo) > 0);
01265 }
01266
01267 Datum
01268 record_le(PG_FUNCTION_ARGS)
01269 {
01270 PG_RETURN_BOOL(record_cmp(fcinfo) <= 0);
01271 }
01272
01273 Datum
01274 record_ge(PG_FUNCTION_ARGS)
01275 {
01276 PG_RETURN_BOOL(record_cmp(fcinfo) >= 0);
01277 }
01278
01279 Datum
01280 btrecordcmp(PG_FUNCTION_ARGS)
01281 {
01282 PG_RETURN_INT32(record_cmp(fcinfo));
01283 }