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

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 {
                    231:                                        switch (Z_TYPE_P(param->parameter)) {
                    232:                                                case IS_NULL:
                    233:                                                        plc->quoted = "NULL";
                    234:                                                        plc->qlen = sizeof("NULL")-1;
                    235:                                                        plc->freeq = 0;
                    236:                                                        break;
                    237: 
                    238:                                                case IS_BOOL:
                    239:                                                        convert_to_long(param->parameter);
                    240: 
                    241:                                                case IS_LONG:
                    242:                                                case IS_DOUBLE:
                    243:                                                        convert_to_string(param->parameter);
                    244:                                                        plc->qlen = Z_STRLEN_P(param->parameter);
                    245:                                                        plc->quoted = Z_STRVAL_P(param->parameter);
                    246:                                                        plc->freeq = 0;
                    247:                                                        break;
                    248: 
                    249:                                                default:
                    250:                                                        convert_to_string(param->parameter);
                    251:                                                        if (!stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter),
                    252:                                                                        Z_STRLEN_P(param->parameter), &plc->quoted, &plc->qlen,
                    253:                                                                        param->param_type TSRMLS_CC)) {
                    254:                                                                /* bork */
                    255:                                                                ret = -1;
                    256:                                                                strncpy(stmt->error_code, stmt->dbh->error_code, 6);
                    257:                                                                goto clean_up;
                    258:                                                        }
                    259:                                                        plc->freeq = 1;
                    260:                                        }
                    261:                                }
                    262:                        } else {
                    263:                                plc->quoted = Z_STRVAL_P(param->parameter);
                    264:                                plc->qlen = Z_STRLEN_P(param->parameter);
                    265:                        }
                    266:                        newbuffer_len += plc->qlen;
                    267:                }
                    268: 
                    269: rewrite:
                    270:                /* allocate output buffer */
                    271:                newbuffer = emalloc(newbuffer_len + 1);
                    272:                *outquery = newbuffer;
                    273: 
                    274:                /* and build the query */
                    275:                plc = placeholders;
                    276:                ptr = inquery;
                    277: 
                    278:                do {
                    279:                        t = plc->pos - ptr;
                    280:                        if (t) {
                    281:                                memcpy(newbuffer, ptr, t);
                    282:                                newbuffer += t;
                    283:                        }
                    284:                        memcpy(newbuffer, plc->quoted, plc->qlen);
                    285:                        newbuffer += plc->qlen;
                    286:                        ptr = plc->pos + plc->len;
                    287: 
                    288:                        plc = plc->next;
                    289:                } while (plc);
                    290: 
                    291:                t = (inquery + inquery_len) - ptr;
                    292:                if (t) {
                    293:                        memcpy(newbuffer, ptr, t);
                    294:                        newbuffer += t;
                    295:                }
                    296:                *newbuffer = '\0';
                    297:                *outquery_len = newbuffer - *outquery;
                    298: 
                    299:                ret = 1;
                    300:                goto clean_up;
                    301: 
                    302:        } else if (query_type == PDO_PLACEHOLDER_POSITIONAL) {
                    303:                /* rewrite ? to :pdoX */
                    304:                char *name, *idxbuf;
                    305:                const char *tmpl = stmt->named_rewrite_template ? stmt->named_rewrite_template : ":pdo%d";
                    306:                int bind_no = 1;
                    307:                
                    308:                newbuffer_len = inquery_len;
                    309: 
                    310:                if (stmt->bound_param_map == NULL) {
                    311:                        ALLOC_HASHTABLE(stmt->bound_param_map);
                    312:                        zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
                    313:                }
                    314: 
                    315:                for (plc = placeholders; plc; plc = plc->next) {
                    316:                        int skip_map = 0;
                    317:                        char *p;
                    318:                        name = estrndup(plc->pos, plc->len);
                    319: 
                    320:                        /* check if bound parameter is already available */
                    321:                        if (!strcmp(name, "?") || zend_hash_find(stmt->bound_param_map, name, plc->len + 1, (void**) &p) == FAILURE) {
                    322:                                spprintf(&idxbuf, 0, tmpl, bind_no++);
                    323:                        } else {
                    324:                                idxbuf = estrdup(p);
                    325:                                skip_map = 1;
                    326:                        }
                    327: 
                    328:                        plc->quoted = idxbuf;
                    329:                        plc->qlen = strlen(plc->quoted);
                    330:                        plc->freeq = 1;
                    331:                        newbuffer_len += plc->qlen;
                    332: 
                    333:                        if (!skip_map && stmt->named_rewrite_template) {
                    334:                                /* create a mapping */
                    335:                                zend_hash_update(stmt->bound_param_map, name, plc->len + 1, idxbuf, plc->qlen + 1, NULL);
                    336:                        }
                    337: 
                    338:                        /* map number to name */
                    339:                        zend_hash_index_update(stmt->bound_param_map, plc->bindno, idxbuf, plc->qlen + 1, NULL);
                    340:                        
                    341:                        efree(name);
                    342:                }
                    343:                                
                    344:                goto rewrite;
                    345: 
                    346:        } else {
                    347:                /* rewrite :name to ? */
                    348:                
                    349:                newbuffer_len = inquery_len;
                    350:        
                    351:                if (stmt->bound_param_map == NULL) {
                    352:                        ALLOC_HASHTABLE(stmt->bound_param_map);
                    353:                        zend_hash_init(stmt->bound_param_map, 13, NULL, NULL, 0);
                    354:                }
                    355:                
                    356:                for (plc = placeholders; plc; plc = plc->next) {
                    357:                        char *name;
                    358:                        
                    359:                        name = estrndup(plc->pos, plc->len);
                    360:                        zend_hash_index_update(stmt->bound_param_map, plc->bindno, name, plc->len + 1, NULL);
                    361:                        efree(name);
                    362:                        plc->quoted = "?";
                    363:                        plc->qlen = 1;
                    364:                }
                    365: 
                    366:                goto rewrite;
                    367:        }
                    368: 
                    369: clean_up:
                    370: 
                    371:        while (placeholders) {
                    372:                plc = placeholders;
                    373:                placeholders = plc->next;
                    374: 
                    375:                if (plc->freeq) {
                    376:                        efree(plc->quoted);
                    377:                }
                    378: 
                    379:                efree(plc);
                    380:        }
                    381: 
                    382:        return ret;
                    383: }
                    384: 
                    385: #if 0
                    386: int old_pdo_parse_params(pdo_stmt_t *stmt, char *inquery, int inquery_len, char **outquery, 
                    387:                int *outquery_len TSRMLS_DC)
                    388: {
                    389:        Scanner s;
                    390:        char *ptr;
                    391:        int t;
                    392:        int bindno = 0;
                    393:        int newbuffer_len;
                    394:        int padding;
                    395:        HashTable *params = stmt->bound_params;
                    396:        struct pdo_bound_param_data *param;
                    397:        /* allocate buffer for query with expanded binds, ptr is our writing pointer */
                    398:        newbuffer_len = inquery_len;
                    399: 
                    400:        /* calculate the possible padding factor due to quoting */
                    401:        if(stmt->dbh->max_escaped_char_length) {
                    402:                padding = stmt->dbh->max_escaped_char_length;
                    403:        } else {
                    404:                padding = 3;
                    405:        }
                    406:        if(params) {
                    407:                zend_hash_internal_pointer_reset(params);
                    408:                while (SUCCESS == zend_hash_get_current_data(params, (void**)&param)) {
                    409:                        if(param->parameter) {
                    410:                                convert_to_string(param->parameter);
1.1.1.3   misho     411:                                /* accommodate a string that needs to be fully quoted
1.1       misho     412:                    bind placeholders are at least 2 characters, so
1.1.1.3   misho     413:                    the accommodate their own "'s
1.1       misho     414:                 */
                    415:                                newbuffer_len += padding * Z_STRLEN_P(param->parameter);
                    416:                        }
                    417:                        zend_hash_move_forward(params);
                    418:                }
                    419:        }
                    420:        *outquery = (char *) emalloc(newbuffer_len + 1);
                    421:        *outquery_len = 0;
                    422: 
                    423:        ptr = *outquery;
                    424:        s.cur = inquery;
                    425:        while((t = scan(&s)) != PDO_PARSER_EOI) {
                    426:                if(t == PDO_PARSER_TEXT) {
                    427:                        memcpy(ptr, s.tok, s.cur - s.tok);
                    428:                        ptr += (s.cur - s.tok);
                    429:                        *outquery_len += (s.cur - s.tok);
                    430:                }
                    431:                else if(t == PDO_PARSER_BIND) {
                    432:                        if(!params) { 
                    433:                                /* error */
                    434:                                efree(*outquery);
                    435:                                *outquery = NULL;
                    436:                                return (int) (s.cur - inquery);
                    437:                        }
                    438:                        /* lookup bind first via hash and then index */
                    439:                        /* stupid keys need to be null-terminated, even though we know their length */
                    440:                        if((SUCCESS == zend_hash_find(params, s.tok, s.cur-s.tok,(void **)&param))  
                    441:                            ||
                    442:                           (SUCCESS == zend_hash_index_find(params, bindno, (void **)&param))) 
                    443:                        {
                    444:                                char *quotedstr;
                    445:                                int quotedstrlen;
                    446:                                /* restore the in-string key, doesn't need null-termination here */
                    447:                                /* currently everything is a string here */
                    448:                                
                    449:                                /* quote the bind value if necessary */
                    450:                                if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter), 
                    451:                                        Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen TSRMLS_CC))
                    452:                                {
                    453:                                        memcpy(ptr, quotedstr, quotedstrlen);
                    454:                                        ptr += quotedstrlen;
                    455:                                        *outquery_len += quotedstrlen;
                    456:                                        efree(quotedstr);
                    457:                                } else {
                    458:                                        memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
                    459:                                        ptr += Z_STRLEN_P(param->parameter);
                    460:                                        *outquery_len += (Z_STRLEN_P(param->parameter));
                    461:                                }
                    462:                        }
                    463:                        else {
                    464:                                /* error and cleanup */
                    465:                                efree(*outquery);
                    466:                                *outquery = NULL;
                    467:                                return (int) (s.cur - inquery);
                    468:                        }
                    469:                        bindno++;
                    470:                }
                    471:                else if(t == PDO_PARSER_BIND_POS) {
                    472:                        if(!params) { 
                    473:                                /* error */
                    474:                                efree(*outquery);
                    475:                                *outquery = NULL;
                    476:                                return (int) (s.cur - inquery);
                    477:                        }
                    478:                        /* lookup bind by index */
                    479:                        if(SUCCESS == zend_hash_index_find(params, bindno, (void **)&param)) 
                    480:                        {
                    481:                                char *quotedstr;
                    482:                                int quotedstrlen;
                    483:                                /* currently everything is a string here */
                    484:                                
                    485:                                /* quote the bind value if necessary */
                    486:                                if(stmt->dbh->methods->quoter(stmt->dbh, Z_STRVAL_P(param->parameter), 
                    487:                                        Z_STRLEN_P(param->parameter), &quotedstr, &quotedstrlen TSRMLS_CC))
                    488:                                {
                    489:                                        memcpy(ptr, quotedstr, quotedstrlen);
                    490:                                        ptr += quotedstrlen;
                    491:                                        *outquery_len += quotedstrlen;
                    492:                                        efree(quotedstr);
                    493:                                } else {
                    494:                                        memcpy(ptr, Z_STRVAL_P(param->parameter), Z_STRLEN_P(param->parameter));
                    495:                                        ptr += Z_STRLEN_P(param->parameter);
                    496:                                        *outquery_len += (Z_STRLEN_P(param->parameter));
                    497:                                }
                    498:                        }
                    499:                        else {
                    500:                                /* error and cleanup */
                    501:                                efree(*outquery);
                    502:                                *outquery = NULL;
                    503:                                return (int) (s.cur - inquery);
                    504:                        }
                    505:                        bindno++;
                    506:                }
                    507:        }       
                    508:        *ptr = '\0';
                    509:        return 0;
                    510: }
                    511: #endif
                    512: 
                    513: /*
                    514:  * Local variables:
                    515:  * tab-width: 4
                    516:  * c-basic-offset: 4
                    517:  * End:
                    518:  * vim600: noet sw=4 ts=4 fdm=marker ft=c
                    519:  * vim<600: noet sw=4 ts=4
                    520:  */

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