Thread: "timer" script from SAMS book or equivalent?

"timer" script from SAMS book or equivalent?

From
John Wells
Date:
Guys,

In the book PostgreSQL (2nd ed) the author mentions a timer script he wrote to analyze various performance bits about
PostgreSQL.I've looked everywhere and can't find it. Does anyone know where I can find a copy, or find an equivalent
tool?

Thanks!
John

Re: "timer" script from SAMS book or equivalent?

From
Felipe de Jesús Molina Bravo
Date:
May be can help you \timing in psql



El vie, 05-10-2007 a las 21:32 +0400, John Wells escribió:
> Guys,
>
> In the book PostgreSQL (2nd ed) the author mentions a timer script he wrote to analyze various performance bits about
PostgreSQL.I've looked everywhere and can't find it. Does anyone know where I can find a copy, or find an equivalent
tool?
>
> Thanks!
> John
>
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Have you searched our list archives?
>
>                http://archives.postgresql.org/

Re: "timer" script from SAMS book or equivalent?

From
John Wells
Date:
----- "Felipe de Jesús Molina Bravo" <felipe.molina@inegi.gob.mx> wrote:
> May be can help you \timing in psql
>
> El vie, 05-10-2007 a las 21:32 +0400, John Wells escribió:
> > Guys,
> >
> > In the book PostgreSQL (2nd ed) the author mentions a timer script
> he wrote to analyze various performance bits about PostgreSQL. I've
> looked everywhere and can't find it. Does anyone know where I can find
> a copy, or find an equivalent tool?


I'm afraid it's not even close in functionality. Here is sample results from the "timer" tool he mentions. This is the
kindof data I'm after: 

$ timer "SELECT * FROM recalls"

+-------------+--------------------------------+-------------------------------+
|             |         SEQUENTIAL I/O         |        INDEXED I/O            |
|             |scans |tuples |heap_blks |cached|scans |tuples |idx_blks |cached|
|-------------+------+-------+----------+------+------+-------+---------+------+
|pg_aggregate |    0 |     0 |        1 |    0 |    1 |     1 |       2 |    0 |
|pg_am        |    1 |     1 |        1 |    0 |    0 |     0 |       0 |    0 |
|pg_amop      |    0 |     0 |        2 |   10 |   10 |    24 |       4 |   16 |
|pg_amproc    |    0 |     0 |        1 |    5 |    6 |     6 |       2 |   10 |
|pg_attribute |    0 |     0 |        8 |   14 |   21 |    65 |       6 |   57 |
|pg_cast      |    0 |     0 |        2 |    6 |   60 |     8 |       2 |  118 |
|pg_class     |    4 |   740 |        5 |   32 |   18 |    17 |       7 |   34 |
|pg_database  |    1 |     1 |        1 |    0 |    0 |     0 |       0 |    0 |
|pg_index     |    2 |   146 |        3 |   11 |    8 |    12 |       4 |   12 |
|pg_namespace |    2 |    10 |        1 |    2 |    2 |     1 |       2 |    2 |
|pg_opclass   |    0 |     0 |        2 |   11 |    5 |    73 |       4 |    6 |
|pg_operator  |    0 |     0 |        4 |    6 |   10 |    10 |       4 |   26 |
|pg_proc      |    0 |     0 |        6 |    8 |   14 |    14 |      12 |   31 |
|pg_rewrite   |    0 |     0 |        1 |    1 |    2 |     2 |       2 |    2 |
|pg_shadow    |    0 |     0 |        1 |    2 |    3 |     3 |       4 |    2 |
|pg_statistic |    0 |     0 |        3 |    5 |   33 |     8 |       2 |   64 |
|pg_trigger   |    0 |     0 |        1 |    1 |    2 |     2 |       2 |    2 |
|pg_type      |    0 |     0 |        2 |    5 |    7 |     7 |       2 |   12 |
|recalls      |    1 | 39241 |     4413 |    0 |    0 |     0 |       0 |    0 |
+-------------+------+-------+----------+------+------+-------+---------+------+
|Totals       |   11 | 40139 |     4458 |  119 |  202 |   253 |      61 |  394 |
+-------------+------+-------+----------+------+------+-------+---------+------+


Thanks,
John

Re: "timer" script from SAMS book or equivalent?

