static bool do_fetch()

in hphp/runtime/ext/pdo/ext_pdo.cpp [1710:1999]


static bool do_fetch(sp_PDOStatement stmt,
                     Variant& ret,
                     PDOFetchType how,
                     PDOFetchOrientation ori,
                     long offset,
                     Variant *return_all) {
  if (how == PDO_FETCH_USE_DEFAULT) {
    how = stmt->default_fetch_type;
  }
  int flags = how & PDO_FETCH_FLAGS;
  how = (PDOFetchType)(how & ~PDO_FETCH_FLAGS);

  if (!do_fetch_common(stmt, ori, offset)) {
    return false;
  }

  if (how == PDO_FETCH_BOUND) {
    ret = true;
    return true;
  }

  int colno;
  if ((flags & PDO_FETCH_GROUP) && stmt->fetch.column == -1) {
    colno = 1;
  } else {
    colno = stmt->fetch.column;
  }

  if (how == PDO_FETCH_LAZY) {
    get_lazy_object(stmt, ret);
    return true;
  }

  String clsname, old_clsname;
  Variant old_ctor_args;
  ret = false;
  int i = 0;
  switch (how) {
  case PDO_FETCH_USE_DEFAULT:
  case PDO_FETCH_ASSOC:
  case PDO_FETCH_BOTH:
  case PDO_FETCH_NUM:
  case PDO_FETCH_NAMED:
    ret = Array::CreateDict();
    break;

  case PDO_FETCH_KEY_PAIR:
    if (stmt->column_count != 2) {
      pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
                           "PDO::FETCH_KEY_PAIR fetch mode requires the "
                           "result set to contain extactly 2 columns.");
      return false;
    }
    if (!return_all) {
      ret = Array::CreateDict();
    }
    break;

  case PDO_FETCH_COLUMN:
    if (colno >= 0 && colno < stmt->column_count) {
      if (flags == PDO_FETCH_GROUP && stmt->fetch.column == -1) {
        fetch_value(stmt, ret, 1, NULL);
      } else if (flags == PDO_FETCH_GROUP && colno) {
        fetch_value(stmt, ret, 0, NULL);
      } else {
        fetch_value(stmt, ret, colno, NULL);
      }
      if (!return_all) {
        return true;
      }
      break;
    } else {
      pdo_raise_impl_error(stmt->dbh, stmt, "HY000", "Invalid column index");
    }
    return false;

  case PDO_FETCH_OBJ:
    ret = SystemLib::AllocStdClassObject();
    break;

  case PDO_FETCH_CLASS:
    if (flags & PDO_FETCH_CLASSTYPE) {
      old_clsname = stmt->fetch.clsname;
      old_ctor_args = stmt->fetch.ctor_args;

      Variant val;
      fetch_value(stmt, val, i++, NULL);
      if (!val.isNull()) {
        if (!HHVM_FN(class_exists)(val.toString())) {
          stmt->fetch.clsname = "stdclass";
        } else {
          stmt->fetch.clsname = val.toString();
        }
      }

      do_fetch_class_prepare(stmt);
    }
    clsname = stmt->fetch.clsname;
    if (clsname.empty()) {
      pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
                           "No fetch class specified");
      return false;
    }
    if ((flags & PDO_FETCH_SERIALIZE) == 0) {
      ret = create_object_only(clsname);
      if (!do_fetch_class_prepare(stmt)) {
        return false;
      }
      if (!stmt->fetch.constructor.empty() &&
          (flags & PDO_FETCH_PROPS_LATE)) {
        ret.asCObjRef()->o_invoke(stmt->fetch.constructor,
                                  stmt->fetch.ctor_args.toArray());
      }
    }
    break;

  case PDO_FETCH_INTO:
    if (stmt->fetch.into.isNull()) {
      pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
                           "No fetch-into object specified.");
      return false;
    }

    ret = stmt->fetch.into;
    if (ret.isObject() &&
        ret.getObjectData()->instanceof(SystemLib::s_stdclassClass)) {
      how = PDO_FETCH_OBJ;
    }
    break;

  case PDO_FETCH_FUNC:
    if (stmt->fetch.func.empty()) {
      pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
                           "No fetch function specified");
      return false;
    }
    if (!do_fetch_func_prepare(stmt)) {
      return false;
    }
    break;

  default:
    assertx(false);
    return false;
  }

  Variant grp_val;
  if (return_all && how != PDO_FETCH_KEY_PAIR) {
    if (flags == PDO_FETCH_GROUP && how == PDO_FETCH_COLUMN &&
        stmt->fetch.column > 0) {
      fetch_value(stmt, grp_val, colno, NULL);
    } else {
      fetch_value(stmt, grp_val, i, NULL);
    }
    grp_val = grp_val.toString();
    if (how == PDO_FETCH_COLUMN) {
      i = stmt->column_count; /* no more data to fetch */
    } else {
      i++;
    }
  }

  for (int idx = 0; i < stmt->column_count; i++, idx++) {
    const String& name = cast<PDOColumn>(stmt->columns[i])->name;
    Variant val;
    fetch_value(stmt, val, i, NULL);

    switch (how) {
    case PDO_FETCH_ASSOC: {
      auto const name_key =
        ret.asArrRef().convertKey<IntishCast::Cast>(name);
      ret.asArrRef().set(name_key, *val.asTypedValue());
      break;
    }
    case PDO_FETCH_KEY_PAIR: {
      Variant tmp;
      fetch_value(stmt, tmp, ++i, NULL);
      if (return_all) {
        auto const val_key_ret =
          return_all->asArrRef().convertKey<IntishCast::Cast>(val);
        return_all->asArrRef().set(val_key_ret, *tmp.asTypedValue());
      } else {
        auto const val_key =
          ret.asArrRef().convertKey<IntishCast::Cast>(val);
        ret.asArrRef().set(val_key, *tmp.asTypedValue());
      }
      return true;
    }
    case PDO_FETCH_USE_DEFAULT:
    case PDO_FETCH_BOTH: {
      auto const name_key =
        ret.asArrRef().convertKey<IntishCast::Cast>(name);
      ret.asArrRef().set(name_key, *val.asTypedValue());
      ret.asArrRef().append(val);
      break;
    }

    case PDO_FETCH_NAMED: {
      auto const name_key =
        ret.asArrRef().convertKey<IntishCast::Cast>(name);
      /* already have an item with this name? */
      forceToDict(ret);
      if (ret.asArrRef().exists(name_key)) {
        auto const curr_val = ret.asArrRef().lval(name_key);
        if (!isArrayLikeType(curr_val.type())) {
          Array arr = Array::CreateVec();
          arr.append(curr_val.tv());
          arr.append(val);
          ret.toArray().set(name_key, make_array_like_tv(arr.get()));
        } else {
          asArrRef(curr_val).append(val);
        }
      } else {
        ret.asArrRef().set(name_key, *val.asTypedValue());
      }
      break;
    }
    case PDO_FETCH_NUM:
      ret.asArrRef().append(val);
      break;

    case PDO_FETCH_OBJ:
    case PDO_FETCH_INTO:
      ret.toObject()->o_set(name, val);
      break;

    case PDO_FETCH_CLASS:
      if ((flags & PDO_FETCH_SERIALIZE) == 0 || idx) {
        ret.toObject()->o_set(name, val);
      } else {
#ifdef MBO_0
        ret = unserialize_from_string(val);
        if (same(ret, false)) {
          pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
                               "cannot unserialize data");
          return false;
        }
#endif
        // hzhao: not sure how we support class serialization
        pdo_raise_impl_error(stmt->dbh, stmt, "HY000",
                             "cannot unserialize class");
        return false;
      }
      break;

    case PDO_FETCH_FUNC:
      forceToDict(stmt->fetch.values).set(idx, val);
      break;

    default:
      pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range");
      return false;
    }
  }

  switch (how) {
  case PDO_FETCH_CLASS:
    if (!stmt->fetch.constructor.empty() &&
        !(flags & (PDO_FETCH_PROPS_LATE | PDO_FETCH_SERIALIZE))) {
      ret.toObject()->o_invoke(stmt->fetch.constructor,
                               stmt->fetch.ctor_args.toArray());
    }
    if (flags & PDO_FETCH_CLASSTYPE) {
      stmt->fetch.clsname = old_clsname;
      stmt->fetch.ctor_args = old_ctor_args;
    }
    break;

  case PDO_FETCH_FUNC:
    ret = vm_call_user_func(stmt->fetch.func,
                            stmt->fetch.values.toArray());
    break;

  default:
    break;
  }

  if (return_all) {
    auto const grp_key =
      return_all->asArrRef().convertKey<IntishCast::Cast>(grp_val);
    if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
      return_all->asArrRef().set(grp_key, *ret.asTypedValue());
    } else {
      auto const lval = return_all->asArrRef().lval(grp_key);
      forceToArray(lval).append(ret);
    }
  }

  return true;
}