diff --git a/UPGRADING b/UPGRADING index a77af84df3e..5fb5bf00356 100644 --- a/UPGRADING +++ b/UPGRADING @@ -111,6 +111,9 @@ PHP 7.2 UPGRADE NOTES subpatterns and empty matches by reporting NULL and "" (empty string), respectively. +- SQLite3: + . Implemented writing to BLOBs. + - Standard: . Simplified password hashing API updated to support Argon2i hashes when PHP is compiled with libargon2 (https://wiki.php.net/rfc/argon2_password_hash). diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 67daa79f88f..b56c2388d91 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -1057,13 +1057,36 @@ typedef struct { sqlite3_blob *blob; size_t position; size_t size; + int flags; } php_stream_sqlite3_data; static size_t php_sqlite3_stream_write(php_stream *stream, const char *buf, size_t count) { -/* php_stream_sqlite3_data *sqlite3_stream = (php_stream_sqlite3_data *) stream->abstract; */ + php_stream_sqlite3_data *sqlite3_stream = (php_stream_sqlite3_data *) stream->abstract; - return 0; + if (sqlite3_stream->flags & SQLITE_OPEN_READONLY) { + php_error_docref(NULL, E_WARNING, "Can't write to blob stream: is open as read only"); + return 0; + } + + if (sqlite3_stream->position + count > sqlite3_stream->size) { + php_error_docref(NULL, E_WARNING, "It is not possible to increase the size of a BLOB"); + return 0; + } + + if (sqlite3_blob_write(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) { + return 0; + } + + if (sqlite3_stream->position + count >= sqlite3_stream->size) { + stream->eof = 1; + sqlite3_stream->position = sqlite3_stream->size; + } + else { + sqlite3_stream->position += count; + } + + return count; } static size_t php_sqlite3_stream_read(php_stream *stream, char *buf, size_t count) @@ -1190,15 +1213,15 @@ static php_stream_ops php_stream_sqlite3_ops = { NULL }; -/* {{{ proto resource SQLite3::openBlob(string table, string column, int rowid [, string dbname]) +/* {{{ proto resource SQLite3::openBlob(string table, string column, int rowid [, string dbname [, int flags]]) Open a blob as a stream which we can read / write to. */ PHP_METHOD(sqlite3, openBlob) { php_sqlite3_db_object *db_obj; zval *object = getThis(); - char *table, *column, *dbname = "main"; + char *table, *column, *dbname = "main", *mode = "rb"; size_t table_len, column_len, dbname_len; - zend_long rowid, flags = 0; + zend_long rowid, flags = SQLITE_OPEN_READONLY, sqlite_flags = 0; sqlite3_blob *blob = NULL; php_stream_sqlite3_data *sqlite3_stream; php_stream *stream; @@ -1207,21 +1230,28 @@ PHP_METHOD(sqlite3, openBlob) SQLITE3_CHECK_INITIALIZED(db_obj, db_obj->initialised, SQLite3) - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl|s", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl|sl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) { return; } - if (sqlite3_blob_open(db_obj->db, dbname, table, column, rowid, flags, &blob) != SQLITE_OK) { + sqlite_flags = (flags & SQLITE_OPEN_READWRITE) ? 1 : 0; + + if (sqlite3_blob_open(db_obj->db, dbname, table, column, rowid, sqlite_flags, &blob) != SQLITE_OK) { php_sqlite3_error(db_obj, "Unable to open blob: %s", sqlite3_errmsg(db_obj->db)); RETURN_FALSE; } sqlite3_stream = emalloc(sizeof(php_stream_sqlite3_data)); sqlite3_stream->blob = blob; + sqlite3_stream->flags = flags; sqlite3_stream->position = 0; sqlite3_stream->size = sqlite3_blob_bytes(blob); - stream = php_stream_alloc(&php_stream_sqlite3_ops, sqlite3_stream, 0, "rb"); + if (sqlite_flags != 0) { + mode = "r+b"; + } + + stream = php_stream_alloc(&php_stream_sqlite3_ops, sqlite3_stream, 0, mode); if (stream) { php_stream_to_zval(stream, return_value); @@ -1921,6 +1951,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_openblob, 0, 0, 3) ZEND_ARG_INFO(0, column) ZEND_ARG_INFO(0, rowid) ZEND_ARG_INFO(0, dbname) + ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_sqlite3_enableexceptions, 0, 0, 0) diff --git a/ext/sqlite3/tests/sqlite3_30_blobopen.phpt b/ext/sqlite3/tests/sqlite3_30_blobopen.phpt index 1daa4b1ffb4..2e9a9f2be2c 100644 --- a/ext/sqlite3/tests/sqlite3_30_blobopen.phpt +++ b/ext/sqlite3/tests/sqlite3_30_blobopen.phpt @@ -25,6 +25,20 @@ $stream = $db->openBlob('test', 'data', 1); var_dump($stream); echo "Stream Contents\n"; var_dump(stream_get_contents($stream)); +echo "Writing to read-only stream\n"; +var_dump(fwrite($stream, 'ABCD')); +echo "Closing Stream\n"; +var_dump(fclose($stream)); +echo "Opening stream in write mode\n"; +$stream = $db->openBlob('test', 'data', 1, 'main', SQLITE3_OPEN_READWRITE); +var_dump($stream); +echo "Writing to blob\n"; +var_dump(fwrite($stream, 'ABCD')); +echo "Stream Contents\n"; +fseek($stream, 0); +var_dump(stream_get_contents($stream)); +echo "Expanding blob size\n"; +var_dump(fwrite($stream, 'ABCD ABCD ABCD')); echo "Closing Stream\n"; var_dump(fclose($stream)); echo "Closing database\n"; @@ -43,6 +57,22 @@ bool(true) resource(%d) of type (stream) Stream Contents string(9) "TEST TEST" +Writing to read-only stream + +Warning: fwrite(): Can't write to blob stream: is open as read only in %s on line %d +int(0) +Closing Stream +bool(true) +Opening stream in write mode +resource(%d) of type (stream) +Writing to blob +int(4) +Stream Contents +string(9) "ABCD TEST" +Expanding blob size + +Warning: fwrite(): It is not possible to increase the size of a BLOB in %s on line %d +int(0) Closing Stream bool(true) Closing database