You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2013/11/28 23:59:30 UTC

[1/2] git commit: updated refs/heads/master to bae0b27

Updated Branches:
  refs/heads/master 2b8070875 -> bae0b2730


Fix whitespace


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/bae0b273
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/bae0b273
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/bae0b273

Branch: refs/heads/master
Commit: bae0b2730c2155c5b5f2106da273e51b71d32280
Parents: ca41964
Author: Klaus Trainer <kl...@posteo.de>
Authored: Thu Nov 28 17:40:19 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Thu Nov 28 22:59:13 2013 +0000

----------------------------------------------------------------------
 src/couch_mrview/src/couch_mrview_util.erl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/bae0b273/src/couch_mrview/src/couch_mrview_util.erl
----------------------------------------------------------------------
diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index a3b0581..c4272f8 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -540,7 +540,7 @@ delete_index_file(DbName, Sig) ->
 
 delete_compaction_file(DbName, Sig) ->
     delete_file(compaction_file(DbName, Sig)).
-    
+
 
 delete_file(FName) ->
     case filelib:is_file(FName) of


[2/2] git commit: updated refs/heads/master to bae0b27

Posted by rn...@apache.org.
Extend support for attachment-related query params

Until now, the boolean query parameters `attachments` and
`att_encoding_info` have only been supported for the document API
endpoint (`/{db}/{docid}`).

This extends support for queries to the changes (`/{db}/_changes`) and
view (`/{db}/_design/{ddoc}/_view/{view}`) API endpoints:

* If `include_docs` and `attachments` equal `true`, the Base64-encoded
  contents of attachments are included with the documents in changes or
  view query results, respectively.

* If `include_docs` and `att_encoding_info` equal `true`, encoding
  information is included in attachment stubs if the particular
  attachment is compressed.

Closes COUCHDB-1923.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/ca41964b
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/ca41964b
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/ca41964b

Branch: refs/heads/master
Commit: ca41964b70b96b5fa76504e4001523ed29c718b5
Parents: 2b80708
Author: Klaus Trainer <kl...@posteo.de>
Authored: Fri Nov 15 17:02:20 2013 +0100
Committer: Robert Newson <rn...@apache.org>
Committed: Thu Nov 28 22:59:13 2013 +0000

----------------------------------------------------------------------
 share/doc/src/api/database/changes.rst     | 15 ++++++
 share/doc/src/api/ddoc/views.rst           | 20 +++++++-
 share/doc/src/api/document/common.rst      | 30 +++++++----
 share/www/script/test/attachment_views.js  | 54 +++++++++++++++++---
 share/www/script/test/changes.js           | 68 +++++++++++++++++++++++++
 src/couch_mrview/include/couch_mrview.hrl  |  1 +
 src/couch_mrview/src/couch_mrview_http.erl | 16 ++++++
 src/couch_mrview/src/couch_mrview_util.erl | 22 ++++----
 src/couchdb/couch_changes.erl              | 16 ++++--
 src/couchdb/couch_db.hrl                   |  2 +
 src/couchdb/couch_httpd_db.erl             |  6 +++
 test/etap/073-changes.t                    |  1 +
 12 files changed, 220 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/share/doc/src/api/database/changes.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/api/database/changes.rst b/share/doc/src/api/database/changes.rst
index 2036220..21d44ea 100644
--- a/share/doc/src/api/database/changes.rst
+++ b/share/doc/src/api/database/changes.rst
@@ -59,6 +59,13 @@
   :query boolean include_docs: Include the associated document with each result.
     If there are conflicts, only the winning revision is returned.
     Default is ``false``.
+  :query boolean attachments: Include the Base64-encoded content of
+    :ref:`attachments <api/doc/attachments>` in the documents that are included
+    if `include_docs` is ``true``. Ignored if `include_docs` isn't ``true``.
+    Default is ``false``.
+  :query boolean att_encoding_info: Include encoding information in attachment
+    stubs if `include_docs` is ``true`` and the particular attachment is
+    compressed. Ignored if `include_docs` isn't ``true``. Default is ``false``.
   :query number last-event-id: Alias of `Last-Event-ID` header.
   :query number limit: Limit number of result rows to the specified value
     (note that using ``0`` here has the same effect as ``1``).
