pdo_sql_parser.re 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  4. +----------------------------------------------------------------------+
  5. | Copyright (c) 1997-2018 The PHP Group |
  6. +----------------------------------------------------------------------+
  7. | This source file is subject to version 3.01 of the PHP license, |
  8. | that is bundled with this package in the file LICENSE, and is |
  9. | available through the world-wide-web at the following url: |
  10. | http://www.php.net/license/3_01.txt |
  11. | If you did not receive a copy of the PHP license and are unable to |
  12. | obtain it through the world-wide-web, please send a note to |
  13. | license@php.net so we can mail you a copy immediately. |
  14. +----------------------------------------------------------------------+
  15. | Author: George Schlossnagle <george@omniti.com> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "php.h"
  19. #include "php_pdo_driver.h"
  20. #include "php_pdo_int.h"
  21. #define PDO_PARSER_TEXT 1
  22. #define PDO_PARSER_BIND 2
  23. #define PDO_PARSER_BIND_POS 3
  24. #define PDO_PARSER_EOI 4
  25. #define RET(i) {s->cur = cursor; return i; }
  26. #define SKIP_ONE(i) {s->cur = s->tok + 1; return i; }
  27. #define YYCTYPE unsigned char
  28. #define YYCURSOR cursor
  29. #define YYLIMIT s->end
  30. #define YYMARKER s->ptr
  31. #define YYFILL(n) { RET(PDO_PARSER_EOI); }
  32. typedef struct Scanner {
  33. char *ptr, *cur, *tok, *end;
  34. } Scanner;
  35. static int scan(Scanner *s)
  36. {
  37. char *cursor = s->cur;
  38. s->tok = cursor;
  39. /*!re2c
  40. BINDCHR = [:][a-zA-Z0-9_]+;
  41. QUESTION = [?];
  42. COMMENTS = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
  43. SPECIALS = [:?"'-/];
  44. MULTICHAR = ([:]{2,}|[?]{2,});
  45. ANYNOEOF = [\001-\377];
  46. */
  47. /*!re2c
  48. (["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); }
  49. (['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
  50. MULTICHAR { RET(PDO_PARSER_TEXT); }
  51. BINDCHR { RET(PDO_PARSER_BIND); }
  52. QUESTION { RET(PDO_PARSER_BIND_POS); }
  53. SPECIALS { SKIP_ONE(PDO_PARSER_TEXT); }
  54. COMMENTS { RET(PDO_PARSER_TEXT); }
  55. (ANYNOEOF\SPECIALS)+ { RET(PDO_PARSER_TEXT); }
  56. */
  57. }
  58. struct placeholder {
  59. char *pos;
  60. size_t len;
  61. size_t qlen; /* quoted length of value */
  62. char *quoted; /* quoted value */
  63. int freeq;
  64. int bindno;
  65. struct placeholder *next;
  66. };
  67. static void free_param_name(zval *el) {
  68. efree(Z_PTR_P(el));
  69. }
  70. PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, size_t inquery_len,
  71. char **outquery, size_t *outquery_len)
  72. {
  73. Scanner s;
  74. char *ptr, *newbuffer;
  75. ptrdiff_t t;
  76. uint32_t bindno = 0;
  77. int ret = 0;
  78. size_t newbuffer_len;
  79. HashTable *params;
  80. struct pdo_bound_param_data *param;
  81. int query_type = PDO_PLACEHOLDER_NONE;
  82. struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
  83. ptr = *outquery;
  84. s.cur = inquery;
  85. s.end = inquery + inquery_len + 1;
  86. /* phase 1: look for args */
  87. while((t = scan(&s)) != PDO_PARSER_EOI) {
  88. if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
  89. if (t == PDO_PARSER_BIND) {
  90. ptrdiff_t len = s.cur - s.tok;
  91. if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
  92. continue;
  93. }
  94. query_type |= PDO_PLACEHOLDER_NAMED;
  95. } else {
  96. query_type |= PDO_PLACEHOLDER_POSITIONAL;
  97. }
  98. plc = emalloc(sizeof(*plc));
  99. memset(plc, 0, sizeof(*plc));
  100. plc->next = NULL;
  101. plc->pos = s.tok;
  102. plc->len = s.cur - s.tok;
  103. plc->bindno = bindno++;
  104. if (placetail) {
  105. placetail->next = plc;
  106. } else {
  107. placeholders = plc;
  108. }
  109. placetail = plc;
  110. }
  111. }
  112. if (bindno == 0) {
  113. /* nothing to do; good! */
  114. return 0;
  115. }
  116. /* did the query make sense to me? */
  117. if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
  118. /* they mixed both types; punt */
  119. pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters");
  120. ret = -1;
  121. goto clean_up;
  122. }
  123. if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
  124. /* query matches native syntax */
  125. ret = 0;
  126. goto clean_up;
  127. }
  128. if (stmt->named_rewrite_template) {
  129. /* magic/hack.
  130. * We we pretend that the query was positional even if
  131. * it was named so that we fall into the
  132. * named rewrite case below. Not too pretty,
  133. * but it works. */
  134. query_type = PDO_PLACEHOLDER_POSITIONAL;
  135. }
  136. params = stmt->bound_params;
  137. /* Do we have placeholders but no bound params */
  138. if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
  139. pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound");
  140. ret = -1;
  141. goto clean_up;
  142. }
  143. if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
  144. /* extra bit of validation for instances when same params are bound more than once */
  145. if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
  146. int ok = 1;
  147. for (plc = placeholders; plc; plc = plc->next) {
  148. if ((param = zend_hash_str_find_ptr(params, plc->pos, plc->len)) == NULL) {
  149. ok = 0;
  150. break;
  151. }
  152. }
  153. if (ok) {
  154. goto safe;
  155. }
  156. }
  157. pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens");
  158. ret = -1;
  159. goto clean_up;
  160. }
  161. safe:
  162. /* what are we going to do ? */
  163. if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
  164. /* query generation */
  165. newbuffer_len = inquery_len;
  166. /* let's quote all the values */
  167. for (plc = placeholders; plc; plc = plc->next) {
  168. if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
  169. param = zend_hash_index_find_ptr(params, plc->bindno);
  170. } else {
  171. param = zend_hash_str_find_ptr(params, plc->pos, plc->len);
  172. }
  173. if (param == NULL) {
  174. /* parameter was not defined */
  175. ret = -1;
  176. pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined");
  177. goto clean_up;
  178. }
  179. if (stmt->dbh->methods->quoter) {
  180. zval *parameter;
  181. if (Z_ISREF(param->parameter)) {
  182. parameter = Z_REFVAL(param->parameter);
  183. } else {
  184. parameter = &param->parameter;
  185. }
  186. if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(parameter) == IS_RESOURCE) {
  187. php_stream *stm;
  188. php_stream_from_zval_no_verify(stm, parameter);
  189. if (stm) {
  190. zend_string *buf;
  191. buf = php_stream_copy_to_mem(stm, PHP_STREAM_COPY_ALL, 0);
  192. if (!buf) {
  193. buf = ZSTR_EMPTY_ALLOC();
  194. }
  195. if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf), ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
  196. param->param_type)) {
  197. /* bork */
  198. ret = -1;
  199. strncpy(stmt->error_code, stmt->dbh->error_code, 6);
  200. if (buf) {
  201. zend_string_release_ex(buf, 0);
  202. }
  203. goto clean_up;
  204. }
  205. if (buf) {
  206. zend_string_release_ex(buf, 0);
  207. }
  208. } else {
  209. pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource");
  210. ret = -1;
  211. goto clean_up;
  212. }
  213. plc->freeq = 1;
  214. } else {
  215. enum pdo_param_type param_type = param->param_type;
  216. zend_string *buf = NULL;
  217. /* assume all types are nullable */
  218. if (Z_TYPE_P(parameter) == IS_NULL) {
  219. param_type = PDO_PARAM_NULL;
  220. }
  221. switch (param_type) {
  222. case PDO_PARAM_BOOL:
  223. plc->quoted = zend_is_true(parameter) ? "1" : "0";
  224. plc->qlen = sizeof("1")-1;
  225. plc->freeq = 0;
  226. break;
  227. case PDO_PARAM_INT:
  228. buf = zend_long_to_str(zval_get_long(parameter));
  229. plc->qlen = ZSTR_LEN(buf);
  230. plc->quoted = estrdup(ZSTR_VAL(buf));
  231. plc->freeq = 1;
  232. break;
  233. case PDO_PARAM_NULL:
  234. plc->quoted = "NULL";
  235. plc->qlen = sizeof("NULL")-1;
  236. plc->freeq = 0;
  237. break;
  238. default:
  239. buf = zval_get_string(parameter);
  240. if (!stmt->dbh->methods->quoter(stmt->dbh, ZSTR_VAL(buf),
  241. ZSTR_LEN(buf), &plc->quoted, &plc->qlen,
  242. param_type)) {
  243. /* bork */
  244. ret = -1;
  245. strncpy(stmt->error_code, stmt->dbh->error_code, 6);
  246. if (buf) {
  247. zend_string_release_ex(buf, 0);
  248. }
  249. goto clean_up;
  250. }
  251. plc->freeq = 1;
  252. }
  253. if (buf) {
  254. zend_string_release_ex(buf, 0);
  255. }
  256. }
  257. } else {
  258. zval *parameter;
  259. if (Z_ISREF(param->parameter)) {
  260. parameter = Z_REFVAL(param->parameter);
  261. } else {
  262. parameter = &param->parameter;
  263. }
  264. plc->quoted = Z_STRVAL_P(parameter);
  265. plc->qlen = Z_STRLEN_P(parameter);
  266. }
  267. newbuffer_len += plc->qlen;
  268. }
  269. rewrite:
  270. /* allocate output buffer */
  271. newbuffer = emalloc(newbuffer_len + 1);
  272. *outquery = newbuffer;
  273. /* and build the query */
  274. plc = placeholders;
  275. ptr = inquery;
  276. do {
  277. t = plc->pos - ptr;
  278. if (t) {
  279. memcpy(newbuffer, ptr, t);
  280. newbuffer += t;
  281. }
  282. memcpy(newbuffer, plc->quoted, plc->qlen);
  283. newbuffer += plc->qlen;
  284. ptr = plc->pos + plc->len;
  285. plc = plc->next;
  286. } while (plc);
  287. t = (inquery + inquery_len) - ptr;
  288. if (t) {
  289. memcpy(newbuffer, ptr, t);
  290. newbuffer += t;
  291. }
  292. *newbuffer = '\0';
  293. *outquery_len = newbuffer - *outquery;
  294. ret = 1;
  295. goto clean_up;
  296. } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
  297. /* rewrite ? to :pdoX */
  298. char *name, *idxbuf;
  299. const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
  300. int bind_no = 1;
  301. newbuffer_len = inquery_len;
  302. if (stmt->bound_param_map == NULL) {
  303. ALLOC_HASHTABLE(stmt->bound_param_map);
  304. zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
  305. }
  306. for (plc = placeholders; plc; plc = plc->next) {
  307. int skip_map = 0;
  308. char *p;
  309. name = estrndup(plc->pos, plc->len);
  310. /* check if bound parameter is already available */
  311. if (!strcmp(name, "?") || (p = zend_hash_str_find_ptr(stmt->bound_param_map, name, plc->len)) == NULL) {
  312. spprintf(&idxbuf, 0, tmpl, bind_no++);
  313. } else {
  314. idxbuf = estrdup(p);
  315. skip_map = 1;
  316. }
  317. plc->quoted = idxbuf;
  318. plc->qlen = strlen(plc->quoted);
  319. plc->freeq = 1;
  320. newbuffer_len += plc->qlen;
  321. if (!skip_map && stmt->named_rewrite_template) {
  322. /* create a mapping */
  323. zend_hash_str_update_mem(stmt->bound_param_map, name, plc->len, idxbuf, plc->qlen + 1);
  324. }
  325. /* map number to name */
  326. zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1);
  327. efree(name);
  328. }
  329. goto rewrite;
  330. } else {
  331. /* rewrite :name to ? */
  332. newbuffer_len = inquery_len;
  333. if (stmt->bound_param_map == NULL) {
  334. ALLOC_HASHTABLE(stmt->bound_param_map);
  335. zend_hash_init(stmt->bound_param_map, 13, NULL, free_param_name, 0);
  336. }
  337. for (plc = placeholders; plc; plc = plc->next) {
  338. char *name;
  339. name = estrndup(plc->pos, plc->len);
  340. zend_hash_index_update_mem(stmt->bound_param_map, plc->bindno, name, plc->len + 1);
  341. efree(name);
  342. plc->quoted = "?";
  343. plc->qlen = 1;
  344. }
  345. goto rewrite;
  346. }
  347. clean_up:
  348. while (placeholders) {
  349. plc = placeholders;
  350. placeholders = plc->next;
  351. if (plc->freeq) {
  352. efree(plc->quoted);
  353. }
  354. efree(plc);
  355. }
  356. return ret;
  357. }
  358. /*
  359. * Local variables:
  360. * tab-width: 4
  361. * c-basic-offset: 4
  362. * End:
  363. * vim600: noet sw=4 ts=4 fdm=marker ft=c
  364. * vim<600: noet sw=4 ts=4
  365. */