Annotation of embedaddon/php/ext/pdo/pdo_sql_parser.re, revision 1.1.1.5

1.1       misho       1: /*
                      2:   +----------------------------------------------------------------------+
                      3:   | PHP Version 5                                                        |
                      4:   +----------------------------------------------------------------------+
1.1.1.3   misho       5:   | Copyright (c) 1997-2013 The PHP Group                                |
1.1       misho       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: 
1.1.1.2   misho      19: /* $Id$ */
1.1       misho      20: 
                     21: #include "php.h"
                     22: #include "php_pdo_driver.h"
                     23: #include "php_pdo_int.h"
                     24: 
                     25: #define PDO_PARSER_TEXT 1
                     26: #define PDO_PARSER_BIND 2
                     27: #define PDO_PARSER_BIND_POS 3
                     28: #define PDO_PARSER_EOI 4
                     29: 
                     30: #define RET(i) {s->cur = cursor; return i; }
                     31: #define SKIP_ONE(i) {s->cur = s->tok + 1; return i; }
                     32: 
                     33: #define YYCTYPE         unsigned char
                     34: #define YYCURSOR        cursor
1.1.1.3   misho      35: #define YYLIMIT         s->end
1.1       misho      36: #define YYMARKER        s->ptr
1.1.1.3   misho      37: #define YYFILL(n)              { RET(PDO_PARSER_EOI); }
1.1       misho      38: 
                     39: typedef struct Scanner {
1.1.1.3   misho      40:        char    *ptr, *cur, *tok, *end;
1.1       misho      41: } Scanner;
                     42: 
                     43: static int scan(Scanner *s) 
                     44: {
                     45:        char *cursor = s->cur;
                     46: 
                     47:        s->tok = cursor;
                     48:        /*!re2c
                     49:        BINDCHR         = [:][a-zA-Z0-9_]+;
                     50:        QUESTION        = [?];
                     51:        COMMENTS        = ("/*"([^*]+|[*]+[^/*])*[*]*"*/"|"--"[^\r\n]*);
                     52:        SPECIALS        = [:?"'];
1.1.1.4   misho      53:        MULTICHAR       = ([:]{2,}|[?]{2,});
1.1       misho      54:        ANYNOEOF        = [\001-\377];
                     55:        */
                     56: 
                     57:        /*!re2c
                     58:                (["](([\\]ANYNOEOF)|ANYNOEOF\["\\])*["]) { RET(PDO_PARSER_TEXT); }
                     59:                (['](([\\]ANYNOEOF)|ANYNOEOF\['\\])*[']) { RET(PDO_PARSER_TEXT); }
1.1.1.4   misho      60:                MULTICHAR                                                               { RET(PDO_PARSER_TEXT); }
1.1       misho      61:                BINDCHR                                                                 { RET(PDO_PARSER_BIND); }
                     62:                QUESTION                                                                { RET(PDO_PARSER_BIND_POS); }
                     63:                SPECIALS                                                                { SKIP_ONE(PDO_PARSER_TEXT); }
                     64:                COMMENTS                                                                { RET(PDO_PARSER_TEXT); }
                     65:                (ANYNOEOF\SPECIALS)+                                    { RET(PDO_PARSER_TEXT); }
                     66:        */      
                     67: }
                     68: 
                     69: struct placeholder {
                     70:        char *pos;
                     71:        int len;
                     72:        int bindno;
                     73:        int qlen;               /* quoted length of value */
                     74:        char *quoted;   /* quoted value */
                     75:        int freeq;
                     76:        struct placeholder *next;
                     77: };
                     78: 
                     79: PDO_API int pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, 
                     80:        char **outquery, int *outquery_len TSRMLS_DC)
                     81: {
                     82:        Scanner s;
                     83:        char *ptr, *newbuffer;
                     84:        int t;
                     85:        int bindno = 0;
                     86:        int ret = 0;
                     87:        int newbuffer_len;
                     88:        HashTable *params;
                     89:        struct pdo_bound_param_data *param;
                     90:        int query_type = PDO_PLACEHOLDER_NONE;
                     91:        struct placeholder *placeholders = NULL, *placetail = NULL, *plc = NULL;
                     92: 
                     93:        ptr = *outquery;
                     94:        s.cur = inquery;
1.1.1.3   misho      95:        s.end = inquery + inquery_len + 1;
1.1       misho      96: 
                     97:        /* phase 1: look for args */
                     98:        while((t = scan(&s)) != PDO_PARSER_EOI) {
                     99:                if (t == PDO_PARSER_BIND || t == PDO_PARSER_BIND_POS) {
                    100:                        if (t == PDO_PARSER_BIND) {
                    101:                                int len = s.cur - s.tok;
                    102:                                if ((inquery < (s.cur - len)) && isalnum(*(s.cur - len - 1))) {
                    103:                                        continue;
                    104:                                }
                    105:                                query_type |= PDO_PLACEHOLDER_NAMED;
                    106:                        } else {
                    107:                                query_type |= PDO_PLACEHOLDER_POSITIONAL;
                    108:                        }
                    109: 
                    110:                        plc = emalloc(sizeof(*plc));
                    111:                        memset(plc, 0, sizeof(*plc));
                    112:                        plc->next = NULL;
                    113:                        plc->pos = s.tok;
                    114:                        plc->len = s.cur - s.tok;
                    115:                        plc->bindno = bindno++;
                    116: 
                    117:                        if (placetail) {
                    118:                                placetail->next = plc;
                    119:                        } else {
                    120:                                placeholders = plc;
                    121:                        }
                    122:                        placetail = plc;
                    123:                }
                    124:        }
                    125: 
                    126:        if (bindno == 0) {
                    127:                /* nothing to do; good! */
                    128:                return 0;
                    129:        }
                    130: 
                    131:        /* did the query make sense to me? */
                    132:        if (query_type == (PDO_PLACEHOLDER_NAMED|PDO_PLACEHOLDER_POSITIONAL)) {
                    133:                /* they mixed both types; punt */
                    134:                pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "mixed named and positional parameters" TSRMLS_CC);
                    135:                ret = -1;
                    136:                goto clean_up;
                    137:        }
                    138: 
                    139:        if (stmt->supports_placeholders == query_type && !stmt->named_rewrite_template) {
                    140:                /* query matches native syntax */
                    141:                ret = 0;
                    142:                goto clean_up;
                    143:        }
                    144: 
                    145:        if (stmt->named_rewrite_template) {
                    146:                /* magic/hack.
                    147:                 * We we pretend that the query was positional even if
                    148:                 * it was named so that we fall into the
                    149:                 * named rewrite case below.  Not too pretty,
                    150:                 * but it works. */
                    151:                query_type = PDO_PLACEHOLDER_POSITIONAL;
                    152:        }
                    153:        
                    154:        params = stmt->bound_params;
                    155:        
                    156:        /* Do we have placeholders but no bound params */
                    157:        if (bindno && !params && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
                    158:                pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "no parameters were bound" TSRMLS_CC);
                    159:                ret = -1;
                    160:                goto clean_up;
                    161:        }
                    162: 
                    163:        if (params && bindno != zend_hash_num_elements(params) && stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
                    164:                /* extra bit of validation for instances when same params are bound more then once */
                    165:                if (query_type != PDO_PLACEHOLDER_POSITIONAL && bindno > zend_hash_num_elements(params)) {
                    166:                        int ok = 1;
                    167:                        for (plc = placeholders; plc; plc = plc->next) {
                    168:                                if (zend_hash_find(params, plc->pos, plc->len, (void**) &param) == FAILURE) {
                    169:                                        ok = 0;
                    170:                                        break;
                    171:                                }
                    172:                        }
                    173:                        if (ok) {
                    174:                                goto safe;
                    175:                        }
                    176:                }
                    177:                pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "number of bound variables does not match number of tokens" TSRMLS_CC);
                    178:                ret = -1;
                    179:                goto clean_up;
                    180:        }
                    181: safe:
                    182:        /* what are we going to do ? */
                    183:        if (stmt->supports_placeholders == PDO_PLACEHOLDER_NONE) {
                    184:                /* query generation */
                    185: 
                    186:                newbuffer_len = inquery_len;
                    187: 
                    188:                /* let's quote all the values */        
                    189:                for (plc = placeholders; plc; plc = plc->next) {
                    190:                        if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
                    191:                                ret = zend_hash_index_find(params, plc->bindno, (void**) &param);
                    192:                        } else {
                    193:                                ret = zend_hash_find(params, plc->pos, plc->len, (void**) &param);
                    194:                        }
                    195:                        if (ret == FAILURE) {
                    196:                                /* parameter was not defined */
                    197:                                ret = -1;
                    198:                                pdo_raise_impl_error(stmt->dbh, stmt, "HY093", "parameter was not defined" TSRMLS_CC);
                    199:                                goto clean_up;
                    200:                        }
                    201:                        if (stmt->dbh->methods->quoter) {
                    202:                                if (param->param_type == PDO_PARAM_LOB && Z_TYPE_P(param->parameter) == IS_RESOURCE) {
                    203:                                        php_stream *stm;
                    204: 
                    205:                                        php_stream_from_zval_no_verify(stm, &param->parameter);
                    206:                                        if (stm) {
                    207:                                                size_t len;
                    208:                                                char *buf = NULL;
                    209:                                        
                    210:                                                len = php_stream_copy_to_mem(stm, &buf, PHP_STREAM_COPY_ALL, 0);
                    211:                                                if (!stmt->dbh->methods->quoter(stmt->dbh, buf, len, &plc->quoted, &plc->qlen,
                    212:                                                                param->param_type TSRMLS_CC)) {
                    213:                                                        /* bork */
                    214:                                                        ret = -1;
                    215:                                                        strncpy(stmt->error_code, stmt->dbh->error_code, 6);
                    216:                                                        if (buf) {
                    217:                                                                efree(buf);
                    218:                                                        }
                    219:                                                        goto clean_up;
                    220:                                                }
                    221:                                                if (buf) {
                    222:                                                        efree(buf);
                    223:                                                }
                    224:                                        } else {
                    225:                                                pdo_raise_impl_error(stmt->dbh, stmt, "HY105", "Expected a stream resource" TSRMLS_CC);
                    226:                                                ret = -1;
                    227:                                                goto clean_up;
                    228:                                        }
                    229:                                        plc->freeq = 1;
                    230:                                } else {
1.1.1.5 ! misho     231:                                        zval tmp_param = *param->parameter;
        !           232:                                        zval_copy_ctor(&tmp_param);
        !           233:                                        switch (Z_TYPE(tmp_param)) {
1.1       misho     234:                                                case IS_NULL:
                    235:                                                        plc->quoted = "NULL";
                    236:                                                        plc->qlen = sizeof("NULL")-1;
                    237:                                                        plc->freeq = 0;
                    238:                                                        break;
                    239: 
                    240:                                                case IS_BOOL:
1.1.1.5 ! misho     241:                                                        convert_to_long(&tmp_param);
        !           242:                                                        /* fall through */
1.1       misho     243:                                                case IS_LONG:
                    244:                                                case IS_DOUBLE:
1.1.1.5 ! misho     245:                                                        convert_to_string(&tmp_param);
        !           246:                                                        plc->qlen = Z_STRLEN(tmp_param);
        !           247:                                                        plc->quoted = estrdup(Z_STRVAL(tmp_param));
        !           248:                                                        plc->freeq = 1;
1.1       misho     249:                                                        break;
                    250: 
                    251:                                                default:
1.1.1.5 ! misho     252:                                                        convert_to_string(&tmp_param);
        !           253:                                                        if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL(tmp_param),
        !           254:                                                                        Z_STRLEN(tmp_param), &plc->quoted, &plc->qlen,
1.1       misho     255:                                                                        param->param_type TSRMLS_CC)) {
                    256:                                                                /* bork */
                    257:                                                                ret = -1;
                    258:                                                                strncpy(stmt->error_code, stmt->dbh->error_code, 6);
                    259:                                                                goto clean_up;
                    260:                                                        }
                    261:                                                        plc->freeq = 1;
                    262:                                        }
1.1.1.5 ! misho     263:                                        zval_dtor(&tmp_param);
1.1       misho     264:                                }
                    265:                        } else {
                    266:                                plc->quoted = Z_STRVAL_P(param->parameter);
                    267:                                plc->qlen = Z_STRLEN_P(param->parameter);
                    268:                        }
                    269:                        newbuffer_len += plc->qlen;
                    270:                }
                    271: 
                    272: rewrite:
                    273:                /* allocate output buffer */
                    274:                newbuffer = emalloc(newbuffer_len + 1);
                    275:                *outquery = newbuffer;
                    276: 
                    277:                /* and build the query */
                    278:                plc = placeholders;
                    279:                ptr = inquery;
                    280: 
                    281:                do {
                    282:                        t = plc->pos - ptr;
                    283:                        if (t) {
                    284:                                memcpy(newbuffer, ptr, t);
                    285:                                newbuffer += t;
                    286:                        }
                    287:                        memcpy(newbuffer, plc->quoted, plc->qlen);
                    288:                        newbuffer += plc->qlen;
                    289:                        ptr = plc->pos + plc->len;
                    290: 
                    291:                        plc = plc->next;
                    292:                } while (plc);
                    293: 
                    294:                t = (inquery + inquery_len) - ptr;
                    295:                if (t) {
                    296:                        memcpy(newbuffer, ptr, t);
                    297:                        newbuffer += t;
                    298:                }
                    299:                *newbuffer = '\0';
                    300:                *outquery_len = newbuffer - *outquery;
                    301: 
                    302:                ret = 1;
                    303:                goto clean_up;
                    304: 
                    305:        } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
                    306:                /* rewrite ? to :pdoX */
                    307:                char *name, *idxbuf;
                    308:                const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
                    309:                int bind_no = 1;
                    310:                
                    311:                newbuffer_len = inquery_len;
                    312: 
                    313:                if (stmt->bound_param_map == NULL) {
                    314:                        ALLOC_HASHTABLE(stmt->bound_param_map);
                    315:                        zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
                    316:                }
                    317: 
                    318:                for (plc = placeholders; plc; plc = plc->next) {
                    319:                        int skip_map = 0;
                    320:                        char *p;
                    321:                        name = estrndup(plc->pos, plc->len);
                    322: 
                    323:                        /* check if bound parameter is already available */
                    324:                        if (!strcmp(name, "?") || zend_hash_find(stmt->bound_param_map, name, plc->len + 1, (void**) &p) == FAILURE) {
                    325:                                spprintf(&idxbuf, 0, tmpl, bind_no++);
                    326:                        } else {
                    327:                                idxbuf = estrdup(p);
                    328:                                skip_map = 1;
                    329:                        }
                    330: 
                    331:                        plc->quoted = idxbuf;
                    332:                        plc->qlen = strlen(plc->quoted);
                    333:                        plc->freeq = 1;
                    334:                        newbuffer_len += plc->qlen;
                    335: 
                    336:                        if (!skip_map && stmt->named_rewrite_template) {
                    337:                                /* create a mapping */
                    338:                                zend_hash_update(stmt->bound_param_map, name, plc->len + 1, idxbuf, plc->qlen + 1, NULL);
                    339:                        }
                    340: 
                    341:                        /* map number to name */
                    342:                        zend_hash_index_update(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1, NULL);
                    343:                        
                    344:                        efree(name);
                    345:                }
                    346:                                
                    347:                goto rewrite;
                    348: 
                    349:        } else {
                    350:                /* rewrite :name to ? */
                    351:                
                    352:                newbuffer_len = inquery_len;
                    353:        
                    354:                if (stmt->bound_param_map == NULL) {
                    355:                        ALLOC_HASHTABLE(stmt->bound_param_map);
                    356:                        zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
                    357:                }
                    358:                
                    359:                for (plc = placeholders; plc; plc = plc->next) {
                    360:                        char *name;
                    361:                        
                    362:                        name = estrndup(plc->pos, plc->len);
                    363:                        zend_hash_index_update(stmt->bound_param_map, plc->bindno, name, plc->len + 1, NULL);
                    364:                        efree(name);
                    365:                        plc->quoted = "?";
                    366:                        plc->qlen = 1;
                    367:                }
                    368: 
                    369:                goto rewrite;
                    370:        }
                    371: 
                    372: clean_up:
                    373: 
                    374:        while (placeholders) {
                    375:                plc = placeholders;
                    376:                placeholders = plc->next;
                    377: 
                    378:                if (plc->freeq) {
                    379:                        efree(plc->quoted);
                    380:                }
                    381: 
                    382:                efree(plc);
                    383:        }
                    384: 
                    385:        return ret;
                    386: }
                    387: 
                    388: #if 0
                    389: int old_pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery, 
                    390:                int *outquery_len TSRMLS_DC)
                    391: {
                    392:        Scanner s;
                    393:        char *ptr;
                    394:        int t;
                    395:        int bindno = 0;
                    396:        int newbuffer_len;
                    397:        int padding;
                    398:        HashTable *params = stmt->bound_params;
                    399:        struct pdo_bound_param_data *param;
                    400:        /* allocate buffer for query with expanded binds, ptr is our writing pointer */
                    401:        newbuffer_len = inquery_len;
                    402: 
                    403:        /* calculate the possible padding factor due to quoting */
                    404:        if(stmt->dbh->max_escaped_char_length) {
                    405:                padding = stmt->dbh->max_escaped_char_length;
                    406:        } else {
                    407:                padding = 3;
                    408:        }
                    409:        if(params) {
                    410:                zend_hash_internal_pointer_reset(params);
                    411:                while (SUCCESS == zend_hash_get_current_data(params, (void**)&param)) {
                    412:                        if(param->parameter) {
                    413:                                convert_to_string(param->parameter);
1.1.1.3   misho     414:                                /* accommodate a string that needs to be fully quoted
1.1       misho     415:                    bind placeholders are at least 2 characters, so
1.1.1.3   misho     416:                    the accommodate their own "'s
1.1       misho     417:                 */
                    418:                                newbuffer_len += padding * Z_STRLEN_P(param->parameter);
                    419:                        }
                    420:                        zend_hash_move_forward(params);
                    421:                }
                    422:        }
                    423:        *outquery = (char *) emalloc(newbuffer_len + 1);
                    424:        *outquery_len = 0;
                    425: 
                    426:        ptr = *outquery;
                    427:        s.cur = inquery;
                    428:        while((t = scan(&s)) != PDO_PARSER_EOI) {
                    429:                if(t == PDO_PARSER_TEXT) {
                    430:                        memcpy(ptr, s.tok, s.cur - s.tok);
                    431:                        ptr += (s.cur - s.tok);
                    432:                        *outquery_len += (s.cur - s.tok);
                    433:                }
                    434:                else if(t == PDO_PARSER_BIND) {
                    435:                        if(!params) { 
                    436:                                /* error */
                    437:                                efree(*outquery);
                    438:                                *outquery = NULL;
                    439:                                return (int) (s.cur - inquery);
                    440:                        }
                    441:                        /* lookup bind first via hash and then index */
                    442:                        /* stupid keys need to be null-terminated, even though we know their length */
                    443:                        if((SUCCESS == zend_hash_find(params, s.tok, s.cur-s.tok,(void **)&param))  
                    444:                            ||
                    445:                           (SUCCESS == zend_hash_index_find(params, bindno, (void **)&param))) 
                    446:                        {
                    447:                                char *quotedstr;
                    448:                                int quotedstrlen;
                    449:                                /* restore the in-string key, doesn't need null-termination here */
                    450:                                /* currently everything is a string here */
                    451:                                
                    452:                                /* quote the bind value if necessary */
                    453:                                if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter), 
                    454:                                        Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen TSRMLS_CC))
                    455:                                {
                    456:                                        memcpy(ptr, quotedstr, quotedstrlen);
                    457:                                        ptr += quotedstrlen;
                    458:                                        *outquery_len += quotedstrlen;
                    459:                                        efree(quotedstr);
                    460:                                } else {
                    461:                                        memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
                    462:                                        ptr += Z_STRLEN_P(param->parameter);
                    463:                                        *outquery_len += (Z_STRLEN_P(param->parameter));
                    464:                                }
                    465:                        }
                    466:                        else {
                    467:                                /* error and cleanup */
                    468:                                efree(*outquery);
                    469:                                *outquery = NULL;
                    470:                                return (int) (s.cur - inquery);
                    471:                        }
                    472:                        bindno++;
                    473:                }
                    474:                else if(t == PDO_PARSER_BIND_POS) {
                    475:                        if(!params) { 
                    476:                                /* error */
                    477:                                efree(*outquery);
                    478:                                *outquery = NULL;
                    479:                                return (int) (s.cur - inquery);
                    480:                        }
                    481:                        /* lookup bind by index */
                    482:                        if(SUCCESS == zend_hash_index_find(params, bindno, (void **)&param)) 
                    483:                        {
                    484:                                char *quotedstr;
                    485:                                int quotedstrlen;
                    486:                                /* currently everything is a string here */
                    487:                                
                    488:                                /* quote the bind value if necessary */
                    489:                                if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter), 
                    490:                                        Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen TSRMLS_CC))
                    491:                                {
                    492:                                        memcpy(ptr, quotedstr, quotedstrlen);
                    493:                                        ptr += quotedstrlen;
                    494:                                        *outquery_len += quotedstrlen;
                    495:                                        efree(quotedstr);
                    496:                                } else {
                    497:                                        memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
                    498:                                        ptr += Z_STRLEN_P(param->parameter);
                    499:                                        *outquery_len += (Z_STRLEN_P(param->parameter));
                    500:                                }
                    501:                        }
                    502:                        else {
                    503:                                /* error and cleanup */
                    504:                                efree(*outquery);
                    505:                                *outquery = NULL;
                    506:                                return (int) (s.cur - inquery);
                    507:                        }
                    508:                        bindno++;
                    509:                }
                    510:        }       
                    511:        *ptr = '\0';
                    512:        return 0;
                    513: }
                    514: #endif
                    515: 
                    516: /*
                    517:  * Local variables:
                    518:  * tab-width: 4
                    519:  * c-basic-offset: 4
                    520:  * End:
                    521:  * vim600: noet sw=4 ts=4 fdm=marker ft=c
                    522:  * vim<600: noet sw=4 ts=4
                    523:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>