@@ -162,6 +169,14 @@
    listen changes since current seq number.
 .. versionchanged:: 1.3.0 ``eventsource`` feed type added.
 .. versionchanged:: 1.4.0 Support ``Last-Event-ID`` header.
+.. versionchanged:: 1.6.0 added ``attachments`` and ``att_encoding_info``
+   parameters
+
+.. warning::
+   Using the ``attachments`` parameter to include attachments in the changes
+   feed is not recommended for large attachment sizes. Also note that the
+   Base64-encoding that is used leads to a 33% overhead (i.e. one third) in
+   transfer size for attachments.
 
 
 .. http:post:: /{db}/_changes

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/share/doc/src/api/ddoc/views.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/api/ddoc/views.rst b/share/doc/src/api/ddoc/views.rst
index 5e56f9c..3d14396 100644
--- a/share/doc/src/api/ddoc/views.rst
+++ b/share/doc/src/api/ddoc/views.rst
@@ -41,8 +41,15 @@
   :query boolean group: Group the results using the reduce function to a group
     or single row. Default is ``false``
   :query number group_level: Specify the group level to be used. *Optional*
-  :query boolean include_docs: Include the full content of the documents in
-    the return. Default is ``false``
+  :query boolean include_docs: Include the associated document with each row.
+    Default is ``false``.
+  :query boolean attachments: Include the Base64-encoded content of
+    :ref:`attachments <api/doc/attachments>` in the documents that are included
+    if `include_docs` is ``true``. Ignored if `include_docs` isn't ``true``.
+    Default is ``false``.
+  :query boolean att_encoding_info: Include encoding information in attachment
+    stubs if `include_docs` is ``true`` and the particular attachment is
+    compressed. Ignored if `include_docs` isn't ``true``. Default is ``false``.
   :query boolean inclusive_end: Specifies whether the specified end key should
     be included in the result. Default is ``true``
   :query string key: Return only documents that match the specified key.
@@ -123,6 +130,15 @@
         "total_rows": 3
     }
 
+.. versionchanged:: 1.6.0 added ``attachments`` and ``att_encoding_info``
+   parameters
+
+.. warning::
+   Using the ``attachments`` parameter to include attachments in view results
+   is not recommended for large attachment sizes. Also note that the
+   Base64-encoding that is used leads to a 33% overhead (i.e. one third) in
+   transfer size for attachments.
+
 
 .. http:post:: /{db}/_design/{ddoc}/_view/{view}
   :synopsis: Returns certain rows for the specified stored view

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/share/doc/src/api/document/common.rst
----------------------------------------------------------------------
diff --git a/share/doc/src/api/document/common.rst b/share/doc/src/api/document/common.rst
index 62775cf..089d856 100644
--- a/share/doc/src/api/document/common.rst
+++ b/share/doc/src/api/document/common.rst
@@ -80,8 +80,8 @@
 
   :query boolean attachments: Includes attachments bodies in response.
     Default is ``false``
-  :query boolean att_encoding_info: Includes encoding information into
-    attachment's stubs for compressed ones. Default is ``false``
+  :query boolean att_encoding_info: Includes encoding information in attachment
+    stubs if the particular attachment is compressed. Default is ``false``.
   :query array atts_since: Includes attachments only since specified revisions.
     Doesn't includes attachments for specified revisions. *Optional*
   :query boolean conflicts: Includes information about conflicts in document.
@@ -396,16 +396,28 @@ information objects with next structure:
 
 - **content_type** (*string*): Attachment MIME type
 - **data** (*string*): Base64-encoded content. Available if attachment content
-  requested by using ``attachments=true`` or ``atts_since`` query parameters
+  is requested by using the following query parameters:
+    - ``attachments=true`` when querying a document
+    - ``attachments=true&include_docs=true`` when querying a
+      :ref:`changes feed <api/db/changes>` or a :ref:`view <api/ddoc/view>`
+    - ``atts_since``.
 - **digest** (*string*): Content hash digest.
   It starts with prefix which announce hash type (``md5-``) and continues with
   Base64-encoded hash digest
