PageRenderTime 59ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/hphp/runtime/ext_zend_compat/mongo/collection.cpp

https://gitlab.com/Blueprint-Marketing/hhvm
C++ | 1671 lines | 1173 code | 302 blank | 196 comment | 307 complexity | 4c4959076c6cac4661f212997c998b2d MD5 | raw file
  1. /**
  2. * Copyright 2009-2013 10gen, Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include <php.h>
  17. #include <zend_exceptions.h>
  18. #include "php_mongo.h"
  19. #include "collection.h"
  20. #include "cursor.h"
  21. #include "bson.h"
  22. #include "types/code.h"
  23. #include "types/db_ref.h"
  24. #include "db.h"
  25. #include "mcon/manager.h"
  26. #include "mcon/io.h"
  27. #include "log_stream.h"
  28. extern zend_class_entry *mongo_ce_MongoClient, *mongo_ce_DB, *mongo_ce_Cursor;
  29. extern zend_class_entry *mongo_ce_Code, *mongo_ce_Exception, *mongo_ce_ResultException;
  30. extern int le_pconnection, le_connection;
  31. extern zend_object_handlers mongo_default_handlers;
  32. ZEND_EXTERN_MODULE_GLOBALS(mongo)
  33. zend_class_entry *mongo_ce_Collection = NULL;
  34. static mongo_connection* get_server(mongo_collection *c, int connection_flags TSRMLS_DC);
  35. static int is_gle_op(zval *options, mongo_server_options *server_options TSRMLS_DC);
  36. static void do_safe_op(mongo_con_manager *manager, mongo_connection *connection, zval *cursor_z, buffer *buf, zval *return_value TSRMLS_DC);
  37. static zval* append_getlasterror(zval *coll, buffer *buf, zval *options, mongo_connection *connection TSRMLS_DC);
  38. static int php_mongo_trigger_error_on_command_failure(zval *document TSRMLS_DC);
  39. /* {{{ proto MongoCollection MongoCollection::__construct(MongoDB db, string name)
  40. Initializes a new MongoCollection */
  41. PHP_METHOD(MongoCollection, __construct)
  42. {
  43. zval *parent, *name, *zns, *w, *wtimeout;
  44. mongo_collection *c;
  45. mongo_db *db;
  46. char *ns, *name_str;
  47. int name_len;
  48. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os", &parent, mongo_ce_DB, &name_str, &name_len) == FAILURE) {
  49. zval *object = getThis();
  50. ZVAL_NULL(object);
  51. return;
  52. }
  53. /* check for empty and invalid collection names */
  54. if (
  55. name_len == 0 ||
  56. memchr(name_str, '\0', name_len) != 0
  57. ) {
  58. zend_throw_exception_ex(mongo_ce_Exception, 2 TSRMLS_CC, "MongoDB::__construct(): invalid name %s", name_str);
  59. return;
  60. }
  61. c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
  62. PHP_MONGO_GET_DB(parent);
  63. c->link = db->link;
  64. zval_add_ref(&db->link);
  65. c->parent = parent;
  66. zval_add_ref(&parent);
  67. MAKE_STD_ZVAL(name);
  68. ZVAL_STRINGL(name, name_str, name_len, 1);
  69. c->name = name;
  70. spprintf(&ns, 0, "%s.%s", Z_STRVAL_P(db->name), Z_STRVAL_P(name));
  71. MAKE_STD_ZVAL(zns);
  72. ZVAL_STRING(zns, ns, 0);
  73. c->ns = zns;
  74. mongo_read_preference_copy(&db->read_pref, &c->read_pref);
  75. w = zend_read_property(mongo_ce_DB, parent, "w", strlen("w"), NOISY TSRMLS_CC);
  76. if (Z_TYPE_P(w) == IS_STRING) {
  77. zend_update_property_string(mongo_ce_Collection, getThis(), "w", strlen("w"), Z_STRVAL_P(w) TSRMLS_CC);
  78. } else {
  79. convert_to_long(w);
  80. zend_update_property_long(mongo_ce_Collection, getThis(), "w", strlen("w"), Z_LVAL_P(w) TSRMLS_CC);
  81. }
  82. wtimeout = zend_read_property(mongo_ce_DB, parent, "wtimeout", strlen("wtimeout"), NOISY TSRMLS_CC);
  83. convert_to_long(wtimeout);
  84. zend_update_property_long(mongo_ce_Collection, getThis(), "wtimeout", strlen("wtimeout"), Z_LVAL_P(wtimeout) TSRMLS_CC);
  85. }
  86. /* }}} */
  87. /* {{{ proto string MongoCollection::__toString()
  88. Returns the full namespace for this collection (includes database name) */
  89. PHP_METHOD(MongoCollection, __toString)
  90. {
  91. mongo_collection *c;
  92. PHP_MONGO_GET_COLLECTION(getThis());
  93. RETURN_ZVAL(c->ns, 1, 0);
  94. }
  95. /* }}} */
  96. /* {{{ proto string MongoCollection::getName()
  97. Returns the collection name */
  98. PHP_METHOD(MongoCollection, getName)
  99. {
  100. mongo_collection *c;
  101. PHP_MONGO_GET_COLLECTION(getThis());
  102. RETURN_ZVAL(c->name, 1, 0);
  103. }
  104. /* }}} */
  105. /* {{{ proto bool MongoCollection::getSlaveOkay()
  106. Returns the slaveOkay flag for this collection */
  107. PHP_METHOD(MongoCollection, getSlaveOkay)
  108. {
  109. mongo_collection *c;
  110. PHP_MONGO_GET_COLLECTION(getThis());
  111. RETURN_BOOL(c->read_pref.type != MONGO_RP_PRIMARY);
  112. }
  113. /* }}} */
  114. /* {{{ proto bool MongoCollection::setSlaveOkay([bool slave_okay = true])
  115. Sets the slaveOkay flag for this collection and returns the previous value */
  116. PHP_METHOD(MongoCollection, setSlaveOkay)
  117. {
  118. zend_bool slave_okay = 1;
  119. mongo_collection *c;
  120. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &slave_okay) == FAILURE) {
  121. return;
  122. }
  123. PHP_MONGO_GET_COLLECTION(getThis());
  124. RETVAL_BOOL(c->read_pref.type != MONGO_RP_PRIMARY);
  125. c->read_pref.type = slave_okay ? MONGO_RP_SECONDARY_PREFERRED : MONGO_RP_PRIMARY;
  126. }
  127. /* }}} */
  128. /* {{{ proto array MongoCollection::getReadPreference()
  129. Returns an array describing the read preference for this collection. Tag sets will be included if available. */
  130. PHP_METHOD(MongoCollection, getReadPreference)
  131. {
  132. mongo_collection *c;
  133. PHP_MONGO_GET_COLLECTION(getThis());
  134. array_init(return_value);
  135. add_assoc_string(return_value, "type", mongo_read_preference_type_to_name(c->read_pref.type), 1);
  136. php_mongo_add_tagsets(return_value, &c->read_pref);
  137. }
  138. /* }}} */
  139. /* {{{ proto bool MongoCollection::setReadPreference(string read_preference [, array tags ])
  140. Sets the read preference for this collection */
  141. PHP_METHOD(MongoCollection, setReadPreference)
  142. {
  143. char *read_preference;
  144. int read_preference_len;
  145. mongo_collection *c;
  146. HashTable *tags = NULL;
  147. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|h", &read_preference, &read_preference_len, &tags) == FAILURE) {
  148. return;
  149. }
  150. PHP_MONGO_GET_COLLECTION(getThis());
  151. if (php_mongo_set_readpreference(&c->read_pref, read_preference, tags TSRMLS_CC)) {
  152. RETURN_TRUE;
  153. } else {
  154. RETURN_FALSE;
  155. }
  156. }
  157. /* }}} */
  158. /* {{{ proto array MongoCollection::drop()
  159. Drops the current collection and returns the database response */
  160. PHP_METHOD(MongoCollection, drop)
  161. {
  162. zval *data;
  163. mongo_collection *c;
  164. PHP_MONGO_GET_COLLECTION(getThis());
  165. MAKE_STD_ZVAL(data);
  166. array_init(data);
  167. add_assoc_zval(data, "drop", c->name);
  168. zval_add_ref(&c->name);
  169. MONGO_CMD(return_value, c->parent);
  170. zval_ptr_dtor(&data);
  171. }
  172. /* }}} */
  173. /* {{{ proto array MongoCollection::validate([bool scan_data])
  174. Validates the current collection, optionally include the data, and returns the database response */
  175. PHP_METHOD(MongoCollection, validate)
  176. {
  177. zval *data;
  178. zend_bool scan_data = 0;
  179. mongo_collection *c;
  180. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &scan_data) == FAILURE) {
  181. return;
  182. }
  183. PHP_MONGO_GET_COLLECTION(getThis());
  184. MAKE_STD_ZVAL(data);
  185. array_init(data);
  186. add_assoc_string(data, "validate", Z_STRVAL_P(c->name), 1);
  187. add_assoc_bool(data, "full", scan_data);
  188. MONGO_CMD(return_value, c->parent);
  189. zval_ptr_dtor(&data);
  190. }
  191. /* }}} */
  192. /* This should probably be split into two methods... right now appends the
  193. * getlasterror query to the buffer and alloc & inits the cursor zval. */
  194. static zval* append_getlasterror(zval *coll, buffer *buf, zval *options, mongo_connection *connection TSRMLS_DC)
  195. {
  196. zval *cmd_ns_z, *cmd, *cursor_z, *temp, *timeout_p;
  197. char *cmd_ns, *w_str = NULL;
  198. mongo_cursor *cursor;
  199. mongo_collection *c = (mongo_collection*)zend_object_store_get_object(coll TSRMLS_CC);
  200. mongo_db *db = (mongo_db*)zend_object_store_get_object(c->parent TSRMLS_CC);
  201. int response, w = 0, fsync = 0, journal = 0, timeout = -1;
  202. mongoclient *link = (mongoclient*) zend_object_store_get_object(c->link TSRMLS_CC);
  203. int max_document_size = connection->max_bson_size;
  204. int max_message_size = connection->max_message_size;
  205. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror");
  206. timeout_p = zend_read_static_property(mongo_ce_Cursor, "timeout", strlen("timeout"), NOISY TSRMLS_CC);
  207. convert_to_long(timeout_p);
  208. timeout = Z_LVAL_P(timeout_p);
  209. /* Overwrite the timeout if MongoCursor::$timeout is the default and we
  210. * passed in socketTimeoutMS in the connection string */
  211. if (timeout == PHP_MONGO_DEFAULT_SOCKET_TIMEOUT && link->servers->options.socketTimeoutMS > 0) {
  212. timeout = link->servers->options.socketTimeoutMS;
  213. }
  214. /* Get the default value for journalling */
  215. fsync = link->servers->options.default_fsync;
  216. journal = link->servers->options.default_journal;
  217. /* Read the default_* properties from the link */
  218. if (link->servers->options.default_w != -1) {
  219. w = link->servers->options.default_w;
  220. }
  221. if (link->servers->options.default_wstring != NULL) {
  222. w_str = link->servers->options.default_wstring;
  223. }
  224. /* This picks up the default "w" through the properties of MongoCollection
  225. * and MongoDb, but only if w is still 1 - as otherwise it was perhaps
  226. * overridden with the "w" (or "safe") option. */
  227. {
  228. zval *w_prop = zend_read_property(mongo_ce_Collection, coll, "w", strlen("w"), NOISY TSRMLS_CC);
  229. if (Z_TYPE_P(w_prop) == IS_STRING) {
  230. w_str = Z_STRVAL_P(w_prop);
  231. } else {
  232. convert_to_long(w_prop);
  233. if (Z_LVAL_P(w_prop) != 1) {
  234. w = Z_LVAL_P(w_prop);
  235. w_str = NULL;
  236. }
  237. }
  238. }
  239. /* Fetch all the options from the options array */
  240. if (options && !IS_SCALAR_P(options)) {
  241. zval **w_pp = NULL, **fsync_pp, **timeout_pp, **journal_pp;
  242. /* First we try "w", and if that is not found we check for "safe" */
  243. if (zend_hash_find(HASH_P(options), "w", strlen("w") + 1, (void**) &w_pp) == SUCCESS) {
  244. switch (Z_TYPE_PP(w_pp)) {
  245. case IS_STRING:
  246. w_str = Z_STRVAL_PP(w_pp);
  247. break;
  248. case IS_BOOL:
  249. case IS_LONG:
  250. w = Z_LVAL_PP(w_pp); /* This is actually "wrong" for bools, but it works */
  251. break;
  252. default:
  253. php_error_docref(NULL TSRMLS_CC, E_WARNING, "The value of the 'w' option either needs to be a integer or string");
  254. }
  255. } else if(zend_hash_find(HASH_P(options), "safe", strlen("safe") + 1, (void**) &w_pp) == SUCCESS) {
  256. switch (Z_TYPE_PP(w_pp)) {
  257. case IS_STRING:
  258. w_str = Z_STRVAL_PP(w_pp);
  259. break;
  260. case IS_LONG:
  261. w = Z_LVAL_PP(w_pp);
  262. break;
  263. case IS_BOOL:
  264. if (Z_BVAL_PP(w_pp)) {
  265. /* If we already provided Write Concern, do not overwrite it with w=1 */
  266. if (!(w > 1 || w_str)) {
  267. w = 1;
  268. }
  269. } else {
  270. w = 0;
  271. }
  272. break;
  273. default:
  274. php_error_docref(NULL TSRMLS_CC, E_WARNING, "The value of the 'safe' option either needs to be a integer or string");
  275. }
  276. }
  277. if (SUCCESS == zend_hash_find(HASH_P(options), "fsync", strlen("fsync") + 1, (void**) &fsync_pp)) {
  278. convert_to_boolean(*fsync_pp);
  279. fsync = Z_BVAL_PP(fsync_pp);
  280. }
  281. if (zend_hash_find(HASH_P(options), "j", strlen("j") + 1, (void**) &journal_pp) == SUCCESS) {
  282. convert_to_boolean(*journal_pp);
  283. journal = Z_BVAL_PP(journal_pp);
  284. }
  285. if (SUCCESS == zend_hash_find(HASH_P(options), "timeout", strlen("timeout") + 1, (void**) &timeout_pp)) {
  286. convert_to_long(*timeout_pp);
  287. timeout = Z_LVAL_PP(timeout_pp);
  288. }
  289. }
  290. /* fsync forces "w" to be at least 1, so don't touch it if it's
  291. * already set to something else above while parsing "w" (and
  292. * "safe") */
  293. if (fsync && w == 0) {
  294. w = 1;
  295. }
  296. /* get "db.$cmd" zval */
  297. MAKE_STD_ZVAL(cmd_ns_z);
  298. spprintf(&cmd_ns, 0, "%s.$cmd", Z_STRVAL_P(db->name));
  299. ZVAL_STRING(cmd_ns_z, cmd_ns, 0);
  300. /* get {"getlasterror" : 1} zval */
  301. MAKE_STD_ZVAL(cmd);
  302. array_init(cmd);
  303. add_assoc_long(cmd, "getlasterror", 1);
  304. /* if we have either a string, or w > 1, then we need to add "w" and
  305. * perhaps "wtimeout" to GLE */
  306. if (w_str || w > 1) {
  307. zval *wtimeout, **wtimeout_pp;
  308. if (w_str) {
  309. add_assoc_string(cmd, "w", w_str, 1);
  310. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added w='%s'", w_str);
  311. } else {
  312. add_assoc_long(cmd, "w", w);
  313. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added w=%d", w);
  314. }
  315. if (options && zend_hash_find(HASH_P(options), "wTimeoutMS", strlen("wTimeoutMS") + 1, (void **)&wtimeout_pp) == SUCCESS) {
  316. convert_to_long(*wtimeout_pp);
  317. add_assoc_long(cmd, "wtimeout", Z_LVAL_PP(wtimeout_pp));
  318. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added wtimeout=%d (wTimeoutMS from options array)", Z_LVAL_PP(wtimeout_pp));
  319. } else if (options && zend_hash_find(HASH_P(options), "wtimeout", strlen("wtimeout") + 1, (void **)&wtimeout_pp) == SUCCESS) {
  320. convert_to_long(*wtimeout_pp);
  321. add_assoc_long(cmd, "wtimeout", Z_LVAL_PP(wtimeout_pp));
  322. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added wtimeout=%d (wtimeout from options array)", Z_LVAL_PP(wtimeout_pp));
  323. } else {
  324. wtimeout = zend_read_property(mongo_ce_Collection, coll, "wtimeout", strlen("wtimeout"), NOISY TSRMLS_CC);
  325. convert_to_long(wtimeout);
  326. add_assoc_long(cmd, "wtimeout", Z_LVAL_P(wtimeout));
  327. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added wtimeout=%d (from collection property)", Z_LVAL_P(wtimeout));
  328. }
  329. }
  330. if (fsync) {
  331. add_assoc_bool(cmd, "fsync", 1);
  332. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added fsync=1");
  333. }
  334. if (journal) {
  335. add_assoc_bool(cmd, "journal", 1);
  336. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added journal=1");
  337. }
  338. /* get cursor */
  339. MAKE_STD_ZVAL(cursor_z);
  340. object_init_ex(cursor_z, mongo_ce_Cursor);
  341. MAKE_STD_ZVAL(temp);
  342. ZVAL_NULL(temp);
  343. MONGO_METHOD2(MongoCursor, __construct, temp, cursor_z, c->link, cmd_ns_z);
  344. zval_ptr_dtor(&temp);
  345. if (EG(exception)) {
  346. zval_ptr_dtor(&cursor_z);
  347. zval_ptr_dtor(&cmd_ns_z);
  348. zval_ptr_dtor(&cmd);
  349. return 0;
  350. }
  351. cursor = (mongo_cursor*)zend_object_store_get_object(cursor_z TSRMLS_CC);
  352. /* Make sure the "getLastError" also gets send to a primary. This should
  353. * be refactored alongside with the getLastError redirection in
  354. * db.c/MongoDB::command. The Cursor creation should be done through an
  355. * init method otherwise a connection have to be requested twice. */
  356. mongo_manager_log(link->manager, MLOG_CON, MLOG_INFO, "forcing primary for getlasterror");
  357. php_mongo_connection_force_primary(cursor);
  358. cursor->limit = -1;
  359. cursor->timeout = timeout;
  360. zval_ptr_dtor(&cursor->query);
  361. /* cmd is now part of cursor, so it shouldn't be dtored until cursor is */
  362. cursor->query = cmd;
  363. /* append the query */
  364. response = php_mongo_write_query(buf, cursor, max_document_size, max_message_size TSRMLS_CC);
  365. zval_ptr_dtor(&cmd_ns_z);
  366. #if MONGO_PHP_STREAMS
  367. mongo_log_stream_query(connection, cursor TSRMLS_CC);
  368. #endif
  369. if (FAILURE == response) {
  370. zval_ptr_dtor(&cursor_z);
  371. return 0;
  372. }
  373. return cursor_z;
  374. }
  375. /* Returns a connection for the operation.
  376. * Connection flags (connection_flags) are MONGO_CON_TYPE_READ and MONGO_CON_TYPE_WRITE. */
  377. static mongo_connection* get_server(mongo_collection *c, int connection_flags TSRMLS_DC)
  378. {
  379. mongoclient *link;
  380. mongo_connection *connection;
  381. char *error_message = NULL;
  382. link = (mongoclient*)zend_object_store_get_object((c->link) TSRMLS_CC);
  383. if (!link) {
  384. zend_throw_exception(mongo_ce_Exception, "The MongoCollection object has not been correctly initialized by its constructor", 17 TSRMLS_CC);
  385. return 0;
  386. }
  387. /* TODO: Fix better error message */
  388. if ((connection = mongo_get_read_write_connection(link->manager, link->servers, connection_flags, (char **) &error_message)) == NULL) {
  389. if (error_message) {
  390. mongo_cursor_throw(NULL, 16 TSRMLS_CC, "Couldn't get connection: %s", error_message);
  391. free(error_message);
  392. } else {
  393. mongo_cursor_throw(NULL, 16 TSRMLS_CC, "Couldn't get connection");
  394. }
  395. return 0;
  396. }
  397. return connection;
  398. }
  399. /* Wrapper for sending and wrapping in a safe op */
  400. static int send_message(zval *this_ptr, mongo_connection *connection, buffer *buf, zval *options, zval *return_value TSRMLS_DC)
  401. {
  402. int retval = 1;
  403. char *error_message = NULL;
  404. mongoclient *link;
  405. mongo_collection *c;
  406. c = (mongo_collection*)zend_object_store_get_object(this_ptr TSRMLS_CC);
  407. if (!c->ns) {
  408. zend_throw_exception(mongo_ce_Exception, "The MongoCollection object has not been correctly initialized by its constructor", 0 TSRMLS_CC);
  409. return 0;
  410. }
  411. link = (mongoclient*)zend_object_store_get_object((c->link) TSRMLS_CC);
  412. if (!link) {
  413. zend_throw_exception(mongo_ce_Exception, "The MongoCollection object has not been correctly initialized by its constructor", 17 TSRMLS_CC);
  414. return 0;
  415. }
  416. if (is_gle_op(options, &link->servers->options TSRMLS_CC)) {
  417. zval *cursor = append_getlasterror(getThis(), buf, options, connection TSRMLS_CC);
  418. if (cursor) {
  419. do_safe_op(link->manager, connection, cursor, buf, return_value TSRMLS_CC);
  420. retval = -1;
  421. } else {
  422. retval = 0;
  423. }
  424. } else if (link->manager->send(connection, &link->servers->options, buf->start, buf->pos - buf->start, (char **) &error_message) == -1) {
  425. /* TODO: Find out what to do with the error message here */
  426. free(error_message);
  427. retval = 0;
  428. } else {
  429. retval = 1;
  430. }
  431. return retval;
  432. }
  433. static int is_gle_op(zval *options, mongo_server_options *server_options TSRMLS_DC)
  434. {
  435. zval **gle_pp = 0, **fsync_pp = 0;
  436. int gle_op = 0;
  437. /* First we check for the global (connection string) default */
  438. if (server_options->default_w != -1) {
  439. gle_op = server_options->default_w;
  440. }
  441. if (server_options->default_fsync || server_options->default_journal) {
  442. gle_op = 1;
  443. }
  444. /* Then we check the options array that could overwrite the default */
  445. if (options && Z_TYPE_P(options) == IS_ARRAY) {
  446. zval **journal_pp;
  447. /* First we try "w", and if that is not found we check for "safe" */
  448. if (zend_hash_find(HASH_P(options), "w", strlen("w") + 1, (void**) &gle_pp) == FAILURE) {
  449. zend_hash_find(HASH_P(options), "safe", strlen("safe") + 1, (void**) &gle_pp);
  450. }
  451. /* After that, gle_pp is either still NULL, or set to something if one of
  452. * the options was found */
  453. if (gle_pp) {
  454. /* Check for bool/int value >= 1 */
  455. if ((Z_TYPE_PP(gle_pp) == IS_LONG || Z_TYPE_PP(gle_pp) == IS_BOOL)) {
  456. gle_op = (Z_LVAL_PP(gle_pp) >= 1);
  457. /* Check for string value ("majority", or a tag) */
  458. } else if (Z_TYPE_PP(gle_pp) == IS_STRING) {
  459. gle_op = 1;
  460. } else {
  461. php_error_docref(NULL TSRMLS_CC, E_WARNING, "The value of the 'safe' option either needs to be a boolean or a string");
  462. }
  463. }
  464. /* Check for "fsync" in options array */
  465. if (zend_hash_find(HASH_P(options), "fsync", strlen("fsync") + 1, (void**)&fsync_pp) == SUCCESS) {
  466. convert_to_boolean_ex(fsync_pp);
  467. if (Z_BVAL_PP(fsync_pp)) {
  468. gle_op = 1;
  469. }
  470. }
  471. /* Check for "j" in options array */
  472. if (zend_hash_find(HASH_P(options), "j", strlen("j") + 1, (void**)&journal_pp) == SUCCESS) {
  473. convert_to_boolean_ex(journal_pp);
  474. if (Z_BVAL_PP(journal_pp)) {
  475. gle_op = 1;
  476. }
  477. }
  478. }
  479. mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "is_gle_op: %s", gle_op ? "yes" : "no");
  480. return gle_op;
  481. }
  482. #if PHP_VERSION_ID >= 50300
  483. # define MONGO_ERROR_G EG
  484. #else
  485. # define MONGO_ERROR_G PG
  486. #endif
  487. /* This wrapper temporarily turns off the exception throwing bit if it has been
  488. * set (by calling mongo_cursor_throw() before). We can't call
  489. * mongo_cursor_throw after deregister as it frees up bits of memory that
  490. * mongo_cursor_throw uses to construct its error message.
  491. *
  492. * Without the disabling of the exception bit and when a user defined error
  493. * handler is used on the PHP side, the notice would never been shown because
  494. * the exception bubbles up before the notice can actually be shown. By turning
  495. * the error handling mode to EH_NORMAL temporarily, we circumvent this
  496. * problem. */
  497. static void connection_deregister_wrapper(mongo_con_manager *manager, mongo_connection *connection TSRMLS_DC)
  498. {
  499. zend_error_handling_t orig_error_handling;
  500. /* Save EG/PG(error_handling) so that we can show log messages when we have
  501. * already thrown an exception */
  502. orig_error_handling = MONGO_ERROR_G(error_handling);
  503. MONGO_ERROR_G(error_handling) = EH_NORMAL;
  504. mongo_manager_connection_deregister(manager, connection);
  505. MONGO_ERROR_G(error_handling) = orig_error_handling;
  506. }
  507. static void do_safe_op(mongo_con_manager *manager, mongo_connection *connection, zval *cursor_z, buffer *buf, zval *return_value TSRMLS_DC)
  508. {
  509. zval *errmsg, **err;
  510. mongo_cursor *cursor;
  511. char *error_message;
  512. cursor = (mongo_cursor*)zend_object_store_get_object(cursor_z TSRMLS_CC);
  513. cursor->connection = connection;
  514. if (-1 == manager->send(connection, NULL, buf->start, buf->pos - buf->start, (char **) &error_message)) {
  515. mongo_manager_log(manager, MLOG_IO, MLOG_WARN, "do_safe_op: sending data failed, removing connection %s", connection->hash);
  516. mongo_cursor_throw(connection, 16 TSRMLS_CC, "%s", error_message);
  517. connection_deregister_wrapper(manager, connection TSRMLS_CC);
  518. free(error_message);
  519. cursor->connection = NULL;
  520. zval_ptr_dtor(&cursor_z);
  521. return;
  522. }
  523. /* get reply */
  524. MAKE_STD_ZVAL(errmsg);
  525. ZVAL_NULL(errmsg);
  526. if (FAILURE == php_mongo_get_reply(cursor, errmsg TSRMLS_CC)) {
  527. /* php_mongo_get_reply() throws exceptions */
  528. mongo_manager_connection_deregister(manager, connection);
  529. cursor->connection = NULL;
  530. zval_ptr_dtor(&cursor_z);
  531. zval_ptr_dtor(&errmsg);
  532. return;
  533. }
  534. zval_ptr_dtor(&errmsg);
  535. cursor->started_iterating = 1;
  536. MONGO_METHOD(MongoCursor, getNext, return_value, cursor_z);
  537. if (EG(exception) || (Z_TYPE_P(return_value) == IS_BOOL && Z_BVAL_P(return_value) == 0)) {
  538. cursor->connection = NULL;
  539. zval_ptr_dtor(&cursor_z);
  540. return;
  541. } else if (zend_hash_find(Z_ARRVAL_P(return_value), "errmsg", strlen("errmsg") + 1, (void**)&err) == SUCCESS && Z_TYPE_PP(err) == IS_STRING) {
  542. zval **code;
  543. int status = zend_hash_find(Z_ARRVAL_P(return_value), "n", strlen("n") + 1, (void**)&code);
  544. mongo_cursor_throw(cursor->connection, (status == SUCCESS ? Z_LVAL_PP(code) : 0) TSRMLS_CC, "%s", Z_STRVAL_PP(err));
  545. cursor->connection = NULL;
  546. zval_ptr_dtor(&cursor_z);
  547. return;
  548. }
  549. cursor->connection = NULL;
  550. zval_ptr_dtor(&cursor_z);
  551. return;
  552. }
  553. /* {{{ proto bool|array MongoCollection::insert(array|object document [, array options])
  554. Insert a document into the collection and return the database response if
  555. the write concern is >= 1. Otherwise, boolean true is returned if the
  556. document is not empty. */
  557. PHP_METHOD(MongoCollection, insert)
  558. {
  559. zval *a, *options = 0;
  560. mongo_collection *c;
  561. buffer buf;
  562. mongo_connection *connection;
  563. int retval;
  564. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &a, &options) == FAILURE) {
  565. return;
  566. }
  567. MUST_BE_ARRAY_OR_OBJECT(1, a);
  568. PHP_MONGO_GET_COLLECTION(getThis());
  569. if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == 0) {
  570. RETURN_FALSE;
  571. }
  572. CREATE_BUF(buf, INITIAL_BUF_SIZE);
  573. if (FAILURE == php_mongo_write_insert(&buf, Z_STRVAL_P(c->ns), a, connection->max_bson_size, connection->max_message_size TSRMLS_CC)) {
  574. efree(buf.start);
  575. RETURN_FALSE;
  576. }
  577. #if MONGO_PHP_STREAMS
  578. mongo_log_stream_insert(connection, a, options TSRMLS_CC);
  579. #endif
  580. /* retval == -1 means a GLE response was received, so send_message() has
  581. * either set return_value or thrown an exception via do_safe_op(). */
  582. retval = send_message(this_ptr, connection, &buf, options, return_value TSRMLS_CC);
  583. if (retval != -1) {
  584. RETVAL_BOOL(retval);
  585. }
  586. efree(buf.start);
  587. }
  588. /* }}} */
  589. /* {{{ proto bool|array MongoCollection::batchInsert(array documents [, array options])
  590. Insert an array of documents and return the database response if the write
  591. concern is >= 1. Otherwise, a boolean value is returned indicating whether
  592. the batch was successfully sent. */
  593. PHP_METHOD(MongoCollection, batchInsert)
  594. {
  595. zval *docs, *options = NULL;
  596. mongo_collection *c;
  597. mongo_connection *connection;
  598. buffer buf;
  599. int bit_opts = 0;
  600. int retval;
  601. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z/", &docs, &options) == FAILURE) {
  602. return;
  603. }
  604. /* Options are only supported in the new-style, ie: an array of "named
  605. * parameters": array("continueOnError" => true); */
  606. if (options) {
  607. zval **continue_on_error = NULL;
  608. if (zend_hash_find(HASH_P(options), "continueOnError", strlen("continueOnError") + 1, (void**)&continue_on_error) == SUCCESS) {
  609. convert_to_boolean_ex(continue_on_error);
  610. bit_opts = Z_BVAL_PP(continue_on_error) << 0;
  611. }
  612. Z_ADDREF_P(options);
  613. } else {
  614. MAKE_STD_ZVAL(options);
  615. array_init(options);
  616. }
  617. PHP_MONGO_GET_COLLECTION(getThis());
  618. if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == 0) {
  619. zval_ptr_dtor(&options);
  620. RETURN_FALSE;
  621. }
  622. CREATE_BUF(buf, INITIAL_BUF_SIZE);
  623. if (php_mongo_write_batch_insert(&buf, Z_STRVAL_P(c->ns), bit_opts, docs, connection->max_bson_size, connection->max_message_size TSRMLS_CC) == FAILURE) {
  624. efree(buf.start);
  625. zval_ptr_dtor(&options);
  626. return;
  627. }
  628. #if MONGO_PHP_STREAMS
  629. mongo_log_stream_batchinsert(connection, docs, options, bit_opts TSRMLS_CC);
  630. #endif
  631. /* retval == -1 means a GLE response was received, so send_message() has
  632. * either set return_value or thrown an exception via do_safe_op(). */
  633. retval = send_message(this_ptr, connection, &buf, options, return_value TSRMLS_CC);
  634. if (retval != -1) {
  635. RETVAL_BOOL(retval);
  636. }
  637. efree(buf.start);
  638. zval_ptr_dtor(&options);
  639. }
  640. /* }}} */
  641. /* {{{ proto array MongoCollection::find([array|object criteria [, array|object return_fields]])
  642. Query this collection for documents matching $criteria and use $return_fields
  643. as the projection. Return a MongoCursor for the result set. */
  644. PHP_METHOD(MongoCollection, find)
  645. {
  646. zval *query = 0, *fields = 0;
  647. mongo_collection *c;
  648. zval temp;
  649. mongo_cursor *cursor;
  650. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &query, &fields) == FAILURE) {
  651. return;
  652. }
  653. MUST_BE_ARRAY_OR_OBJECT(1, query);
  654. MUST_BE_ARRAY_OR_OBJECT(2, fields);
  655. PHP_MONGO_GET_COLLECTION(getThis());
  656. object_init_ex(return_value, mongo_ce_Cursor);
  657. /* Add read preferences to cursor */
  658. cursor = (mongo_cursor*)zend_object_store_get_object(return_value TSRMLS_CC);
  659. mongo_read_preference_replace(&c->read_pref, &cursor->read_pref);
  660. /* TODO: Don't call an internal function like this, but add a new C-level
  661. * function for instantiating cursors */
  662. if (!query) {
  663. MONGO_METHOD2(MongoCursor, __construct, &temp, return_value, c->link, c->ns);
  664. } else if (!fields) {
  665. MONGO_METHOD3(MongoCursor, __construct, &temp, return_value, c->link, c->ns, query);
  666. } else {
  667. MONGO_METHOD4(MongoCursor, __construct, &temp, return_value, c->link, c->ns, query, fields);
  668. }
  669. }
  670. /* }}} */
  671. /* {{{ proto array MongoCollection::findOne([array|object criteria [, array|object return_fields]])
  672. Return the first document that matches $criteria and use $return_fields as
  673. the projection. NULL will be returned if no document matches. */
  674. PHP_METHOD(MongoCollection, findOne)
  675. {
  676. zval *query = 0, *fields = 0, *cursor;
  677. zval temp;
  678. zval *limit = &temp;
  679. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &query, &fields) == FAILURE) {
  680. return;
  681. }
  682. MUST_BE_ARRAY_OR_OBJECT(1, query);
  683. MUST_BE_ARRAY_OR_OBJECT(2, fields);
  684. MAKE_STD_ZVAL(cursor);
  685. MONGO_METHOD_BASE(MongoCollection, find)(ZEND_NUM_ARGS(), cursor, NULL, getThis(), 0 TSRMLS_CC);
  686. PHP_MONGO_CHECK_EXCEPTION1(&cursor);
  687. ZVAL_LONG(limit, -1);
  688. MONGO_METHOD1(MongoCursor, limit, cursor, cursor, limit);
  689. MONGO_METHOD(MongoCursor, getNext, return_value, cursor);
  690. zend_objects_store_del_ref(cursor TSRMLS_CC);
  691. zval_ptr_dtor(&cursor);
  692. }
  693. /* }}} */
  694. /* {{{ proto array MongoCollection::findAndModify(array query [, array update[, array fields [, array options]]])
  695. Atomically update and return a document */
  696. PHP_METHOD(MongoCollection, findAndModify)
  697. {
  698. zval *query, *update = 0, *fields = 0, *options = 0;
  699. zval *data, *tmpretval, **values;
  700. mongo_collection *c;
  701. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!|a!a!a!", &query, &update, &fields, &options) == FAILURE) {
  702. return;
  703. }
  704. c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
  705. MONGO_CHECK_INITIALIZED(c->ns, MongoCollection);
  706. MAKE_STD_ZVAL(data);
  707. array_init(data);
  708. add_assoc_zval(data, "findandmodify", c->name);
  709. zval_add_ref(&c->name);
  710. if (query && zend_hash_num_elements(Z_ARRVAL_P(query)) > 0) {
  711. add_assoc_zval(data, "query", query);
  712. zval_add_ref(&query);
  713. }
  714. if (update && zend_hash_num_elements(Z_ARRVAL_P(update)) > 0) {
  715. add_assoc_zval(data, "update", update);
  716. zval_add_ref(&update);
  717. }
  718. if (fields && zend_hash_num_elements(Z_ARRVAL_P(fields)) > 0) {
  719. add_assoc_zval(data, "fields", fields);
  720. zval_add_ref(&fields);
  721. }
  722. if (options && zend_hash_num_elements(Z_ARRVAL_P(options)) > 0) {
  723. zval temp;
  724. zend_hash_merge(HASH_P(data), HASH_P(options), (void (*)(void*))zval_add_ref, &temp, sizeof(zval*), 1);
  725. }
  726. MAKE_STD_ZVAL(tmpretval);
  727. ZVAL_NULL(tmpretval);
  728. MONGO_CMD(tmpretval, c->parent);
  729. if (php_mongo_trigger_error_on_command_failure(tmpretval TSRMLS_CC) == SUCCESS) {
  730. if (zend_hash_find(Z_ARRVAL_P(tmpretval), "value", strlen("value") + 1, (void **)&values) == SUCCESS) {
  731. array_init(return_value);
  732. /* We may wind up with a NULL here if there simply aren't any results */
  733. if (Z_TYPE_PP(values) == IS_ARRAY) {
  734. zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(values), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
  735. }
  736. }
  737. } else {
  738. RETVAL_FALSE;
  739. }
  740. zval_ptr_dtor(&data);
  741. zval_ptr_dtor(&tmpretval);
  742. }
  743. /* }}} */
  744. /* {{{ proto bool|array MongoCollection::update(array|object criteria, array|object $newobj [, array options])
  745. Update one or more documents matching $criteria with $newobj and return the
  746. database response if the write concern is >= 1. Otherwise, boolean true is
  747. returned. */
  748. PHP_METHOD(MongoCollection, update)
  749. {
  750. zval *criteria, *newobj, *options = NULL;
  751. mongo_collection *c;
  752. mongo_connection *connection;
  753. buffer buf;
  754. int bit_opts = 0;
  755. int retval;
  756. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a/", &criteria, &newobj, &options) == FAILURE) {
  757. return;
  758. }
  759. MUST_BE_ARRAY_OR_OBJECT(1, criteria);
  760. MUST_BE_ARRAY_OR_OBJECT(2, newobj);
  761. if (options) {
  762. zval **upsert = 0, **multiple = 0;
  763. if (zend_hash_find(HASH_P(options), "upsert", strlen("upsert") + 1, (void**)&upsert) == SUCCESS) {
  764. convert_to_boolean_ex(upsert);
  765. bit_opts |= Z_BVAL_PP(upsert) << 0;
  766. }
  767. if (zend_hash_find(HASH_P(options), "multiple", strlen("multiple") + 1, (void**)&multiple) == SUCCESS) {
  768. convert_to_boolean_ex(multiple);
  769. bit_opts |= Z_BVAL_PP(multiple) << 1;
  770. }
  771. Z_ADDREF_P(options);
  772. } else {
  773. MAKE_STD_ZVAL(options);
  774. array_init(options);
  775. }
  776. PHP_MONGO_GET_COLLECTION(getThis());
  777. if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == 0) {
  778. zval_ptr_dtor(&options);
  779. RETURN_FALSE;
  780. }
  781. CREATE_BUF(buf, INITIAL_BUF_SIZE);
  782. if (FAILURE == php_mongo_write_update(&buf, Z_STRVAL_P(c->ns), bit_opts, criteria, newobj, connection->max_bson_size, connection->max_message_size TSRMLS_CC)) {
  783. efree(buf.start);
  784. zval_ptr_dtor(&options);
  785. return;
  786. }
  787. #if MONGO_PHP_STREAMS
  788. mongo_log_stream_update(connection, c->ns, criteria, newobj, options, bit_opts TSRMLS_CC);
  789. #endif
  790. /* retval == -1 means a GLE response was received, so send_message() has
  791. * either set return_value or thrown an exception via do_safe_op(). */
  792. retval = send_message(this_ptr, connection, &buf, options, return_value TSRMLS_CC);
  793. if (retval != -1) {
  794. RETVAL_BOOL(retval);
  795. }
  796. efree(buf.start);
  797. zval_ptr_dtor(&options);
  798. }
  799. /* }}} */
  800. /* {{{ proto bool|array MongoCollection::remove([array|object criteria [array options]])
  801. Remove one or more documents matching $criteria */
  802. PHP_METHOD(MongoCollection, remove)
  803. {
  804. zval *criteria = 0, *options = 0;
  805. int bit_opts = 0;
  806. mongo_collection *c;
  807. mongo_connection *connection;
  808. buffer buf;
  809. int retval;
  810. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|za/", &criteria, &options) == FAILURE) {
  811. return;
  812. }
  813. MUST_BE_ARRAY_OR_OBJECT(1, criteria);
  814. if (!criteria) {
  815. MAKE_STD_ZVAL(criteria);
  816. array_init(criteria);
  817. } else {
  818. zval_add_ref(&criteria);
  819. }
  820. if (options) {
  821. zval **just_one = NULL;
  822. if (zend_hash_find(HASH_P(options), "justOne", strlen("justOne") + 1, (void**)&just_one) == SUCCESS) {
  823. convert_to_boolean_ex(just_one);
  824. bit_opts = Z_BVAL_PP(just_one) << 0;
  825. }
  826. Z_ADDREF_P(options);
  827. } else {
  828. MAKE_STD_ZVAL(options);
  829. array_init(options);
  830. }
  831. PHP_MONGO_GET_COLLECTION(getThis());
  832. if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == 0) {
  833. zval_ptr_dtor(&options);
  834. RETURN_FALSE;
  835. }
  836. CREATE_BUF(buf, INITIAL_BUF_SIZE);
  837. if (FAILURE == php_mongo_write_delete(&buf, Z_STRVAL_P(c->ns), bit_opts, criteria, connection->max_bson_size, connection->max_message_size TSRMLS_CC)) {
  838. efree(buf.start);
  839. zval_ptr_dtor(&criteria);
  840. zval_ptr_dtor(&options);
  841. return;
  842. }
  843. #if MONGO_PHP_STREAMS
  844. mongo_log_stream_delete(connection, c->ns, criteria, options, bit_opts TSRMLS_CC);
  845. #endif
  846. /* retval == -1 means a GLE response was received, so send_message() has
  847. * either set return_value or thrown an exception via do_safe_op(). */
  848. retval = send_message(this_ptr, connection, &buf, options, return_value TSRMLS_CC);
  849. if (retval != -1) {
  850. RETVAL_BOOL(retval);
  851. }
  852. efree(buf.start);
  853. zval_ptr_dtor(&criteria);
  854. zval_ptr_dtor(&options);
  855. }
  856. /* }}} */
  857. /* {{{ proto bool MongoCollection::ensureIndex(mixed keys [, array options])
  858. Create the $keys index if it does not already exist */
  859. PHP_METHOD(MongoCollection, ensureIndex)
  860. {
  861. zval *keys, *options = 0, *db, *system_indexes, *collection, *data, *key_str;
  862. mongo_collection *c;
  863. zend_bool done_name = 0;
  864. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &keys, &options) == FAILURE) {
  865. return;
  866. }
  867. if (IS_SCALAR_P(keys)) {
  868. zval *key_array;
  869. convert_to_string(keys);
  870. if (Z_STRLEN_P(keys) == 0) {
  871. return;
  872. }
  873. MAKE_STD_ZVAL(key_array);
  874. array_init(key_array);
  875. add_assoc_long(key_array, Z_STRVAL_P(keys), 1);
  876. keys = key_array;
  877. } else {
  878. zval_add_ref(&keys);
  879. }
  880. PHP_MONGO_GET_COLLECTION(getThis());
  881. /* get the system.indexes collection */
  882. db = c->parent;
  883. MAKE_STD_ZVAL(system_indexes);
  884. ZVAL_STRING(system_indexes, "system.indexes", 1);
  885. MAKE_STD_ZVAL(collection);
  886. MONGO_METHOD1(MongoDB, selectCollection, collection, db, system_indexes);
  887. PHP_MONGO_CHECK_EXCEPTION3(&keys, &system_indexes, &collection);
  888. /* set up data */
  889. MAKE_STD_ZVAL(data);
  890. array_init(data);
  891. /* ns */
  892. add_assoc_zval(data, "ns", c->ns);
  893. zval_add_ref(&c->ns);
  894. add_assoc_zval(data, "key", keys);
  895. zval_add_ref(&keys);
  896. if (options) {
  897. zval temp, **safe_pp, **fsync_pp, **timeout_pp, **name;
  898. zend_hash_merge(HASH_P(data), HASH_P(options), (void (*)(void*))zval_add_ref, &temp, sizeof(zval*), 1);
  899. if (zend_hash_find(HASH_P(options), "safe", strlen("safe") + 1, (void**)&safe_pp) == SUCCESS) {
  900. zend_hash_del(HASH_P(data), "safe", strlen("safe") + 1);
  901. }
  902. if (zend_hash_find(HASH_P(options), "fsync", strlen("fsync") + 1, (void**)&fsync_pp) == SUCCESS) {
  903. zend_hash_del(HASH_P(data), "fsync", strlen("fsync") + 1);
  904. }
  905. if (zend_hash_find(HASH_P(options), "timeout", strlen("timeout") + 1, (void**)&timeout_pp) == SUCCESS) {
  906. zend_hash_del(HASH_P(data), "timeout", strlen("timeout") + 1);
  907. }
  908. if (zend_hash_find(HASH_P(options), "name", strlen("name") + 1, (void**)&name) == SUCCESS) {
  909. if (Z_TYPE_PP(name) == IS_STRING && Z_STRLEN_PP(name) > MAX_INDEX_NAME_LEN) {
  910. zval_ptr_dtor(&data);
  911. zend_throw_exception_ex(mongo_ce_Exception, 14 TSRMLS_CC, "index name too long: %d, max %d characters", Z_STRLEN_PP(name), MAX_INDEX_NAME_LEN);
  912. return;
  913. }
  914. done_name = 1;
  915. }
  916. zval_add_ref(&options);
  917. } else {
  918. zval *opts;
  919. MAKE_STD_ZVAL(opts);
  920. array_init(opts);
  921. options = opts;
  922. }
  923. if (!done_name) {
  924. /* turn keys into a string */
  925. MAKE_STD_ZVAL(key_str);
  926. ZVAL_NULL(key_str);
  927. MONGO_METHOD1(MongoCollection, toIndexString, key_str, NULL, keys);
  928. if (Z_STRLEN_P(key_str) > MAX_INDEX_NAME_LEN) {
  929. zval_ptr_dtor(&data);
  930. zend_throw_exception_ex(mongo_ce_Exception, 14 TSRMLS_CC, "index name too long: %d, max %d characters", Z_STRLEN_P(key_str), MAX_INDEX_NAME_LEN);
  931. zval_ptr_dtor(&key_str);
  932. zval_ptr_dtor(&options);
  933. return;
  934. }
  935. add_assoc_zval(data, "name", key_str);
  936. zval_add_ref(&key_str);
  937. }
  938. MONGO_METHOD2(MongoCollection, insert, return_value, collection, data, options);
  939. zval_ptr_dtor(&options);
  940. zval_ptr_dtor(&data);
  941. zval_ptr_dtor(&system_indexes);
  942. zval_ptr_dtor(&collection);
  943. zval_ptr_dtor(&keys);
  944. if (!done_name) {
  945. zval_ptr_dtor(&key_str);
  946. }
  947. }
  948. /* }}} */
  949. /* {{{ proto array MongoCollection::deleteIndex(mixed keys)
  950. Remove the $keys index */
  951. PHP_METHOD(MongoCollection, deleteIndex)
  952. {
  953. zval *keys, *key_str, *data;
  954. mongo_collection *c;
  955. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &keys) == FAILURE) {
  956. return;
  957. }
  958. MAKE_STD_ZVAL(key_str);
  959. MONGO_METHOD1(MongoCollection, toIndexString, key_str, NULL, keys);
  960. PHP_MONGO_GET_COLLECTION(getThis());
  961. MAKE_STD_ZVAL(data);
  962. array_init(data);
  963. add_assoc_zval(data, "deleteIndexes", c->name);
  964. zval_add_ref(&c->name);
  965. add_assoc_zval(data, "index", key_str);
  966. MONGO_CMD(return_value, c->parent);
  967. zval_ptr_dtor(&data);
  968. }
  969. /* }}} */
  970. /* {{{ proto array MongoCollection::deleteIndex()
  971. Removes all indexes for this collection */
  972. PHP_METHOD(MongoCollection, deleteIndexes)
  973. {
  974. zval *data;
  975. mongo_collection *c;
  976. PHP_MONGO_GET_COLLECTION(getThis());
  977. MAKE_STD_ZVAL(data);
  978. array_init(data);
  979. add_assoc_string(data, "deleteIndexes", Z_STRVAL_P(c->name), 1);
  980. add_assoc_string(data, "index", "*", 1);
  981. MONGO_CMD(return_value, c->parent);
  982. zval_ptr_dtor(&data);
  983. }
  984. /* }}} */
  985. /* {{{ proto MongoCollection::getIndexInfo()
  986. Get all indexes for this collection */
  987. PHP_METHOD(MongoCollection, getIndexInfo)
  988. {
  989. zval *collection, *i_str, *query, *cursor, *next;
  990. mongo_collection *c;
  991. PHP_MONGO_GET_COLLECTION(getThis());
  992. MAKE_STD_ZVAL(collection);
  993. MAKE_STD_ZVAL(i_str);
  994. ZVAL_STRING(i_str, "system.indexes", 1);
  995. MONGO_METHOD1(MongoDB, selectCollection, collection, c->parent, i_str);
  996. zval_ptr_dtor(&i_str);
  997. PHP_MONGO_CHECK_EXCEPTION1(&collection);
  998. MAKE_STD_ZVAL(query);
  999. array_init(query);
  1000. add_assoc_string(query, "ns", Z_STRVAL_P(c->ns), 1);
  1001. MAKE_STD_ZVAL(cursor);
  1002. MONGO_METHOD1(MongoCollection, find, cursor, collection, query);
  1003. PHP_MONGO_CHECK_EXCEPTION3(&collection, &query, &cursor);
  1004. zval_ptr_dtor(&query);
  1005. zval_ptr_dtor(&collection);
  1006. array_init(return_value);
  1007. MAKE_STD_ZVAL(next);
  1008. MONGO_METHOD(MongoCursor, getNext, next, cursor);
  1009. PHP_MONGO_CHECK_EXCEPTION2(&cursor, &next);
  1010. while (Z_TYPE_P(next) != IS_NULL) {
  1011. add_next_index_zval(return_value, next);
  1012. MAKE_STD_ZVAL(next);
  1013. MONGO_METHOD(MongoCursor, getNext, next, cursor);
  1014. PHP_MONGO_CHECK_EXCEPTION2(&cursor, &next);
  1015. }
  1016. zval_ptr_dtor(&next);
  1017. zval_ptr_dtor(&cursor);
  1018. }
  1019. /* }}} */
  1020. /* {{{ proto MongoCollection::count([array criteria [, int limit [, int skip]]])
  1021. Count all documents matching $criteria with an optional limit and/or skip */
  1022. PHP_METHOD(MongoCollection, count)
  1023. {
  1024. zval *response, *data, *query=0;
  1025. long limit = 0, skip = 0;
  1026. zval **n;
  1027. mongo_collection *c;
  1028. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zll", &query, &limit, &skip) == FAILURE) {
  1029. return;
  1030. }
  1031. PHP_MONGO_GET_COLLECTION(getThis());
  1032. MAKE_STD_ZVAL(data);
  1033. array_init(data);
  1034. add_assoc_string(data, "count", Z_STRVAL_P(c->name), 1);
  1035. if (query) {
  1036. add_assoc_zval(data, "query", query);
  1037. zval_add_ref(&query);
  1038. }
  1039. if (limit) {
  1040. add_assoc_long(data, "limit", limit);
  1041. }
  1042. if (skip) {
  1043. add_assoc_long(data, "skip", skip);
  1044. }
  1045. MAKE_STD_ZVAL(response);
  1046. ZVAL_NULL(response);
  1047. MONGO_CMD_WITH_RP(response, c->parent, c);
  1048. zval_ptr_dtor(&data);
  1049. if (EG(exception) || Z_TYPE_P(response) != IS_ARRAY) {
  1050. zval_ptr_dtor(&response);
  1051. return;
  1052. }
  1053. if (zend_hash_find(HASH_P(response), "n", 2, (void**)&n) == SUCCESS) {
  1054. convert_to_long(*n);
  1055. RETVAL_ZVAL(*n, 1, 0);
  1056. zval_ptr_dtor(&response);
  1057. } else {
  1058. zval **errmsg;
  1059. /* The command failed, try to find an error message */
  1060. if (zend_hash_find(HASH_P(response), "errmsg", strlen("errmsg") + 1 , (void**)&errmsg) == SUCCESS) {
  1061. zend_throw_exception_ex(mongo_ce_Exception, 20 TSRMLS_CC, "Cannot run command count(): %s", Z_STRVAL_PP(errmsg));
  1062. } else {
  1063. zend_throw_exception(mongo_ce_Exception, "Cannot run command count()", 20 TSRMLS_CC);
  1064. }
  1065. zval_ptr_dtor(&response);
  1066. }
  1067. }
  1068. /* }}} */
  1069. /* {{{ proto mixed MongoCollection::save(array|object document [, array options])
  1070. Saves $document to this collection. An upsert will be used if the document's
  1071. _id is set; otherwise, it will be inserted. Return the database response if
  1072. the write concern is >= 1. Otherwise, boolean true is returned if the
  1073. document is not empty. */
  1074. PHP_METHOD(MongoCollection, save)
  1075. {
  1076. zval *a, *options = NULL;
  1077. zval **id;
  1078. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a/", &a, &options) == FAILURE) {
  1079. return;
  1080. }
  1081. MUST_BE_ARRAY_OR_OBJECT(1, a);
  1082. if (!options) {
  1083. MAKE_STD_ZVAL(options);
  1084. array_init(options);
  1085. } else {
  1086. Z_ADDREF_P(options);
  1087. }
  1088. if (zend_hash_find(HASH_P(a), "_id", 4, (void**)&id) == SUCCESS) {
  1089. zval *criteria;
  1090. MAKE_STD_ZVAL(criteria);
  1091. array_init(criteria);
  1092. add_assoc_zval(criteria, "_id", *id);
  1093. zval_add_ref(id);
  1094. add_assoc_bool(options, "upsert", 1);
  1095. Z_ADDREF_P(options);
  1096. MONGO_METHOD3(MongoCollection, update, return_value, getThis(), criteria, a, options);
  1097. zval_ptr_dtor(&criteria);
  1098. zval_ptr_dtor(&options);
  1099. return;
  1100. }
  1101. MONGO_METHOD2(MongoCollection, insert, return_value, getThis(), a, options);
  1102. zval_ptr_dtor(&options);
  1103. }
  1104. /* }}} */
  1105. /* {{{ proto array MongoCollection::createDBRef(array dbref)
  1106. Create a database reference object */
  1107. PHP_METHOD(MongoCollection, createDBRef)
  1108. {
  1109. zval *obj;
  1110. mongo_collection *c;
  1111. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &obj) == FAILURE) {
  1112. return;
  1113. }
  1114. PHP_MONGO_GET_COLLECTION(getThis());
  1115. MONGO_METHOD2(MongoDB, createDBRef, return_value, c->parent, c->name, obj);
  1116. }
  1117. /* }}} */
  1118. /* {{{ proto array MongoCollection::getDBRef(array dbref)
  1119. Retrieves the document referenced by $dbref */
  1120. PHP_METHOD(MongoCollection, getDBRef)
  1121. {
  1122. zval *ref;
  1123. mongo_collection *c;
  1124. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &ref) == FAILURE) {
  1125. return;
  1126. }
  1127. MUST_BE_ARRAY_OR_OBJECT(1, ref);
  1128. PHP_MONGO_GET_COLLECTION(getThis());
  1129. MONGO_METHOD2(MongoDBRef, get, return_value, NULL, c->parent, ref);
  1130. }
  1131. /* }}} */
  1132. static char *replace_dots(char *key, int key_len, char *position)
  1133. {
  1134. int i;
  1135. for (i = 0; i < key_len; i++) {
  1136. if (key[i] == '.') {
  1137. *(position)++ = '_';
  1138. } else {
  1139. *(position)++ = key[i];
  1140. }
  1141. }
  1142. return position;
  1143. }
  1144. /* {{{ proto protected static string MongoCollection::toIndexString(array|string keys)
  1145. Converts $keys to an identifying string for an index */
  1146. PHP_METHOD(MongoCollection, toIndexString)
  1147. {
  1148. zval *zkeys;
  1149. char *name, *position;
  1150. int len = 0;
  1151. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zkeys) == FAILURE) {
  1152. return;
  1153. }
  1154. if (Z_TYPE_P(zkeys) == IS_ARRAY || Z_TYPE_P(zkeys) == IS_OBJECT) {
  1155. HashTable *hindex = HASH_P(zkeys);
  1156. HashPosition pointer;
  1157. zval **data;
  1158. char *key;
  1159. uint key_len, first = 1, key_type;
  1160. ulong index;
  1161. for (
  1162. zend_hash_internal_pointer_reset_ex(hindex, &pointer);
  1163. zend_hash_get_current_data_ex(hindex, (void**)&data, &pointer) == SUCCESS;
  1164. zend_hash_move_forward_ex(hindex, &pointer)
  1165. ) {
  1166. key_type = zend_hash_get_current_key_ex(hindex, &key, &key_len, &index, NO_DUP, &pointer);
  1167. switch (key_type) {
  1168. case HASH_KEY_IS_STRING: {
  1169. len += key_len;
  1170. if (Z_TYPE_PP(data) == IS_STRING) {
  1171. len += Z_STRLEN_PP(data) + 1;
  1172. } else {
  1173. len += Z_LVAL_PP(data) == 1 ? 2 : 3;
  1174. }
  1175. break;
  1176. }
  1177. case HASH_KEY_IS_LONG:
  1178. convert_to_string(*data);
  1179. len += Z_STRLEN_PP(data);
  1180. len += 2;
  1181. break;
  1182. default:
  1183. continue;
  1184. }
  1185. }
  1186. name = (char*)emalloc(len + 1);
  1187. position = name;
  1188. for (
  1189. zend_hash_internal_pointer_reset_ex(hindex, &pointer);
  1190. zend_hash_get_current_data_ex(hindex, (void**)&data, &pointer) == SUCCESS;
  1191. zend_hash_move_forward_ex(hindex, &pointer)
  1192. ) {
  1193. if (!first) {
  1194. *(position)++ = '_';
  1195. }
  1196. first = 0;
  1197. key_type = zend_hash_get_current_key_ex(hindex, &key, &key_len, &index, NO_DUP, &pointer);
  1198. if (key_type == HASH_KEY_IS_LONG) {
  1199. key_len = spprintf(&key, 0, "%ld", index);
  1200. key_len += 1;
  1201. }
  1202. /* copy str, replacing '.' with '_' */
  1203. position = replace_dots(key, key_len-1, position);
  1204. *(position)++ = '_';
  1205. if (Z_TYPE_PP(data) == IS_STRING) {
  1206. memcpy(position, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
  1207. position += Z_STRLEN_PP(data);
  1208. } else {
  1209. if (Z_LVAL_PP(data) != 1) {
  1210. *(position)++ = '-';
  1211. }
  1212. *(position)++ = '1';
  1213. }
  1214. if (key_type == HASH_KEY_IS_LONG) {
  1215. efree(key);
  1216. }
  1217. }
  1218. *(position) = 0;
  1219. } else if (Z_TYPE_P(zkeys) == IS_STRING) {
  1220. int len;
  1221. convert_to_string(zkeys);
  1222. len = Z_STRLEN_P(zkeys);
  1223. name = (char*)emalloc(len + 3);
  1224. position = name;
  1225. /* copy str, replacing '.' with '_' */
  1226. position = replace_dots(Z_STRVAL_P(zkeys), Z_STRLEN_P(zkeys), position);
  1227. *(position)++ = '_';
  1228. *(position)++ = '1';
  1229. *(position) = '\0';
  1230. } else {
  1231. php_error_docref(NULL TSRMLS_CC, E_WARNING, "The key needs to be either a string or an array");
  1232. return;
  1233. }
  1234. RETURN_STRING(name, 0)
  1235. }
  1236. /* }}} */
  1237. /* {{{ proto array MongoCollection::aggregate(array pipeline, [, array op [, ...]])
  1238. Wrapper for aggregate command. The pipeline may be specified as a single
  1239. array of operations or a variable number of operation arguments. Returns the
  1240. database response for the command. Aggregation results will be stored in the
  1241. "result" key of the response. */
  1242. PHP_METHOD(MongoCollection, aggregate)
  1243. {
  1244. zval ***argv, *pipeline, *data, *tmp;
  1245. int argc, i;
  1246. mongo_collection *c;
  1247. zpp_var_args(argv, argc);
  1248. c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
  1249. MONGO_CHECK_INITIALIZED(c->ns, MongoCollection);
  1250. for (i = 0; i < argc; i++) {
  1251. tmp = *argv[i];
  1252. if (Z_TYPE_P(tmp) != IS_ARRAY) {
  1253. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument %d is not an array", i + 1);
  1254. efree(argv);
  1255. return;
  1256. }
  1257. }
  1258. MAKE_STD_ZVAL(data);
  1259. array_init(data);
  1260. add_assoc_zval(data, "aggregate", c->name);
  1261. zval_add_ref(&c->name);
  1262. /* If the single array argument contains a zeroth index, consider it an
  1263. * array of pipeline operators. Otherwise, assume it is a single pipeline
  1264. * operator and allow it to be wrapped in an array. */
  1265. if (argc == 1 && zend_hash_index_exists(Z_ARRVAL_PP(argv[0]), 0)) {
  1266. Z_ADDREF_PP(*argv);
  1267. add_assoc_zval(data, "pipeline", **argv);
  1268. } else {
  1269. MAKE_STD_ZVAL(pipeline);
  1270. array_init(pipeline);
  1271. for (i = 0; i < argc; i++) {
  1272. tmp = *argv[i];
  1273. Z_ADDREF_P(tmp);
  1274. if (zend_hash_next_index_insert(Z_ARRVAL_P(pipeline), &tmp, sizeof(zval*), NULL) == FAILURE) {
  1275. Z_DELREF_P(tmp);
  1276. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create pipeline array");
  1277. efree(argv);
  1278. RETURN_FALSE;
  1279. }
  1280. }
  1281. add_assoc_zval(data, "pipeline", pipeline);
  1282. }
  1283. efree(argv);
  1284. MONGO_CMD_WITH_RP(return_value, c->parent, c);
  1285. zval_ptr_dtor(&data);
  1286. }
  1287. /* }}} */
  1288. /* {{{ proto array MongoCollection::distinct(string key [, array query])
  1289. Wrapper for distinct command. Returns a list of distinct values for the given
  1290. key across a collection. An optional $query may be applied to filter the
  1291. documents considered. */
  1292. PHP_METHOD(MongoCollection, distinct)
  1293. {
  1294. char *key;
  1295. int key_len;
  1296. zval *data, **values, *tmp, *query = NULL;
  1297. mongo_collection *c;
  1298. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a!", &key, &key_len, &query) == FAILURE) {
  1299. return;
  1300. }
  1301. c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
  1302. MONGO_CHECK_INITIALIZED(c->ns, MongoCollection);
  1303. MAKE_STD_ZVAL(data);
  1304. array_init(data);
  1305. add_assoc_zval(data, "distinct", c->name);
  1306. zval_add_ref(&c->name);
  1307. add_assoc_stringl(data, "key", key, key_len, 1);
  1308. if (query) {
  1309. add_assoc_zval(data, "query", query);
  1310. zval_add_ref(&query);
  1311. }
  1312. MAKE_STD_ZVAL(tmp);
  1313. MONGO_CMD_WITH_RP(tmp, c->parent, c);
  1314. if (zend_hash_find(Z_ARRVAL_P(tmp), "values", strlen("values") + 1, (void **)&values) == SUCCESS) {
  1315. #ifdef array_init_size
  1316. array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_PP(values)));
  1317. #else
  1318. array_init(return_value);
  1319. #endif
  1320. zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(values), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
  1321. } else {
  1322. RETVAL_FALSE;
  1323. }
  1324. zval_ptr_dtor(&data);
  1325. zval_ptr_dtor(&tmp);
  1326. }
  1327. /* }}} */
  1328. /* {{{ proto array MongoCollection::group(mixed keys, array initial, MongoCode reduce [, array options])
  1329. Wrapper for group command. Returns the database response for the command.
  1330. Aggregation results will be stored in the "retval" key of the response. */
  1331. PHP_METHOD(MongoCollection, group)
  1332. {
  1333. zval *key, *initial, *options = 0, *group, *data, *reduce;
  1334. mongo_collection *c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
  1335. MONGO_CHECK_INITIALIZED(c->ns, MongoCollection);
  1336. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzz|z", &key, &initial, &reduce, &options) == FAILURE) {
  1337. return;
  1338. }
  1339. MUST_BE_ARRAY_OR_OBJECT(4, options);
  1340. if (Z_TYPE_P(reduce) == IS_STRING) {
  1341. zval *code;
  1342. MAKE_STD_ZVAL(code);
  1343. object_init_ex(code, mongo_ce_Code);
  1344. MONGO_METHOD1(MongoCode, __construct, return_value, code, reduce);
  1345. reduce = code;
  1346. } else {
  1347. zval_add_ref(&reduce);
  1348. }
  1349. MAKE_STD_ZVAL(group);
  1350. array_init(group);
  1351. add_assoc_zval(group, "ns", c->name);
  1352. zval_add_ref(&c->name);
  1353. add_assoc_zval(group, "$reduce", reduce);
  1354. zval_add_ref(&reduce);
  1355. if (Z_TYPE_P(key) == IS_OBJECT && Z_OBJCE_P(key) == mongo_ce_Code) {
  1356. add_assoc_zval(group, "$keyf", key);
  1357. } else if (!IS_SCALAR_P(key)) {
  1358. add_assoc_zval(group, "key", key);
  1359. } else {
  1360. zval_ptr_dtor(&group);
  1361. zval_ptr_dtor(&reduce);
  1362. zend_throw_exception(mongo_ce_Exception, "MongoCollection::group takes an array, object, or MongoCode key", 0 TSRMLS_CC);
  1363. return;
  1364. }
  1365. zval_add_ref(&key);
  1366. /* options used to just be "condition" but now can be "condition" or
  1367. * "finalize" */
  1368. if (options) {
  1369. zval **condi