CodeIgniter 2’s Active Record escapes values for you, which is why it gets recommended as the safe alternative to writing SQL by hand. It escapes the arguments it recognises as values, and it has three ordinary-looking ways of not doing that — two of which appear in every codebase.
// escaped: the value is quoted by the driver
$this->db->where('status', $status);
// not escaped: one argument, so the whole string is treated as SQL
$this->db->where("YEAR(created_at) = $year");
// not escaped: the third argument turns it off
$this->db->where('created_at <', 'NOW()', false);
// not escaped, and rarely noticed: a column name is SQL
$this->db->order_by($_GET['sort'], 'ASC');
What the builder does is not a prepared statement — the driver quotes the value into the query string, so it also depends on the connection charset being what you assume. Both dangerous cases are user input arriving somewhere the builder reads as SQL: a single-argument where(), and any column name, including the one behind a sort link. Columns and tables cannot be escaped at all, so the only correct treatment there is a whitelist that maps sort=price onto a known column and rejects everything else. escape() and escape_str() exist for the fragments that genuinely have to be assembled by hand.