4 #include "access/genam.h"
5 #include "access/heapam.h"
6 #include "access/nbtree.h"
7 #include "catalog/indexing.h"
8 #include "catalog/pg_am.h"
9 #include "catalog/pg_amproc.h"
10 #include "catalog/pg_cast.h"
11 #include "catalog/pg_opclass.h"
12 #include "catalog/pg_type.h"
13 #include "executor/spi.h"
14 #include "utils/fmgroids.h"
15 #include "utils/lsyscache.h"
16 #include "utils/memutils.h"
17 #include "utils/tqual.h"
18 #include "utils/syscache.h"
19 #include "utils/typcache.h"
24 getDefaultOpclass(Oid amoid, Oid typid)
30 Oid opclassOid = InvalidOid;
32 heapRel = heap_open(OperatorClassRelationId, AccessShareLock);
35 Anum_pg_opclass_opcmethod,
36 BTEqualStrategyNumber, F_OIDEQ,
37 ObjectIdGetDatum(amoid));
39 scan = systable_beginscan(heapRel,
40 OpclassAmNameNspIndexId, true,
41 SnapshotNow, 1, &skey);
43 while (HeapTupleIsValid((tuple = systable_getnext(scan))))
45 Form_pg_opclass opclass = (Form_pg_opclass)GETSTRUCT(tuple);
47 if ( opclass->opcintype == typid && opclass->opcdefault )
49 if ( OidIsValid(opclassOid) )
50 elog(ERROR, "Ambiguous opclass for type %u (access method %u)", typid, amoid);
51 opclassOid = HeapTupleGetOid(tuple);
55 systable_endscan(scan);
56 heap_close(heapRel, AccessShareLock);
62 getAMProc(Oid amoid, Oid typid)
64 Oid opclassOid = getDefaultOpclass(amoid, typid);
65 Oid procOid = InvalidOid;
72 if ( !OidIsValid(opclassOid) )
74 typid = getBaseType(typid);
75 opclassOid = getDefaultOpclass(amoid, typid);
78 if ( !OidIsValid(opclassOid) )
84 * Search binary-coercible type
86 catlist = SearchSysCacheList(CASTSOURCETARGET, 1,
87 ObjectIdGetDatum(typid),
90 for (i = 0; i < catlist->n_members; i++)
92 HeapTuple tuple = &catlist->members[i]->tuple;
93 Form_pg_cast castForm = (Form_pg_cast)GETSTRUCT(tuple);
95 if ( castForm->castfunc == InvalidOid && castForm->castcontext == COERCION_CODE_IMPLICIT )
97 typid = castForm->casttarget;
98 opclassOid = getDefaultOpclass(amoid, typid);
99 if( OidIsValid(opclassOid) )
104 ReleaseSysCacheList(catlist);
107 if ( !OidIsValid(opclassOid) )
110 opfamilyOid = get_opclass_family(opclassOid);
112 heapRel = heap_open(AccessMethodProcedureRelationId, AccessShareLock);
113 ScanKeyInit(&skey[0],
114 Anum_pg_amproc_amprocfamily,
115 BTEqualStrategyNumber, F_OIDEQ,
116 ObjectIdGetDatum(opfamilyOid));
117 ScanKeyInit(&skey[1],
118 Anum_pg_amproc_amproclefttype,
119 BTEqualStrategyNumber, F_OIDEQ,
120 ObjectIdGetDatum(typid));
121 ScanKeyInit(&skey[2],
122 Anum_pg_amproc_amprocrighttype,
123 BTEqualStrategyNumber, F_OIDEQ,
124 ObjectIdGetDatum(typid));
125 #if PG_VERSION_NUM >= 90200
126 ScanKeyInit(&skey[3],
127 Anum_pg_amproc_amprocnum,
128 BTEqualStrategyNumber, F_OIDEQ,
129 Int32GetDatum(BTORDER_PROC));
132 scan = systable_beginscan(heapRel, AccessMethodProcedureIndexId, true,
134 #if PG_VERSION_NUM >= 90200
140 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
142 Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(tuple);
148 if ( OidIsValid(procOid) )
149 elog(ERROR,"Ambiguous support function for type %u (opclass %u)", typid, opfamilyOid);
150 procOid = amprocform->amproc;
153 elog(ERROR,"Unsupported access method");
157 systable_endscan(scan);
158 heap_close(heapRel, AccessShareLock);
163 static ProcTypeInfo *cacheProcs = NULL;
164 static int nCacheProcs = 0;
169 ProcTypeInfo info = malloc(sizeof(ProcTypeInfoData));
172 elog(ERROR, "Can't allocate %u memory", (uint32)sizeof(ProcTypeInfoData));
175 info->typtype = get_typtype(typid);
177 if (info->typtype == 'c')
181 MemoryContext oldcontext;
183 tupdesc = lookup_rowtype_tupdesc(typid, -1);
185 if (tupdesc->natts != 2)
186 elog(ERROR,"Composite type has wrong number of fields");
187 if (tupdesc->attrs[1]->atttypid != FLOAT4OID)
188 elog(ERROR,"Second field of composite type is not float4");
190 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
191 info->tupDesc = CreateTupleDescCopyConstr(tupdesc);
192 MemoryContextSwitchTo(oldcontext);
194 ReleaseTupleDesc(tupdesc);
196 info->cmpFuncOid = getAMProc(BTREE_AM_OID, info->tupDesc->attrs[0]->atttypid);
197 info->hashFuncOid = getAMProc(HASH_AM_OID, info->tupDesc->attrs[0]->atttypid);
201 info->tupDesc = NULL;
204 info->cmpFuncOid = getAMProc(BTREE_AM_OID, typid);
205 info->hashFuncOid = getAMProc(HASH_AM_OID, typid);
208 get_typlenbyvalalign(typid, &info->typlen, &info->typbyval, &info->typalign);
209 info->hashFuncInited = info->cmpFuncInited = false;
216 getFmgrInfoCmp(ProcTypeInfo info)
218 if ( info->cmpFuncInited == false )
220 if ( !OidIsValid(info->cmpFuncOid) )
221 elog(ERROR, "Could not find cmp function for type %u", info->typid);
223 fmgr_info_cxt( info->cmpFuncOid, &info->cmpFunc, TopMemoryContext );
224 info->cmpFuncInited = true;
229 getFmgrInfoHash(ProcTypeInfo info)
231 if ( info->hashFuncInited == false )
233 if ( !OidIsValid(info->hashFuncOid) )
234 elog(ERROR, "Could not find hash function for type %u", info->typid);
236 fmgr_info_cxt( info->hashFuncOid, &info->hashFunc, TopMemoryContext );
237 info->hashFuncInited = true;
242 cmpProcTypeInfo(const void *a, const void *b)
244 ProcTypeInfo av = *(ProcTypeInfo*)a;
245 ProcTypeInfo bv = *(ProcTypeInfo*)b;
247 Assert( av->typid != bv->typid );
249 return ( av->typid > bv->typid ) ? 1 : -1;
255 ProcTypeInfo info = NULL;
257 if ( nCacheProcs == 1 )
259 if ( cacheProcs[0]->typid == typid )
261 /*cacheProcs[0]->hashFuncInited = cacheProcs[0]->cmpFuncInited = false;*/
262 return cacheProcs[0];
265 else if ( nCacheProcs > 1 )
267 ProcTypeInfo *StopMiddle;
268 ProcTypeInfo *StopLow = cacheProcs,
269 *StopHigh = cacheProcs + nCacheProcs;
271 while (StopLow < StopHigh) {
272 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
275 if ( info->typid == typid )
277 /* info->hashFuncInited = info->cmpFuncInited = false; */
280 else if ( info->typid < typid )
281 StopLow = StopMiddle + 1;
283 StopHigh = StopMiddle;
289 info = fillProcs(typid);
290 if ( nCacheProcs == 0 )
292 cacheProcs = malloc(sizeof(ProcTypeInfo));
295 elog(ERROR, "Can't allocate %u memory", (uint32)sizeof(ProcTypeInfo));
299 cacheProcs[0] = info;
304 ProcTypeInfo *cacheProcsTmp = realloc(cacheProcs, (nCacheProcs+1) * sizeof(ProcTypeInfo));
307 elog(ERROR, "Can't allocate %u memory", (uint32)sizeof(ProcTypeInfo) * (nCacheProcs+1));
310 cacheProcs = cacheProcsTmp;
311 cacheProcs[ nCacheProcs ] = info;
313 qsort(cacheProcs, nCacheProcs, sizeof(ProcTypeInfo), cmpProcTypeInfo);
317 /* info->hashFuncInited = info->cmpFuncInited = false; */
323 * WARNING. Array2SimpleArray* doesn't copy Datum!
326 Array2SimpleArray(ProcTypeInfo info, ArrayType *a)
328 SimpleArray *s = palloc(sizeof(SimpleArray));
333 info = findProcs(ARR_ELEMTYPE(a));
339 deconstruct_array(a, info->typid,
340 info->typlen, info->typbyval, info->typalign,
341 &s->elems, NULL, &s->nelems);
347 deconstructCompositeType(ProcTypeInfo info, Datum in, double *weight)
349 HeapTupleHeader rec = DatumGetHeapTupleHeader(in);
354 /* Build a temporary HeapTuple control structure */
355 tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
356 ItemPointerSetInvalid(&(tuple.t_self));
357 tuple.t_tableOid = InvalidOid;
360 heap_deform_tuple(&tuple, info->tupDesc, values, nulls);
361 if (nulls[0] || nulls[1])
362 elog(ERROR, "Both fields in composite type could not be NULL");
365 *weight = DatumGetFloat4(values[1]);
370 cmpArrayElem(const void *a, const void *b, void *arg)
372 ProcTypeInfo info = (ProcTypeInfo)arg;
376 return DatumGetInt32( FCall2( &info->cmpFunc,
377 deconstructCompositeType(info, *(Datum*)a, NULL),
378 deconstructCompositeType(info, *(Datum*)b, NULL) ) );
380 return DatumGetInt32( FCall2( &info->cmpFunc,
381 *(Datum*)a, *(Datum*)b ) );
385 Array2SimpleArrayS(ProcTypeInfo info, ArrayType *a)
387 SimpleArray *s = Array2SimpleArray(info, a);
391 getFmgrInfoCmp(s->info);
393 qsort_arg(s->elems, s->nelems, sizeof(Datum), cmpArrayElem, s->info);
399 typedef struct cmpArrayElemData {
406 cmpArrayElemArg(const void *a, const void *b, void *arg)
408 cmpArrayElemData *data = (cmpArrayElemData*)arg;
411 if (data->info->tupDesc)
412 res = DatumGetInt32( FCall2( &data->info->cmpFunc,
413 deconstructCompositeType(data->info, *(Datum*)a, NULL),
414 deconstructCompositeType(data->info, *(Datum*)b, NULL) ) );
416 res = DatumGetInt32( FCall2( &data->info->cmpFunc,
417 *(Datum*)a, *(Datum*)b ) );
420 data->hasDuplicate = true;
426 * Uniquefy array and calculate TF. Although
427 * result doesn't depend on normalization, we
428 * normalize TF by length array to have possiblity
429 * to limit estimation for index support.
431 * Cache signals of needing of TF caclulation
435 Array2SimpleArrayU(ProcTypeInfo info, ArrayType *a, void *cache)
437 SimpleArray *s = Array2SimpleArray(info, a);
438 StatElem *stat = NULL;
440 if ( s->nelems > 0 && cache )
442 s->df = palloc(sizeof(double) * s->nelems);
443 s->df[0] = 1.0; /* init */
448 cmpArrayElemData data;
451 getFmgrInfoCmp(s->info);
453 data.hasDuplicate = false;
455 qsort_arg(s->elems, s->nelems, sizeof(Datum), cmpArrayElemArg, &data);
457 if ( data.hasDuplicate )
465 data = tmp = dr = s->elems;
467 while (tmp - data < num)
469 cmp = (tmp == dr) ? 0 : cmpArrayElem(tmp, dr, s->info);
474 s->df[ dr - data ] = 1.0;
479 s->df[ dr - data ] += 1.0;
484 s->nelems = dr + 1 - s->elems;
488 int tfm = getTFMethod();
490 for(i=0;i<s->nelems;i++)
492 stat = fingArrayStat(cache, s->info->typid, s->elems[i], stat);
498 s->df[i] = (1.0 + log( s->df[i] ));
500 s->df[i] *= stat->idf;
503 s->df[i] = stat->idf;
506 elog(ERROR,"Unknown TF method: %d", tfm);
511 s->df[i] = 0.0; /* unknown word */
518 for(i=0;i<s->nelems;i++)
520 stat = fingArrayStat(cache, s->info->typid, s->elems[i], stat);
522 s->df[i] = stat->idf;
528 else if (s->nelems > 0 && cache)
530 stat = fingArrayStat(cache, s->info->typid, s->elems[0], stat);
532 s->df[0] = stat->idf;
541 numOfIntersect(SimpleArray *a, SimpleArray *b)
545 Datum *aptr = a->elems,
547 ProcTypeInfo info = a->info;
549 Assert( a->info->typid == b->info->typid );
551 getFmgrInfoCmp(info);
553 while( aptr - a->elems < a->nelems && bptr - b->elems < b->nelems )
555 cmp = cmpArrayElem(aptr, bptr, info);
572 TFIDFSml(SimpleArray *a, SimpleArray *b)
575 Datum *aptr = a->elems,
577 ProcTypeInfo info = a->info;
579 double suma = 0.0, sumb = 0.0;
581 Assert( a->info->typid == b->info->typid );
585 getFmgrInfoCmp(info);
587 while( aptr - a->elems < a->nelems && bptr - b->elems < b->nelems )
589 cmp = cmpArrayElem(aptr, bptr, info);
592 suma += a->df[ aptr - a->elems ] * a->df[ aptr - a->elems ];
597 sumb += b->df[ bptr - b->elems ] * b->df[ bptr - b->elems ];
602 res += a->df[ aptr - a->elems ] * b->df[ bptr - b->elems ];
603 suma += a->df[ aptr - a->elems ] * a->df[ aptr - a->elems ];
604 sumb += b->df[ bptr - b->elems ] * b->df[ bptr - b->elems ];
611 * Compute last elements
613 while( aptr - a->elems < a->nelems )
615 suma += a->df[ aptr - a->elems ] * a->df[ aptr - a->elems ];
619 while( bptr - b->elems < b->nelems )
621 sumb += b->df[ bptr - b->elems ] * b->df[ bptr - b->elems ];
625 if ( suma > 0.0 && sumb > 0.0 )
626 res = res / sqrt( suma * sumb );
634 PG_FUNCTION_INFO_V1(arraysml);
635 Datum arraysml(PG_FUNCTION_ARGS);
637 arraysml(PG_FUNCTION_ARGS)
640 SimpleArray *sa, *sb;
642 fcinfo->flinfo->fn_extra = SearchArrayCache(
643 fcinfo->flinfo->fn_extra,
644 fcinfo->flinfo->fn_mcxt,
645 PG_GETARG_DATUM(0), &a, &sa, NULL);
646 fcinfo->flinfo->fn_extra = SearchArrayCache(
647 fcinfo->flinfo->fn_extra,
648 fcinfo->flinfo->fn_mcxt,
649 PG_GETARG_DATUM(1), &b, &sb, NULL);
651 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
652 elog(ERROR,"Arguments array are not the same type!");
654 if (ARRISVOID(a) || ARRISVOID(b))
655 PG_RETURN_FLOAT4(0.0);
660 PG_RETURN_FLOAT4( TFIDFSml(sa, sb) );
667 power = ((double)(sa->nelems)) * ((double)(sb->nelems));
668 cnt = numOfIntersect(sa, sb);
670 PG_RETURN_FLOAT4( ((double)cnt) / sqrt( power ) );
675 float4 res = (float4)numOfIntersect(sa, sb);
677 PG_RETURN_FLOAT4(res);
681 elog(ERROR,"Unsupported formula type of similarity");
684 PG_RETURN_FLOAT4(0.0); /* keep compiler quiet */
687 PG_FUNCTION_INFO_V1(arraysmlw);
688 Datum arraysmlw(PG_FUNCTION_ARGS);
690 arraysmlw(PG_FUNCTION_ARGS)
693 SimpleArray *sa, *sb;
694 bool useIntersect = PG_GETARG_BOOL(2);
695 double numerator = 0.0;
696 double denominatorA = 0.0,
704 fcinfo->flinfo->fn_extra = SearchArrayCache(
705 fcinfo->flinfo->fn_extra,
706 fcinfo->flinfo->fn_mcxt,
707 PG_GETARG_DATUM(0), &a, &sa, NULL);
708 fcinfo->flinfo->fn_extra = SearchArrayCache(
709 fcinfo->flinfo->fn_extra,
710 fcinfo->flinfo->fn_mcxt,
711 PG_GETARG_DATUM(1), &b, &sb, NULL);
713 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
714 elog(ERROR,"Arguments array are not the same type!");
716 if (ARRISVOID(a) || ARRISVOID(b))
717 PG_RETURN_FLOAT4(0.0);
720 if (info->tupDesc == NULL)
721 elog(ERROR, "Only weigthed (composite) types should be used");
722 getFmgrInfoCmp(info);
724 while(ai < sa->nelems && bi < sb->nelems)
726 Datum ad = deconstructCompositeType(info, sa->elems[ai], &tmpA),
727 bd = deconstructCompositeType(info, sb->elems[bi], &tmpB);
729 cmp = DatumGetInt32(FCall2(&info->cmpFunc, ad, bd));
732 if (useIntersect == false)
733 denominatorA += tmpA * tmpA;
735 } else if ( cmp > 0 ) {
736 if (useIntersect == false)
737 denominatorB += tmpB * tmpB;
740 denominatorA += tmpA * tmpA;
741 denominatorB += tmpB * tmpB;
742 numerator += tmpA * tmpB;
748 if (useIntersect == false) {
749 while(ai < sa->nelems) {
750 deconstructCompositeType(info, sa->elems[ai], &tmpA);
751 denominatorA += tmpA * tmpA;
755 while(bi < sb->nelems) {
756 deconstructCompositeType(info, sb->elems[bi], &tmpB);
757 denominatorB += tmpB * tmpB;
762 if (numerator != 0.0) {
763 numerator = numerator / sqrt( denominatorA * denominatorB );
766 PG_RETURN_FLOAT4(numerator);
769 PG_FUNCTION_INFO_V1(arraysml_op);
770 Datum arraysml_op(PG_FUNCTION_ARGS);
772 arraysml_op(PG_FUNCTION_ARGS)
775 SimpleArray *sa, *sb;
778 fcinfo->flinfo->fn_extra = SearchArrayCache(
779 fcinfo->flinfo->fn_extra,
780 fcinfo->flinfo->fn_mcxt,
781 PG_GETARG_DATUM(0), &a, &sa, NULL);
782 fcinfo->flinfo->fn_extra = SearchArrayCache(
783 fcinfo->flinfo->fn_extra,
784 fcinfo->flinfo->fn_mcxt,
785 PG_GETARG_DATUM(1), &b, &sb, NULL);
787 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
788 elog(ERROR,"Arguments array are not the same type!");
790 if (ARRISVOID(a) || ARRISVOID(b))
791 PG_RETURN_BOOL(false);
796 power = TFIDFSml(sa, sb);
802 power = sqrt( ((double)(sa->nelems)) * ((double)(sb->nelems)) );
804 if ( ((double)Min(sa->nelems, sb->nelems)) / power < GetSmlarLimit() )
805 PG_RETURN_BOOL(false);
807 cnt = numOfIntersect(sa, sb);
808 power = ((double)cnt) / power;
812 power = (double)numOfIntersect(sa, sb);
815 elog(ERROR,"Unsupported formula type of similarity");
818 PG_RETURN_BOOL(power >= GetSmlarLimit());
822 static char cachedFormula[QBSIZE];
823 static int cachedLen = 0;
824 static void *cachedPlan = NULL;
826 PG_FUNCTION_INFO_V1(arraysml_func);
827 Datum arraysml_func(PG_FUNCTION_ARGS);
829 arraysml_func(PG_FUNCTION_ARGS)
832 SimpleArray *sa, *sb;
834 float4 result = -1.0;
835 Oid arg[] = {INT4OID, INT4OID, INT4OID};
840 text *formula = PG_GETARG_TEXT_P(2);
842 fcinfo->flinfo->fn_extra = SearchArrayCache(
843 fcinfo->flinfo->fn_extra,
844 fcinfo->flinfo->fn_mcxt,
845 PG_GETARG_DATUM(0), &a, &sa, NULL);
846 fcinfo->flinfo->fn_extra = SearchArrayCache(
847 fcinfo->flinfo->fn_extra,
848 fcinfo->flinfo->fn_mcxt,
849 PG_GETARG_DATUM(1), &b, &sb, NULL);
851 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
852 elog(ERROR,"Arguments array are not the same type!");
854 if (ARRISVOID(a) || ARRISVOID(b))
855 PG_RETURN_BOOL(false);
857 cnt = numOfIntersect(sa, sb);
859 if ( VARSIZE(formula) - VARHDRSZ > QBSIZE - 1024 )
860 elog(ERROR,"Formula is too long");
865 if ( cachedPlan == NULL || cachedLen != VARSIZE(formula) - VARHDRSZ ||
866 memcmp( cachedFormula, VARDATA(formula), VARSIZE(formula) - VARHDRSZ ) != 0 )
868 char *ptr, buf[QBSIZE];
870 *cachedFormula = '\0';
872 SPI_freeplan(cachedPlan);
876 ptr = stpcpy( buf, "SELECT (" );
877 memcpy( ptr, VARDATA(formula), VARSIZE(formula) - VARHDRSZ );
878 ptr += VARSIZE(formula) - VARHDRSZ;
879 ptr = stpcpy( ptr, ")::float4 FROM");
880 ptr = stpcpy( ptr, " (SELECT $1 ::float8 AS i, $2 ::float8 AS a, $3 ::float8 AS b) AS N;");
883 plan = SPI_prepare(buf, 3, arg);
885 elog(ERROR, "SPI_prepare() failed");
887 cachedPlan = SPI_saveplan(plan);
889 elog(ERROR, "SPI_saveplan() failed");
892 cachedLen = VARSIZE(formula) - VARHDRSZ;
893 memcpy( cachedFormula, VARDATA(formula), VARSIZE(formula) - VARHDRSZ );
899 pars[0] = Int32GetDatum( cnt );
900 pars[1] = Int32GetDatum( sa->nelems );
901 pars[2] = Int32GetDatum( sb->nelems );
903 stat = SPI_execute_plan(plan, pars, NULL, true, 3);
905 elog(ERROR, "SPI_execute_plan() returns %d", stat);
907 if ( SPI_processed > 0)
908 result = DatumGetFloat4(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull));
912 PG_RETURN_FLOAT4(result);
915 PG_FUNCTION_INFO_V1(array_unique);
916 Datum array_unique(PG_FUNCTION_ARGS);
918 array_unique(PG_FUNCTION_ARGS)
920 ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
924 sa = Array2SimpleArrayU(NULL, a, NULL);
926 res = construct_array( sa->elems,
935 PG_FREE_IF_COPY(a, 0);
937 PG_RETURN_ARRAYTYPE_P(res);
940 PG_FUNCTION_INFO_V1(inarray);
941 Datum inarray(PG_FUNCTION_ARGS);
943 inarray(PG_FUNCTION_ARGS)
947 Datum query = PG_GETARG_DATUM(1);
954 fcinfo->flinfo->fn_extra = SearchArrayCache(
955 fcinfo->flinfo->fn_extra,
956 fcinfo->flinfo->fn_mcxt,
957 PG_GETARG_DATUM(0), &a, &sa, NULL);
959 queryTypeOid = get_fn_expr_argtype(fcinfo->flinfo, 1);
961 if ( queryTypeOid == InvalidOid )
962 elog(ERROR,"inarray: could not determine actual argument type");
964 if ( queryTypeOid != sa->info->typid )
965 elog(ERROR,"inarray: Type of array's element and type of argument are not the same");
967 getFmgrInfoCmp(sa->info);
969 StopHigh = sa->elems + sa->nelems;
971 while (StopLow < StopHigh)
973 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
974 cmp = cmpArrayElem(StopMiddle, &query, sa->info);
979 if ( PG_NARGS() >= 3 )
980 PG_RETURN_DATUM(PG_GETARG_DATUM(2));
981 PG_RETURN_FLOAT4(1.0);
984 StopLow = StopMiddle + 1;
986 StopHigh = StopMiddle;
989 if ( PG_NARGS() >= 4 )
990 PG_RETURN_DATUM(PG_GETARG_DATUM(3));
991 PG_RETURN_FLOAT4(0.0);