Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

719 lines (557 sloc) 17.694 kb
// db.c
/**
* Copyright 2009-2010 10gen, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <php.h>
#include <zend_exceptions.h>
#include <ext/standard/md5.h>
#include "db.h"
#include "php_mongo.h"
#include "collection.h"
#include "cursor.h"
#include "gridfs.h"
#include "mongo_types.h"
extern zend_class_entry *mongo_ce_Mongo,
*mongo_ce_Collection,
*mongo_ce_Cursor,
*mongo_ce_GridFS,
*mongo_ce_Id,
*mongo_ce_Code,
*mongo_ce_Exception;
extern int le_pconnection,
le_connection;
extern zend_object_handlers mongo_default_handlers;
zend_class_entry *mongo_ce_DB = NULL;
/*
* arginfo needs to be set for __get because if PHP doesn't know it only takes
* one arg, it will issue a warning.
*/
#if ZEND_MODULE_API_NO < 20090115
static
#endif
ZEND_BEGIN_ARG_INFO_EX(arginfo___get, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
/* {{{ MongoDB::__construct
*/
PHP_METHOD(MongoDB, __construct) {
zval *zlink, **slave_okay = 0;
char *name;
int name_len;
mongo_db *db;
mongo_link *link;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os", &zlink, mongo_ce_Mongo, &name, &name_len) == FAILURE) {
return;
}
if (0 == name_len ||
0 != strchr(name, ' ') || 0 != strchr(name, '.') || 0 != strchr(name, '\\') ||
0 != strchr(name, '/') || 0 != strchr(name, '$')) {
#if ZEND_MODULE_API_NO >= 20060613
zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "MongoDB::__construct(): invalid name %s", name);
#else
zend_throw_exception_ex(zend_exception_get_default(), 0 TSRMLS_CC, "MongoDB::__construct(): invalid name %s", name);
#endif /* ZEND_MODULE_API_NO >= 20060613 */
return;
}
db = (mongo_db*)zend_object_store_get_object(getThis() TSRMLS_CC);
db->link = zlink;
zval_add_ref(&db->link);
PHP_MONGO_GET_LINK(zlink);
db->slave_okay = link->slave_okay;
MAKE_STD_ZVAL(db->name);
ZVAL_STRING(db->name, name, 1);
}
/* }}} */
PHP_METHOD(MongoDB, __toString) {
mongo_db *db = (mongo_db*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED_STRING(db->name, MongoDB);
RETURN_ZVAL(db->name, 1, 0);
}
PHP_METHOD(MongoDB, selectCollection) {
zval temp;
zval *collection;
mongo_db *db;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &collection) == FAILURE) {
return;
}
db = (mongo_db*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(db->name, MongoDB);
object_init_ex(return_value, mongo_ce_Collection);
MONGO_METHOD2(MongoCollection, __construct, &temp, return_value, getThis(), collection);
}
PHP_METHOD(MongoDB, getGridFS) {
zval temp;
zval *arg1 = 0, *arg2 = 0;
// arg2 is deprecated
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &arg1, &arg2) == FAILURE) {
return;
}
object_init_ex(return_value, mongo_ce_GridFS);
if (!arg1) {
MONGO_METHOD1(MongoGridFS, __construct, &temp, return_value, getThis());
}
else {
MONGO_METHOD2(MongoGridFS, __construct, &temp, return_value, getThis(), arg1);
}
}
PHP_METHOD(MongoDB, getSlaveOkay) {
mongo_db *db;
PHP_MONGO_GET_DB(getThis());
RETURN_BOOL(db->slave_okay);
}
PHP_METHOD(MongoDB, setSlaveOkay) {
zend_bool slave_okay;
mongo_db *db;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &slave_okay) == FAILURE) {
return;
}
PHP_MONGO_GET_DB(getThis());
RETVAL_BOOL(db->slave_okay);
db->slave_okay = slave_okay;
}
PHP_METHOD(MongoDB, getProfilingLevel) {
zval l;
Z_TYPE(l) = IS_LONG;
Z_LVAL(l) = -1;
MONGO_METHOD1(MongoDB, setProfilingLevel, return_value, getThis(), &l);
}
PHP_METHOD(MongoDB, setProfilingLevel) {
long level;
zval *data, *cmd_return;
zval **ok;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &level) == FAILURE) {
return;
}
MAKE_STD_ZVAL(data);
array_init(data);
add_assoc_long(data, "profile", level);
MAKE_STD_ZVAL(cmd_return);
MONGO_CMD(cmd_return, getThis());
zval_ptr_dtor(&data);
if (EG(exception)) {
zval_ptr_dtor(&cmd_return);
return;
}
if (zend_hash_find(HASH_P(cmd_return), "ok", 3, (void**)&ok) == SUCCESS &&
((Z_TYPE_PP(ok) == IS_BOOL && Z_BVAL_PP(ok)) || Z_DVAL_PP(ok) == 1)) {
zend_hash_find(HASH_P(cmd_return), "was", 4, (void**)&ok);
RETVAL_ZVAL(*ok, 1, 0);
}
else {
RETVAL_NULL();
}
zval_ptr_dtor(&cmd_return);
}
PHP_METHOD(MongoDB, drop) {
zval *data;
MAKE_STD_ZVAL(data);
array_init(data);
add_assoc_long(data, "dropDatabase", 1);
MONGO_CMD(return_value, getThis());
zval_ptr_dtor(&data);
}
PHP_METHOD(MongoDB, repair) {
zend_bool cloned=0, original=0;
zval *data;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|bb", &cloned, &original) == FAILURE) {
return;
}
MAKE_STD_ZVAL(data);
array_init(data);
add_assoc_long(data, "repairDatabase", 1);
add_assoc_bool(data, "preserveClonedFilesOnFailure", cloned);
add_assoc_bool(data, "backupOriginalFiles", original);
MONGO_CMD(return_value, getThis());
zval_ptr_dtor(&data);
}
PHP_METHOD(MongoDB, createCollection) {
zval *collection, *data, *temp;
zend_bool capped=0;
long size=0, max=0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|bll", &collection, &capped, &size, &max) == FAILURE) {
return;
}
MAKE_STD_ZVAL(data);
array_init(data);
convert_to_string(collection);
add_assoc_zval(data, "create", collection);
zval_add_ref(&collection);
if (size) {
add_assoc_long(data, "size", size);
}
if (capped) {
add_assoc_bool(data, "capped", 1);
if (max) {
add_assoc_long(data, "max", max);
}
}
MAKE_STD_ZVAL(temp);
MONGO_CMD(temp, getThis());
zval_ptr_dtor(&temp);
zval_ptr_dtor(&data);
if (!EG(exception)) {
// get the collection we just created
MONGO_METHOD1(MongoDB, selectCollection, return_value, getThis(), collection);
}
}
PHP_METHOD(MongoDB, dropCollection) {
zval *collection;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &collection) == FAILURE) {
return;
}
if (Z_TYPE_P(collection) != IS_OBJECT ||
Z_OBJCE_P(collection) != mongo_ce_Collection) {
zval *temp;
MAKE_STD_ZVAL(temp);
MONGO_METHOD1(MongoDB, selectCollection, temp, getThis(), collection);
collection = temp;
}
else {
zval_add_ref(&collection);
}
MONGO_METHOD(MongoCollection, drop, return_value, collection);
zval_ptr_dtor(&collection);
}
PHP_METHOD(MongoDB, listCollections) {
// select db.system.namespaces collection
zval *nss, *collection, *cursor, *list, *next;
MAKE_STD_ZVAL(nss);
ZVAL_STRING(nss, "system.namespaces", 1);
MAKE_STD_ZVAL(collection);
MONGO_METHOD1(MongoDB, selectCollection, collection, getThis(), nss);
// list to return
MAKE_STD_ZVAL(list);
array_init(list);
// do find
MAKE_STD_ZVAL(cursor);
MONGO_METHOD(MongoCollection, find, cursor, collection);
// populate list
MAKE_STD_ZVAL(next);
MONGO_METHOD(MongoCursor, getNext, next, cursor);
while (!IS_SCALAR_P(next)) {
zval *c, *zname;
zval **collection;
char *name, *first_dot, *system;
// check that the ns is valid and not an index (contains $)
if (zend_hash_find(HASH_P(next), "name", 5, (void**)&collection) == FAILURE ||
strchr(Z_STRVAL_PP(collection), '$')) {
zval_ptr_dtor(&next);
MAKE_STD_ZVAL(next);
MONGO_METHOD(MongoCursor, getNext, next, cursor);
continue;
}
first_dot = strchr(Z_STRVAL_PP(collection), '.');
system = strstr(Z_STRVAL_PP(collection), ".system.");
// check that this isn't a system ns
if ((system && first_dot == system) ||
(name = strchr(Z_STRVAL_PP(collection), '.')) == 0) {
zval_ptr_dtor(&next);
MAKE_STD_ZVAL(next);
MONGO_METHOD(MongoCursor, getNext, next, cursor);
continue;
}
// take a substring after the first "."
name++;
MAKE_STD_ZVAL(c);
MAKE_STD_ZVAL(zname);
// name must be copied because it is a substring of
// a string that will be garbage collected in a sec
ZVAL_STRING(zname, name, 1);
MONGO_METHOD1(MongoDB, selectCollection, c, getThis(), zname);
add_next_index_zval(list, c);
zval_ptr_dtor(&zname);
zval_ptr_dtor(&next);
MAKE_STD_ZVAL(next);
MONGO_METHOD(MongoCursor, getNext, next, cursor);
}
zval_ptr_dtor(&next);
zval_ptr_dtor(&nss);
zval_ptr_dtor(&cursor);
zval_ptr_dtor(&collection);
RETURN_ZVAL(list, 0, 1);
}
PHP_METHOD(MongoDB, createDBRef) {
zval *ns, *obj;
zval **id;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &ns, &obj) == FAILURE) {
return;
}
if (Z_TYPE_P(obj) == IS_ARRAY ||
Z_TYPE_P(obj) == IS_OBJECT) {
if (zend_hash_find(HASH_P(obj), "_id", 4, (void**)&id) == SUCCESS) {
MONGO_METHOD2(MongoDBRef, create, return_value, NULL, ns, *id);
return;
}
else if (Z_TYPE_P(obj) == IS_ARRAY) {
return;
}
}
MONGO_METHOD2(MongoDBRef, create, return_value, NULL, ns, obj);
}
PHP_METHOD(MongoDB, getDBRef) {
zval *ref;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &ref) == FAILURE) {
return;
}
MONGO_METHOD2(MongoDBRef, get, return_value, NULL, getThis(), ref);
}
PHP_METHOD(MongoDB, execute) {
zval *code = 0, *args = 0, *zdata;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &code, &args) == FAILURE) {
return;
}
if (!args) {
MAKE_STD_ZVAL(args);
array_init(args);
}
else {
zval_add_ref(&args);
}
// turn the first argument into MongoCode
if (Z_TYPE_P(code) != IS_OBJECT ||
Z_OBJCE_P(code) != mongo_ce_Code) {
zval *obj;
MAKE_STD_ZVAL(obj);
object_init_ex(obj, mongo_ce_Code);
MONGO_METHOD1(MongoCode, __construct, return_value, obj, code);
code = obj;
}
else {
zval_add_ref(&code);
}
// create { $eval : code, args : [] }
MAKE_STD_ZVAL(zdata);
array_init(zdata);
add_assoc_zval(zdata, "$eval", code);
add_assoc_zval(zdata, "args", args);
MONGO_METHOD1(MongoDB, command, return_value, getThis(), zdata);
zval_ptr_dtor(&zdata);
}
static char *get_cmd_ns(char *db, int db_len) {
char *position;
char *cmd_ns = (char*)emalloc(db_len + strlen("$cmd") + 2);
position = cmd_ns;
// db
memcpy(position, db, db_len);
position += db_len;
// .
*(position)++ = '.';
// $cmd
memcpy(position, "$cmd", strlen("$cmd"));
position += strlen("$cmd");
// \0
*(position) = '\0';
return cmd_ns;
}
PHP_METHOD(MongoDB, command) {
zval limit, *temp, *cmd, *cursor, *ns;
mongo_db *db;
mongo_link *link;
char *cmd_ns;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &cmd) == FAILURE) {
return;
}
if (IS_SCALAR_P(cmd)) {
zend_error(E_WARNING, "MongoDB::command() expects parameter 1 to be an array or object");
return;
}
PHP_MONGO_GET_DB(getThis());
// create db.$cmd
MAKE_STD_ZVAL(ns);
cmd_ns = get_cmd_ns(Z_STRVAL_P(db->name), Z_STRLEN_P(db->name));
ZVAL_STRING(ns, cmd_ns, 0);
// create cursor
MAKE_STD_ZVAL(cursor);
object_init_ex(cursor, mongo_ce_Cursor);
MAKE_STD_ZVAL(temp);
ZVAL_NULL(temp);
MONGO_METHOD3(MongoCursor, __construct, temp, cursor, db->link, ns, cmd);
zval_ptr_dtor(&ns);
zval_ptr_dtor(&temp);
MAKE_STD_ZVAL(temp);
ZVAL_NULL(temp);
// limit
Z_TYPE(limit) = IS_LONG;
Z_LVAL(limit) = -1;
MONGO_METHOD1(MongoCursor, limit, temp, cursor, &limit);
zval_ptr_dtor(&temp);
// make sure commands aren't be sent to slaves
PHP_MONGO_GET_LINK(db->link);
if (link->rs) {
zval slave_okay;
Z_TYPE(slave_okay) = IS_BOOL;
Z_LVAL(slave_okay) = 0;
MAKE_STD_ZVAL(temp);
ZVAL_NULL(temp);
MONGO_METHOD1(MongoCursor, slaveOkay, temp, cursor, &slave_okay);
zval_ptr_dtor(&temp);
}
// query
MONGO_METHOD(MongoCursor, getNext, return_value, cursor);
zend_objects_store_del_ref(cursor TSRMLS_CC);
zval_ptr_dtor(&cursor);
}
static void md5_hash(char *md5str, char *arg) {
PHP_MD5_CTX context;
unsigned char digest[16];
md5str[0] = '\0';
PHP_MD5Init(&context);
PHP_MD5Update(&context, arg, strlen(arg));
PHP_MD5Final(digest, &context);
make_digest(md5str, digest);
}
PHP_METHOD(MongoDB, authenticate) {
char *username, *password;
int ulen, plen;
zval *data, *result, **nonce;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &username, &ulen, &password, &plen) == FAILURE) {
return;
}
MAKE_STD_ZVAL(data);
array_init(data);
add_assoc_long(data, "getnonce", 1);
MAKE_STD_ZVAL(result);
MONGO_CMD(result, getThis());
zval_ptr_dtor(&data);
if (EG(exception)) {
zval_ptr_dtor(&result);
RETURN_FALSE;
}
if (zend_hash_find(HASH_P(result), "nonce", strlen("nonce")+1, (void**)&nonce) == SUCCESS) {
char *salt, *rash;
char hash[33], digest[33];
// create username:mongo:password hash
spprintf(&salt, 0, "%s:mongo:%s", username, password);
md5_hash(hash, salt);
efree(salt);
// create nonce|username|hash hash
spprintf(&rash, 0, "%s%s%s", Z_STRVAL_PP(nonce), username, hash);
md5_hash(digest, rash);
efree(rash);
// make actual authentication cmd
MAKE_STD_ZVAL(data);
array_init(data);
add_assoc_long(data, "authenticate", 1);
add_assoc_stringl(data, "user", username, ulen, 1);
add_assoc_zval(data, "nonce", *nonce);
zval_add_ref(nonce);
add_assoc_string(data, "key", digest, 1);
MONGO_CMD(return_value, getThis());
zval_ptr_dtor(&data);
}
else {
RETVAL_FALSE;
}
zval_ptr_dtor(&result);
}
static void run_err(char *cmd, zval *return_value, zval *db TSRMLS_DC) {
zval *data;
MAKE_STD_ZVAL(data);
array_init(data);
add_assoc_long(data, cmd, 1);
MONGO_CMD(return_value, db);
zval_ptr_dtor(&data);
}
/* {{{ MongoDB->lastError()
*/
PHP_METHOD(MongoDB, lastError) {
run_err("getlasterror", return_value, getThis() TSRMLS_CC);
}
/* }}} */
/* {{{ MongoDB->prevError()
*/
PHP_METHOD(MongoDB, prevError) {
run_err("getpreverror", return_value, getThis() TSRMLS_CC);
}
/* }}} */
/* {{{ MongoDB->resetError()
*/
PHP_METHOD(MongoDB, resetError) {
run_err("reseterror", return_value, getThis() TSRMLS_CC);
}
/* }}} */
/* {{{ MongoDB->forceError()
*/
PHP_METHOD(MongoDB, forceError) {
run_err("forceerror", return_value, getThis() TSRMLS_CC);
}
/* }}} */
/* {{{ MongoDB::__get
*/
PHP_METHOD(MongoDB, __get) {
zval *name;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
return;
}
// select this collection
MONGO_METHOD1(MongoDB, selectCollection, return_value, getThis(), name);
}
/* }}} */
static function_entry MongoDB_methods[] = {
PHP_ME(MongoDB, __construct, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, __toString, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, __get, arginfo___get, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, getGridFS, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, getSlaveOkay, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, setSlaveOkay, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, getProfilingLevel, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, setProfilingLevel, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, drop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, repair, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, selectCollection, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, createCollection, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, dropCollection, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, listCollections, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, createDBRef, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, getDBRef, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, execute, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, command, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, lastError, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, prevError, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, resetError, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, forceError, NULL, ZEND_ACC_PUBLIC)
PHP_ME(MongoDB, authenticate, NULL, ZEND_ACC_PUBLIC)
{ NULL, NULL, NULL }
};
static void php_mongo_db_free(void *object TSRMLS_DC) {
mongo_db *db = (mongo_db*)object;
if (db) {
if (db->link) {
zval_ptr_dtor(&db->link);
}
if (db->name) {
zval_ptr_dtor(&db->name);
}
zend_object_std_dtor(&db->std TSRMLS_CC);
efree(db);
}
}
/* {{{ mongo_mongo_db_new
*/
zend_object_value php_mongo_db_new(zend_class_entry *class_type TSRMLS_DC) {
php_mongo_obj_new(mongo_db);
}
/* }}} */
void mongo_init_MongoDB(TSRMLS_D) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "MongoDB", MongoDB_methods);
ce.create_object = php_mongo_db_new;
mongo_ce_DB = zend_register_internal_class(&ce TSRMLS_CC);
zend_declare_class_constant_long(mongo_ce_DB, "PROFILING_OFF", strlen("PROFILING_OFF"), 0 TSRMLS_CC);
zend_declare_class_constant_long(mongo_ce_DB, "PROFILING_SLOW", strlen("PROFILING_SLOW"), 1 TSRMLS_CC);
zend_declare_class_constant_long(mongo_ce_DB, "PROFILING_ON", strlen("PROFILING_ON"), 2 TSRMLS_CC);
zend_declare_property_long(mongo_ce_DB, "w", strlen("w"), 1, ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_long(mongo_ce_DB, "wtimeout", strlen("wtimeout"), PHP_MONGO_DEFAULT_TIMEOUT, ZEND_ACC_PUBLIC TSRMLS_CC);
}
Jump to Line
Something went wrong with that request. Please try again.