4 #include "access/genam.h"
5 #include "access/heapam.h"
6 #include "access/htup_details.h"
7 #include "access/nbtree.h"
8 #include "catalog/indexing.h"
9 #include "catalog/pg_am.h"
10 #include "catalog/pg_amproc.h"
11 #include "catalog/pg_cast.h"
12 #include "catalog/pg_opclass.h"
13 #include "catalog/pg_type.h"
14 #include "executor/spi.h"
15 #include "utils/catcache.h"
16 #include "utils/fmgroids.h"
17 #include "utils/lsyscache.h"
18 #include "utils/memutils.h"
19 #include "utils/tqual.h"
20 #include "utils/syscache.h"
21 #include "utils/typcache.h"
26 getDefaultOpclass(Oid amoid, Oid typid)
32 Oid opclassOid = InvalidOid;
34 heapRel = heap_open(OperatorClassRelationId, AccessShareLock);
37 Anum_pg_opclass_opcmethod,
38 BTEqualStrategyNumber, F_OIDEQ,
39 ObjectIdGetDatum(amoid));
41 scan = systable_beginscan(heapRel,
42 OpclassAmNameNspIndexId, true,
43 SnapshotNow, 1, &skey);
45 while (HeapTupleIsValid((tuple = systable_getnext(scan))))
47 Form_pg_opclass opclass = (Form_pg_opclass)GETSTRUCT(tuple);
49 if ( opclass->opcintype == typid && opclass->opcdefault )
51 if ( OidIsValid(opclassOid) )
52 elog(ERROR, "Ambiguous opclass for type %u (access method %u)", typid, amoid);
53 opclassOid = HeapTupleGetOid(tuple);
57 systable_endscan(scan);
58 heap_close(heapRel, AccessShareLock);
64 getAMProc(Oid amoid, Oid typid)
66 Oid opclassOid = getDefaultOpclass(amoid, typid);
67 Oid procOid = InvalidOid;
74 if ( !OidIsValid(opclassOid) )
76 typid = getBaseType(typid);
77 opclassOid = getDefaultOpclass(amoid, typid);
80 if ( !OidIsValid(opclassOid) )
86 * Search binary-coercible type
88 catlist = SearchSysCacheList(CASTSOURCETARGET, 1,
89 ObjectIdGetDatum(typid),
92 for (i = 0; i < catlist->n_members; i++)
94 HeapTuple tuple = &catlist->members[i]->tuple;
95 Form_pg_cast castForm = (Form_pg_cast)GETSTRUCT(tuple);
97 if ( castForm->castfunc == InvalidOid && castForm->castcontext == COERCION_CODE_IMPLICIT )
99 typid = castForm->casttarget;
100 opclassOid = getDefaultOpclass(amoid, typid);
101 if( OidIsValid(opclassOid) )
106 ReleaseSysCacheList(catlist);
109 if ( !OidIsValid(opclassOid) )
112 opfamilyOid = get_opclass_family(opclassOid);
114 heapRel = heap_open(AccessMethodProcedureRelationId, AccessShareLock);
115 ScanKeyInit(&skey[0],
116 Anum_pg_amproc_amprocfamily,
117 BTEqualStrategyNumber, F_OIDEQ,
118 ObjectIdGetDatum(opfamilyOid));
119 ScanKeyInit(&skey[1],
120 Anum_pg_amproc_amproclefttype,
121 BTEqualStrategyNumber, F_OIDEQ,
122 ObjectIdGetDatum(typid));
123 ScanKeyInit(&skey[2],
124 Anum_pg_amproc_amprocrighttype,
125 BTEqualStrategyNumber, F_OIDEQ,
126 ObjectIdGetDatum(typid));
127 #if PG_VERSION_NUM >= 90200
128 ScanKeyInit(&skey[3],
129 Anum_pg_amproc_amprocnum,
130 BTEqualStrategyNumber, F_OIDEQ,
131 Int32GetDatum(BTORDER_PROC));
134 scan = systable_beginscan(heapRel, AccessMethodProcedureIndexId, true,
136 #if PG_VERSION_NUM >= 90200
142 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
144 Form_pg_amproc amprocform = (Form_pg_amproc) GETSTRUCT(tuple);
150 if ( OidIsValid(procOid) )
151 elog(ERROR,"Ambiguous support function for type %u (opclass %u)", typid, opfamilyOid);
152 procOid = amprocform->amproc;
155 elog(ERROR,"Unsupported access method");
159 systable_endscan(scan);
160 heap_close(heapRel, AccessShareLock);
165 static ProcTypeInfo *cacheProcs = NULL;
166 static int nCacheProcs = 0;
171 ProcTypeInfo info = malloc(sizeof(ProcTypeInfoData));
174 elog(ERROR, "Can't allocate %u memory", (uint32)sizeof(ProcTypeInfoData));
177 info->typtype = get_typtype(typid);
179 if (info->typtype == 'c')
183 MemoryContext oldcontext;
185 tupdesc = lookup_rowtype_tupdesc(typid, -1);
187 if (tupdesc->natts != 2)
188 elog(ERROR,"Composite type has wrong number of fields");
189 if (tupdesc->attrs[1]->atttypid != FLOAT4OID)
190 elog(ERROR,"Second field of composite type is not float4");
192 oldcontext = MemoryContextSwitchTo(TopMemoryContext);
193 info->tupDesc = CreateTupleDescCopyConstr(tupdesc);
194 MemoryContextSwitchTo(oldcontext);
196 ReleaseTupleDesc(tupdesc);
198 info->cmpFuncOid = getAMProc(BTREE_AM_OID, info->tupDesc->attrs[0]->atttypid);
199 info->hashFuncOid = getAMProc(HASH_AM_OID, info->tupDesc->attrs[0]->atttypid);
203 info->tupDesc = NULL;
206 info->cmpFuncOid = getAMProc(BTREE_AM_OID, typid);
207 info->hashFuncOid = getAMProc(HASH_AM_OID, typid);
210 get_typlenbyvalalign(typid, &info->typlen, &info->typbyval, &info->typalign);
211 info->hashFuncInited = info->cmpFuncInited = false;
218 getFmgrInfoCmp(ProcTypeInfo info)
220 if ( info->cmpFuncInited == false )
222 if ( !OidIsValid(info->cmpFuncOid) )
223 elog(ERROR, "Could not find cmp function for type %u", info->typid);
225 fmgr_info_cxt( info->cmpFuncOid, &info->cmpFunc, TopMemoryContext );
226 info->cmpFuncInited = true;
231 getFmgrInfoHash(ProcTypeInfo info)
233 if ( info->hashFuncInited == false )
235 if ( !OidIsValid(info->hashFuncOid) )
236 elog(ERROR, "Could not find hash function for type %u", info->typid);
238 fmgr_info_cxt( info->hashFuncOid, &info->hashFunc, TopMemoryContext );
239 info->hashFuncInited = true;
244 cmpProcTypeInfo(const void *a, const void *b)
246 ProcTypeInfo av = *(ProcTypeInfo*)a;
247 ProcTypeInfo bv = *(ProcTypeInfo*)b;
249 Assert( av->typid != bv->typid );
251 return ( av->typid > bv->typid ) ? 1 : -1;
257 ProcTypeInfo info = NULL;
259 if ( nCacheProcs == 1 )
261 if ( cacheProcs[0]->typid == typid )
263 /*cacheProcs[0]->hashFuncInited = cacheProcs[0]->cmpFuncInited = false;*/
264 return cacheProcs[0];
267 else if ( nCacheProcs > 1 )
269 ProcTypeInfo *StopMiddle;
270 ProcTypeInfo *StopLow = cacheProcs,
271 *StopHigh = cacheProcs + nCacheProcs;
273 while (StopLow < StopHigh) {
274 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
277 if ( info->typid == typid )
279 /* info->hashFuncInited = info->cmpFuncInited = false; */
282 else if ( info->typid < typid )
283 StopLow = StopMiddle + 1;
285 StopHigh = StopMiddle;
291 info = fillProcs(typid);
292 if ( nCacheProcs == 0 )
294 cacheProcs = malloc(sizeof(ProcTypeInfo));
297 elog(ERROR, "Can't allocate %u memory", (uint32)sizeof(ProcTypeInfo));
301 cacheProcs[0] = info;
306 ProcTypeInfo *cacheProcsTmp = realloc(cacheProcs, (nCacheProcs+1) * sizeof(ProcTypeInfo));
309 elog(ERROR, "Can't allocate %u memory", (uint32)sizeof(ProcTypeInfo) * (nCacheProcs+1));
312 cacheProcs = cacheProcsTmp;
313 cacheProcs[ nCacheProcs ] = info;
315 qsort(cacheProcs, nCacheProcs, sizeof(ProcTypeInfo), cmpProcTypeInfo);
319 /* info->hashFuncInited = info->cmpFuncInited = false; */
325 * WARNING. Array2SimpleArray* doesn't copy Datum!
328 Array2SimpleArray(ProcTypeInfo info, ArrayType *a)
330 SimpleArray *s = palloc(sizeof(SimpleArray));
335 info = findProcs(ARR_ELEMTYPE(a));
341 deconstruct_array(a, info->typid,
342 info->typlen, info->typbyval, info->typalign,
343 &s->elems, NULL, &s->nelems);
349 deconstructCompositeType(ProcTypeInfo info, Datum in, double *weight)
351 HeapTupleHeader rec = DatumGetHeapTupleHeader(in);
356 /* Build a temporary HeapTuple control structure */
357 tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
358 ItemPointerSetInvalid(&(tuple.t_self));
359 tuple.t_tableOid = InvalidOid;
362 heap_deform_tuple(&tuple, info->tupDesc, values, nulls);
363 if (nulls[0] || nulls[1])
364 elog(ERROR, "Both fields in composite type could not be NULL");
367 *weight = DatumGetFloat4(values[1]);
372 cmpArrayElem(const void *a, const void *b, void *arg)
374 ProcTypeInfo info = (ProcTypeInfo)arg;
378 return DatumGetInt32( FCall2( &info->cmpFunc,
379 deconstructCompositeType(info, *(Datum*)a, NULL),
380 deconstructCompositeType(info, *(Datum*)b, NULL) ) );
382 return DatumGetInt32( FCall2( &info->cmpFunc,
383 *(Datum*)a, *(Datum*)b ) );
387 Array2SimpleArrayS(ProcTypeInfo info, ArrayType *a)
389 SimpleArray *s = Array2SimpleArray(info, a);
393 getFmgrInfoCmp(s->info);
395 qsort_arg(s->elems, s->nelems, sizeof(Datum), cmpArrayElem, s->info);
401 typedef struct cmpArrayElemData {
408 cmpArrayElemArg(const void *a, const void *b, void *arg)
410 cmpArrayElemData *data = (cmpArrayElemData*)arg;
413 if (data->info->tupDesc)
414 res = DatumGetInt32( FCall2( &data->info->cmpFunc,
415 deconstructCompositeType(data->info, *(Datum*)a, NULL),
416 deconstructCompositeType(data->info, *(Datum*)b, NULL) ) );
418 res = DatumGetInt32( FCall2( &data->info->cmpFunc,
419 *(Datum*)a, *(Datum*)b ) );
422 data->hasDuplicate = true;
428 * Uniquefy array and calculate TF. Although
429 * result doesn't depend on normalization, we
430 * normalize TF by length array to have possiblity
431 * to limit estimation for index support.
433 * Cache signals of needing of TF caclulation
437 Array2SimpleArrayU(ProcTypeInfo info, ArrayType *a, void *cache)
439 SimpleArray *s = Array2SimpleArray(info, a);
440 StatElem *stat = NULL;
442 if ( s->nelems > 0 && cache )
444 s->df = palloc(sizeof(double) * s->nelems);
445 s->df[0] = 1.0; /* init */
450 cmpArrayElemData data;
453 getFmgrInfoCmp(s->info);
455 data.hasDuplicate = false;
457 qsort_arg(s->elems, s->nelems, sizeof(Datum), cmpArrayElemArg, &data);
459 if ( data.hasDuplicate )
467 data = tmp = dr = s->elems;
469 while (tmp - data < num)
471 cmp = (tmp == dr) ? 0 : cmpArrayElem(tmp, dr, s->info);
476 s->df[ dr - data ] = 1.0;
481 s->df[ dr - data ] += 1.0;
486 s->nelems = dr + 1 - s->elems;
490 int tfm = getTFMethod();
492 for(i=0;i<s->nelems;i++)
494 stat = fingArrayStat(cache, s->info->typid, s->elems[i], stat);
500 s->df[i] = (1.0 + log( s->df[i] ));
502 s->df[i] *= stat->idf;
505 s->df[i] = stat->idf;
508 elog(ERROR,"Unknown TF method: %d", tfm);
513 s->df[i] = 0.0; /* unknown word */
520 for(i=0;i<s->nelems;i++)
522 stat = fingArrayStat(cache, s->info->typid, s->elems[i], stat);
524 s->df[i] = stat->idf;
530 else if (s->nelems > 0 && cache)
532 stat = fingArrayStat(cache, s->info->typid, s->elems[0], stat);
534 s->df[0] = stat->idf;
543 numOfIntersect(SimpleArray *a, SimpleArray *b)
547 Datum *aptr = a->elems,
549 ProcTypeInfo info = a->info;
551 Assert( a->info->typid == b->info->typid );
553 getFmgrInfoCmp(info);
555 while( aptr - a->elems < a->nelems && bptr - b->elems < b->nelems )
557 cmp = cmpArrayElem(aptr, bptr, info);
574 TFIDFSml(SimpleArray *a, SimpleArray *b)
577 Datum *aptr = a->elems,
579 ProcTypeInfo info = a->info;
581 double suma = 0.0, sumb = 0.0;
583 Assert( a->info->typid == b->info->typid );
587 getFmgrInfoCmp(info);
589 while( aptr - a->elems < a->nelems && bptr - b->elems < b->nelems )
591 cmp = cmpArrayElem(aptr, bptr, info);
594 suma += a->df[ aptr - a->elems ] * a->df[ aptr - a->elems ];
599 sumb += b->df[ bptr - b->elems ] * b->df[ bptr - b->elems ];
604 res += a->df[ aptr - a->elems ] * b->df[ bptr - b->elems ];
605 suma += a->df[ aptr - a->elems ] * a->df[ aptr - a->elems ];
606 sumb += b->df[ bptr - b->elems ] * b->df[ bptr - b->elems ];
613 * Compute last elements
615 while( aptr - a->elems < a->nelems )
617 suma += a->df[ aptr - a->elems ] * a->df[ aptr - a->elems ];
621 while( bptr - b->elems < b->nelems )
623 sumb += b->df[ bptr - b->elems ] * b->df[ bptr - b->elems ];
627 if ( suma > 0.0 && sumb > 0.0 )
628 res = res / sqrt( suma * sumb );
636 PG_FUNCTION_INFO_V1(arraysml);
637 Datum arraysml(PG_FUNCTION_ARGS);
639 arraysml(PG_FUNCTION_ARGS)
642 SimpleArray *sa, *sb;
644 fcinfo->flinfo->fn_extra = SearchArrayCache(
645 fcinfo->flinfo->fn_extra,
646 fcinfo->flinfo->fn_mcxt,
647 PG_GETARG_DATUM(0), &a, &sa, NULL);
648 fcinfo->flinfo->fn_extra = SearchArrayCache(
649 fcinfo->flinfo->fn_extra,
650 fcinfo->flinfo->fn_mcxt,
651 PG_GETARG_DATUM(1), &b, &sb, NULL);
653 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
654 elog(ERROR,"Arguments array are not the same type!");
656 if (ARRISVOID(a) || ARRISVOID(b))
657 PG_RETURN_FLOAT4(0.0);
662 PG_RETURN_FLOAT4( TFIDFSml(sa, sb) );
669 power = ((double)(sa->nelems)) * ((double)(sb->nelems));
670 cnt = numOfIntersect(sa, sb);
672 PG_RETURN_FLOAT4( ((double)cnt) / sqrt( power ) );
677 float4 res = (float4)numOfIntersect(sa, sb);
679 PG_RETURN_FLOAT4(res);
683 elog(ERROR,"Unsupported formula type of similarity");
686 PG_RETURN_FLOAT4(0.0); /* keep compiler quiet */
689 PG_FUNCTION_INFO_V1(arraysmlw);
690 Datum arraysmlw(PG_FUNCTION_ARGS);
692 arraysmlw(PG_FUNCTION_ARGS)
695 SimpleArray *sa, *sb;
696 bool useIntersect = PG_GETARG_BOOL(2);
697 double numerator = 0.0;
698 double denominatorA = 0.0,
706 fcinfo->flinfo->fn_extra = SearchArrayCache(
707 fcinfo->flinfo->fn_extra,
708 fcinfo->flinfo->fn_mcxt,
709 PG_GETARG_DATUM(0), &a, &sa, NULL);
710 fcinfo->flinfo->fn_extra = SearchArrayCache(
711 fcinfo->flinfo->fn_extra,
712 fcinfo->flinfo->fn_mcxt,
713 PG_GETARG_DATUM(1), &b, &sb, NULL);
715 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
716 elog(ERROR,"Arguments array are not the same type!");
718 if (ARRISVOID(a) || ARRISVOID(b))
719 PG_RETURN_FLOAT4(0.0);
722 if (info->tupDesc == NULL)
723 elog(ERROR, "Only weigthed (composite) types should be used");
724 getFmgrInfoCmp(info);
726 while(ai < sa->nelems && bi < sb->nelems)
728 Datum ad = deconstructCompositeType(info, sa->elems[ai], &tmpA),
729 bd = deconstructCompositeType(info, sb->elems[bi], &tmpB);
731 cmp = DatumGetInt32(FCall2(&info->cmpFunc, ad, bd));
734 if (useIntersect == false)
735 denominatorA += tmpA * tmpA;
737 } else if ( cmp > 0 ) {
738 if (useIntersect == false)
739 denominatorB += tmpB * tmpB;
742 denominatorA += tmpA * tmpA;
743 denominatorB += tmpB * tmpB;
744 numerator += tmpA * tmpB;
750 if (useIntersect == false) {
751 while(ai < sa->nelems) {
752 deconstructCompositeType(info, sa->elems[ai], &tmpA);
753 denominatorA += tmpA * tmpA;
757 while(bi < sb->nelems) {
758 deconstructCompositeType(info, sb->elems[bi], &tmpB);
759 denominatorB += tmpB * tmpB;
764 if (numerator != 0.0) {
765 numerator = numerator / sqrt( denominatorA * denominatorB );
768 PG_RETURN_FLOAT4(numerator);
771 PG_FUNCTION_INFO_V1(arraysml_op);
772 Datum arraysml_op(PG_FUNCTION_ARGS);
774 arraysml_op(PG_FUNCTION_ARGS)
777 SimpleArray *sa, *sb;
780 fcinfo->flinfo->fn_extra = SearchArrayCache(
781 fcinfo->flinfo->fn_extra,
782 fcinfo->flinfo->fn_mcxt,
783 PG_GETARG_DATUM(0), &a, &sa, NULL);
784 fcinfo->flinfo->fn_extra = SearchArrayCache(
785 fcinfo->flinfo->fn_extra,
786 fcinfo->flinfo->fn_mcxt,
787 PG_GETARG_DATUM(1), &b, &sb, NULL);
789 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
790 elog(ERROR,"Arguments array are not the same type!");
792 if (ARRISVOID(a) || ARRISVOID(b))
793 PG_RETURN_BOOL(false);
798 power = TFIDFSml(sa, sb);
804 power = sqrt( ((double)(sa->nelems)) * ((double)(sb->nelems)) );
806 if ( ((double)Min(sa->nelems, sb->nelems)) / power < GetSmlarLimit() )
807 PG_RETURN_BOOL(false);
809 cnt = numOfIntersect(sa, sb);
810 power = ((double)cnt) / power;
814 power = (double)numOfIntersect(sa, sb);
817 elog(ERROR,"Unsupported formula type of similarity");
820 PG_RETURN_BOOL(power >= GetSmlarLimit());
824 static char cachedFormula[QBSIZE];
825 static int cachedLen = 0;
826 static void *cachedPlan = NULL;
828 PG_FUNCTION_INFO_V1(arraysml_func);
829 Datum arraysml_func(PG_FUNCTION_ARGS);
831 arraysml_func(PG_FUNCTION_ARGS)
834 SimpleArray *sa, *sb;
836 float4 result = -1.0;
837 Oid arg[] = {INT4OID, INT4OID, INT4OID};
842 text *formula = PG_GETARG_TEXT_P(2);
844 fcinfo->flinfo->fn_extra = SearchArrayCache(
845 fcinfo->flinfo->fn_extra,
846 fcinfo->flinfo->fn_mcxt,
847 PG_GETARG_DATUM(0), &a, &sa, NULL);
848 fcinfo->flinfo->fn_extra = SearchArrayCache(
849 fcinfo->flinfo->fn_extra,
850 fcinfo->flinfo->fn_mcxt,
851 PG_GETARG_DATUM(1), &b, &sb, NULL);
853 if ( ARR_ELEMTYPE(a) != ARR_ELEMTYPE(b) )
854 elog(ERROR,"Arguments array are not the same type!");
856 if (ARRISVOID(a) || ARRISVOID(b))
857 PG_RETURN_BOOL(false);
859 cnt = numOfIntersect(sa, sb);
861 if ( VARSIZE(formula) - VARHDRSZ > QBSIZE - 1024 )
862 elog(ERROR,"Formula is too long");
867 if ( cachedPlan == NULL || cachedLen != VARSIZE(formula) - VARHDRSZ ||
868 memcmp( cachedFormula, VARDATA(formula), VARSIZE(formula) - VARHDRSZ ) != 0 )
870 char *ptr, buf[QBSIZE];
872 *cachedFormula = '\0';
874 SPI_freeplan(cachedPlan);
878 ptr = stpcpy( buf, "SELECT (" );
879 memcpy( ptr, VARDATA(formula), VARSIZE(formula) - VARHDRSZ );
880 ptr += VARSIZE(formula) - VARHDRSZ;
881 ptr = stpcpy( ptr, ")::float4 FROM");
882 ptr = stpcpy( ptr, " (SELECT $1 ::float8 AS i, $2 ::float8 AS a, $3 ::float8 AS b) AS N;");
885 plan = SPI_prepare(buf, 3, arg);
887 elog(ERROR, "SPI_prepare() failed");
889 cachedPlan = SPI_saveplan(plan);
891 elog(ERROR, "SPI_saveplan() failed");
894 cachedLen = VARSIZE(formula) - VARHDRSZ;
895 memcpy( cachedFormula, VARDATA(formula), VARSIZE(formula) - VARHDRSZ );
901 pars[0] = Int32GetDatum( cnt );
902 pars[1] = Int32GetDatum( sa->nelems );
903 pars[2] = Int32GetDatum( sb->nelems );
905 stat = SPI_execute_plan(plan, pars, NULL, true, 3);
907 elog(ERROR, "SPI_execute_plan() returns %d", stat);
909 if ( SPI_processed > 0)
910 result = DatumGetFloat4(SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull));
914 PG_RETURN_FLOAT4(result);
917 PG_FUNCTION_INFO_V1(array_unique);
918 Datum array_unique(PG_FUNCTION_ARGS);
920 array_unique(PG_FUNCTION_ARGS)
922 ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
926 sa = Array2SimpleArrayU(NULL, a, NULL);
928 res = construct_array( sa->elems,
937 PG_FREE_IF_COPY(a, 0);
939 PG_RETURN_ARRAYTYPE_P(res);
942 PG_FUNCTION_INFO_V1(inarray);
943 Datum inarray(PG_FUNCTION_ARGS);
945 inarray(PG_FUNCTION_ARGS)
949 Datum query = PG_GETARG_DATUM(1);
956 fcinfo->flinfo->fn_extra = SearchArrayCache(
957 fcinfo->flinfo->fn_extra,
958 fcinfo->flinfo->fn_mcxt,
959 PG_GETARG_DATUM(0), &a, &sa, NULL);
961 queryTypeOid = get_fn_expr_argtype(fcinfo->flinfo, 1);
963 if ( queryTypeOid == InvalidOid )
964 elog(ERROR,"inarray: could not determine actual argument type");
966 if ( queryTypeOid != sa->info->typid )
967 elog(ERROR,"inarray: Type of array's element and type of argument are not the same");
969 getFmgrInfoCmp(sa->info);
971 StopHigh = sa->elems + sa->nelems;
973 while (StopLow < StopHigh)
975 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
976 cmp = cmpArrayElem(StopMiddle, &query, sa->info);
981 if ( PG_NARGS() >= 3 )
982 PG_RETURN_DATUM(PG_GETARG_DATUM(2));
983 PG_RETURN_FLOAT4(1.0);
986 StopLow = StopMiddle + 1;
988 StopHigh = StopMiddle;
991 if ( PG_NARGS() >= 4 )
992 PG_RETURN_DATUM(PG_GETARG_DATUM(3));
993 PG_RETURN_FLOAT4(0.0);