#!/usr/pkg/bin/ruby31 -w

#-------------------------------------------------------------------------------------------------
# The test cases of the remote database API
#                                                       Copyright (C) 2006-2008 Mikio Hirabayashi
# This file is part of Tokyo Tyrant.
# Tokyo Tyrant is free software; you can redistribute it and/or modify it under the terms of
# the GNU Lesser General Public License as published by the Free Software Foundation; either
# version 2.1 of the License or any later version.  Tokyo Tyrant is distributed in the hope
# that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
# License for more details.
# You should have received a copy of the GNU Lesser General Public License along with Tokyo
# Tyrant; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA 02111-1307 USA.
#-------------------------------------------------------------------------------------------------


require 'tokyotyrant'
include TokyoTyrant


# main routine
def main
  ARGV.length >= 1 || usage
  if ARGV[0] == "write"
    rv = runwrite
  elsif ARGV[0] == "read"
    rv = runread
  elsif ARGV[0] == "remove"
    rv = runremove
  elsif ARGV[0] == "rcat"
    rv = runrcat
  elsif ARGV[0] == "misc"
    rv = runmisc
  elsif ARGV[0] == "table"
    rv = runtable
  else
    usage
  end
  GC.start
  return rv
end


# print the usage and exit
def usage
  STDERR.printf("%s: test cases of the remote database API\n", $progname)
  STDERR.printf("\n")
  STDERR.printf("usage:\n")
  STDERR.printf("  %s write [-port num] [-tout num] [-nr] [-rnd] host rnum\n", $progname)
  STDERR.printf("  %s read [-port num] [-tout num] [-mul num] [-rnd] host rnum\n", $progname)
  STDERR.printf("  %s remove [-port num] [-tout num] [-rnd] host rnum\n", $progname)
  STDERR.printf("  %s rcat [-port num] [-tout num] [-shl num] [-dai|-dad] [-ext name]" +
                " [-xlg|-xlr] host rnum\n", $progname)
  STDERR.printf("  %s misc [-port num] [-tout num] host rnum\n", $progname)
  STDERR.printf("  %s table [-port num] [-tout num] host rnum\n", $progname)
  STDERR.printf("\n")
  exit(1)
end


# print error message of remote database
def eprint(rdb, func)
  ecode = rdb.ecode
  STDERR.printf("%s: %s: error: %d: %s\n", $progname, func, ecode, rdb.errmsg(ecode))
end


# parse arguments of write command
def runwrite
  host = nil
  rnum = nil
  port = 1978
  tout = 0
  nr = false
  rnd = false
  i = 1
  while i < ARGV.length
    if !host && ARGV[i] =~ /^-/
      if ARGV[i] == "-port"
        usage if (i += 1) >= ARGV.length
        port = ARGV[i].to_i
      elsif ARGV[i] == "-tout"
        usage if (i += 1) >= ARGV.length
        tout = ARGV[i].to_i
      elsif ARGV[i] == "-nr"
        nr = true
      elsif ARGV[i] == "-rnd"
        rnd = true
      else
        usage
      end
    elsif !host
      host = ARGV[i]
    elsif !rnum
      rnum = ARGV[i].to_i
    else
      usage
    end
    i += 1
  end
  usage if !host || !rnum || rnum < 1
  rv = procwrite(host, port, tout, rnum, nr, rnd)
  return rv
end


# parse arguments of read command
def runread
  host = nil
  port = 1978
  tout = 0
  mul = 0
  rnd = false
  i = 1
  while i < ARGV.length
    if !host && ARGV[i] =~ /^-/
      if ARGV[i] == "-port"
        usage if (i += 1) >= ARGV.length
        port = ARGV[i].to_i
      elsif ARGV[i] == "-tout"
        usage if (i += 1) >= ARGV.length
        tout = ARGV[i].to_i
      elsif ARGV[i] == "-mul"
        usage if (i += 1) >= ARGV.length
        mul = ARGV[i].to_i
      elsif ARGV[i] == "-rnd"
        rnd = true
      else
        usage
      end
    elsif !host
      host = ARGV[i]
    else
      usage
    end
    i += 1
  end
  usage if !host
  rv = procread(host, port, tout, mul, rnd)
  return rv
end


