You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2013/02/14 14:25:19 UTC
svn commit: r1446169 - in /subversion/trunk/subversion: include/svn_dav.h
include/svn_ra.h include/svn_ra_svn.h libsvn_ra_local/ra_plugin.c
libsvn_ra_serf/options.c libsvn_ra_svn/client.c mod_dav_svn/version.c
svnserve/serve.c
Author: rhuijben
Date: Thu Feb 14 13:25:19 2013
New Revision: 1446169
URL: http://svn.apache.org/r1446169
Log:
Following up on r1446118, make the knowledge whether reversed obtaining of
file revisions is available visible by adding an ra capability.
This to allow a future blame improvement to decide whether it can use an
improved algorithm instead of the current one without waiting for an error
on old servers.
* subversion/include/svn_dav.h
(SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS): Move new in 1.7 define below one that
exists in 1.6.
(SVN_DAV_NS_DAV_SVN_INHERITED_PROPS): Mark new in 1.8.
(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS): Mark new in 1.8.
(SVN_DAV_NS_DAV_SVN_INLINE_PROPS): Mark new in 1.8.
(SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE): New macro.
* subversion/include/svn_ra.h
(SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE): New macro.
* subversion/include/svn_ra_svn.h
(SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE): New macro.
* subversion/libsvn_ra_local/ra_plugin.c
(svn_ra_local__has_capability): Declare support for reversed file
revisions *and* ephemeral txnprops.
* subversion/libsvn_ra_serf/options.c
(capabilities_headers_iterator_callback): Forward
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE support.
* subversion/libsvn_ra_svn/client.c
(ra_svn_has_capability): Make function table driven. Add
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE support.
* subversion/mod_dav_svn/version.c
(get_vsn_options): Write SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE.
* subversion/svnserve/serve.c
(serve): Send SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE support.
Modified:
subversion/trunk/subversion/include/svn_dav.h
subversion/trunk/subversion/include/svn_ra.h
subversion/trunk/subversion/include/svn_ra_svn.h
subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c
subversion/trunk/subversion/libsvn_ra_serf/options.c
subversion/trunk/subversion/libsvn_ra_svn/client.c
subversion/trunk/subversion/mod_dav_svn/version.c
subversion/trunk/subversion/svnserve/serve.c
Modified: subversion/trunk/subversion/include/svn_dav.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_dav.h?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_dav.h (original)
+++ subversion/trunk/subversion/include/svn_dav.h Thu Feb 14 13:25:19 2013
@@ -300,33 +300,37 @@ extern "C" {
#define SVN_DAV_NS_DAV_SVN_LOG_REVPROPS SVN_DAV_PROP_NS_DAV "svn/log-revprops"
/** Presence of this in a DAV header in an OPTIONS response indicates
- * that the transmitter (in this case, the server) knows how to enforce
- * old-value atomicity in PROPPATCH (for editing revprops). */
-#define SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS\
- SVN_DAV_PROP_NS_DAV "svn/atomic-revprops"
-
-/** Presence of this in a DAV header in an OPTIONS response indicates
* that the transmitter (in this case, the server) knows how to handle
* a replay of a directory in the repository (not root). */
#define SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY\
SVN_DAV_PROP_NS_DAV "svn/partial-replay"
/** Presence of this in a DAV header in an OPTIONS response indicates
+ * that the transmitter (in this case, the server) knows how to enforce
+ * old-value atomicity in PROPPATCH (for editing revprops).
+ * @since New in 1.7 */
+#define SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS\
+ SVN_DAV_PROP_NS_DAV "svn/atomic-revprops"
+
+/** Presence of this in a DAV header in an OPTIONS response indicates
* that the transmitter (in this case, the server) knows how to get
- * inherited properties. */
+ * inherited properties.
+ * @since New in 1.8. */
#define SVN_DAV_NS_DAV_SVN_INHERITED_PROPS \
SVN_DAV_PROP_NS_DAV "svn/inherited-props"
/** Presence of this in a DAV header in an OPTIONS response indicates
* that the transmitter (in this case, the server) knows how to
* properly handle ephemeral (that is, deleted-just-before-commit) FS
- * transaction properties. */
+ * transaction properties.
+ * @since New in 1.8. */
#define SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS\
SVN_DAV_PROP_NS_DAV "svn/ephemeral-txnprops"
/** Presence of this in a DAV header in an OPTIONS response indicates
* that the transmitter (in this case, the server) supports serving properties
- * inline in update editor when 'send-all' is 'false'. */
+ * inline in update editor when 'send-all' is 'false'.
+ * @since New in 1.8. */
#define SVN_DAV_NS_DAV_SVN_INLINE_PROPS\
SVN_DAV_PROP_NS_DAV "svn/inline-props"
@@ -337,6 +341,13 @@ extern "C" {
#define SVN_DAV_NS_DAV_SVN_REPLAY_REV_RESOURCE\
SVN_DAV_PROP_NS_DAV "svn/replay-rev-resource"
+/** Presence of this in a DAV header in an OPTIONS response indicates
+ * that the transmitter (in this case, the server) knows how to handle
+ * a reversed fetch of file versions.
+ * @since New in 1.8. */
+#define SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE\
+ SVN_DAV_PROP_NS_DAV "svn/get-file-revs-rvrs"
+
/** @} */
Modified: subversion/trunk/subversion/include/svn_ra.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_ra.h?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_ra.h (original)
+++ subversion/trunk/subversion/include/svn_ra.h Thu Feb 14 13:25:19 2013
@@ -2039,6 +2039,15 @@ svn_ra_has_capability(svn_ra_session_t *
*/
#define SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS "ephemeral-txnprops"
+/**
+ * The capability of a server to walk revisions backwards in
+ * svn_ra_get_file_revs2
+ *
+ * @since New in 1.8.
+ */
+#define SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE "get-file-revs-reversed"
+
+
/* *** PLEASE READ THIS IF YOU ADD A NEW CAPABILITY ***
*
* RA layers generally fetch all capabilities when asked about any
Modified: subversion/trunk/subversion/include/svn_ra_svn.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/svn_ra_svn.h?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/include/svn_ra_svn.h (original)
+++ subversion/trunk/subversion/include/svn_ra_svn.h Thu Feb 14 13:25:19 2013
@@ -66,6 +66,9 @@ extern "C" {
#define SVN_RA_SVN_CAP_INHERITED_PROPS "inherited-props"
/* maps to SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS */
#define SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS "ephemeral-txnprops"
+/* maps to SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE */
+#define SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE "file-revs-reverse"
+
/** ra_svn passes @c svn_dirent_t fields over the wire as a list of
* words, these are the values used to represent each field.
Modified: subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c (original)
+++ subversion/trunk/subversion/libsvn_ra_local/ra_plugin.c Thu Feb 14 13:25:19 2013
@@ -1513,7 +1513,10 @@ svn_ra_local__has_capability(svn_ra_sess
|| strcmp(capability, SVN_RA_CAPABILITY_PARTIAL_REPLAY) == 0
|| strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0
|| strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0
- || strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0)
+ || strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0
+ || strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0
+ || strcmp(capability, SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) == 0
+ )
{
*has = TRUE;
}
Modified: subversion/trunk/subversion/libsvn_ra_serf/options.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_serf/options.c?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_serf/options.c (original)
+++ subversion/trunk/subversion/libsvn_ra_serf/options.c Thu Feb 14 13:25:19 2013
@@ -203,6 +203,13 @@ capabilities_headers_iterator_callback(v
svn_hash_sets(session->capabilities,
SVN_RA_CAPABILITY_INHERITED_PROPS, capability_yes);
}
+ if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE,
+ vals))
+ {
+ svn_hash_sets(session->capabilities,
+ SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
+ capability_yes);
+ }
if (svn_cstring_match_list(SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS, vals))
{
svn_hash_sets(session->capabilities,
Modified: subversion/trunk/subversion/libsvn_ra_svn/client.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_ra_svn/client.c?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_ra_svn/client.c (original)
+++ subversion/trunk/subversion/libsvn_ra_svn/client.c Thu Feb 14 13:25:19 2013
@@ -2589,43 +2589,46 @@ ra_svn_replay_range(svn_ra_session_t *se
}
-static svn_error_t *ra_svn_has_capability(svn_ra_session_t *session,
- svn_boolean_t *has,
- const char *capability,
- apr_pool_t *pool)
+static svn_error_t *
+ra_svn_has_capability(svn_ra_session_t *session,
+ svn_boolean_t *has,
+ const char *capability,
+ apr_pool_t *pool)
{
svn_ra_svn__session_baton_t *sess = session->priv;
+ static const char* capabilities[][2] =
+ {
+ /* { ra capability string, svn:// wire capability string} */
+ {SVN_RA_CAPABILITY_DEPTH, SVN_RA_SVN_CAP_DEPTH},
+ {SVN_RA_CAPABILITY_MERGEINFO, SVN_RA_SVN_CAP_MERGEINFO},
+ {SVN_RA_CAPABILITY_LOG_REVPROPS, SVN_RA_SVN_CAP_LOG_REVPROPS},
+ {SVN_RA_CAPABILITY_PARTIAL_REPLAY, SVN_RA_SVN_CAP_PARTIAL_REPLAY},
+ {SVN_RA_CAPABILITY_COMMIT_REVPROPS, SVN_RA_SVN_CAP_COMMIT_REVPROPS},
+ {SVN_RA_CAPABILITY_ATOMIC_REVPROPS, SVN_RA_SVN_CAP_ATOMIC_REVPROPS},
+ {SVN_RA_CAPABILITY_INHERITED_PROPS, SVN_RA_SVN_CAP_INHERITED_PROPS},
+ {SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS,
+ SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS},
+ {SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
+ SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE},
+
+ {NULL, NULL} /* End of list marker */
+ };
+ int i;
*has = FALSE;
- if (strcmp(capability, SVN_RA_CAPABILITY_DEPTH) == 0)
- *has = svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_DEPTH);
- else if (strcmp(capability, SVN_RA_CAPABILITY_MERGEINFO) == 0)
- *has = svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_MERGEINFO);
- else if (strcmp(capability, SVN_RA_CAPABILITY_LOG_REVPROPS) == 0)
- *has = svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_LOG_REVPROPS);
- else if (strcmp(capability, SVN_RA_CAPABILITY_PARTIAL_REPLAY) == 0)
- *has = svn_ra_svn_has_capability(sess->conn, SVN_RA_SVN_CAP_PARTIAL_REPLAY);
- else if (strcmp(capability, SVN_RA_CAPABILITY_COMMIT_REVPROPS) == 0)
- *has = svn_ra_svn_has_capability(sess->conn,
- SVN_RA_SVN_CAP_COMMIT_REVPROPS);
- else if (strcmp(capability, SVN_RA_CAPABILITY_ATOMIC_REVPROPS) == 0)
- *has = svn_ra_svn_has_capability(sess->conn,
- SVN_RA_SVN_CAP_ATOMIC_REVPROPS);
- else if (strcmp(capability, SVN_RA_CAPABILITY_INHERITED_PROPS) == 0)
- *has = svn_ra_svn_has_capability(sess->conn,
- SVN_RA_SVN_CAP_INHERITED_PROPS);
- else if (strcmp(capability, SVN_RA_CAPABILITY_EPHEMERAL_TXNPROPS) == 0)
- *has = svn_ra_svn_has_capability(sess->conn,
- SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS);
- else /* Don't know any other capabilities, so error. */
+ for (i = 0; capabilities[i][0]; i++)
{
- return svn_error_createf
- (SVN_ERR_UNKNOWN_CAPABILITY, NULL,
- _("Don't know anything about capability '%s'"), capability);
+ if (strcmp(capability, capabilities[i][0]))
+ {
+ *has = svn_ra_svn_has_capability(sess->conn, capabilities[i][1]);
+ return SVN_NO_ERROR;
+ }
}
- return SVN_NO_ERROR;
+ return svn_error_createf(SVN_ERR_UNKNOWN_CAPABILITY, NULL,
+ _("Don't know anything about capability '%s'"),
+ capability);
}
static svn_error_t *
Modified: subversion/trunk/subversion/mod_dav_svn/version.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/mod_dav_svn/version.c?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/mod_dav_svn/version.c (original)
+++ subversion/trunk/subversion/mod_dav_svn/version.c Thu Feb 14 13:25:19 2013
@@ -150,6 +150,7 @@ get_vsn_options(apr_pool_t *p, apr_text_
apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_PARTIAL_REPLAY);
apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_INHERITED_PROPS);
apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_INLINE_PROPS);
+ apr_text_append(p, phdr, SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE);
/* Mergeinfo is a special case: here we merely say that the server
* knows how to handle mergeinfo -- whether the repository does too
* is a separate matter.
Modified: subversion/trunk/subversion/svnserve/serve.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svnserve/serve.c?rev=1446169&r1=1446168&r2=1446169&view=diff
==============================================================================
--- subversion/trunk/subversion/svnserve/serve.c (original)
+++ subversion/trunk/subversion/svnserve/serve.c Thu Feb 14 13:25:19 2013
@@ -3403,7 +3403,7 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
/* Send greeting. We don't support version 1 any more, so we can
* send an empty mechlist. */
if (params->compression_level > 0)
- SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwwwwwwww)",
+ SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_SVNDIFF1,
@@ -3414,9 +3414,11 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
SVN_RA_SVN_CAP_PARTIAL_REPLAY,
SVN_RA_SVN_CAP_INHERITED_PROPS,
- SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS));
+ SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
+ SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
+ ));
else
- SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwwwwwww)",
+ SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "nn()(wwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_ABSENT_ENTRIES,
@@ -3426,7 +3428,9 @@ svn_error_t *serve(svn_ra_svn_conn_t *co
SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
SVN_RA_SVN_CAP_PARTIAL_REPLAY,
SVN_RA_SVN_CAP_INHERITED_PROPS,
- SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS));
+ SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
+ SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
+ ));
/* Read client response, which we assume to be in version 2 format:
* version, capability list, and client URL; then we do an auth
Re: r1446169 - obtaining file-revs in reverse order
Posted by Julian Foad <ju...@btopenworld.com>.
> URL: http://svn.apache.org/r1446169
> Log:
> Following up on r1446118, make the knowledge whether reversed obtaining of
> file revisions is available visible by adding an ra capability.
>
> This to allow a future blame improvement to decide whether it can use an
> improved algorithm instead of the current one without waiting for an error
> on old servers.
Nice.
A few questions.
When you first added this "reverse order" functionality in <http://svn.apache.org/r1445973>, you documented for svn_ra_get_file_revs2() and svn_repos_get_file_revs2():
+ * On subversion 1.8 and newer servers this function has been enabled
+ * to support reversion of the revision range for @a include_merged_revision
+ * @c FALSE reporting by switching @a end with @a start.
Is that still the case or do those functions now support a backwards range with merged revisions included?
Reference the capability string in those doc strings.
There's still a big comment on the definition (rather than declaration) of svn_repos_get_file_revs2() which says reverse ranges are not supported; that should go away or be updated.
For testing, in r1445973:
* subversion/tests/libsvn_repos/repos-test.c
(test_get_file_revs): Extend test to also walk the revisions backwards.
Would it be true to say that the results of a reverse range should always be the reversal of the results from the equivalent forward range? If so, that would be a useful test. The current test doesn't appear to verify that the results are in any particular order.
More below...
> * subversion/include/svn_dav.h
> (SVN_DAV_NS_DAV_SVN_ATOMIC_REVPROPS): Move new in 1.7 define below one that
> exists in 1.6.
> (SVN_DAV_NS_DAV_SVN_INHERITED_PROPS): Mark new in 1.8.
> (SVN_DAV_NS_DAV_SVN_EPHEMERAL_TXNPROPS): Mark new in 1.8.
> (SVN_DAV_NS_DAV_SVN_INLINE_PROPS): Mark new in 1.8.
> (SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE): New macro.
>
> * subversion/include/svn_ra.h
> (SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE): New macro.
>
> * subversion/include/svn_ra_svn.h
> (SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE): New macro.
>
> * subversion/libsvn_ra_local/ra_plugin.c
> (svn_ra_local__has_capability): Declare support for reversed file
> revisions *and* ephemeral txnprops.
That's an unrelated change, fixing ra-local to advertise ephemeral txnprops. That's worth drawing attention to, as it indicates we aren't testing that feature. (It would have been better as a separate commit.)
> * subversion/libsvn_ra_serf/options.c
> (capabilities_headers_iterator_callback): Forward
> SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE support.
>
> * subversion/libsvn_ra_svn/client.c
> (ra_svn_has_capability): Make function table driven. Add
> SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE support.
>
> * subversion/mod_dav_svn/version.c
> (get_vsn_options): Write SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE.
>
> * subversion/svnserve/serve.c
> (serve): Send SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE support.
> +/**
> + * The capability of a server to walk revisions backwards in
> + * svn_ra_get_file_revs2
Doxygen will make this a hyperlink if you add '()' after the function name.
> + *
> + * @since New in 1.8.
> + */
> +#define SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE "get-file-revs-reversed"
> +/** Presence of this in a DAV header in an OPTIONS response indicates
> + * that the transmitter (in this case, the server) knows how to handle
> + * a reversed fetch of file versions.
> + * @since New in 1.8. */
> +#define SVN_DAV_NS_DAV_SVN_GET_FILE_REVS_REVERSE\
> + SVN_DAV_PROP_NS_DAV "svn/get-file-revs-rvrs"
> +/* maps to SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE */
> +#define SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE "file-revs-reverse"
Would it be useful to refer to the relevant DAV/svnserve API functions from these two doc strings?
In the doc string of svn_ra_get_file_revs2(), detail what the presence or absence of this capability really means to a caller. Something like:
[[[
With a forward range, svn_ra_get_file_revs2() calculates the list
of revisions before starting to send them to the caller.
When a backward range is requested, with the 'reverse' capability
(#SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE) svn_ra_get_file_revs2()
returns the revisions streamily in backward order, but a request
for merged revisions throws an error; without the capability, it
either throws an error or returns an incomplete result.
]]]
- Julian