From
John Wells
Date:
----- "Owen Hartnett" <owen@clipboardinc.com> wrote:
> At 11:57 PM +0400 10/5/07, John Wells wrote:
> >----- "Felipe de Jesús Molina Bravo" <felipe.molina@inegi.gob.mx>
> wrote:
> >>  May be can help you \timing in psql
> >>
> >>  El vie, 05-10-2007 a las 21:32 +0400, John Wells escribió:
> >>  > Guys,
> >>  >
> >>  > In the book PostgreSQL (2nd ed) the author mentions a timer
> script
> >>  he wrote to analyze various performance bits about PostgreSQL.
> I've
> >>  looked everywhere and can't find it. Does anyone know where I can
> find
> >>  a copy, or find an equivalent tool?
> >
> >I'm afraid it's not even close in functionality. Here is sample
> >results from the "timer" tool he mentions. This is the kind of data
> >I'm after:
> >
>
> This was available on line at www.developers-library.com, but now it
> looks like that has gone away.  Sometimes an author keeps a web site
> for code he uses to supplement a book, but I can't find one for him.
> Here's the code:

Thanks to all. Having problems building it myself. I plan to reimplement in ruby if possible. Will post results.

Thanks,
John

Re: "timer" script from SAMS book or equivalent?

From
"korry.douglas"
Date:
> In the book PostgreSQL (2nd ed) the author mentions a timer script he wrote to analyze various performance bits about
PostgreSQL.I've looked everywhere and can't find it. Does anyone know where I can find a copy, or find an equivalent
tool?
>
Hi John, sorry for the delay in getting back to you.

You can find the timer program at
http://www.conjectrix.com/pgbook/index.html.  You want the sample
data/code for chapter 4.

       -- Korry

Re: "timer" script from SAMS book or equivalent?

From
John Wells
Date:
Could someone explain to me how the connection is initialized below? I'm re-writing the script in Ruby...but this is a
stumblingblock for me in the way the C++ libs work. Does the library initial conn automagically? How would one emulate
thisin another language...would it be to run it as the postgres user and connection to template1? 

Thanks,
John

int main( int argc, char * argv[] )
{
   // how is this being initialized?
   connection conn;

   work tran1( conn, "getBegValues" );

   const result & begTupleValues = getTupleValues( tran1, argc > 2 ?
argv[2] : 0 );
   const result & begBlockValues = getBlockValues( tran1, argc > 2 ?
argv[2] : 0 );

   const result & ignore = tran1.exec( argv[1] );

   tran1.commit();

   sleep( 1 );

   work tran2( conn, "getEndValues" );

   const result & endTupleValues = getTupleValues( tran2, argc > 2 ?
argv[2] : 0 );
   const result & endBlockValues = getBlockValues( tran2, argc > 2 ?
argv[2] : 0 );

   printDeltas( begTupleValues, endTupleValues, begBlockValues,
endBlockValues );


}

Re: "timer" script from SAMS book or equivalent?

From
John Wells
Date:
My Ruby version is found below. Note it requires the postgres-pr ruby driver. Also note I didn't really ruby-ize it to
much...forthe most part it's a one-to-one translation.  

One different thing to note...this script expects you to have postgres-pr installed via rubygems. You can modify the
requirestatements (actually just comment out the rubygems line) if you're is installed in a non-rubygems way. 

Also note, this requires you to define your connection parameters in env variables, which works in my situation.

PG_TIMER_DB   - name of the database
PG_TIMER_USER - name of database user
PG_TIMER_PASS - password of database user
PG_TIMER_URI  - connection uri that postgres-pr understands.
  Examples:
    tcp://localhost:5432
    unix:/tmp/.s.PGSQL.5432


Hope it helps someone else.

###################################################
require 'rubygems'
require 'postgres-pr/connection'


$tupleQuery = "SELECT relname, seq_scan,
                      seq_tup_read, idx_scan,
                      idx_tup_fetch
               FROM pg_stat_all_tables"


$blockQuery = "SELECT relname, heap_blks_read,
                      heap_blks_hit, idx_blks_read,
                      idx_blks_hit
               FROM pg_statio_all_tables"

$use_jdbc = false

def usage
  usstr = <<-EOL
    #{$0} <query>
    Example: #{$0} "select * from users"

    Note: the following environment variables MUST be set:
    PG_TIMER_DB   - name of the database
    PG_TIMER_USER - name of database user
    PG_TIMER_PASS - password of database user
    PG_TIMER_URI  - connection uri that postgres-pr understands.
      Examples:
        tcp://localhost:5432
        unix:/tmp/.s.PGSQL.5432
  EOL
  puts usstr
  exit()
