You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modperl@perl.apache.org by quagly <qu...@home.com> on 2000/12/23 18:38:11 UTC

can't flush buffers?

	I posted something like this a week ago, but typos in my message kept
anyone from understanding the issue.

	I am trying to return each row to the client as it comes from the
database, instead of waiting for all the rows to be returned before
displaying them.  

	I have set $|=1 and added $r->flush; after every print statement ( I
realize this is redundant ) but to no avail.  

This is the relevant code:

while ($sth->fetch) {
   $r->print ("<TR>",
       map("<TD>$_</TD>",@cols),
       "</TR>");
  $r->rflush;
}

Here is the complete package:

package Sql::Client;

use Apache::Request;
use strict;
use warnings;
use Apache::Constants qw(:common);

my $r;                                      #request
my $apr;                                   #Apache::Request 
my $host;                                  #hostname of remote user
my $sql;                                    #sql to execute

$|=1;

sub getarray ($) {                                                     

        my $dbh;              # Database handle
        my $sth;                # Statement handle
        my $p_sql;             # sql statement passed as parameter
        my @cols;              #column array to bind results
        my $titles;               # array ref to column headers

        $p_sql = shift;

        # Connect
        $dbh = DBI->connect (
                "DBI:mysql:links_db::localhost",
                "nobody",
                "somebody",
                {
                        PrintError => 1,            # warn() on errors
                        RaiseError => 0,           # don't die on error
                        AutoCommit => 1,        # commit executes
immediately
                }
        );

        # prepare statment
        $sth = $dbh->prepare($p_sql);

        $sth->execute;

        $titles = $sth->{NAME_uc};
        #--------------
        # for minimal memory use, do it this way
        @cols[0..$#$titles] = ();
        $sth->bind_columns(\(@cols));
        $r->print( "<TABLE BORDER>");
        $r->print ("<TR>",
                map("<TD>$_</TD>",@$titles),
                                                "</TR>");
        while ($sth->fetch) {
                $r->print ("<TR>",
                        map("<TD>$_</TD>",@cols),
                                                        "</TR>");
                $r->rflush;
        }
        $r->print ("</TABLE>");
        return; 
}


sub handler {
        $r = shift;
        $apr =  Apache::Request->new($r);
        $sql = $apr->param('sql') || 'SELECT';
        $sql='SELECT' if  $apr->param('reset');

        $r->content_type( 'text/html' );
        $r->send_http_header;
        return OK if $r->header_only;
        $host = $r->get_remote_host;
        $r->print(<<HTMLEND);
<HTML>
<HEAD>
<LINK REL="stylesheet" TYPE="text/css" 
    HREF="/styles/lightstyle.css" 
>
<TITLE>Hello $host</TITLE>
<BODY>
<H1>Sql Client</H1>
<FORM METHOD="POST">
<P>Enter your Select Statement:
<BR>
<TEXTAREA  NAME="sql" ROWS=8 COLS=60 WRAP>$sql</TEXTAREA>
<BR>
<INPUT TYPE="SUBMIT" VALUE="Submit">
<INPUT TYPE="SUBMIT" NAME="reset" VALUE="Reset">
</FORM>
HTMLEND
        $r->rflush;
        getarray($sql) unless $sql =~ /^SELECT$/;

        $r->print(<<HTMLEND);
</BODY>
</HTML>
HTMLEND
        return OK;
}
1;

Re: can't flush buffers?

Posted by Carlos Ramirez <cr...@gte.net>.
Browsers cannot display a table until it has enough information about
the table, this includes the end table tag. Even if you flush output,
the browser does not display until it encounters the end table tag. So
from the looks of your example, the end table tag
is not printed until all records are retreived, which is what I think
you are trying to avoid.

You might want to try printing smaller tables at a time. And use width
property of the TD tag to maintain the same format for all tables. This
will display the retreived data incrementally, which is what you want,
right?

-Carlos


quagly wrote:
> 
>         I posted something like this a week ago, but typos in my message kept
> anyone from understanding the issue.
> 
>         I am trying to return each row to the client as it comes from the
> database, instead of waiting for all the rows to be returned before
> displaying them.
> 
>         I have set $|=1 and added $r->flush; after every print statement ( I
> realize this is redundant ) but to no avail.
> 
> This is the relevant code:
> 
> while ($sth->fetch) {
>    $r->print ("<TR>",
>        map("<TD>$_</TD>",@cols),
>        "</TR>");
>   $r->rflush;
> }
> 
> Here is the complete package:
> 
> package Sql::Client;
> 
> use Apache::Request;
> use strict;
> use warnings;
> use Apache::Constants qw(:common);
> 
> my $r;                                      #request
> my $apr;                                   #Apache::Request
> my $host;                                  #hostname of remote user
> my $sql;                                    #sql to execute
> 
> $|=1;
> 
> sub getarray ($) {
> 
>         my $dbh;              # Database handle
>         my $sth;                # Statement handle
>         my $p_sql;             # sql statement passed as parameter
>         my @cols;              #column array to bind results
>         my $titles;               # array ref to column headers
> 
>         $p_sql = shift;
> 
>         # Connect
>         $dbh = DBI->connect (
>                 "DBI:mysql:links_db::localhost",
>                 "nobody",
>                 "somebody",
>                 {
>                         PrintError => 1,            # warn() on errors
>                         RaiseError => 0,           # don't die on error
>                         AutoCommit => 1,        # commit executes
> immediately
>                 }
>         );
> 
>         # prepare statment
>         $sth = $dbh->prepare($p_sql);
> 
>         $sth->execute;
> 
>         $titles = $sth->{NAME_uc};
>         #--------------
>         # for minimal memory use, do it this way
>         @cols[0..$#$titles] = ();
>         $sth->bind_columns(\(@cols));
>         $r->print( "<TABLE BORDER>");
>         $r->print ("<TR>",
>                 map("<TD>$_</TD>",@$titles),
>                                                 "</TR>");
>         while ($sth->fetch) {
>                 $r->print ("<TR>",
>                         map("<TD>$_</TD>",@cols),
>                                                         "</TR>");
>                 $r->rflush;
>         }
>         $r->print ("</TABLE>");
>         return;
> }
> 
> sub handler {
>         $r = shift;
>         $apr =  Apache::Request->new($r);
>         $sql = $apr->param('sql') || 'SELECT';
>         $sql='SELECT' if  $apr->param('reset');
> 
>         $r->content_type( 'text/html' );
>         $r->send_http_header;
>         return OK if $r->header_only;
>         $host = $r->get_remote_host;
>         $r->print(<<HTMLEND);
> <HTML>
> <HEAD>
> <LINK REL="stylesheet" TYPE="text/css"
>     HREF="/styles/lightstyle.css"
> >
> <TITLE>Hello $host</TITLE>
> <BODY>
> <H1>Sql Client</H1>
> <FORM METHOD="POST">
> <P>Enter your Select Statement:
> <BR>
> <TEXTAREA  NAME="sql" ROWS=8 COLS=60 WRAP>$sql</TEXTAREA>
> <BR>
> <INPUT TYPE="SUBMIT" VALUE="Submit">
> <INPUT TYPE="SUBMIT" NAME="reset" VALUE="Reset">
> </FORM>
> HTMLEND
>         $r->rflush;
>         getarray($sql) unless $sql =~ /^SELECT$/;
> 
>         $r->print(<<HTMLEND);
> </BODY>
> </HTML>
> HTMLEND
>         return OK;
> }
> 1;

-- 
RTFM: Not just an acronym, it's the LAW!

Re: can't flush buffers?

Posted by Les Mikesell <le...@home.com>.
----- Original Message -----
From: "Wesley Darlington" <we...@blackstar.co.uk>
To: <mo...@apache.org>
Sent: Saturday, December 23, 2000 1:44 PM
Subject: Re: can't flush buffers?


> Hi All,
>
> On Sat, Dec 23, 2000 at 09:38:11AM -0800, quagly wrote:
> > This is the relevant code:
> >
> > while ($sth->fetch) {
> >    $r->print ("<TR>",
> >        map("<TD>$_</TD>",@cols),
> >        "</TR>");
> >   $r->rflush;
> > }
>
> A thought is knocking at the back of my head - browsers don't render
> tables until they've got the whole thing. I think. Try sending lots
> of single-row tables instead of one big table...?

Yes, this is most likely the real problem - if the browser has to compute
the
column widths it can't do it until it has seen the end of the table.  You
can
avoid it by specifying the widths in the table tag or by closing and
restarting
the table after some reasonable sized number of rows.

        Les Mikesell
            lesmikesell@home.com



Re: can't flush buffers?

Posted by Wesley Darlington <we...@blackstar.co.uk>.
Hi All,

On Sat, Dec 23, 2000 at 09:38:11AM -0800, quagly wrote:
> This is the relevant code:
> 
> while ($sth->fetch) {
>    $r->print ("<TR>",
>        map("<TD>$_</TD>",@cols),
>        "</TR>");
>   $r->rflush;
> }

A thought is knocking at the back of my head - browsers don't render
tables until they've got the whole thing. I think. Try sending lots
of single-row tables instead of one big table...?

Also, you may find it useful to send output in chunks. Keep a wee
counter in the loop and send output every $n$ items or so. Maybe.

ATB,
Wesley.

Re: can't flush buffers?

Posted by Stas Bekman <st...@stason.org>.
On Sat, 23 Dec 2000, quagly wrote:

> 
> 	I posted something like this a week ago, but typos in my message kept
> anyone from understanding the issue.
> 
> 	I am trying to return each row to the client as it comes from the
> database, instead of waiting for all the rows to be returned before
> displaying them.  
> 
> 	I have set $|=1 and added $r->flush; after every print statement ( I
> realize this is redundant ) but to no avail.  

gmm, may I suggest the guide?

http://perl.apache.org/guide/performance.html#Work_With_Databases

> This is the relevant code:
> 
> while ($sth->fetch) {
>    $r->print ("<TR>",
>        map("<TD>$_</TD>",@cols),
>        "</TR>");
>   $r->rflush;
> }
> 
> Here is the complete package:
> 
> package Sql::Client;
> 
> use Apache::Request;
> use strict;
> use warnings;
> use Apache::Constants qw(:common);
> 
> my $r;                                      #request
> my $apr;                                   #Apache::Request 
> my $host;                                  #hostname of remote user
> my $sql;                                    #sql to execute
> 
> $|=1;
> 
> sub getarray ($) {                                                     
> 
>         my $dbh;              # Database handle
>         my $sth;                # Statement handle
>         my $p_sql;             # sql statement passed as parameter
>         my @cols;              #column array to bind results
>         my $titles;               # array ref to column headers
> 
>         $p_sql = shift;
> 
>         # Connect
>         $dbh = DBI->connect (
>                 "DBI:mysql:links_db::localhost",
>                 "nobody",
>                 "somebody",
>                 {
>                         PrintError => 1,            # warn() on errors
>                         RaiseError => 0,           # don't die on error
>                         AutoCommit => 1,        # commit executes
> immediately
>                 }
>         );
> 
>         # prepare statment
>         $sth = $dbh->prepare($p_sql);
> 
>         $sth->execute;
> 
>         $titles = $sth->{NAME_uc};
>         #--------------
>         # for minimal memory use, do it this way
>         @cols[0..$#$titles] = ();
>         $sth->bind_columns(\(@cols));
>         $r->print( "<TABLE BORDER>");
>         $r->print ("<TR>",
>                 map("<TD>$_</TD>",@$titles),
>                                                 "</TR>");
>         while ($sth->fetch) {
>                 $r->print ("<TR>",
>                         map("<TD>$_</TD>",@cols),
>                                                         "</TR>");
>                 $r->rflush;
>         }
>         $r->print ("</TABLE>");
>         return; 
> }
> 
> 
> sub handler {
>         $r = shift;
>         $apr =  Apache::Request->new($r);
>         $sql = $apr->param('sql') || 'SELECT';
>         $sql='SELECT' if  $apr->param('reset');
> 
>         $r->content_type( 'text/html' );
>         $r->send_http_header;
>         return OK if $r->header_only;
>         $host = $r->get_remote_host;
>         $r->print(<<HTMLEND);
> <HTML>
> <HEAD>
> <LINK REL="stylesheet" TYPE="text/css" 
>     HREF="/styles/lightstyle.css" 
> >
> <TITLE>Hello $host</TITLE>
> <BODY>
> <H1>Sql Client</H1>
> <FORM METHOD="POST">
> <P>Enter your Select Statement:
> <BR>
> <TEXTAREA  NAME="sql" ROWS=8 COLS=60 WRAP>$sql</TEXTAREA>
> <BR>
> <INPUT TYPE="SUBMIT" VALUE="Submit">
> <INPUT TYPE="SUBMIT" NAME="reset" VALUE="Reset">
> </FORM>
> HTMLEND
>         $r->rflush;
>         getarray($sql) unless $sql =~ /^SELECT$/;
> 
>         $r->print(<<HTMLEND);
> </BODY>
> </HTML>
> HTMLEND
>         return OK;
> }
> 1;
> 



_____________________________________________________________________
Stas Bekman              JAm_pH     --   Just Another mod_perl Hacker
http://stason.org/       mod_perl Guide  http://perl.apache.org/guide 
mailto:stas@stason.org   http://apachetoday.com http://logilune.com/
http://singlesheaven.com http://perl.apache.org http://perlmonth.com/  



Re: can't flush buffers?

Posted by Ken Williams <ke...@forum.swarthmore.edu>.
jlarsen@richweb.com (C. Jon Larsen) wrote:
>quagly wrote:
>> 	I posted something like this a week ago, but typos in my message kept
>> anyone from understanding the issue.
>> 
>> 	I am trying to return each row to the client as it comes from the
>> database, instead of waiting for all the rows to be returned before
>> displaying them.  
>
>Why would you want to do this ?
>
>Writing your application this way will ensure that:
>
>a. end users can crash your server/application.

Huh??

>b. your application will preform poorly on the network.
>

I presume that the application is already performing poorly (delivering
content as one chunk after 60 seconds, for example) and he wants it to
be friendlier (delivering 15 chunks, each of which takes 5 seconds).  

I admit I've never tried doing this (so I'm afraid I can't help quagly),
but I can imagine situations in which it might be appropriate.


  -------------------                            -------------------
  Ken Williams                             Last Bastion of Euclidity
  ken@forum.swarthmore.edu                            The Math Forum

Re: can't flush buffers?

Posted by "C. Jon Larsen" <jl...@richweb.com>.
> 
> 	I posted something like this a week ago, but typos in my message kept
> anyone from understanding the issue.
> 
> 	I am trying to return each row to the client as it comes from the
> database, instead of waiting for all the rows to be returned before
> displaying them.  

Why would you want to do this ?

Writing your application this way will ensure that:

a. end users can crash your server/application.
b. your application will preform poorly on the network.

Buffer your output, and when all the output is collected, print it, and
let tcp deliver the data in network-friendly chunks. If your database is
that slow that you think you need an approach like this, investigate the
possibility of a caching server process that you can sit in front of the
actual db. 

You need to consider what happens when a user executes a query that can
return more rows that a browser can reasonably display. In other words,
having a query results pagination module or feature is probably a must.

If you were writing a stand-alone application that ran on a single cpu
(like MS Access on a local file) in  this style (no pagination, no
buffering) I would consider this to be marginally bad style. Inside a
web-based application, this approach is horrendous.

Just my 2 cents . . .

On Sat, 23 Dec 2000, quagly wrote:

> 
> 	I have set $|=1 and added $r->flush; after every print statement ( I
> realize this is redundant ) but to no avail.  
> 
> This is the relevant code:
> 
> while ($sth->fetch) {
>    $r->print ("<TR>",
>        map("<TD>$_</TD>",@cols),
>        "</TR>");
>   $r->rflush;
> }
> 
> Here is the complete package:
> 
> package Sql::Client;
> 
> use Apache::Request;
> use strict;
> use warnings;
> use Apache::Constants qw(:common);
> 
> my $r;                                      #request
> my $apr;                                   #Apache::Request 
> my $host;                                  #hostname of remote user
> my $sql;                                    #sql to execute
> 
> $|=1;
> 
> sub getarray ($) {                                                     
> 
>         my $dbh;              # Database handle
>         my $sth;                # Statement handle
>         my $p_sql;             # sql statement passed as parameter
>         my @cols;              #column array to bind results
>         my $titles;               # array ref to column headers
> 
>         $p_sql = shift;
> 
>         # Connect
>         $dbh = DBI->connect (
>                 "DBI:mysql:links_db::localhost",
>                 "nobody",
>                 "somebody",
>                 {
>                         PrintError => 1,            # warn() on errors
>                         RaiseError => 0,           # don't die on error
>                         AutoCommit => 1,        # commit executes
> immediately
>                 }
>         );
> 
>         # prepare statment
>         $sth = $dbh->prepare($p_sql);
> 
>         $sth->execute;
> 
>         $titles = $sth->{NAME_uc};
>         #--------------
>         # for minimal memory use, do it this way
>         @cols[0..$#$titles] = ();
>         $sth->bind_columns(\(@cols));
>         $r->print( "<TABLE BORDER>");
>         $r->print ("<TR>",
>                 map("<TD>$_</TD>",@$titles),
>                                                 "</TR>");
>         while ($sth->fetch) {
>                 $r->print ("<TR>",
>                         map("<TD>$_</TD>",@cols),
>                                                         "</TR>");
>                 $r->rflush;
>         }
>         $r->print ("</TABLE>");
>         return; 
> }
> 
> 
> sub handler {
>         $r = shift;
>         $apr =  Apache::Request->new($r);
>         $sql = $apr->param('sql') || 'SELECT';
>         $sql='SELECT' if  $apr->param('reset');
> 
>         $r->content_type( 'text/html' );
>         $r->send_http_header;
>         return OK if $r->header_only;
>         $host = $r->get_remote_host;
>         $r->print(<<HTMLEND);
> <HTML>
> <HEAD>
> <LINK REL="stylesheet" TYPE="text/css" 
>     HREF="/styles/lightstyle.css" 
> >
> <TITLE>Hello $host</TITLE>
> <BODY>
> <H1>Sql Client</H1>
> <FORM METHOD="POST">
> <P>Enter your Select Statement:
> <BR>
> <TEXTAREA  NAME="sql" ROWS=8 COLS=60 WRAP>$sql</TEXTAREA>
> <BR>
> <INPUT TYPE="SUBMIT" VALUE="Submit">
> <INPUT TYPE="SUBMIT" NAME="reset" VALUE="Reset">
> </FORM>
> HTMLEND
>         $r->rflush;
>         getarray($sql) unless $sql =~ /^SELECT$/;
> 
>         $r->print(<<HTMLEND);
> </BODY>
> </HTML>
> HTMLEND
>         return OK;
> }
> 1;
>