-- **encoded_length** (*number*): Compressed attachment size in bytes
-  Available when query parameter ``att_encoding_info=true`` was specified and
-  ``content_type`` is in :config:option:`list of compressiable types
-  <attachments/compressible_types>`
-- **encoding** (*string*): Compression codec. Available when query parameter
-  ``att_encoding_info=true`` was specified
+- **encoded_length** (*number*): Compressed attachment size in bytes.
+  Available if ``content_type`` is in :config:option:`list of compressible types
+  <attachments/compressible_types>` when the attachment was added and the
+  following query parameters are specified:
+    - ``att_encoding_info=true`` when querying a document
+    - ``att_encoding_info=true&include_docs=true`` when querying a
+      :ref:`changes feed <api/db/changes>` or a :ref:`view <api/ddoc/view>`
+- **encoding** (*string*): Compression codec. Available if ``content_type`` is
+  in :config:option:`list of compressible types
+  <attachments/compressible_types>` when the attachment was added and the
+  following query parameters are specified:
+    - ``att_encoding_info=true`` when querying a document
+    - ``att_encoding_info=true&include_docs=true`` when querying a
+      :ref:`changes feed <api/db/changes>` or a :ref:`view <api/ddoc/view>`
 - **length** (*number*): Real attachment size in bytes. Not available if attachment
   content requested
 - **revpos** (*number*): Revision *number* when attachment was added

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/share/www/script/test/attachment_views.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/attachment_views.js b/share/www/script/test/attachment_views.js
index a92a8ad..b55aabe 100644
--- a/share/www/script/test/attachment_views.js
+++ b/share/www/script/test/attachment_views.js
@@ -19,13 +19,15 @@ couchTests.attachment_views= function(debug) {
 
   // count attachments in a view
 
+  var attachmentData = "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=";
+
   db.bulkSave(makeDocs(0, 10));
 
   db.bulkSave(makeDocs(10, 20, {
     _attachments:{
       "foo.txt": {
         content_type:"text/plain",
-        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+        data: attachmentData
       }
     }
   }));
@@ -34,11 +36,11 @@ couchTests.attachment_views= function(debug) {
     _attachments:{
       "foo.txt": {
         content_type:"text/plain",
-        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+        data: attachmentData
       },
       "bar.txt": {
         content_type:"text/plain",
-        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+        data: attachmentData
       }
     }
   }));
@@ -47,15 +49,15 @@ couchTests.attachment_views= function(debug) {
     _attachments:{
       "foo.txt": {
         content_type:"text/plain",
-        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+        data: attachmentData
       },
       "bar.txt": {
         content_type:"text/plain",
-        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+        data: attachmentData
       },
       "baz.txt": {
         content_type:"text/plain",
-        data: "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
+        data: attachmentData
       }
     }
   }));
