code: func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) {
  	uf := newUnionFind(func(r1, r2 *unionFindRoot) (*unionFindRoot, *unionFindRoot) {
  		if livevars.Contains(r1.key) {
  			return r1, r2
  		}
  		return r2, r1
  	})
  	for _, expr := range query {
  		if expr.IsEquality() && !expr.Negated && len(expr.With) == 0 {
  			a, b := expr.Operand(0), expr.Operand(1)
  			varA, ok1 := a.Value.(ast.Var)
  			varB, ok2 := b.Value.(ast.Var)
  			if ok1 && ok2 {
  				if _, ok := uf.Merge(varA, varB); !ok {
  					return nil, false
  				}
  			} else if ok1 && ast.IsConstant(b.Value) {
  				root := uf.MakeSet(varA)
  				if root.constant != nil && !root.constant.Equal(b) {
  					return nil, false
  				}
  				root.constant = b
  			} else if ok2 && ast.IsConstant(a.Value) {
  				root := uf.MakeSet(varB)
  				if root.constant != nil && !root.constant.Equal(a) {
  					return nil, false
  				}
  				root.constant = a
  			}
  		}
  	}
  
  	return uf, true
  }
docs: // makeDisjointSets builds the union-find structure for the query. The structure
  // is built by processing all of the equality exprs in the query. Sets represent
  // vars that must be equal to each other. In addition to vars, each set can have
  // at most one constant. If the query contains expressions that cannot be
  // satisfied (e.g., because a set has multiple constants) this function returns
  // false.