/**
 * Handle MySQL's plugin functions
 * This is mostly for handling the null related functions that MySQL adds to the execution plan
 */
void InSub::handleFunc(gp_walk_info* gwip, Item_func* func)
{
    if (func->functype() == Item_func::TRIG_COND_FUNC || func->functype() == Item_func::COND_OR_FUNC)
    {
        // purpose: remove the isnull() function from the parsetree in ptWorkStack.
        // IDB handles the null semantics in the join operation
        // trigcond(or_cond) is the only form we recognize for now
        if (func->argument_count() > 2)
        {
            fGwip.fatalParseError = true;
            fGwip.parseErrorText = "Unsupported item in IN subquery";
            return;
        }

        Item_cond* cond;

        if (func->functype() == Item_func::TRIG_COND_FUNC)
        {
            Item* item;

            if (func->arguments()[0]->type() == Item::REF_ITEM)
                item = (Item_ref*)(func->arguments()[0])->real_item();
            else
                item = func->arguments()[0];

            cond = (Item_cond*)(item);
        }
        else
        {
            cond = (Item_cond*)(func);
        }

        if (cond->functype() == Item_func::COND_OR_FUNC)
        {
            // (cache=item) case. do nothing. ignore trigcond()?
            if (cond->argument_list()->elements == 1)
                return;

            // (cache=item or isnull(item)) case. remove "or isnull()"
            if (cond->argument_list()->elements == 2)
            {
                // don't know how to deal with this. don't think it's a fatal error either.
                if (gwip->ptWorkStack.empty())
                    return;

                ParseTree* pt = gwip->ptWorkStack.top();

                if (!pt->left() || !pt->right())
                    return;

                SimpleFilter* sf = dynamic_cast<SimpleFilter*>(pt->left()->data());

                //assert (sf && sf->op()->op() == execplan::OP_ISNULL);
                if (!sf || sf->op()->op() != execplan::OP_ISNULL)
                    return;

                delete sf;
                sf = dynamic_cast<SimpleFilter*>(pt->right()->data());

                //idbassert(sf && sf->op()->op() == execplan::OP_EQ);
                if (!sf || sf->op()->op() != execplan::OP_EQ)
                    return;

                // set NULLMATCH for both operand. It's really a setting for the join.
                // should only set NULLMATCH when the subtype is NOT_IN. for some IN subquery
                // with aggregation column, MySQL inefficiently convert to:
                // (cache=item or item is null) and item is not null, which is equivalent to
                // cache = item. Do not set NULLMATCH for this case.
                // Because we don't know IN or NOTIN yet, set candidate bit and switch to NULLMATCH
                // later in handleNot function.
                if (sf->lhs()->joinInfo() & JOIN_CORRELATED)
                    sf->lhs()->joinInfo(sf->lhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE);

                if (sf->rhs()->joinInfo() & JOIN_CORRELATED)
                    sf->rhs()->joinInfo(sf->rhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE);

                pt = pt->right();
                gwip->ptWorkStack.pop();
                gwip->ptWorkStack.push(pt);
            }
        }
        else if (cond->functype() == Item_func::EQ_FUNC)
        {
            // not in (select const ...)
            if (gwip->ptWorkStack.empty())
                return;

            ParseTree* pt = gwip->ptWorkStack.top();
            SimpleFilter* sf = dynamic_cast<SimpleFilter*>(pt->data());

            if (!sf || sf->op()->op() != execplan::OP_EQ)
                return;

            if (sf->lhs()->joinInfo() & JOIN_CORRELATED)
                sf->lhs()->joinInfo(sf->lhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE);

            if (sf->rhs()->joinInfo() & JOIN_CORRELATED)
                sf->rhs()->joinInfo(sf->rhs()->joinInfo() | JOIN_NULLMATCH_CANDIDATE);
        }
    }
}
示例#2
0
void PrintItemTree(Item* item, int indent)
{
#ifdef PURE_LIBRARY
	BHERROR("NOT IMPLEMENTED! Depends on MySQL code.");
#else
	static const char* name_of[] = {
			"FIELD_ITEM", "FUNC_ITEM", "SUM_FUNC_ITEM", "STRING_ITEM", "INT_ITEM",
			"REAL_ITEM", "NULL_ITEM", "VARBIN_ITEM", "COPY_STR_ITEM", "FIELD_AVG_ITEM",
			"DEFAULT_VALUE_ITEM", "PROC_ITEM", "COND_ITEM", "REF_ITEM", "FIELD_STD_ITEM",
			"FIELD_VARIANCE_ITEM", "INSERT_VALUE_ITEM", "SUBSELECT_ITEM", "ROW_ITEM",
			"CACHE_ITEM", "TYPE_HOLDER", "PARAM_ITEM", "TRIGGER_FIELD_ITEM", "DECIMAL_ITEM",
			"XPATH_NODESET", "XPATH_NODESET_CMP", "VIEW_FIXER_ITEM" };
	static const char* sum_name_of[] =  {
	    "COUNT_FUNC", "COUNT_DISTINCT_FUNC", "SUM_FUNC", "SUM_DISTINCT_FUNC", "AVG_FUNC",
	    "AVG_DISTINCT_FUNC", "MIN_FUNC", "MAX_FUNC", "STD_FUNC",
	    "VARIANCE_FUNC", "SUM_BIT_FUNC", "UDF_SUM_FUNC", "GROUP_CONCAT_FUNC" };

	for(int i = 0; i <= indent; i++)
		fprintf(stderr, "*");
	fprintf(stderr, " ");
	indent += 1;

	if(!item) {
		fprintf(stderr, "NULL item\n");
		return;
	}

	const char* name;
    Item::Type type = item->type();

	if((int)type < sizeof(name_of)/sizeof(*name_of))
		name = name_of[type];
	else name = "BHFIELD_ITEM";

	const char* result = "<unknown result type>";
    switch (item->result_type()) {
		case STRING_RESULT: result = "STRING_RESULT"; break;
		case INT_RESULT: result = "INT_RESULT"; break;
		case REAL_RESULT: result = "REAL_RESULT"; break;
		case DECIMAL_RESULT: result = "DECIMAL_RESULT"; break;
    }
    fprintf(stderr, "%s %s @%p %s %s max_length=%d",
    		name, item->full_name(),  (void*)item,
    		FieldType(item->field_type()), result, item->max_length);

    if(item->result_type() == DECIMAL_RESULT)
    	fprintf(stderr, " [prec=%d,dec=%d,int=%d]",
    			item->decimal_precision(), (int)item->decimals, item->decimal_int_part());

    switch(type) {
		case Item::FUNC_ITEM: {
			Item_func* func = static_cast <Item_func*> (item);

			fprintf(stderr, " func_name=%s\n", func->func_name());
			String str;
			// GA, print function takes extra argument but do not use it in the base class.
			func->print(&str,QT_ORDINARY);
			fprintf(stderr, " f contents: %s\n", str.c_ptr_safe());

			Item** args = func->arguments();
			for(uint i = 0; i < func->argument_count(); i++)
				PrintItemTree(args[i], indent);
			return;
		}
		case Item::COND_ITEM: {
			Item_cond* cond = static_cast <Item_cond*> (item);

			fprintf(stderr, " func_name=%s\n", cond->func_name());

			List_iterator<Item> li(*cond->argument_list());
			Item* arg;
			while ((arg = li++))
				PrintItemTree(arg, indent);
			return;
		}
		case Item::SUM_FUNC_ITEM: {
			Item_sum* sum_func = static_cast <Item_sum*> (item);

			uint index = sum_func->sum_func();
			const char* sumname;
			if (index >= sizeof(sum_name_of)/sizeof(*sum_name_of))
				sumname = "<UNKNOWN>";
			else
				sumname = sum_name_of[index];

			fprintf(stderr, "  %s\n", sumname);

			Item** args = sum_func->args;
			uint args_count = sum_func->arg_count;
			for(uint i=0; i < args_count; i++)
				PrintItemTree(args[i], indent);
			return;
		}
		case Item::REF_ITEM: {
			Item_ref* ref = static_cast<Item_ref*> (item);
			Item* real = ref->real_item();
			fprintf(stderr, "\n");
			if(ref != real) PrintItemTree(real, indent);
			return;
		}
		case Item::INT_ITEM: {
			Item_int_with_ref* int_ref = dynamic_cast<Item_int_with_ref*>(item);
			if(!int_ref)
				break;
			// else item is an instance of Item_int_with_ref, not Item_int
			fprintf(stderr, " [Item_int_with_ref]\n");
			PrintItemTree(int_ref->real_item(), indent);
			return;
		}
		case Item::SUBSELECT_ITEM:
			Item_subselect* ss = dynamic_cast<Item_subselect*>(item);
			fprintf(stderr, " subselect type %d\n", ss->substype());
			String str;
			// GA, print function takes extra argument but do not use it.
			ss->get_unit()->print(&str, QT_ORDINARY);
			fprintf(stderr, "%s\n", str.c_ptr_safe());
    }

    fprintf(stderr, "\n");
#endif
}