@@ -95,4 +97,44 @@ couchTests.attachment_views= function(debug) {
   T(result.rows.length == 1);
   T(result.rows[0].value == 20);
 
+  var result = db.query(mapFunction, null, {
+    startkey: 30,
+    endkey: 39,
+    include_docs: true
+  });
+
+  T(result.rows.length == 10);
+  T(result.rows[0].value == 3);
+  T(result.rows[0].doc._attachments['baz.txt'].stub === true);
+  T(result.rows[0].doc._attachments['baz.txt'].data === undefined);
+  T(result.rows[0].doc._attachments['baz.txt'].encoding === undefined);
+  T(result.rows[0].doc._attachments['baz.txt'].encoded_length === undefined);
+
+  var result = db.query(mapFunction, null, {
+    startkey: 30,
+    endkey: 39,
+    include_docs: true,
+    attachments: true
+  });
+
+  T(result.rows.length == 10);
+  T(result.rows[0].value == 3);
+  T(result.rows[0].doc._attachments['baz.txt'].data === attachmentData);
+  T(result.rows[0].doc._attachments['baz.txt'].stub === undefined);
+  T(result.rows[0].doc._attachments['baz.txt'].encoding === undefined);
+  T(result.rows[0].doc._attachments['baz.txt'].encoded_length === undefined);
+
+  var result = db.query(mapFunction, null, {
+    startkey: 30,
+    endkey: 39,
+    include_docs: true,
+    att_encoding_info: true
+  });
+
+  T(result.rows.length == 10);
+  T(result.rows[0].value == 3);
+  T(result.rows[0].doc._attachments['baz.txt'].data === undefined);
+  T(result.rows[0].doc._attachments['baz.txt'].stub === true);
+  T(result.rows[0].doc._attachments['baz.txt'].encoding === "gzip");
+  T(result.rows[0].doc._attachments['baz.txt'].encoded_length === 47);
 };

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/share/www/script/test/changes.js
----------------------------------------------------------------------
diff --git a/share/www/script/test/changes.js b/share/www/script/test/changes.js
index fcd8306..0fba9f9 100644
--- a/share/www/script/test/changes.js
+++ b/share/www/script/test/changes.js
@@ -646,6 +646,74 @@ couchTests.changes = function(debug) {
   T(changes[0][1] === "3");
   T(changes[1][1] === "4");
 
+  // COUCHDB-1923
+  T(db.deleteDb());
+  T(db.createDb());
+
+  var attachmentData = "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ=";
+
+  db.bulkSave(makeDocs(20, 30, {
+    _attachments:{
+      "foo.txt": {
+        content_type:"text/plain",
+        data: attachmentData
+      },
+      "bar.txt": {
+        content_type:"text/plain",
+        data: attachmentData
+      }
+    }
+  }));
+
+  var mapFunction = function(doc) {
+    var count = 0;
+
+    for(var idx in doc._attachments) {
+      count = count + 1;
+    }
+
+    emit(parseInt(doc._id), count);
+  };
+
+  var req = CouchDB.request("GET", "/test_suite_db/_changes?include_docs=true");
+  var resp = JSON.parse(req.responseText);
+
+  T(resp.results.length == 10);
+  T(resp.results[0].doc._attachments['foo.txt'].stub === true);
+  T(resp.results[0].doc._attachments['foo.txt'].data === undefined);
+  T(resp.results[0].doc._attachments['foo.txt'].encoding === undefined);
+  T(resp.results[0].doc._attachments['foo.txt'].encoded_length === undefined);
+  T(resp.results[0].doc._attachments['bar.txt'].stub === true);
+  T(resp.results[0].doc._attachments['bar.txt'].data === undefined);
+  T(resp.results[0].doc._attachments['bar.txt'].encoding === undefined);
+  T(resp.results[0].doc._attachments['bar.txt'].encoded_length === undefined);
+
+  var req = CouchDB.request("GET", "/test_suite_db/_changes?include_docs=true&attachments=true");
+  var resp = JSON.parse(req.responseText);
+
+  T(resp.results.length == 10);
+  T(resp.results[0].doc._attachments['foo.txt'].stub === undefined);
+  T(resp.results[0].doc._attachments['foo.txt'].data === attachmentData);
+  T(resp.results[0].doc._attachments['foo.txt'].encoding === undefined);
+  T(resp.results[0].doc._attachments['foo.txt'].encoded_length === undefined);
+  T(resp.results[0].doc._attachments['bar.txt'].stub === undefined);
+  T(resp.results[0].doc._attachments['bar.txt'].data == attachmentData);
+  T(resp.results[0].doc._attachments['bar.txt'].encoding === undefined);
+  T(resp.results[0].doc._attachments['bar.txt'].encoded_length === undefined);
+
+  var req = CouchDB.request("GET", "/test_suite_db/_changes?include_docs=true&att_encoding_info=true");
+  var resp = JSON.parse(req.responseText);
+
+  T(resp.results.length == 10);
+  T(resp.results[0].doc._attachments['foo.txt'].stub === true);
+  T(resp.results[0].doc._attachments['foo.txt'].data === undefined);
+  T(resp.results[0].doc._attachments['foo.txt'].encoding === "gzip");
+  T(resp.results[0].doc._attachments['foo.txt'].encoded_length === 47);
+  T(resp.results[0].doc._attachments['bar.txt'].stub === true);
+  T(resp.results[0].doc._attachments['bar.txt'].data === undefined);
+  T(resp.results[0].doc._attachments['bar.txt'].encoding === "gzip");
+  T(resp.results[0].doc._attachments['bar.txt'].encoded_length === 47);
+
   // cleanup
   db.deleteDb();
 };

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/src/couch_mrview/include/couch_mrview.hrl
----------------------------------------------------------------------
diff --git a/src/couch_mrview/include/couch_mrview.hrl b/src/couch_mrview/include/couch_mrview.hrl
index bf3bcac..e4ec66d 100644
--- a/src/couch_mrview/include/couch_mrview.hrl
+++ b/src/couch_mrview/include/couch_mrview.hrl
@@ -72,6 +72,7 @@
     stale = false,
     inclusive_end = true,
     include_docs = false,