end

$dbname = ENV['PG_TIMER_DB']
$dbuser = ENV['PG_TIMER_USER']
$dbpass = ENV['PG_TIMER_PASS']
$dburi  = ENV['PG_TIMER_URI']

[$dbname, $dbuser, $dbpass, $dburi].each do |one|
  one.nil? && usage()
end

class PostgresPR::Connection::Result
  def get_field_at_row(field, row)
    idx = @fields.collect{|f|f.name}.index field
    @rows[row][idx]
  end
end


class PureDBUtil
    def initialize()
          @conn = PostgresPR::Connection.new($dbname,
                                       $dbuser,
                                       $dbpass,
                                       $dburi)
    end
  def start_tran
      @conn.query("BEGIN")
    end
    def commit
        @conn.query("COMMIT")
    end
    def exec(query)
        @conn.query(query)
    end
end

class JDBCDBUtil
    def initialize(dbname=nil)
    raise Exception, "not implemented"
    end
end

def getTupleValues(tran, table_name=nil)
    if !table_name.nil?
        return tran.exec($tupleQuery + " ORDER by relname")
    else
        return tran.exec($tupleQuery + " WHERE relname = '#{table_name}' ")
    end
end

def getBlockValues(tran, table_name)
    if !table_name.nil?
        return tran.exec($blockQuery + " ORDER by relname")
    else
        return tran.exec($blockQuery + " WHERE relname = '#{table_name}' ")
    end
end

def getDelta(n, beginning, ending, col)
  endVal = 0
  begVal = 0
  endVal = ending.get_field_at_row(col, n)
  begVal = beginning.get_field_at_row(col, n)
  return endVal.to_f - begVal.to_f;
end


def getColumnWidth(res, col)
    max = 0
    res.rows.each do |one|
        if one[col].size > max
            max = one[col].size
        end
    end
    return max
end

def fill(len, c)
    c * len
end


def printDeltas(begTuples, endTuples, begBlocks, endBlocks)
    nameWidth = 15
  str = ""
  str << '+' << fill( nameWidth, '-' ) << "+-----------------------------------+----------------------------------+" <<
"\n";
  str << '|' << fill( nameWidth, ' ' ) << "|         SEQUENTIAL I/O            |        INDEXED I/O               |" <<
"\n";
  str << '|' << fill( nameWidth, ' ' ) << "| scans | tuples |heap_blks |cached | scans | tuples | idx_blks |cached|" <<
"\n";
  str << '|' << fill( nameWidth, '-' ) << "+-------+--------+-----------+------+-------+--------+----------+------+" <<
"\n";
  totSeqScans  = 0
  totSeqTuples = 0
  totHeapBlks  = 0
  totHeapHits  = 0
  totIdxScans  = 0
  totIdxTuples = 0
  totIdxBlks   = 0
  totIdxHits   = 0
  tableCount   = 0

  0.upto begTuples.rows.size-1 do |row|
    seqScans  = getDelta(row, begTuples, endTuples, "seq_scan")
    seqTuples = getDelta(row, begTuples, endTuples, "seq_tup_read")
    heapBlks  = getDelta(row, begBlocks, endBlocks, "heap_blks_read")
    heapHits  = getDelta(row, begBlocks, endBlocks, "heap_blks_hit")
    idxScans  = getDelta(row, begTuples, endTuples, "idx_scan")
    idxTuples = getDelta(row, begTuples, endTuples, "idx_tup_fetch")
    idxBlks   = getDelta(row, begBlocks, endBlocks, "idx_blks_read")
    idxHits   = getDelta(row, begBlocks, endBlocks, "idx_blks_hit")

    if(( seqScans + seqTuples + heapBlks +
         heapHits + idxScans + idxTuples +
         idxBlks + idxHits ) > 0 )
      str << '|' << begTuples.get_field_at_row("relname", row).ljust(nameWidth);
      str << '|' << seqScans.to_s.rjust(6)  << ' '
      str << '|' << seqTuples.to_s.rjust(7) << ' '
      str << '|' << heapBlks.to_s.rjust(10) << ' '
      str << '|' << heapHits.to_s.rjust(5)  << ' '
      str << '|' << idxScans.to_s.rjust(6)  << ' '
      str << '|' << idxTuples.to_s.rjust(7) << ' '
      str << '|' << idxBlks.to_s.rjust(9)   << ' '
      str << '|' << idxHits.to_s.rjust(5)   << ' '
      str << '|' << "\n"

      tableCount   += 1
      totSeqScans  += seqScans
      totSeqTuples += seqTuples
      totHeapBlks  += heapBlks
      totHeapHits  += heapHits
      totIdxScans  += idxScans
      totIdxTuples += idxTuples
      totIdxBlks   += idxBlks
      totIdxHits   += idxHits
    end
  end
  str << '+' << fill( nameWidth, '-' ) <<
         "+-------+--------+-----------+" <<
         "------+-------+--------+----------+------+\n";

  if( tableCount > 1 )
    str << '|' << "Totals".ljust(nameWidth)
    str << '|' << totSeqScans.to_s.rjust(6)  << ' ';
    str << '|' << totSeqTuples.to_s.rjust(7) << ' ';
    str << '|' << totHeapBlks.to_s.rjust(10) << ' ';
    str << '|' << totHeapHits.to_s.rjust(5)  << ' ';

    str << '|' << totIdxScans.to_s.rjust(6)  << ' ';
    str << '|' << totIdxTuples.to_s.rjust(7) << ' ';
    str << '|' << totIdxBlks.to_s.rjust(9)   << ' ';
    str << '|' << totIdxHits.to_s.rjust(5)   << ' ';
    str << '|' << "\n";
    str << '+' << fill( nameWidth, '-' ) <<
         "+-------+--------+-----------+" <<
         "------+-------+--------+----------+------+\n";
  end
  puts str
