# 2014 Jan 08
#
# The author disclaims copyright to this source code.  In place of
# a legal notice, here is a blessing:
#
#    May you do good and not evil.
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
#
# Tests focused on phrase queries.
#

source [file join [file dirname [info script]] fts5_common.tcl]
set testprefix fts5origintext

# If SQLITE_ENABLE_FTS5 is not defined, omit this file.
ifcapable !fts5 {
  finish_test
  return
}

foreach_detail_mode $testprefix {
foreach {tn insttoken} {
  1 0
  2 1
} {
reset_db

sqlite3_fts5_register_origintext db
do_execsql_test $tn.1.0 {
  CREATE VIRTUAL TABLE ft USING fts5(
      x, tokenize="origintext unicode61", detail=%DETAIL%
  );
  INSERT INTO ft(ft, rank) VALUES('insttoken', $insttoken);
  CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance);
}

do_execsql_test $tn.1.1 {
  INSERT INTO ft VALUES('Hello world');
}

do_execsql_test $tn.1.2 {
  INSERT INTO ft(ft) VALUES('integrity-check');
}

proc b {x} { string map [list "\0" "."] $x }
db func b b

do_execsql_test $tn.1.3 {
  select b(term) from vocab;
} {
  hello.Hello
  world
}

do_execsql_test $tn.1.4 {
  SELECT rowid FROM ft('Hello');
} {1}

#-------------------------------------------------------------------------
reset_db

# Return a random integer between 0 and n-1.
#
proc random {n} {
  expr {abs(int(rand()*$n))}
}

proc select_one {list} {
  set n [llength $list]
  lindex $list [random $n]
}

proc term {} {
  set first_letter {
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
  }

  set term [select_one $first_letter]
  append term [random 100]
}

proc document {} {
  set nTerm [expr [random 5] + 5]
  set doc ""
  for {set ii 0} {$ii < $nTerm} {incr ii} {
    lappend doc [term]
  }
  set doc
}
db func document document

sqlite3_fts5_register_origintext db
do_execsql_test $tn.2.0 {
  CREATE VIRTUAL TABLE ft USING fts5(
      x, tokenize="origintext unicode61", detail=%DETAIL%
  );
  INSERT INTO ft(ft, rank) VALUES('insttoken', $insttoken);
  INSERT INTO ft(ft, rank) VALUES('pgsz', 128);
  CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance);
}

do_test $tn.2.1 {
  for {set ii 0} {$ii < 500} {incr ii} {
    execsql { INSERT INTO ft VALUES( document() ) }
  }
} {}

do_execsql_test $tn.2.2 {
  INSERT INTO ft(ft) VALUES('integrity-check');
}

do_execsql_test $tn.2.3 {
  INSERT INTO ft(ft, rank) VALUES('merge', 16);
}

do_execsql_test $tn.2.4 {
  INSERT INTO ft(ft) VALUES('integrity-check');
}

do_execsql_test $tn.2.5 {
  INSERT INTO ft(ft) VALUES('optimize');
}

#-------------------------------------------------------------------------
reset_db

sqlite3_fts5_register_origintext db
do_execsql_test $tn.3.0 {
  CREATE VIRTUAL TABLE ft USING fts5(
      x, tokenize="origintext unicode61", detail=%DETAIL%
  );
  INSERT INTO ft(ft, rank) VALUES('insttoken', $insttoken);
  CREATE VIRTUAL TABLE vocab USING fts5vocab(ft, instance);

  INSERT INTO ft(rowid, x) VALUES(1, 'hello');
  INSERT INTO ft(rowid, x) VALUES(2, 'Hello');
  INSERT INTO ft(rowid, x) VALUES(3, 'HELLO');
}

#proc b {x} { string map [list "\0" "."] $x }
#db func b b
#execsql_pp { SELECT b(term) FROM vocab }

do_execsql_test $tn.3.1.1 { SELECT rowid FROM ft('hello') } 1
do_execsql_test $tn.3.1.2 { SELECT rowid FROM ft('Hello') } 2
do_execsql_test $tn.3.1.3 { SELECT rowid FROM ft('HELLO') } 3

