# 2014 August 16
#
# 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.
#
#***********************************************************************
#
#

if {![info exists testdir]} {
  set testdir [file join [file dirname [info script]] .. .. test]
} 
source [file join [file dirname [info script]] session_common.tcl]
source $testdir/tester.tcl
ifcapable !session {finish_test; return}

set testprefix sessionC

#-------------------------------------------------------------------------
# Test the outcome of a DELETE operation made as part of applying a
# changeset failing with SQLITE_CONSTRAINT. This may happen if an
# ON DELETE RESTRICT foreign key action is triggered, or if a trigger
# program raises a constraint somehow.
#
# UPDATE: The above is no longer true, as "PRAGMA defer_foreign_keys"
# now disables "RESTRICT" processing. The test below has been rewritten 
# to use a trigger instead of a foreign key to test this case.
#
do_execsql_test 1.0 {
  PRAGMA foreign_keys = 1;

  CREATE TABLE p(a PRIMARY KEY, b, c);
  CREATE TABLE c(d PRIMARY KEY, e /* REFERENCES p ON DELETE RESTRICT */);

  CREATE TRIGGER restrict_trig BEFORE DELETE ON p BEGIN
    SELECT raise(ABORT, 'error!') FROM c WHERE e=old.a;
  END;

  INSERT INTO p VALUES('one', 1, 1);
  INSERT INTO p VALUES('two', 2, 2);
  INSERT INTO p VALUES('three', 3, 3);

  INSERT INTO c VALUES(1, 'one');
  INSERT INTO c VALUES(3, 'three');
}

do_test 1.1 {
  execsql BEGIN
  set C [changeset_from_sql {
    INSERT INTO c VALUES(4, 'one');
    DELETE FROM p WHERE a='two';
  }]
  execsql ROLLBACK
  execsql {
    INSERT INTO c VALUES(2, 'two');
  }
} {}

do_test 1.2.1 {
  proc xConflict {args} { return "ABORT" }
  catch { sqlite3changeset_apply db $C xConflict } msg
  set msg
} {SQLITE_ABORT}
do_execsql_test 1.2.2 { SELECT * FROM c } {1 one 3 three 2 two}

do_test 1.3.1 {
  proc xConflict {args} { return "OMIT" }
  catch { sqlite3changeset_apply db $C xConflict } msg
  set msg
} {}
do_execsql_test 1.3.2 { SELECT * FROM c } {1 one 3 three 2 two 4 one}
do_execsql_test 1.3.3 { 
  SELECT * FROM p;
} {one 1 1 two 2 2 three 3 3}


#-------------------------------------------------------------------------
# Test that concatenating a changeset with a patchset does not work.
# Any attempt to do so returns SQLITE_ERROR.
#
reset_db
do_execsql_test 2.0 {
  CREATE TABLE x1(t, v PRIMARY KEY);
  INSERT INTO x1 VALUES(12, 55);
  INSERT INTO x1 VALUES(55, 14);
}

do_test 2.1 {
  execsql BEGIN

  sqlite3session S1 db main
  S1 attach *
  execsql {
    UPDATE x1 SET t=13 WHERE v=55;
    INSERT INTO x1 VALUES(99, 123);
  }
  set patchset [S1 patchset]
  S1 delete

  sqlite3session S1 db main
  S1 attach *
  execsql {
    UPDATE x1 SET t=56 WHERE v=14;
    INSERT INTO x1 VALUES(22, 998);
  }
  set changeset [S1 changeset]
  S1 delete

  execsql ROLLBACK
} {}

do_test 2.2 {
  set rc [catch { sqlite3changeset_concat $patchset $changeset } msg]
  list $rc $msg
} {1 SQLITE_ERROR}

do_test 2.3 {
  set rc [catch { sqlite3changeset_concat $changeset $patchset } msg]
  list $rc $msg
} {1 SQLITE_ERROR}

do_test 2.4 {
  set rc [catch { sqlite3changeset_concat {} $patchset } msg]
  list $rc $msg
} [list 0 $patchset]

do_test 2.5 {
  set rc [catch { sqlite3changeset_concat $patchset {} } msg]
  list $rc $msg
} [list 0 $patchset]

do_test 2.6 {
  set rc [catch { sqlite3changeset_concat {} $changeset } msg]
  list $rc $msg
} [list 0 $changeset]