end


def main(args)
  arg = args[0]

  usage() if arg.nil?
  if $use_jdbc
    db_class = JDBCDBUtil
  else
    db_class = PureDBUtil
  end
  tran1 = db_class.new()
  tran1.start_tran()
  begTupleValues = getTupleValues(tran1, arg)
  begBlockValues = getBlockValues(tran1, arg)
  ignore = tran1.exec(args[0])
  tran1.commit()

  sleep 1

  tran2 = db_class.new()
  endTupleValues = getTupleValues(tran2, arg)
  endBlockValues = getBlockValues(tran2, arg)

  printDeltas(begTupleValues,
              endTupleValues,
              begBlockValues,
              endBlockValues)
end

main(ARGV)

Re: "timer" script from SAMS book or equivalent?

From
John Wells
Date:
Sorry...the first version was a bit rash and left out some features...particularly filtering by table. Here's the patch
tocorrect: 

If anyone wants the corrected version, email me off list.

Thanks,
John


# diff -w pg_timer_old.rb pg_timer.rb
18a19
>   app = File.basename $0
20,21c21,25
<     #{$0} <query>
<     Example: #{$0} "select * from users"
---
>     #{app} <query> [tablename]
>       or
>     #{app} /path/to/file/containing/query.sql [tablename]
>
>     Example: #{app} "select * from users" "users"
54a59
>
78c83
< if !table_name.nil?
---
>       if table_name.nil?
86c91
< if !table_name.nil?
---
>       if table_name.nil?
194c199,211
<   arg = args[0]
---
>   first = args[0]
>   query = nil
>   if !first.nil? and File.exists?(first)
>     File.open(first, "r") do |f|
>       query = f.read
>     end
>   else
>     query = first
>   end
>
>   table = args[1]
>
>   usage() if args.size < 1
196d212
<   usage() if arg.nil?
201a218
>
204,206c221,223
<   begTupleValues = getTupleValues(tran1, arg)
<   begBlockValues = getBlockValues(tran1, arg)
<   ignore = tran1.exec(args[0])
---
>   begTupleValues = getTupleValues(tran1, table)
>   begBlockValues = getBlockValues(tran1, table)
>   ignore = tran1.exec(query)
212,213c229,232
<   endTupleValues = getTupleValues(tran2, arg)
<   endBlockValues = getBlockValues(tran2, arg)
---
>   tran2.start_tran()
>   endTupleValues = getTupleValues(tran2, table)
>   endBlockValues = getBlockValues(tran2, table)
>   tran2.commit()


Re: "timer" script from SAMS book or equivalent?

