/** * Read the dimensionality from a flag, if provided. Then check that the * dimensionality matches that of the pointarray. If the dimension counts * match, ensure the pointarray is using the right "Z" or "M". */ static int wkt_pointarray_dimensionality(POINTARRAY *pa, uint8_t flags) { int hasz = FLAGS_GET_Z(flags); int hasm = FLAGS_GET_M(flags); int ndims = 2 + hasz + hasm; /* No dimensionality or array means we go with what we have */ if( ! (flags && pa) ) return LW_TRUE; LWDEBUGF(5,"dimensionality ndims == %d", ndims); LWDEBUGF(5,"FLAGS_NDIMS(pa->flags) == %d", FLAGS_NDIMS(pa->flags)); /* * ndims > 2 implies that the flags have something useful to add, * that there is a 'Z' or an 'M' or both. */ if( ndims > 2 ) { /* Mismatch implies a problem */ if ( FLAGS_NDIMS(pa->flags) != ndims ) return LW_FALSE; /* Match means use the explicit dimensionality */ else { FLAGS_SET_Z(pa->flags, hasz); FLAGS_SET_M(pa->flags, hasm); } } return LW_TRUE; }
/** * Build a 2d coordinate. */ POINT wkt_parser_coord_2(double c1, double c2) { POINT p; p.flags = 0; p.x = c1; p.y = c2; p.z = p.m = 0.0; FLAGS_SET_Z(p.flags, 0); FLAGS_SET_M(p.flags, 0); return p; };
POINT wkt_parser_coord_4(double c1, double c2, double c3, double c4) { POINT p; p.flags = 0; p.x = c1; p.y = c2; p.z = c3; p.m = c4; FLAGS_SET_Z(p.flags, 1); FLAGS_SET_M(p.flags, 1); return p; };
Datum LWGEOM_to_BOX2DF(PG_FUNCTION_ARGS) { GSERIALIZED *geom = PG_GETARG_GSERIALIZED_P(0); GBOX gbox; if ( gserialized_get_gbox_p(geom, &gbox) == LW_FAILURE ) PG_RETURN_NULL(); /* Strip out higher dimensions */ FLAGS_SET_Z(gbox.flags, 0); FLAGS_SET_M(gbox.flags, 0); PG_FREE_IF_COPY(geom, 0); PG_RETURN_POINTER(gbox_copy(&gbox)); }
static uint8_t wkt_dimensionality(char *dimensionality) { int i = 0; uint8_t flags = 0; if( ! dimensionality ) return flags; /* If there's an explicit dimensionality, we use that */ for( i = 0; i < strlen(dimensionality); i++ ) { if( (dimensionality[i] == 'Z') || (dimensionality[i] == 'z') ) FLAGS_SET_Z(flags,1); if( (dimensionality[i] == 'M') || (dimensionality[i] == 'm') ) FLAGS_SET_M(flags,1); } return flags; }
static uint8_t wkt_dimensionality(char *dimensionality) { int i = 0; uint8_t flags = 0; if( ! dimensionality ) return flags; /* If there's an explicit dimensionality, we use that */ for( i = 0; i < strlen(dimensionality); i++ ) { if( (dimensionality[i] == 'Z') || (dimensionality[i] == 'z') ) FLAGS_SET_Z(flags,1); else if( (dimensionality[i] == 'M') || (dimensionality[i] == 'm') ) FLAGS_SET_M(flags,1); /* only a space is accepted in between */ else if( ! isspace(dimensionality[i]) ) break; } return flags; }
Datum LWGEOM_to_BOX2D(PG_FUNCTION_ARGS) { GSERIALIZED *geom = (GSERIALIZED *)PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); LWGEOM *lwgeom = lwgeom_from_gserialized(geom); GBOX gbox; /* Cannot box empty! */ if ( lwgeom_is_empty(lwgeom) ) PG_RETURN_NULL(); /* Cannot calculate box? */ if ( lwgeom_calculate_gbox(lwgeom, &gbox) == LW_FAILURE ) PG_RETURN_NULL(); /* Strip out higher dimensions */ FLAGS_SET_Z(gbox.flags, 0); FLAGS_SET_M(gbox.flags, 0); PG_RETURN_POINTER(gbox_copy(&gbox)); }
/* * Create a new empty tgeom struct * Return a pointer on the newly allocated struct */ TGEOM* tgeom_new(uint8_t type, int hasz, int hasm) { TGEOM *tgeom; tgeom = lwalloc(sizeof(TGEOM)); tgeom->type = type; FLAGS_SET_Z(tgeom->flags, hasz); FLAGS_SET_M(tgeom->flags, hasm); tgeom->bbox=NULL; tgeom->srid=0; tgeom->nedges=0; tgeom->maxedges=0; tgeom->edges=NULL; tgeom->maxfaces=0; tgeom->nfaces=0; tgeom->faces=NULL; return tgeom; }
/* * Construct a new point. point will not be copied * use SRID=SRID_UNKNOWN for unknown SRID (will have 8bit type's S = 0) */ LWPOINT * lwpoint_construct(int srid, GBOX *bbox, POINTARRAY *point) { LWPOINT *result; uint8_t flags = 0; if (point == NULL) return NULL; /* error */ result = lwalloc(sizeof(LWPOINT)); result->type = POINTTYPE; FLAGS_SET_Z(flags, FLAGS_GET_Z(point->flags)); FLAGS_SET_M(flags, FLAGS_GET_M(point->flags)); FLAGS_SET_BBOX(flags, bbox?1:0); result->flags = flags; result->srid = srid; result->point = point; result->bbox = bbox; return result; }
/** * Force the dimensionality of a geometry to match the dimensionality * of a set of flags (usually derived from a ZM WKT tag). */ static int wkt_parser_set_dims(LWGEOM *geom, uint8_t flags) { int hasz = FLAGS_GET_Z(flags); int hasm = FLAGS_GET_M(flags); int i = 0; /* Error on junk */ if( ! geom ) return LW_FAILURE; FLAGS_SET_Z(geom->flags, hasz); FLAGS_SET_M(geom->flags, hasm); if( ! lwgeom_is_empty(geom) ) { if( geom->type == POINTTYPE ) { LWPOINT *pt = (LWPOINT*)geom; FLAGS_SET_Z(pt->point->flags, hasz); FLAGS_SET_M(pt->point->flags, hasm); return LW_SUCCESS; } else if ( (geom->type == TRIANGLETYPE) || (geom->type == CIRCSTRINGTYPE) || (geom->type == LINETYPE) ) { LWLINE *ln = (LWLINE*)geom; FLAGS_SET_Z(ln->points->flags, hasz); FLAGS_SET_M(ln->points->flags, hasm); return LW_SUCCESS; } else if ( geom->type == POLYGONTYPE ) { LWPOLY *poly = (LWPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) { FLAGS_SET_Z(poly->rings[i]->flags, hasz); FLAGS_SET_M(poly->rings[i]->flags, hasm); } return LW_SUCCESS; } else if ( geom->type == CURVEPOLYTYPE ) { LWCURVEPOLY *poly = (LWCURVEPOLY*)geom; for ( i = 0; i < poly->nrings; i++ ) wkt_parser_set_dims(poly->rings[i], flags); return LW_SUCCESS; } else if ( lwtype_is_collection(geom->type) ) { LWCOLLECTION *col = (LWCOLLECTION*)geom; for ( i = 0; i < col->ngeoms; i++ ) wkt_parser_set_dims(col->geoms[i], flags); return LW_SUCCESS; } else { LWDEBUGF(2,"Unknown geometry type: %d", geom->type); return LW_FAILURE; } } return LW_SUCCESS; }
/** * Clip an input MULTILINESTRING between two values, on any ordinate input. */ LWCOLLECTION* lwmline_clip_to_ordinate_range(const LWMLINE *mline, char ordinate, double from, double to) { LWCOLLECTION *lwgeom_out = NULL; if ( ! mline ) { lwerror("Null input geometry."); return NULL; } if ( mline->ngeoms == 1) { lwgeom_out = lwline_clip_to_ordinate_range(mline->geoms[0], ordinate, from, to); } else { LWCOLLECTION *col; char hasz = lwgeom_has_z(lwmline_as_lwgeom(mline)); char hasm = lwgeom_has_m(lwmline_as_lwgeom(mline)); int i, j; char homogeneous = 1; size_t geoms_size = 0; lwgeom_out = lwcollection_construct_empty(MULTILINETYPE, mline->srid, hasz, hasm); FLAGS_SET_Z(lwgeom_out->flags, hasz); FLAGS_SET_M(lwgeom_out->flags, hasm); for ( i = 0; i < mline->ngeoms; i ++ ) { col = lwline_clip_to_ordinate_range(mline->geoms[i], ordinate, from, to); if ( col ) { /* Something was left after the clip. */ if ( lwgeom_out->ngeoms + col->ngeoms > geoms_size ) { geoms_size += 16; if ( lwgeom_out->geoms ) { lwgeom_out->geoms = lwrealloc(lwgeom_out->geoms, geoms_size * sizeof(LWGEOM*)); } else { lwgeom_out->geoms = lwalloc(geoms_size * sizeof(LWGEOM*)); } } for ( j = 0; j < col->ngeoms; j++ ) { lwgeom_out->geoms[lwgeom_out->ngeoms] = col->geoms[j]; lwgeom_out->ngeoms++; } if ( col->type != mline->type ) { homogeneous = 0; } /* Shallow free the struct, leaving the geoms behind. */ if ( col->bbox ) lwfree(col->bbox); lwfree(col->geoms); lwfree(col); } } lwgeom_drop_bbox((LWGEOM*)lwgeom_out); lwgeom_add_bbox((LWGEOM*)lwgeom_out); if ( ! homogeneous ) { lwgeom_out->type = COLLECTIONTYPE; } } if ( ! lwgeom_out || lwgeom_out->ngeoms == 0 ) /* Nothing left after clip. */ { return NULL; } return lwgeom_out; }
/** * @brief Generate an allocated geometry string for shapefile object obj using the state parameters * * This function basically deals with the polygon case. It sorts the polys in order of outer, * inner,inner, so that inners always come after outers they are within. * */ int GeneratePolygonGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry) { Ring **Outer; int polygon_total, ring_total; int pi, vi; /* part index and vertex index */ LWGEOM **lwpolygons; LWGEOM *lwgeom; POINT4D point4d; int dims = 0; char *mem; size_t mem_length; FLAGS_SET_Z(dims, state->has_z); FLAGS_SET_M(dims, state->has_m); polygon_total = FindPolygons(obj, &Outer); if (state->config->simple_geometries == 1 && polygon_total != 1) /* We write Non-MULTI geometries, but have several parts: */ { snprintf(state->message, SHPLOADERMSGLEN, _("We have a Multipolygon with %d parts, can't use -S switch!"), polygon_total); return SHPLOADERERR; } /* Allocate memory for our array of LWPOLYs */ lwpolygons = malloc(sizeof(LWPOLY *) * polygon_total); /* Cycle through each individual polygon */ for (pi = 0; pi < polygon_total; pi++) { LWPOLY *lwpoly = lwpoly_construct_empty(state->from_srid, state->has_z, state->has_m); Ring *polyring; int ring_index = 0; /* Firstly count through the total number of rings in this polygon */ ring_total = 0; polyring = Outer[pi]; while (polyring) { ring_total++; polyring = polyring->next; } /* Cycle through each ring within the polygon, starting with the outer */ polyring = Outer[pi]; while (polyring) { /* Create a POINTARRAY containing the points making up the ring */ POINTARRAY *pa = ptarray_construct_empty(state->has_z, state->has_m, polyring->n); for (vi = 0; vi < polyring->n; vi++) { /* Build up a point array of all the points in this ring */ point4d.x = polyring->list[vi].x; point4d.y = polyring->list[vi].y; if (state->has_z) point4d.z = polyring->list[vi].z; if (state->has_m) point4d.m = polyring->list[vi].m; ptarray_append_point(pa, &point4d, LW_TRUE); } /* Copy the POINTARRAY pointer so we can use the LWPOLY constructor */ lwpoly_add_ring(lwpoly, pa); polyring = polyring->next; ring_index++; } /* Generate the LWGEOM */ lwpolygons[pi] = lwpoly_as_lwgeom(lwpoly); } /* If using MULTIPOLYGONS then generate the serialized collection, otherwise just a single POLYGON */ if (state->config->simple_geometries == 0) { lwgeom = lwcollection_as_lwgeom(lwcollection_construct(MULTIPOLYGONTYPE, state->from_srid, NULL, polygon_total, lwpolygons)); } else { lwgeom = lwpolygons[0]; lwfree(lwpolygons); } if (!state->config->use_wkt) mem = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &mem_length); else mem = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, WKT_PRECISION, &mem_length); if ( !mem ) { snprintf(state->message, SHPLOADERMSGLEN, "unable to write geometry"); return SHPLOADERERR; } /* Free all of the allocated items */ lwgeom_free(lwgeom); /* Free the linked list of rings */ ReleasePolygons(Outer, polygon_total); /* Return the string - everything ok */ *geometry = mem; return SHPLOADEROK; }
/** * @brief Generate an allocated geometry string for shapefile object obj using the state parameters */ int GenerateLineStringGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry) { LWGEOM **lwmultilinestrings; LWGEOM *lwgeom = NULL; POINT4D point4d; int dims = 0; int u, v, start_vertex, end_vertex; char *mem; size_t mem_length; FLAGS_SET_Z(dims, state->has_z); FLAGS_SET_M(dims, state->has_m); if (state->config->simple_geometries == 1 && obj->nParts > 1) { snprintf(state->message, SHPLOADERMSGLEN, _("We have a Multilinestring with %d parts, can't use -S switch!"), obj->nParts); return SHPLOADERERR; } /* Allocate memory for our array of LWLINEs and our dynptarrays */ lwmultilinestrings = malloc(sizeof(LWPOINT *) * obj->nParts); /* We need an array of pointers to each of our sub-geometries */ for (u = 0; u < obj->nParts; u++) { /* Create a ptarray containing the line points */ POINTARRAY *pa = ptarray_construct_empty(state->has_z, state->has_m, obj->nParts); /* Set the start/end vertices depending upon whether this is a MULTILINESTRING or not */ if ( u == obj->nParts-1 ) end_vertex = obj->nVertices; else end_vertex = obj->panPartStart[u + 1]; start_vertex = obj->panPartStart[u]; for (v = start_vertex; v < end_vertex; v++) { /* Generate the point */ point4d.x = obj->padfX[v]; point4d.y = obj->padfY[v]; if (state->has_z) point4d.z = obj->padfZ[v]; if (state->has_m) point4d.m = obj->padfM[v]; ptarray_append_point(pa, &point4d, LW_FALSE); } /* Generate the LWLINE */ lwmultilinestrings[u] = lwline_as_lwgeom(lwline_construct(state->from_srid, NULL, pa)); } /* If using MULTILINESTRINGs then generate the serialized collection, otherwise just a single LINESTRING */ if (state->config->simple_geometries == 0) { lwgeom = lwcollection_as_lwgeom(lwcollection_construct(MULTILINETYPE, state->from_srid, NULL, obj->nParts, lwmultilinestrings)); } else { lwgeom = lwmultilinestrings[0]; lwfree(lwmultilinestrings); } if (!state->config->use_wkt) mem = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &mem_length); else mem = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, WKT_PRECISION, &mem_length); if ( !mem ) { snprintf(state->message, SHPLOADERMSGLEN, "unable to write geometry"); return SHPLOADERERR; } /* Free all of the allocated items */ lwgeom_free(lwgeom); /* Return the string - everything ok */ *geometry = mem; return SHPLOADEROK; }
/** * @brief Generate an allocated geometry string for shapefile object obj using the state parameters * if "force_multi" is true, single points will instead be created as multipoints with a single vertice. */ int GeneratePointGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry, int force_multi) { LWGEOM **lwmultipoints; LWGEOM *lwgeom = NULL; POINT4D point4d; int dims = 0; int u; char *mem; size_t mem_length; FLAGS_SET_Z(dims, state->has_z); FLAGS_SET_M(dims, state->has_m); /* Allocate memory for our array of LWPOINTs and our dynptarrays */ lwmultipoints = malloc(sizeof(LWPOINT *) * obj->nVertices); /* We need an array of pointers to each of our sub-geometries */ for (u = 0; u < obj->nVertices; u++) { /* Create a ptarray containing a single point */ POINTARRAY *pa = ptarray_construct_empty(state->has_z, state->has_m, 1); /* Generate the point */ point4d.x = obj->padfX[u]; point4d.y = obj->padfY[u]; if (state->has_z) point4d.z = obj->padfZ[u]; if (state->has_m) point4d.m = obj->padfM[u]; /* Add in the point! */ ptarray_append_point(pa, &point4d, LW_TRUE); /* Generate the LWPOINT */ lwmultipoints[u] = lwpoint_as_lwgeom(lwpoint_construct(state->from_srid, NULL, pa)); } /* If we have more than 1 vertex then we are working on a MULTIPOINT and so generate a MULTIPOINT rather than a POINT */ if ((obj->nVertices > 1) || force_multi) { lwgeom = lwcollection_as_lwgeom(lwcollection_construct(MULTIPOINTTYPE, state->from_srid, NULL, obj->nVertices, lwmultipoints)); } else { lwgeom = lwmultipoints[0]; lwfree(lwmultipoints); } if (state->config->use_wkt) { mem = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, WKT_PRECISION, &mem_length); } else { mem = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &mem_length); } if ( !mem ) { snprintf(state->message, SHPLOADERMSGLEN, "unable to write geometry"); return SHPLOADERERR; } /* Free all of the allocated items */ lwgeom_free(lwgeom); /* Return the string - everything ok */ *geometry = mem; return SHPLOADEROK; }
/* * Return a LWGEOM pointer from an TGEOM struct * Geometries are NOT copied */ LWGEOM* lwgeom_from_tgeom(TGEOM *tgeom) { int i, j, k; LWGEOM *geom; POINTARRAY *dpa; POINTARRAY **ppa; int hasz, hasm, edge_id; int dims=0; assert(tgeom); hasz=FLAGS_GET_Z(tgeom->flags); hasm=FLAGS_GET_M(tgeom->flags); geom = (LWGEOM *)lwcollection_construct_empty(COLLECTIONTYPE, tgeom->srid, hasz, hasm); switch (tgeom->type) { case TINTYPE: geom->type = TINTYPE; for (i=0 ; i < tgeom->nfaces ; i++) { FLAGS_SET_Z(dims, hasz?1:0); FLAGS_SET_M(dims, hasm?1:0); dpa = ptarray_construct_empty(hasz, hasm, 4); for (j=0 ; j < tgeom->faces[i]->nedges ; j++) { edge_id = tgeom->faces[i]->edges[j]; LWDEBUGF(3, "TIN edge_id: %i\n", edge_id); assert(edge_id); if (edge_id > 0) ptarray_append_point(dpa, tgeom->edges[edge_id]->s, LW_TRUE); else ptarray_append_point(dpa, tgeom->edges[-edge_id]->e, LW_TRUE); } edge_id = tgeom->faces[i]->edges[0]; LWDEBUGF(3, "TIN edge_id: %i\n", edge_id); if (edge_id > 0) ptarray_append_point(dpa, tgeom->edges[edge_id]->s, LW_TRUE); else ptarray_append_point(dpa, tgeom->edges[-edge_id]->e, LW_TRUE); geom = (LWGEOM *) lwtin_add_lwtriangle((LWTIN *) geom, lwtriangle_construct(tgeom->srid, NULL, dpa)); } break; case POLYHEDRALSURFACETYPE: geom->type = POLYHEDRALSURFACETYPE; for (i=0 ; i < tgeom->nfaces ; i++) { FLAGS_SET_Z(dims, hasz?1:0); FLAGS_SET_M(dims, hasm?1:0);; dpa = ptarray_construct_empty(hasz, hasm, 4); for (j=0 ; j < tgeom->faces[i]->nedges ; j++) { edge_id = tgeom->faces[i]->edges[j]; assert(edge_id); LWDEBUGF(3, "POLYHEDRALSURFACE edge_id: %i\n", edge_id); if (edge_id > 0) ptarray_append_point(dpa, tgeom->edges[edge_id]->s, LW_TRUE); else ptarray_append_point(dpa, tgeom->edges[-edge_id]->e, LW_TRUE); } edge_id = tgeom->faces[i]->edges[0]; LWDEBUGF(3, "POLYHEDRALSURFACE edge_id: %i\n", edge_id); if (edge_id > 0) ptarray_append_point(dpa, tgeom->edges[edge_id]->s, LW_TRUE); else ptarray_append_point(dpa, tgeom->edges[-edge_id]->e, LW_TRUE); ppa = lwalloc(sizeof(POINTARRAY*) * (tgeom->faces[i]->nrings + 1)); ppa[0] = dpa; for (k=0; k < tgeom->faces[i]->nrings ; k++) ppa[k+1] = tgeom->faces[i]->rings[k]; geom = (LWGEOM *) lwpsurface_add_lwpoly((LWPSURFACE *) geom, lwpoly_construct(tgeom->srid, NULL, k + 1, ppa)); } break; default: lwerror("lwgeom_from_tgeom: Unkwnown type %i - %s\n", tgeom->type, lwtype_name(tgeom->type)); } if (geom->srid == 0) geom->srid = SRID_UNKNOWN; return geom; }
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); }