2 * In/Out definitions for tsvector type
4 * string of values, array of position lexem in string and it's length
5 * Teodor Sigaev <teodor@sigaev.ru>
9 #include "access/gist.h"
10 #include "access/itup.h"
11 #include "utils/builtins.h"
12 #include "storage/bufpage.h"
13 #include "executor/spi.h"
14 #include "commands/trigger.h"
15 #include "nodes/pg_list.h"
16 #include "catalog/namespace.h"
18 #include "utils/pg_locale.h"
20 #include <ctype.h> /* tolower */
26 PG_FUNCTION_INFO_V1(tsvector_in);
27 Datum tsvector_in(PG_FUNCTION_ARGS);
29 PG_FUNCTION_INFO_V1(tsvector_out);
30 Datum tsvector_out(PG_FUNCTION_ARGS);
32 PG_FUNCTION_INFO_V1(to_tsvector);
33 Datum to_tsvector(PG_FUNCTION_ARGS);
35 PG_FUNCTION_INFO_V1(to_tsvector_current);
36 Datum to_tsvector_current(PG_FUNCTION_ARGS);
38 PG_FUNCTION_INFO_V1(to_tsvector_name);
39 Datum to_tsvector_name(PG_FUNCTION_ARGS);
41 PG_FUNCTION_INFO_V1(tsearch2);
42 Datum tsearch2(PG_FUNCTION_ARGS);
44 PG_FUNCTION_INFO_V1(tsvector_length);
45 Datum tsvector_length(PG_FUNCTION_ARGS);
48 * in/out text index type
51 comparePos(const void *a, const void *b)
53 if (((WordEntryPos *) a)->pos == ((WordEntryPos *) b)->pos)
55 return (((WordEntryPos *) a)->pos > ((WordEntryPos *) b)->pos) ? 1 : -1;
59 uniquePos(WordEntryPos * a, int4 l)
68 qsort((void *) a, l, sizeof(WordEntryPos), comparePos);
73 if (ptr->pos != res->pos)
77 res->weight = ptr->weight;
78 if (res - a >= MAXNUMPOS - 1 || res->pos == MAXENTRYPOS - 1)
81 else if (ptr->weight > res->weight)
82 res->weight = ptr->weight;
88 static char *BufferStr;
90 compareentry(const void *a, const void *b)
92 if (((WordEntryIN *) a)->entry.len == ((WordEntryIN *) b)->entry.len)
95 &BufferStr[((WordEntryIN *) a)->entry.pos],
96 &BufferStr[((WordEntryIN *) b)->entry.pos],
97 ((WordEntryIN *) a)->entry.len);
99 return (((WordEntryIN *) a)->entry.len > ((WordEntryIN *) b)->entry.len) ? 1 : -1;
103 uniqueentry(WordEntryIN * a, int4 l, char *buf, int4 *outbuflen)
113 *(uint16 *) (a->pos) = uniquePos(&(a->pos[1]), *(uint16 *) (a->pos));
114 *outbuflen = SHORTALIGN(res->entry.len) + (*(uint16 *) (a->pos) + 1) * sizeof(WordEntryPos);
121 qsort((void *) a, l, sizeof(WordEntryIN), compareentry);
125 if (!(ptr->entry.len == res->entry.len &&
126 strncmp(&buf[ptr->entry.pos], &buf[res->entry.pos], res->entry.len) == 0))
128 if (res->entry.haspos)
130 *(uint16 *) (res->pos) = uniquePos(&(res->pos[1]), *(uint16 *) (res->pos));
131 *outbuflen += *(uint16 *) (res->pos) * sizeof(WordEntryPos);
133 *outbuflen += SHORTALIGN(res->entry.len);
135 memcpy(res, ptr, sizeof(WordEntryIN));
137 else if (ptr->entry.haspos)
139 if (res->entry.haspos)
141 int4 len = *(uint16 *) (ptr->pos) + 1 + *(uint16 *) (res->pos);
143 res->pos = (WordEntryPos *) repalloc(res->pos, len * sizeof(WordEntryPos));
144 memcpy(&(res->pos[*(uint16 *) (res->pos) + 1]),
145 &(ptr->pos[1]), *(uint16 *) (ptr->pos) * sizeof(WordEntryPos));
146 *(uint16 *) (res->pos) += *(uint16 *) (ptr->pos);
151 res->entry.haspos = 1;
157 if (res->entry.haspos)
159 *(uint16 *) (res->pos) = uniquePos(&(res->pos[1]), *(uint16 *) (res->pos));
160 *outbuflen += *(uint16 *) (res->pos) * sizeof(WordEntryPos);
162 *outbuflen += SHORTALIGN(res->entry.len);
168 #define WAITENDWORD 2
169 #define WAITNEXTCHAR 3
170 #define WAITENDCMPLX 4
171 #define WAITPOSINFO 5
173 #define WAITPOSDELIM 7
175 #define RESIZEPRSBUF \
177 if ( state->curpos - state->word + 1 >= state->len ) \
179 int4 clen = state->curpos - state->word; \
181 state->word = (char*)repalloc( (void*)state->word, state->len ); \
182 state->curpos = state->word + clen; \
187 gettoken_tsvector(TI_IN_STATE * state)
191 state->curpos = state->word;
192 state->state = WAITWORD;
197 if (state->state == WAITWORD)
199 if (*(state->prsbuf) == '\0')
201 else if (*(state->prsbuf) == '\'')
202 state->state = WAITENDCMPLX;
203 else if (*(state->prsbuf) == '\\')
205 state->state = WAITNEXTCHAR;
206 oldstate = WAITENDWORD;
208 else if (state->oprisdelim && ISOPERATOR(*(state->prsbuf)))
210 (errcode(ERRCODE_SYNTAX_ERROR),
211 errmsg("syntax error")));
212 else if (*(state->prsbuf) != ' ')
214 *(state->curpos) = *(state->prsbuf);
216 state->state = WAITENDWORD;
219 else if (state->state == WAITNEXTCHAR)
221 if (*(state->prsbuf) == '\0')
223 (errcode(ERRCODE_SYNTAX_ERROR),
224 errmsg("there is no escaped character")));
228 *(state->curpos) = *(state->prsbuf);
230 state->state = oldstate;
233 else if (state->state == WAITENDWORD)
235 if (*(state->prsbuf) == '\\')
237 state->state = WAITNEXTCHAR;
238 oldstate = WAITENDWORD;
240 else if (*(state->prsbuf) == ' ' || *(state->prsbuf) == '\0' ||
241 (state->oprisdelim && ISOPERATOR(*(state->prsbuf))))
244 if (state->curpos == state->word)
246 (errcode(ERRCODE_SYNTAX_ERROR),
247 errmsg("syntax error")));
248 *(state->curpos) = '\0';
251 else if (*(state->prsbuf) == ':')
253 if (state->curpos == state->word)
255 (errcode(ERRCODE_SYNTAX_ERROR),
256 errmsg("syntax error")));
257 *(state->curpos) = '\0';
258 if (state->oprisdelim)
261 state->state = INPOSINFO;
266 *(state->curpos) = *(state->prsbuf);
270 else if (state->state == WAITENDCMPLX)
272 if (*(state->prsbuf) == '\'')
275 *(state->curpos) = '\0';
276 if (state->curpos == state->word)
278 (errcode(ERRCODE_SYNTAX_ERROR),
279 errmsg("syntax error")));
280 if (state->oprisdelim)
286 state->state = WAITPOSINFO;
288 else if (*(state->prsbuf) == '\\')
290 state->state = WAITNEXTCHAR;
291 oldstate = WAITENDCMPLX;
293 else if (*(state->prsbuf) == '\0')
295 (errcode(ERRCODE_SYNTAX_ERROR),
296 errmsg("syntax error")));
300 *(state->curpos) = *(state->prsbuf);
304 else if (state->state == WAITPOSINFO)
306 if (*(state->prsbuf) == ':')
307 state->state = INPOSINFO;
311 else if (state->state == INPOSINFO)
313 if (isdigit(*(state->prsbuf)))
315 if (state->alen == 0)
318 state->pos = (WordEntryPos *) palloc(sizeof(WordEntryPos) * state->alen);
319 *(uint16 *) (state->pos) = 0;
321 else if (*(uint16 *) (state->pos) + 1 >= state->alen)
324 state->pos = (WordEntryPos *) repalloc(state->pos, sizeof(WordEntryPos) * state->alen);
326 (*(uint16 *) (state->pos))++;
327 state->pos[*(uint16 *) (state->pos)].pos = LIMITPOS(atoi(state->prsbuf));
328 if (state->pos[*(uint16 *) (state->pos)].pos == 0)
330 (errcode(ERRCODE_SYNTAX_ERROR),
331 errmsg("wrong position info")));
332 state->pos[*(uint16 *) (state->pos)].weight = 0;
333 state->state = WAITPOSDELIM;
337 (errcode(ERRCODE_SYNTAX_ERROR),
338 errmsg("syntax error")));
340 else if (state->state == WAITPOSDELIM)
342 if (*(state->prsbuf) == ',')
343 state->state = INPOSINFO;
344 else if (tolower(*(state->prsbuf)) == 'a' || *(state->prsbuf) == '*')
346 if (state->pos[*(uint16 *) (state->pos)].weight)
348 (errcode(ERRCODE_SYNTAX_ERROR),
349 errmsg("syntax error")));
350 state->pos[*(uint16 *) (state->pos)].weight = 3;
352 else if (tolower(*(state->prsbuf)) == 'b')
354 if (state->pos[*(uint16 *) (state->pos)].weight)
356 (errcode(ERRCODE_SYNTAX_ERROR),
357 errmsg("syntax error")));
358 state->pos[*(uint16 *) (state->pos)].weight = 2;
360 else if (tolower(*(state->prsbuf)) == 'c')
362 if (state->pos[*(uint16 *) (state->pos)].weight)
364 (errcode(ERRCODE_SYNTAX_ERROR),
365 errmsg("syntax error")));
366 state->pos[*(uint16 *) (state->pos)].weight = 1;
368 else if (tolower(*(state->prsbuf)) == 'd')
370 if (state->pos[*(uint16 *) (state->pos)].weight)
372 (errcode(ERRCODE_SYNTAX_ERROR),
373 errmsg("syntax error")));
374 state->pos[*(uint16 *) (state->pos)].weight = 0;
376 else if (isspace(*(state->prsbuf)) || *(state->prsbuf) == '\0')
378 else if (!isdigit(*(state->prsbuf)))
380 (errcode(ERRCODE_SYNTAX_ERROR),
381 errmsg("syntax error")));
385 elog(ERROR, "internal error");
393 tsvector_in(PG_FUNCTION_ARGS)
395 char *buf = PG_GETARG_CSTRING(0);
409 state.word = (char *) palloc(state.len);
410 state.oprisdelim = false;
412 arr = (WordEntryIN *) palloc(sizeof(WordEntryIN) * totallen);
413 cur = tmpbuf = (char *) palloc(buflen);
414 while (gettoken_tsvector(&state))
419 arr = (WordEntryIN *) repalloc((void *) arr, sizeof(WordEntryIN) * totallen);
421 while ((cur - tmpbuf) + (state.curpos - state.word) >= buflen)
423 int4 dist = cur - tmpbuf;
426 tmpbuf = (char *) repalloc((void *) tmpbuf, buflen);
429 if (state.curpos - state.word >= MAXSTRLEN)
431 (errcode(ERRCODE_SYNTAX_ERROR),
432 errmsg("word is too long")));
433 arr[len].entry.len = state.curpos - state.word;
434 if (cur - tmpbuf > MAXSTRPOS)
436 (errcode(ERRCODE_SYNTAX_ERROR),
437 errmsg("too long value")));
438 arr[len].entry.pos = cur - tmpbuf;
439 memcpy((void *) cur, (void *) state.word, arr[len].entry.len);
440 cur += arr[len].entry.len;
443 arr[len].entry.haspos = 1;
444 arr[len].pos = state.pos;
447 arr[len].entry.haspos = 0;
453 len = uniqueentry(arr, len, tmpbuf, &buflen);
456 totallen = CALCDATASIZE(len, buflen);
457 in = (tsvector *) palloc(totallen);
458 memset(in, 0, totallen);
463 for (i = 0; i < len; i++)
465 memcpy((void *) cur, (void *) &tmpbuf[arr[i].entry.pos], arr[i].entry.len);
466 arr[i].entry.pos = cur - STRPTR(in);
467 cur += SHORTALIGN(arr[i].entry.len);
468 if (arr[i].entry.haspos)
470 memcpy(cur, arr[i].pos, (*(uint16 *) arr[i].pos + 1) * sizeof(WordEntryPos));
471 cur += (*(uint16 *) arr[i].pos + 1) * sizeof(WordEntryPos);
474 memcpy(&(inarr[i]), &(arr[i].entry), sizeof(WordEntry));
478 PG_RETURN_POINTER(in);
482 tsvector_length(PG_FUNCTION_ARGS)
484 tsvector *in = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
487 PG_FREE_IF_COPY(in, 0);
488 PG_RETURN_INT32(ret);
492 tsvector_out(PG_FUNCTION_ARGS)
494 tsvector *out = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
500 WordEntry *ptr = ARRPTR(out);
504 lenbuf = out->size * 2 /* '' */ + out->size - 1 /* space */ + 2 /* \0 */ ;
505 for (i = 0; i < out->size; i++)
507 lenbuf += ptr[i].len * 2 /* for escape */ ;
509 lenbuf += 7 * POSDATALEN(out, &(ptr[i]));
512 curout = outbuf = (char *) palloc(lenbuf);
513 for (i = 0; i < out->size; i++)
515 curin = STRPTR(out) + ptr->pos;
524 int4 pos = curout - outbuf;
526 outbuf = (char *) repalloc((void *) outbuf, ++lenbuf);
527 curout = outbuf + pos;
530 *curout++ = *curin++;
533 if ((pp = POSDATALEN(out, ptr)) != 0)
538 wptr = POSDATAPTR(out, ptr);
541 sprintf(curout, "%d", wptr->pos);
542 curout = strchr(curout, '\0');
543 switch (wptr->weight)
567 outbuf[lenbuf - 1] = '\0';
568 PG_FREE_IF_COPY(out, 0);
569 PG_RETURN_POINTER(outbuf);
573 compareWORD(const void *a, const void *b)
575 if (((WORD *) a)->len == ((WORD *) b)->len)
583 return (((WORD *) a)->pos.pos > ((WORD *) b)->pos.pos) ? 1 : -1;
586 return (((WORD *) a)->len > ((WORD *) b)->len) ? 1 : -1;
590 uniqueWORD(WORD * a, int4 l)
598 tmppos = LIMITPOS(a->pos.pos);
600 a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
602 a->pos.apos[1] = tmppos;
609 qsort((void *) a, l, sizeof(WORD), compareWORD);
610 tmppos = LIMITPOS(a->pos.pos);
612 a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
614 a->pos.apos[1] = tmppos;
618 if (!(ptr->len == res->len &&
619 strncmp(ptr->word, res->word, res->len) == 0))
623 res->word = ptr->word;
624 tmppos = LIMITPOS(ptr->pos.pos);
626 res->pos.apos = (uint16 *) palloc(sizeof(uint16) * res->alen);
627 res->pos.apos[0] = 1;
628 res->pos.apos[1] = tmppos;
633 if (res->pos.apos[0] < MAXNUMPOS - 1 && res->pos.apos[res->pos.apos[0]] != MAXENTRYPOS - 1)
635 if (res->pos.apos[0] + 1 >= res->alen)
638 res->pos.apos = (uint16 *) repalloc(res->pos.apos, sizeof(uint16) * res->alen);
640 if ( res->pos.apos[0]==0 || res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos) ) {
641 res->pos.apos[res->pos.apos[0] + 1] = LIMITPOS(ptr->pos.pos);
653 * make value of tsvector
656 makevalue(PRSTEXT * prs)
667 prs->curwords = uniqueWORD(prs->words, prs->curwords);
668 for (i = 0; i < prs->curwords; i++)
670 lenstr += SHORTALIGN(prs->words[i].len);
672 if (prs->words[i].alen)
673 lenstr += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos);
676 totallen = CALCDATASIZE(prs->curwords, lenstr);
677 in = (tsvector *) palloc(totallen);
678 memset(in, 0, totallen);
680 in->size = prs->curwords;
683 cur = str = STRPTR(in);
684 for (i = 0; i < prs->curwords; i++)
686 ptr->len = prs->words[i].len;
687 if (cur - str > MAXSTRPOS)
689 (errcode(ERRCODE_SYNTAX_ERROR),
690 errmsg("value is too big")));
691 ptr->pos = cur - str;
692 memcpy((void *) cur, (void *) prs->words[i].word, prs->words[i].len);
693 pfree(prs->words[i].word);
694 cur += SHORTALIGN(prs->words[i].len);
695 if (prs->words[i].alen)
700 *(uint16 *) cur = prs->words[i].pos.apos[0];
701 wptr = POSDATAPTR(in, ptr);
702 for (j = 0; j < *(uint16 *) cur; j++)
705 wptr[j].pos = prs->words[i].pos.apos[j + 1];
707 cur += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos);
708 pfree(prs->words[i].pos.apos);
720 to_tsvector(PG_FUNCTION_ARGS)
722 text *in = PG_GETARG_TEXT_P(1);
724 tsvector *out = NULL;
725 TSCfgInfo *cfg = findcfg(PG_GETARG_INT32(0));
730 prs.words = (WORD *) palloc(sizeof(WORD) * prs.lenwords);
732 parsetext_v2(cfg, &prs, VARDATA(in), VARSIZE(in) - VARHDRSZ);
733 PG_FREE_IF_COPY(in, 1);
736 out = makevalue(&prs);
740 out = palloc(CALCDATASIZE(0, 0));
741 out->len = CALCDATASIZE(0, 0);
744 PG_RETURN_POINTER(out);
748 to_tsvector_name(PG_FUNCTION_ARGS)
750 text *cfg = PG_GETARG_TEXT_P(0);
751 Datum res = DirectFunctionCall3(
753 Int32GetDatum(name2id_cfg(cfg)),
758 PG_FREE_IF_COPY(cfg, 0);
759 PG_RETURN_DATUM(res);
763 to_tsvector_current(PG_FUNCTION_ARGS)
765 Datum res = DirectFunctionCall3(
767 Int32GetDatum(get_currcfg()),
772 PG_RETURN_DATUM(res);
776 findFunc(char *fname)
778 FuncCandidateList clist,
780 Oid funcid = InvalidOid;
781 List *names = makeList1(makeString(fname));
783 ptr = clist = FuncnameGetCandidates(names, 1);
791 if (ptr->args[0] == TEXTOID && funcid == InvalidOid)
805 tsearch2(PG_FUNCTION_ARGS)
807 TriggerData *trigdata;
810 HeapTuple rettuple = NULL;
811 TSCfgInfo *cfg = findcfg(get_currcfg());
815 Datum datum = (Datum) 0;
816 Oid funcoid = InvalidOid;
818 if (!CALLED_AS_TRIGGER(fcinfo))
820 elog(ERROR, "TSearch: Not fired by trigger manager");
822 trigdata = (TriggerData *) fcinfo->context;
823 if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
825 elog(ERROR, "TSearch: Can't process STATEMENT events");
826 if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
828 elog(ERROR, "TSearch: Must be fired BEFORE event");
830 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
831 rettuple = trigdata->tg_trigtuple;
832 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
833 rettuple = trigdata->tg_newtuple;
836 elog(ERROR, "TSearch: Unknown event");
838 trigger = trigdata->tg_trigger;
839 rel = trigdata->tg_relation;
841 if (trigger->tgnargs < 2)
843 elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)");
845 numidxattr = SPI_fnumber(rel->rd_att, trigger->tgargs[0]);
846 if (numidxattr == SPI_ERROR_NOATTRIBUTE)
848 (errcode(ERRCODE_UNDEFINED_COLUMN),
849 errmsg("tsvector column \"%s\" does not exist",
850 trigger->tgargs[0])));
855 prs.words = (WORD *) palloc(sizeof(WORD) * prs.lenwords);
857 /* find all words in indexable column */
858 for (i = 1; i < trigger->tgnargs; i++)
866 numattr = SPI_fnumber(rel->rd_att, trigger->tgargs[i]);
867 if (numattr == SPI_ERROR_NOATTRIBUTE)
869 funcoid = findFunc(trigger->tgargs[i]);
870 if (funcoid == InvalidOid)
872 (errcode(ERRCODE_UNDEFINED_COLUMN),
873 errmsg("could not find function or field \"%s\"",
874 trigger->tgargs[i])));
878 oidtype = SPI_gettypeid(rel->rd_att, numattr);
879 /* We assume char() and varchar() are binary-equivalent to text */
880 if (!(oidtype == TEXTOID ||
881 oidtype == VARCHAROID ||
882 oidtype == BPCHAROID))
884 elog(WARNING, "TSearch: '%s' is not of character type",
888 txt_toasted = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
892 if (funcoid != InvalidOid)
894 text *txttmp = (text *) DatumGetPointer(OidFunctionCall1(
896 PointerGetDatum(txt_toasted)
899 txt = (text *) DatumGetPointer(PG_DETOAST_DATUM(PointerGetDatum(txttmp)));
901 txt_toasted = PointerGetDatum(txt);
904 txt = (text *) DatumGetPointer(PG_DETOAST_DATUM(PointerGetDatum(txt_toasted)));
906 parsetext_v2(cfg, &prs, VARDATA(txt), VARSIZE(txt) - VARHDRSZ);
907 if (txt != (text *) DatumGetPointer(txt_toasted))
911 /* make tsvector value */
914 datum = PointerGetDatum(makevalue(&prs));
915 rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr,
917 pfree(DatumGetPointer(datum));
921 tsvector *out = palloc(CALCDATASIZE(0, 0));
923 out->len = CALCDATASIZE(0, 0);
925 datum = PointerGetDatum(out);
927 rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr,
931 if (rettuple == NULL)
933 elog(ERROR, "TSearch: %d returned by SPI_modifytuple", SPI_result);
935 return PointerGetDatum(rettuple);
939 silly_cmp_tsvector(const tsvector *a, const tsvector *b) {
940 if ( a->len < b->len )
942 else if ( a->len > b->len )
944 else if ( a->size < b->size )
946 else if ( a->size > b->size )
949 unsigned char *aptr=(unsigned char *)(a->data) + DATAHDRSIZE;
950 unsigned char *bptr=(unsigned char *)(b->data) + DATAHDRSIZE;
952 while( aptr - ( (unsigned char *)(a->data) ) < a->len ) {
953 if ( *aptr != *bptr )
954 return ( *aptr < *bptr ) ? -1 : 1;
961 PG_FUNCTION_INFO_V1(tsvector_cmp);
962 PG_FUNCTION_INFO_V1(tsvector_lt);
963 PG_FUNCTION_INFO_V1(tsvector_le);
964 PG_FUNCTION_INFO_V1(tsvector_eq);
965 PG_FUNCTION_INFO_V1(tsvector_ne);
966 PG_FUNCTION_INFO_V1(tsvector_ge);
967 PG_FUNCTION_INFO_V1(tsvector_gt);
968 Datum tsvector_cmp(PG_FUNCTION_ARGS);
969 Datum tsvector_lt(PG_FUNCTION_ARGS);
970 Datum tsvector_le(PG_FUNCTION_ARGS);
971 Datum tsvector_eq(PG_FUNCTION_ARGS);
972 Datum tsvector_ne(PG_FUNCTION_ARGS);
973 Datum tsvector_ge(PG_FUNCTION_ARGS);
974 Datum tsvector_gt(PG_FUNCTION_ARGS);
977 tsvector *a = (tsvector *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));\
978 tsvector *b = (tsvector *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));\
979 int res = silly_cmp_tsvector(a,b); \
980 PG_FREE_IF_COPY(a,0); \
981 PG_FREE_IF_COPY(b,1); \
984 tsvector_cmp(PG_FUNCTION_ARGS) {
986 PG_RETURN_INT32(res);
990 tsvector_lt(PG_FUNCTION_ARGS) {
992 PG_RETURN_BOOL((res < 0) ? true : false);
996 tsvector_le(PG_FUNCTION_ARGS) {
998 PG_RETURN_BOOL((res <= 0) ? true : false);
1002 tsvector_eq(PG_FUNCTION_ARGS) {
1004 PG_RETURN_BOOL((res == 0) ? true : false);
1008 tsvector_ge(PG_FUNCTION_ARGS) {
1010 PG_RETURN_BOOL((res >= 0) ? true : false);
1014 tsvector_gt(PG_FUNCTION_ARGS) {
1016 PG_RETURN_BOOL((res > 0) ? true : false);
1020 tsvector_ne(PG_FUNCTION_ARGS) {
1022 PG_RETURN_BOOL((res != 0) ? true : false);