Databases 19 min read

MySQL Source Code Analysis: How a Record Is Determined to Match a WHERE Clause

This article walks through MySQL 8.0.32 source code to explain how the server builds a tree for a WHERE clause, evaluates each condition using classes such as Item_cond_and, Item_cond_or, Item_func_gt, and Item_func_eq, and ultimately decides whether a row satisfies the query.

Aikesheng Open Source Community
Aikesheng Open Source Community
Aikesheng Open Source Community
MySQL Source Code Analysis: How a Record Is Determined to Match a WHERE Clause

1. Preparation

We create a test table t1 and insert sample rows, then run the example SQL:

SELECT * FROM t1
WHERE i2 > 20 AND (i1 = 50 OR i1 = 80);

2. Overall Introduction

In MySQL the WHERE clause is represented as a tree of Item objects. The tree for the example consists of an Item_cond_and node that connects an Item_func_gt (i2 > 20) and an Item_cond_or (i1 = 50 OR i1 = 80). Each leaf is an Item_func_eq or Item_func_gt that holds an Item_field (the column) and an Item_int (the constant).

3. Source Code Analysis

3.1 ExecuteIteratorQuery()

bool Query_expression::ExecuteIteratorQuery(THD *thd) {
  ...
  for (;;) {
    int error = m_root_iterator->Read();
    if (error > 0 || thd->is_error())
      return true;               // fatal error
    else if (error < 0)
      break;                     // no more rows
    else if (thd->killed) {
      thd->send_kill_message();
      return true;               // aborted by user
    }
    if (query_result->send_data(thd, *fields))
      return true;
    ...
  }
  ...
}

This is the entry point for a SELECT statement. For our example the root iterator is a FilterIterator , which reads rows from the storage engine and then evaluates the WHERE condition.

3.2 FilterIterator::Read()

int FilterIterator::Read() {
  for (;;) {
    int err = m_source->Read();
    if (err != 0) return err;
    bool matched = m_condition->val_int();
    if (thd()->killed) {
      thd()->send_kill_message();
      return 1;
    }
    if (thd()->is_error()) return 1;
    if (!matched) {
      m_source->UnlockRow();
      continue;
    }
    return 0;   // successful row
  }
}

The iterator reads a row, then calls Item_cond_and::val_int() (the root condition) to decide whether the row matches.

3.3 compare_int_signed()

int Arg_comparator::compare_int_signed() {
  longlong val1 = (*left)->val_int();
  if (current_thd->is_error()) return 0;
  if (!(*left)->null_value) {
    longlong val2 = (*right)->val_int();
    if (current_thd->is_error()) return 0;
    if (!(*right)->null_value) {
      if (set_null) owner->null_value = false;
      if (val1 < val2) return -1;
      if (val1 == val2) return 0;
      return 1;
    }
  }
  if (set_null) owner->null_value = true;
  return -1;
}

This function compares the left‑hand side and right‑hand side integer values of a comparison operator, handling NULLs and returning -1, 0, or 1.

3.4 Arg_comparator::compare()

inline int Arg_comparator::compare() { return (this->*func)(); }

The func pointer is set to the appropriate comparison routine (e.g., compare_int_signed ) during query preparation.

3.5 Item_func_gt::val_int()

longlong Item_func_gt::val_int() {
  assert(fixed == 1);
  int value = cmp.compare();
  return value > 0 ? 1 : 0;
}

Evaluates the ‘greater‑than’ condition by delegating to the comparator and returns 1 if the left value is larger.

3.6 Item_cond_and::val_int()

longlong Item_cond_and::val_int() {
  List_iterator_fast
li(list);
  Item *item;
  null_value = false;
  while ((item = li++)) {
    if (!item->val_bool()) {
      if (ignore_unknown() || !(null_value = item->null_value))
        return 0;   // false
    }
    if (current_thd->is_error()) return error_int();
  }
  return null_value ? 0 : 1;
}

The method iterates over all AND‑connected conditions; all must evaluate to true. If any condition is false and either ignore_unknown() is true or the condition is not NULL, the whole expression returns false. NULL handling is controlled by ignore_unknown() .

3.7 Item_func_eq::val_int()

longlong Item_func_eq::val_int() {
  assert(fixed == 1);
  int value = cmp.compare();
  return value == 0 ? 1 : 0;
}

Returns 1 when the left and right operands are equal.

3.8 Item_cond_or::val_int()

longlong Item_cond_or::val_int() {
  List_iterator_fast
li(list);
  Item *item;
  null_value = false;
  while ((item = li++)) {
    if (item->val_bool()) {
      null_value = false;
      return 1;   // true
    }
    if (item->null_value) null_value = true;
  }
  return 0;       // false
}

The OR node returns true as soon as any sub‑condition evaluates to true; otherwise it returns false, propagating NULL information when needed.

4. Summary

The article demonstrates how MySQL evaluates WHERE clauses that contain AND and OR operators. After a row is fetched, Item_cond_and::val_int() checks that every AND sub‑condition is true, while Item_cond_or::val_int() succeeds if any OR sub‑condition is true. The comparison logic is performed by compare_int_signed() and the surrounding wrapper classes, with careful handling of NULL values via ignore_unknown() and the null_value flag.

SQLsource code analysisMySQLquery executionDatabase InternalsWHERE clause
Aikesheng Open Source Community
Written by

Aikesheng Open Source Community

The Aikesheng Open Source Community provides stable, enterprise‑grade MySQL open‑source tools and services, releases a premium open‑source component each year (1024), and continuously operates and maintains them.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.