|
27 | 27 | #include "php_pdo_sqlite_int.h"
|
28 | 28 | #include "zend_exceptions.h"
|
29 | 29 | #include "sqlite_driver_arginfo.h"
|
| 30 | +#include "ext/standard/php_string.h" |
| 31 | +#include "zend_smart_str.h" |
30 | 32 |
|
31 | 33 | int _pdo_sqlite_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *file, int line) /* {{{ */
|
32 | 34 | {
|
@@ -219,19 +221,80 @@ static zend_string *pdo_sqlite_last_insert_id(pdo_dbh_t *dbh, const zend_string
|
219 | 221 | return zend_i64_to_str(sqlite3_last_insert_rowid(H->db));
|
220 | 222 | }
|
221 | 223 |
|
222 |
| -/* NB: doesn't handle binary strings... use prepared stmts for that */ |
223 | 224 | static zend_string* sqlite_handle_quoter(pdo_dbh_t *dbh, const zend_string *unquoted, enum pdo_param_type paramtype)
|
224 | 225 | {
|
225 |
| - char *quoted; |
| 226 | + const char *ptr = ZSTR_VAL(unquoted); |
| 227 | + const char *const end = ZSTR_VAL(unquoted) + ZSTR_LEN(unquoted); |
| 228 | + smart_str output = {0}; |
| 229 | + bool is_in_quote = false; |
226 | 230 | if (ZSTR_LEN(unquoted) > (INT_MAX - 3) / 2) {
|
| 231 | + // php_error_docref(NULL, E_WARNING, "String is too long to quote"); |
227 | 232 | return NULL;
|
228 | 233 | }
|
229 |
| - quoted = safe_emalloc(2, ZSTR_LEN(unquoted), 3); |
230 |
| - /* TODO use %Q format? */ |
231 |
| - sqlite3_snprintf(2*ZSTR_LEN(unquoted) + 3, quoted, "'%q'", ZSTR_VAL(unquoted)); |
232 |
| - zend_string *quoted_str = zend_string_init(quoted, strlen(quoted), 0); |
233 |
| - efree(quoted); |
234 |
| - return quoted_str; |
| 234 | + if(ptr == end) { |
| 235 | + smart_str_appendl(&output, "''", 2); |
| 236 | + return smart_str_extract(&output); |
| 237 | + } |
| 238 | + const bool has_null_bytes = memchr(ptr, '\0', ZSTR_LEN(unquoted)) != NULL; |
| 239 | + if(has_null_bytes) { |
| 240 | + smart_str_appendc(&output, '('); |
| 241 | + } else { |
| 242 | + // todo: a faster implementation of the loop below is possible when we know there are no null bytes |
| 243 | + // (but lets just keep it simple while fixing the problem, and worry about micro-optimizations later) |
| 244 | + } |
| 245 | + while (ptr < end) { |
| 246 | + // \x00 and ' needs special handling |
| 247 | + const char special_characters[2] = "'\0"; |
| 248 | + const size_t bytes_until_special = php_strcspn(ptr, special_characters, end, special_characters + sizeof(special_characters)); |
| 249 | + if (bytes_until_special) { |
| 250 | + if(!is_in_quote) { |
| 251 | + if(ptr != ZSTR_VAL(unquoted)) { |
| 252 | + smart_str_appendl(&output, "||", 2); |
| 253 | + } |
| 254 | + smart_str_appendc(&output, '\''); |
| 255 | + is_in_quote = true; |
| 256 | + } |
| 257 | + smart_str_appendl(&output, ptr, bytes_until_special); |
| 258 | + ptr += bytes_until_special; |
| 259 | + ZEND_ASSERT(ptr <= end); |
| 260 | + if(ptr == end) { |
| 261 | + break; |
| 262 | + } |
| 263 | + } |
| 264 | + if(*ptr == '\'') { |
| 265 | + if(!is_in_quote) { |
| 266 | + if(ptr != ZSTR_VAL(unquoted)) { |
| 267 | + smart_str_appendl(&output, "||", 2); |
| 268 | + } |
| 269 | + smart_str_appendc(&output, '\''); |
| 270 | + is_in_quote = true; |
| 271 | + } |
| 272 | + const size_t number_of_consecutive_single_quotes = php_strspn(ptr, special_characters, end, special_characters + 1); |
| 273 | + smart_str_appendl(&output, ptr, number_of_consecutive_single_quotes); |
| 274 | + smart_str_appendl(&output, ptr, number_of_consecutive_single_quotes); |
| 275 | + ptr += number_of_consecutive_single_quotes; |
| 276 | + } else { |
| 277 | + ZEND_ASSERT(*ptr == '\0'); |
| 278 | + if(is_in_quote) { |
| 279 | + smart_str_appendl(&output, "'||", 3); |
| 280 | + is_in_quote = false; |
| 281 | + } |
| 282 | + const size_t number_of_consecutive_nulls = php_strspn(ptr, special_characters + 1, end, special_characters + 2); // ... |
| 283 | + smart_str_appendl(&output, "x'", 2); |
| 284 | + for(size_t i = 0; i < number_of_consecutive_nulls; ++i) { |
| 285 | + smart_str_appendl(&output, "00", 2); |
| 286 | + } |
| 287 | + smart_str_appendc(&output, '\''); |
| 288 | + ptr += number_of_consecutive_nulls; |
| 289 | + } |
| 290 | + } |
| 291 | + if(is_in_quote) { |
| 292 | + smart_str_appendc(&output, '\''); |
| 293 | + } |
| 294 | + if(has_null_bytes) { |
| 295 | + smart_str_appendc(&output, ')'); |
| 296 | + } |
| 297 | + return smart_str_extract(&output); |
235 | 298 | }
|
236 | 299 |
|
237 | 300 | static bool sqlite_handle_begin(pdo_dbh_t *dbh)
|
|
0 commit comments