do_execsql_test $tn.3.2 {
  CREATE VIRTUAL TABLE ft2 USING fts5(x, 
      tokenize="origintext unicode61", 
      tokendata=1,
      detail=%DETAIL%
  );
  INSERT INTO ft2(ft2, rank) VALUES('insttoken', $insttoken);
  CREATE VIRTUAL TABLE vocab2 USING fts5vocab(ft2, instance);

  INSERT INTO ft2(rowid, x) VALUES(1, 'hello');
  INSERT INTO ft2(rowid, x) VALUES(2, 'Hello');
  INSERT INTO ft2(rowid, x) VALUES(3, 'HELLO');

  INSERT INTO ft2(rowid, x) VALUES(10, 'helloooo');
}

#proc b {x} { string map [list "\0" "."] $x }
#db func b b
#execsql_pp { SELECT b(term) FROM vocab }

do_execsql_test $tn.3.3.1 { SELECT rowid FROM ft2('hello') } {1 2 3}
do_execsql_test $tn.3.3.2 { SELECT rowid FROM ft2('Hello') } {1 2 3}
do_execsql_test $tn.3.3.3 { SELECT rowid FROM ft2('HELLO') } {1 2 3}

do_execsql_test $tn.3.3.4 { SELECT rowid FROM ft2('hello*') } {1 2 3 10}

do_execsql_test $tn.3.3.5.1 { SELECT rowid FROM ft2('HELLO') ORDER BY rowid DESC} {
  3 2 1
}
do_execsql_test $tn.3.3.5.2 { SELECT rowid FROM ft2('HELLO') ORDER BY +rowid DESC} {
  3 2 1
}

#-------------------------------------------------------------------------
#
reset_db
sqlite3_fts5_register_origintext db
proc querytoken {cmd iPhrase iToken} { 
  set txt [$cmd xQueryToken $iPhrase $iToken]
  string map [list "\0" "."] $txt
}
sqlite3_fts5_create_function db querytoken querytoken

do_execsql_test $tn.4.0 {
  CREATE VIRTUAL TABLE ft USING fts5(
      x, tokenize='origintext unicode61', tokendata=1, detail=%DETAIL%
  );
  INSERT INTO ft(ft, rank) VALUES('insttoken', $insttoken);
  INSERT INTO ft VALUES('one two three four');
}

do_execsql_test $tn.4.1 {
  SELECT rowid, querytoken(ft, 0, 0) FROM ft('TwO')
} {1 two.TwO}
do_execsql_test $tn.4.2 {
  SELECT rowid, querytoken(ft, 0, 0) FROM ft('one TWO ThreE')
} {1 one}
do_execsql_test $tn.4.3 {
  SELECT rowid, querytoken(ft, 1, 0) FROM ft('one TWO ThreE')
} {1 two.TWO}

if {"%DETAIL%"=="full"} {
  # Phrase queries are only supported for detail=full.
  #
  do_execsql_test $tn.4.4 {
    SELECT rowid, querytoken(ft, 0, 2) FROM ft('"one TWO ThreE"')
  } {1 three.ThreE}
  do_catchsql_test $tn.4.5 {
    SELECT rowid, querytoken(ft, 0, 3) FROM ft('"one TWO ThreE"')
  } {1 SQLITE_RANGE}
  do_catchsql_test $tn.4.6 {
    SELECT rowid, querytoken(ft, 1, 0) FROM ft('"one TWO ThreE"')
  } {1 SQLITE_RANGE}
  do_catchsql_test $tn.4.7 {
    SELECT rowid, querytoken(ft, -1, 0) FROM ft('"one TWO ThreE"')
  } {1 SQLITE_RANGE}
}

#-------------------------------------------------------------------------
#
reset_db
sqlite3_fts5_register_origintext db
proc insttoken {cmd iIdx iToken} { 
  set txt [$cmd xInstToken $iIdx $iToken]
  string map [list "\0" "."] $txt
}
sqlite3_fts5_create_function db insttoken insttoken
fts5_aux_test_functions db

