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((unsigned char) *(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((unsigned char) *(state->prsbuf)) ||
377 *(state->prsbuf) == '\0')
379 else if (!isdigit((unsigned char) *(state->prsbuf)))
381 (errcode(ERRCODE_SYNTAX_ERROR),
382 errmsg("syntax error")));
386 elog(ERROR, "internal error");
394 tsvector_in(PG_FUNCTION_ARGS)
396 char *buf = PG_GETARG_CSTRING(0);
410 state.word = (char *) palloc(state.len);
411 state.oprisdelim = false;
413 arr = (WordEntryIN *) palloc(sizeof(WordEntryIN) * totallen);
414 cur = tmpbuf = (char *) palloc(buflen);
415 while (gettoken_tsvector(&state))
420 arr = (WordEntryIN *) repalloc((void *) arr, sizeof(WordEntryIN) * totallen);
422 while ((cur - tmpbuf) + (state.curpos - state.word) >= buflen)
424 int4 dist = cur - tmpbuf;
427 tmpbuf = (char *) repalloc((void *) tmpbuf, buflen);
430 if (state.curpos - state.word >= MAXSTRLEN)
432 (errcode(ERRCODE_SYNTAX_ERROR),
433 errmsg("word is too long")));
434 arr[len].entry.len = state.curpos - state.word;
435 if (cur - tmpbuf > MAXSTRPOS)
437 (errcode(ERRCODE_SYNTAX_ERROR),
438 errmsg("too long value")));
439 arr[len].entry.pos = cur - tmpbuf;
440 memcpy((void *) cur, (void *) state.word, arr[len].entry.len);
441 cur += arr[len].entry.len;
444 arr[len].entry.haspos = 1;
445 arr[len].pos = state.pos;
448 arr[len].entry.haspos = 0;
454 len = uniqueentry(arr, len, tmpbuf, &buflen);
457 totallen = CALCDATASIZE(len, buflen);
458 in = (tsvector *) palloc(totallen);
459 memset(in, 0, totallen);
464 for (i = 0; i < len; i++)
466 memcpy((void *) cur, (void *) &tmpbuf[arr[i].entry.pos], arr[i].entry.len);
467 arr[i].entry.pos = cur - STRPTR(in);
468 cur += SHORTALIGN(arr[i].entry.len);
469 if (arr[i].entry.haspos)
471 memcpy(cur, arr[i].pos, (*(uint16 *) arr[i].pos + 1) * sizeof(WordEntryPos));
472 cur += (*(uint16 *) arr[i].pos + 1) * sizeof(WordEntryPos);
475 memcpy(&(inarr[i]), &(arr[i].entry), sizeof(WordEntry));
479 PG_RETURN_POINTER(in);
483 tsvector_length(PG_FUNCTION_ARGS)
485 tsvector *in = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
488 PG_FREE_IF_COPY(in, 0);
489 PG_RETURN_INT32(ret);
493 tsvector_out(PG_FUNCTION_ARGS)
495 tsvector *out = (tsvector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
501 WordEntry *ptr = ARRPTR(out);
505 lenbuf = out->size * 2 /* '' */ + out->size - 1 /* space */ + 2 /* \0 */ ;
506 for (i = 0; i < out->size; i++)
508 lenbuf += ptr[i].len * 2 /* for escape */ ;
510 lenbuf += 7 * POSDATALEN(out, &(ptr[i]));
513 curout = outbuf = (char *) palloc(lenbuf);
514 for (i = 0; i < out->size; i++)
516 curin = STRPTR(out) + ptr->pos;
525 int4 pos = curout - outbuf;
527 outbuf = (char *) repalloc((void *) outbuf, ++lenbuf);
528 curout = outbuf + pos;
531 *curout++ = *curin++;
534 if ((pp = POSDATALEN(out, ptr)) != 0)
539 wptr = POSDATAPTR(out, ptr);
542 sprintf(curout, "%d", wptr->pos);
543 curout = strchr(curout, '\0');
544 switch (wptr->weight)
568 outbuf[lenbuf - 1] = '\0';
569 PG_FREE_IF_COPY(out, 0);
570 PG_RETURN_POINTER(outbuf);
574 compareWORD(const void *a, const void *b)
576 if (((WORD *) a)->len == ((WORD *) b)->len)
584 return (((WORD *) a)->pos.pos > ((WORD *) b)->pos.pos) ? 1 : -1;
587 return (((WORD *) a)->len > ((WORD *) b)->len) ? 1 : -1;
591 uniqueWORD(WORD * a, int4 l)
599 tmppos = LIMITPOS(a->pos.pos);
601 a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
603 a->pos.apos[1] = tmppos;
610 qsort((void *) a, l, sizeof(WORD), compareWORD);
611 tmppos = LIMITPOS(a->pos.pos);
613 a->pos.apos = (uint16 *) palloc(sizeof(uint16) * a->alen);
615 a->pos.apos[1] = tmppos;
619 if (!(ptr->len == res->len &&
620 strncmp(ptr->word, res->word, res->len) == 0))
624 res->word = ptr->word;
625 tmppos = LIMITPOS(ptr->pos.pos);
627 res->pos.apos = (uint16 *) palloc(sizeof(uint16) * res->alen);
628 res->pos.apos[0] = 1;
629 res->pos.apos[1] = tmppos;
634 if (res->pos.apos[0] < MAXNUMPOS - 1 && res->pos.apos[res->pos.apos[0]] != MAXENTRYPOS - 1)
636 if (res->pos.apos[0] + 1 >= res->alen)
639 res->pos.apos = (uint16 *) repalloc(res->pos.apos, sizeof(uint16) * res->alen);
641 if ( res->pos.apos[0]==0 || res->pos.apos[res->pos.apos[0]] != LIMITPOS(ptr->pos.pos) ) {
642 res->pos.apos[res->pos.apos[0] + 1] = LIMITPOS(ptr->pos.pos);
654 * make value of tsvector
657 makevalue(PRSTEXT * prs)
668 prs->curwords = uniqueWORD(prs->words, prs->curwords);
669 for (i = 0; i < prs->curwords; i++)
671 lenstr += SHORTALIGN(prs->words[i].len);
673 if (prs->words[i].alen)
674 lenstr += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos);
677 totallen = CALCDATASIZE(prs->curwords, lenstr);
678 in = (tsvector *) palloc(totallen);
679 memset(in, 0, totallen);
681 in->size = prs->curwords;
684 cur = str = STRPTR(in);
685 for (i = 0; i < prs->curwords; i++)
687 ptr->len = prs->words[i].len;
688 if (cur - str > MAXSTRPOS)
690 (errcode(ERRCODE_SYNTAX_ERROR),
691 errmsg("value is too big")));
692 ptr->pos = cur - str;
693 memcpy((void *) cur, (void *) prs->words[i].word, prs->words[i].len);
694 pfree(prs->words[i].word);
695 cur += SHORTALIGN(prs->words[i].len);
696 if (prs->words[i].alen)
701 *(uint16 *) cur = prs->words[i].pos.apos[0];
702 wptr = POSDATAPTR(in, ptr);
703 for (j = 0; j < *(uint16 *) cur; j++)
706 wptr[j].pos = prs->words[i].pos.apos[j + 1];
708 cur += sizeof(uint16) + prs->words[i].pos.apos[0] * sizeof(WordEntryPos);
709 pfree(prs->words[i].pos.apos);
721 to_tsvector(PG_FUNCTION_ARGS)
723 text *in = PG_GETARG_TEXT_P(1);
725 tsvector *out = NULL;
726 TSCfgInfo *cfg = findcfg(PG_GETARG_INT32(0));
731 prs.words = (WORD *) palloc(sizeof(WORD) * prs.lenwords);
733 parsetext_v2(cfg, &prs, VARDATA(in), VARSIZE(in) - VARHDRSZ);
734 PG_FREE_IF_COPY(in, 1);
737 out = makevalue(&prs);
741 out = palloc(CALCDATASIZE(0, 0));
742 out->len = CALCDATASIZE(0, 0);
745 PG_RETURN_POINTER(out);
749 to_tsvector_name(PG_FUNCTION_ARGS)
751 text *cfg = PG_GETARG_TEXT_P(0);
752 Datum res = DirectFunctionCall3(
754 Int32GetDatum(name2id_cfg(cfg)),
759 PG_FREE_IF_COPY(cfg, 0);
760 PG_RETURN_DATUM(res);
764 to_tsvector_current(PG_FUNCTION_ARGS)
766 Datum res = DirectFunctionCall3(
768 Int32GetDatum(get_currcfg()),
773 PG_RETURN_DATUM(res);
777 findFunc(char *fname)
779 FuncCandidateList clist,
781 Oid funcid = InvalidOid;
782 List *names = makeList1(makeString(fname));
784 ptr = clist = FuncnameGetCandidates(names, 1);
792 if (ptr->args[0] == TEXTOID && funcid == InvalidOid)
806 tsearch2(PG_FUNCTION_ARGS)
808 TriggerData *trigdata;
811 HeapTuple rettuple = NULL;
812 TSCfgInfo *cfg = findcfg(get_currcfg());
816 Datum datum = (Datum) 0;
817 Oid funcoid = InvalidOid;
819 if (!CALLED_AS_TRIGGER(fcinfo))
821 elog(ERROR, "TSearch: Not fired by trigger manager");
823 trigdata = (TriggerData *) fcinfo->context;
824 if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
826 elog(ERROR, "TSearch: Can't process STATEMENT events");
827 if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
829 elog(ERROR, "TSearch: Must be fired BEFORE event");
831 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
832 rettuple = trigdata->tg_trigtuple;
833 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
834 rettuple = trigdata->tg_newtuple;
837 elog(ERROR, "TSearch: Unknown event");
839 trigger = trigdata->tg_trigger;
840 rel = trigdata->tg_relation;
842 if (trigger->tgnargs < 2)
844 elog(ERROR, "TSearch: format tsearch2(tsvector_field, text_field1,...)");
846 numidxattr = SPI_fnumber(rel->rd_att, trigger->tgargs[0]);
847 if (numidxattr == SPI_ERROR_NOATTRIBUTE)
849 (errcode(ERRCODE_UNDEFINED_COLUMN),
850 errmsg("tsvector column \"%s\" does not exist",
851 trigger->tgargs[0])));
856 prs.words = (WORD *) palloc(sizeof(WORD) * prs.lenwords);
858 /* find all words in indexable column */
859 for (i = 1; i < trigger->tgnargs; i++)
867 numattr = SPI_fnumber(rel->rd_att, trigger->tgargs[i]);
868 if (numattr == SPI_ERROR_NOATTRIBUTE)
870 funcoid = findFunc(trigger->tgargs[i]);
871 if (funcoid == InvalidOid)
873 (errcode(ERRCODE_UNDEFINED_COLUMN),
874 errmsg("could not find function or field \"%s\"",
875 trigger->tgargs[i])));
879 oidtype = SPI_gettypeid(rel->rd_att, numattr);
880 /* We assume char() and varchar() are binary-equivalent to text */
881 if (!(oidtype == TEXTOID ||
882 oidtype == VARCHAROID ||
883 oidtype == BPCHAROID))
885 elog(WARNING, "TSearch: '%s' is not of character type",
889 txt_toasted = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
893 if (funcoid != InvalidOid)
895 text *txttmp = (text *) DatumGetPointer(OidFunctionCall1(
897 PointerGetDatum(txt_toasted)
900 txt = (text *) DatumGetPointer(PG_DETOAST_DATUM(PointerGetDatum(txttmp)));
902 txt_toasted = PointerGetDatum(txt);
905 txt = (text *) DatumGetPointer(PG_DETOAST_DATUM(PointerGetDatum(txt_toasted)));
907 parsetext_v2(cfg, &prs, VARDATA(txt), VARSIZE(txt) - VARHDRSZ);
908 if (txt != (text *) DatumGetPointer(txt_toasted))
912 /* make tsvector value */
915 datum = PointerGetDatum(makevalue(&prs));
916 rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr,
918 pfree(DatumGetPointer(datum));
922 tsvector *out = palloc(CALCDATASIZE(0, 0));
924 out->len = CALCDATASIZE(0, 0);
926 datum = PointerGetDatum(out);
928 rettuple = SPI_modifytuple(rel, rettuple, 1, &numidxattr,
932 if (rettuple == NULL)
934 elog(ERROR, "TSearch: %d returned by SPI_modifytuple", SPI_result);
936 return PointerGetDatum(rettuple);
940 silly_cmp_tsvector(const tsvector *a, const tsvector *b) {
941 if ( a->len < b->len )
943 else if ( a->len > b->len )
945 else if ( a->size < b->size )
947 else if ( a->size > b->size )
950 unsigned char *aptr=(unsigned char *)(a->data) + DATAHDRSIZE;
951 unsigned char *bptr=(unsigned char *)(b->data) + DATAHDRSIZE;
953 while( aptr - ( (unsigned char *)(a->data) ) < a->len ) {
954 if ( *aptr != *bptr )
955 return ( *aptr < *bptr ) ? -1 : 1;
962 PG_FUNCTION_INFO_V1(tsvector_cmp);
963 PG_FUNCTION_INFO_V1(tsvector_lt);
964 PG_FUNCTION_INFO_V1(tsvector_le);
965 PG_FUNCTION_INFO_V1(tsvector_eq);
966 PG_FUNCTION_INFO_V1(tsvector_ne);
967 PG_FUNCTION_INFO_V1(tsvector_ge);
968 PG_FUNCTION_INFO_V1(tsvector_gt);
969 Datum tsvector_cmp(PG_FUNCTION_ARGS);
970 Datum tsvector_lt(PG_FUNCTION_ARGS);
971 Datum tsvector_le(PG_FUNCTION_ARGS);
972 Datum tsvector_eq(PG_FUNCTION_ARGS);
973 Datum tsvector_ne(PG_FUNCTION_ARGS);
974 Datum tsvector_ge(PG_FUNCTION_ARGS);
975 Datum tsvector_gt(PG_FUNCTION_ARGS);
978 tsvector *a = (tsvector *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(0)));\
979 tsvector *b = (tsvector *) DatumGetPointer(PG_DETOAST_DATUM(PG_GETARG_DATUM(1)));\
980 int res = silly_cmp_tsvector(a,b); \
981 PG_FREE_IF_COPY(a,0); \
982 PG_FREE_IF_COPY(b,1); \
985 tsvector_cmp(PG_FUNCTION_ARGS) {
987 PG_RETURN_INT32(res);
991 tsvector_lt(PG_FUNCTION_ARGS) {
993 PG_RETURN_BOOL((res < 0) ? true : false);
997 tsvector_le(PG_FUNCTION_ARGS) {
999 PG_RETURN_BOOL((res <= 0) ? true : false);
1003 tsvector_eq(PG_FUNCTION_ARGS) {
1005 PG_RETURN_BOOL((res == 0) ? true : false);
1009 tsvector_ge(PG_FUNCTION_ARGS) {
1011 PG_RETURN_BOOL((res >= 0) ? true : false);
1015 tsvector_gt(PG_FUNCTION_ARGS) {
1017 PG_RETURN_BOOL((res > 0) ? true : false);
1021 tsvector_ne(PG_FUNCTION_ARGS) {
1023 PG_RETURN_BOOL((res != 0) ? true : false);