in hphp/runtime/ext/pdo/ext_pdo.cpp [2336:2635]
int pdo_parse_params(sp_PDOStatement stmt, const String& in, String &out) {
Scanner s;
const char *ptr;
char *newbuffer;
int t;
int bindno = 0;
int ret = 0;
int newbuffer_len;
Array params;
req::ptr<PDOBoundParam> param;
int query_type = PDO_PLACEHOLDER_NONE;
struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
s.cur = (char*)in.data();
s.lim = (char*)in.data() + in.size() + 1;
/* phase 1: look for args */
while ((t = scan(&s)) != PDO_PARSER_EOI) {
if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
if (t == PDO_PARSER_BIND) {
int len = s.cur - s.tok;
if ((in.data() < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
continue;
}
query_type |= PDO_PLACEHOLDER_NAMED;
} else {
query_type |= PDO_PLACEHOLDER_POSITIONAL;
}
plc = req::make_raw<placeholder>();
memset(plc, 0, sizeof(*plc));
plc->next = NULL;
plc->pos = s.tok;
plc->len = s.cur - s.tok;
plc->bindno = bindno++;
if (placetail) {
placetail->next = plc;
} else {
placeholders = plc;
}
placetail = plc;
}
}
if (bindno == 0) {
/* nothing to do; good! */
return 0;
}
/* did the query make sense to me? */
if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
/* they mixed both types; punt */
pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
"mixed named and positional parameters");
ret = -1;
goto clean_up;
}
if ((int)stmt->supports_placeholders == query_type &&
!stmt->named_rewrite_template) {
/* query matches native syntax */
ret = 0;
goto clean_up;
}
if (stmt->named_rewrite_template) {
/* magic/hack.
* We we pretend that the query was positional even if
* it was named so that we fall into the
* named rewrite case below. Not too pretty,
* but it works. */
query_type = PDO_PLACEHOLDER_POSITIONAL;
}
params = stmt->bound_params;
/* Do we have placeholders but no bound params */
if (bindno && params.empty() &&
stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound");
ret = -1;
goto clean_up;
}
if (!params.empty() && bindno != params.size() &&
stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
/* extra bit of validation for instances when same params are bound
more then once */
if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > params.size()) {
int ok = 1;
for (plc = placeholders; plc; plc = plc->next) {
if (!params.exists(String(plc->pos, plc->len, CopyString))) {
ok = 0;
break;
}
}
if (ok) {
goto safe;
}
}
pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
"number of bound variables does not match number "
"of tokens");
ret = -1;
goto clean_up;
}
safe:
/* what are we going to do ? */
if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
/* query generation */
newbuffer_len = in.size();
/* let's quote all the values */
for (plc = placeholders; plc; plc = plc->next) {
Variant vparam;
if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
vparam = params[plc->bindno];
} else {
String str(plc->pos, plc->len, CopyString);
auto const arrkey = params.convertKey<IntishCast::Cast>(str);
vparam = params[arrkey];
}
if (vparam.isNull()) {
/* parameter was not defined */
ret = -1;
pdo_raise_impl_error(stmt->dbh, stmt, "HY093",
"parameter was not defined");
goto clean_up;
}
param = cast<PDOBoundParam>(vparam);
if (stmt->dbh->conn()->support(PDOConnection::MethodQuoter)) {
if (param->param_type == PDO_PARAM_LOB &&
param->parameter.isResource()) {
Variant buf = HHVM_FN(stream_get_contents)(
param->parameter.toResource());
if (!same(buf, false)) {
if (!stmt->dbh->conn()->quoter(buf.toString(), plc->quoted,
param->param_type)) {
/* bork */
ret = -1;
setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
goto clean_up;
}
} else {
pdo_raise_impl_error(stmt->dbh, stmt, "HY105",
"Expected a stream resource");
ret = -1;
goto clean_up;
}
} else {
do {
switch (param->parameter.getType()) {
case KindOfUninit:
case KindOfNull:
plc->quoted = "NULL";
continue;
case KindOfInt64:
case KindOfDouble:
plc->quoted = param->parameter.toString();
continue;
case KindOfBoolean:
param->parameter = param->parameter.toInt64();
// fallthru
case KindOfPersistentString:
case KindOfString:
case KindOfPersistentVec:
case KindOfVec:
case KindOfPersistentDict:
case KindOfDict:
case KindOfPersistentKeyset:
case KindOfKeyset:
case KindOfObject:
case KindOfResource:
case KindOfRFunc:
case KindOfFunc:
case KindOfClass:
case KindOfLazyClass:
case KindOfClsMeth:
case KindOfRClsMeth:
if (!stmt->dbh->conn()->quoter(
param->parameter.toString(),
plc->quoted,
param->param_type)) {
/* bork */
ret = -1;
setPDOError(stmt->error_code, stmt->dbh->conn()->error_code);
goto clean_up;
}
continue;
}
not_reached();
} while (0);
}
} else {
plc->quoted = param->parameter.toString();
}
newbuffer_len += plc->quoted.size();
}
rewrite:
/* allocate output buffer */
out = String(newbuffer_len, ReserveString);
newbuffer = out.mutableData();
/* and build the query */
plc = placeholders;
ptr = in.data();
do {
t = plc->pos - ptr;
if (t) {
memcpy(newbuffer, ptr, t);
newbuffer += t;
}
memcpy(newbuffer, plc->quoted.data(), plc->quoted.size());
newbuffer += plc->quoted.size();
ptr = plc->pos + plc->len;
plc = plc->next;
} while (plc);
t = (in.data() + in.size()) - ptr;
if (t) {
memcpy(newbuffer, ptr, t);
newbuffer += t;
}
out.setSize(newbuffer - out.data());
ret = 1;
goto clean_up;
} else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
/* rewrite ? to :pdoX */
StringBuffer idxbuf;
const char *tmpl = stmt->named_rewrite_template ?
stmt->named_rewrite_template : ":pdo%d";
int bind_no = 1;
newbuffer_len = in.size();
for (plc = placeholders; plc; plc = plc->next) {
int skip_map = 0;
String name(plc->pos, plc->len, CopyString);
auto const name_key =
stmt->bound_param_map.convertKey<IntishCast::Cast>(name);
/* check if bound parameter is already available */
if (!strcmp(name.c_str(), "?") ||
!stmt->bound_param_map.exists(name_key)) {
idxbuf.printf(tmpl, bind_no++);
} else {
idxbuf.clear();
idxbuf.append(stmt->bound_param_map[name_key].toString());
skip_map = 1;
}
plc->quoted = idxbuf.detach();
newbuffer_len += plc->quoted.size();
if (!skip_map && stmt->named_rewrite_template) {
/* create a mapping */
stmt->bound_param_map.set(name_key,
make_tv<KindOfString>(plc->quoted.get()));
}
/* map number to name */
stmt->bound_param_map.set(plc->bindno, plc->quoted);
}
goto rewrite;
} else {
/* rewrite :name to ? */
newbuffer_len = in.size();
for (plc = placeholders; plc; plc = plc->next) {
String name(plc->pos, plc->len, CopyString);
stmt->bound_param_map.set(plc->bindno, name);
plc->quoted = "?";
}
goto rewrite;
}
clean_up:
while (placeholders) {
plc = placeholders;
placeholders = plc->next;
plc->quoted.reset();
req::free(plc);
}
return ret;
}