00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 """Database level operations."""
00016
00017 import warnings
00018
00019 from bson.code import Code
00020 from bson.dbref import DBRef
00021 from bson.son import SON
00022 from pymongo import helpers
00023 from pymongo.collection import Collection
00024 from pymongo.errors import (CollectionInvalid,
00025 InvalidName,
00026 OperationFailure)
00027 from pymongo.son_manipulator import ObjectIdInjector
00028
00029
00030 def _check_name(name):
00031 """Check if a database name is valid.
00032 """
00033 if not name:
00034 raise InvalidName("database name cannot be the empty string")
00035
00036 for invalid_char in [" ", ".", "$", "/", "\\"]:
00037 if invalid_char in name:
00038 raise InvalidName("database names cannot contain the "
00039 "character %r" % invalid_char)
00040
00041
00042 class Database(object):
00043 """A Mongo database.
00044 """
00045
00046 def __init__(self, connection, name):
00047 """Get a database by connection and name.
00048
00049 Raises :class:`TypeError` if `name` is not an instance of
00050 :class:`basestring`. Raises
00051 :class:`~pymongo.errors.InvalidName` if `name` is not a valid
00052 database name.
00053
00054 :Parameters:
00055 - `connection`: a :class:`~pymongo.connection.Connection`
00056 instance
00057 - `name`: database name
00058
00059 .. mongodoc:: databases
00060 """
00061 if not isinstance(name, basestring):
00062 raise TypeError("name must be an instance of basestring")
00063
00064 _check_name(name)
00065
00066 self.__name = unicode(name)
00067 self.__connection = connection
00068
00069 self.__incoming_manipulators = []
00070 self.__incoming_copying_manipulators = []
00071 self.__outgoing_manipulators = []
00072 self.__outgoing_copying_manipulators = []
00073 self.add_son_manipulator(ObjectIdInjector())
00074 self.__system_js = SystemJS(self)
00075
00076 def add_son_manipulator(self, manipulator):
00077 """Add a new son manipulator to this database.
00078
00079 Newly added manipulators will be applied before existing ones.
00080
00081 :Parameters:
00082 - `manipulator`: the manipulator to add
00083 """
00084 def method_overwritten(instance, method):
00085 return getattr(instance, method) != \
00086 getattr(super(instance.__class__, instance), method)
00087
00088 if manipulator.will_copy():
00089 if method_overwritten(manipulator, "transform_incoming"):
00090 self.__incoming_copying_manipulators.insert(0, manipulator)
00091 if method_overwritten(manipulator, "transform_outgoing"):
00092 self.__outgoing_copying_manipulators.insert(0, manipulator)
00093 else:
00094 if method_overwritten(manipulator, "transform_incoming"):
00095 self.__incoming_manipulators.insert(0, manipulator)
00096 if method_overwritten(manipulator, "transform_outgoing"):
00097 self.__outgoing_manipulators.insert(0, manipulator)
00098
00099 @property
00100 def system_js(self):
00101 """A :class:`SystemJS` helper for this :class:`Database`.
00102
00103 See the documentation for :class:`SystemJS` for more details.
00104
00105 .. versionadded:: 1.5
00106 """
00107 return self.__system_js
00108
00109 @property
00110 def connection(self):
00111 """The :class:`~pymongo.connection.Connection` instance for this
00112 :class:`Database`.
00113
00114 .. versionchanged:: 1.3
00115 ``connection`` is now a property rather than a method.
00116 """
00117 return self.__connection
00118
00119 @property
00120 def name(self):
00121 """The name of this :class:`Database`.
00122
00123 .. versionchanged:: 1.3
00124 ``name`` is now a property rather than a method.
00125 """
00126 return self.__name
00127
00128 def __cmp__(self, other):
00129 if isinstance(other, Database):
00130 return cmp((self.__connection, self.__name),
00131 (other.__connection, other.__name))
00132 return NotImplemented
00133
00134 def __repr__(self):
00135 return "Database(%r, %r)" % (self.__connection, self.__name)
00136
00137 def __getattr__(self, name):
00138 """Get a collection of this database by name.
00139
00140 Raises InvalidName if an invalid collection name is used.
00141
00142 :Parameters:
00143 - `name`: the name of the collection to get
00144 """
00145 return Collection(self, name)
00146
00147 def __getitem__(self, name):
00148 """Get a collection of this database by name.
00149
00150 Raises InvalidName if an invalid collection name is used.
00151
00152 :Parameters:
00153 - `name`: the name of the collection to get
00154 """
00155 return self.__getattr__(name)
00156
00157 def create_collection(self, name, options=None, **kwargs):
00158 """Create a new :class:`~pymongo.collection.Collection` in this
00159 database.
00160
00161 Normally collection creation is automatic. This method should
00162 only be used to specify options on
00163 creation. :class:`~pymongo.errors.CollectionInvalid` will be
00164 raised if the collection already exists.
00165
00166 Options should be passed as keyword arguments to this
00167 method. Any of the following options are valid:
00168
00169 - "size": desired initial size for the collection (in
00170 bytes). must be less than or equal to 10000000000. For
00171 capped collections this size is the max size of the
00172 collection.
00173 - "capped": if True, this is a capped collection
00174 - "max": maximum number of objects if capped (optional)
00175
00176 :Parameters:
00177 - `name`: the name of the collection to create
00178 - `options`: DEPRECATED options to use on the new collection
00179 - `**kwargs` (optional): additional keyword arguments will
00180 be passed as options for the create collection command
00181
00182 .. versionchanged:: 1.5
00183 deprecating `options` in favor of kwargs
00184 """
00185 opts = {"create": True}
00186 if options is not None:
00187 warnings.warn("the options argument to create_collection is "
00188 "deprecated and will be removed. please use "
00189 "kwargs instead.", DeprecationWarning)
00190 opts.update(options)
00191 opts.update(kwargs)
00192
00193 if name in self.collection_names():
00194 raise CollectionInvalid("collection %s already exists" % name)
00195
00196 return Collection(self, name, **opts)
00197
00198 def _fix_incoming(self, son, collection):
00199 """Apply manipulators to an incoming SON object before it gets stored.
00200
00201 :Parameters:
00202 - `son`: the son object going into the database
00203 - `collection`: the collection the son object is being saved in
00204 """
00205 for manipulator in self.__incoming_manipulators:
00206 son = manipulator.transform_incoming(son, collection)
00207 for manipulator in self.__incoming_copying_manipulators:
00208 son = manipulator.transform_incoming(son, collection)
00209 return son
00210
00211 def _fix_outgoing(self, son, collection):
00212 """Apply manipulators to a SON object as it comes out of the database.
00213
00214 :Parameters:
00215 - `son`: the son object coming out of the database
00216 - `collection`: the collection the son object was saved in
00217 """
00218 for manipulator in reversed(self.__outgoing_manipulators):
00219 son = manipulator.transform_outgoing(son, collection)
00220 for manipulator in reversed(self.__outgoing_copying_manipulators):
00221 son = manipulator.transform_outgoing(son, collection)
00222 return son
00223
00224 def command(self, command, value=1,
00225 check=True, allowable_errors=[], **kwargs):
00226 """Issue a MongoDB command.
00227
00228 Send command `command` to the database and return the
00229 response. If `command` is an instance of :class:`basestring`
00230 then the command {`command`: `value`} will be sent. Otherwise,
00231 `command` must be an instance of :class:`dict` and will be
00232 sent as is.
00233
00234 Any additional keyword arguments will be added to the final
00235 command document before it is sent.
00236
00237 For example, a command like ``{buildinfo: 1}`` can be sent
00238 using:
00239
00240 >>> db.command("buildinfo")
00241
00242 For a command where the value matters, like ``{collstats:
00243 collection_name}`` we can do:
00244
00245 >>> db.command("collstats", collection_name)
00246
00247 For commands that take additional arguments we can use
00248 kwargs. So ``{filemd5: object_id, root: file_root}`` becomes:
00249
00250 >>> db.command("filemd5", object_id, root=file_root)
00251
00252 :Parameters:
00253 - `command`: document representing the command to be issued,
00254 or the name of the command (for simple commands only).
00255
00256 .. note:: the order of keys in the `command` document is
00257 significant (the "verb" must come first), so commands
00258 which require multiple keys (e.g. `findandmodify`)
00259 should use an instance of :class:`~bson.son.SON` or
00260 a string and kwargs instead of a Python `dict`.
00261
00262 - `value` (optional): value to use for the command verb when
00263 `command` is passed as a string
00264 - `check` (optional): check the response for errors, raising
00265 :class:`~pymongo.errors.OperationFailure` if there are any
00266 - `allowable_errors`: if `check` is ``True``, error messages
00267 in this list will be ignored by error-checking
00268 - `**kwargs` (optional): additional keyword arguments will
00269 be added to the command document before it is sent
00270
00271 .. versionchanged:: 1.6
00272 Added the `value` argument for string commands, and keyword
00273 arguments for additional command options.
00274 .. versionchanged:: 1.5
00275 `command` can be a string in addition to a full document.
00276 .. versionadded:: 1.4
00277
00278 .. mongodoc:: commands
00279 """
00280
00281 if isinstance(command, basestring):
00282 command = SON([(command, value)])
00283
00284 command.update(kwargs)
00285
00286 result = self["$cmd"].find_one(command,
00287 _must_use_master=True,
00288 _is_command=True)
00289
00290 if check:
00291 msg = "command %r failed: %%s" % command
00292 helpers._check_command_response(result, self.connection.disconnect,
00293 msg, allowable_errors)
00294
00295 return result
00296
00297 def collection_names(self):
00298 """Get a list of all the collection names in this database.
00299 """
00300 results = self["system.namespaces"].find(_must_use_master=True)
00301 names = [r["name"] for r in results]
00302 names = [n[len(self.__name) + 1:] for n in names
00303 if n.startswith(self.__name + ".")]
00304 names = [n for n in names if "$" not in n]
00305 return names
00306
00307 def drop_collection(self, name_or_collection):
00308 """Drop a collection.
00309
00310 :Parameters:
00311 - `name_or_collection`: the name of a collection to drop or the
00312 collection object itself
00313 """
00314 name = name_or_collection
00315 if isinstance(name, Collection):
00316 name = name.name
00317
00318 if not isinstance(name, basestring):
00319 raise TypeError("name_or_collection must be an instance of "
00320 "(Collection, str, unicode)")
00321
00322 self.__connection._purge_index(self.__name, name)
00323
00324 self.command("drop", unicode(name), allowable_errors=["ns not found"])
00325
00326 def validate_collection(self, name_or_collection):
00327 """Validate a collection.
00328
00329 Returns a string of validation info. Raises CollectionInvalid if
00330 validation fails.
00331 """
00332 name = name_or_collection
00333 if isinstance(name, Collection):
00334 name = name.name
00335
00336 if not isinstance(name, basestring):
00337 raise TypeError("name_or_collection must be an instance of "
00338 "(Collection, str, unicode)")
00339
00340 result = self.command("validate", unicode(name))
00341
00342 info = result["result"]
00343 if info.find("exception") != -1 or info.find("corrupt") != -1:
00344 raise CollectionInvalid("%s invalid: %s" % (name, info))
00345 return info
00346
00347 def profiling_level(self):
00348 """Get the database's current profiling level.
00349
00350 Returns one of (:data:`~pymongo.OFF`,
00351 :data:`~pymongo.SLOW_ONLY`, :data:`~pymongo.ALL`).
00352
00353 .. mongodoc:: profiling
00354 """
00355 result = self.command("profile", -1)
00356
00357 assert result["was"] >= 0 and result["was"] <= 2
00358 return result["was"]
00359
00360 def set_profiling_level(self, level):
00361 """Set the database's profiling level.
00362
00363 Raises :class:`ValueError` if level is not one of
00364 (:data:`~pymongo.OFF`, :data:`~pymongo.SLOW_ONLY`,
00365 :data:`~pymongo.ALL`).
00366
00367 :Parameters:
00368 - `level`: the profiling level to use
00369
00370 .. mongodoc:: profiling
00371 """
00372 if not isinstance(level, int) or level < 0 or level > 2:
00373 raise ValueError("level must be one of (OFF, SLOW_ONLY, ALL)")
00374
00375 self.command("profile", level)
00376
00377 def profiling_info(self):
00378 """Returns a list containing current profiling information.
00379
00380 .. mongodoc:: profiling
00381 """
00382 return list(self["system.profile"].find())
00383
00384 def error(self):
00385 """Get a database error if one occured on the last operation.
00386
00387 Return None if the last operation was error-free. Otherwise return the
00388 error that occurred.
00389 """
00390 error = self.command("getlasterror")
00391 if error.get("err", 0) is None:
00392 return None
00393 if error["err"] == "not master":
00394 self.__connection.disconnect()
00395 return error
00396
00397 def last_status(self):
00398 """Get status information from the last operation.
00399
00400 Returns a SON object with status information.
00401 """
00402 return self.command("getlasterror")
00403
00404 def previous_error(self):
00405 """Get the most recent error to have occurred on this database.
00406
00407 Only returns errors that have occurred since the last call to
00408 `Database.reset_error_history`. Returns None if no such errors have
00409 occurred.
00410 """
00411 error = self.command("getpreverror")
00412 if error.get("err", 0) is None:
00413 return None
00414 return error
00415
00416 def reset_error_history(self):
00417 """Reset the error history of this database.
00418
00419 Calls to `Database.previous_error` will only return errors that have
00420 occurred since the most recent call to this method.
00421 """
00422 self.command("reseterror")
00423
00424 def __iter__(self):
00425 return self
00426
00427 def next(self):
00428 raise TypeError("'Database' object is not iterable")
00429
00430 def add_user(self, name, password):
00431 """Create user `name` with password `password`.
00432
00433 Add a new user with permissions for this :class:`Database`.
00434
00435 .. note:: Will change the password if user `name` already exists.
00436
00437 :Parameters:
00438 - `name`: the name of the user to create
00439 - `password`: the password of the user to create
00440
00441 .. versionadded:: 1.4
00442 """
00443 pwd = helpers._password_digest(name, password)
00444 self.system.users.update({"user": name},
00445 {"user": name,
00446 "pwd": pwd},
00447 upsert=True, safe=True)
00448
00449 def remove_user(self, name):
00450 """Remove user `name` from this :class:`Database`.
00451
00452 User `name` will no longer have permissions to access this
00453 :class:`Database`.
00454
00455 :Parameters:
00456 - `name`: the name of the user to remove
00457
00458 .. versionadded:: 1.4
00459 """
00460 self.system.users.remove({"user": name}, safe=True)
00461
00462 def authenticate(self, name, password):
00463 """Authenticate to use this database.
00464
00465 Once authenticated, the user has full read and write access to
00466 this database. Raises :class:`TypeError` if either `name` or
00467 `password` is not an instance of ``(str,
00468 unicode)``. Authentication lasts for the life of the database
00469 connection, or until :meth:`logout` is called.
00470
00471 The "admin" database is special. Authenticating on "admin"
00472 gives access to *all* databases. Effectively, "admin" access
00473 means root access to the database.
00474
00475 .. note:: Currently, authentication is per
00476 :class:`~socket.socket`. This means that there are a couple
00477 of situations in which re-authentication is necessary:
00478
00479 - On failover (when an
00480 :class:`~pymongo.errors.AutoReconnect` exception is
00481 raised).
00482
00483 - After a call to
00484 :meth:`~pymongo.connection.Connection.disconnect` or
00485 :meth:`~pymongo.connection.Connection.end_request`.
00486
00487 - When sharing a :class:`~pymongo.connection.Connection`
00488 between multiple threads, each thread will need to
00489 authenticate separately.
00490
00491 .. warning:: Currently, calls to
00492 :meth:`~pymongo.connection.Connection.end_request` will
00493 lead to unpredictable behavior in combination with
00494 auth. The :class:`~socket.socket` owned by the calling
00495 thread will be returned to the pool, so whichever thread
00496 uses that :class:`~socket.socket` next will have whatever
00497 permissions were granted to the calling thread.
00498
00499 :Parameters:
00500 - `name`: the name of the user to authenticate
00501 - `password`: the password of the user to authenticate
00502
00503 .. mongodoc:: authenticate
00504 """
00505 if not isinstance(name, basestring):
00506 raise TypeError("name must be an instance of basestring")
00507 if not isinstance(password, basestring):
00508 raise TypeError("password must be an instance of basestring")
00509
00510 nonce = self.command("getnonce")["nonce"]
00511 key = helpers._auth_key(nonce, name, password)
00512 try:
00513 self.command("authenticate", user=unicode(name),
00514 nonce=nonce, key=key)
00515 return True
00516 except OperationFailure:
00517 return False
00518
00519 def logout(self):
00520 """Deauthorize use of this database for this connection.
00521
00522 Note that other databases may still be authorized.
00523 """
00524 self.command("logout")
00525
00526 def dereference(self, dbref):
00527 """Dereference a :class:`~bson.dbref.DBRef`, getting the
00528 document it points to.
00529
00530 Raises :class:`TypeError` if `dbref` is not an instance of
00531 :class:`~bson.dbref.DBRef`. Returns a document, or ``None`` if
00532 the reference does not point to a valid document. Raises
00533 :class:`ValueError` if `dbref` has a database specified that
00534 is different from the current database.
00535
00536 :Parameters:
00537 - `dbref`: the reference
00538 """
00539 if not isinstance(dbref, DBRef):
00540 raise TypeError("cannot dereference a %s" % type(dbref))
00541 if dbref.database is not None and dbref.database != self.__name:
00542 raise ValueError("trying to dereference a DBRef that points to "
00543 "another database (%r not %r)" % (dbref.database,
00544 self.__name))
00545 return self[dbref.collection].find_one({"_id": dbref.id})
00546
00547 def eval(self, code, *args):
00548 """Evaluate a JavaScript expression in MongoDB.
00549
00550 Useful if you need to touch a lot of data lightly; in such a
00551 scenario the network transfer of the data could be a
00552 bottleneck. The `code` argument must be a JavaScript
00553 function. Additional positional arguments will be passed to
00554 that function when it is run on the server.
00555
00556 Raises :class:`TypeError` if `code` is not an instance of
00557 (str, unicode, `Code`). Raises
00558 :class:`~pymongo.errors.OperationFailure` if the eval
00559 fails. Returns the result of the evaluation.
00560
00561 :Parameters:
00562 - `code`: string representation of JavaScript code to be
00563 evaluated
00564 - `args` (optional): additional positional arguments are
00565 passed to the `code` being evaluated
00566 """
00567 if not isinstance(code, Code):
00568 code = Code(code)
00569
00570 result = self.command("$eval", code, args=args)
00571 return result.get("retval", None)
00572
00573 def __call__(self, *args, **kwargs):
00574 """This is only here so that some API misusages are easier to debug.
00575 """
00576 raise TypeError("'Database' object is not callable. If you meant to "
00577 "call the '%s' method on a 'Connection' object it is "
00578 "failing because no such method exists." % self.__name)
00579
00580
00581 class SystemJS(object):
00582 """Helper class for dealing with stored JavaScript.
00583 """
00584
00585 def __init__(self, database):
00586 """Get a system js helper for the database `database`.
00587
00588 An instance of :class:`SystemJS` is automatically created for
00589 each :class:`Database` instance as :attr:`Database.system_js`,
00590 manual instantiation of this class should not be necessary.
00591
00592 :class:`SystemJS` instances allow for easy manipulation and
00593 access to server-side JavaScript:
00594
00595 .. doctest::
00596
00597 >>> db.system_js.add1 = "function (x) { return x + 1; }"
00598 >>> db.system.js.find({"_id": "add1"}).count()
00599 1
00600 >>> db.system_js.add1(5)
00601 6.0
00602 >>> del db.system_js.add1
00603 >>> db.system.js.find({"_id": "add1"}).count()
00604 0
00605
00606 .. note:: Requires server version **>= 1.1.1**
00607
00608 .. versionadded:: 1.5
00609 """
00610
00611 object.__setattr__(self, "_db", database)
00612
00613 def __setattr__(self, name, code):
00614 self._db.system.js.save({"_id": name, "value": Code(code)}, safe=True)
00615
00616 def __delattr__(self, name):
00617 self._db.system.js.remove({"_id": name}, safe=True)
00618
00619 def __getattr__(self, name):
00620 return lambda *args: self._db.eval("function() { return %s.apply(this,"
00621 "arguments); }" % name, *args)
00622
00623 def list(self):
00624 """Get a list of the names of the functions stored in this database.
00625
00626 .. versionadded:: 1.9
00627 """
00628 return [x["_id"] for x in self._db.system.js.find(fields=["_id"])]