# parse arguments of remove command
def runremove
  host = nil
  port = 1978
  tout = 0
  rnd = false
  i = 1
  while i < ARGV.length
    if !host && ARGV[i] =~ /^-/
      if ARGV[i] == "-port"
        usage if (i += 1) >= ARGV.length
        port = ARGV[i].to_i
      elsif ARGV[i] == "-tout"
        usage if (i += 1) >= ARGV.length
        tout = ARGV[i].to_i
      elsif ARGV[i] == "-rnd"
        rnd = true
      else
        usage
      end
    elsif !host
      host = ARGV[i]
    else
      usage
    end
    i += 1
  end
  usage if !host
  rv = procremove(host, port, tout, rnd)
  return rv
end


# parse arguments of rcat command
def runrcat
  host = nil
  rnum = nil
  port = 1978
  tout = 0
  shl = 0
  dai = false
  dad = false
  ext = nil
  xopts = 0
  i = 1
  while i < ARGV.length
    if !host && ARGV[i] =~ /^-/
      if ARGV[i] == "-port"
        usage if (i += 1) >= ARGV.length
        port = ARGV[i].to_i
      elsif ARGV[i] == "-tout"
        usage if (i += 1) >= ARGV.length
        tout = ARGV[i].to_i
      elsif ARGV[i] == "-shl"
        usage if (i += 1) >= ARGV.length
        shl = ARGV[i].to_i
      elsif ARGV[i] == "-dai"
        dai = true
      elsif ARGV[i] == "-dad"
        dad = true
      elsif ARGV[i] == "-ext"
        usage if (i += 1) >= ARGV.length
        ext = ARGV[i]
      elsif ARGV[i] == "-xlr"
        xopts |= RDB::XOLCKREC
      elsif ARGV[i] == "-xlg"
        xopts |= RDB::XOLCKGLB
      else
        usage
      end
    elsif !host
      host = ARGV[i]
    elsif !rnum
      rnum = ARGV[i].to_i
    else
      usage
    end
    i += 1
  end
  usage if !host || !rnum || rnum < 1
  rv = procrcat(host, port, tout, rnum, shl, dai, dad, ext, xopts)
  return rv
end


# parse arguments of misc command
def runmisc
  host = nil
  rnum = nil
  port = 1978
  tout = 0
  i = 1
  while i < ARGV.length
    if !host && ARGV[i] =~ /^-/
      if ARGV[i] == "-port"
        usage if (i += 1) >= ARGV.length
        port = ARGV[i].to_i
      elsif ARGV[i] == "-tout"
        usage if (i += 1) >= ARGV.length
        tout = ARGV[i].to_i
      else
        usage
      end
    elsif !host
      host = ARGV[i]
    elsif !rnum
      rnum = ARGV[i].to_i
    else
      usage
    end
    i += 1
  end
  usage if !host || !rnum || rnum < 1
  rv = procmisc(host, port, tout, rnum)
  return rv
end


# parse arguments of table command
def runtable
  host = nil
  rnum = nil
  port = 1978
  tout = 0
  i = 1
  while i < ARGV.length
    if !host && ARGV[i] =~ /^-/
      if ARGV[i] == "-port"
        usage if (i += 1) >= ARGV.length
        port = ARGV[i].to_i
      elsif ARGV[i] == "-tout"
        usage if (i += 1) >= ARGV.length
        tout = ARGV[i].to_i
      else
        usage
      end
    elsif !host
      host = ARGV[i]
    elsif !rnum
      rnum = ARGV[i].to_i
    else
      usage
    end
    i += 1
  end
  usage if !host || !rnum || rnum < 1
  rv = proctable(host, port, tout, rnum)
  return rv
end


