Server IP : 103.119.228.120 / Your IP : 3.143.241.253 Web Server : Apache System : Linux v8.techscape8.com 3.10.0-1160.119.1.el7.tuxcare.els2.x86_64 #1 SMP Mon Jul 15 12:09:18 UTC 2024 x86_64 User : nobody ( 99) PHP Version : 5.6.40 Disable Function : shell_exec,symlink,system,exec,proc_get_status,proc_nice,proc_terminate,define_syslog_variables,syslog,openlog,closelog,escapeshellcmd,passthru,ocinum cols,ini_alter,leak,listen,chgrp,apache_note,apache_setenv,debugger_on,debugger_off,ftp_exec,dl,dll,myshellexec,proc_open,socket_bind,proc_close,escapeshellarg,parse_ini_filepopen,fpassthru,exec,passthru,escapeshellarg,escapeshellcmd,proc_close,proc_open,ini_alter,popen,show_source,proc_nice,proc_terminate,proc_get_status,proc_close,pfsockopen,leak,apache_child_terminate,posix_kill,posix_mkfifo,posix_setpgid,posix_setsid,posix_setuid,dl,symlink,shell_exec,system,dl,passthru,escapeshellarg,escapeshellcmd,myshellexec,c99_buff_prepare,c99_sess_put,fpassthru,getdisfunc,fx29exec,fx29exec2,is_windows,disp_freespace,fx29sh_getupdate,fx29_buff_prepare,fx29_sess_put,fx29shexit,fx29fsearch,fx29ftpbrutecheck,fx29sh_tools,fx29sh_about,milw0rm,imagez,sh_name,myshellexec,checkproxyhost,dosyayicek,c99_buff_prepare,c99_sess_put,c99getsource,c99sh_getupdate,c99fsearch,c99shexit,view_perms,posix_getpwuid,posix_getgrgid,posix_kill,parse_perms,parsesort,view_perms_color,set_encoder_input,ls_setcheckboxall,ls_reverse_all,rsg_read,rsg_glob,selfURL,dispsecinfo,unix2DosTime,addFile,system,get_users,view_size,DirFiles,DirFilesWide,DirPrintHTMLHeaders,GetFilesTotal,GetTitles,GetTimeTotal,GetMatchesCount,GetFileMatchesCount,GetResultFiles,fs_copy_dir,fs_copy_obj,fs_move_dir,fs_move_obj,fs_rmdir,SearchText,getmicrotime MySQL : ON | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /usr/local/ssl/local/ssl/share/doc/postgresql-9.2.24/html/ |
Upload File : |
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <HTML ><HEAD ><TITLE >Asynchronous Command Processing</TITLE ><META NAME="GENERATOR" CONTENT="Modular DocBook HTML Stylesheet Version 1.79"><LINK REV="MADE" HREF="mailto:pgsql-docs@postgresql.org"><LINK REL="HOME" TITLE="PostgreSQL 9.2.24 Documentation" HREF="index.html"><LINK REL="UP" TITLE="libpq - C Library" HREF="libpq.html"><LINK REL="PREVIOUS" TITLE="Command Execution Functions" HREF="libpq-exec.html"><LINK REL="NEXT" TITLE="Retrieving Query Results Row-By-Row" HREF="libpq-single-row-mode.html"><LINK REL="STYLESHEET" TYPE="text/css" HREF="stylesheet.css"><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1"><META NAME="creation" CONTENT="2017-11-06T22:43:11"></HEAD ><BODY CLASS="SECT1" ><DIV CLASS="NAVHEADER" ><TABLE SUMMARY="Header navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TH COLSPAN="5" ALIGN="center" VALIGN="bottom" ><A HREF="index.html" >PostgreSQL 9.2.24 Documentation</A ></TH ></TR ><TR ><TD WIDTH="10%" ALIGN="left" VALIGN="top" ><A TITLE="Command Execution Functions" HREF="libpq-exec.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="10%" ALIGN="left" VALIGN="top" ><A HREF="libpq.html" ACCESSKEY="U" >Up</A ></TD ><TD WIDTH="60%" ALIGN="center" VALIGN="bottom" >Chapter 31. <SPAN CLASS="APPLICATION" >libpq</SPAN > - C Library</TD ><TD WIDTH="20%" ALIGN="right" VALIGN="top" ><A TITLE="Retrieving Query Results Row-By-Row" HREF="libpq-single-row-mode.html" ACCESSKEY="N" >Next</A ></TD ></TR ></TABLE ><HR ALIGN="LEFT" WIDTH="100%"></DIV ><DIV CLASS="SECT1" ><H1 CLASS="SECT1" ><A NAME="LIBPQ-ASYNC" >31.4. Asynchronous Command Processing</A ></H1 ><P > The <CODE CLASS="FUNCTION" >PQexec</CODE > function is adequate for submitting commands in normal, synchronous applications. It has a few deficiencies, however, that can be of importance to some users: <P ></P ></P><UL ><LI ><P > <CODE CLASS="FUNCTION" >PQexec</CODE > waits for the command to be completed. The application might have other work to do (such as maintaining a user interface), in which case it won't want to block waiting for the response. </P ></LI ><LI ><P > Since the execution of the client application is suspended while it waits for the result, it is hard for the application to decide that it would like to try to cancel the ongoing command. (It can be done from a signal handler, but not otherwise.) </P ></LI ><LI ><P > <CODE CLASS="FUNCTION" >PQexec</CODE > can return only one <TT CLASS="STRUCTNAME" >PGresult</TT > structure. If the submitted command string contains multiple <ACRONYM CLASS="ACRONYM" >SQL</ACRONYM > commands, all but the last <TT CLASS="STRUCTNAME" >PGresult</TT > are discarded by <CODE CLASS="FUNCTION" >PQexec</CODE >. </P ></LI ><LI ><P > <CODE CLASS="FUNCTION" >PQexec</CODE > always collects the command's entire result, buffering it in a single <TT CLASS="STRUCTNAME" >PGresult</TT >. While this simplifies error-handling logic for the application, it can be impractical for results containing many rows. </P ></LI ></UL ><P> </P ><P > Applications that do not like these limitations can instead use the underlying functions that <CODE CLASS="FUNCTION" >PQexec</CODE > is built from: <CODE CLASS="FUNCTION" >PQsendQuery</CODE > and <CODE CLASS="FUNCTION" >PQgetResult</CODE >. There are also <CODE CLASS="FUNCTION" >PQsendQueryParams</CODE >, <CODE CLASS="FUNCTION" >PQsendPrepare</CODE >, <CODE CLASS="FUNCTION" >PQsendQueryPrepared</CODE >, <CODE CLASS="FUNCTION" >PQsendDescribePrepared</CODE >, and <CODE CLASS="FUNCTION" >PQsendDescribePortal</CODE >, which can be used with <CODE CLASS="FUNCTION" >PQgetResult</CODE > to duplicate the functionality of <CODE CLASS="FUNCTION" >PQexecParams</CODE >, <CODE CLASS="FUNCTION" >PQprepare</CODE >, <CODE CLASS="FUNCTION" >PQexecPrepared</CODE >, <CODE CLASS="FUNCTION" >PQdescribePrepared</CODE >, and <CODE CLASS="FUNCTION" >PQdescribePortal</CODE > respectively. <P ></P ></P><DIV CLASS="VARIABLELIST" ><DL ><DT ><A NAME="LIBPQ-PQSENDQUERY" ></A ><CODE CLASS="FUNCTION" >PQsendQuery</CODE > </DT ><DD ><P > Submits a command to the server without waiting for the result(s). 1 is returned if the command was successfully dispatched and 0 if not (in which case, use <CODE CLASS="FUNCTION" >PQerrorMessage</CODE > to get more information about the failure). </P><PRE CLASS="SYNOPSIS" >int PQsendQuery(PGconn *conn, const char *command);</PRE ><P> After successfully calling <CODE CLASS="FUNCTION" >PQsendQuery</CODE >, call <CODE CLASS="FUNCTION" >PQgetResult</CODE > one or more times to obtain the results. <CODE CLASS="FUNCTION" >PQsendQuery</CODE > cannot be called again (on the same connection) until <CODE CLASS="FUNCTION" >PQgetResult</CODE > has returned a null pointer, indicating that the command is done. </P ></DD ><DT ><A NAME="LIBPQ-PQSENDQUERYPARAMS" ></A ><CODE CLASS="FUNCTION" >PQsendQueryParams</CODE > </DT ><DD ><P > Submits a command and separate parameters to the server without waiting for the result(s). </P><PRE CLASS="SYNOPSIS" >int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);</PRE ><P> This is equivalent to <CODE CLASS="FUNCTION" >PQsendQuery</CODE > except that query parameters can be specified separately from the query string. The function's parameters are handled identically to <CODE CLASS="FUNCTION" >PQexecParams</CODE >. Like <CODE CLASS="FUNCTION" >PQexecParams</CODE >, it will not work on 2.0-protocol connections, and it allows only one command in the query string. </P ></DD ><DT ><A NAME="LIBPQ-PQSENDPREPARE" ></A ><CODE CLASS="FUNCTION" >PQsendPrepare</CODE > </DT ><DD ><P > Sends a request to create a prepared statement with the given parameters, without waiting for completion. </P><PRE CLASS="SYNOPSIS" >int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes);</PRE ><P> This is an asynchronous version of <CODE CLASS="FUNCTION" >PQprepare</CODE >: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, call <CODE CLASS="FUNCTION" >PQgetResult</CODE > to determine whether the server successfully created the prepared statement. The function's parameters are handled identically to <CODE CLASS="FUNCTION" >PQprepare</CODE >. Like <CODE CLASS="FUNCTION" >PQprepare</CODE >, it will not work on 2.0-protocol connections. </P ></DD ><DT ><A NAME="LIBPQ-PQSENDQUERYPREPARED" ></A ><CODE CLASS="FUNCTION" >PQsendQueryPrepared</CODE > </DT ><DD ><P > Sends a request to execute a prepared statement with given parameters, without waiting for the result(s). </P><PRE CLASS="SYNOPSIS" >int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char * const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat);</PRE ><P> This is similar to <CODE CLASS="FUNCTION" >PQsendQueryParams</CODE >, but the command to be executed is specified by naming a previously-prepared statement, instead of giving a query string. The function's parameters are handled identically to <CODE CLASS="FUNCTION" >PQexecPrepared</CODE >. Like <CODE CLASS="FUNCTION" >PQexecPrepared</CODE >, it will not work on 2.0-protocol connections. </P ></DD ><DT ><A NAME="LIBPQ-PQSENDDESCRIBEPREPARED" ></A ><CODE CLASS="FUNCTION" >PQsendDescribePrepared</CODE > </DT ><DD ><P > Submits a request to obtain information about the specified prepared statement, without waiting for completion. </P><PRE CLASS="SYNOPSIS" >int PQsendDescribePrepared(PGconn *conn, const char *stmtName);</PRE ><P> This is an asynchronous version of <CODE CLASS="FUNCTION" >PQdescribePrepared</CODE >: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, call <CODE CLASS="FUNCTION" >PQgetResult</CODE > to obtain the results. The function's parameters are handled identically to <CODE CLASS="FUNCTION" >PQdescribePrepared</CODE >. Like <CODE CLASS="FUNCTION" >PQdescribePrepared</CODE >, it will not work on 2.0-protocol connections. </P ></DD ><DT ><A NAME="LIBPQ-PQSENDDESCRIBEPORTAL" ></A ><CODE CLASS="FUNCTION" >PQsendDescribePortal</CODE > </DT ><DD ><P > Submits a request to obtain information about the specified portal, without waiting for completion. </P><PRE CLASS="SYNOPSIS" >int PQsendDescribePortal(PGconn *conn, const char *portalName);</PRE ><P> This is an asynchronous version of <CODE CLASS="FUNCTION" >PQdescribePortal</CODE >: it returns 1 if it was able to dispatch the request, and 0 if not. After a successful call, call <CODE CLASS="FUNCTION" >PQgetResult</CODE > to obtain the results. The function's parameters are handled identically to <CODE CLASS="FUNCTION" >PQdescribePortal</CODE >. Like <CODE CLASS="FUNCTION" >PQdescribePortal</CODE >, it will not work on 2.0-protocol connections. </P ></DD ><DT ><A NAME="LIBPQ-PQGETRESULT" ></A ><CODE CLASS="FUNCTION" >PQgetResult</CODE > </DT ><DD ><P > Waits for the next result from a prior <CODE CLASS="FUNCTION" >PQsendQuery</CODE >, <CODE CLASS="FUNCTION" >PQsendQueryParams</CODE >, <CODE CLASS="FUNCTION" >PQsendPrepare</CODE >, <CODE CLASS="FUNCTION" >PQsendQueryPrepared</CODE >, <CODE CLASS="FUNCTION" >PQsendDescribePrepared</CODE >, or <CODE CLASS="FUNCTION" >PQsendDescribePortal</CODE > call, and returns it. A null pointer is returned when the command is complete and there will be no more results. </P><PRE CLASS="SYNOPSIS" >PGresult *PQgetResult(PGconn *conn);</PRE ><P> </P ><P > <CODE CLASS="FUNCTION" >PQgetResult</CODE > must be called repeatedly until it returns a null pointer, indicating that the command is done. (If called when no command is active, <CODE CLASS="FUNCTION" >PQgetResult</CODE > will just return a null pointer at once.) Each non-null result from <CODE CLASS="FUNCTION" >PQgetResult</CODE > should be processed using the same <TT CLASS="STRUCTNAME" >PGresult</TT > accessor functions previously described. Don't forget to free each result object with <CODE CLASS="FUNCTION" >PQclear</CODE > when done with it. Note that <CODE CLASS="FUNCTION" >PQgetResult</CODE > will block only if a command is active and the necessary response data has not yet been read by <CODE CLASS="FUNCTION" >PQconsumeInput</CODE >. </P ><DIV CLASS="NOTE" ><BLOCKQUOTE CLASS="NOTE" ><P ><B >Note: </B > Even when <CODE CLASS="FUNCTION" >PQresultStatus</CODE > indicates a fatal error, <CODE CLASS="FUNCTION" >PQgetResult</CODE > should be called until it returns a null pointer, to allow <SPAN CLASS="APPLICATION" >libpq</SPAN > to process the error information completely. </P ></BLOCKQUOTE ></DIV ></DD ></DL ></DIV ><P> </P ><P > Using <CODE CLASS="FUNCTION" >PQsendQuery</CODE > and <CODE CLASS="FUNCTION" >PQgetResult</CODE > solves one of <CODE CLASS="FUNCTION" >PQexec</CODE >'s problems: If a command string contains multiple <ACRONYM CLASS="ACRONYM" >SQL</ACRONYM > commands, the results of those commands can be obtained individually. (This allows a simple form of overlapped processing, by the way: the client can be handling the results of one command while the server is still working on later queries in the same command string.) </P ><P > Another frequently-desired feature that can be obtained with <CODE CLASS="FUNCTION" >PQsendQuery</CODE > and <CODE CLASS="FUNCTION" >PQgetResult</CODE > is retrieving large query results a row at a time. This is discussed in <A HREF="libpq-single-row-mode.html" >Section 31.5</A >. </P ><P > By itself, calling <CODE CLASS="FUNCTION" >PQgetResult</CODE > will still cause the client to block until the server completes the next <ACRONYM CLASS="ACRONYM" >SQL</ACRONYM > command. This can be avoided by proper use of two more functions: <P ></P ></P><DIV CLASS="VARIABLELIST" ><DL ><DT ><A NAME="LIBPQ-PQCONSUMEINPUT" ></A ><CODE CLASS="FUNCTION" >PQconsumeInput</CODE > </DT ><DD ><P > If input is available from the server, consume it. </P><PRE CLASS="SYNOPSIS" >int PQconsumeInput(PGconn *conn);</PRE ><P> </P ><P > <CODE CLASS="FUNCTION" >PQconsumeInput</CODE > normally returns 1 indicating <SPAN CLASS="QUOTE" >"no error"</SPAN >, but returns 0 if there was some kind of trouble (in which case <CODE CLASS="FUNCTION" >PQerrorMessage</CODE > can be consulted). Note that the result does not say whether any input data was actually collected. After calling <CODE CLASS="FUNCTION" >PQconsumeInput</CODE >, the application can check <CODE CLASS="FUNCTION" >PQisBusy</CODE > and/or <CODE CLASS="FUNCTION" >PQnotifies</CODE > to see if their state has changed. </P ><P > <CODE CLASS="FUNCTION" >PQconsumeInput</CODE > can be called even if the application is not prepared to deal with a result or notification just yet. The function will read available data and save it in a buffer, thereby causing a <CODE CLASS="FUNCTION" >select()</CODE > read-ready indication to go away. The application can thus use <CODE CLASS="FUNCTION" >PQconsumeInput</CODE > to clear the <CODE CLASS="FUNCTION" >select()</CODE > condition immediately, and then examine the results at leisure. </P ></DD ><DT ><A NAME="LIBPQ-PQISBUSY" ></A ><CODE CLASS="FUNCTION" >PQisBusy</CODE > </DT ><DD ><P > Returns 1 if a command is busy, that is, <CODE CLASS="FUNCTION" >PQgetResult</CODE > would block waiting for input. A 0 return indicates that <CODE CLASS="FUNCTION" >PQgetResult</CODE > can be called with assurance of not blocking. </P><PRE CLASS="SYNOPSIS" >int PQisBusy(PGconn *conn);</PRE ><P> </P ><P > <CODE CLASS="FUNCTION" >PQisBusy</CODE > will not itself attempt to read data from the server; therefore <CODE CLASS="FUNCTION" >PQconsumeInput</CODE > must be invoked first, or the busy state will never end. </P ></DD ></DL ></DIV ><P> </P ><P > A typical application using these functions will have a main loop that uses <CODE CLASS="FUNCTION" >select()</CODE > or <CODE CLASS="FUNCTION" >poll()</CODE > to wait for all the conditions that it must respond to. One of the conditions will be input available from the server, which in terms of <CODE CLASS="FUNCTION" >select()</CODE > means readable data on the file descriptor identified by <CODE CLASS="FUNCTION" >PQsocket</CODE >. When the main loop detects input ready, it should call <CODE CLASS="FUNCTION" >PQconsumeInput</CODE > to read the input. It can then call <CODE CLASS="FUNCTION" >PQisBusy</CODE >, followed by <CODE CLASS="FUNCTION" >PQgetResult</CODE > if <CODE CLASS="FUNCTION" >PQisBusy</CODE > returns false (0). It can also call <CODE CLASS="FUNCTION" >PQnotifies</CODE > to detect <TT CLASS="COMMAND" >NOTIFY</TT > messages (see <A HREF="libpq-notify.html" >Section 31.8</A >). </P ><P > A client that uses <CODE CLASS="FUNCTION" >PQsendQuery</CODE >/<CODE CLASS="FUNCTION" >PQgetResult</CODE > can also attempt to cancel a command that is still being processed by the server; see <A HREF="libpq-cancel.html" >Section 31.6</A >. But regardless of the return value of <CODE CLASS="FUNCTION" >PQcancel</CODE >, the application must continue with the normal result-reading sequence using <CODE CLASS="FUNCTION" >PQgetResult</CODE >. A successful cancellation will simply cause the command to terminate sooner than it would have otherwise. </P ><P > By using the functions described above, it is possible to avoid blocking while waiting for input from the database server. However, it is still possible that the application will block waiting to send output to the server. This is relatively uncommon but can happen if very long SQL commands or data values are sent. (It is much more probable if the application sends data via <TT CLASS="COMMAND" >COPY IN</TT >, however.) To prevent this possibility and achieve completely nonblocking database operation, the following additional functions can be used. <P ></P ></P><DIV CLASS="VARIABLELIST" ><DL ><DT ><A NAME="LIBPQ-PQSETNONBLOCKING" ></A ><CODE CLASS="FUNCTION" >PQsetnonblocking</CODE > </DT ><DD ><P > Sets the nonblocking status of the connection. </P><PRE CLASS="SYNOPSIS" >int PQsetnonblocking(PGconn *conn, int arg);</PRE ><P> </P ><P > Sets the state of the connection to nonblocking if <TT CLASS="PARAMETER" >arg</TT > is 1, or blocking if <TT CLASS="PARAMETER" >arg</TT > is 0. Returns 0 if OK, -1 if error. </P ><P > In the nonblocking state, calls to <CODE CLASS="FUNCTION" >PQsendQuery</CODE >, <CODE CLASS="FUNCTION" >PQputline</CODE >, <CODE CLASS="FUNCTION" >PQputnbytes</CODE >, and <CODE CLASS="FUNCTION" >PQendcopy</CODE > will not block but instead return an error if they need to be called again. </P ><P > Note that <CODE CLASS="FUNCTION" >PQexec</CODE > does not honor nonblocking mode; if it is called, it will act in blocking fashion anyway. </P ></DD ><DT ><A NAME="LIBPQ-PQISNONBLOCKING" ></A ><CODE CLASS="FUNCTION" >PQisnonblocking</CODE > </DT ><DD ><P > Returns the blocking status of the database connection. </P><PRE CLASS="SYNOPSIS" >int PQisnonblocking(const PGconn *conn);</PRE ><P> </P ><P > Returns 1 if the connection is set to nonblocking mode and 0 if blocking. </P ></DD ><DT ><A NAME="LIBPQ-PQFLUSH" ></A ><CODE CLASS="FUNCTION" >PQflush</CODE > </DT ><DD ><P > Attempts to flush any queued output data to the server. Returns 0 if successful (or if the send queue is empty), -1 if it failed for some reason, or 1 if it was unable to send all the data in the send queue yet (this case can only occur if the connection is nonblocking). </P><PRE CLASS="SYNOPSIS" >int PQflush(PGconn *conn);</PRE ><P> </P ></DD ></DL ></DIV ><P> </P ><P > After sending any command or data on a nonblocking connection, call <CODE CLASS="FUNCTION" >PQflush</CODE >. If it returns 1, wait for the socket to become read- or write-ready. If it becomes write-ready, call <CODE CLASS="FUNCTION" >PQflush</CODE > again. If it becomes read-ready, call <CODE CLASS="FUNCTION" >PQconsumeInput</CODE >, then call <CODE CLASS="FUNCTION" >PQflush</CODE > again. Repeat until <CODE CLASS="FUNCTION" >PQflush</CODE > returns 0. (It is necessary to check for read-ready and drain the input with <CODE CLASS="FUNCTION" >PQconsumeInput</CODE >, because the server can block trying to send us data, e.g. NOTICE messages, and won't read our data until we read its.) Once <CODE CLASS="FUNCTION" >PQflush</CODE > returns 0, wait for the socket to be read-ready and then read the response as described above. </P ></DIV ><DIV CLASS="NAVFOOTER" ><HR ALIGN="LEFT" WIDTH="100%"><TABLE SUMMARY="Footer navigation table" WIDTH="100%" BORDER="0" CELLPADDING="0" CELLSPACING="0" ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" ><A HREF="libpq-exec.html" ACCESSKEY="P" >Prev</A ></TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="index.html" ACCESSKEY="H" >Home</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" ><A HREF="libpq-single-row-mode.html" ACCESSKEY="N" >Next</A ></TD ></TR ><TR ><TD WIDTH="33%" ALIGN="left" VALIGN="top" >Command Execution Functions</TD ><TD WIDTH="34%" ALIGN="center" VALIGN="top" ><A HREF="libpq.html" ACCESSKEY="U" >Up</A ></TD ><TD WIDTH="33%" ALIGN="right" VALIGN="top" >Retrieving Query Results Row-By-Row</TD ></TR ></TABLE ></DIV ></BODY ></HTML >