/* * relation-info - * Retrieves catalog information for a given relation. Given the oid of * the relation, return the following information: * whether the relation has secondary indices * number of pages * number of tuples */ void relation_info(Query *root, Index relid, bool *hasindex, int *pages, int *tuples) { HeapTuple relationTuple; Form_pg_class relation; Oid relationObjectId; relationObjectId = getrelid(relid, root->rtable); relationTuple = SearchSysCacheTuple(RELOID, ObjectIdGetDatum(relationObjectId), 0,0,0); if (HeapTupleIsValid(relationTuple)) { relation = (Form_pg_class)GETSTRUCT(relationTuple); *hasindex = (relation->relhasindex) ? TRUE : FALSE; *pages = relation->relpages; *tuples = relation->reltuples; } else { elog(WARN, "RelationCatalogInformation: Relation %d not found", relationObjectId); } return; }
/* * DynamicScan_GetOriginalRelationOid * Returns the original relation as stored in the estate range table * by the optimizer */ static Oid DynamicScan_GetOriginalRelationOid(ScanState *scanState) { EState *estate = scanState->ps.state; Scan *scan = (Scan *) scanState->ps.plan; Oid reloid = getrelid(scan->scanrelid, estate->es_range_table); Assert(OidIsValid(reloid)); return reloid; }
/* * DynamicScan_GetTableOid * Returns the Oid of the table/partition to scan. * * For partitioned case this method returns InvalidOid * if the partition iterator hasn't been initialized yet. */ Oid DynamicScan_GetTableOid(ScanState *scanState) { /* For non-partitioned scan, just lookup the RTE */ if (!isDynamicScan((Scan *)scanState->ps.plan)) { return getrelid(((Scan *)scanState->ps.plan)->scanrelid, scanState->ps.state->es_range_table); } /* We are yet to initialize the iterator, so return InvalidOid */ if (SCAN_INIT == scanState->scan_state) { return InvalidOid; } /* Get the iterator and look up the oid of the current relation */ DynamicPartitionIterator *iterator = DynamicScan_GetIterator(scanState); Assert(NULL != iterator); Assert(OidIsValid(iterator->curRelOid)); return iterator->curRelOid; }
DynamicTableScanState * ExecInitDynamicTableScan(DynamicTableScan *node, EState *estate, int eflags) { Assert((eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)) == 0); DynamicTableScanState *state = makeNode(DynamicTableScanState); state->tableScanState.ss.scan_state = SCAN_INIT; /* We do not open the relation. We open it later, per-partition. */ InitScanStateInternal((ScanState *)state, (Plan *)node, estate, eflags, false /* initCurrentRelation */); Oid reloid = getrelid(node->scanrelid, estate->es_range_table); Assert(OidIsValid(reloid)); state->firstPartition = true; /* lastRelOid is used to remap varattno for heterogeneous partitions */ state->lastRelOid = reloid; state->scanrelid = node->scanrelid; /* * This context will be reset per-partition to free up per-partition * qual and targetlist allocations */ state->partitionMemoryContext = AllocSetContextCreate(CurrentMemoryContext, "DynamicTableScanPerPartition", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); initGpmonPktForDynamicTableScan((Plan *)node, &state->tableScanState.ss.ps.gpmon_pkt, estate); return state; }
/* * index-info-- * Retrieves catalog information on an index on a given relation. * * The index relation is opened on the first invocation. The current * retrieves the next index relation within the catalog that has not * already been retrieved by a previous call. The index catalog * is closed when no more indices for 'relid' can be found. * * 'first' is 1 if this is the first call * * Returns true if successful and false otherwise. Index info is returned * via the transient data structure 'info'. * */ bool index_info(Query *root, bool first, int relid, IdxInfoRetval *info) { register i; HeapTuple indexTuple, amopTuple; IndexTupleForm index; Relation indexRelation; uint16 amstrategy; Oid relam; Oid indrelid; static Relation relation = (Relation) NULL; static HeapScanDesc scan = (HeapScanDesc) NULL; static ScanKeyData indexKey; /* find the oid of the indexed relation */ indrelid = getrelid(relid, root->rtable); memset(info, 0, sizeof(IdxInfoRetval)); /* * the maximum number of elements in each of the following arrays is * 8. We allocate one more for a terminating 0 to indicate the end * of the array. */ info->indexkeys = (int *)palloc(sizeof(int)*9); memset(info->indexkeys, 0, sizeof(int)*9); info->orderOprs = (Oid *)palloc(sizeof(Oid)*9); memset(info->orderOprs, 0, sizeof(Oid)*9); info->classlist = (Oid *)palloc(sizeof(Oid)*9); memset(info->classlist, 0, sizeof(Oid)*9); /* Find an index on the given relation */ if (first) { if (RelationIsValid(relation)) heap_close(relation); if (HeapScanIsValid(scan)) heap_endscan(scan); ScanKeyEntryInitialize(&indexKey, 0, Anum_pg_index_indrelid, F_OIDEQ, ObjectIdGetDatum(indrelid)); relation = heap_openr(IndexRelationName); scan = heap_beginscan(relation, 0, NowTimeQual, 1, &indexKey); } if (!HeapScanIsValid(scan)) elog(WARN, "index_info: scan not started"); indexTuple = heap_getnext(scan, 0, (Buffer *) NULL); if (!HeapTupleIsValid(indexTuple)) { heap_endscan(scan); heap_close(relation); scan = (HeapScanDesc) NULL; relation = (Relation) NULL; return(0); } /* Extract info from the index tuple */ index = (IndexTupleForm)GETSTRUCT(indexTuple); info->relid = index->indexrelid; /* index relation */ for (i = 0; i < 8; i++) info->indexkeys[i] = index->indkey[i]; for (i = 0; i < 8; i++) info->classlist[i] = index->indclass[i]; info->indproc = index->indproc; /* functional index ?? */ /* partial index ?? */ if (VARSIZE(&index->indpred) != 0) { /* * The memory allocated here for the predicate (in lispReadString) * only needs to stay around until it's used in find_index_paths, * which is all within a command, so the automatic pfree at end * of transaction should be ok. */ char *predString; predString = fmgr(F_TEXTOUT, &index->indpred); info->indpred = (Node*)stringToNode(predString); pfree(predString); } /* Extract info from the relation descriptor for the index */ indexRelation = index_open(index->indexrelid); #ifdef notdef /* XXX should iterate through strategies -- but how? use #1 for now */ amstrategy = indexRelation->rd_am->amstrategies; #endif /* notdef */ amstrategy = 1; relam = indexRelation->rd_rel->relam; info->relam = relam; info->pages = indexRelation->rd_rel->relpages; info->tuples = indexRelation->rd_rel->reltuples; heap_close(indexRelation); /* * Find the index ordering keys * * Must use indclass to know when to stop looking since with * functional indices there could be several keys (args) for * one opclass. -mer 27 Sept 1991 */ for (i = 0; i < 8 && index->indclass[i]; ++i) { amopTuple = SearchSysCacheTuple(AMOPSTRATEGY, ObjectIdGetDatum(relam), ObjectIdGetDatum(index->indclass[i]), UInt16GetDatum(amstrategy), 0); if (!HeapTupleIsValid(amopTuple)) elog(WARN, "index_info: no amop %d %d %d", relam, index->indclass[i], amstrategy); info->orderOprs[i] = ((Form_pg_amop)GETSTRUCT(amopTuple))->amopopr; } return(TRUE); }
/* * DefineIndex * Creates a new index. * * 'heapRelation': the relation the index will apply to. * 'indexRelationName': the name for the new index, or NULL to indicate * that a nonconflicting default name should be picked. * 'indexRelationId': normally InvalidOid, but during bootstrap can be * nonzero to specify a preselected OID for the index. * 'accessMethodName': name of the AM to use. * 'tableSpaceName': name of the tablespace to create the index in. * NULL specifies using the appropriate default. * 'attributeList': a list of IndexElem specifying columns and expressions * to index on. * 'predicate': the partial-index condition, or NULL if none. * 'rangetable': needed to interpret the predicate. * 'options': reloptions from WITH (in list-of-DefElem form). * 'unique': make the index enforce uniqueness. * 'primary': mark the index as a primary key in the catalogs. * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint, * so build a pg_constraint entry for it. * 'is_alter_table': this is due to an ALTER rather than a CREATE operation. * 'check_rights': check for CREATE rights in the namespace. (This should * be true except when ALTER is deleting/recreating an index.) * 'skip_build': make the catalog entries but leave the index file empty; * it will be filled later. * 'quiet': suppress the NOTICE chatter ordinarily provided for constraints. * 'concurrent': avoid blocking writers to the table while building. */ void DefineIndex(RangeVar *heapRelation, char *indexRelationName, Oid indexRelationId, char *accessMethodName, char *tableSpaceName, List *attributeList, Expr *predicate, List *rangetable, List *options, bool unique, bool primary, bool isconstraint, bool is_alter_table, bool check_rights, bool skip_build, bool quiet, bool concurrent) { Oid *classObjectId; Oid accessMethodId; Oid relationId; Oid namespaceId; Oid tablespaceId; Relation rel; HeapTuple tuple; Form_pg_am accessMethodForm; RegProcedure amoptions; Datum reloptions; IndexInfo *indexInfo; int numberOfAttributes; List *old_xact_list; ListCell *lc; uint32 ixcnt; LockRelId heaprelid; LOCKTAG heaplocktag; Snapshot snapshot; Relation pg_index; HeapTuple indexTuple; Form_pg_index indexForm; /* * count attributes in index */ numberOfAttributes = list_length(attributeList); if (numberOfAttributes <= 0) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("must specify at least one column"))); if (numberOfAttributes > INDEX_MAX_KEYS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_COLUMNS), errmsg("cannot use more than %d columns in an index", INDEX_MAX_KEYS))); /* * Open heap relation, acquire a suitable lock on it, remember its OID * * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE * (but not VACUUM). */ rel = heap_openrv(heapRelation, (concurrent ? ShareUpdateExclusiveLock : ShareLock)); relationId = RelationGetRelid(rel); namespaceId = RelationGetNamespace(rel); /* Note: during bootstrap may see uncataloged relation */ if (rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_UNCATALOGED) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", heapRelation->relname))); /* * Don't try to CREATE INDEX on temp tables of other backends. */ if (isOtherTempNamespace(namespaceId)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot create indexes on temporary tables of other sessions"))); /* * Verify we (still) have CREATE rights in the rel's namespace. * (Presumably we did when the rel was created, but maybe not anymore.) * Skip check if caller doesn't want it. Also skip check if * bootstrapping, since permissions machinery may not be working yet. */ if (check_rights && !IsBootstrapProcessingMode()) { AclResult aclresult; aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); } /* * Select tablespace to use. If not specified, use default_tablespace * (which may in turn default to database's default). */ if (tableSpaceName) { tablespaceId = get_tablespace_oid(tableSpaceName); if (!OidIsValid(tablespaceId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("tablespace \"%s\" does not exist", tableSpaceName))); } else { tablespaceId = GetDefaultTablespace(); /* note InvalidOid is OK in this case */ } /* Check permissions except when using database's default */ if (OidIsValid(tablespaceId)) { AclResult aclresult; aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tablespaceId)); } /* * Force shared indexes into the pg_global tablespace. This is a bit of a * hack but seems simpler than marking them in the BKI commands. */ if (rel->rd_rel->relisshared) tablespaceId = GLOBALTABLESPACE_OID; /* * Select name for index if caller didn't specify */ if (indexRelationName == NULL) { if (primary) indexRelationName = ChooseRelationName(RelationGetRelationName(rel), NULL, "pkey", namespaceId); else { IndexElem *iparam = (IndexElem *) linitial(attributeList); indexRelationName = ChooseRelationName(RelationGetRelationName(rel), iparam->name, "key", namespaceId); } } /* * look up the access method, verify it can handle the requested features */ tuple = SearchSysCache(AMNAME, PointerGetDatum(accessMethodName), 0, 0, 0); if (!HeapTupleIsValid(tuple)) { /* * Hack to provide more-or-less-transparent updating of old RTREE * indexes to GIST: if RTREE is requested and not found, use GIST. */ if (strcmp(accessMethodName, "rtree") == 0) { ereport(NOTICE, (errmsg("substituting access method \"gist\" for obsolete method \"rtree\""))); accessMethodName = "gist"; tuple = SearchSysCache(AMNAME, PointerGetDatum(accessMethodName), 0, 0, 0); } if (!HeapTupleIsValid(tuple)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("access method \"%s\" does not exist", accessMethodName))); } accessMethodId = HeapTupleGetOid(tuple); accessMethodForm = (Form_pg_am) GETSTRUCT(tuple); if (unique && !accessMethodForm->amcanunique) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"%s\" does not support unique indexes", accessMethodName))); if (numberOfAttributes > 1 && !accessMethodForm->amcanmulticol) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("access method \"%s\" does not support multicolumn indexes", accessMethodName))); amoptions = accessMethodForm->amoptions; ReleaseSysCache(tuple); /* * If a range table was created then check that only the base rel is * mentioned. */ if (rangetable != NIL) { if (list_length(rangetable) != 1 || getrelid(1, rangetable) != relationId) ereport(ERROR, (errcode(ERRCODE_INVALID_COLUMN_REFERENCE), errmsg("index expressions and predicates may refer only to the table being indexed"))); } /* * Validate predicate, if given */ if (predicate) CheckPredicate(predicate); /* * Extra checks when creating a PRIMARY KEY index. */ if (primary) { List *cmds; ListCell *keys; /* * If ALTER TABLE, check that there isn't already a PRIMARY KEY. In * CREATE TABLE, we have faith that the parser rejected multiple pkey * clauses; and CREATE INDEX doesn't have a way to say PRIMARY KEY, so * it's no problem either. */ if (is_alter_table && relationHasPrimaryKey(rel)) { ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("multiple primary keys for table \"%s\" are not allowed", RelationGetRelationName(rel)))); } /* * Check that all of the attributes in a primary key are marked as not * null, otherwise attempt to ALTER TABLE .. SET NOT NULL */ cmds = NIL; foreach(keys, attributeList) { IndexElem *key = (IndexElem *) lfirst(keys); HeapTuple atttuple; if (!key->name) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("primary keys cannot be expressions"))); /* System attributes are never null, so no problem */ if (SystemAttributeByName(key->name, rel->rd_rel->relhasoids)) continue; atttuple = SearchSysCacheAttName(relationId, key->name); if (HeapTupleIsValid(atttuple)) { if (!((Form_pg_attribute) GETSTRUCT(atttuple))->attnotnull) { /* Add a subcommand to make this one NOT NULL */ AlterTableCmd *cmd = makeNode(AlterTableCmd); cmd->subtype = AT_SetNotNull; cmd->name = key->name; cmds = lappend(cmds, cmd); } ReleaseSysCache(atttuple); } else { /* * This shouldn't happen during CREATE TABLE, but can happen * during ALTER TABLE. Keep message in sync with * transformIndexConstraints() in parser/analyze.c. */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("column \"%s\" named in key does not exist", key->name))); } } /* * XXX: Shouldn't the ALTER TABLE .. SET NOT NULL cascade to child * tables? Currently, since the PRIMARY KEY itself doesn't cascade, * we don't cascade the notnull constraint(s) either; but this is * pretty debatable. * * XXX: possible future improvement: when being called from ALTER * TABLE, it would be more efficient to merge this with the outer * ALTER TABLE, so as to avoid two scans. But that seems to * complicate DefineIndex's API unduly. */ if (cmds) AlterTableInternal(relationId, cmds, false); }
Datum geography_gist_join_selectivity(PG_FUNCTION_ARGS) { PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); /* Oid operator = PG_GETARG_OID(1); */ List *args = (List *) PG_GETARG_POINTER(2); JoinType jointype = (JoinType) PG_GETARG_INT16(3); Node *arg1, *arg2; Var *var1, *var2; Oid relid1, relid2; HeapTuple stats1_tuple, stats2_tuple; GEOG_STATS *geogstats1, *geogstats2; /* * These are to avoid casting the corresponding * "type-punned" pointers, which would break * "strict-aliasing rules". */ GEOG_STATS **gs1ptr=&geogstats1, **gs2ptr=&geogstats2; int geogstats1_nvalues = 0, geogstats2_nvalues = 0; float8 selectivity1 = 0.0, selectivity2 = 0.0; float4 num1_tuples = 0.0, num2_tuples = 0.0; float4 total_tuples = 0.0, rows_returned = 0.0; GBOX search_box; /** * Join selectivity algorithm. To calculation the selectivity we * calculate the intersection of the two column sample extents, * sum the results, and then multiply by two since for each * geometry in col 1 that intersects a geometry in col 2, the same * will also be true. */ POSTGIS_DEBUGF(3, "geography_gist_join_selectivity called with jointype %d", jointype); /* * We'll only respond to an inner join/unknown context join */ if (jointype != JOIN_INNER) { elog(NOTICE, "geography_gist_join_selectivity called with incorrect join type"); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } /* * Determine the oids of the geometry columns we are working with */ arg1 = (Node *) linitial(args); arg2 = (Node *) lsecond(args); if (!IsA(arg1, Var) || !IsA(arg2, Var)) { elog(DEBUG1, "geography_gist_join_selectivity called with arguments that are not column references"); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } var1 = (Var *)arg1; var2 = (Var *)arg2; relid1 = getrelid(var1->varno, root->parse->rtable); relid2 = getrelid(var2->varno, root->parse->rtable); POSTGIS_DEBUGF(3, "Working with relations oids: %d %d", relid1, relid2); /* Read the stats tuple from the first column */ stats1_tuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid1), Int16GetDatum(var1->varattno), 0, 0); if ( ! stats1_tuple ) { POSTGIS_DEBUG(3, " No statistics, returning default geometry join selectivity"); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } if ( ! get_attstatsslot(stats1_tuple, 0, 0, STATISTIC_KIND_GEOGRAPHY, InvalidOid, NULL, NULL, #if POSTGIS_PGSQL_VERSION >= 85 NULL, #endif (float4 **)gs1ptr, &geogstats1_nvalues) ) { POSTGIS_DEBUG(3, " STATISTIC_KIND_GEOGRAPHY stats not found - returning default geometry join selectivity"); ReleaseSysCache(stats1_tuple); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } /* Read the stats tuple from the second column */ stats2_tuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid2), Int16GetDatum(var2->varattno), 0, 0); if ( ! stats2_tuple ) { POSTGIS_DEBUG(3, " No statistics, returning default geometry join selectivity"); free_attstatsslot(0, NULL, 0, (float *)geogstats1, geogstats1_nvalues); ReleaseSysCache(stats1_tuple); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } if ( ! get_attstatsslot(stats2_tuple, 0, 0, STATISTIC_KIND_GEOGRAPHY, InvalidOid, NULL, NULL, #if POSTGIS_PGSQL_VERSION >= 85 NULL, #endif (float4 **)gs2ptr, &geogstats2_nvalues) ) { POSTGIS_DEBUG(3, " STATISTIC_KIND_GEOGRAPHY stats not found - returning default geometry join selectivity"); free_attstatsslot(0, NULL, 0, (float *)geogstats1, geogstats1_nvalues); ReleaseSysCache(stats2_tuple); ReleaseSysCache(stats1_tuple); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } /** * Setup the search box - this is the intersection of the two column * extents. */ search_box.xmin = Max(geogstats1->xmin, geogstats2->xmin); search_box.ymin = Max(geogstats1->ymin, geogstats2->ymin); search_box.zmin = Max(geogstats1->zmin, geogstats2->zmin); search_box.xmax = Min(geogstats1->xmax, geogstats2->xmax); search_box.ymax = Min(geogstats1->ymax, geogstats2->ymax); search_box.zmax = Min(geogstats1->zmax, geogstats2->zmax); /* If the extents of the two columns don't intersect, return zero */ if (search_box.xmin > search_box.xmax || search_box.ymin > search_box.ymax || search_box.zmin > search_box.zmax) PG_RETURN_FLOAT8(0.0); POSTGIS_DEBUGF(3, " -- geomstats1 box: %.15g %.15g %.15g, %.15g %.15g %.15g", geogstats1->xmin, geogstats1->ymin, geogstats1->zmin, geogstats1->xmax, geogstats1->ymax, geogstats1->zmax); POSTGIS_DEBUGF(3, " -- geomstats2 box: %.15g %.15g %.15g, %.15g %.15g %.15g", geogstats2->xmin, geogstats2->ymin, geogstats2->zmin, geogstats2->xmax, geogstats2->ymax, geogstats2->zmax); POSTGIS_DEBUGF(3, " -- calculated intersection box is : %.15g %.15g %.15g, %.15g %.15g %.15g", search_box.xmin, search_box.ymin, search_box.zmin, search_box.xmax, search_box.ymax, search_box.zmax); /* Do the selectivity */ selectivity1 = estimate_selectivity(&search_box, geogstats1); selectivity2 = estimate_selectivity(&search_box, geogstats2); POSTGIS_DEBUGF(3, "selectivity1: %.15g selectivity2: %.15g", selectivity1, selectivity2); /* * OK, so before we calculate the join selectivity we also need to * know the number of tuples in each of the columns since * estimate_selectivity returns the number of estimated tuples * divided by the total number of tuples. */ num1_tuples = geogstats1->totalrows; num2_tuples = geogstats2->totalrows; /* Free the statistic tuples */ free_attstatsslot(0, NULL, 0, (float *)geogstats1, geogstats1_nvalues); ReleaseSysCache(stats1_tuple); free_attstatsslot(0, NULL, 0, (float *)geogstats2, geogstats2_nvalues); ReleaseSysCache(stats2_tuple); /* * Finally calculate the estimate of the number of rows returned * * = 2 * (nrows from col1 + nrows from col2) / * total nrows in col1 x total nrows in col2 * * The factor of 2 accounts for the fact that for each tuple in * col 1 matching col 2, * there will be another match in col 2 matching col 1 */ total_tuples = num1_tuples * num2_tuples; rows_returned = 2 * ((num1_tuples * selectivity1) + (num2_tuples * selectivity2)); POSTGIS_DEBUGF(3, "Rows from rel1: %f", num1_tuples * selectivity1); POSTGIS_DEBUGF(3, "Rows from rel2: %f", num2_tuples * selectivity2); POSTGIS_DEBUGF(3, "Estimated rows returned: %f", rows_returned); /* * One (or both) tuple count is zero... * We return default selectivity estimate. * We could probably attempt at an estimate * w/out looking at tables tuple count, with * a function of selectivity1, selectivity2. */ if ( ! total_tuples ) { POSTGIS_DEBUG(3, "Total tuples == 0, returning default join selectivity"); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } if ( rows_returned > total_tuples ) PG_RETURN_FLOAT8(1.0); PG_RETURN_FLOAT8(rows_returned / total_tuples); }
Datum geography_gist_selectivity(PG_FUNCTION_ARGS) { PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); /* Oid operator = PG_GETARG_OID(1); */ List *args = (List *) PG_GETARG_POINTER(2); /* int varRelid = PG_GETARG_INT32(3); */ Oid relid; HeapTuple stats_tuple; GEOG_STATS *geogstats; /* * This is to avoid casting the corresponding * "type-punned" pointer, which would break * "strict-aliasing rules". */ GEOG_STATS **gsptr=&geogstats; int geogstats_nvalues = 0; Node *other; Var *self; GBOX search_box; float8 selectivity = 0; POSTGIS_DEBUG(2, "geography_gist_selectivity called"); /* Fail if not a binary opclause (probably shouldn't happen) */ if (list_length(args) != 2) { POSTGIS_DEBUG(3, "geography_gist_selectivity: not a binary opclause"); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } /* * This selectivity function is invoked by a clause of the form <arg> && <arg> * * In typical usage, one argument will be a column reference, while the other will * be a geography constant; set self to point to the column argument and other * to point to the constant argument. */ other = (Node *) linitial(args); if ( ! IsA(other, Const) ) { self = (Var *)other; other = (Node *) lsecond(args); } else { self = (Var *) lsecond(args); } if ( ! IsA(other, Const) ) { POSTGIS_DEBUG(3, " no constant arguments - returning default selectivity"); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } /* * We don't have a nice <const> && <var> or <var> && <const> * situation here. <const> && <const> would probably get evaluated * away by PgSQL earlier on. <func> && <const> is harder, and the * case we get often is <const> && ST_Expand(<var>), which does * actually have a subtly different selectivity than a bae * <const> && <var> call. It's calculatable though, by expanding * every cell in the histgram appropriately. * * Discussion: http://trac.osgeo.org/postgis/ticket/1828 * * To do? Do variable selectivity based on the <func> node. */ if ( ! IsA(self, Var) ) { POSTGIS_DEBUG(3, " no bare variable argument ? - returning a moderate selectivity"); // PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL); PG_RETURN_FLOAT8(0.33333); } /* Convert coordinates to 3D geodesic */ search_box.flags = 1; FLAGS_SET_GEODETIC(search_box.flags, 1); if ( ! gserialized_datum_get_gbox_p(((Const*)other)->constvalue, &search_box) ) { POSTGIS_DEBUG(3, " search box cannot be calculated"); PG_RETURN_FLOAT8(0.0); } POSTGIS_DEBUGF(4, " requested search box is : %.15g %.15g %.15g, %.15g %.15g %.15g", search_box.xmin, search_box.ymin, search_box.zmin, search_box.xmax, search_box.ymax, search_box.zmax); /* * Get pg_statistic row */ relid = getrelid(self->varno, root->parse->rtable); stats_tuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid), Int16GetDatum(self->varattno), 0, 0); if ( ! stats_tuple ) { POSTGIS_DEBUG(3, " No statistics, returning default estimate"); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } if ( ! get_attstatsslot(stats_tuple, 0, 0, STATISTIC_KIND_GEOGRAPHY, InvalidOid, NULL, NULL, #if POSTGIS_PGSQL_VERSION >= 85 NULL, #endif (float4 **)gsptr, &geogstats_nvalues) ) { POSTGIS_DEBUG(3, " STATISTIC_KIND_GEOGRAPHY stats not found - returning default geography selectivity"); ReleaseSysCache(stats_tuple); PG_RETURN_FLOAT8(DEFAULT_GEOGRAPHY_SEL); } POSTGIS_DEBUGF(4, " %d read from stats", geogstats_nvalues); POSTGIS_DEBUGF(4, " histo: xmin,ymin,zmin: %f,%f,%f", geogstats->xmin, geogstats->ymin, geogstats->zmin); POSTGIS_DEBUGF(4, " histo: xmax,ymax: %f,%f,%f", geogstats->xmax, geogstats->ymax, geogstats->zmax); POSTGIS_DEBUGF(4, " histo: unitsx: %f", geogstats->unitsx); POSTGIS_DEBUGF(4, " histo: unitsy: %f", geogstats->unitsy); POSTGIS_DEBUGF(4, " histo: unitsz: %f", geogstats->unitsz); POSTGIS_DEBUGF(4, " histo: avgFeatureCoverage: %f", geogstats->avgFeatureCoverage); POSTGIS_DEBUGF(4, " histo: avgFeatureCells: %f", geogstats->avgFeatureCells); /* * Do the estimation */ selectivity = estimate_selectivity(&search_box, geogstats); POSTGIS_DEBUGF(3, " returning computed value: %f", selectivity); free_attstatsslot(0, NULL, 0, (float *)geogstats, geogstats_nvalues); ReleaseSysCache(stats_tuple); PG_RETURN_FLOAT8(selectivity); }
/* ---------------------------------------------------------------- * ExecInsert * * INSERTs have to add the tuple into * the base relation and insert appropriate tuples into the * index relations. * Insert can be part of an update operation when * there is a preceding SplitUpdate node. * ---------------------------------------------------------------- */ void ExecInsert(TupleTableSlot *slot, DestReceiver *dest, EState *estate, PlanGenerator planGen, bool isUpdate) { void *tuple = NULL; ResultRelInfo *resultRelInfo = NULL; Relation resultRelationDesc = NULL; Oid newId = InvalidOid; TupleTableSlot *partslot = NULL; AOTupleId aoTupleId = AOTUPLEID_INIT; bool rel_is_heap = false; bool rel_is_aorows = false; bool rel_is_aocols = false; bool rel_is_external = false; /* * get information on the (current) result relation */ if (estate->es_result_partitions) { resultRelInfo = slot_get_partition(slot, estate); /* Check whether the user provided the correct leaf part only if required */ if (!dml_ignore_target_partition_check) { Assert(NULL != estate->es_result_partitions->part && NULL != resultRelInfo->ri_RelationDesc); List *resultRelations = estate->es_plannedstmt->resultRelations; /* * Only inheritance can generate multiple result relations and inheritance * is not compatible with partitions. As we are in inserting in partitioned * table, we should not have more than one resultRelation */ Assert(list_length(resultRelations) == 1); /* We only have one resultRelations entry where the user originally intended to insert */ int rteIdxForUserRel = linitial_int(resultRelations); Assert (rteIdxForUserRel > 0); Oid userProvidedRel = InvalidOid; if (1 == rteIdxForUserRel) { /* Optimization for typical case */ userProvidedRel = ((RangeTblEntry *) estate->es_plannedstmt->rtable->head->data.ptr_value)->relid; } else { userProvidedRel = getrelid(rteIdxForUserRel, estate->es_plannedstmt->rtable); } /* Error out if user provides a leaf partition that does not match with our calculated partition */ if (userProvidedRel != estate->es_result_partitions->part->parrelid && userProvidedRel != resultRelInfo->ri_RelationDesc->rd_id) { ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("Trying to insert row into wrong partition"), errdetail("Expected partition: %s, provided partition: %s", resultRelInfo->ri_RelationDesc->rd_rel->relname.data, estate->es_result_relation_info->ri_RelationDesc->rd_rel->relname.data))); } } estate->es_result_relation_info = resultRelInfo; } else { resultRelInfo = estate->es_result_relation_info; } Assert (!resultRelInfo->ri_projectReturning); resultRelationDesc = resultRelInfo->ri_RelationDesc; rel_is_heap = RelationIsHeap(resultRelationDesc); rel_is_aocols = RelationIsAoCols(resultRelationDesc); rel_is_aorows = RelationIsAoRows(resultRelationDesc); rel_is_external = RelationIsExternal(resultRelationDesc); partslot = reconstructMatchingTupleSlot(slot, resultRelInfo); if (rel_is_heap) { tuple = ExecFetchSlotHeapTuple(partslot); } else if (rel_is_aorows) { tuple = ExecFetchSlotMemTuple(partslot, false); } else if (rel_is_external) { if (estate->es_result_partitions && estate->es_result_partitions->part->parrelid != 0) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Insert into external partitions not supported."))); return; } else { tuple = ExecFetchSlotHeapTuple(partslot); } } else { Assert(rel_is_aocols); tuple = ExecFetchSlotMemTuple(partslot, true); } Assert(partslot != NULL && tuple != NULL); /* Execute triggers in Planner-generated plans */ if (planGen == PLANGEN_PLANNER) { /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) { HeapTuple newtuple; /* NYI */ if(rel_is_aocols) elog(ERROR, "triggers are not supported on tables that use column-oriented storage"); newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); if (newtuple == NULL) /* "do nothing" */ { return; } if (newtuple != tuple) /* modified by Trigger(s) */ { /* * Put the modified tuple into a slot for convenience of routines * below. We assume the tuple was allocated in per-tuple memory * context, and therefore will go away by itself. The tuple table * slot should not try to clear it. */ TupleTableSlot *newslot = estate->es_trig_tuple_slot; if (newslot->tts_tupleDescriptor != partslot->tts_tupleDescriptor) ExecSetSlotDescriptor(newslot, partslot->tts_tupleDescriptor); ExecStoreGenericTuple(newtuple, newslot, false); newslot->tts_tableOid = partslot->tts_tableOid; /* for constraints */ tuple = newtuple; partslot = newslot; } } } /* * Check the constraints of the tuple */ if (resultRelationDesc->rd_att->constr && planGen == PLANGEN_PLANNER) { ExecConstraints(resultRelInfo, partslot, estate); } /* * insert the tuple * * Note: heap_insert returns the tid (location) of the new tuple in the * t_self field. * * NOTE: for append-only relations we use the append-only access methods. */ if (rel_is_aorows) { if (resultRelInfo->ri_aoInsertDesc == NULL) { /* Set the pre-assigned fileseg number to insert into */ ResultRelInfoSetSegno(resultRelInfo, estate->es_result_aosegnos); resultRelInfo->ri_aoInsertDesc = appendonly_insert_init(resultRelationDesc, ActiveSnapshot, resultRelInfo->ri_aosegno, false); } appendonly_insert(resultRelInfo->ri_aoInsertDesc, tuple, &newId, &aoTupleId); } else if (rel_is_aocols) { if (resultRelInfo->ri_aocsInsertDesc == NULL) { ResultRelInfoSetSegno(resultRelInfo, estate->es_result_aosegnos); resultRelInfo->ri_aocsInsertDesc = aocs_insert_init(resultRelationDesc, resultRelInfo->ri_aosegno, false); } newId = aocs_insert(resultRelInfo->ri_aocsInsertDesc, partslot); aoTupleId = *((AOTupleId*)slot_get_ctid(partslot)); } else if (rel_is_external) { /* Writable external table */ if (resultRelInfo->ri_extInsertDesc == NULL) resultRelInfo->ri_extInsertDesc = external_insert_init(resultRelationDesc); newId = external_insert(resultRelInfo->ri_extInsertDesc, tuple); } else { Insist(rel_is_heap); newId = heap_insert(resultRelationDesc, tuple, estate->es_snapshot->curcid, true, true, GetCurrentTransactionId()); } IncrAppended(); (estate->es_processed)++; (resultRelInfo->ri_aoprocessed)++; estate->es_lastoid = newId; partslot->tts_tableOid = RelationGetRelid(resultRelationDesc); if (rel_is_aorows || rel_is_aocols) { /* * insert index entries for AO Row-Store tuple */ if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(partslot, (ItemPointer)&aoTupleId, estate, false); } else { /* Use parttuple for index update in case this is an indexed heap table. */ TupleTableSlot *xslot = partslot; void *xtuple = tuple; setLastTid(&(((HeapTuple) xtuple)->t_self)); /* * insert index entries for tuple */ if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(xslot, &(((HeapTuple) xtuple)->t_self), estate, false); } if (planGen == PLANGEN_PLANNER) { /* AFTER ROW INSERT Triggers */ ExecARInsertTriggers(estate, resultRelInfo, tuple); } }
Datum geometry_gist_sel_2d(PG_FUNCTION_ARGS) { PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0); /* Oid operator = PG_GETARG_OID(1); */ List *args = (List *) PG_GETARG_POINTER(2); /* int varRelid = PG_GETARG_INT32(3); */ Oid relid; HeapTuple stats_tuple; GEOM_STATS *geomstats; /* * This is to avoid casting the corresponding * "type-punned" pointer, which would break * "strict-aliasing rules". */ GEOM_STATS **gsptr=&geomstats; int geomstats_nvalues=0; Node *other; Var *self; GBOX search_box; float8 selectivity=0; POSTGIS_DEBUG(2, "geometry_gist_sel called"); /* Fail if not a binary opclause (probably shouldn't happen) */ if (list_length(args) != 2) { POSTGIS_DEBUG(3, "geometry_gist_sel: not a binary opclause"); PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL); } /* * Find the constant part */ other = (Node *) linitial(args); if ( ! IsA(other, Const) ) { self = (Var *)other; other = (Node *) lsecond(args); } else { self = (Var *) lsecond(args); } if ( ! IsA(other, Const) ) { POSTGIS_DEBUG(3, " no constant arguments - returning default selectivity"); PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL); } /* * We are working on two constants.. * TODO: check if expression is true, * returned set would be either * the whole or none. */ if ( ! IsA(self, Var) ) { POSTGIS_DEBUG(3, " no variable argument ? - returning default selectivity"); PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL); } /* * Convert the constant to a BOX */ if( ! gserialized_datum_get_gbox_p(((Const*)other)->constvalue, &search_box) ) { POSTGIS_DEBUG(3, "search box is EMPTY"); PG_RETURN_FLOAT8(0.0); } POSTGIS_DEBUGF(4, " requested search box is : %.15g %.15g, %.15g %.15g",search_box.xmin,search_box.ymin,search_box.xmax,search_box.ymax); /* * Get pg_statistic row */ relid = getrelid(self->varno, root->parse->rtable); stats_tuple = SearchSysCache(STATRELATT, ObjectIdGetDatum(relid), Int16GetDatum(self->varattno), 0, 0); if ( ! stats_tuple ) { POSTGIS_DEBUG(3, " No statistics, returning default estimate"); PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL); } if ( ! get_attstatsslot(stats_tuple, 0, 0, STATISTIC_KIND_GEOMETRY, InvalidOid, NULL, NULL, #if POSTGIS_PGSQL_VERSION >= 85 NULL, #endif (float4 **)gsptr, &geomstats_nvalues) ) { POSTGIS_DEBUG(3, " STATISTIC_KIND_GEOMETRY stats not found - returning default geometry selectivity"); ReleaseSysCache(stats_tuple); PG_RETURN_FLOAT8(DEFAULT_GEOMETRY_SEL); } POSTGIS_DEBUGF(4, " %d read from stats", geomstats_nvalues); POSTGIS_DEBUGF(4, " histo: xmin,ymin: %f,%f", geomstats->xmin, geomstats->ymin); POSTGIS_DEBUGF(4, " histo: xmax,ymax: %f,%f", geomstats->xmax, geomstats->ymax); POSTGIS_DEBUGF(4, " histo: cols: %f", geomstats->rows); POSTGIS_DEBUGF(4, " histo: rows: %f", geomstats->cols); POSTGIS_DEBUGF(4, " histo: avgFeatureArea: %f", geomstats->avgFeatureArea); POSTGIS_DEBUGF(4, " histo: avgFeatureCells: %f", geomstats->avgFeatureCells); /* * Do the estimation */ selectivity = estimate_selectivity(&search_box, geomstats); POSTGIS_DEBUGF(3, " returning computed value: %f", selectivity); free_attstatsslot(0, NULL, 0, (float *)geomstats, geomstats_nvalues); ReleaseSysCache(stats_tuple); PG_RETURN_FLOAT8(selectivity); }