do_test 2.7 {
  set rc [catch { sqlite3changeset_concat $changeset {} } msg]
  list $rc $msg
} [list 0 $changeset]

do_test 2.8 {
  set rc [catch { sqlite3changeset_concat {} {} } msg]
  list $rc $msg
} [list 0 {}]


#-------------------------------------------------------------------------
# Test that the xFilter argument to sqlite3changeset_apply() works.
#
reset_db
do_execsql_test 3.0 {
  CREATE TABLE t1(a PRIMARY KEY, b);
  CREATE TABLE t2(a PRIMARY KEY, b);
  CREATE TABLE t3(a PRIMARY KEY, b);
}
do_test 3.1 {
  execsql BEGIN
  set changeset [changeset_from_sql {
    INSERT INTO t1 VALUES(1, 1);
    INSERT INTO t2 VALUES(2, 2);
    INSERT INTO t3 VALUES(3, 3);
  }]
  execsql ROLLBACK
} {}
do_test 3.2 {
  proc xFilter {zName} {
    if {$zName == "t1"} { return 1 }
    return 0
  }
  sqlite3changeset_apply db $changeset noop xFilter
  execsql {
    SELECT * FROM t1;
    SELECT * FROM t2;
    SELECT * FROM t3;
  }
} {1 1}
do_test 3.3 {
  proc xFilter {zName} {
    if {$zName == "t3"} { return 1 }
    return 0
  }
  sqlite3changeset_apply db $changeset noop xFilter
  execsql {
    SELECT * FROM t1;
    SELECT * FROM t2;
    SELECT * FROM t3;
  }
} {1 1 3 3}

#-------------------------------------------------------------------------
#
reset_db
set C [binary format c* {0x54 0x01 0x01 0x00 0x12 0x00 0x05}]

do_test 4.0 {
  sqlite3changegroup grp
  list [catch { grp add $C } msg] $msg
} {0 {}}
grp delete

#-------------------------------------------------------------------------
#
reset_db
set C [binary format c* {0x54 0xda 0xda 0xda 0xda 0xda}]

do_execsql_test 5.0 {
  CREATE TABLE t1(a PRIMARY KEY, b, c, d);
}

do_test 5.1 {
  list [catch { sqlite3changeset_apply db $C noop xFilter } msg] $msg
} {1 SQLITE_CORRUPT}

#-------------------------------------------------------------------------
#

foreach {tn type C2hex C3hex} {
  1 changeset 54020100743100170001000000000000000A030374656E000306656C6576656E
              54020100743100170001000000000000000A030374656E0003FFFF01656C6576656E

  2 patchset  50020100743100170001000000000000000A0306656C6576656E
              50020100743100170001000000000000000A03FFFF06656C6576656E

  3 changeset 54020100743100170001000000000000000A030374656E000306656C6576656E
              54020100743100170001000000000000000A030374656E0003FFFF
} {
  reset_db

  do_execsql_test 6.$tn.0 {
    CREATE TABLE t1(x INTEGER PRIMARY KEY, y TEXT);
    INSERT INTO t1 VALUES(1, 'one');
    INSERT INTO t1 VALUES(2, 'two');
    INSERT INTO t1 VALUES(3, 'three');
  } {}
  
  do_test 6.$tn.1 {
    set C1 [${type}_from_sql {
      INSERT INTO t1 VALUES(10, 'ten');
    }]
    set C2 [${type}_from_sql {
      UPDATE t1 SET y='eleven' WHERE x=10;
    }]
    db one { SELECT quote($C2) }
  } "X'$C2hex'"
  
  do_test 6.$tn.2 {
    changeset_to_list [sqlite3changeset_concat $C1 $C2]
  } {{INSERT t1 0 X. {} {i 10 t eleven}}}
  
  set C3 [db one {SELECT unhex($C3hex)}]
  
  do_test 6.$tn.3 {
   list [catch { sqlite3changeset_concat $C1 $C3 } msg] $msg
  } {1 SQLITE_CORRUPT}
}

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

set CSD 5402010074000900010000000000000001030441414141
set CSI 5402010074001200010000000000000001063258585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858585858

do_test 7.0 {
  sqlite3changegroup grp
  grp add [db one {SELECT unhex($CSD)}]
  list [catch { grp add [db one {SELECT unhex($CSI)}] } msg] $msg
} {1 SQLITE_CORRUPT}
grp delete

finish_test
