int2vector * getPrimaryKey(Oid tblOid) { char *queryBase; char *query; bool isNull; int2vector *resultKey; int2vector *tpResultKey; HeapTuple resTuple; Datum resDatum; int ret; queryBase = "SELECT indkey FROM pg_index WHERE indisprimary='t' AND indrelid="; query = SPI_palloc(strlen(queryBase) + MAX_OID_LEN + 1); sprintf(query, "%s%d", queryBase, tblOid); ret = SPI_exec(query, 1); SPI_pfree(query); if (ret != SPI_OK_SELECT || SPI_processed != 1) return NULL; resTuple = SPI_tuptable->vals[0]; resDatum = SPI_getbinval(resTuple, SPI_tuptable->tupdesc, 1, &isNull); tpResultKey = (int2vector *) DatumGetPointer(resDatum); resultKey = SPI_palloc(VARSIZE(tpResultKey)); memcpy(resultKey, tpResultKey, VARSIZE(tpResultKey)); return resultKey; }
char * plj_get_configvalue_string(const char *paramName) { char *sql; int proc, ret; /* * no SPI_connect, we are already connected. */ sql = SPI_palloc(strlen(paramName) + strlen(get_sql)); sprintf(sql, get_sql, paramName); ret = SPI_exec(sql, 1); proc = SPI_processed; if (ret == SPI_OK_SELECT && proc > 0) { TupleDesc tupdesc = SPI_tuptable->tupdesc; SPITupleTable *tuptable = SPI_tuptable; return SPI_getvalue(tuptable->vals[0], tupdesc, 1); } elog(WARNING, "[config db] config value not set: %s", paramName); return ""; }
long long * all_referenced_files(int * countOut) { char query[128]; snprintf(query, 128, "SELECT file_id FROM "WDB_SCHEMA".file_blob"); SPI_connect(); int result = SPI_execute(query, true, 0); if ( SPI_OK_SELECT != result ) ereport(ERROR, (errcode( ERRCODE_RAISE_EXCEPTION ), errmsg("Error when reading from file_blob"))); * countOut = SPI_processed; long long * ret = (long long *) SPI_palloc(sizeof(long long) * (* countOut)); int i; for ( i = 0; i < * countOut; ++ i ) { bool isNull; // unused Datum d = SPI_getbinval(SPI_tuptable->vals[i], SPI_tuptable->tupdesc, 1, & isNull); ret[i] = DatumGetInt64(d); } SPI_finish(); return ret; }
PCSCHEMA * pc_schema_from_pcid_uncached(uint32 pcid) { char sql[256]; char *xml, *xml_spi, *srid_spi; int err, srid; size_t size; PCSCHEMA *schema; if (SPI_OK_CONNECT != SPI_connect ()) { SPI_finish(); elog(ERROR, "pc_schema_from_pcid: could not connect to SPI manager"); return NULL; } sprintf(sql, "select %s, %s from %s where pcid = %d", POINTCLOUD_FORMATS_XML, POINTCLOUD_FORMATS_SRID, POINTCLOUD_FORMATS, pcid); err = SPI_exec(sql, 1); if ( err < 0 ) { SPI_finish(); elog(ERROR, "pc_schema_from_pcid: error (%d) executing query: %s", err, sql); return NULL; } /* No entry in POINTCLOUD_FORMATS */ if (SPI_processed <= 0) { SPI_finish(); elog(ERROR, "no entry in \"%s\" for pcid = %d", POINTCLOUD_FORMATS, pcid); return NULL; } /* Result */ xml_spi = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1); srid_spi = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 2); /* NULL result */ if ( ! ( xml_spi && srid_spi ) ) { SPI_finish(); elog(ERROR, "unable to read row from \"%s\" for pcid = %d", POINTCLOUD_FORMATS, pcid); return NULL; } /* Copy result to upper executor context */ size = strlen(xml_spi) + 1; xml = SPI_palloc(size); memcpy(xml, xml_spi, size); /* Parse the SRID string into the function stack */ srid = atoi(srid_spi); /* Disconnect from SPI, losing all our SPI-allocated memory now... */ SPI_finish(); /* Build the schema object */ err = pc_schema_from_xml(xml, &schema); if ( ! err ) { ereport(ERROR, (errcode(ERRCODE_NOT_AN_XML_DOCUMENT), errmsg("unable to parse XML for pcid = %d in \"%s\"", pcid, POINTCLOUD_FORMATS))); } schema->pcid = pcid; schema->srid = srid; return schema; }
static int load_rules(RULES *rules, char *tab) { int ret; SPIPlanPtr SPIplan; Portal SPIportal; bool moredata = TRUE; #ifdef DEBUG struct timeval t1, t2; double elapsed; #endif char *sql; int rule_arr[MAX_RULE_LENGTH]; int ntuples; int total_tuples = 0; rules_columns_t rules_columns = {rule: -1}; char *rule; DBG("start load_rules\n"); SET_TIME(t1); if (!tab || !strlen(tab)) { elog(NOTICE, "load_rules: rules table is not usable"); return -1; } if (!tableNameOk(tab)) { elog(NOTICE, "load_rules: rules table name may only be alphanum and '.\"_' characters (%s)", tab); return -1; } sql = SPI_palloc(strlen(tab)+35); strcpy(sql, "select rule from "); strcat(sql, tab); strcat(sql, " order by id "); /* get the sql for the lexicon records and prepare the query */ SPIplan = SPI_prepare(sql, 0, NULL); if (SPIplan == NULL) { elog(NOTICE, "load_rules: couldn't create query plan for the rule data via SPI (%s)", sql); return -1; } if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL) { elog(NOTICE, "load_rules: SPI_cursor_open('%s') returns NULL", sql); return -1; } while (moredata == TRUE) { //DBG("calling SPI_cursor_fetch"); SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT); if (SPI_tuptable == NULL) { elog(NOTICE, "load_rules: SPI_tuptable is NULL"); return -1; } if (rules_columns.rule == -1) { ret = fetch_rules_columns(SPI_tuptable, &rules_columns); if (ret) return ret; } ntuples = SPI_processed; //DBG("Reading edges: %i - %i", total_tuples, total_tuples+ntuples); if (ntuples > 0) { int t; SPITupleTable *tuptable = SPI_tuptable; TupleDesc tupdesc = SPI_tuptable->tupdesc; for (t = 0; t < ntuples; t++) { int nr; //if (t%100 == 0) { DBG(" t: %i", t); } HeapTuple tuple = tuptable->vals[t]; GET_TEXT_FROM_TUPLE(rule,rules_columns.rule); nr = parse_rule(rule, rule_arr); if (nr == -1) { elog(NOTICE, "load_roles: rule exceeds 128 terms"); return -1; } ret = rules_add_rule(rules, nr, rule_arr); if (ret != 0) { elog(NOTICE,"load_roles: failed to add rule %d (%d): %s", total_tuples+t+1, ret, rule); return -1; } } //DBG("calling SPI_freetuptable"); SPI_freetuptable(tuptable); //DBG("back from SPI_freetuptable"); } else moredata = FALSE; total_tuples += ntuples; } ret = rules_ready(rules); if (ret != 0) { elog(NOTICE, "load_roles: failed to ready the rules: err: %d", ret); return -1; } SET_TIME(t2); ELAPSED_T(t1, t2); DBG("Time to read %i rule records: %.1f ms.", total_tuples, elapsed); return 0; }
static int load_lex(LEXICON *lex, char *tab) { int ret; SPIPlanPtr SPIplan; Portal SPIportal; bool moredata = TRUE; #ifdef DEBUG struct timeval t1, t2; double elapsed; #endif char *sql; int ntuples; int total_tuples = 0; lex_columns_t lex_columns = {seq: -1, word: -1, stdword: -1, token: -1}; int seq; char *word; char *stdword; int token; DBG("start load_lex\n"); SET_TIME(t1); if (!tab || !strlen(tab)) { elog(NOTICE, "load_lex: rules table is not usable"); return -1; } if (!tableNameOk(tab)) { elog(NOTICE, "load_lex: lex and gaz table names may only be alphanum and '.\"_' characters (%s)", tab); return -1; } sql = SPI_palloc(strlen(tab)+65); strcpy(sql, "select seq, word, stdword, token from "); strcat(sql, tab); strcat(sql, " order by id "); /* get the sql for the lexicon records and prepare the query */ SPIplan = SPI_prepare(sql, 0, NULL); if (SPIplan == NULL) { elog(NOTICE, "load_lex: couldn't create query plan for the lex data via SPI (%s)", sql); return -1; } /* get the sql for the lexicon records and prepare the query */ SPIplan = SPI_prepare(sql, 0, NULL); if (SPIplan == NULL) { elog(NOTICE, "load_lex: couldn't create query plan for the lexicon data via SPI"); return -1; } if ((SPIportal = SPI_cursor_open(NULL, SPIplan, NULL, NULL, true)) == NULL) { elog(NOTICE, "load_lex: SPI_cursor_open('%s') returns NULL", sql); return -1; } while (moredata == TRUE) { //DBG("calling SPI_cursor_fetch"); SPI_cursor_fetch(SPIportal, TRUE, TUPLIMIT); if (SPI_tuptable == NULL) { elog(NOTICE, "load_lex: SPI_tuptable is NULL"); return -1; } if (lex_columns.seq == -1) { ret = fetch_lex_columns(SPI_tuptable, &lex_columns); if (ret) return ret; } ntuples = SPI_processed; //DBG("Reading edges: %i - %i", total_tuples, total_tuples+ntuples); total_tuples += ntuples; if (ntuples > 0) { int t; Datum binval; bool isnull; SPITupleTable *tuptable = SPI_tuptable; TupleDesc tupdesc = SPI_tuptable->tupdesc; for (t = 0; t < ntuples; t++) { //if (t%100 == 0) { DBG(" t: %i", t); } HeapTuple tuple = tuptable->vals[t]; GET_INT_FROM_TUPLE(seq,lex_columns.seq,"load_lex: seq contains a null value"); GET_TEXT_FROM_TUPLE(word,lex_columns.word); GET_TEXT_FROM_TUPLE(stdword,lex_columns.stdword); GET_INT_FROM_TUPLE(token,lex_columns.token,"load_lex: token contains a null value"); lex_add_entry(lex, seq, word, stdword, token); } //DBG("calling SPI_freetuptable"); SPI_freetuptable(tuptable); //DBG("back from SPI_freetuptable"); } else moredata = FALSE; } SET_TIME(t2); ELAPSED_T(t1, t2); DBG("Time to read %i lexicon records: %.1f ms.", total_tuples, elapsed); return 0; } static int fetch_rules_columns(SPITupleTable *tuptable, rules_columns_t *rules_cols) { int err = 0; FETCH_COL(rules_cols,rule,"rule"); if (err) { elog(NOTICE, "rules queries must return column 'rule'"); return -1; } CHECK_TYP(rules_cols,rule,TEXTOID); if (err) { elog(NOTICE, "rules column type must be: 'rule' text"); return -1; } return 0; }
/***************************************************************************** * The entry point for the trigger function. * The Trigger takes a single SQL 'text' argument indicating the name of the * table the trigger was applied to. If this name is incorrect so will the * mirroring. ****************************************************************************/ Datum recordchange(PG_FUNCTION_ARGS) { TriggerData *trigdata; TupleDesc tupdesc; HeapTuple beforeTuple = NULL; HeapTuple afterTuple = NULL; HeapTuple retTuple = NULL; char *tblname; char op = 0; char *schemaname; char *fullyqualtblname; char *pkxpress = NULL; if (fcinfo->context != NULL) { if (SPI_connect() < 0) { ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("dbmirror:recordchange could not connect to SPI"))); return -1; } trigdata = (TriggerData *) fcinfo->context; /* Extract the table name */ tblname = SPI_getrelname(trigdata->tg_relation); #ifndef NOSCHEMAS schemaname = get_namespace_name(RelationGetNamespace(trigdata->tg_relation)); fullyqualtblname = SPI_palloc(strlen(tblname) + strlen(schemaname) + 6); sprintf(fullyqualtblname, "\"%s\".\"%s\"", schemaname, tblname); #else fullyqualtblname = SPI_palloc(strlen(tblname) + 3); sprintf(fullyqualtblname, "\"%s\"", tblname); #endif tupdesc = trigdata->tg_relation->rd_att; if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) { retTuple = trigdata->tg_newtuple; beforeTuple = trigdata->tg_trigtuple; afterTuple = trigdata->tg_newtuple; op = 'u'; } else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) { retTuple = trigdata->tg_trigtuple; afterTuple = trigdata->tg_trigtuple; op = 'i'; } else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) { retTuple = trigdata->tg_trigtuple; beforeTuple = trigdata->tg_trigtuple; op = 'd'; } else { ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("dbmirror:recordchange Unknown operation"))); } if (storePending(fullyqualtblname, beforeTuple, afterTuple, tupdesc, retTuple->t_tableOid, op)) { /* An error occoured. Skip the operation. */ ereport(ERROR, (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION), errmsg("operation could not be mirrored"))); return PointerGetDatum(NULL); } debug_msg("dbmirror:recordchange returning on success"); SPI_pfree(fullyqualtblname); if (pkxpress != NULL) SPI_pfree(pkxpress); SPI_finish(); return PointerGetDatum(retTuple); } else { /* * Not being called as a trigger. */ return PointerGetDatum(NULL); } }
/** * Packages the data in tTupleData into a string of the format * FieldName='value text' where any quotes inside of value text * are escaped with a backslash and any backslashes in value text * are esacped by a second back slash. * * tTupleDesc should be a description of the tuple stored in * tTupleData. * * eFieldUsage specifies which fields to use. * PRIMARY implies include only primary key fields. * NONPRIMARY implies include only non-primary key fields. * ALL implies include all fields. */ char * packageData(HeapTuple tTupleData, TupleDesc tTupleDesc, Oid tableOid, enum FieldUsage eKeyUsage) { int iNumCols; int2vector *tpPKeys = NULL; int iColumnCounter; char *cpDataBlock; int iDataBlockSize; int iUsedDataBlock; iNumCols = tTupleDesc->natts; if (eKeyUsage != ALL) { tpPKeys = getPrimaryKey(tableOid); if (tpPKeys == NULL) return NULL; } if (tpPKeys != NULL) debug_msg("dbmirror:packageData have primary keys"); cpDataBlock = SPI_palloc(BUFFER_SIZE); iDataBlockSize = BUFFER_SIZE; iUsedDataBlock = 0; /* To account for the null */ for (iColumnCounter = 1; iColumnCounter <= iNumCols; iColumnCounter++) { int iIsPrimaryKey; int iPrimaryKeyIndex; char *cpUnFormatedPtr; char *cpFormatedPtr; char *cpFieldName; char *cpFieldData; if (eKeyUsage != ALL) { /* Determine if this is a primary key or not. */ iIsPrimaryKey = 0; for (iPrimaryKeyIndex = 0; iPrimaryKeyIndex < tpPKeys->dim1; iPrimaryKeyIndex++) { if (tpPKeys->values[iPrimaryKeyIndex] == iColumnCounter) { iIsPrimaryKey = 1; break; } } if (iIsPrimaryKey ? (eKeyUsage != PRIMARY) : (eKeyUsage != NONPRIMARY)) { /** * Don't use. */ debug_msg("dbmirror:packageData skipping column"); continue; } } /* KeyUsage!=ALL */ if (tTupleDesc->attrs[iColumnCounter - 1]->attisdropped) { /** * This column has been dropped. * Do not mirror it. */ continue; } cpFieldName = DatumGetPointer(NameGetDatum (&tTupleDesc->attrs [iColumnCounter - 1]->attname)); debug_msg2("dbmirror:packageData field name: %s", cpFieldName); while (iDataBlockSize - iUsedDataBlock < strlen(cpFieldName) + 6) { cpDataBlock = SPI_repalloc(cpDataBlock, iDataBlockSize + BUFFER_SIZE); iDataBlockSize = iDataBlockSize + BUFFER_SIZE; } sprintf(cpDataBlock + iUsedDataBlock, "\"%s\"=", cpFieldName); iUsedDataBlock = iUsedDataBlock + strlen(cpFieldName) + 3; cpFieldData = SPI_getvalue(tTupleData, tTupleDesc, iColumnCounter); cpUnFormatedPtr = cpFieldData; cpFormatedPtr = cpDataBlock + iUsedDataBlock; if (cpFieldData != NULL) { *cpFormatedPtr = '\''; iUsedDataBlock++; cpFormatedPtr++; } else { sprintf(cpFormatedPtr, " "); iUsedDataBlock++; cpFormatedPtr++; continue; } debug_msg2("dbmirror:packageData field data: \"%s\"", cpFieldData); debug_msg("dbmirror:packageData starting format loop"); while (*cpUnFormatedPtr != 0) { while (iDataBlockSize - iUsedDataBlock < 2) { cpDataBlock = SPI_repalloc(cpDataBlock, iDataBlockSize + BUFFER_SIZE); iDataBlockSize = iDataBlockSize + BUFFER_SIZE; cpFormatedPtr = cpDataBlock + iUsedDataBlock; } if (*cpUnFormatedPtr == '\\' || *cpUnFormatedPtr == '\'') { *cpFormatedPtr = '\\'; cpFormatedPtr++; iUsedDataBlock++; } *cpFormatedPtr = *cpUnFormatedPtr; cpFormatedPtr++; cpUnFormatedPtr++; iUsedDataBlock++; } SPI_pfree(cpFieldData); while (iDataBlockSize - iUsedDataBlock < 3) { cpDataBlock = SPI_repalloc(cpDataBlock, iDataBlockSize + BUFFER_SIZE); iDataBlockSize = iDataBlockSize + BUFFER_SIZE; cpFormatedPtr = cpDataBlock + iUsedDataBlock; } sprintf(cpFormatedPtr, "' "); iUsedDataBlock = iUsedDataBlock + 2; debug_msg2("dbmirror:packageData data block: \"%s\"", cpDataBlock); } /* for iColumnCounter */ if (tpPKeys != NULL) SPI_pfree(tpPKeys); debug_msg3("dbmirror:packageData returning DataBlockSize:%d iUsedDataBlock:%d", iDataBlockSize, iUsedDataBlock); memset(cpDataBlock + iUsedDataBlock, 0, iDataBlockSize - iUsedDataBlock); return cpDataBlock; }
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); }