mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
448 lines
13 KiB
C
448 lines
13 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| https://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Author: Wez Furlong <wez@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "php.h"
|
|
#include "php_ini.h"
|
|
#include "ext/standard/info.h"
|
|
#include "SAPI.h"
|
|
#include "ext/pdo/php_pdo.h"
|
|
#include "ext/pdo/php_pdo_driver.h"
|
|
#include "php_pdo_sqlite.h"
|
|
#include "php_pdo_sqlite_int.h"
|
|
#include "zend_exceptions.h"
|
|
#include "pdo_sqlite_arginfo.h"
|
|
|
|
static zend_class_entry *pdosqlite_ce;
|
|
|
|
/* {{{ pdo_sqlite_deps */
|
|
static const zend_module_dep pdo_sqlite_deps[] = {
|
|
ZEND_MOD_REQUIRED("pdo")
|
|
ZEND_MOD_END
|
|
};
|
|
/* }}} */
|
|
|
|
/* {{{ pdo_sqlite_module_entry */
|
|
zend_module_entry pdo_sqlite_module_entry = {
|
|
STANDARD_MODULE_HEADER_EX, NULL,
|
|
pdo_sqlite_deps,
|
|
"pdo_sqlite",
|
|
NULL,
|
|
PHP_MINIT(pdo_sqlite),
|
|
PHP_MSHUTDOWN(pdo_sqlite),
|
|
NULL,
|
|
NULL,
|
|
PHP_MINFO(pdo_sqlite),
|
|
PHP_PDO_SQLITE_VERSION,
|
|
STANDARD_MODULE_PROPERTIES
|
|
};
|
|
/* }}} */
|
|
|
|
#if defined(COMPILE_DL_PDO_SQLITE) || defined(COMPILE_DL_PDO_SQLITE_EXTERNAL)
|
|
ZEND_GET_MODULE(pdo_sqlite)
|
|
#endif
|
|
|
|
/* proto bool PdoSqlite::createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0)
|
|
Creates a function that can be used in a query
|
|
*/
|
|
PHP_METHOD(Pdo_Sqlite, createFunction)
|
|
{
|
|
pdo_sqlite_create_function_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
|
|
}
|
|
|
|
#ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION
|
|
/* Attempts to load an SQLite extension library. */
|
|
PHP_METHOD(Pdo_Sqlite, loadExtension)
|
|
{
|
|
char *extension, *errtext = NULL;
|
|
char fullpath[MAXPATHLEN];
|
|
size_t extension_len;
|
|
|
|
pdo_dbh_t *dbh;
|
|
pdo_sqlite_db_handle *db_handle;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &extension, &extension_len) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (extension_len == 0) {
|
|
zend_argument_must_not_be_empty_error(1);
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
dbh = Z_PDO_DBH_P(ZEND_THIS);
|
|
PDO_CONSTRUCT_CHECK;
|
|
|
|
db_handle = (pdo_sqlite_db_handle *)dbh->driver_data;
|
|
|
|
#ifdef ZTS
|
|
if ((strncmp(sapi_module.name, "cgi", 3) != 0) &&
|
|
(strcmp(sapi_module.name, "cli") != 0) &&
|
|
(strncmp(sapi_module.name, "embed", 5) != 0)
|
|
) {
|
|
zend_throw_exception_ex(php_pdo_get_exception(), 0, "Not supported in multithreaded Web servers");
|
|
RETURN_THROWS();
|
|
}
|
|
#endif
|
|
|
|
if (!VCWD_REALPATH(extension, fullpath)) {
|
|
zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension \"%s\"", extension);
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
sqlite3 *sqlite_handle;
|
|
sqlite_handle = db_handle->db;
|
|
|
|
/* This only enables extension loading for the C api, not for SQL */
|
|
sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL);
|
|
|
|
if (sqlite3_load_extension(sqlite_handle, fullpath, 0, &errtext) != SQLITE_OK) {
|
|
zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension \"%s\"", errtext);
|
|
sqlite3_free(errtext);
|
|
sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL);
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
/* We disable extension loading for a vague feeling of safety. This is probably not necessary
|
|
as extensions can only be loaded through C code, not through SQL, and if someone can get
|
|
some C code to run on the server, they can do anything.*/
|
|
sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL);
|
|
}
|
|
#endif
|
|
|
|
typedef struct {
|
|
sqlite3_blob *blob;
|
|
size_t position;
|
|
size_t size;
|
|
int flags;
|
|
} php_stream_pdosqlite3_data;
|
|
|
|
static ssize_t php_pdosqlite3_stream_write(php_stream *stream, const char *buf, size_t count)
|
|
{
|
|
php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
|
|
|
|
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 -1;
|
|
}
|
|
|
|
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 -1;
|
|
}
|
|
|
|
if (sqlite3_blob_write(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) {
|
|
return -1;
|
|
}
|
|
|
|
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 ssize_t php_pdosqlite3_stream_read(php_stream *stream, char *buf, size_t count)
|
|
{
|
|
php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
|
|
|
|
if (sqlite3_stream->position + count >= sqlite3_stream->size) {
|
|
count = sqlite3_stream->size - sqlite3_stream->position;
|
|
stream->eof = 1;
|
|
}
|
|
if (count) {
|
|
if (sqlite3_blob_read(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) {
|
|
return -1;
|
|
}
|
|
sqlite3_stream->position += count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int php_pdosqlite3_stream_close(php_stream *stream, int close_handle)
|
|
{
|
|
php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
|
|
|
|
if (sqlite3_blob_close(sqlite3_stream->blob) != SQLITE_OK) {
|
|
/* Error occurred, but it still closed */
|
|
}
|
|
|
|
efree(sqlite3_stream);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int php_pdosqlite3_stream_flush(php_stream *stream)
|
|
{
|
|
/* do nothing */
|
|
return 0;
|
|
}
|
|
|
|
static int php_pdosqlite3_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs)
|
|
{
|
|
php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
|
|
|
|
switch(whence) {
|
|
case SEEK_CUR:
|
|
if (offset < 0) {
|
|
if (sqlite3_stream->position < (size_t)(-offset)) {
|
|
sqlite3_stream->position = 0;
|
|
*newoffs = -1;
|
|
return -1;
|
|
} else {
|
|
sqlite3_stream->position = sqlite3_stream->position + offset;
|
|
*newoffs = sqlite3_stream->position;
|
|
stream->eof = 0;
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (sqlite3_stream->position + (size_t)(offset) > sqlite3_stream->size) {
|
|
sqlite3_stream->position = sqlite3_stream->size;
|
|
*newoffs = -1;
|
|
return -1;
|
|
} else {
|
|
sqlite3_stream->position = sqlite3_stream->position + offset;
|
|
*newoffs = sqlite3_stream->position;
|
|
stream->eof = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
case SEEK_SET:
|
|
if (sqlite3_stream->size < (size_t)(offset)) {
|
|
sqlite3_stream->position = sqlite3_stream->size;
|
|
*newoffs = -1;
|
|
return -1;
|
|
} else {
|
|
sqlite3_stream->position = offset;
|
|
*newoffs = sqlite3_stream->position;
|
|
stream->eof = 0;
|
|
return 0;
|
|
}
|
|
case SEEK_END:
|
|
if (offset > 0) {
|
|
sqlite3_stream->position = sqlite3_stream->size;
|
|
*newoffs = -1;
|
|
return -1;
|
|
} else if (sqlite3_stream->size < (size_t)(-offset)) {
|
|
sqlite3_stream->position = 0;
|
|
*newoffs = -1;
|
|
return -1;
|
|
} else {
|
|
sqlite3_stream->position = sqlite3_stream->size + offset;
|
|
*newoffs = sqlite3_stream->position;
|
|
stream->eof = 0;
|
|
return 0;
|
|
}
|
|
default:
|
|
*newoffs = sqlite3_stream->position;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static int php_pdosqlite3_stream_cast(php_stream *stream, int castas, void **ret)
|
|
{
|
|
return FAILURE;
|
|
}
|
|
|
|
static int php_pdosqlite3_stream_stat(php_stream *stream, php_stream_statbuf *ssb)
|
|
{
|
|
php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract;
|
|
ssb->sb.st_size = sqlite3_stream->size;
|
|
return 0;
|
|
}
|
|
|
|
static const php_stream_ops php_stream_pdosqlite3_ops = {
|
|
php_pdosqlite3_stream_write,
|
|
php_pdosqlite3_stream_read,
|
|
php_pdosqlite3_stream_close,
|
|
php_pdosqlite3_stream_flush,
|
|
"PDOSQLite",
|
|
php_pdosqlite3_stream_seek,
|
|
php_pdosqlite3_stream_cast,
|
|
php_pdosqlite3_stream_stat,
|
|
NULL
|
|
};
|
|
|
|
/* Open a blob as a stream which we can read / write to. */
|
|
PHP_METHOD(Pdo_Sqlite, openBlob)
|
|
{
|
|
char *table, *column, *dbname = "main", *mode = "rb";
|
|
size_t table_len, column_len, dbname_len;
|
|
zend_long rowid, flags = SQLITE_OPEN_READONLY, sqlite_flags = 0;
|
|
sqlite3_blob *blob = NULL;
|
|
php_stream_pdosqlite3_data *sqlite3_stream;
|
|
php_stream *stream;
|
|
|
|
pdo_dbh_t *dbh;
|
|
pdo_sqlite_db_handle *db_handle;
|
|
|
|
dbh = Z_PDO_DBH_P(ZEND_THIS);
|
|
PDO_CONSTRUCT_CHECK;
|
|
db_handle = (pdo_sqlite_db_handle *)dbh->driver_data;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "ppl|pl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
sqlite3 *sqlite_handle;
|
|
sqlite_handle = db_handle->db;
|
|
sqlite_flags = (flags & SQLITE_OPEN_READWRITE) ? 1 : 0;
|
|
|
|
if (sqlite3_blob_open(sqlite_handle, dbname, table, column, rowid, sqlite_flags, &blob) != SQLITE_OK) {
|
|
zend_error(E_WARNING, "Unable to open blob: %s", sqlite3_errmsg(sqlite_handle));
|
|
RETURN_FALSE;
|
|
}
|
|
|
|
sqlite3_stream = emalloc(sizeof(php_stream_pdosqlite3_data));
|
|
sqlite3_stream->blob = blob;
|
|
sqlite3_stream->flags = flags;
|
|
sqlite3_stream->position = 0;
|
|
sqlite3_stream->size = sqlite3_blob_bytes(blob);
|
|
|
|
if (sqlite_flags != 0) {
|
|
mode = "r+b";
|
|
}
|
|
|
|
stream = php_stream_alloc(&php_stream_pdosqlite3_ops, sqlite3_stream, 0, mode);
|
|
|
|
if (stream) {
|
|
php_stream_to_zval(stream, return_value);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
PHP_METHOD(Pdo_Sqlite, setAuthorizer)
|
|
{
|
|
zend_fcall_info fci;
|
|
zend_fcall_info_cache fcc;
|
|
|
|
ZEND_PARSE_PARAMETERS_START(1, 1)
|
|
Z_PARAM_FUNC_NO_TRAMPOLINE_FREE_OR_NULL(fci, fcc)
|
|
ZEND_PARSE_PARAMETERS_END();
|
|
|
|
pdo_dbh_t *dbh = Z_PDO_DBH_P(ZEND_THIS);
|
|
PDO_CONSTRUCT_CHECK_WITH_CLEANUP(free_fcc);
|
|
pdo_sqlite_db_handle *db_handle = (pdo_sqlite_db_handle *) dbh->driver_data;
|
|
|
|
/* Clear previously set callback */
|
|
if (ZEND_FCC_INITIALIZED(db_handle->authorizer_fcc)) {
|
|
zend_fcc_dtor(&db_handle->authorizer_fcc);
|
|
}
|
|
|
|
/* Only enable userland authorizer if argument is not NULL */
|
|
if (ZEND_FCI_INITIALIZED(fci)) {
|
|
zend_fcc_dup(&db_handle->authorizer_fcc, &fcc);
|
|
}
|
|
|
|
return;
|
|
|
|
free_fcc:
|
|
zend_release_fcall_info_cache(&fcc);
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
static int php_sqlite_collation_callback(void *context, int string1_len, const void *string1,
|
|
int string2_len, const void *string2)
|
|
{
|
|
int ret = 0;
|
|
zval zargs[2];
|
|
zval retval;
|
|
struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context;
|
|
|
|
// Prepare the arguments.
|
|
ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len);
|
|
ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len);
|
|
|
|
zend_call_known_fcc(&collation->callback, &retval, /* argc */ 2, zargs, /* named_params */ NULL);
|
|
|
|
zval_ptr_dtor(&zargs[0]);
|
|
zval_ptr_dtor(&zargs[1]);
|
|
|
|
if (!Z_ISUNDEF(retval)) {
|
|
if (Z_TYPE(retval) != IS_LONG) {
|
|
zend_string *func_name = get_active_function_or_method_name();
|
|
zend_type_error("%s(): Return value of the collation callback must be of type int, %s returned",
|
|
ZSTR_VAL(func_name), zend_zval_value_name(&retval));
|
|
zend_string_release(func_name);
|
|
zval_ptr_dtor(&retval);
|
|
return FAILURE;
|
|
}
|
|
ret = ZEND_NORMALIZE_BOOL(Z_LVAL(retval));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
PHP_METHOD(Pdo_Sqlite, createAggregate)
|
|
{
|
|
pdo_sqlite_create_aggregate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU);
|
|
}
|
|
|
|
PHP_METHOD(Pdo_Sqlite, createCollation)
|
|
{
|
|
pdo_sqlite_create_collation_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU, php_sqlite_collation_callback);
|
|
}
|
|
|
|
/* {{{ PHP_MINIT_FUNCTION */
|
|
PHP_MINIT_FUNCTION(pdo_sqlite)
|
|
{
|
|
#ifdef SQLITE_DETERMINISTIC
|
|
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_DETERMINISTIC", (zend_long)SQLITE_DETERMINISTIC);
|
|
#endif
|
|
|
|
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_OPEN_FLAGS", (zend_long)PDO_SQLITE_ATTR_OPEN_FLAGS);
|
|
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READONLY", (zend_long)SQLITE_OPEN_READONLY);
|
|
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_READWRITE", (zend_long)SQLITE_OPEN_READWRITE);
|
|
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_OPEN_CREATE", (zend_long)SQLITE_OPEN_CREATE);
|
|
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT);
|
|
REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES);
|
|
|
|
pdosqlite_ce = register_class_Pdo_Sqlite(pdo_dbh_ce);
|
|
pdosqlite_ce->create_object = pdo_dbh_new;
|
|
|
|
if (php_pdo_register_driver(&pdo_sqlite_driver) == FAILURE) {
|
|
return FAILURE;
|
|
}
|
|
|
|
return php_pdo_register_driver_specific_ce(&pdo_sqlite_driver, pdosqlite_ce);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MSHUTDOWN_FUNCTION */
|
|
PHP_MSHUTDOWN_FUNCTION(pdo_sqlite)
|
|
{
|
|
php_pdo_unregister_driver(&pdo_sqlite_driver);
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MINFO_FUNCTION */
|
|
PHP_MINFO_FUNCTION(pdo_sqlite)
|
|
{
|
|
php_info_print_table_start();
|
|
php_info_print_table_row(2, "PDO Driver for SQLite 3.x", "enabled");
|
|
php_info_print_table_row(2, "SQLite Library", sqlite3_libversion());
|
|
php_info_print_table_end();
|
|
}
|
|
/* }}} */
|