# perform write command
def procwrite(host, port, tout, rnum, nr, rnd)
  printf("<Writing Test>\n  host=%s  port=%d  tout=%d  rnum=%d  nr=%s  rnd=%s\n\n",
         host, port, tout, rnum, nr, rnd)
  err = false
  stime = Time.now
  rdb = RDB::new
  if !rdb.open(host, port, tout)
    eprint(rdb, "open")
    err = true
  end
  if !rnd && !rdb.vanish
    eprint(rdb, "vanish")
    err = true
  end
  for i in 1..rnum
    buf = sprintf("%08d", rnd ? rand(rnum) + 1: i)
    if nr
      if !rdb.putnr(buf, buf)
        eprint(rdb, "putnr")
        err = true
        break
      end
    else
      if !rdb.put(buf, buf)
        eprint(rdb, "put")
        err = true
        break
      end
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("record number: %d\n", rdb.rnum)
  printf("size: %d\n", rdb.size)
  if !rdb.close
    eprint(rdb, "close")
    err = true
  end
  printf("time: %.3f\n", Time.now - stime)
  printf("%s\n\n", err ? "error" : "ok")
  return err ? 1 : 0
end


# perform read command
def procread(host, port, tout, mul, rnd)
  printf("<Reading Test>\n  host=%s  port=%d  tout=%d  mul=%d  rnd=%s\n\n",
         host, port, tout, mul, rnd)
  err = false
  stime = Time.now
  rdb = RDB::new
  if !rdb.open(host, port, tout)
    eprint(rdb, "open")
    err = true
  end
  recs = Hash::new
  rnum = rdb.rnum
  for i in 1..rnum
    buf = sprintf("%08d", rnd ? rand(rnum) + 1: i)
    if mul > 1
      recs[buf] = ""
      if i % mul == 0
        if rdb.mget(recs) < 0
          eprint(rdb, "mget")
          err = true
          break
        end
        recs.clear
      end
    else
      if !rdb.get(buf) && !rnd
        eprint(rdb, "get")
        err = true
        break
      end
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("record number: %d\n", rdb.rnum)
  printf("size: %d\n", rdb.size)
  if !rdb.close
    eprint(rdb, "close")
    err = true
  end
  printf("time: %.3f\n", Time.now - stime)
  printf("%s\n\n", err ? "error" : "ok")
  return err ? 1 : 0
end


# perform remove command
def procremove(host, port, tout, rnd)
  printf("<Removing Test>\n  host=%s  port=%d  tout=%d  rnd=%s\n\n",
         host, port, tout, rnd)
  err = false
  stime = Time.now
  rdb = RDB::new
  if !rdb.open(host, port, tout)
    eprint(rdb, "open")
    err = true
  end
  rnum = rdb.rnum
  for i in 1..rnum
    buf = sprintf("%08d", rnd ? rand(rnum) + 1: i)
    if !rdb.out(buf) && !rnd
      eprint(rdb, "out")
      err = true
      break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("record number: %d\n", rdb.rnum)
  printf("size: %d\n", rdb.size)
  if !rdb.close
    eprint(rdb, "close")
    err = true
  end
  printf("time: %.3f\n", Time.now - stime)
  printf("%s\n\n", err ? "error" : "ok")
  return err ? 1 : 0
end


# perform rcat command
def procrcat(host, port, tout, rnum, shl, dai, dad, ext, xopts)
  printf("<Random Concatenating Test>\n  host=%s  port=%d  tout=%d  rnum=%d" +
         "  shl=%d  dai=%s  dad=%s  ext=%s  xopts=%d\n\n",
         host, port, tout, rnum, shl, dai, dad, ext ? ext : "", xopts)
  err = false
  stime = Time.now
  rdb = RDB::new
  if !rdb.open(host, port, tout)
    eprint(rdb, "open")
    err = true
  end
  if !rdb.vanish
    eprint(rdb, "vanish")
    err = true
  end
  for i in 1..rnum
    buf = sprintf("%08d", rand(rnum) + 1)
    if shl > 0
      if !rdb.putshl(buf, buf, shl)
        eprint(rdb, "putshl")
        err = true
        break
      end
    elsif dai
      if !rdb.addint(buf, 1)
        eprint(rdb, "addint")
        err = true
        break
      end
    elsif dad
      if !rdb.adddouble(buf, 1.0)
        eprint(rdb, "adddouble")
        err = true
        break
      end
    elsif ext
      if !rdb.ext(ext, buf, buf, xopts) && rdb.ecode != RDB::EMISC
        eprint(rdb, "ext")
        err = true
        break
      end
    else
      if !rdb.putcat(buf, buf)
        eprint(rdb, "putcat")
        err = true
        break
      end
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("record number: %d\n", rdb.rnum)
  printf("size: %d\n", rdb.size)
  if !rdb.close
    eprint(rdb, "close")
    err = true
  end
  printf("time: %.3f\n", Time.now - stime)
  printf("%s\n\n", err ? "error" : "ok")
  return err ? 1 : 0