+    doc_options = [],
     update_seq=false,
     conflicts,
     callback,

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/src/couch_mrview/src/couch_mrview_http.erl
----------------------------------------------------------------------
diff --git a/src/couch_mrview/src/couch_mrview_http.erl b/src/couch_mrview/src/couch_mrview_http.erl
index 8b914ef..7b92034 100644
--- a/src/couch_mrview/src/couch_mrview_http.erl
+++ b/src/couch_mrview/src/couch_mrview_http.erl
@@ -348,6 +348,22 @@ parse_qs(Key, Val, Args) ->
             Args#mrargs{inclusive_end=parse_boolean(Val)};
         "include_docs" ->
             Args#mrargs{include_docs=parse_boolean(Val)};
+        "attachments" ->
+            case parse_boolean(Val) of
+            true ->
+                Opts = Args#mrargs.doc_options,
+                Args#mrargs{doc_options=[attachments|Opts]};
+            false ->
+                Args
+            end;
+        "att_encoding_info" ->
+            case parse_boolean(Val) of
+            true ->
+                Opts = Args#mrargs.doc_options,
+                Args#mrargs{doc_options=[att_encoding_info|Opts]};
+            false ->
+                Args
+            end;
         "update_seq" ->
             Args#mrargs{update_seq=parse_boolean(Val)};
         "conflicts" ->

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/src/couch_mrview/src/couch_mrview_util.erl
----------------------------------------------------------------------
diff --git a/src/couch_mrview/src/couch_mrview_util.erl b/src/couch_mrview/src/couch_mrview_util.erl
index 18185af..a3b0581 100644
--- a/src/couch_mrview/src/couch_mrview_util.erl
+++ b/src/couch_mrview/src/couch_mrview_util.erl
@@ -668,24 +668,24 @@ expand_dups([KV | Rest], Acc) ->
 
 maybe_load_doc(_Db, _DI, #mrargs{include_docs=false}) ->
     [];
-maybe_load_doc(Db, #doc_info{}=DI, #mrargs{conflicts=true}) ->
-    doc_row(couch_index_util:load_doc(Db, DI, [conflicts]));
-maybe_load_doc(Db, #doc_info{}=DI, _Args) ->
-    doc_row(couch_index_util:load_doc(Db, DI, [])).
+maybe_load_doc(Db, #doc_info{}=DI, #mrargs{conflicts=true, doc_options=Opts}) ->
+    doc_row(couch_index_util:load_doc(Db, DI, [conflicts]), Opts);
+maybe_load_doc(Db, #doc_info{}=DI, #mrargs{doc_options=Opts}) ->
+    doc_row(couch_index_util:load_doc(Db, DI, []), Opts).
 
 
 maybe_load_doc(_Db, _Id, _Val, #mrargs{include_docs=false}) ->
     [];
-maybe_load_doc(Db, Id, Val, #mrargs{conflicts=true}) ->
-    doc_row(couch_index_util:load_doc(Db, docid_rev(Id, Val), [conflicts]));
-maybe_load_doc(Db, Id, Val, _Args) ->
-    doc_row(couch_index_util:load_doc(Db, docid_rev(Id, Val), [])).
+maybe_load_doc(Db, Id, Val, #mrargs{conflicts=true, doc_options=Opts}) ->
+    doc_row(couch_index_util:load_doc(Db, docid_rev(Id, Val), [conflicts]), Opts);
+maybe_load_doc(Db, Id, Val, #mrargs{doc_options=Opts}) ->
+    doc_row(couch_index_util:load_doc(Db, docid_rev(Id, Val), []), Opts).
 
 
-doc_row(null) ->
+doc_row(null, _Opts) ->
     [{doc, null}];
-doc_row(Doc) ->
-    [{doc, couch_doc:to_json_obj(Doc, [])}].
+doc_row(Doc, Opts) ->
+    [{doc, couch_doc:to_json_obj(Doc, Opts)}].
 
 
 docid_rev(Id, {Props}) ->

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/src/couchdb/couch_changes.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_changes.erl b/src/couchdb/couch_changes.erl
index 85c9e54..6edde32 100644
--- a/src/couchdb/couch_changes.erl
+++ b/src/couchdb/couch_changes.erl
@@ -29,6 +29,7 @@
     resp_type,
     limit,
     include_docs,
+    doc_options,
     conflicts,
     timeout,
     timeout_fun
@@ -272,6 +273,7 @@ start_sending_changes(Callback, UserAcc, ResponseType) ->
 build_acc(Args, Callback, UserAcc, Db, StartSeq, Prepend, Timeout, TimeoutFun) ->
     #changes_args{
         include_docs = IncludeDocs,
+        doc_options = DocOpts,
         conflicts = Conflicts,
         limit = Limit,
         feed = ResponseType,
@@ -287,6 +289,7 @@ build_acc(Args, Callback, UserAcc, Db, StartSeq, Prepend, Timeout, TimeoutFun) -
         resp_type = ResponseType,
         limit = Limit,
         include_docs = IncludeDocs,
+        doc_options = DocOpts,
         conflicts = Conflicts,
         timeout = Timeout,
         timeout_fun = TimeoutFun
@@ -498,7 +501,12 @@ changes_row(Results, DocInfo, Acc) ->
     #doc_info{
         id = Id, high_seq = Seq, revs = [#rev_info{deleted = Del} | _]
     } = DocInfo,
-    #changes_acc{db = Db, include_docs = IncDoc, conflicts = Conflicts} = Acc,
+    #changes_acc{
+        db = Db,
+        include_docs = IncDoc,
+        doc_options = DocOpts,
+        conflicts = Conflicts
+    } = Acc,
     {[{<<"seq">>, Seq}, {<<"id">>, Id}, {<<"changes">>, Results}] ++
         deleted_item(Del) ++ case IncDoc of
             true ->
@@ -508,8 +516,10 @@ changes_row(Results, DocInfo, Acc) ->
                 end,
                 Doc = couch_index_util:load_doc(Db, DocInfo, Opts),
                 case Doc of
-                    null -> [{doc, null}];
-                    _ ->  [{doc, couch_doc:to_json_obj(Doc, [])}]
+                    null ->
+                        [{doc, null}];
+                    _ ->
+                        [{doc, couch_doc:to_json_obj(Doc, DocOpts)}]
                 end;
             false ->
                 []

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/src/couchdb/couch_db.hrl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_db.hrl b/src/couchdb/couch_db.hrl
index 4036934..6888f10 100644
--- a/src/couchdb/couch_db.hrl
+++ b/src/couchdb/couch_db.hrl
@@ -219,6 +219,7 @@
 
     view_type = nil,
     include_docs = false,
+    doc_options = [],
     conflicts = false,
     stale = false,
     multi_get = false,
@@ -269,6 +270,7 @@
     filter_fun,
     filter_args = [],
     include_docs = false,
+    doc_options = [],
     conflicts = false,
     db_open_options = []
 }).

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/src/couchdb/couch_httpd_db.erl
----------------------------------------------------------------------
diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl
index f5fe483..0a7c17c 100644
--- a/src/couchdb/couch_httpd_db.erl
+++ b/src/couchdb/couch_httpd_db.erl
@@ -1141,6 +1141,12 @@ parse_changes_query(Req, Db) ->
             Args#changes_args{timeout=list_to_integer(Value)};
         {"include_docs", "true"} ->
             Args#changes_args{include_docs=true};
+        {"attachments", "true"} ->
+            Opts = Args#changes_args.doc_options,
+            Args#changes_args{doc_options=[attachments|Opts]};
+        {"att_encoding_info", "true"} ->
+            Opts = Args#changes_args.doc_options,
+            Args#changes_args{doc_options=[att_encoding_info|Opts]};
         {"conflicts", "true"} ->
             Args#changes_args{conflicts=true};
         {"filter", _} ->

http://git-wip-us.apache.org/repos/asf/couchdb/blob/ca41964b/test/etap/073-changes.t
----------------------------------------------------------------------
diff --git a/test/etap/073-changes.t b/test/etap/073-changes.t
index 845cd79..d632c2f 100755
--- a/test/etap/073-changes.t
+++ b/test/etap/073-changes.t
@@ -34,6 +34,7 @@
     filter_fun,
     filter_args = [],
     include_docs = false,
+    doc_options = [],
     conflicts = false,
     db_open_options = []
 }).