加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
collection.c 96.77 KB
一键复制 编辑 原始数据 按行查看 历史
Derick Rethans 提交于 2014-10-08 13:51 . Merge branch 'v1.5'
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026
/**
* Copyright 2009-2014 MongoDB, 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/php_smart_str.h>
#include "php_mongo.h"
#include "collection.h"
#include "command_cursor.h"
#include "cursor.h"
#include "cursor_shared.h"
#include "bson.h"
#include "db.h"
#include "types/code.h"
#include "types/db_ref.h"
#include "db.h"
#include "mcon/manager.h"
#include "mcon/utils.h"
#include "log_stream.h"
#include "api/write.h"
#include "api/wire_version.h"
#include "contrib/php-json.h"
#undef fsync
extern zend_class_entry *mongo_ce_MongoClient, *mongo_ce_DB, *mongo_ce_Cursor;
extern zend_class_entry *mongo_ce_CommandCursor;
extern zend_class_entry *mongo_ce_Code, *mongo_ce_Exception, *mongo_ce_ResultException;
extern zend_class_entry *mongo_ce_CursorException;
extern zend_object_handlers mongo_default_handlers;
ZEND_EXTERN_MODULE_GLOBALS(mongo)
zend_class_entry *mongo_ce_Collection = NULL;
static int is_gle_op(zval *coll, zval *options, mongo_server_options *server_options TSRMLS_DC);
static void do_gle_op(mongo_con_manager *manager, mongo_connection *connection, zval *cursor_z, mongo_buffer *buf, zval *return_value TSRMLS_DC);
static zval* append_getlasterror(zval *coll, mongo_buffer *buf, zval *options, mongo_connection *connection TSRMLS_DC);
static char *to_index_string(zval *zkeys, int *key_len TSRMLS_DC);
/**
* Unsets an option if it exists.
*
* @param zval *options
* @param const char *name
*/
#define DELETE_OPTION_IF_EXISTS(options, name) do { \
zval **tmp_option; \
if (zend_hash_find(HASH_P(options), name, strlen(name) + 1, (void**)&tmp_option) == SUCCESS) { \
zend_hash_del(HASH_P(options), name, strlen(name) + 1); \
} \
} while(0);
static int is_valid_collectionname(char *colname, int colname_len TSRMLS_DC)
{
if (
colname_len == 0 ||
/* strchr(colname, '$') != 0 || — we can not exclude this as we need it to run commands */
memchr(colname, '\0', colname_len) != 0
) {
zend_throw_exception_ex(mongo_ce_Exception, 2 TSRMLS_CC, "invalid collection name '%s'", colname);
return 0;
}
return 1;
}
void php_mongo_collection_construct(zval *this, zval *parent, char *name_str, int name_len TSRMLS_DC)
{
zval *name, *zns, *w, *wtimeout;
mongo_collection *c;
mongo_db *db;
char *ns;
/* check for empty and invalid collection names */
if (!is_valid_collectionname(name_str, name_len TSRMLS_CC)) {
return;
}
c = (mongo_collection*)zend_object_store_get_object(this TSRMLS_CC);
db = (mongo_db*)zend_object_store_get_object(parent TSRMLS_CC);
if (!(db->name)) {
zend_throw_exception(mongo_ce_Exception, "The MongoDB object has not been correctly initialized by its constructor", 0 TSRMLS_CC);
return;
}
c->link = db->link;
zval_add_ref(&db->link);
c->parent = parent;
zval_add_ref(&parent);
MAKE_STD_ZVAL(name);
ZVAL_STRINGL(name, name_str, name_len, 1);
c->name = name;
spprintf(&ns, 0, "%s.%s", Z_STRVAL_P(db->name), Z_STRVAL_P(name));
MAKE_STD_ZVAL(zns);
ZVAL_STRING(zns, ns, 0);
c->ns = zns;
mongo_read_preference_copy(&db->read_pref, &c->read_pref);
w = zend_read_property(mongo_ce_DB, parent, "w", strlen("w"), NOISY TSRMLS_CC);
if (Z_TYPE_P(w) == IS_STRING) {
zend_update_property_string(mongo_ce_Collection, this, "w", strlen("w"), Z_STRVAL_P(w) TSRMLS_CC);
} else {
convert_to_long(w);
zend_update_property_long(mongo_ce_Collection, this, "w", strlen("w"), Z_LVAL_P(w) TSRMLS_CC);
}
wtimeout = zend_read_property(mongo_ce_DB, parent, "wtimeout", strlen("wtimeout"), NOISY TSRMLS_CC);
convert_to_long(wtimeout);
zend_update_property_long(mongo_ce_Collection, this, "wtimeout", strlen("wtimeout"), Z_LVAL_P(wtimeout) TSRMLS_CC);
}
/* {{{ proto MongoCollection MongoCollection::__construct(MongoDB db, string name)
Initializes a new MongoCollection */
PHP_METHOD(MongoCollection, __construct)
{
zval *db;
char *name_str;
int name_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os", &db, mongo_ce_DB, &name_str, &name_len) == FAILURE) {
zval *object = getThis();
ZVAL_NULL(object);
return;
}
php_mongo_collection_construct(getThis(), db, name_str, name_len TSRMLS_CC);
}
/* }}} */
/* {{{ proto string MongoCollection::__toString()
Returns the full namespace for this collection (includes database name) */
PHP_METHOD(MongoCollection, __toString)
{
mongo_collection *c;
PHP_MONGO_GET_COLLECTION(getThis());
RETURN_ZVAL(c->ns, 1, 0);
}
/* }}} */
/* {{{ proto string MongoCollection::getName()
Returns the collection name */
PHP_METHOD(MongoCollection, getName)
{
mongo_collection *c;
PHP_MONGO_GET_COLLECTION(getThis());
RETURN_ZVAL(c->name, 1, 0);
}
/* }}} */
/* {{{ proto bool MongoCollection::getSlaveOkay()
Returns the slaveOkay flag for this collection */
PHP_METHOD(MongoCollection, getSlaveOkay)
{
mongo_collection *c;
PHP_MONGO_GET_COLLECTION(getThis());
RETURN_BOOL(c->read_pref.type != MONGO_RP_PRIMARY);
}
/* }}} */
/* {{{ proto bool MongoCollection::setSlaveOkay([bool slave_okay = true])
Sets the slaveOkay flag for this collection and returns the previous value */
PHP_METHOD(MongoCollection, setSlaveOkay)
{
zend_bool slave_okay = 1;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &slave_okay) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
RETVAL_BOOL(c->read_pref.type != MONGO_RP_PRIMARY);
c->read_pref.type = slave_okay ? MONGO_RP_SECONDARY_PREFERRED : MONGO_RP_PRIMARY;
}
/* }}} */
/* {{{ proto array MongoCollection::getReadPreference()
Returns an array describing the read preference for this collection. Tag sets will be included if available. */
PHP_METHOD(MongoCollection, getReadPreference)
{
mongo_collection *c;
PHP_MONGO_GET_COLLECTION(getThis());
array_init(return_value);
add_assoc_string(return_value, "type", mongo_read_preference_type_to_name(c->read_pref.type), 1);
php_mongo_add_tagsets(return_value, &c->read_pref);
}
/* }}} */
/* {{{ proto bool MongoCollection::setReadPreference(string read_preference [, array tags ])
Sets the read preference for this collection */
PHP_METHOD(MongoCollection, setReadPreference)
{
char *read_preference;
int read_preference_len;
mongo_collection *c;
HashTable *tags = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|h", &read_preference, &read_preference_len, &tags) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
if (php_mongo_set_readpreference(&c->read_pref, read_preference, tags TSRMLS_CC)) {
RETURN_TRUE;
} else {
RETURN_FALSE;
}
}
/* }}} */
/* {{{ array MongoCollection::getWriteConcern()
* Get the MongoCollection write concern. */
PHP_METHOD(MongoCollection, getWriteConcern)
{
zval *write_concern, *wtimeout;
if (zend_parse_parameters_none()) {
return;
}
write_concern = zend_read_property(mongo_ce_DB, getThis(), "w", strlen("w"), 0 TSRMLS_CC);
wtimeout = zend_read_property(mongo_ce_DB, getThis(), "wtimeout", strlen("wtimeout"), 0 TSRMLS_CC);
Z_ADDREF_P(write_concern);
Z_ADDREF_P(wtimeout);
array_init(return_value);
add_assoc_zval(return_value, "w", write_concern);
add_assoc_zval(return_value, "wtimeout", wtimeout);
}
/* }}} */
/* {{{ bool MongoCollection::setWriteConcern(mixed w [, int wtimeout])
* Set the MongoCollection write concern. */
PHP_METHOD(MongoCollection, setWriteConcern)
{
zval *write_concern;
long wtimeout;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &write_concern, &wtimeout) == FAILURE) {
return;
}
if (Z_TYPE_P(write_concern) == IS_LONG) {
zend_update_property_long(mongo_ce_Collection, getThis(), "w", strlen("w"), Z_LVAL_P(write_concern) TSRMLS_CC);
} else if (Z_TYPE_P(write_concern) == IS_STRING) {
zend_update_property_stringl(mongo_ce_Collection, getThis(), "w", strlen("w"), Z_STRVAL_P(write_concern), Z_STRLEN_P(write_concern) TSRMLS_CC);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "expects parameter 1 to be an string or integer, %s given", zend_get_type_by_const(Z_TYPE_P(write_concern)));
RETURN_FALSE;
}
if (ZEND_NUM_ARGS() > 1) {
zend_update_property_long(mongo_ce_Collection, getThis(), "wtimeout", strlen("wtimeout"), wtimeout TSRMLS_CC);
}
RETURN_TRUE;
}
/* }}} */
/* {{{ proto array MongoCollection::drop()
Drops the current collection and returns the database response */
PHP_METHOD(MongoCollection, drop)
{
zval *cmd, *retval;
mongo_collection *c;
mongo_db *db;
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "drop", c->name);
zval_add_ref(&c->name);
retval = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, NULL TSRMLS_CC);
zval_ptr_dtor(&cmd);
if (retval) {
RETURN_ZVAL(retval, 0, 1);
}
}
/* }}} */
/* {{{ proto array MongoCollection::validate([bool scan_data])
Validates the current collection, optionally include the data, and returns the database response */
PHP_METHOD(MongoCollection, validate)
{
zval *cmd, *retval;
zend_bool scan_data = 0;
mongo_collection *c;
mongo_db *db;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &scan_data) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_string(cmd, "validate", Z_STRVAL_P(c->name), 1);
add_assoc_bool(cmd, "full", scan_data);
retval = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, NULL TSRMLS_CC);
zval_ptr_dtor(&cmd);
if (retval) {
RETURN_ZVAL(retval, 0, 1);
}
}
/* }}} */
/* This should probably be split into two methods... right now appends the
* getlasterror query to the buffer and alloc & inits the cursor zval. */
static zval* append_getlasterror(zval *coll, mongo_buffer *buf, zval *options, mongo_connection *connection TSRMLS_DC)
{
zval *cmd_ns_z, *cmd, *cursor_z, *temp, *timeout_p;
char *cmd_ns, *w_str = NULL;
mongo_cursor *cursor;
mongo_collection *c = (mongo_collection*)zend_object_store_get_object(coll TSRMLS_CC);
mongo_db *db = (mongo_db*)zend_object_store_get_object(c->parent TSRMLS_CC);
int response, w = 0, fsync = 0, journal = 0, timeout = -1;
mongoclient *link = (mongoclient*) zend_object_store_get_object(c->link TSRMLS_CC);
int max_document_size = connection->max_bson_size;
int max_message_size = connection->max_message_size;
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror");
timeout_p = zend_read_static_property(mongo_ce_Cursor, "timeout", strlen("timeout"), NOISY TSRMLS_CC);
convert_to_long(timeout_p);
/* The value hasn't been modified from what we registered it as originally, but we do
* need to set it to a real value */
if (Z_LVAL_P(timeout_p) == PHP_MONGO_STATIC_CURSOR_TIMEOUT_NOT_SET_INITIALIZER) {
timeout = link->servers->options.socketTimeoutMS;
} else {
/* The value was modified, bad user, bad user! Tell him its deprecated */
timeout = Z_LVAL_P(timeout_p);
php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The 'MongoCursor::$timeout' static property is deprecated, please call MongoCursor->timeout() instead");
}
/* Get the default value for journalling */
fsync = link->servers->options.default_fsync;
journal = link->servers->options.default_journal;
/* Read the default_* properties from the link */
if (link->servers->options.default_w != -1) {
w = link->servers->options.default_w;
}
if (link->servers->options.default_wstring != NULL) {
w_str = link->servers->options.default_wstring;
}
/* This picks up the default "w" through the properties of MongoCollection
* and MongoDb, but only if w is still 1 - as otherwise it was perhaps
* overridden with the "w" (or "safe") option. */
{
zval *w_prop = zend_read_property(mongo_ce_Collection, coll, "w", strlen("w"), NOISY TSRMLS_CC);
if (Z_TYPE_P(w_prop) == IS_STRING) {
w_str = Z_STRVAL_P(w_prop);
} else {
convert_to_long(w_prop);
if (Z_LVAL_P(w_prop) != 1) {
w = Z_LVAL_P(w_prop);
w_str = NULL;
}
}
}
/* Fetch all the options from the options array */
if (options && IS_ARRAY_OR_OBJECT_P(options)) {
zval **gle_pp = NULL, **fsync_pp, **timeout_pp, **journal_pp;
/* First we try "w", and if that is not found we check for "safe" */
if (zend_hash_find(HASH_P(options), "w", strlen("w") + 1, (void**) &gle_pp) == SUCCESS) {
switch (Z_TYPE_PP(gle_pp)) {
case IS_STRING:
w_str = Z_STRVAL_PP(gle_pp);
break;
case IS_BOOL:
case IS_LONG:
w = Z_LVAL_PP(gle_pp); /* This is actually "wrong" for bools, but it works */
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The value of the 'w' option either needs to be a integer or string");
}
} else if (zend_hash_find(HASH_P(options), "safe", strlen("safe") + 1, (void**) &gle_pp) == SUCCESS) {
switch (Z_TYPE_PP(gle_pp)) {
case IS_STRING:
w_str = Z_STRVAL_PP(gle_pp);
break;
case IS_LONG:
w = Z_LVAL_PP(gle_pp);
break;
case IS_BOOL:
if (Z_BVAL_PP(gle_pp)) {
/* If we already provided Write Concern, do not overwrite it with w=1 */
if (!(w > 1 || w_str)) {
w = 1;
}
} else {
w = 0;
}
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The value of the 'safe' option either needs to be a integer or string");
}
}
if (SUCCESS == zend_hash_find(HASH_P(options), "fsync", strlen("fsync") + 1, (void**) &fsync_pp)) {
convert_to_boolean(*fsync_pp);
fsync = Z_BVAL_PP(fsync_pp);
}
if (zend_hash_find(HASH_P(options), "j", strlen("j") + 1, (void**) &journal_pp) == SUCCESS) {
convert_to_boolean(*journal_pp);
journal = Z_BVAL_PP(journal_pp);
}
if (SUCCESS == zend_hash_find(HASH_P(options), "socketTimeoutMS", strlen("socketTimeoutMS") + 1, (void**) &timeout_pp)) {
convert_to_long(*timeout_pp);
timeout = Z_LVAL_PP(timeout_pp);
} else if (SUCCESS == zend_hash_find(HASH_P(options), "timeout", strlen("timeout") + 1, (void**) &timeout_pp)) {
php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The 'timeout' option is deprecated, please use 'socketTimeoutMS' instead");
convert_to_long(*timeout_pp);
timeout = Z_LVAL_PP(timeout_pp);
}
}
/* fsync forces "w" to be atleast 1, so don't touch it if it's
* already set to something else above while parsing "w" (and
* "safe") */
if (fsync && w == 0) {
w = 1;
}
/* get "db.$cmd" zval */
MAKE_STD_ZVAL(cmd_ns_z);
spprintf(&cmd_ns, 0, "%s.$cmd", Z_STRVAL_P(db->name));
ZVAL_STRING(cmd_ns_z, cmd_ns, 0);
/* get {"getlasterror" : 1} zval */
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_long(cmd, "getlasterror", 1);
/* if we have either a string, or w > 1, then we need to add "w" and
* perhaps "wtimeout" to GLE */
if (w_str || w > 1) {
zval *wtimeout, **wtimeout_pp;
if (w_str) {
add_assoc_string(cmd, "w", w_str, 1);
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added w='%s'", w_str);
} else {
add_assoc_long(cmd, "w", w);
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added w=%d", w);
}
if (options && zend_hash_find(HASH_P(options), "wTimeoutMS", strlen("wTimeoutMS") + 1, (void **)&wtimeout_pp) == SUCCESS) {
convert_to_long(*wtimeout_pp);
add_assoc_long(cmd, "wtimeout", Z_LVAL_PP(wtimeout_pp));
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added wtimeout=%d (wTimeoutMS from options array)", Z_LVAL_PP(wtimeout_pp));
} else if (options && zend_hash_find(HASH_P(options), "wtimeout", strlen("wtimeout") + 1, (void **)&wtimeout_pp) == SUCCESS) {
php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The 'wtimeout' option is deprecated, please use 'wTimeoutMS' instead");
convert_to_long(*wtimeout_pp);
add_assoc_long(cmd, "wtimeout", Z_LVAL_PP(wtimeout_pp));
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added wtimeout=%d (wtimeout from options array)", Z_LVAL_PP(wtimeout_pp));
} else {
wtimeout = zend_read_property(mongo_ce_Collection, coll, "wtimeout", strlen("wtimeout"), NOISY TSRMLS_CC);
convert_to_long(wtimeout);
add_assoc_long(cmd, "wtimeout", Z_LVAL_P(wtimeout));
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added wtimeout=%d (from collection property)", Z_LVAL_P(wtimeout));
}
}
if (fsync) {
add_assoc_bool(cmd, "fsync", 1);
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added fsync=1");
}
if (journal) {
add_assoc_bool(cmd, "j", 1);
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "append_getlasterror: added j=1");
}
/* get cursor */
MAKE_STD_ZVAL(cursor_z);
object_init_ex(cursor_z, mongo_ce_Cursor);
MAKE_STD_ZVAL(temp);
ZVAL_NULL(temp);
MONGO_METHOD2(MongoCursor, __construct, temp, cursor_z, c->link, cmd_ns_z);
zval_ptr_dtor(&temp);
if (EG(exception)) {
zval_ptr_dtor(&cursor_z);
zval_ptr_dtor(&cmd_ns_z);
zval_ptr_dtor(&cmd);
return 0;
}
cursor = (mongo_cursor*)zend_object_store_get_object(cursor_z TSRMLS_CC);
/* Make sure the "getLastError" also gets send to a primary. This should
* be refactored alongside with the getLastError redirection in
* db.c/MongoDB::command. The Cursor creation should be done through an
* init method otherwise a connection have to be requested twice. */
mongo_manager_log(link->manager, MLOG_CON, MLOG_INFO, "forcing primary for getlasterror");
php_mongo_cursor_force_primary(cursor);
cursor->limit = -1;
cursor->timeout = timeout;
zval_ptr_dtor(&cursor->query);
/* cmd is now part of cursor, so it shouldn't be dtored until cursor is */
cursor->query = cmd;
/* append the query */
response = php_mongo_write_query(buf, cursor, max_document_size, max_message_size TSRMLS_CC);
zval_ptr_dtor(&cmd_ns_z);
mongo_log_stream_query(connection, cursor TSRMLS_CC);
if (FAILURE == response) {
zval_ptr_dtor(&cursor_z);
return 0;
}
return cursor_z;
}
/* Returns a connection for the operation.
* Connection flags (connection_flags) are MONGO_CON_TYPE_READ and MONGO_CON_TYPE_WRITE. */
mongo_connection* get_server(mongo_collection *c, int connection_flags TSRMLS_DC)
{
mongoclient *link;
mongo_connection *connection;
char *error_message = NULL;
link = (mongoclient*)zend_object_store_get_object((c->link) TSRMLS_CC);
if (!link) {
zend_throw_exception(mongo_ce_Exception, "The MongoCollection object has not been correctly initialized by its constructor", 17 TSRMLS_CC);
return NULL;
}
/* TODO: Fix better error message */
if ((connection = mongo_get_read_write_connection(link->manager, link->servers, connection_flags, (char **) &error_message)) == NULL) {
if (error_message) {
php_mongo_cursor_throw(mongo_ce_CursorException, NULL, 16 TSRMLS_CC, "Couldn't get connection: %s", error_message);
free(error_message);
} else {
php_mongo_cursor_throw(mongo_ce_CursorException, NULL, 16 TSRMLS_CC, "Couldn't get connection");
}
return NULL;
}
return connection;
}
/* Wrapper for sending and wrapping in a safe op */
static int send_message(zval *this_ptr, mongo_connection *connection, mongo_buffer *buf, zval *options, zval *return_value TSRMLS_DC)
{
int retval = 1;
char *error_message = NULL;
mongoclient *link;
mongo_collection *c;
c = (mongo_collection*)zend_object_store_get_object(this_ptr TSRMLS_CC);
if (!c->ns) {
zend_throw_exception(mongo_ce_Exception, "The MongoCollection object has not been correctly initialized by its constructor", 0 TSRMLS_CC);
return 0;
}
link = (mongoclient*)zend_object_store_get_object((c->link) TSRMLS_CC);
if (!link) {
zend_throw_exception(mongo_ce_Exception, "The MongoCollection object has not been correctly initialized by its constructor", 17 TSRMLS_CC);
return 0;
}
if (is_gle_op(this_ptr, options, &link->servers->options TSRMLS_CC)) {
zval *cursor = append_getlasterror(this_ptr, buf, options, connection TSRMLS_CC);
if (cursor) {
do_gle_op(link->manager, connection, cursor, buf, return_value TSRMLS_CC);
zval_ptr_dtor(&cursor);
retval = -1;
} else {
retval = 0;
}
} else if (link->manager->send(connection, &link->servers->options, buf->start, buf->pos - buf->start, (char **) &error_message) == -1) {
/* TODO: Find out what to do with the error message here */
free(error_message);
retval = 0;
} else {
retval = 1;
}
return retval;
}
/* Determine if the operation should have a GLE command appended. This is based
* on whether a write concern ("w" or "safe") or fsync/journal options are
* specified.
*/
static int is_gle_op(zval *coll, zval *options, mongo_server_options *server_options TSRMLS_DC)
{
int gle_op = 0, default_fsync, default_journal, coll_w = 0;
zval *z_coll_w;
/* Get the fsync/journal defaults from the MongoClient server options */
default_fsync = server_options->default_fsync;
default_journal = server_options->default_journal;
/* Get the MongoCollection write concern instead of checking the MongoClient
* server options, since MongoCollection will have inherited any defaults
* through the MongoDB class. Additionally, this ensures that we respect a
* write concern set directly on the MongoCollection instance. */
z_coll_w = zend_read_property(mongo_ce_Collection, coll, "w", strlen("w"), NOISY TSRMLS_CC);
if (Z_TYPE_P(z_coll_w) == IS_STRING) {
/* We don't actually care what the string is, only that it was specified */
coll_w = 1;
} else {
convert_to_long(z_coll_w);
coll_w = Z_LVAL_P(z_coll_w);
}
/* Then we check the options array that could overwrite the default */
if (options && Z_TYPE_P(options) == IS_ARRAY) {
zval **gle_pp, **fsync_pp, **journal_pp;
/* Check for "w" in the options array. If it is not found, consult the
* "safe" option, followed by the MongoCollection write concern. */
if (zend_hash_find(HASH_P(options), "w", strlen("w") + 1, (void**) &gle_pp) == SUCCESS) {
switch (Z_TYPE_PP(gle_pp)) {
case IS_STRING:
gle_op = 1;
break;
case IS_BOOL:
case IS_LONG:
/* This is actually "wrong" for bools, but it works */
if (Z_LVAL_PP(gle_pp) >= 1) {
gle_op = 1;
}
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The value of the 'w' option either needs to be a integer or string");
}
} else if (zend_hash_find(HASH_P(options), "safe", strlen("safe") + 1, (void**) &gle_pp) == SUCCESS) {
php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The 'safe' option is deprecated, please use 'w' instead");
switch (Z_TYPE_PP(gle_pp)) {
case IS_STRING:
gle_op = 1;
break;
case IS_BOOL:
case IS_LONG:
/* This is actually "wrong" for bools, but it works */
if (Z_LVAL_PP(gle_pp) >= 1) {
gle_op = 1;
}
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The value of the 'safe' option either needs to be a boolean or a string");
}
} else if (coll_w >= 1) {
gle_op = 1;
}
/* Check for "fsync" in the options array. If it is not found, respect
* the MongoClient "fsync" option. */
if (zend_hash_find(HASH_P(options), "fsync", strlen("fsync") + 1, (void**)&fsync_pp) == SUCCESS) {
convert_to_boolean_ex(fsync_pp);
if (Z_BVAL_PP(fsync_pp)) {
gle_op = 1;
}
} else if (default_fsync) {
gle_op = 1;
}
/* Check for "j" in the options array. If it is not found, respect the
* MongoClient "journal" option. */
if (zend_hash_find(HASH_P(options), "j", strlen("j") + 1, (void**)&journal_pp) == SUCCESS) {
convert_to_boolean_ex(journal_pp);
if (Z_BVAL_PP(journal_pp)) {
gle_op = 1;
}
} else if (default_journal) {
gle_op = 1;
}
} else {
gle_op = (coll_w >= 1 || default_fsync || default_journal);
}
mongo_manager_log(MonGlo(manager), MLOG_IO, MLOG_FINE, "is_gle_op: %s", gle_op ? "yes" : "no");
return gle_op;
}
/* This wrapper temporarily turns off the exception throwing bit if it has been
* set (by calling php_mongo_cursor_throw() before). We can't call
* php_mongo_cursor_throw after deregister as it frees up bits of memory that
* php_mongo_cursor_throw uses to construct its error message.
*
* Without the disabling of the exception bit and when a user defined error
* handler is used on the PHP side, the notice would never been shown because
* the exception bubbles up before the notice can actually be shown. By turning
* the error handling mode to EH_NORMAL temporarily, we circumvent this
* problem. */
static void connection_deregister_wrapper(mongo_con_manager *manager, mongo_connection *connection TSRMLS_DC)
{
int orig_error_handling;
/* Save EG/PG(error_handling) so that we can show log messages when we have
* already thrown an exception */
orig_error_handling = EG(error_handling);
EG(error_handling) = EH_NORMAL;
mongo_manager_connection_deregister(manager, connection);
EG(error_handling) = orig_error_handling;
}
static void do_gle_op(mongo_con_manager *manager, mongo_connection *connection, zval *cursor_z, mongo_buffer *buf, zval *return_value TSRMLS_DC)
{
char *error_message;
mongo_cursor *cursor;
mongoclient *client;
cursor = (mongo_cursor*)zend_object_store_get_object(cursor_z TSRMLS_CC);
client = (mongoclient*)zend_object_store_get_object(cursor->zmongoclient TSRMLS_CC);
cursor->connection = connection;
if (-1 == manager->send(connection, &client->servers->options, buf->start, buf->pos - buf->start, (char **) &error_message)) {
mongo_manager_log(manager, MLOG_IO, MLOG_WARN, "do_gle_op: sending data failed, removing connection %s", connection->hash);
php_mongo_cursor_throw(mongo_ce_CursorException, connection, 16 TSRMLS_CC, "%s", error_message);
connection_deregister_wrapper(manager, connection TSRMLS_CC);
free(error_message);
cursor->connection = NULL;
return;
}
/* get reply */
if (FAILURE == php_mongo_get_reply(cursor TSRMLS_CC)) {
/* php_mongo_get_reply() throws exceptions */
mongo_manager_connection_deregister(manager, connection);
cursor->connection = NULL;
return;
}
cursor->started_iterating = 1;
php_mongocursor_load_current_element(cursor TSRMLS_CC);
/* MongoCursor::next() threw an exception */
if (EG(exception)) {
cursor->connection = NULL;
return;
}
/* Check if either the GLE command or the previous write operation failed */
php_mongo_trigger_error_on_gle(cursor->connection, cursor->current TSRMLS_CC);
RETVAL_ZVAL(cursor->current, 1, 0);
cursor->connection = NULL;
return;
}
int mongo_collection_insert_opcode(mongo_con_manager *manager, mongo_connection *connection, mongo_server_options *options, zval *write_options, zval *this_ptr, mongo_buffer *buf, char *namespace, int namespace_len, zval *document, zval *return_value TSRMLS_DC)
{
int retval = 0;
if (FAILURE == php_mongo_write_insert(buf, namespace, document, connection->max_bson_size, connection->max_message_size TSRMLS_CC)) {
return 0;
}
mongo_log_stream_insert(connection, document, write_options TSRMLS_CC);
/* retval == -1 means a GLE response was received, so send_message() has
* either set return_value or thrown an exception via do_gle_op(). */
retval = send_message(this_ptr, connection, buf, write_options, return_value TSRMLS_CC);
return retval;
}
void mongo_convert_write_api_return_to_legacy_retval(zval *return_value, php_mongo_write_types type, int write_concern TSRMLS_DC)
{
zval **ok, **err, **errmsg, **n;
if (write_concern < 1) {
/* Not kidding.. Surpress any exception thrown for w=0 */
zend_clear_exception(TSRMLS_C);
convert_to_boolean(return_value);
return;
}
if (SUCCESS == zend_hash_find(HASH_P(return_value), "ok", strlen("ok") + 1, (void**) &ok) && Z_TYPE_PP(ok) != IS_DOUBLE) {
convert_to_double(*ok);
}
if (FAILURE == zend_hash_find(HASH_P(return_value), "err", strlen("err") + 1, (void**) &err)) {
add_assoc_null(return_value, "err");
}
if (FAILURE == zend_hash_find(HASH_P(return_value), "errmsg", strlen("errmsg") + 1, (void**) &errmsg)) {
add_assoc_null(return_value, "errmsg");
}
switch (type) {
case MONGODB_API_COMMAND_INSERT:
if (SUCCESS == zend_hash_find(HASH_P(return_value), "n", strlen("n") + 1, (void**) &n)) {
/* "n" was always 0 for OP_INSERT gle. */
convert_to_long(*n);
Z_LVAL_PP(n) = 0;
}
break;
case MONGODB_API_COMMAND_UPDATE: {
int updatedExisting = 0;
if (SUCCESS == zend_hash_find(HASH_P(return_value), "n", strlen("n") + 1, (void**) &n)) {
zval **upserted;
convert_to_long_ex(n);
if (SUCCESS == zend_hash_find(HASH_P(return_value), "upserted", strlen("upserted") + 1, (void**) &upserted) && Z_TYPE_PP(upserted) == IS_ARRAY) {
zval **upserted_index;
if (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_PP(upserted), (void **)&upserted_index)) {
zval **upserted_id;
/* upserted is an array of _ids in 2.6 */
if (SUCCESS == zend_hash_find(HASH_PP(upserted_index), "_id", strlen("_id") + 1, (void**) &upserted_id)) {
zval *id;
MAKE_STD_ZVAL(id);
MAKE_COPY_ZVAL(upserted_id, id);
zend_hash_del(HASH_OF(return_value), "upserted", strlen("upserted") + 1);
add_assoc_zval(return_value, "upserted", id);
}
}
}
else if (Z_LVAL_PP(n) > 0) {
/* updatedExisting needs to be set to true when existing documents were modified */
updatedExisting = 1;
}
}
add_assoc_bool(return_value, "updatedExisting", updatedExisting);
} break;
case MONGODB_API_COMMAND_DELETE:
break;
}
}
void mongo_apply_implicit_write_options(php_mongo_write_options *write_options, mongo_server_options *server_options, zval *collection TSRMLS_DC)
{
if (write_options->fsync == -1) {
write_options->fsync = server_options->default_fsync;
}
if (write_options->j == -1) {
write_options->j = server_options->default_journal;
}
if (write_options->wtimeout == -1) {
zval *wtimeout_prop;
write_options->wtimeout = server_options->default_wtimeout;
wtimeout_prop = zend_read_property(mongo_ce_Collection, collection, "wtimeout", strlen("wtimeout"), NOISY TSRMLS_CC);
convert_to_long(wtimeout_prop);
if (Z_LVAL_P(wtimeout_prop) != PHP_MONGO_DEFAULT_WTIMEOUT) {
write_options->wtimeout = Z_LVAL_P(wtimeout_prop);
}
}
if (write_options->wtype == -1) {
zval *w_prop = zend_read_property(mongo_ce_Collection, collection, "w", strlen("w"), NOISY TSRMLS_CC);
if (Z_TYPE_P(w_prop) == IS_LONG || Z_TYPE_P(w_prop) == IS_BOOL) {
if (Z_LVAL_P(w_prop) == 1) {
if (server_options->default_w != -1) {
write_options->write_concern.w = server_options->default_w;
write_options->wtype = 1;
} else if (server_options->default_wstring != NULL) {
write_options->write_concern.wstring = server_options->default_wstring;
write_options->wtype = 2;
} else {
write_options->write_concern.w = Z_LVAL_P(w_prop);
write_options->wtype = 1;
}
} else {
write_options->write_concern.w = Z_LVAL_P(w_prop);
write_options->wtype = 1;
}
} else {
convert_to_string(w_prop);
write_options->write_concern.wstring = Z_STRVAL_P(w_prop);
write_options->wtype = 2;
}
}
}
/*
* Returns the socket read timeout (in ms) from the specified write options
* ('socketTimeoutMS', with a deprecated fallback to 'timeout').
*/
int mongo_get_socket_read_timeout(mongo_server_options *server_options, zval *z_write_options TSRMLS_DC)
{
if (z_write_options && Z_TYPE_P(z_write_options) == IS_ARRAY) {
zval **timeout_pp;
if (SUCCESS == zend_hash_find(HASH_P(z_write_options), "socketTimeoutMS", strlen("socketTimeoutMS") + 1, (void**) &timeout_pp)) {
convert_to_long(*timeout_pp);
return Z_LVAL_PP(timeout_pp);
} else if (SUCCESS == zend_hash_find(HASH_P(z_write_options), "timeout", strlen("timeout") + 1, (void**) &timeout_pp)) {
php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The 'timeout' option is deprecated, please use 'socketTimeoutMS' instead");
convert_to_long(*timeout_pp);
return Z_LVAL_PP(timeout_pp);
}
}
return server_options->socketTimeoutMS;
}
int mongo_collection_delete_api(mongo_con_manager *manager, mongo_connection *connection, mongo_server_options *server_options, int socket_read_timeout, php_mongo_write_delete_args *delete_options, php_mongo_write_options *write_options, char *dbname, zval *collection, zval *return_value TSRMLS_DC)
{
char *command_ns;
char *error_message;
int retval = 0;
int bytes_written = 0;
int request_id;
mongo_buffer buf;
mongo_collection *c = (mongo_collection*)zend_object_store_get_object(collection TSRMLS_CC);
spprintf(&command_ns, 0, "%s.$cmd", dbname);
CREATE_BUF(buf, INITIAL_BUF_SIZE);
request_id = php_mongo_api_delete_single(&buf, command_ns, Z_STRVAL_P(c->name), delete_options, write_options, connection TSRMLS_CC);
efree(command_ns);
if (request_id == 0) {
/* The document is greater then allowed limits, exception already thrown */
efree(buf.start);
return 0;
}
bytes_written = manager->send(connection, server_options, buf.start, buf.pos - buf.start, &error_message);
if (bytes_written < 1) {
/* Didn't write anything, something bad must have happened */
free(error_message);
efree(buf.start);
return 0;
}
array_init(return_value);
retval = php_mongo_api_get_reply(manager, connection, server_options, socket_read_timeout, request_id, &return_value TSRMLS_CC);
efree(buf.start);
if (retval != 0) {
mongo_manager_connection_deregister(manager, connection);
if (write_options->wtype == 1 && write_options->write_concern.w < 1) {
/* Clear out exception when w=0 */
zend_clear_exception(TSRMLS_C);
convert_to_boolean(return_value);
return 0;
}
return 0;
}
if (php_mongo_api_raise_exception_on_command_failure(connection, return_value TSRMLS_CC)) {
return 0;
}
if (php_mongo_api_raise_exception_on_write_failure(connection, return_value TSRMLS_CC)) {
return 0;
}
return 1;
}
int mongo_collection_update_api(mongo_con_manager *manager, mongo_connection *connection, mongo_server_options *server_options, int socket_read_timeout, php_mongo_write_update_args *update_options, php_mongo_write_options *write_options, char *dbname, zval *collection, zval *return_value TSRMLS_DC)
{
char *command_ns;
char *error_message;
int retval = 0;
int bytes_written = 0;
int request_id;
mongo_buffer buf;
mongo_collection *c = (mongo_collection*)zend_object_store_get_object(collection TSRMLS_CC);
spprintf(&command_ns, 0, "%s.$cmd", dbname);
CREATE_BUF(buf, INITIAL_BUF_SIZE);
request_id = php_mongo_api_update_single(&buf, command_ns, Z_STRVAL_P(c->name), update_options, write_options, connection TSRMLS_CC);
efree(command_ns);
if (request_id == 0) {
/* The document is greater then allowed limits, exception already thrown */
efree(buf.start);
return 0;
}
bytes_written = manager->send(connection, server_options, buf.start, buf.pos - buf.start, &error_message);
if (bytes_written < 1) {
/* Didn't write anything, something bad must have happened */
free(error_message);
efree(buf.start);
return 0;
}
array_init(return_value);
retval = php_mongo_api_get_reply(manager, connection, server_options, socket_read_timeout, request_id, &return_value TSRMLS_CC);
efree(buf.start);
if (retval != 0) {
mongo_manager_connection_deregister(manager, connection);
if (write_options->wtype == 1 && write_options->write_concern.w < 1) {
/* Clear out exception when w=0 */
zend_clear_exception(TSRMLS_C);
convert_to_boolean(return_value);
return 0;
}
return 0;
}
if (php_mongo_api_raise_exception_on_command_failure(connection, return_value TSRMLS_CC)) {
return 0;
}
if (php_mongo_api_raise_exception_on_write_failure(connection, return_value TSRMLS_CC)) {
return 0;
}
return 1;
}
/* Returns 0 on failure, throwing exception?
* Returns 1 on success, setting zval return_value to the return document
*/
int mongo_collection_insert_api(mongo_con_manager *manager, mongo_connection *connection, mongo_server_options *server_options, int socket_read_timeout, php_mongo_write_options *write_options, char *dbname, zval *collection, zval *document, zval *return_value TSRMLS_DC)
{
char *command_ns;
char *error_message;
int retval = 0;
int bytes_written = 0;
int request_id;
mongo_buffer buf;
mongo_collection *c = (mongo_collection*)zend_object_store_get_object(collection TSRMLS_CC);
spprintf(&command_ns, 0, "%s.$cmd", dbname);
CREATE_BUF(buf, INITIAL_BUF_SIZE);
request_id = php_mongo_api_insert_single(&buf, command_ns, Z_STRVAL_P(c->name), document, write_options, connection TSRMLS_CC);
efree(command_ns);
if (request_id == 0) {
/* The document is greater then allowed limits, exception already thrown */
efree(buf.start);
return 0;
}
bytes_written = manager->send(connection, server_options, buf.start, buf.pos - buf.start, &error_message);
if (bytes_written < 1) {
/* Didn't write anything, something bad must have happened */
free(error_message);
efree(buf.start);
return 0;
}
array_init(return_value);
retval = php_mongo_api_get_reply(manager, connection, server_options, socket_read_timeout, request_id, &return_value TSRMLS_CC);
efree(buf.start);
if (retval != 0) {
mongo_manager_connection_deregister(manager, connection);
/* Exception already thrown */
return 0;
}
if (php_mongo_api_raise_exception_on_command_failure(connection, return_value TSRMLS_CC)) {
return 0;
}
if (php_mongo_api_raise_exception_on_write_failure(connection, return_value TSRMLS_CC)) {
return 0;
}
return 1;
}
/* {{{ proto bool|array MongoCollection::insert(array|object document [, array options])
Insert a document into the collection and return the database response if
the write concern is >= 1. Otherwise, boolean true is returned if the
document is not empty. */
PHP_METHOD(MongoCollection, insert)
{
zval *document, *z_write_options = 0;
mongoclient *link;
mongo_connection *connection;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|z", &document, &z_write_options) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(1, document);
PHP_MONGO_GET_COLLECTION(getThis());
link = (mongoclient*)zend_object_store_get_object(c->link TSRMLS_CC);
if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == NULL) {
RETURN_FALSE;
}
if (php_mongo_api_connection_supports_feature(connection, PHP_MONGO_API_WRITE_API)) {
php_mongo_write_options write_options = {-1, {-1}, -1, -1, -1, -1};
int retval;
mongo_db *db;
int socket_read_timeout = 0;
PHP_MONGO_GET_DB(c->parent);
mongo_apply_implicit_write_options(&write_options, &link->servers->options, getThis() TSRMLS_CC);
php_mongo_api_write_options_from_zval(&write_options, z_write_options TSRMLS_CC);
socket_read_timeout = mongo_get_socket_read_timeout(&link->servers->options, z_write_options TSRMLS_CC);
retval = mongo_collection_insert_api(link->manager, connection, &link->servers->options, socket_read_timeout, &write_options, Z_STRVAL_P(db->name), getThis(), document, return_value TSRMLS_CC);
if (retval) {
/* Adds random "err", "code", "errmsg" empty fields to be compatible with
* old-style return values */
mongo_convert_write_api_return_to_legacy_retval(return_value, MONGODB_API_COMMAND_INSERT, write_options.wtype == 1 ? write_options.write_concern.w : 1 TSRMLS_CC);
} else {
if (write_options.wtype == 1 && write_options.write_concern.w < 1) {
zend_clear_exception(TSRMLS_C);
convert_to_boolean(return_value);
return;
}
}
return;
} else if (php_mongo_api_connection_supports_feature(connection, PHP_MONGO_API_RELEASE_2_4_AND_BEFORE)) {
int retval;
mongo_buffer buf;
CREATE_BUF(buf, INITIAL_BUF_SIZE);
retval = mongo_collection_insert_opcode(link->manager, connection, &link->servers->options, z_write_options, getThis(), &buf, Z_STRVAL_P(c->ns), Z_STRLEN_P(c->ns), document, return_value TSRMLS_CC);
/* retval == -1 means a GLE response was received, so send_message() has
* either set return_value or thrown an exception via do_gle_op(). */
if (retval != -1) {
RETVAL_BOOL(retval);
}
efree(buf.start);
} else {
zend_throw_exception_ex(mongo_ce_Exception, 0 TSRMLS_CC, "Cannot determine how to write documents to the server");
}
}
/* }}} */
/* {{{ proto bool|array MongoCollection::batchInsert(array documents [, array options])
Insert an array of documents and return the database response if the write
concern is >= 1. Otherwise, a boolean value is returned indicating whether
the batch was successfully sent. */
PHP_METHOD(MongoCollection, batchInsert)
{
zval *docs, *options = NULL;
mongo_collection *c;
mongo_connection *connection;
mongo_buffer buf;
int bit_opts = 0;
int retval, count;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|z/", &docs, &options) == FAILURE) {
return;
}
/* Options are only supported in the new-style, ie: an array of "named
* parameters": array("continueOnError" => true); */
if (options) {
zval **continue_on_error = NULL;
if (zend_hash_find(HASH_P(options), "continueOnError", strlen("continueOnError") + 1, (void**)&continue_on_error) == SUCCESS) {
convert_to_boolean_ex(continue_on_error);
bit_opts = Z_BVAL_PP(continue_on_error) << 0;
}
Z_ADDREF_P(options);
} else {
MAKE_STD_ZVAL(options);
array_init(options);
}
PHP_MONGO_GET_COLLECTION(getThis());
if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == NULL) {
zval_ptr_dtor(&options);
RETURN_FALSE;
}
CREATE_BUF(buf, INITIAL_BUF_SIZE);
count = php_mongo_write_batch_insert(&buf, Z_STRVAL_P(c->ns), bit_opts, docs, connection->max_bson_size, connection->max_message_size TSRMLS_CC);
if (count == FAILURE) {
efree(buf.start);
zval_ptr_dtor(&options);
return;
}
if (count == 0) {
/* Empty array, or just scalar data */
zend_throw_exception(mongo_ce_Exception, "No write ops were included in the batch", 16 TSRMLS_CC);
efree(buf.start);
zval_ptr_dtor(&options);
return;
}
mongo_log_stream_batchinsert(connection, docs, options, bit_opts TSRMLS_CC);
/* retval == -1 means a GLE response was received, so send_message() has
* either set return_value or thrown an exception via do_gle_op(). */
retval = send_message(this_ptr, connection, &buf, options, return_value TSRMLS_CC);
if (retval != -1) {
RETVAL_BOOL(retval);
}
efree(buf.start);
zval_ptr_dtor(&options);
}
/* }}} */
/* {{{ proto array MongoCollection::find([array|object criteria [, array|object return_fields]])
Query this collection for documents matching $criteria and use $return_fields
as the projection. Return a MongoCursor for the result set. */
PHP_METHOD(MongoCollection, find)
{
zval *query = 0, *fields = 0;
mongo_collection *c;
mongo_cursor *cursor;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zz", &query, &fields) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(1, query);
MUST_BE_ARRAY_OR_OBJECT(2, fields);
PHP_MONGO_GET_COLLECTION(getThis());
object_init_ex(return_value, mongo_ce_Cursor);
/* Add read preferences to cursor */
cursor = (mongo_cursor*)zend_object_store_get_object(return_value TSRMLS_CC);
mongo_read_preference_replace(&c->read_pref, &cursor->read_pref);
php_mongocursor_create(cursor, c->link, Z_STRVAL_P(c->ns), Z_STRLEN_P(c->ns), query, fields TSRMLS_CC);
}
/* }}} */
/* {{{ proto array MongoCollection::findOne([array|object criteria [, array|object return_fields [, array options]]])
Return the first document that matches $criteria and use $return_fields as
the projection. NULL will be returned if no document matches. */
PHP_METHOD(MongoCollection, findOne)
{
zval *query = NULL, *fields = NULL, *options = NULL, *zcursor = NULL;
mongo_cursor *cursor;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zza", &query, &fields, &options) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(1, query);
MUST_BE_ARRAY_OR_OBJECT(2, fields);
PHP_MONGO_GET_COLLECTION(getThis());
MAKE_STD_ZVAL(zcursor);
object_init_ex(zcursor, mongo_ce_Cursor);
/* Add read preferences to cursor */
cursor = (mongo_cursor*)zend_object_store_get_object(zcursor TSRMLS_CC);
mongo_read_preference_replace(&c->read_pref, &cursor->read_pref);
if (php_mongocursor_create(cursor, c->link, Z_STRVAL_P(c->ns), Z_STRLEN_P(c->ns), query, fields TSRMLS_CC) == FAILURE) {
zval_ptr_dtor(&zcursor);
return;
}
/* Set limit to 1 */
php_mongo_cursor_set_limit(cursor, -1);
if (options) {
HashTable *hindex = HASH_P(options);
HashPosition pointer;
zval **data;
char *key;
uint key_type, index_key_len;
ulong index;
for (
zend_hash_internal_pointer_reset_ex(hindex, &pointer);
zend_hash_get_current_data_ex(hindex, (void**)&data, &pointer) == SUCCESS;
zend_hash_move_forward_ex(hindex, &pointer)
) {
key_type = zend_hash_get_current_key_ex(hindex, &key, &index_key_len, &index, NO_DUP, &pointer);
if (key_type == HASH_KEY_IS_LONG) {
continue;
}
if (zend_binary_strcasecmp(key, index_key_len, "maxTimeMS", strlen("maxTimeMS") + 1) == 0) {
convert_to_long_ex(data);
if ( ! php_mongo_cursor_add_option(cursor, "$maxTimeMS", *data TSRMLS_CC)) {
goto cleanup_on_failure;
}
}
}
}
/* query */
php_mongo_runquery(cursor TSRMLS_CC);
if (EG(exception)) {
zval_ptr_dtor(&zcursor);
RETURN_NULL();
}
/* Find return value */
if (php_mongocursor_load_current_element(cursor TSRMLS_CC) == FAILURE) {
zval_ptr_dtor(&zcursor);
RETURN_NULL();
}
if (php_mongo_handle_error(cursor TSRMLS_CC)) {
zval_ptr_dtor(&zcursor);
RETURN_NULL();
}
if (php_mongocursor_is_valid(cursor) == FAILURE) {
zval_ptr_dtor(&zcursor);
RETURN_NULL();
}
RETVAL_ZVAL(cursor->current, 1, 0);
cleanup_on_failure:
zend_objects_store_del_ref(zcursor TSRMLS_CC);
zval_ptr_dtor(&zcursor);
}
/* }}} */
/* {{{ proto array MongoCollection::findAndModify(array query [, array update [, array fields [, array options]]])
Atomically update and return a document */
PHP_METHOD(MongoCollection, findAndModify)
{
zval *query, *update = 0, *fields = 0, *options = 0;
zval *cmd, *retval, **values;
mongo_connection *used_connection;
mongo_collection *c;
mongo_db *db;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a!|a!a!a!", &query, &update, &fields, &options) == FAILURE) {
return;
}
c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(c->ns, MongoCollection);
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "findandmodify", c->name);
zval_add_ref(&c->name);
if (query && zend_hash_num_elements(Z_ARRVAL_P(query)) > 0) {
add_assoc_zval(cmd, "query", query);
zval_add_ref(&query);
}
if (update && zend_hash_num_elements(Z_ARRVAL_P(update)) > 0) {
add_assoc_zval(cmd, "update", update);
zval_add_ref(&update);
}
if (fields && zend_hash_num_elements(Z_ARRVAL_P(fields)) > 0) {
add_assoc_zval(cmd, "fields", fields);
zval_add_ref(&fields);
}
if (options && zend_hash_num_elements(Z_ARRVAL_P(options)) > 0) {
zval *temp;
zend_hash_merge(HASH_P(cmd), HASH_P(options), (void (*)(void*))zval_add_ref, &temp, sizeof(zval*), 1);
}
retval = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, &used_connection TSRMLS_CC);
if (retval && php_mongo_trigger_error_on_command_failure(used_connection, retval TSRMLS_CC) == SUCCESS) {
if (zend_hash_find(Z_ARRVAL_P(retval), "value", strlen("value") + 1, (void **)&values) == SUCCESS) {
/* We may wind up with a NULL here if there simply aren't any results */
if (Z_TYPE_PP(values) == IS_ARRAY) {
array_init(return_value);
zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(values), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
}
/* If it's not an array, we should return NULL, which is the
* *default* return value and without "RETVAL_NULL" we simply do
* nothing */
}
} else {
RETVAL_FALSE;
}
zval_ptr_dtor(&cmd);
zval_ptr_dtor(&retval);
}
/* }}} */
/* Takes OP_UPDATE flags (bit vector) and sets the correct update_args options */
static void mongo_apply_update_options_from_bits(php_mongo_write_update_args *update_options, int bits)
{
update_options->upsert = bits & (1 << 0) ? 1 : 0;
update_options->multi = bits & (1 << 1) ? 1 : 0;
}
static void php_mongocollection_update(zval *this_ptr, mongo_collection *c, zval *criteria, zval *newobj, zval *z_write_options, zval *return_value TSRMLS_DC)
{
int bit_opts = 0;
mongo_connection *connection;
if (z_write_options) {
zval **upsert = 0, **multiple = 0;
if (zend_hash_find(HASH_P(z_write_options), "upsert", strlen("upsert") + 1, (void**)&upsert) == SUCCESS) {
convert_to_boolean_ex(upsert);
bit_opts |= Z_BVAL_PP(upsert) << 0;
}
if (zend_hash_find(HASH_P(z_write_options), "multiple", strlen("multiple") + 1, (void**)&multiple) == SUCCESS) {
convert_to_boolean_ex(multiple);
bit_opts |= Z_BVAL_PP(multiple) << 1;
}
Z_ADDREF_P(z_write_options);
} else {
MAKE_STD_ZVAL(z_write_options);
array_init(z_write_options);
}
if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == NULL) {
zval_ptr_dtor(&z_write_options);
RETURN_FALSE;
}
if (php_mongo_api_connection_supports_feature(connection, PHP_MONGO_API_WRITE_API)) {
php_mongo_write_options write_options = {-1, {-1}, -1, -1, -1, -1};
php_mongo_write_update_args update_options = { NULL, NULL, -1, -1 };
mongoclient *link;
int retval;
mongo_db *db;
int socket_read_timeout = 0;
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
link = (mongoclient*)zend_object_store_get_object(c->link TSRMLS_CC);
update_options.query = criteria;
update_options.update = newobj;
mongo_apply_update_options_from_bits(&update_options, bit_opts);
mongo_apply_implicit_write_options(&write_options, &link->servers->options, getThis() TSRMLS_CC);
php_mongo_api_write_options_from_zval(&write_options, z_write_options TSRMLS_CC);
socket_read_timeout = mongo_get_socket_read_timeout(&link->servers->options, z_write_options TSRMLS_CC);
retval = mongo_collection_update_api(link->manager, connection, &link->servers->options, socket_read_timeout, &update_options, &write_options, Z_STRVAL_P(db->name), getThis(), return_value TSRMLS_CC);
if (retval) {
/* Adds random "err", "code", "errmsg" empty fields to be compatible with
* old-style return values */
mongo_convert_write_api_return_to_legacy_retval(return_value, MONGODB_API_COMMAND_UPDATE, write_options.wtype == 1 ? write_options.write_concern.w : 1 TSRMLS_CC);
}
zval_ptr_dtor(&z_write_options);
} else if (php_mongo_api_connection_supports_feature(connection, PHP_MONGO_API_RELEASE_2_4_AND_BEFORE)) {
int retval = 1;
mongo_buffer buf;
CREATE_BUF(buf, INITIAL_BUF_SIZE);
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)) {
efree(buf.start);
zval_ptr_dtor(&z_write_options);
return;
}
mongo_log_stream_update(connection, c->ns, criteria, newobj, z_write_options, bit_opts TSRMLS_CC);
/* retval == -1 means a GLE response was received, so send_message() has
* either set return_value or thrown an exception via do_gle_op(). */
retval = send_message(this_ptr, connection, &buf, z_write_options, return_value TSRMLS_CC);
if (retval != -1) {
RETVAL_BOOL(retval);
}
efree(buf.start);
zval_ptr_dtor(&z_write_options);
} else {
zend_throw_exception_ex(mongo_ce_Exception, 0 TSRMLS_CC, "Cannot determine how to update documents on the server");
}
}
/* {{{ proto bool|array MongoCollection::update(array|object criteria, array|object $newobj [, array options])
Update one or more documents matching $criteria with $newobj and return the
database response if the write concern is >= 1. Otherwise, boolean true is
returned. */
PHP_METHOD(MongoCollection, update)
{
zval *criteria, *newobj, *options = 0;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz|a/", &criteria, &newobj, &options) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(1, criteria);
MUST_BE_ARRAY_OR_OBJECT(2, newobj);
PHP_MONGO_GET_COLLECTION(getThis());
php_mongocollection_update(this_ptr, c, criteria, newobj, options, return_value TSRMLS_CC);
}
/* }}} */
static void mongo_apply_delete_options_from_bits(php_mongo_write_delete_args *delete_options, int bits)
{
delete_options->limit = bits & (1 << 0) ? 1 : 0;
}
static void php_mongocollection_remove(zval *this_ptr, mongo_collection *c, zval *criteria, zval *z_write_options, zval *return_value TSRMLS_DC)
{
int bit_opts = 0;
mongo_connection *connection;
if (!criteria) {
MAKE_STD_ZVAL(criteria);
array_init(criteria);
} else {
zval_add_ref(&criteria);
}
if (z_write_options) {
zval **just_one = NULL;
if (zend_hash_find(HASH_P(z_write_options), "justOne", strlen("justOne") + 1, (void**)&just_one) == SUCCESS) {
convert_to_boolean_ex(just_one);
bit_opts = Z_BVAL_PP(just_one) << 0;
}
Z_ADDREF_P(z_write_options);
} else {
MAKE_STD_ZVAL(z_write_options);
array_init(z_write_options);
}
if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == NULL) {
zval_ptr_dtor(&z_write_options);
zval_ptr_dtor(&criteria);
RETURN_FALSE;
}
if (php_mongo_api_connection_supports_feature(connection, PHP_MONGO_API_WRITE_API)) {
php_mongo_write_options write_options = {-1, {-1}, -1, -1, -1, -1};
php_mongo_write_delete_args delete_options = { NULL, -1 };
mongoclient *link;
int retval;
mongo_db *db;
int socket_read_timeout = 0;
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
link = (mongoclient*)zend_object_store_get_object(c->link TSRMLS_CC);
delete_options.query = criteria;
mongo_apply_delete_options_from_bits(&delete_options, bit_opts);
mongo_apply_implicit_write_options(&write_options, &link->servers->options, getThis() TSRMLS_CC);
php_mongo_api_write_options_from_zval(&write_options, z_write_options TSRMLS_CC);
socket_read_timeout = mongo_get_socket_read_timeout(&link->servers->options, z_write_options TSRMLS_CC);
retval = mongo_collection_delete_api(link->manager, connection, &link->servers->options, socket_read_timeout, &delete_options, &write_options, Z_STRVAL_P(db->name), getThis(), return_value TSRMLS_CC);
if (retval) {
/* Adds random "err", "code", "errmsg" empty fields to be compatible with
* old-style return values */
mongo_convert_write_api_return_to_legacy_retval(return_value, MONGODB_API_COMMAND_DELETE, write_options.wtype == 1 ? write_options.write_concern.w : 1 TSRMLS_CC);
}
zval_ptr_dtor(&z_write_options);
zval_ptr_dtor(&criteria);
} else if (php_mongo_api_connection_supports_feature(connection, PHP_MONGO_API_RELEASE_2_4_AND_BEFORE)) {
mongo_buffer buf;
int retval = 1;
CREATE_BUF(buf, INITIAL_BUF_SIZE);
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)) {
efree(buf.start);
zval_ptr_dtor(&criteria);
zval_ptr_dtor(&z_write_options);
return;
}
mongo_log_stream_delete(connection, c->ns, criteria, z_write_options, bit_opts TSRMLS_CC);
/* retval == -1 means a GLE response was received, so send_message() has
* either set return_value or thrown an exception via do_gle_op(). */
retval = send_message(this_ptr, connection, &buf, z_write_options, return_value TSRMLS_CC);
if (retval != -1) {
RETVAL_BOOL(retval);
}
efree(buf.start);
zval_ptr_dtor(&criteria);
zval_ptr_dtor(&z_write_options);
} else {
zend_throw_exception_ex(mongo_ce_Exception, 0 TSRMLS_CC, "Cannot determine how to update documents on the server");
}
}
/* {{{ proto bool|array MongoCollection::remove([array|object criteria [array options]])
Remove one or more documents matching $criteria */
PHP_METHOD(MongoCollection, remove)
{
zval *criteria = 0, *options = 0;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|za/", &criteria, &options) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(1, criteria);
PHP_MONGO_GET_COLLECTION(getThis());
php_mongocollection_remove(this_ptr, c, criteria, options, return_value TSRMLS_CC);
}
/* }}} */
static void mongo_collection_create_index_command(mongo_connection *connection, mongo_collection *collection, zval *keys, zval *options, zval *return_value TSRMLS_DC)
{
zval *cmd, *indexes, *index_spec, *retval;
zend_bool done_name = 0;
mongo_db *db = (mongo_db*)zend_object_store_get_object(collection->parent TSRMLS_CC);
/* set up data */
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "createIndexes", collection->name);
zval_add_ref(&collection->name);
/* set up "indexes" wrapper */
MAKE_STD_ZVAL(indexes);
array_init(indexes);
add_assoc_zval(cmd, "indexes", indexes);
/* set up index fields */
MAKE_STD_ZVAL(index_spec);
array_init(index_spec);
add_next_index_zval(indexes, index_spec);
/* add index keys */
if (IS_SCALAR_P(keys)) {
zval *key_array;
convert_to_string(keys);
if (Z_STRLEN_P(keys) == 0) {
zend_throw_exception_ex(mongo_ce_Exception, 22 TSRMLS_CC, "empty string passed as key field");
zval_ptr_dtor(&cmd);
return;
}
MAKE_STD_ZVAL(key_array);
array_init(key_array);
add_assoc_long(key_array, Z_STRVAL_P(keys), 1);
add_assoc_zval(index_spec, "key", key_array);
} else if (Z_TYPE_P(keys) == IS_ARRAY || Z_TYPE_P(keys) == IS_OBJECT) {
if (HASH_OF(keys)->nNumOfElements == 0) {
zend_throw_exception_ex(mongo_ce_Exception, 22 TSRMLS_CC, "index specification has no elements");
zval_ptr_dtor(&cmd);
return;
}
add_assoc_zval(index_spec, "key", keys);
Z_ADDREF_P(keys);
} else {
zend_throw_exception_ex(mongo_ce_Exception, 22 TSRMLS_CC, "index specification has to be an array");
zval_ptr_dtor(&cmd);
return;
}
/* process options */
if (options) {
zval *temp, **name, **maxtimems;
/* "maxTimeMS" belongs on top-level command document */
if (zend_hash_find(HASH_P(options), "maxTimeMS", strlen("maxTimeMS") + 1, (void**)&maxtimems) == SUCCESS) {
add_assoc_zval(cmd, "maxTimeMS", *maxtimems);
Z_ADDREF_PP(maxtimems);
zend_hash_del(HASH_P(options), "maxTimeMS", strlen("maxTimeMS") + 1);
}
/* Write concern options are not relevant to the createIndexes command */
DELETE_OPTION_IF_EXISTS(options, "fsync");
DELETE_OPTION_IF_EXISTS(options, "j");
DELETE_OPTION_IF_EXISTS(options, "safe");
DELETE_OPTION_IF_EXISTS(options, "w");
DELETE_OPTION_IF_EXISTS(options, "wtimeout");
DELETE_OPTION_IF_EXISTS(options, "wTimeoutMS");
zend_hash_merge(HASH_P(index_spec), HASH_P(options), (void (*)(void*))zval_add_ref, &temp, sizeof(zval*), 1);
/* "socketTimeoutMS" and "timeout" are php_mongo_runcommand() options */
DELETE_OPTION_IF_EXISTS(index_spec, "socketTimeoutMS");
DELETE_OPTION_IF_EXISTS(index_spec, "timeout");
if (zend_hash_find(HASH_P(options), "name", strlen("name") + 1, (void**)&name) == SUCCESS) {
if (Z_TYPE_PP(name) == IS_STRING && Z_STRLEN_PP(name) > MAX_INDEX_NAME_LEN) {
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);
zval_ptr_dtor(&cmd);
return;
}
done_name = 1;
add_assoc_zval(index_spec, "name", *name);
Z_ADDREF_PP(name);
}
}
/* make sure we set the name, based on the keys, if no name was part of options */
if (!done_name) {
char *key_str;
int key_str_len;
key_str = to_index_string(keys, &key_str_len TSRMLS_CC);
if (!key_str) {
zval_ptr_dtor(&cmd);
return;
}
if (key_str_len > MAX_INDEX_NAME_LEN) {
zend_throw_exception_ex(mongo_ce_Exception, 14 TSRMLS_CC, "index name too long: %d, max %d characters", key_str_len, MAX_INDEX_NAME_LEN);
efree(key_str);
zval_ptr_dtor(&cmd);
return;
}
add_assoc_stringl(index_spec, "name", key_str, key_str_len, 0);
}
retval = php_mongo_runcommand(collection->link, &collection->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, options, 0, NULL TSRMLS_CC);
zval_ptr_dtor(&cmd);
if (!retval) {
return;
}
if (php_mongo_trigger_error_on_command_failure(connection, retval TSRMLS_CC) == SUCCESS) {
RETVAL_ZVAL(retval, 0, 1);
} else {
zval_ptr_dtor(&retval);
}
}
/* }}} */
static void mongo_collection_create_index_legacy(mongo_connection *connection, mongo_collection *collection, zval *keys, zval *options, zval *return_value TSRMLS_DC)
{
zval *db, *system_indexes_collection, *data;
zend_bool done_name = 0;
if (IS_SCALAR_P(keys)) {
zval *key_array;
convert_to_string(keys);
if (Z_STRLEN_P(keys) == 0) {
zend_throw_exception_ex(mongo_ce_Exception, 22 TSRMLS_CC, "empty string passed as key field");
return;
}
MAKE_STD_ZVAL(key_array);
array_init(key_array);
add_assoc_long(key_array, Z_STRVAL_P(keys), 1);
keys = key_array;
} else if (Z_TYPE_P(keys) == IS_ARRAY || Z_TYPE_P(keys) == IS_OBJECT) {
if (HASH_OF(keys)->nNumOfElements == 0) {
zend_throw_exception_ex(mongo_ce_Exception, 22 TSRMLS_CC, "index specification has no elements");
return;
}
zval_add_ref(&keys);
} else {
zend_throw_exception_ex(mongo_ce_Exception, 22 TSRMLS_CC, "index specification has to be an array");
}
/* get the system.indexes collection */
db = collection->parent;
system_indexes_collection = php_mongo_db_selectcollection(db, "system.indexes", strlen("system.indexes") TSRMLS_CC);
PHP_MONGO_CHECK_EXCEPTION2(&keys, &system_indexes_collection);
/* set up data */
MAKE_STD_ZVAL(data);
array_init(data);
/* ns */
add_assoc_zval(data, "ns", collection->ns);
zval_add_ref(&collection->ns);
add_assoc_zval(data, "key", keys);
zval_add_ref(&keys);
if (options) {
zval *temp, **gle_pp, **fsync_pp, **timeout_pp, **name;
zend_hash_merge(HASH_P(data), HASH_P(options), (void (*)(void*))zval_add_ref, &temp, sizeof(zval*), 1);
if (zend_hash_find(HASH_P(options), "safe", strlen("safe") + 1, (void**)&gle_pp) == SUCCESS) {
zend_hash_del(HASH_P(data), "safe", strlen("safe") + 1);
}
if (zend_hash_find(HASH_P(options), "w", strlen("w") + 1, (void**)&gle_pp) == SUCCESS) {
zend_hash_del(HASH_P(data), "w", strlen("w") + 1);
}
if (zend_hash_find(HASH_P(options), "fsync", strlen("fsync") + 1, (void**)&fsync_pp) == SUCCESS) {
zend_hash_del(HASH_P(data), "fsync", strlen("fsync") + 1);
}
if (zend_hash_find(HASH_P(options), "timeout", strlen("timeout") + 1, (void**)&timeout_pp) == SUCCESS) {
zend_hash_del(HASH_P(data), "timeout", strlen("timeout") + 1);
}
if (zend_hash_find(HASH_P(options), "name", strlen("name") + 1, (void**)&name) == SUCCESS) {
if (Z_TYPE_PP(name) == IS_STRING && Z_STRLEN_PP(name) > MAX_INDEX_NAME_LEN) {
zval_ptr_dtor(&data);
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);
return;
}
done_name = 1;
}
zval_add_ref(&options);
} else {
zval *opts;
MAKE_STD_ZVAL(opts);
array_init(opts);
options = opts;
}
if (!done_name) {
char *key_str;
int key_str_len;
key_str = to_index_string(keys, &key_str_len TSRMLS_CC);
if (!key_str) {
zval_ptr_dtor(&data);
zval_ptr_dtor(&options);
return;
}
if (key_str_len > MAX_INDEX_NAME_LEN) {
zval_ptr_dtor(&data);
zend_throw_exception_ex(mongo_ce_Exception, 14 TSRMLS_CC, "index name too long: %d, max %d characters", key_str_len, MAX_INDEX_NAME_LEN);
efree(key_str);
zval_ptr_dtor(&options);
return;
}
add_assoc_stringl(data, "name", key_str, key_str_len, 0);
}
MONGO_METHOD2(MongoCollection, insert, return_value, system_indexes_collection, data, options);
/* Check for whether an exception was thrown. In the special case where
* there is an index-adding problem, we need to change the exception to a
* different one */
if (EG(exception)) {
long code = 0;
char *message;
code = Z_LVAL_P(zend_read_property(mongo_ce_Exception, EG(exception), "code", strlen("code"), NOISY TSRMLS_CC));
if (code == 10098 || code == 16734) {
message = estrdup(Z_STRVAL_P(zend_read_property(mongo_ce_Exception, EG(exception), "message", strlen("message"), NOISY TSRMLS_CC)));
zend_clear_exception(TSRMLS_C);
php_mongo_cursor_throw(mongo_ce_ResultException, NULL, 67 TSRMLS_CC, "%s", message);
efree(message);
}
}
zval_ptr_dtor(&options);
zval_ptr_dtor(&data);
zval_ptr_dtor(&system_indexes_collection);
zval_ptr_dtor(&keys);
}
/* {{{ proto bool MongoCollection::createIndex(array keys [, array options])
Create the $keys index if it does not already exist */
PHP_METHOD(MongoCollection, createIndex)
{
zval *keys, *options = NULL;
mongo_connection *connection;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|a", &keys, &options) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == NULL) {
RETURN_FALSE;
}
if (php_mongo_api_connection_min_server_version(connection, 2, 5, 5)) {
mongo_collection_create_index_command(connection, c, keys, options, return_value TSRMLS_CC);
} else {
mongo_collection_create_index_legacy(connection, c, keys, options, return_value TSRMLS_CC);
}
PHP_MONGO_GET_COLLECTION(getThis());
}
/* }}} */
/* {{{ proto bool MongoCollection::ensureIndex(mixed keys [, array options])
Create the $keys index if it does not already exist */
PHP_METHOD(MongoCollection, ensureIndex)
{
zval *keys, *options = NULL;
mongo_connection *connection;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a", &keys, &options) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
if ((connection = get_server(c, MONGO_CON_FLAG_WRITE TSRMLS_CC)) == NULL) {
RETURN_FALSE;
}
if (php_mongo_api_connection_min_server_version(connection, 2, 5, 5)) {
mongo_collection_create_index_command(connection, c, keys, options, return_value TSRMLS_CC);
} else {
mongo_collection_create_index_legacy(connection, c, keys, options, return_value TSRMLS_CC);
}
PHP_MONGO_GET_COLLECTION(getThis());
}
/* }}} */
/* {{{ proto array MongoCollection::deleteIndex(mixed keys)
Remove the $keys index */
PHP_METHOD(MongoCollection, deleteIndex)
{
zval *keys, *cmd, *retval;
char *key_str;
int key_str_len;
mongo_collection *c;
mongo_db *db;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &keys) == FAILURE) {
return;
}
key_str = to_index_string(keys, &key_str_len TSRMLS_CC);
if (!key_str) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "dropIndexes", c->name);
zval_add_ref(&c->name);
add_assoc_string(cmd, "index", key_str, 1);
retval = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, NULL TSRMLS_CC);
zval_ptr_dtor(&cmd);
efree(key_str);
if (retval) {
RETVAL_ZVAL(retval, 0, 1);
}
}
/* }}} */
/* {{{ proto array MongoCollection::deleteIndex()
Removes all indexes for this collection */
PHP_METHOD(MongoCollection, deleteIndexes)
{
zval *cmd, *retval;
mongo_collection *c;
mongo_db *db;
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_string(cmd, "dropIndexes", Z_STRVAL_P(c->name), 1);
add_assoc_string(cmd, "index", "*", 1);
retval = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, NULL TSRMLS_CC);
zval_ptr_dtor(&cmd);
if (retval) {
RETURN_ZVAL(retval, 0, 1);
}
}
/* }}} */
/* {{{ proto MongoCollection::getIndexInfo()
Get all indexes for this collection */
PHP_METHOD(MongoCollection, getIndexInfo)
{
zval *collection, *query, *cursor, *next;
mongo_collection *c;
PHP_MONGO_GET_COLLECTION(getThis());
collection = php_mongo_db_selectcollection(c->parent, "system.indexes", strlen("system.indexes") TSRMLS_CC);
PHP_MONGO_CHECK_EXCEPTION1(&collection);
MAKE_STD_ZVAL(query);
array_init(query);
add_assoc_string(query, "ns", Z_STRVAL_P(c->ns), 1);
MAKE_STD_ZVAL(cursor);
MONGO_METHOD1(MongoCollection, find, cursor, collection, query);
PHP_MONGO_CHECK_EXCEPTION3(&collection, &query, &cursor);
zval_ptr_dtor(&query);
zval_ptr_dtor(&collection);
array_init(return_value);
MAKE_STD_ZVAL(next);
MONGO_METHOD(MongoCursor, next, next, cursor);
PHP_MONGO_CHECK_EXCEPTION2(&cursor, &next);
while (Z_TYPE_P(next) != IS_NULL) {
add_next_index_zval(return_value, next);
MAKE_STD_ZVAL(next);
MONGO_METHOD(MongoCursor, next, next, cursor);
PHP_MONGO_CHECK_EXCEPTION2(&cursor, &next);
}
zval_ptr_dtor(&next);
zval_ptr_dtor(&cursor);
}
/* }}} */
/* {{{ proto MongoCollection::count([array criteria [, int limit [, int skip]]])
Count all documents matching $criteria with an optional limit and/or skip */
PHP_METHOD(MongoCollection, count)
{
zval *response, *cmd, *query=0;
long limit = 0, skip = 0;
zval **n;
mongo_collection *c;
mongo_db *db;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|zll", &query, &limit, &skip) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_string(cmd, "count", Z_STRVAL_P(c->name), 1);
if (query) {
add_assoc_zval(cmd, "query", query);
zval_add_ref(&query);
}
if (limit) {
add_assoc_long(cmd, "limit", limit);
}
if (skip) {
add_assoc_long(cmd, "skip", skip);
}
response = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, NULL TSRMLS_CC);
zval_ptr_dtor(&cmd);
if (!response) {
return;
}
if (zend_hash_find(HASH_P(response), "n", 2, (void**)&n) == SUCCESS) {
convert_to_long(*n);
RETVAL_ZVAL(*n, 1, 0);
zval_ptr_dtor(&response);
} else {
zval **errmsg;
/* The command failed, try to find an error message */
if (zend_hash_find(HASH_P(response), "errmsg", strlen("errmsg") + 1 , (void**)&errmsg) == SUCCESS) {
zend_throw_exception_ex(mongo_ce_Exception, 20 TSRMLS_CC, "Cannot run command count(): %s", Z_STRVAL_PP(errmsg));
} else {
zend_throw_exception(mongo_ce_Exception, "Cannot run command count()", 20 TSRMLS_CC);
}
zval_ptr_dtor(&response);
}
}
/* }}} */
/* {{{ proto mixed MongoCollection::save(array|object document [, array options])
Saves $document to this collection. An upsert will be used if the document's
_id is set; otherwise, it will be inserted. Return the database response if
the write concern is >= 1. Otherwise, boolean true is returned if the
document is not empty. */
PHP_METHOD(MongoCollection, save)
{
zval *a, *options = 0;
zval **id;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|a/", &a, &options) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(1, a);
if (!options) {
MAKE_STD_ZVAL(options);
array_init(options);
} else {
Z_ADDREF_P(options);
}
if (zend_hash_find(HASH_P(a), "_id", 4, (void**)&id) == SUCCESS) {
zval *criteria;
MAKE_STD_ZVAL(criteria);
array_init(criteria);
add_assoc_zval(criteria, "_id", *id);
zval_add_ref(id);
add_assoc_bool(options, "upsert", 1);
PHP_MONGO_GET_COLLECTION(getThis());
php_mongocollection_update(this_ptr, c, criteria, a, options, return_value TSRMLS_CC);
zval_ptr_dtor(&criteria);
zval_ptr_dtor(&options);
return;
}
MONGO_METHOD2(MongoCollection, insert, return_value, getThis(), a, options);
zval_ptr_dtor(&options);
}
/* }}} */
/* {{{ proto array MongoCollection::createDBRef(array dbref)
Create a database reference object */
PHP_METHOD(MongoCollection, createDBRef)
{
zval *obj;
mongo_collection *c;
mongo_db *db;
zval *retval;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &obj) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
if (
(obj = php_mongo_dbref_resolve_id(obj TSRMLS_CC)) &&
(retval = php_mongo_dbref_create(obj, Z_STRVAL_P(c->name), NULL TSRMLS_CC))
) {
RETURN_ZVAL(retval, 0, 1);
}
RETURN_NULL();
}
/* }}} */
/* {{{ proto array MongoCollection::getDBRef(array dbref)
Retrieves the document referenced by $dbref */
PHP_METHOD(MongoCollection, getDBRef)
{
zval *ref;
mongo_collection *c;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &ref) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(1, ref);
PHP_MONGO_GET_COLLECTION(getThis());
MONGO_METHOD2(MongoDBRef, get, return_value, NULL, c->parent, ref);
}
/* }}} */
static void replace_dots(char *key, int key_len)
{
int i;
for (i = 0; i < key_len; i++) {
if (key[i] == '.') {
key[i] = '_';
}
}
}
static char *to_index_string(zval *zkeys, int *key_len TSRMLS_DC)
{
smart_str str = { NULL, 0, 0 };
switch (Z_TYPE_P(zkeys)) {
case IS_ARRAY:
case IS_OBJECT: {
HashTable *hindex = HASH_P(zkeys);
HashPosition pointer;
zval **data;
char *key;
uint index_key_len, first = 1, key_type;
ulong index;
for (
zend_hash_internal_pointer_reset_ex(hindex, &pointer);
zend_hash_get_current_data_ex(hindex, (void**)&data, &pointer) == SUCCESS;
zend_hash_move_forward_ex(hindex, &pointer)
) {
if (!first) {
smart_str_appendc(&str, '_');
}
first = 0;
key_type = zend_hash_get_current_key_ex(hindex, &key, &index_key_len, &index, NO_DUP, &pointer);
switch (key_type) {
case HASH_KEY_IS_STRING:
smart_str_appendl(&str, key, index_key_len - 1);
break;
case HASH_KEY_IS_LONG:
smart_str_append_long(&str, (long) index);
break;
default:
continue;
}
smart_str_appendc(&str, '_');
switch (Z_TYPE_PP(data)) {
case IS_STRING:
smart_str_appendl(&str, Z_STRVAL_PP(data), Z_STRLEN_PP(data));
break;
case IS_BOOL:
/* -1 cannot be expressed as a boolean, so the order is
* is ascending. Emit a notice if boolean false is used,
* since users may expect a descending order. */
if (Z_BVAL_PP(data) == 0) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Boolean false ordering is ascending");
}
smart_str_append_long(&str, 1);
break;
case IS_LONG:
smart_str_append_long(&str, Z_LVAL_PP(data) < 0 ? -1 : 1);
break;
case IS_DOUBLE:
smart_str_append_long(&str, Z_DVAL_PP(data) < 0 ? -1 : 1);
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Key orderings must be scalar; %s given", zend_get_type_by_const(Z_TYPE_PP(data)));
/* -1 cannot be expressed as a non-scalar, so the order
* is ascending. While the server will accept a null
* ordering, arrays and objects should be rejected. */
smart_str_append_long(&str, 1);
}
}
} break;
case IS_STRING: {
smart_str_appendl(&str, Z_STRVAL_P(zkeys), Z_STRLEN_P(zkeys));
smart_str_appendl(&str, "_1", 2);
} break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The key needs to be either a string or an array");
return NULL;
}
smart_str_0(&str);
replace_dots(str.c, str.len);
if (key_len) {
*key_len = str.len;
}
return str.c;
}
/* {{{ proto protected static string MongoCollection::toIndexString(array|string keys)
Converts $keys to an identifying string for an index */
PHP_METHOD(MongoCollection, toIndexString)
{
zval *zkeys;
char *key_str;
int key_str_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zkeys) == FAILURE) {
return;
}
key_str = to_index_string(zkeys, &key_str_len TSRMLS_CC);
if (key_str) {
RETVAL_STRING(key_str, 0);
} else {
return;
}
}
/* }}} */
void php_mongodb_aggregate(zval *pipeline, zval *options, mongo_db *db, mongo_collection *collection, zval *return_value TSRMLS_DC)
{
int original_rp;
zval **op;
zval *cmd;
zval *retval;
mongo_connection *connection;
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "aggregate", collection->name);
add_assoc_zval(cmd, "pipeline", pipeline);
zval_add_ref(&collection->name);
zval_add_ref(&pipeline);
original_rp = collection->read_pref.type;
zend_hash_internal_pointer_reset(HASH_OF(pipeline));
while (zend_hash_get_current_data(HASH_OF(pipeline), (void **)&op) == SUCCESS) {
if (zend_symtable_exists(Z_ARRVAL_PP(op), "$out", strlen("$out") + 1)) {
if (collection->read_pref.type > MONGO_RP_PRIMARY_PREFERRED) {
mongo_manager_log(MonGlo(manager), MLOG_RS, MLOG_WARN, "Forcing aggregate with $out to run on primary");
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Forcing aggregate with $out to run on primary");
collection->read_pref.type = MONGO_RP_PRIMARY;
break;
}
}
zend_hash_move_forward(HASH_OF(pipeline));
}
zend_hash_internal_pointer_reset(HASH_OF(pipeline));
if (options) {
zval *temp;
zend_hash_merge(HASH_P(cmd), HASH_P(options), (void (*)(void*))zval_add_ref, &temp, sizeof(zval*), 1);
}
retval = php_mongo_runcommand(collection->link, &collection->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, &connection TSRMLS_CC);
if (retval && php_mongo_trigger_error_on_command_failure(connection, retval TSRMLS_CC) == SUCCESS) {
RETVAL_ZVAL(retval, 0, 1);
}
collection->read_pref.type = original_rp;
zval_ptr_dtor(&cmd);
}
/* {{{ proto array MongoCollection::aggregate(array pipeline [, array options ])
proto array MongoCollection::aggregate(array op [, array op [, array op]])
Wrapper for aggregate command. The pipeline may be specified as a single
array of operations or a variable number of operation arguments. Returns the
database response for the command. Aggregation results will be stored in the
"result" key of the response. */
PHP_METHOD(MongoCollection, aggregate)
{
zval ***argv, *pipeline, *tmp, *options = NULL;
int argc, i;
mongo_collection *collection;
mongo_db *db;
collection = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(collection->ns, MongoCollection);
PHP_MONGO_GET_DB(collection->parent);
if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "a|a", &pipeline, &options) == SUCCESS) {
if (php_mongo_is_numeric_array(pipeline TSRMLS_CC) == SUCCESS) {
php_mongodb_aggregate(pipeline, options, db, collection, return_value TSRMLS_CC);
return;
}
/* If its not numeric array then we have $pipe, $pipe, $pipe, $pipe, ... */
}
/* array, array, array, array, .... */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &argv, &argc) == FAILURE) {
return;
}
for (i = 0; i < argc; i++) {
tmp = *argv[i];
if (Z_TYPE_P(tmp) != IS_ARRAY) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument %d is not an array", i + 1);
efree(argv);
return;
}
}
MAKE_STD_ZVAL(pipeline);
array_init(pipeline);
for (i = 0; i < argc; i++) {
tmp = *argv[i];
Z_ADDREF_P(tmp);
if (zend_hash_next_index_insert(Z_ARRVAL_P(pipeline), &tmp, sizeof(zval*), NULL) == FAILURE) {
Z_DELREF_P(tmp);
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create pipeline array");
efree(argv);
RETURN_FALSE;
}
if (zend_symtable_exists(Z_ARRVAL_P(tmp), "$out", strlen("$out") + 1)) {
if (collection->read_pref.type > MONGO_RP_PRIMARY_PREFERRED) {
mongo_manager_log(MonGlo(manager), MLOG_RS, MLOG_WARN, "Forcing aggregate with $out to run on primary");
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Forcing aggregate with $out to run on primary");
collection->read_pref.type = MONGO_RP_PRIMARY;
}
}
}
php_mongodb_aggregate(pipeline, NULL, db, collection, return_value TSRMLS_CC);
zval_ptr_dtor(&pipeline);
efree(argv);
}
/* }}} */
static zval* create_aggregate_command_from_pipeline(char *collname, zval *pipeline, zval *options TSRMLS_DC)
{
zval *command;
MAKE_STD_ZVAL(command);
array_init(command);
/* Command entry */
add_assoc_string(command, "aggregate", collname, 1);
/* Pipeline */
add_assoc_zval(command, "pipeline", pipeline);
Z_ADDREF_P(pipeline);
if (options) {
zval *temp;
zend_hash_merge(HASH_P(command), HASH_P(options), (void (*)(void*))zval_add_ref, &temp, sizeof(zval*), 1);
}
/* Make sure we have a cursor/batchSize object, if this fails,
* EG(exception) is set. */
if (!php_mongo_enforce_batch_size_on_command(command, MONGO_DEFAULT_COMMAND_BATCH_SIZE TSRMLS_CC)) {
zval_ptr_dtor(&command);
return NULL;
}
return command;
}
/* {{{ proto MongoCommandCursor MongoCollection::aggregateCursor(array pipeline [, array options ]])
Returns a command cursor after running the specified aggregation pipeline. */
PHP_METHOD(MongoCollection, aggregateCursor)
{
zval *pipeline = NULL, *options = NULL, *command = NULL;
mongo_collection *c;
mongo_cursor *cmd_cursor;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|a", &pipeline, &options) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
command = create_aggregate_command_from_pipeline(Z_STRVAL_P(c->name), pipeline, options TSRMLS_CC);
if (!command) {
return;
}
object_init_ex(return_value, mongo_ce_CommandCursor);
cmd_cursor = (mongo_cursor*)zend_object_store_get_object(return_value TSRMLS_CC);
mongo_command_cursor_init(cmd_cursor, Z_STRVAL_P(c->ns), c->link, command TSRMLS_CC);
zval_ptr_dtor(&command);
/* Add read preferences to cursor, overriding the one set on the link */
mongo_read_preference_replace(&c->read_pref, &cmd_cursor->read_pref);
}
/* }}} */
/* {{{ proto array MongoCollection::distinct(string key [, array query])
Wrapper for distinct command. Returns a list of distinct values for the given
key across a collection. An optional $query may be applied to filter the
documents considered. */
PHP_METHOD(MongoCollection, distinct)
{
char *key;
int key_len;
zval *cmd, **values, *tmp, *query = NULL;
mongo_collection *c;
mongo_db *db;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a!", &key, &key_len, &query) == FAILURE) {
return;
}
c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
MONGO_CHECK_INITIALIZED(c->ns, MongoCollection);
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "distinct", c->name);
zval_add_ref(&c->name);
add_assoc_stringl(cmd, "key", key, key_len, 1);
if (query) {
add_assoc_zval(cmd, "query", query);
zval_add_ref(&query);
}
tmp = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, NULL TSRMLS_CC);
zval_ptr_dtor(&cmd);
if (!tmp) {
/* Exception thrown */
return;
}
if (zend_hash_find(Z_ARRVAL_P(tmp), "values", strlen("values") + 1, (void **)&values) == SUCCESS) {
#ifdef array_init_size
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_PP(values)));
#else
array_init(return_value);
#endif
zend_hash_copy(Z_ARRVAL_P(return_value), Z_ARRVAL_PP(values), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *));
} else {
RETVAL_FALSE;
}
zval_ptr_dtor(&tmp);
}
/* }}} */
/* {{{ proto array MongoCollection::group(mixed keys, array initial, MongoCode reduce [, array options])
Wrapper for group command. Returns the database response for the command.
Aggregation results will be stored in the "retval" key of the response. */
PHP_METHOD(MongoCollection, group)
{
zval *key, *initial, *options = 0, *group, *cmd, *reduce;
zval *retval;
zval **maxtimems = 0;
mongo_connection *used_connection;
mongo_collection *c = (mongo_collection*)zend_object_store_get_object(getThis() TSRMLS_CC);
mongo_db *db;
MONGO_CHECK_INITIALIZED(c->ns, MongoCollection);
PHP_MONGO_GET_DB(c->parent);
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zzz|z", &key, &initial, &reduce, &options) == FAILURE) {
return;
}
MUST_BE_ARRAY_OR_OBJECT(4, options);
if (Z_TYPE_P(reduce) == IS_STRING) {
zval *code;
MAKE_STD_ZVAL(code);
object_init_ex(code, mongo_ce_Code);
MONGO_METHOD1(MongoCode, __construct, return_value, code, reduce);
reduce = code;
} else {
zval_add_ref(&reduce);
}
MAKE_STD_ZVAL(group);
array_init(group);
add_assoc_zval(group, "ns", c->name);
zval_add_ref(&c->name);
add_assoc_zval(group, "$reduce", reduce);
zval_add_ref(&reduce);
if (Z_TYPE_P(key) == IS_OBJECT && Z_OBJCE_P(key) == mongo_ce_Code) {
add_assoc_zval(group, "$keyf", key);
} else if (IS_ARRAY_OR_OBJECT_P(key)) {
add_assoc_zval(group, "key", key);
} else {
zval_ptr_dtor(&group);
zval_ptr_dtor(&reduce);
zend_throw_exception(mongo_ce_Exception, "MongoCollection::group takes an array, object, or MongoCode key", 0 TSRMLS_CC);
return;
}
zval_add_ref(&key);
/* options used to just be "condition" but now can be "condition" or
* "finalize" */
if (options) {
zval **condition = 0, **finalize = 0;
/* new case */
if (zend_hash_find(HASH_P(options), "condition", strlen("condition") + 1, (void**)&condition) == SUCCESS) {
add_assoc_zval(group, "cond", *condition);
zval_add_ref(condition);
}
if (zend_hash_find(HASH_P(options), "finalize", strlen("finalize") + 1, (void**)&finalize) == SUCCESS) {
add_assoc_zval(group, "finalize", *finalize);
zval_add_ref(finalize);
}
/* The maxTimeMS option needs to be added to the cmd object, not group.
* Check now, but add it to cmd later if the pointer is not null. */
zend_hash_find(HASH_P(options), "maxTimeMS", strlen("maxTimeMS") + 1, (void**)&maxtimems);
if (!condition && !finalize && !maxtimems) {
php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "Implicitly passing condition as $options will be removed in the future");
add_assoc_zval(group, "cond", options);
zval_add_ref(&options);
}
}
add_assoc_zval(group, "initial", initial);
zval_add_ref(&initial);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "group", group);
if (maxtimems) {
add_assoc_zval(cmd, "maxTimeMS", *maxtimems);
zval_add_ref(maxtimems);
}
retval = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, NULL, 0, &used_connection TSRMLS_CC);
if (retval && php_mongo_trigger_error_on_command_failure(used_connection, retval TSRMLS_CC) == FAILURE) {
RETVAL_FALSE;
}
zval_ptr_dtor(&cmd);
zval_ptr_dtor(&reduce);
if (retval) {
RETURN_ZVAL(retval, 0, 1);
}
}
/* }}} */
/* {{{ proto MongoCollection MongoCollection::__get(string name)
Appends this collection name with a period and $name and returns a new
MongoCollection for the combined string. This is used to allow for concisely
selecting collections with dotted names. */
PHP_METHOD(MongoCollection, __get)
{
/* This is a little trickier than the getters in Mongo and MongoDB... we
* need to combine the current collection name with the parameter passed
* in, get the parent db, then select the new collection from it. */
zval *collection;
char *full_name, *name;
int full_name_len, name_len;
mongo_collection *c;
PHP_MONGO_GET_COLLECTION(getThis());
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
return;
}
/* If this is "db", return the parent database. This can't actually be a
* property of the obj because apache does weird things on object
* destruction that will cause the link to be destroyed twice. */
if (strcmp(name, "db") == 0) {
RETURN_ZVAL(c->parent, 1, 0);
}
full_name_len = spprintf(&full_name, 0, "%s.%s", Z_STRVAL_P(c->name), name);
/* select this collection */
collection = php_mongo_db_selectcollection(c->parent, full_name, full_name_len TSRMLS_CC);
if (collection) {
/* Only copy the zval into return_value if it worked. If collection is
* NULL here, an exception is set */
RETVAL_ZVAL(collection, 0, 1);
}
efree(full_name);
}
/* }}} */
/* {{{ proto array MongoCollection::parallelCollectionScan(int num_cursors [, array options])
Returns an array of a maximum of num_cursors MongoCommandCursor objects. */
PHP_METHOD(MongoCollection, parallelCollectionScan)
{
zval *options = NULL;
long num_cursors = 0;
mongo_db *db;
mongo_connection *connection;
mongo_collection *c;
zval *cmd, *document;
zval **cursor_desc;
mongo_cursor *cmd_cursor;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|a", &num_cursors, &options) == FAILURE) {
return;
}
PHP_MONGO_GET_COLLECTION(getThis());
PHP_MONGO_GET_DB(c->parent);
MAKE_STD_ZVAL(cmd);
array_init(cmd);
add_assoc_zval(cmd, "parallelCollectionScan", c->name);
add_assoc_long(cmd, "numCursors", num_cursors);
zval_add_ref(&c->name);
document = php_mongo_runcommand(c->link, &c->read_pref, Z_STRVAL_P(db->name), Z_STRLEN_P(db->name), cmd, options, 0, &connection TSRMLS_CC);
zval_ptr_dtor(&cmd);
if (!document) {
return;
}
if (php_mongo_trigger_error_on_command_failure(connection, document TSRMLS_CC) == FAILURE) {
zval_ptr_dtor(&document);
return;
}
if (zend_hash_find(Z_ARRVAL_P(document), "cursors", sizeof("cursors"), (void **)&cursor_desc) == FAILURE || Z_TYPE_PP(cursor_desc) != IS_ARRAY) {
zend_throw_exception_ex(mongo_ce_CursorException, 30 TSRMLS_CC, "Cursor command response does not have the expected structure");
zval_ptr_dtor(&document);
return;
}
{
HashPosition pointer;
zval **cursor_doc;
array_init(return_value);
for (
zend_hash_internal_pointer_reset_ex(Z_ARRVAL_PP(cursor_desc), &pointer);
zend_hash_get_current_data_ex(Z_ARRVAL_PP(cursor_desc), (void**)&cursor_doc, &pointer) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_PP(cursor_desc), &pointer)
) {
zval *zcursor, **cursor_element;
if (Z_TYPE_PP(cursor_doc) != IS_ARRAY) {
continue;
}
if (zend_hash_find(Z_ARRVAL_PP(cursor_doc), "cursor", sizeof("cursor"), (void **)&cursor_element) == FAILURE || Z_TYPE_PP(cursor_element) != IS_ARRAY) {
zend_throw_exception_ex(mongo_ce_Exception, 34 TSRMLS_CC, "Cursor structure is invalid");
zval_ptr_dtor(&document);
return;
}
MAKE_STD_ZVAL(zcursor);
object_init_ex(zcursor, mongo_ce_CommandCursor);
cmd_cursor = (mongo_cursor*)zend_object_store_get_object(zcursor TSRMLS_CC);
php_mongo_command_cursor_init_from_document(c->link, cmd_cursor, connection->hash, *cursor_element TSRMLS_CC);
add_next_index_zval(return_value, zcursor);
}
}
zval_ptr_dtor(&document);
}
/* }}} */
ZEND_BEGIN_ARG_INFO_EX(arginfo___construct, 0, ZEND_RETURN_VALUE, 2)
ZEND_ARG_OBJ_INFO(0, database, MongoDB, 0)
ZEND_ARG_INFO(0, collection_name)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_distinct, 0, 0, 1)
ZEND_ARG_INFO(0, key)
ZEND_ARG_INFO(0, query)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_no_parameters, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo___get, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, name)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_setSlaveOkay, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, slave_okay)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_setReadPreference, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, read_preference)
ZEND_ARG_ARRAY_INFO(0, tags, 0) /* Yes, this should be an array */
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getWriteConcern, 0, ZEND_RETURN_VALUE, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_setWriteConcern, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, w)
ZEND_ARG_INFO(0, wtimeout)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_validate, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, validate)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_insert, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, array_of_fields_OR_object)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_batchInsert, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_ARRAY_INFO(0, documents, 0) /* Array of documents */
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_find, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, query)
ZEND_ARG_INFO(0, fields)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_find_one, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, query)
ZEND_ARG_INFO(0, fields)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_findandmodify, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_ARRAY_INFO(0, query, 1)
ZEND_ARG_ARRAY_INFO(0, update, 1)
ZEND_ARG_ARRAY_INFO(0, fields, 1)
ZEND_ARG_ARRAY_INFO(0, options, 1)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_aggregatecursor, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_ARRAY_INFO(0, pipeline, 1)
ZEND_ARG_ARRAY_INFO(0, options, 1)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_update, 0, ZEND_RETURN_VALUE, 2)
ZEND_ARG_INFO(0, old_array_of_fields_OR_object)
ZEND_ARG_INFO(0, new_array_of_fields_OR_object)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_remove, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, array_of_fields_OR_object)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_createIndex, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, array_of_keys)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_ensureIndex, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, key_OR_array_of_keys)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_deleteIndex, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, string_OR_array_of_keys)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_count, 0, ZEND_RETURN_VALUE, 0)
ZEND_ARG_INFO(0, query_AS_array_of_fields_OR_object)
ZEND_ARG_INFO(0, limit)
ZEND_ARG_INFO(0, skip)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_createDBRef, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, array_with_id_fields_OR_MongoID)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_getDBRef, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, reference)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_toIndexString, 0, ZEND_RETURN_VALUE, 1)
ZEND_ARG_INFO(0, string_OR_array_of_keys)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_group, 0, ZEND_RETURN_VALUE, 3)
ZEND_ARG_INFO(0, keys_or_MongoCode)
ZEND_ARG_INFO(0, initial_value)
ZEND_ARG_INFO(0, array_OR_MongoCode)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_aggregate, 0, 0, 1)
ZEND_ARG_INFO(0, pipeline)
ZEND_ARG_INFO(0, op)
ZEND_ARG_INFO(0, ...)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_parallelcollectionscan, 0, 0, 1)
ZEND_ARG_INFO(0, num_cursors)
ZEND_ARG_ARRAY_INFO(0, options, 0)
ZEND_END_ARG_INFO()
static zend_function_entry MongoCollection_methods[] = {
PHP_ME(MongoCollection, __construct, arginfo___construct, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, __toString, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, __get, arginfo___get, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, getName, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, getSlaveOkay, arginfo_no_parameters, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
PHP_ME(MongoCollection, setSlaveOkay, arginfo_setSlaveOkay, ZEND_ACC_PUBLIC|ZEND_ACC_DEPRECATED)
PHP_ME(MongoCollection, getReadPreference, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, setReadPreference, arginfo_setReadPreference, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, getWriteConcern, arginfo_getWriteConcern, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, setWriteConcern, arginfo_setWriteConcern, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, drop, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, validate, arginfo_validate, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, insert, arginfo_insert, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, batchInsert, arginfo_batchInsert, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, update, arginfo_update, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, remove, arginfo_remove, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, find, arginfo_find, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, findOne, arginfo_find_one, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, findAndModify, arginfo_findandmodify, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, createIndex, arginfo_createIndex, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, ensureIndex, arginfo_ensureIndex, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, deleteIndex, arginfo_deleteIndex, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, deleteIndexes, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, getIndexInfo, arginfo_no_parameters, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, count, arginfo_count, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, save, arginfo_insert, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, createDBRef, arginfo_createDBRef, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, getDBRef, arginfo_getDBRef, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, toIndexString, arginfo_toIndexString, ZEND_ACC_PROTECTED|ZEND_ACC_DEPRECATED|ZEND_ACC_STATIC)
PHP_ME(MongoCollection, group, arginfo_group, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, distinct, arginfo_distinct, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, aggregate, arginfo_aggregate, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, aggregateCursor, arginfo_aggregatecursor, ZEND_ACC_PUBLIC)
PHP_ME(MongoCollection, parallelCollectionScan, arginfo_parallelcollectionscan, ZEND_ACC_PUBLIC)
PHP_FE_END
};
static void php_mongo_collection_free(void *object TSRMLS_DC)
{
mongo_collection *c = (mongo_collection*)object;
if (c) {
if (c->parent) {
zval_ptr_dtor(&c->parent);
}
if (c->link) {
zval_ptr_dtor(&c->link);
}
if (c->name) {
zval_ptr_dtor(&c->name);
}
if (c->ns) {
zval_ptr_dtor(&c->ns);
}
mongo_read_preference_dtor(&c->read_pref);
zend_object_std_dtor(&c->std TSRMLS_CC);
efree(c);
}
}
/* {{{ php_mongo_collection_new
*/
zend_object_value php_mongo_collection_new(zend_class_entry *class_type TSRMLS_DC) {
PHP_MONGO_OBJ_NEW(mongo_collection);
}
/* }}} */
void mongo_init_MongoCollection(TSRMLS_D) {
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "MongoCollection", MongoCollection_methods);
ce.create_object = php_mongo_collection_new;
mongo_ce_Collection = zend_register_internal_class(&ce TSRMLS_CC);
zend_declare_class_constant_long(mongo_ce_Collection, "ASCENDING", strlen("ASCENDING"), 1 TSRMLS_CC);
zend_declare_class_constant_long(mongo_ce_Collection, "DESCENDING", strlen("DESCENDING"), -1 TSRMLS_CC);
zend_declare_property_long(mongo_ce_Collection, "w", strlen("w"), 1, ZEND_ACC_PUBLIC TSRMLS_CC);
zend_declare_property_long(mongo_ce_Collection, "wtimeout", strlen("wtimeout"), PHP_MONGO_DEFAULT_WTIMEOUT, ZEND_ACC_PUBLIC TSRMLS_CC);
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: fdm=marker
* vim: noet sw=4 ts=4
*/
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化