end


# perform misc command
def procmisc(host, port, tout, rnum)
  printf("<Miscellaneous Test>\n  host=%s  port=%d  tout=%d  rnum=%d\n\n",
         host, port, tout, rnum)
  err = false
  stime = Time.now
  rdb = RDB::new
  if !rdb.open(host, port, tout)
    eprint(rdb, "open")
    err = true
  end
  if !rdb.vanish
    eprint(rdb, "vanish")
    err = true
  end
  printf("writing:\n")
  for i in 1..rnum
    buf = sprintf("%08d", i)
    if rand(10) > 0
      if !rdb.putkeep(buf, buf)
        eprint(rdb, "putkeep")
        err = true
        break
      end
    else
      if !rdb.putnr(buf, buf)
        eprint(rdb, "putnr")
        err = true
        break
      end
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("reading:\n")
  for i in 1..rnum
    kbuf = sprintf("%08d", i)
    vbuf = rdb.get(kbuf)
    if !vbuf
        eprint(rdb, "get")
        err = true
        break
    end
    if vbuf != kbuf
        eprint(rdb, "(validation)")
        err = true
        break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  if rdb.rnum != rnum
    eprint(rdb, "rnum")
    err = true
  end
  printf("random writing:\n")
  for i in 1..rnum
    kbuf = sprintf("%08d", rand(rnum) + 1)
    vbuf = "*" * rand(32)
    if !rdb.put(kbuf, vbuf)
        eprint(rdb, "put")
        err = true
        break
    end
    rbuf = rdb.get(kbuf)
    if !rbuf
        eprint(rdb, "get")
        err = true
        break
    end
    if rbuf != vbuf
        eprint(rdb, "(validation)")
        err = true
        break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("random erasing:\n")
  for i in 1..rnum
    kbuf = sprintf("%08d", rand(rnum) + 1)
    if !rdb.out(kbuf) && rdb.ecode != RDB::ENOREC
      eprint(rdb, "out")
      err = true
      break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("script extension calling:\n")
  for i in 1..rnum
    buf = sprintf("(%d)", rand(rnum) + 1)
    name = "put"
    case rand(7)
    when 1
      name = "putkeep"
    when 2
      name = "putcat"
    when 3
      name = "out"
    when 4
      name = "get"
    when 5
      name = "iterinit"
    when 6
      name = "iternext"
    end
    xbuf = rdb.ext(name, buf, buf)
    if !xbuf && rdb.ecode != RDB::EMISC
      eprint(rdb, "ext")
      err = true
      break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("checking iterator:\n")
  if !rdb.iterinit
    eprint(rdb, "iterinit")
    err = true
  end
  inum = 0
  while key = rdb.iternext
    inum += 1
    value = rdb.get(key)
    if !value
      eprint(rdb, "get")
      err = true
      break
    end
    if rnum > 250 && inum % (rnum / 250) == 0
      print('.')
      if inum == rnum || inum % (rnum / 10) == 0
        printf(" (%08d)\n", inum)
      end
    end
  end
  printf(" (%08d)\n", inum) if rnum > 250
  if rdb.ecode != RDB::ENOREC || inum != rdb.rnum
    eprint(rdb, "(validation)")
    err = true
  end
  keys = rdb.fwmkeys("0", 10)
  if rdb.rnum >= 10 && keys.size != 10
    eprint(rdb, "fwmkeys")
    err = true
  end
  printf("checking counting:\n")
  for i in 1..rnum
    buf = sprintf("[%d]", rand(rnum) + 1)
    if rand(2) == 0
      if !rdb.addint(buf, 123) && rdb.ecode != RDB::EKEEP
        eprint(rdb, "addint")
        err = true
        break
      end
    else
      if !rdb.adddouble(buf, 123.456) && rdb.ecode != RDB::EKEEP
        eprint(rdb, "addint")
        err = true
        break
      end
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("checking versatile functions:\n")
  args = []
  for i in 1..rnum
    if rand(10) == 0
      name = "putlist"
      case rand(3)
      when 1
        name = "outlist"
      when 2
        name = "getlist"
      end
      if !rdb.misc(name, args)
        eprint(rdb, "misc")
        err = true
        break
      end
      args = []
    else
      buf = sprintf("(%d)", rand(rnum))
      args.push(buf)
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  if !rdb.stat
    eprint(rdb, "stat")
    err = true
  end
  if !rdb.sync
    eprint(rdb, "sync")
    err = true
  end
  if !rdb.optimize
    eprint(rdb, "optimize")
    err = true
  end
  if !rdb.vanish
    eprint(rdb, "vanish")
    err = true
  end
  printf("checking hash-like updating:\n")
  for i in 1..rnum
    buf = sprintf("[%d]", rand(rnum))
    case rand(4)
    when 0
      rdb[buf] = buf
    when 1
      value = rdb[buf]
    when 2
      res = rdb.has_key?(buf)
    when 3
      rdb.delete(buf)
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("checking hash-like iterator:\n")
  inum = 0
  rdb.each do |tkey, tvalue|
    if inum > 0 && rnum > 250 && inum % (rnum / 250) == 0
      print('.')
      if inum == rnum || inum % (rnum / 10) == 0
        printf(" (%08d)\n", inum)
      end
    end
    inum += 1
  end
  printf(" (%08d)\n", inum) if rnum > 250
  rdb.clear
  printf("record number: %d\n", rdb.rnum)
  printf("size: %d\n", rdb.size)
  if !rdb.close
    eprint(rdb, "close")
    err = true
  end
  printf("time: %.3f\n", Time.now - stime)
  printf("%s\n\n", err ? "error" : "ok")
  return err ? 1 : 0
