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

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

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