do_execsql_test $tn.5.0 {
  CREATE VIRTUAL TABLE ft USING fts5(
      x, tokenize='origintext unicode61', tokendata=1, detail=%DETAIL%
  );
  INSERT INTO ft(ft, rank) VALUES('insttoken', $insttoken);
  INSERT INTO ft VALUES('one ONE One oNe oNE one');
}

do_execsql_test $tn.5.1 {
  SELECT insttoken(ft, 0, 0), 
         insttoken(ft, 1, 0),
         insttoken(ft, 2, 0),
         insttoken(ft, 3, 0),
         insttoken(ft, 4, 0),
         insttoken(ft, 5, 0)
  FROM ft('one');
} {
  one one.ONE one.One one.oNe one.oNE one
}

do_execsql_test $tn.5.2 {
  SELECT insttoken(ft, 0, 0), 
         insttoken(ft, 1, 0),
         insttoken(ft, 2, 0),
         insttoken(ft, 3, 0),
         insttoken(ft, 4, 0),
         insttoken(ft, 5, 0)
  FROM ft('on*');
} {
  one one.ONE one.One one.oNe one.oNE one
}

do_execsql_test $tn.5.3 {
  SELECT insttoken(ft, 0, 0), 
         insttoken(ft, 1, 0),
         insttoken(ft, 2, 0),
         insttoken(ft, 3, 0),
         insttoken(ft, 4, 0),
         insttoken(ft, 5, 0)
  FROM ft(fts5_insttoken('on*'));
} {
  one one.ONE one.One one.oNe one.oNE one
}

do_execsql_test $tn.5.4 {
  SELECT insttoken(ft, 1, 0) FROM ft('one');
} {
  one.ONE
}

do_execsql_test $tn.5.5 {
  SELECT fts5_test_poslist(ft) FROM ft('one');
} {
  {0.0.0 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5}
}

#-------------------------------------------------------------------------
# Test the xInstToken() API with:
#
#   * a non tokendata=1 table.
#   * prefix queries.
#
reset_db
sqlite3_fts5_register_origintext db
do_execsql_test $tn.6.0 {
  CREATE VIRTUAL TABLE ft USING fts5( 
      x, y, tokenize='origintext unicode61', detail=%DETAIL%, tokendata=0
  );
  INSERT INTO ft(ft, rank) VALUES('insttoken', $insttoken);

  INSERT INTO ft VALUES('One Two', 'Three two');
  INSERT INTO ft VALUES('three Three', 'one One');
}
proc tokens {cmd} { 
  set ret [list]
  for {set iTok 0} {$iTok < [$cmd xInstCount]} {incr iTok} {
    set txt [$cmd xInstToken $iTok 0]
    set txt [string map [list "\0" "."] $txt]
    lappend ret $txt
  }
  set ret
}
sqlite3_fts5_create_function db tokens tokens

do_execsql_test $tn.6.1 {
  SELECT rowid, tokens(ft) FROM ft('One');
} {1 one.One 2 one.One}

do_execsql_test $tn.6.2 {
  SELECT rowid, tokens(ft) FROM ft('on*');
} {1 one.One 2 {one one.One}}

do_execsql_test $tn.6.3 {
  SELECT rowid, tokens(ft) FROM ft('Three*');
} {1 three.Three 2 three.Three}

fts5_aux_test_functions db
do_catchsql_test $tn.6.4 {
  SELECT fts5_test_insttoken(ft, -1, 0) FROM ft('one');
} {1 SQLITE_RANGE}

do_catchsql_test $tn.6.5 {
  SELECT fts5_test_insttoken(ft, 1, 0) FROM ft('one');
} {1 SQLITE_RANGE}

do_catchsql_test $tn.6.6 {
  CREATE VIRTUAL TABLE ft2 USING fts5(x, tokendata=2);
} {1 {malformed tokendata=... directive}}
do_catchsql_test $tn.6.7 {
  CREATE VIRTUAL TABLE ft2 USING fts5(x, content='', tokendata=11);
} {1 {malformed tokendata=... directive}}

}
}

finish_test

