--- /dev/null
+\def\partitle#1{\par{\textbf{#1}}\par}
+
+\label{ConsOview}
+This section describes the aspects of principal use of the \LB\ consumer API.
+It begins with the simplest examples how to query the bookkeeping
+service, continues with various selection criteria and their combinations, and
+through queries on user tags and timestamps it concludes with the discussion of
+application specific queries.
+
+The document is intended for users interested in the \LB\
+consumer API or those interested in better understanding of the \LB\ service
+capabilities.
+
+In the presented examples only the C \LB\ API (Sect.~\ref{query-C}) is considered.
+The C++ API (Sect.~\ref{query-CPP}) covers the same functionality.
+
+\subsubsection{Returned results}
+
+\LB\ server returns errors which are classified as hard and soft errors.
+The main difference between these categories is that in the case of soft
+errors results may still be returned.
+The authentication errors belong to
+``soft error'' sort. Hard errors are typically all unrecoverable errors like ENOMEM.
+
+When the item count returned by \LB\ server exceeds the defined limits, the E2BIG error occur.
+There are two limits\,---\,the server and the user limit. The user defined limit may be set in
+the context at the client side in the following way:
+\begin{verbatim}
+ #include <glite/lb/context.h>
+ ...
+ edg_wll_Context ctx;
+
+ edg_wll_InitContext(&ctx);
+ edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_JOBS_LIMIT, 10);
+ edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_EVENTS_LIMIT, 20);
+ edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_RESULTS, 30);
+ ...
+ edg_wll_FreeContext(ctx);
+\end{verbatim}
+This code sets the user query limits to the given maximal count of items.
+
+The E2BIG error may fall into both categories depending on the setting of
+another context parameter, EDG\_WLL\_PARAM\_QUERY\_RESULTS.
+It may take the following values:
+
+%The E2BIG error can be classified as hard or soft error too because this error can be affected
+%by one more parameter which can make this error hard or soft. This parameter can be set like this:\\
+%\texttt{edg\_wll\_SetParam(ctx, EDG\_WLL\_PARAM\_QUERY\_RESULTS, EDG\_WLL\_QUERYRES\_ALL);}
+%and possible values follows:
+\begin{itemize}
+ \item{EDG\_WLL\_QUERYRES\_NONE}\,---\,No results are returned.
+ In this case an E2BIG error acts like a hard error.
+ \item{EDG\_WLL\_QUERYRES\_LIMITED}\,---\,A result contains at most ``limit'' item count.
+ In this case an E2BIG error acts like a soft error.
+ \item{EDG\_WLL\_QUERYRES\_ALL}\,---\,All results are returned and limits has no effect.
+ This option is available only in special cases such as ``user jobs query'' and
+ the ``job status query''. Otherwise the EINVAL error is returned.
+\end{itemize}
+Default value is EDG\_WLL\_QUERYRES\_NONE.
+
+
+\subsubsection{Job queries}
+
+\partitle{Job status}
+\label{JS}
+
+The simplest case corresponds to the situation when an exact job ID
+is known and the only information requested is the job status. The job ID
+format is described in~\cite{lbapi}.
+The following example shows
+all the relevant structures and API calls to retrieve status information
+about a job with the ID\\
+\texttt{https://lhun.ics.muni.cz:9000/OirOgeWh\_F9sfMZjnIPYhQ}.
+
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wll_QueryRec jc[2];
+ ...
+ edg_wll_InitContext(&ctx);
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_JOBID;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ if ( edg_wlc_JobIdParse(
+ "https://lhun.ics.muni.cz:9000/OirOgeWh_F9sfMZjnIPYhQ",
+ &jc[0].value.j) )
+ {
+ edg_wll_FreeContext(ctx);
+ exit(1);
+ }
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ if (edg_wll_QueryJobs(ctx, jc, 0, NULL, &statesOut)) {
+ char *err_text,*err_desc;
+
+ edg_wll_Error(ctx,&err_text,&err_desc);
+ fprintf(stderr,"QueryJobs: %s (%s)\n",err_text,err_desc);
+ free(err_text);
+ free(err_desc);
+ }
+ else {
+ ... /* process the returned data */
+ edg_wll_FreeStatus(statesOut);
+ free(statesOut);
+ }
+ edg_wlc_JobIdFree(jc[0].value.j);
+ edg_wll_FreeContext(ctx);
+\end{verbatim}
+
+The first function call in this example initializes the \LB\ context\,---\,variable
+\texttt{ctx}\,---\,which is necessary for later use. The most important part
+of this code fragment is the \texttt{jc} variable setting.
+Variable \texttt{jc} is a list of conditions terminated with the
+\texttt{EDG\_WLL\_QUERY\_ATTR\_UNDEF} item.
+In this example it contains the only data item\,---\,the job ID
+(in its parsed form).
+
+If \texttt{edg\_wll\_QueryJobs()} is successful, returned results are available
+in the \texttt{statesOut} variable. This variable contains an array of job states\,---\,
+in this example state of a given job.
+
+The code also shows a~complete handling of returned errors as well as memory
+management\,---\,deallocation of data that are not needed anymore.
+\emph{For the sake of simplicity such code is not included in the examples
+in the rest of this document.}
+
+\partitle{All user's jobs}
+\label{JQ-auj}
+
+The simple query example is a request for all user's jobs. Another
+condition type, \\
+\texttt{EDG\_WLL\_QUERY\_ATTR\_OWNER}, is used in this case, with the
+value field filled with a user name. You can found this example in client module, file \texttt{job\_status.c}.
+
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[2];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+The value of the \texttt{attr} field which specifies job owner
+could be set to \texttt{NULL} meaning the authenticated user.
+Obtained results may differ according to the security level, e.g. with strong security
+context only information about jobs of the specified user are returned
+(in general info about all jobs a user is authorized to retrieve should be
+returned).
+
+The query may return either a~list of job ID's or a~list of job states or both,
+depending on the parameters \texttt{jobsOut} and \texttt{statesOut}.
+If either is NULL the corresponding list is not retrieved.
+
+\par
+
+The following examples demonstrates how \texttt{edg\_wll\_QueryJobs()} combines
+the given conditions in a logical conjugation.
+
+\partitle{Running jobs}
+\label{JQ-rj}
+
+If all (user's) running jobs are to be retrieved the following code can
+be used.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[3];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[1].value.i = EDG_WLL_JOB_RUNNING;
+ jc[2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+This example combines previous example with a new criteria. There are used two different attributes
+ - \texttt{EDG\_WLL\_QUERY\_ATTR\_OWNER} and \texttt{EDG\_WLL\_QUERY\_ATTR\_STATE}.
+\texttt{edg\_wll\_QueryJobs()} connects them in the logical conjunction.
+Examples using logical conjunction and logical disjunction are shown in the Sect.~\ref{JQ-AO}.
+
+\partitle{Jobs running at a given CE}
+The following example gives description of all (user's) jobs running at CE XYZ.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[4];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[1].value.i = EDG_WLL_JOB_RUNNING;
+ jc[2].attr = EDG_WLL_QUERY_ATTR_DESTINATION;
+ jc[2].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[2].value.c = "XYZ";
+ jc[3].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+In a case the job is not running the destination (attribute \texttt{EDG\_WLL\_QUERY\_ATTR\_DESTINATION})
+saves a CE name the job will be routed to. If location is needed use the \texttt{EDG\_WLL\_QUERY\_ATTR\_LOCATION} attribute.
+
+
+\partitle{The WITHIN operator}
+The \texttt{EDG\_WLL\_QUERY\_OP\_WITHIN} operator can be used in any condition with numeric values.
+The following example shows a query on all user's jobs that have returned
+an exit code from 2 to 7.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[4];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[1].value.i = EDG_WLL_JOB_DONE;
+ jc[2].attr = EDG_WLL_QUERY_ATTR_EXITCODE;
+ jc[2].op = EDG_WLL_QUERY_OP_WITHIN;
+ jc[2].value.i = 2;
+ jc[2].value2.i = 7;
+ jc[3].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+The second attribute type (``state'') selects jobs in state ``done'' because it doesn't
+make sense to query running jobs on their return code.
+The last attribute (``exit code'') uses the WITHIN operator. The WITHIN operator accepts an
+interval\,---\,the lower bound of the interval is stored in the \texttt{value} union and the
+upper bound is stored in the \texttt{value2} union.
+
+\partitle{Using AND, OR in query clauses}
+\label{JQ-AO}
+In many cases the basic logic using only conjunctions is not sufficient.
+For example, if you need all your jobs running at the destination XXX or at
+the destination YYY, the only way to do this with the \texttt{edg\_wll\_QueryJobs()}
+call is to call it twice. The \texttt{edg\_wll\_QueryJobsExt()} call allows to make
+such a~query in a single step.
+The function accepts an array of condition lists. Conditions within a~single list are
+OR-ed and the lists themselves are AND-ed.
+%It is allowed to use only identical attributes in every standalone condition list.
+%This is forced by an ``indexing'' definition, look at Sect.~\ref{ConsIndx}
+%for further details.
+
+The next query example describes how to get all user's jobs running at
+CE XXX or YYY.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec *jc[4];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0] = (edg_wll_QueryRec *) malloc(2*sizeof(edg_wll_QueryRec));
+ jc[0][0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0][0].value.c = NULL;
+ jc[0][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+ jc[1] = (edg_wll_QueryRec *) malloc(2*sizeof(edg_wll_QueryRec));
+ jc[1][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[1][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[1][0].value.i = EDG_WLL_JOB_RUNNING;
+ jc[1][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+ jc[2] = (edg_wll_QueryRec *) malloc(3*sizeof(edg_wll_QueryRec));
+ jc[2][0].attr = EDG_WLL_QUERY_ATTR_DESTINATION;
+ jc[2][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[2][0].value.c = "XXX";
+ jc[2][1].attr = EDG_WLL_QUERY_ATTR_DESTINATION;
+ jc[2][1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[2][1].value.c = "YYY";
+ jc[2][2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+ jc[3] = NULL;
+ edg_wll_QueryJobsExt(ctx, (const edg_wll_QueryRec **)jc, 0, &jobsOut, &statesOut);
+ free(jc[0]); free(jc[1]); free(jc[2]);
+ ...
+\end{verbatim}
+
+As clearly seen, there are three lists supplied to
+\texttt{edg\_wll\_QueryJobsExt()}. The first list specifies the owner of the
+job, the second list provides the required status (\texttt{Running}) and
+the last list specifies the two destinations.
+The list of lists is terminated with \texttt{NULL}.
+This query equals to the formula
+\begin{quote}
+\texttt{(user=NULL) and (state=Running) and (dest='XXX' or dest='YYY')}.
+\end{quote}
+
+\partitle{User tags}
+\label{JQ_ut}
+User tags can be used for marking (labelling) jobs. A user tag is
+a pair of user defined \texttt{name} and \texttt{value}.
+
+\label{JQ_RedJobs}
+For example, if all jobs marked with the user tag \texttt{color} and with its
+value \texttt{red} should be retrieved, the following code can be used:
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[2];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].attr_id.tag = "color";
+ jc[0].value.c = "red";
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+The condition \texttt{EDG\_WLL\_QUERY\_ATTR\_USER\_TAG} in \texttt{jc[0]}
+specifies that a user tag is set. Tag name is given in
+\texttt{jc[0].attr\_id.tag} and the appropriate tag
+value is given in \texttt{jc[0].value}.
+
+
+Another example\,---\,jobs marked with red or green color:
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[1][3];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0][0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0][0].attr_id.tag = "color";
+ jc[0][0].value.c = "red";
+ jc[0][1].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc[0][1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0][1].attr_id.tag = "color";
+ jc[0][1].value.c = "green";
+ jc[0][2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobsExt(ctx, (const edg_wll_QueryRec **)jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+And the last one (with two user tags)\,---\,jobs marked with red color and using the 'xyz' algorithm:
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[2];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].attr_id.tag = "color";
+ jc[0].value.c = "red";
+ jc[1].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc[1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[1].attr_id.tag = "algorithm";
+ jc[1].value.c = "xyz";
+ jc[2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+Due to performance reasons
+it is not possible to make query with two tags of different type in one
+or-clause.
+% fakt nevim, co tahle veta znamena. ljocha
+%That means that user tags are composed of two variable components
+%and user tag indices depend on tag names.
+
+\partitle{Time attributes}
+
+%A time interval in which a particular state appears is attached to every job
+%state.
+Besides details on the job's current state the job status also carries
+information when the job entered each of the distinguished states
+(if ever).
+This information is also queriable.
+
+
+The following example shows how to get all jobs that were submitted in
+the last 24 hours.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[2];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_TIME;
+ jc[0].op = EDG_WLL_QUERY_OP_GREATER;
+ jc[0].attr_id.state = EDG_WLL_JOB_SUBMITTED;
+ jc[0].value.t.tv_sec = time_now - (24 * 60 * 60);
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+In this case, a record representing the necessary condition is quite
+different. The \LB\ API allows to ask for jobs with a particular status at a
+given time. When \LB\ server gets \texttt{EDG\_WLL\_QUERY\_ATTR\_TIME}
+as a job condition, it checks \texttt{jc[0].attr\_id.state} for job state.
+Note that \texttt{timenow} is a variable which contains current time in
+seconds.
+
+It is easy to modify previous example and add another time boundary. It is then
+possible to ask for all jobs with a specified state within a particular time
+interval.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc[3];
+ edg_wll_JobStat *statesOut = NULL;
+ edg_wlc_JobId *jobsOut = NULL;
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_TIME;
+ jc[1].op = EDG_WLL_QUERY_OP_WITHIN;
+ jc[1].attr_id.state = EDG_WLL_JOB_SUBMITTED;
+ jc[1].value.t.tv_sec = time_now - (48 * 60 * 60);
+ jc[1].value2.t.tv_sec = time_now - (24 * 60 * 60);
+ jc[2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryJobs(ctx, jc, 0, &jobsOut, &statesOut);
+ ...
+\end{verbatim}
+
+
+\subsubsection{Event queries and application specific queries}
+\label{ASQ}
+Event queries and job queries are similar.
+Obviously, the return type is different\Dash the \LB\ raw events.
+There is one more input parameter
+representing specific conditions on events (possibly empty)
+in addition to conditions on jobs.
+Some examples showing event queries
+are considered in the following paragraph.
+
+
+\partitle{All jobs marked as red}
+\label{ASQ_allred}
+This example shows how to select all user's jobs which were (at some time)
+marked with the value red of user tag color.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_Event *eventsOut;
+ edg_wll_QueryRec jc[2];
+ edg_wll_QueryRec ec[2];
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec[0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ ec[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec[0].attr_id.tag = "color";
+ ec[0].value.c = "red";
+ ec[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryEvents(ctx, jc, ec, &eventsOut);
+ ...
+\end{verbatim}
+
+This example uses \texttt{edg\_wll\_QueryEvents()} call. Two condition lists are
+given to \texttt{edg\_wll\_QueryEvents()} call. One represents job conditions and
+the second represents event conditions. These two lists are joined together with
+logical and (both condition lists have to be satisfied). This is necessary as
+events represent a state of a job in a particular moment and this changes in time.
+
+The \texttt{edg\_wll\_QueryEvents()} returns matched events and save them in the
+\texttt{eventsOut} variable. Required job IDs are stored in the edg\_wll\_Event
+structure.
+
+Due to the need of ``historic'' information it's impossible to address
+this type of query with calling the function \texttt{edg\_wll\_QueryJobs()}, raw events has to
+be retrieved instead. The example above retrieves all events marking
+any user's job as ``red''. By gathering the jobid's from those events one
+gets a~list of such jobs, not regarding whether their ``color'' was
+changed afterwards or not (unlike straightforward \texttt{edg\_wll\_QueryJobs()}
+which considers the ``current color'' only). The same applies on all
+subsequent examples using the user's marking.
+
+\partitle{All red jobs at some time marked as green}
+The next example shows how to select all jobs which are just now marked with
+user tag color red, but at some time in the past they were marked as green.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_Event *eventsOut;
+ edg_wll_QueryRec jc[2];
+ edg_wll_QueryRec ec[2];
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].attr_id.tag = "color";
+ jc[0].value.c = "red";
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec[0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ ec[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec[0].attr_id.tag = "color";
+ ec[0].value.c = "green";
+ ec[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryEvents(ctx, jc, ec, &eventsOut);
+ ...
+\end{verbatim}
+
+Jobs conditions selects all jobs with tag ``color = red'' (See example in paragraph
+\ref{JQ_RedJobs}). Event conditions selects all jobs which were sometimes marked
+as green\,---\,this is described in previous example \ref{ASQ_allred}.
+
+\partitle{All resubmitted jobs}
+The next example shows how to get all (your) resubmitted jobs.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_Event *eventsOut;
+ edg_wll_QueryRec jc[2];
+ edg_wll_QueryRec ec[2];
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec[0].attr = EDG_WLL_QUERY_ATTR_EVENT_TYPE;
+ ec[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec[0].value.i = EDG_WLL_EVENT_RESUBMISSION;
+ ec[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryEvents(ctx, jc, ec, &eventsOut);
+ ...
+\end{verbatim}
+
+\partitle{Jobs resubmitted in the last two hours}
+The next example shows how to get all user's jobs which were resubmitted in the last
+2 hours.
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_Event *eventsOut;
+ edg_wll_QueryRec jc[2];
+ edg_wll_QueryRec ec[3];
+ ...
+ jc[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0].value.c = NULL;
+ jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec[0].attr = EDG_WLL_QUERY_ATTR_EVENT_TYPE;
+ ec[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec[0].value.i = EDG_WLL_EVENT_RESUBMISSION;
+ ec[1].attr = EDG_WLL_QUERY_ATTR_TIME;
+ ec[1].op = EDG_WLL_QUERY_OP_GREATER;
+ ec[1].value.t.tv_sec = time_now - (2 * 60 * 60);
+ ec[2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ edg_wll_QueryEvents(ctx, jc, ec, &eventsOut);
+ ...
+\end{verbatim}
+
+
+\partitle{Complex query}
+The last example illustrates the API usage on
+ a~meaningful but rather complex query ``which of my red jobs are heading
+to a~destination that already encountered problems executing red jobs''.
+
+First we retrieve the information of red jobs failures.
+This cannot be accomplished with a~job query because the job may
+get resubmitted automatically to another computing element and terminate
+successfully. Therefore we need to search for the ``historic''
+information\,---\,`Done' events with their minor status
+equal to `Failed'.
+
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_QueryRec jc1[3],ec1[3];
+ edg_wll_Event *failures;
+
+ jc1[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+ jc1[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc1[0].value.c = NULL;
+ jc1[1].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc1[1].attr_id.tag = "color";
+ jc1[1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc1[1].value.c = "red";
+ jc1[2].attr = EDG_WLL_QUERY_ATTR_DONECODE;
+ jc1[2].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc1[2].value.i = EDG_WLL_DONE_FAILED;
+ jc1[3].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+ ec1[0].attr = EDG_WLL_QUERY_ATTR_EVENT_TYPE;
+ ec1[0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec1[0].value.i = EDG_WLL_EVENT_DONE;
+ ec1[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+ edg_wll_QueryEvents(ctx,jc1,ec1,&failures);
+ ...
+\end{verbatim}
+
+Unfortunately, the `Done' event itself does not contain a~complete
+identification of the queue where the job was running.
+This information is contained in the `Match' events.
+Moreover, there may be more than one such events in the job's life cycle
+as the job may have been resubmitted.
+Therefore we loop over the job ID's extracted from the events returned
+in the previous step, and retrieve their `Match' and `Done' events.
+The \LB\ API returns sorted results therefore we can assume that `Done'
+events immediately following a~`Match' belong to the same attempt to submit
+the job\footnote{In reality events may get lost or delayed.
+Therefore strict checking the Match-Done pairing would require analysis
+of the hierarchical event sequence codes.
+However, this falls beyond the scope of this document.}.
+
+Due to job resubmissions again
+a~job may be represented several times in \verb'failures'.
+Because of obvious performance reasons
+it is desirable to avoid repeated queries on the same job.
+On the other hand, we may rely on \LB\ queries returning data grouped
+according to jobs. Therefore checking duplicities is easy.
+
+\begin{verbatim}
+ ...
+ edg_wll_QueryRec *jc2[2],*ec2[2];
+ char *last_job = strdup(""),*this_job,**failed_sites = NULL;
+ edg_wll_Event *match_done;
+ char **failed_sites;
+ int n, i, j;
+ ...
+ jc2[0][0].attr = EDG_WLL_QUERY_ATTR_JOBID;
+ jc2[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc2[0][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ jc2[1] = NULL;
+ ec2[0][0].attr = EDG_WLL_QUERY_ATTR_EVENT_TYPE;
+ ec2[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec2[0][0].value.i = EDG_WLL_EVENT_MATCH;
+ ec2[0][1].attr = EDG_WLL_QUERY_ATTR_EVENT_TYPE;
+ ec2[0][1].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec2[0][1].value.i = EDG_WLL_EVENT_DONE;
+ ec2[0][2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec2[1] = NULL;
+
+ n = 0;
+ for (i=0; failures[i].type; i++) {
+ this_job = edg_wlc_JobIdUnparse(failures[i].any.jobId);
+ if (strcmp(last_job,this_job)) {
+ free(last_job);
+ last_job = this_job;
+ jc2[0][0].value.j = failures[i].any.jobId;
+ edg_wll_QueryEventsExt(ctx,(const edg_wll_QueryRec **)jc2,
+ (const edg_wll_QueryRec **)ec2,&match_done);
+ for (j=0; match_done[j].type; j++) {
+ if (match_done[j].type == EDG_WLL_EVENT_MATCH &&
+ match_done[j+1].type == EDG_WLL_EVENT_DONE &&
+ match_done[j+1].done.status_code == EDG_WLL_DONE_FAILED)
+ {
+ failed_sites = realloc(failed_sites,(n+1)*sizeof *failed_sites);
+ failed_sites[n++] = strdup(match_done[j].match.dest_id);
+ }
+ edg_wll_FreeEvent(&match_done[j]);
+ }
+ }
+ else free(this_job);
+ edg_wll_FreeEvent(&failures[i]);
+ }
+ free(failures);
+ ...
+\end{verbatim}
+
+The API would allow to perform a~single query instead of the loop,
+putting all the job ID's into a~list of OR-ed conditions.
+However, as the query conditions are directly converted to a~SQL statement
+we don't recommend more than approx. 10--20 atomic conditions per query.
+This number can be easily exceeded in the case of this example.
+On the other hand, queries containing a~``jobid equals'' clause are very
+effective and the overhead of repeating them is not very high.
+
+Finally we can query the server for the jobs heading to one of the failing
+sites.
+A~job's destination is known starting from the `Ready' state,
+and the query makes sense also in the `Scheduled' state (\ie\ the job reached
+the LRMS queue but has not been started yet).
+
+\begin{verbatim}
+ ...
+ edg_wll_QueryRec *jc3[3];
+ edg_wlc_JobId *unlucky_jobs;
+ ... /* remove duplicates from failed_sites */
+
+ for (i=0; i<n; i++) {
+ jc3[0][i].attr = EDG_WLL_QUERY_ATTR_DESTINATION;
+ jc3[0][i].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc3[0][i].value.c = failed_sites[i];
+ }
+ jc3[0][i].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+ jc3[1][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc3[1][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc3[1][0].value.i = EDG_WLL_JOB_READY;
+ jc3[1][1].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc3[1][1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc3[1][1].value.i = EDG_WLL_JOB_SCHEDULED;
+ jc3[1][2].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+ jc3[2] = NULL;
+ edg_wll_QueryJobsExt(ctx,(const edg_wll_QueryRec **)jc3,0,&unlucky_jobs,NULL);
+ ...
+\end{verbatim}
+
+See also complex example in client module, file \texttt{query\_ext.c}.
+
+\iffalse
+The last example deals with a more complex situation where too complex actual
+query must be
+split into two simpler queries and user must perform an intersection
+on the obtained results.
+We need to get all red jobs routed to destination X on which were already some
+red jobs, but they have failed.
+
+\begin{verbatim}
+ #include <glite/lb/consumer.h>
+ ...
+ edg_wll_Context ctx;
+ edg_wll_Event *eventsOut;
+ edg_wll_JobStat *statesOut;
+ edg_wlc_JobId *jobsOut;
+ edg_wll_QueryRec **jc;
+ edg_wll_QueryRec **ec;
+ ...
+ jc[0][0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ jc[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[0][0].attr_id.tag = "color";
+ jc[0][0].value.c = "red";
+ jc[0][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ jc[2][0].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[2][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[2][0].value.i = EDG_WLL_JOB_SUBMITTED;
+ jc[2][1].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[2][1].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[2][1].value.i = EDG_WLL_JOB_WAITING;
+ jc[2][2].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[2][2].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[2][2].value.i = EDG_WLL_JOB_READY;
+ jc[2][3].attr = EDG_WLL_QUERY_ATTR_STATUS;
+ jc[2][3].op = EDG_WLL_QUERY_OP_EQUAL;
+ jc[2][3].value.i = EDG_WLL_JOB_SCHEDULED;
+ jc[2][4].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ jc[3] = NULL;
+ edg_wll_QueryJobsExt(ctx, (const edg_wll_QueryRec **)jc, 0, &jobsOut, &statesOut);
+
+ ec[0][0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ ec[0][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec[0][0].attr_id.tag = "color";
+ ec[0][0].value.c = "red";
+ ec[0][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec[1][0].attr = EDG_WLL_QUERY_ATTR_USERTAG;
+ ec[1][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec[1][0].attr_id.tag = "color";
+ ec[1][0].value.c = "red";
+ ec[1][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec[2][0].attr = EDG_WLL_QUERY_ATTR_EVENT_TYPE;
+ ec[2][0].op = EDG_WLL_QUERY_OP_EQUAL;
+ ec[2][0].value.i = EDG_WLL_EVENT_DONE;
+ ec[2][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+ ec[3] = NULL;
+ edg_wll_QueryEventsExt(ctx, (const edg_wll_QueryRec **)jc, (const edg_wll_QueryRec **)ec, &eventsOut);
+ ...
+\end{verbatim}
+\fi