From
John Wells
Date:
The version sent yesterday was creating a LOT of unnecessary objects when running queries with large result sets. I
tweakedthe postpres-pr classes (via Ruby's very nice open class mechanism). The updated version is below...it took
queriesrunning 10+ minutes down to around 20 seconds. 

Thanks,
John

---------------------
require 'rubygems'
require 'postgres-pr/connection'
require 'postgres-pr/message'


$tupleQuery = "SELECT relname, seq_scan,
                      seq_tup_read, idx_scan,
                      idx_tup_fetch
               FROM pg_stat_all_tables"


$blockQuery = "SELECT relname, heap_blks_read,
                      heap_blks_hit, idx_blks_read,
                      idx_blks_hit
               FROM pg_statio_all_tables"

$use_jdbc = false

def usage
  app = File.basename $0
  usstr = <<-EOL
    #{app} <query> [tablename]
      or
    #{app} /path/to/file/containing/query.sql [tablename]

    Example: #{app} "select * from users" "users"

    Note: the following environment variables MUST be set:
    PG_TIMER_DB   - name of the database
    PG_TIMER_USER - name of database user
    PG_TIMER_PASS - password of database user
    PG_TIMER_URI  - connection uri that postgres-pr understands.
      Examples:
        tcp://localhost:5432
        unix:/tmp/.s.PGSQL.5432
  EOL
  puts usstr
  exit
end

class PostgresPR::Connection::Result
  def get_field_at_row(field, row)
    idx = @fields.collect{|f|f.name}.index field
    @rows[row][idx]
  end
end

class PostgresPR::Connection
  def query_no_results(sql)
    puts "Running query in background. Waiting..."
    @conn << PostgresPR::Query.dump(sql)
    loop do
      msg = PostgresPR::Message.read_without_buffer(@conn)
      case msg
      when PostgresPR::ReadyForQuery
        break
      end
    end
  end
end

class PostgresPR::Message
  def self.read_without_buffer(stream, startup=false)
    type = stream.readbytes(1).unpack('C').first unless startup
    length = stream.readbytes(4).unpack('N').first  # FIXME: length should be signed, not unsigned
    if type==?Z
      buffer = Buffer.of_size(startup ? length : 1+length)
      buffer.write_byte(type) unless startup
      buffer.write_int32_network(length)
      buffer.copy_from_stream(stream, length-4)
      (startup ? StartupMessage : MsgTypeMap[type]).create(buffer)
    else
      stream.read(length - 4)
      return nil
    end
  end
end

class PureDBUtil
    def initialize()

        @conn = PostgresPR::Connection.new($dbname,
                                       $dbuser,
                                       $dbpass,
                                       $dburi)
    end
  def start_tran
      @conn.query("BEGIN")
    end
    def commit(results=true)
        if results
      @conn.query("COMMIT")
    else
      @conn.query_no_results("COMMIT")
    end
    end
    def exec(query, results=true)
        if results
      @conn.query(query)
    else
      @conn.query_no_results(query)
    end
    end
end

class JDBCDBUtil
    def initialize(dbname=nil)
    raise Exception, "not implemented"
    end
end

def getTupleValues(tran, table_name=nil)
    if table_name.nil?
        return tran.exec($tupleQuery + " ORDER by relname")
    else
        return tran.exec($tupleQuery + " WHERE relname = '#{table_name}' ")
    end
end

def getBlockValues(tran, table_name)
    if table_name.nil?
        return tran.exec($blockQuery + " ORDER by relname")
    else
        return tran.exec($blockQuery + " WHERE relname = '#{table_name}' ")
    end
end

def getDelta(n, beginning, ending, col)
  endVal = 0
  begVal = 0
  endVal = ending.get_field_at_row(col, n)
  begVal = beginning.get_field_at_row(col, n)
  return endVal.to_f - begVal.to_f;
end


def gcw(res, col)
    max = 0
    0.upto res.rows.size-1 do |n|
    fld_size = res.get_field_at_row(col, n).size
        if fld_size > max
            max = fld_size
        end
    end
    return max
end

def fill(len, c)
    c * len
end


def printDeltas(begTuples, endTuples, begBlocks, endBlocks)
    nameWidth = gcw(begTuples, "relname")
  str = ""
  str << '+' << fill( nameWidth, '-' ) << "+-----------------------------------+----------------------------------+" <<
"\n";
  str << '|' << fill( nameWidth, ' ' ) << "|         SEQUENTIAL I/O            |        INDEXED I/O               |" <<
"\n";
  str << '|' << fill( nameWidth, ' ' ) << "| scans | tuples |heap_blks  |cached| scans | tuples | idx_blks |cached|" <<
"\n";
  str << '|' << fill( nameWidth, '-' ) << "+-------+--------+-----------+------+-------+--------+----------+------+" <<
"\n";
  totSeqScans  = 0
  totSeqTuples = 0
  totHeapBlks  = 0
  totHeapHits  = 0
  totIdxScans  = 0
  totIdxTuples = 0
  totIdxBlks   = 0
  totIdxHits   = 0
  tableCount   = 0

  0.upto begTuples.rows.size-1 do |row|
    seqScans  = getDelta(row, begTuples, endTuples, "seq_scan")
    seqTuples = getDelta(row, begTuples, endTuples, "seq_tup_read")
    heapBlks  = getDelta(row, begBlocks, endBlocks, "heap_blks_read")
    heapHits  = getDelta(row, begBlocks, endBlocks, "heap_blks_hit")
    idxScans  = getDelta(row, begTuples, endTuples, "idx_scan")
    idxTuples = getDelta(row, begTuples, endTuples, "idx_tup_fetch")
    idxBlks   = getDelta(row, begBlocks, endBlocks, "idx_blks_read")
    idxHits   = getDelta(row, begBlocks, endBlocks, "idx_blks_hit")

    if(( seqScans + seqTuples + heapBlks +
         heapHits + idxScans + idxTuples +
         idxBlks + idxHits ) > 0 )
      str << '|' << begTuples.get_field_at_row("relname", row).ljust(nameWidth);
      str << '|' << seqScans.to_s.rjust(6)  << ' '
      str << '|' << seqTuples.to_s.rjust(7) << ' '
      str << '|' << heapBlks.to_s.rjust(10) << ' '
      str << '|' << heapHits.to_s.rjust(5)  << ' '
      str << '|' << idxScans.to_s.rjust(6)  << ' '
      str << '|' << idxTuples.to_s.rjust(7) << ' '
      str << '|' << idxBlks.to_s.rjust(9)   << ' '
      str << '|' << idxHits.to_s.rjust(5)   << ' '
      str << '|' << "\n"

      tableCount   += 1
      totSeqScans  += seqScans
      totSeqTuples += seqTuples
      totHeapBlks  += heapBlks
      totHeapHits  += heapHits
      totIdxScans  += idxScans
      totIdxTuples += idxTuples
      totIdxBlks   += idxBlks
      totIdxHits   += idxHits
    end
  end
  str << '+' << fill( nameWidth, '-' ) <<
         "+-------+--------+-----------+" <<
         "------+-------+--------+----------+------+\n";

  if( tableCount > 1 )
    str << '|' << "Totals".ljust(nameWidth)
    str << '|' << totSeqScans.to_s.rjust(6)  << ' ';
    str << '|' << totSeqTuples.to_s.rjust(7) << ' ';
    str << '|' << totHeapBlks.to_s.rjust(10) << ' ';
    str << '|' << totHeapHits.to_s.rjust(5)  << ' ';

    str << '|' << totIdxScans.to_s.rjust(6)  << ' ';
    str << '|' << totIdxTuples.to_s.rjust(7) << ' ';
    str << '|' << totIdxBlks.to_s.rjust(9)   << ' ';
    str << '|' << totIdxHits.to_s.rjust(5)   << ' ';
    str << '|' << "\n";
    str << '+' << fill( nameWidth, '-' )  <<
         "+-------+--------+-----------+" <<
         "------+-------+--------+----------+------+\n";
  end
  puts str
end

def main(args)
  $dbname = ENV['PG_TIMER_DB']
  $dbuser = ENV['PG_TIMER_USER']
  $dbpass = ENV['PG_TIMER_PASS']
  $dburi  = ENV['PG_TIMER_URI']
  [$dbname, $dbuser, $dbpass, $dburi].each do |one|
    one.nil? && usage()
  end

  first = args[0]
  query = nil
  if !first.nil? and File.exists?(first)
    File.open(first, "r") do |f|
      query = f.read
    end
  else
    query = first
  end

  table = args[1]

  usage() if args.size < 1

  if $use_jdbc
    db_class = JDBCDBUtil
  else
    db_class = PureDBUtil
  end

  tran1 = db_class.new()
  tran1.start_tran()
  begTupleValues = getTupleValues(tran1, table)
  begBlockValues = getBlockValues(tran1, table)
  tran1.exec(query,false)
  tran1.commit(false)

  sleep 1

  tran2 = db_class.new()
  tran2.start_tran()
  endTupleValues = getTupleValues(tran2, table)
  endBlockValues = getBlockValues(tran2, table)
  tran2.commit()

  printDeltas(begTupleValues,
              endTupleValues,
              begBlockValues,
              endBlockValues)
end

main(ARGV)