Datum box2df_out(PG_FUNCTION_ARGS) { ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("function box2df_out not implemented"))); PG_RETURN_POINTER(NULL); }
Datum gserialized_gist_compress_2d(PG_FUNCTION_ARGS) { GISTENTRY *entry_in = (GISTENTRY*)PG_GETARG_POINTER(0); GISTENTRY *entry_out = NULL; BOX2DF bbox_out; int result = LW_SUCCESS; POSTGIS_DEBUG(4, "[GIST] 'compress' function called"); /* ** Not a leaf key? There's nothing to do. ** Return the input unchanged. */ if ( ! entry_in->leafkey ) { POSTGIS_DEBUG(4, "[GIST] non-leafkey entry, returning input unaltered"); PG_RETURN_POINTER(entry_in); } POSTGIS_DEBUG(4, "[GIST] processing leafkey input"); entry_out = palloc(sizeof(GISTENTRY)); /* ** Null key? Make a copy of the input entry and ** return. */ if ( DatumGetPointer(entry_in->key) == NULL ) { POSTGIS_DEBUG(4, "[GIST] leafkey is null"); gistentryinit(*entry_out, (Datum) 0, entry_in->rel, entry_in->page, entry_in->offset, FALSE); POSTGIS_DEBUG(4, "[GIST] returning copy of input"); PG_RETURN_POINTER(entry_out); } /* Extract our index key from the GiST entry. */ result = gserialized_datum_get_box2df_p(entry_in->key, &bbox_out); /* Is the bounding box valid (non-empty, non-infinite)? If not, return input uncompressed. */ if ( result == LW_FAILURE ) { POSTGIS_DEBUG(4, "[GIST] empty geometry!"); PG_RETURN_POINTER(entry_in); } POSTGIS_DEBUGF(4, "[GIST] got entry_in->key: %s", box2df_to_string(&bbox_out)); /* Check all the dimensions for finite values */ if ( ! finite(bbox_out.xmax) || ! finite(bbox_out.xmin) || ! finite(bbox_out.ymax) || ! finite(bbox_out.ymin) ) { POSTGIS_DEBUG(4, "[GIST] infinite geometry!"); PG_RETURN_POINTER(entry_in); } /* Enure bounding box has minimums below maximums. */ box2df_validate(&bbox_out); /* Prepare GISTENTRY for return. */ gistentryinit(*entry_out, PointerGetDatum(box2df_copy(&bbox_out)), entry_in->rel, entry_in->page, entry_in->offset, FALSE); /* Return GISTENTRY. */ POSTGIS_DEBUG(4, "[GIST] 'compress' function complete"); PG_RETURN_POINTER(entry_out); }
/* * in without morphology */ Datum ltxtq_in(PG_FUNCTION_ARGS) { PG_RETURN_POINTER(queryin((char *) PG_GETARG_POINTER(0))); }
Datum gserialized_gist_picksplit_2d(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); OffsetNumber i; OffsetNumber *listL, *listR, *listB, *listT; BOX2DF *unionL, *unionR, *unionB, *unionT; int posL, posR, posB, posT; BOX2DF pageunion; BOX2DF *cur; char direction = ' '; bool allisequal = true; OffsetNumber maxoff; int nbytes; POSTGIS_DEBUG(3, "[GIST] 'picksplit' entered"); posL = posR = posB = posT = 0; maxoff = entryvec->n - 1; cur = (BOX2DF*) DatumGetPointer(entryvec->vector[FirstOffsetNumber].key); memcpy((void *) &pageunion, (void *) cur, sizeof(BOX2DF)); /* find MBR */ for (i = OffsetNumberNext(FirstOffsetNumber); i <= maxoff; i = OffsetNumberNext(i)) { cur = (BOX2DF *) DatumGetPointer(entryvec->vector[i].key); if ( allisequal == true && ( pageunion.xmax != cur->xmax || pageunion.ymax != cur->ymax || pageunion.xmin != cur->xmin || pageunion.ymin != cur->ymin ) ) allisequal = false; if (pageunion.xmax < cur->xmax) pageunion.xmax = cur->xmax; if (pageunion.xmin > cur->xmin) pageunion.xmin = cur->xmin; if (pageunion.ymax < cur->ymax) pageunion.ymax = cur->ymax; if (pageunion.ymin > cur->ymin) pageunion.ymin = cur->ymin; } POSTGIS_DEBUGF(4, "pageunion is %s", box2df_to_string(&pageunion)); nbytes = (maxoff + 2) * sizeof(OffsetNumber); listL = (OffsetNumber *) palloc(nbytes); listR = (OffsetNumber *) palloc(nbytes); unionL = (BOX2DF *) palloc(sizeof(BOX2DF)); unionR = (BOX2DF *) palloc(sizeof(BOX2DF)); if (allisequal) { POSTGIS_DEBUG(4, " AllIsEqual!"); cur = (BOX2DF*) DatumGetPointer(entryvec->vector[OffsetNumberNext(FirstOffsetNumber)].key); if (memcmp((void *) cur, (void *) &pageunion, sizeof(BOX2DF)) == 0) { v->spl_left = listL; v->spl_right = listR; v->spl_nleft = v->spl_nright = 0; memcpy((void *) unionL, (void *) &pageunion, sizeof(BOX2DF)); memcpy((void *) unionR, (void *) &pageunion, sizeof(BOX2DF)); for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { if (i <= (maxoff - FirstOffsetNumber + 1) / 2) { v->spl_left[v->spl_nleft] = i; v->spl_nleft++; } else { v->spl_right[v->spl_nright] = i; v->spl_nright++; } } v->spl_ldatum = PointerGetDatum(unionL); v->spl_rdatum = PointerGetDatum(unionR); PG_RETURN_POINTER(v); } } listB = (OffsetNumber *) palloc(nbytes); listT = (OffsetNumber *) palloc(nbytes); unionB = (BOX2DF *) palloc(sizeof(BOX2DF)); unionT = (BOX2DF *) palloc(sizeof(BOX2DF)); #define ADDLIST( list, unionD, pos, num ) do { \ if ( pos ) { \ if ( unionD->xmax < cur->xmax ) unionD->xmax = cur->xmax; \ if ( unionD->xmin > cur->xmin ) unionD->xmin = cur->xmin; \ if ( unionD->ymax < cur->ymax ) unionD->ymax = cur->ymax; \ if ( unionD->ymin > cur->ymin ) unionD->ymin = cur->ymin; \ } else { \ memcpy( (void*)unionD, (void*) cur, sizeof( BOX2DF ) ); \ } \ list[pos] = num; \ (pos)++; \ } while(0) for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { cur = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key); if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax) ADDLIST(listL, unionL, posL,i); else ADDLIST(listR, unionR, posR,i); if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax) ADDLIST(listB, unionB, posB,i); else ADDLIST(listT, unionT, posT,i); } POSTGIS_DEBUGF(4, "unionL is %s", box2df_to_string(unionL)); POSTGIS_DEBUGF(4, "unionR is %s", box2df_to_string(unionR)); POSTGIS_DEBUGF(4, "unionT is %s", box2df_to_string(unionT)); POSTGIS_DEBUGF(4, "unionB is %s", box2df_to_string(unionB)); /* bad disposition, sort by ascending and resplit */ if ( (posR==0 || posL==0) && (posT==0 || posB==0) ) { KBsort *arr = (KBsort*)palloc( sizeof(KBsort) * maxoff ); posL = posR = posB = posT = 0; for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { arr[i-1].key = (BOX2DF*) DatumGetPointer(entryvec->vector[i].key); arr[i-1].pos = i; } qsort( arr, maxoff, sizeof(KBsort), compare_KB ); for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { cur = arr[i-1].key; if (cur->xmin - pageunion.xmin < pageunion.xmax - cur->xmax) ADDLIST(listL, unionL, posL,arr[i-1].pos); else if ( cur->xmin - pageunion.xmin == pageunion.xmax - cur->xmax ) { if ( posL>posR ) ADDLIST(listR, unionR, posR,arr[i-1].pos); else ADDLIST(listL, unionL, posL,arr[i-1].pos); } else ADDLIST(listR, unionR, posR,arr[i-1].pos); if (cur->ymin - pageunion.ymin < pageunion.ymax - cur->ymax) ADDLIST(listB, unionB, posB,arr[i-1].pos); else if ( cur->ymin - pageunion.ymin == pageunion.ymax - cur->ymax ) { if ( posB>posT ) ADDLIST(listT, unionT, posT,arr[i-1].pos); else ADDLIST(listB, unionB, posB,arr[i-1].pos); } else ADDLIST(listT, unionT, posT,arr[i-1].pos); } pfree(arr); } /* which split more optimal? */ if (Max(posL, posR) < Max(posB, posT)) direction = 'x'; else if (Max(posL, posR) > Max(posB, posT)) direction = 'y'; else { float sizeLR, sizeBT; BOX2DF interLR, interBT; if ( box2df_intersection(unionL, unionR, &interLR) == FALSE ) sizeLR = 0.0; else sizeLR = box2df_size(&interLR); if ( box2df_intersection(unionB, unionT, &interBT) == FALSE ) sizeBT = 0.0; else sizeBT = box2df_size(&interBT); if (sizeLR < sizeBT) direction = 'x'; else direction = 'y'; } POSTGIS_DEBUGF(4, "split direction '%c'", direction); if (direction == 'x') { pfree(unionB); pfree(listB); pfree(unionT); pfree(listT); v->spl_left = listL; v->spl_right = listR; v->spl_nleft = posL; v->spl_nright = posR; v->spl_ldatum = PointerGetDatum(unionL); v->spl_rdatum = PointerGetDatum(unionR); } else { pfree(unionR); pfree(listR); pfree(unionL); pfree(listL); v->spl_left = listB; v->spl_right = listT; v->spl_nleft = posB; v->spl_nright = posT; v->spl_ldatum = PointerGetDatum(unionB); v->spl_rdatum = PointerGetDatum(unionT); } POSTGIS_DEBUG(4, "[GIST] 'picksplit' completed"); PG_RETURN_POINTER(v); }
Datum LWGEOM_interiorringn_polygon(PG_FUNCTION_ARGS) { GSERIALIZED *geom; int32 wanted_index; LWCURVEPOLY *curvepoly = NULL; LWPOLY *poly = NULL; POINTARRAY *ring; LWLINE *line; LWGEOM *lwgeom; GSERIALIZED *result; GBOX *bbox = NULL; int type; POSTGIS_DEBUG(2, "LWGEOM_interierringn_polygon called."); wanted_index = PG_GETARG_INT32(1); if ( wanted_index < 1 ) { /* elog(ERROR, "InteriorRingN: ring number is 1-based"); */ PG_RETURN_NULL(); /* index out of range */ } geom = PG_GETARG_GSERIALIZED_P(0); type = gserialized_get_type(geom); if ( (type != POLYGONTYPE) && (type != CURVEPOLYTYPE) ) { elog(ERROR, "InteriorRingN: geom is not a polygon"); PG_FREE_IF_COPY(geom, 0); PG_RETURN_NULL(); } lwgeom = lwgeom_from_gserialized(geom); if( lwgeom_is_empty(lwgeom) ) { lwpoly_free(poly); PG_FREE_IF_COPY(geom, 0); PG_RETURN_NULL(); } if ( type == POLYGONTYPE) { poly = lwgeom_as_lwpoly(lwgeom_from_gserialized(geom)); /* Ok, now we have a polygon. Let's see if it has enough holes */ if ( wanted_index >= poly->nrings ) { lwpoly_free(poly); PG_FREE_IF_COPY(geom, 0); PG_RETURN_NULL(); } ring = poly->rings[wanted_index]; /* COMPUTE_BBOX==TAINTING */ if ( poly->bbox ) { bbox = lwalloc(sizeof(GBOX)); ptarray_calculate_gbox_cartesian(ring, bbox); } /* This is a LWLINE constructed by interior ring POINTARRAY */ line = lwline_construct(poly->srid, bbox, ring); result = geometry_serialize((LWGEOM *)line); lwline_release(line); lwpoly_free(poly); } else { curvepoly = lwgeom_as_lwcurvepoly(lwgeom_from_gserialized(geom)); if (wanted_index >= curvepoly->nrings) { PG_FREE_IF_COPY(geom, 0); lwgeom_release((LWGEOM *)curvepoly); PG_RETURN_NULL(); } result = geometry_serialize(curvepoly->rings[wanted_index]); lwgeom_free((LWGEOM*)curvepoly); } PG_FREE_IF_COPY(geom, 0); PG_RETURN_POINTER(result); }
Datum LWGEOM_exteriorring_polygon(PG_FUNCTION_ARGS) { GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0); GSERIALIZED *result; POINTARRAY *extring; LWGEOM *lwgeom; LWLINE *line; GBOX *bbox=NULL; int type = gserialized_get_type(geom); POSTGIS_DEBUG(2, "LWGEOM_exteriorring_polygon called."); if ( (type != POLYGONTYPE) && (type != CURVEPOLYTYPE) && (type != TRIANGLETYPE)) { elog(ERROR, "ExteriorRing: geom is not a polygon"); PG_RETURN_NULL(); } lwgeom = lwgeom_from_gserialized(geom); if( lwgeom_is_empty(lwgeom) ) { line = lwline_construct_empty(lwgeom->srid, lwgeom_has_z(lwgeom), lwgeom_has_m(lwgeom)); result = geometry_serialize(lwline_as_lwgeom(line)); } else if ( lwgeom->type == POLYGONTYPE ) { LWPOLY *poly = lwgeom_as_lwpoly(lwgeom); /* Ok, now we have a polygon. Here is its exterior ring. */ extring = poly->rings[0]; /* * This is a LWLINE constructed by exterior ring POINTARRAY * If the input geom has a bbox, use it for * the output geom, as exterior ring makes it up ! */ if ( poly->bbox ) bbox = gbox_copy(poly->bbox); line = lwline_construct(poly->srid, bbox, extring); result = geometry_serialize((LWGEOM *)line); lwgeom_release((LWGEOM *)line); } else if ( lwgeom->type == TRIANGLETYPE ) { LWTRIANGLE *triangle = lwgeom_as_lwtriangle(lwgeom); /* * This is a LWLINE constructed by exterior ring POINTARRAY * If the input geom has a bbox, use it for * the output geom, as exterior ring makes it up ! */ if ( triangle->bbox ) bbox = gbox_copy(triangle->bbox); line = lwline_construct(triangle->srid, bbox, triangle->points); result = geometry_serialize((LWGEOM *)line); lwgeom_release((LWGEOM *)line); } else { LWCURVEPOLY *curvepoly = lwgeom_as_lwcurvepoly(lwgeom); result = geometry_serialize(curvepoly->rings[0]); } lwgeom_free(lwgeom); PG_FREE_IF_COPY(geom, 0); PG_RETURN_POINTER(result); }
Datum gtsquery_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); OffsetNumber maxoff = entryvec->n - 2; OffsetNumber k, j; TSQuerySign datum_l, datum_r; int32 size_alpha, size_beta; int32 size_waste, waste = -1; int32 nbytes; OffsetNumber seed_1 = 0, seed_2 = 0; OffsetNumber *left, *right; SPLITCOST *costvector; nbytes = (maxoff + 2) * sizeof(OffsetNumber); left = v->spl_left = (OffsetNumber *) palloc(nbytes); right = v->spl_right = (OffsetNumber *) palloc(nbytes); v->spl_nleft = v->spl_nright = 0; for (k = FirstOffsetNumber; k < maxoff; k = OffsetNumberNext(k)) for (j = OffsetNumberNext(k); j <= maxoff; j = OffsetNumberNext(j)) { size_waste = hemdist(GETENTRY(entryvec, j), GETENTRY(entryvec, k)); if (size_waste > waste) { waste = size_waste; seed_1 = k; seed_2 = j; } } if (seed_1 == 0 || seed_2 == 0) { seed_1 = 1; seed_2 = 2; } datum_l = GETENTRY(entryvec, seed_1); datum_r = GETENTRY(entryvec, seed_2); maxoff = OffsetNumberNext(maxoff); costvector = (SPLITCOST *) palloc(sizeof(SPLITCOST) * maxoff); for (j = FirstOffsetNumber; j <= maxoff; j = OffsetNumberNext(j)) { costvector[j - 1].pos = j; size_alpha = hemdist(GETENTRY(entryvec, seed_1), GETENTRY(entryvec, j)); size_beta = hemdist(GETENTRY(entryvec, seed_2), GETENTRY(entryvec, j)); costvector[j - 1].cost = abs(size_alpha - size_beta); } qsort((void *) costvector, maxoff, sizeof(SPLITCOST), comparecost); for (k = 0; k < maxoff; k++) { j = costvector[k].pos; if (j == seed_1) { *left++ = j; v->spl_nleft++; continue; } else if (j == seed_2) { *right++ = j; v->spl_nright++; continue; } size_alpha = hemdist(datum_l, GETENTRY(entryvec, j)); size_beta = hemdist(datum_r, GETENTRY(entryvec, j)); if (size_alpha < size_beta + WISH_F(v->spl_nleft, v->spl_nright, 0.05)) { datum_l |= GETENTRY(entryvec, j); *left++ = j; v->spl_nleft++; } else { datum_r |= GETENTRY(entryvec, j); *right++ = j; v->spl_nright++; } } *right = *left = FirstOffsetNumber; v->spl_ldatum = TSQuerySignGetDatum(datum_l); v->spl_rdatum = TSQuerySignGetDatum(datum_r); PG_RETURN_POINTER(v); }
Datum gin_extract_query_trgm(PG_FUNCTION_ARGS) { text *val = (text *) PG_GETARG_TEXT_P(0); int32 *nentries = (int32 *) PG_GETARG_POINTER(1); StrategyNumber strategy = PG_GETARG_UINT16(2); /* bool **pmatch = (bool **) PG_GETARG_POINTER(3); */ /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */ /* bool **nullFlags = (bool **) PG_GETARG_POINTER(5); */ int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); Datum *entries = NULL; TRGM *trg; int32 trglen; trgm *ptr; int32 i; switch (strategy) { case SimilarityStrategyNumber: trg = generate_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); break; case ILikeStrategyNumber: #ifndef IGNORECASE elog(ERROR, "cannot handle ~~* with case-sensitive trigrams"); #endif /* FALL THRU */ case LikeStrategyNumber: /* * For wildcard search we extract all the trigrams that every * potentially-matching string must include. */ trg = generate_wildcard_trgm(VARDATA(val), VARSIZE(val) - VARHDRSZ); break; default: elog(ERROR, "unrecognized strategy number: %d", strategy); trg = NULL; /* keep compiler quiet */ break; } trglen = ARRNELEM(trg); *nentries = trglen; if (trglen > 0) { entries = (Datum *) palloc(sizeof(Datum) * trglen); ptr = GETARR(trg); for (i = 0; i < trglen; i++) { int32 item = trgm2int(ptr); entries[i] = Int32GetDatum(item); ptr++; } } /* * If no trigram was extracted then we have to scan all the index. */ if (trglen == 0) *searchMode = GIN_SEARCH_MODE_ALL; PG_RETURN_POINTER(entries); }
Datum geometry_estimated_extent(PG_FUNCTION_ARGS) { text *txnsp = NULL; text *txtbl = NULL; text *txcol = NULL; char *nsp = NULL; char *tbl = NULL; char *col = NULL; char *query; ArrayType *array = NULL; int SPIcode; SPITupleTable *tuptable; TupleDesc tupdesc ; HeapTuple tuple ; bool isnull; GBOX *box; size_t querysize; GEOM_STATS geomstats; float reltuples; Datum binval; if ( PG_NARGS() == 3 ) { txnsp = PG_GETARG_TEXT_P(0); txtbl = PG_GETARG_TEXT_P(1); txcol = PG_GETARG_TEXT_P(2); } else if ( PG_NARGS() == 2 ) { txtbl = PG_GETARG_TEXT_P(0); txcol = PG_GETARG_TEXT_P(1); } else { elog(ERROR, "estimated_extent() called with wrong number of arguments"); PG_RETURN_NULL(); } POSTGIS_DEBUG(2, "geomtery_estimated_extent called"); /* Connect to SPI manager */ SPIcode = SPI_connect(); if (SPIcode != SPI_OK_CONNECT) { elog(ERROR, "geometry_estimated_extent: couldnt open a connection to SPI"); PG_RETURN_NULL() ; } querysize = VARSIZE(txtbl)+VARSIZE(txcol)+516; if ( txnsp ) { nsp = text2cstring(txnsp); querysize += VARSIZE(txnsp); } else { querysize += 32; /* current_schema() */ } tbl = text2cstring(txtbl); col = text2cstring(txcol); #if POSTGIS_DEBUG_LEVEL > 0 if ( txnsp ) { POSTGIS_DEBUGF(3, " schema:%s table:%s column:%s", nsp, tbl, col); } else { POSTGIS_DEBUGF(3, " schema:current_schema() table:%s column:%s", tbl, col); } #endif query = palloc(querysize); /* Security check: because we access information in the pg_statistic table, we must run as the database superuser (by marking the function as SECURITY DEFINER) and check permissions ourselves */ if ( txnsp ) { sprintf(query, "SELECT has_table_privilege((SELECT usesysid FROM pg_user WHERE usename = session_user), '\"%s\".\"%s\"', 'select')", nsp, tbl); } else { sprintf(query, "SELECT has_table_privilege((SELECT usesysid FROM pg_user WHERE usename = session_user), '\"%s\"', 'select')", tbl); } POSTGIS_DEBUGF(4, "permission check sql query is: %s", query); SPIcode = SPI_exec(query, 1); if (SPIcode != SPI_OK_SELECT) { elog(ERROR, "geometry_estimated_extent: couldn't execute permission check sql via SPI"); SPI_finish(); PG_RETURN_NULL(); } tuptable = SPI_tuptable; tupdesc = SPI_tuptable->tupdesc; tuple = tuptable->vals[0]; if (!DatumGetBool(SPI_getbinval(tuple, tupdesc, 1, &isnull))) { elog(ERROR, "geometry_estimated_extent: permission denied for relation %s", tbl); SPI_finish(); PG_RETURN_NULL(); } /* Return the stats data */ if ( txnsp ) { sprintf(query, "SELECT s.stanumbers1[5:8], c.reltuples FROM pg_class c" " LEFT OUTER JOIN pg_namespace n ON (n.oid = c.relnamespace)" " LEFT OUTER JOIN pg_attribute a ON (a.attrelid = c.oid )" " LEFT OUTER JOIN pg_statistic s ON (s.starelid = c.oid AND " "s.staattnum = a.attnum )" " WHERE c.relname = '%s' AND a.attname = '%s' " " AND n.nspname = '%s';", tbl, col, nsp); } else { sprintf(query, "SELECT s.stanumbers1[5:8], c.reltuples FROM pg_class c" " LEFT OUTER JOIN pg_namespace n ON (n.oid = c.relnamespace)" " LEFT OUTER JOIN pg_attribute a ON (a.attrelid = c.oid )" " LEFT OUTER JOIN pg_statistic s ON (s.starelid = c.oid AND " "s.staattnum = a.attnum )" " WHERE c.relname = '%s' AND a.attname = '%s' " " AND n.nspname = current_schema();", tbl, col); } POSTGIS_DEBUGF(4, " query: %s", query); SPIcode = SPI_exec(query, 1); if (SPIcode != SPI_OK_SELECT ) { elog(ERROR,"geometry_estimated_extent: couldnt execute sql via SPI"); SPI_finish(); PG_RETURN_NULL(); } if (SPI_processed != 1) { POSTGIS_DEBUGF(3, " %d stat rows", SPI_processed); elog(ERROR, "Unexistent field \"%s\".\"%s\".\"%s\"", ( nsp ? nsp : "<current>" ), tbl, col); SPI_finish(); PG_RETURN_NULL() ; } tuptable = SPI_tuptable; tupdesc = SPI_tuptable->tupdesc; tuple = tuptable->vals[0]; /* Check if the table has zero rows first */ binval = SPI_getbinval(tuple, tupdesc, 2, &isnull); if (isnull) { POSTGIS_DEBUG(3, " reltuples is NULL"); elog(ERROR, "geometry_estimated_extent: null reltuples for table"); SPI_finish(); PG_RETURN_NULL(); } reltuples = DatumGetFloat4(binval); if ( ! reltuples ) { POSTGIS_DEBUG(3, "table has estimated zero rows"); /* * TODO: distinguish between empty and not analyzed ? */ elog(NOTICE, "\"%s\".\"%s\".\"%s\" is empty or not analyzed", ( nsp ? nsp : "<current>" ), tbl, col); SPI_finish(); PG_RETURN_NULL(); } binval = SPI_getbinval(tuple, tupdesc, 1, &isnull); if (isnull) { POSTGIS_DEBUG(3, " stats are NULL"); elog(ERROR, "geometry_estimated_extent: null statistics for table"); SPI_finish(); PG_RETURN_NULL(); } array = DatumGetArrayTypeP(binval); if ( ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) != 4 ) { elog(ERROR, " corrupted histogram"); PG_RETURN_NULL(); } POSTGIS_DEBUGF(3, " stats array has %d elems", ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array))); /* * Construct GBOX. * Must allocate this in upper executor context * to keep it alive after SPI_finish(). */ box = SPI_palloc(sizeof(GBOX)); FLAGS_SET_GEODETIC(box->flags, 0); FLAGS_SET_Z(box->flags, 0); FLAGS_SET_M(box->flags, 0); /* Construct the box */ memcpy(&(geomstats.xmin), ARR_DATA_PTR(array), sizeof(float)*4); box->xmin = geomstats.xmin; box->xmax = geomstats.xmax; box->ymin = geomstats.ymin; box->ymax = geomstats.ymax; POSTGIS_DEBUGF(3, " histogram extent = %g %g, %g %g", box->xmin, box->ymin, box->xmax, box->ymax); SPIcode = SPI_finish(); if (SPIcode != SPI_OK_FINISH ) { elog(ERROR, "geometry_estimated_extent: couldn't disconnect from SPI"); } /* TODO: enlarge the box by some factor */ PG_RETURN_POINTER(box); }