end


# perform table command
def proctable(host, port, tout, rnum)
  printf("<Table Extension Test>\n  host=%s  port=%d  tout=%d  rnum=%d\n\n",
         host, port, tout, rnum)
  err = false
  stime = Time.now
  rdb = RDBTBL::new
  if !rdb.open(host, port, tout)
    eprint(rdb, "open")
    err = true
  end
  if !rdb.vanish
    eprint(rdb, "vanish")
    err = true
  end
  if !rdb.setindex("", RDBTBL::ITDECIMAL)
    eprint(rdb, "setindex")
    err = true
  end
  if !rdb.setindex("str", RDBTBL::ITLEXICAL)
    eprint(rdb, "setindex")
    err = true
  end
  if !rdb.setindex("num", RDBTBL::ITDECIMAL)
    eprint(rdb, "setindex")
    err = true
  end
  if !rdb.setindex("type", RDBTBL::ITDECIMAL)
    eprint(rdb, "setindex")
    err = true
  end
  if !rdb.setindex("flag", RDBTBL::ITTOKEN)
    eprint(rdb, "setindex")
    err = true
  end
  if !rdb.setindex("text", RDBTBL::ITQGRAM)
    eprint(rdb, "setindex")
    err = true
  end
  printf("writing:\n")
  for i in 1..rnum
    id = rdb.genuid
    cols = {
      "str" => id,
      "num" => rand(id) + 1,
      "type" => rand(32) + 1,
    }
    vbuf = ""
    num = rand(5)
    pt = 0
    for j in 1..num
      pt += rand(5) + 1
      vbuf += "," if vbuf.length > 0
      vbuf += pt.to_s
    end
    if vbuf.length > 0
      cols["flag"] = vbuf
      cols["text"] = vbuf
    end
    if !rdb.put(id, cols)
      eprint(rdb, "put")
      err = true
      break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("reading:\n")
  for i in 1..rnum
    if !rdb.get(i)
      eprint(rdb, "get")
      err = true
      break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  recs = { 1 => "", 2 => "", 3 => "", 4 => "" }
  if rdb.mget(recs) != 4 || recs.size != 4 || recs["1"]["str"] != "1"
    eprint(rdb, "mget")
    err = true
  end
  printf("removing:\n")
  for i in 1..rnum
    if rand(2) == 0 && !rdb.out(i)
      eprint(rdb, "out")
      err = true
      break
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  printf("searching:\n")
  qry = RDBQRY::new(rdb)
  names = [ "", "str", "num", "type", "flag", "text", "c1" ]
  ops = [ RDBQRY::QCSTREQ, RDBQRY::QCSTRINC, RDBQRY::QCSTRBW, RDBQRY::QCSTREW, RDBQRY::QCSTRAND,
          RDBQRY::QCSTROR, RDBQRY::QCSTROREQ, RDBQRY::QCSTRRX, RDBQRY::QCNUMEQ, RDBQRY::QCNUMGT,
          RDBQRY::QCNUMGE, RDBQRY::QCNUMLT, RDBQRY::QCNUMLE, RDBQRY::QCNUMBT, RDBQRY::QCNUMOREQ ]
  ftsops = [ RDBQRY::QCFTSPH, RDBQRY::QCFTSAND, RDBQRY::QCFTSOR, RDBQRY::QCFTSEX ]
  types = [ RDBQRY::QOSTRASC, RDBQRY::QOSTRDESC, RDBQRY::QONUMASC, RDBQRY::QONUMDESC ]
  for i in 1..rnum
    qry = RDBQRY::new(rdb) if rand(10) > 0
    cnum = rand(4)
    for j in 1..cnum
      name = names[rand(names.length)]
      op = ops[rand(ops.length)]
      op = ftsops[rand(ftsops.length)] if rand(10) == 0
      op |= RDBQRY::QCNEGATE if rand(20) == 0
      op |= RDBQRY::QCNOIDX if rand(20) == 0
      expr = rand(i).to_s
      expr += "," + rand(i).to_s if rand(10) == 0
      expr += "," + rand(i).to_s if rand(10) == 0
      qry.addcond(name, op, expr)
    end
    if rand(3) != 0
      name = names[rand(names.length)]
      type = types[rand(types.length)]
      qry.setorder(name, type)
    end
    if rand(20) == 0
      qry.setlimit(10, rand(10))
      res = qry.searchget
      res.each do |rcols|
        pkey = rcols[""]
        str = rcols["str"]
        if !pkey || !str || pkey != str
          eprint(rdb, "searchget")
          err = true
          break
        end
      end
      if(qry.searchcount != res.size)
        eprint(rdb, "searchcount")
        err = true
        break
      end
      onum = rdb.rnum
      if !qry.searchout
          eprint(rdb, "searchout")
          err = true
        break
      end
      if rdb.rnum != onum - res.size
        eprint(rdb, "(validation)")
        err = true
        break
      end
    elsif rand(20) == 0
      qry.setlimit(10)
      res = qry.metasearch([ qry ], RDBQRY::MSUNION + rand(3))
    else
      qry.setlimit(rand(i), rand(10)) if rand(3) != 0
      res = qry.search
    end
    if rnum > 250 && i % (rnum / 250) == 0
      print('.')
      if i == rnum || i % (rnum / 10) == 0
        printf(" (%08d)\n", i)
      end
    end
  end
  pkey = rand(rnum)
  rdb.put(pkey, { "name" => "mikio", "birth" => "19780211" })
  if rdb.putkeep(pkey, {})
    eprint(rdb, "putkeep")
    err = true
  elsif rdb.ecode != RDB::EKEEP
    eprint(rdb, "putkeep")
    err = true
  end
  if !rdb.get(pkey)
    eprint(rdb, "get")
    err = true
  end
  if !rdb.out(pkey)
    eprint(rdb, "out")
    err = true
  end
  if rdb.get(pkey)
    eprint(rdb, "get")
    err = true
  elsif rdb.ecode() != RDB::ENOREC
    eprint(rdb, "get")
    err = true
  end
  if rdb.out(pkey)
    eprint(rdb, "out")
    err = true
  elsif rdb.ecode() != RDB::ENOREC
    eprint(rdb, "out")
    err = true
  end
  printf("record number: %d\n", rdb.rnum)
  printf("size: %d\n", rdb.size)
  if !rdb.close
    eprint(rdb, "close")
    err = true
  end
  printf("time: %.3f\n", Time.now - stime)
  printf("%s\n\n", err ? "error" : "ok")
  return err ? 1 : 0
end


# execute main
STDOUT.sync = true
$progname = $0.dup
$progname.gsub!(/.*\//, "")
srand
exit(main)



# END OF FILE
