/* * bms_compare - qsort-style comparator for bitmapsets * * This guarantees to report values as equal iff bms_equal would say they are * equal. Otherwise, the highest-numbered bit that is set in one value but * not the other determines the result. (This rule means that, for example, * {6} is greater than {5}, which seems plausible.) */ int bms_compare(const Bitmapset *a, const Bitmapset *b) { int shortlen; int i; /* Handle cases where either input is NULL */ if (a == NULL) return bms_is_empty(b) ? 0 : -1; else if (b == NULL) return bms_is_empty(a) ? 0 : +1; /* Handle cases where one input is longer than the other */ shortlen = Min(a->nwords, b->nwords); for (i = shortlen; i < a->nwords; i++) { if (a->words[i] != 0) return +1; } for (i = shortlen; i < b->nwords; i++) { if (b->words[i] != 0) return -1; } /* Process words in common */ i = shortlen; while (--i >= 0) { bitmapword aw = a->words[i]; bitmapword bw = b->words[i]; if (aw != bw) return (aw > bw) ? +1 : -1; } return 0; }
/* * bms_nonempty_difference - do sets have a nonempty difference? */ bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b) { int shortlen; int i; /* Handle cases where either input is NULL */ if (a == NULL) return false; if (b == NULL) return !bms_is_empty(a); /* Check words in common */ shortlen = Min(a->nwords, b->nwords); for (i = 0; i < shortlen; i++) { if ((a->words[i] & ~b->words[i]) != 0) return true; } /* Check extra words in a */ for (; i < a->nwords; i++) { if (a->words[i] != 0) return true; } return false; }
/* * bms_is_subset - is A a subset of B? */ bool bms_is_subset(const Bitmapset *a, const Bitmapset *b) { int shortlen; int longlen; int i; /* Handle cases where either input is NULL */ if (a == NULL) return true; /* empty set is a subset of anything */ if (b == NULL) return bms_is_empty(a); /* Check common words */ shortlen = Min(a->nwords, b->nwords); for (i = 0; i < shortlen; i++) { if ((a->words[i] & ~b->words[i]) != 0) return false; } /* Check extra words */ if (a->nwords > b->nwords) { longlen = a->nwords; for (; i < longlen; i++) { if (a->words[i] != 0) return false; } } return true; }
/* * bms_equal - are two bitmapsets equal? * * This is logical not physical equality; in particular, a NULL pointer will * be reported as equal to a palloc'd value containing no members. */ bool bms_equal(const Bitmapset *a, const Bitmapset *b) { const Bitmapset *shorter; const Bitmapset *longer; int shortlen; int longlen; int i; /* Handle cases where either input is NULL */ if (a == NULL) { if (b == NULL) return true; return bms_is_empty(b); } else if (b == NULL) return bms_is_empty(a); /* Identify shorter and longer input */ if (a->nwords <= b->nwords) { shorter = a; longer = b; } else { shorter = b; longer = a; } /* And process */ shortlen = shorter->nwords; for (i = 0; i < shortlen; i++) { if (shorter->words[i] != longer->words[i]) return false; } longlen = longer->nwords; for (; i < longlen; i++) { if (longer->words[i] != 0) return false; } return true; }
/* * GetAnyDataNode * Pick any data node from given set, but try a preferred node */ int GetAnyDataNode(Bitmapset *nodes) { Bitmapset *preferred = NULL; int i, nodeid; int nmembers = 0; int members[NumDataNodes]; for (i = 0; i < num_preferred_data_nodes; i++) { char ntype = PGXC_NODE_DATANODE; nodeid = PGXCNodeGetNodeId(preferred_data_node[i], &ntype); /* OK, found one */ if (bms_is_member(nodeid, nodes)) preferred = bms_add_member(preferred, nodeid); } /* * If no preferred data nodes or they are not in the desired set, pick up * from the original set. */ if (bms_is_empty(preferred)) preferred = bms_copy(nodes); /* * Load balance. * We can not get item from the set, convert it to array */ while ((nodeid = bms_first_member(preferred)) >= 0) members[nmembers++] = nodeid; bms_free(preferred); /* If there is a single member nothing to balance */ if (nmembers == 1) return members[0]; /* * In general, the set may contain any number of nodes, and if we save * previous returned index for load balancing the distribution won't be * flat, because small set will probably reset saved value, and lower * indexes will be picked up more often. * So we just get a random value from 0..nmembers-1. */ return members[((unsigned int) random()) % nmembers]; }
/* * add_vars_to_targetlist * For each variable appearing in the list, add it to the owning * relation's targetlist if not already present, and mark the variable * as being needed for the indicated join (or for final output if * where_needed includes "relation 0"). * * The list may also contain PlaceHolderVars. These don't necessarily * have a single owning relation; we keep their attr_needed info in * root->placeholder_list instead. */ void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed) { ListCell *temp; Assert(!bms_is_empty(where_needed)); foreach(temp, vars) { Node *node = (Node *) lfirst(temp); if (IsA(node, Var)) { Var *var = (Var *) node; RelOptInfo *rel = find_base_rel(root, var->varno); int attno = var->varattno; Assert(attno >= rel->min_attr && attno <= rel->max_attr); attno -= rel->min_attr; if (rel->attr_needed[attno] == NULL) { /* Variable not yet requested, so add to reltargetlist */ /* XXX is copyObject necessary here? */ rel->reltargetlist = lappend(rel->reltargetlist, copyObject(var)); } rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno], where_needed); } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; PlaceHolderInfo *phinfo = find_placeholder_info(root, phv); phinfo->ph_needed = bms_add_members(phinfo->ph_needed, where_needed); } else elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); }
/* * CopyIntoStream * * COPY events to a stream from an input source */ void CopyIntoStream(Relation stream, TupleDesc desc, HeapTuple *tuples, int ntuples) { int i; InsertBatchAck *ack = NULL; InsertBatch *batch = NULL; Size size = 0; bool snap = ActiveSnapshotSet(); Bitmapset *all_targets = GetStreamReaders(RelationGetRelid(stream)); Bitmapset *adhoc = GetAdhocContinuousViewIds(); Bitmapset *targets = bms_difference(all_targets, adhoc); dsm_cqueue *cq = NULL; bytea *packed_desc; if (snap) PopActiveSnapshot(); packed_desc = PackTupleDesc(desc); if (!bms_is_empty(targets)) { if (synchronous_stream_insert) { batch = InsertBatchCreate(); ack = palloc0(sizeof(InsertBatchAck)); ack->batch_id = batch->id; ack->batch = batch; } cq = GetWorkerQueue(); } for (i=0; i<ntuples; i++) { StreamTupleState *sts; HeapTuple tup = tuples[i]; int len; sts = StreamTupleStateCreate(tup, desc, packed_desc, targets, ack, &len); if (cq) { dsm_cqueue_push_nolock(cq, sts, len); size += len; } } pfree(packed_desc); if (cq) dsm_cqueue_unlock(cq); stream_stat_report(RelationGetRelid(stream), ntuples, 1, size); if (batch) { pfree(ack); InsertBatchWaitAndRemove(batch, ntuples); } if (snap) PushActiveSnapshot(GetTransactionSnapshot()); bms_free(all_targets); bms_free(adhoc); bms_free(targets); }
/* * bms_subset_compare - compare A and B for equality/subset relationships * * This is more efficient than testing bms_is_subset in both directions. */ BMS_Comparison bms_subset_compare(const Bitmapset *a, const Bitmapset *b) { BMS_Comparison result; int shortlen; int longlen; int i; /* Handle cases where either input is NULL */ if (a == NULL) { if (b == NULL) return BMS_EQUAL; return bms_is_empty(b) ? BMS_EQUAL : BMS_SUBSET1; } if (b == NULL) return bms_is_empty(a) ? BMS_EQUAL : BMS_SUBSET2; /* Check common words */ result = BMS_EQUAL; /* status so far */ shortlen = Min(a->nwords, b->nwords); for (i = 0; i < shortlen; i++) { bitmapword aword = a->words[i]; bitmapword bword = b->words[i]; if ((aword & ~bword) != 0) { /* a is not a subset of b */ if (result == BMS_SUBSET1) return BMS_DIFFERENT; result = BMS_SUBSET2; } if ((bword & ~aword) != 0) { /* b is not a subset of a */ if (result == BMS_SUBSET2) return BMS_DIFFERENT; result = BMS_SUBSET1; } } /* Check extra words */ if (a->nwords > b->nwords) { longlen = a->nwords; for (; i < longlen; i++) { if (a->words[i] != 0) { /* a is not a subset of b */ if (result == BMS_SUBSET1) return BMS_DIFFERENT; result = BMS_SUBSET2; } } } else if (a->nwords < b->nwords) { longlen = b->nwords; for (; i < longlen; i++) { if (b->words[i] != 0) { /* b is not a subset of a */ if (result == BMS_SUBSET2) return BMS_DIFFERENT; result = BMS_SUBSET1; } } } return result; }
/* * sepgsql_dml_privileges * * Entrypoint of the DML permission checks */ bool sepgsql_dml_privileges(List *rangeTabls, bool abort_on_violation) { ListCell *lr; foreach(lr, rangeTabls) { RangeTblEntry *rte = lfirst(lr); uint32 required = 0; List *tableIds; ListCell *li; /* * Only regular relations shall be checked */ if (rte->rtekind != RTE_RELATION) continue; /* * Find out required permissions */ if (rte->requiredPerms & ACL_SELECT) required |= SEPG_DB_TABLE__SELECT; if (rte->requiredPerms & ACL_INSERT) required |= SEPG_DB_TABLE__INSERT; if (rte->requiredPerms & ACL_UPDATE) { if (!bms_is_empty(rte->updatedCols)) required |= SEPG_DB_TABLE__UPDATE; else required |= SEPG_DB_TABLE__LOCK; } if (rte->requiredPerms & ACL_DELETE) required |= SEPG_DB_TABLE__DELETE; /* * Skip, if nothing to be checked */ if (required == 0) continue; /* * If this RangeTblEntry is also supposed to reference inherited * tables, we need to check security label of the child tables. So, we * expand rte->relid into list of OIDs of inheritance hierarchy, then * checker routine will be invoked for each relations. */ if (!rte->inh) tableIds = list_make1_oid(rte->relid); else tableIds = find_all_inheritors(rte->relid, NoLock, NULL); foreach(li, tableIds) { Oid tableOid = lfirst_oid(li); Bitmapset *selectedCols; Bitmapset *insertedCols; Bitmapset *updatedCols; /* * child table has different attribute numbers, so we need to fix * up them. */ selectedCols = fixup_inherited_columns(rte->relid, tableOid, rte->selectedCols); insertedCols = fixup_inherited_columns(rte->relid, tableOid, rte->insertedCols); updatedCols = fixup_inherited_columns(rte->relid, tableOid, rte->updatedCols); /* * check permissions on individual tables */ if (!check_relation_privileges(tableOid, selectedCols, insertedCols, updatedCols, required, abort_on_violation)) return false; }
/* * make_restrictinfo_internal * * Common code for the main entry points and the recursive cases. */ static RestrictInfo * make_restrictinfo_internal(Expr *clause, Expr *orclause, bool is_pushed_down, bool outerjoin_delayed, bool pseudoconstant, Index security_level, Relids required_relids, Relids outer_relids, Relids nullable_relids) { RestrictInfo *restrictinfo = makeNode(RestrictInfo); restrictinfo->clause = clause; restrictinfo->orclause = orclause; restrictinfo->is_pushed_down = is_pushed_down; restrictinfo->outerjoin_delayed = outerjoin_delayed; restrictinfo->pseudoconstant = pseudoconstant; restrictinfo->can_join = false; /* may get set below */ restrictinfo->security_level = security_level; restrictinfo->outer_relids = outer_relids; restrictinfo->nullable_relids = nullable_relids; /* * If it's potentially delayable by lower-level security quals, figure out * whether it's leakproof. We can skip testing this for level-zero quals, * since they would never get delayed on security grounds anyway. */ if (security_level > 0) restrictinfo->leakproof = !contain_leaked_vars((Node *) clause); else restrictinfo->leakproof = false; /* really, "don't know" */ /* * If it's a binary opclause, set up left/right relids info. In any case * set up the total clause relids info. */ if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2) { restrictinfo->left_relids = pull_varnos(get_leftop(clause)); restrictinfo->right_relids = pull_varnos(get_rightop(clause)); restrictinfo->clause_relids = bms_union(restrictinfo->left_relids, restrictinfo->right_relids); /* * Does it look like a normal join clause, i.e., a binary operator * relating expressions that come from distinct relations? If so we * might be able to use it in a join algorithm. Note that this is a * purely syntactic test that is made regardless of context. */ if (!bms_is_empty(restrictinfo->left_relids) && !bms_is_empty(restrictinfo->right_relids) && !bms_overlap(restrictinfo->left_relids, restrictinfo->right_relids)) { restrictinfo->can_join = true; /* pseudoconstant should certainly not be true */ Assert(!restrictinfo->pseudoconstant); } } else { /* Not a binary opclause, so mark left/right relid sets as empty */ restrictinfo->left_relids = NULL; restrictinfo->right_relids = NULL; /* and get the total relid set the hard way */ restrictinfo->clause_relids = pull_varnos((Node *) clause); } /* required_relids defaults to clause_relids */ if (required_relids != NULL) restrictinfo->required_relids = required_relids; else restrictinfo->required_relids = restrictinfo->clause_relids; /* * Fill in all the cacheable fields with "not yet set" markers. None of * these will be computed until/unless needed. Note in particular that we * don't mark a binary opclause as mergejoinable or hashjoinable here; * that happens only if it appears in the right context (top level of a * joinclause list). */ restrictinfo->parent_ec = NULL; restrictinfo->eval_cost.startup = -1; restrictinfo->norm_selec = -1; restrictinfo->outer_selec = -1; restrictinfo->mergeopfamilies = NIL; restrictinfo->left_ec = NULL; restrictinfo->right_ec = NULL; restrictinfo->left_em = NULL; restrictinfo->right_em = NULL; restrictinfo->scansel_cache = NIL; restrictinfo->outer_is_left = false; restrictinfo->hashjoinoperator = InvalidOid; restrictinfo->left_bucketsize = -1; restrictinfo->right_bucketsize = -1; restrictinfo->left_mcvfreq = -1; restrictinfo->right_mcvfreq = -1; return restrictinfo; }