@@ -928,6 +928,130 @@ test("like follows sqlite wildcard and case rules", () => {
928928 assert . deepEqual ( engine . execute ( "select 'axb' like 'a\\%b' escape '\\'" ) . rows , [ [ 0 ] ] ) ;
929929} ) ;
930930
931+ // `IS DISTINCT FROM` compares `NULL` as an ordinary value: two `NULL`s are not
932+ // distinct, while `NULL` against anything else is. Non-null operands keep the
933+ // storage-class ordering, so `1` and `1.0` are equal but `1` and `'1'` are not.
934+ test ( "is distinct from treats null as a value like better-sqlite3" , ( ) => {
935+ const engine = createEngine ( 4096 ) ;
936+ assert . deepEqual ( engine . execute ( "select null is distinct from null" ) . rows , [ [ 0 ] ] ) ;
937+ assert . deepEqual ( engine . execute ( "select null is not distinct from null" ) . rows , [ [ 1 ] ] ) ;
938+ assert . deepEqual ( engine . execute ( "select null is distinct from 1" ) . rows , [ [ 1 ] ] ) ;
939+ assert . deepEqual ( engine . execute ( "select 1 is distinct from null" ) . rows , [ [ 1 ] ] ) ;
940+ assert . deepEqual ( engine . execute ( "select 1 is distinct from '1'" ) . rows , [ [ 1 ] ] ) ;
941+ assert . deepEqual ( engine . execute ( "select 1 is distinct from 1.0" ) . rows , [ [ 0 ] ] ) ;
942+ assert . deepEqual ( engine . execute ( "select 'a' is distinct from 'A'" ) . rows , [ [ 1 ] ] ) ;
943+ assert . deepEqual ( engine . execute ( "select x'31' is distinct from x'31'" ) . rows , [ [ 0 ] ] ) ;
944+ assert . deepEqual ( engine . execute ( "select 1 + 1 is distinct from 2" ) . rows , [ [ 0 ] ] ) ;
945+ assert . deepEqual ( engine . execute ( "select (1 is distinct from 2) as d" ) . rows , [ [ 1 ] ] ) ;
946+ // `AND` binds looser than the comparison.
947+ assert . deepEqual ( engine . execute ( "select 1 is distinct from 2 and 0" ) . rows , [ [ 0 ] ] ) ;
948+
949+ engine . execute ( "create table t (id integer primary key, v integer)" ) ;
950+ engine . execute ( "insert into t values (1, 1), (2, null), (3, 2), (4, null), (5, 3)" ) ;
951+ assert . deepEqual (
952+ engine . execute ( "select v, v is distinct from lag(v) over (order by id) from t order by id" ) . rows ,
953+ [
954+ [ 1 , 1 ] ,
955+ [ null , 1 ] ,
956+ [ 2 , 1 ] ,
957+ [ null , 1 ] ,
958+ [ 3 , 1 ] ,
959+ ] ,
960+ ) ;
961+
962+ const missing = engine . tryExecute ( "select 1 is distinct from" ) ;
963+ assert . equal ( missing . ok , false ) ;
964+ assert . equal ( missing . error ?. code , "syntax" ) ;
965+ } ) ;
966+
967+ // `GLOB` is case sensitive and reads `[...]` as a class: `^` negates, `a-c` is
968+ // a range, a leading `]` is a member, an unclosed `[` never matches, and only
969+ // text operands participate, so a blob never matches and `NULL` propagates.
970+ test ( "glob follows sqlite class and case rules" , ( ) => {
971+ const engine = createEngine ( 4096 ) ;
972+ assert . deepEqual ( engine . execute ( "select 'abc' glob 'a*'" ) . rows , [ [ 1 ] ] ) ;
973+ assert . deepEqual ( engine . execute ( "select 'ABC' glob 'a*'" ) . rows , [ [ 0 ] ] ) ;
974+ assert . deepEqual ( engine . execute ( "select 'abc' not glob 'a*'" ) . rows , [ [ 0 ] ] ) ;
975+ assert . deepEqual ( engine . execute ( "select 'abc' glob 'a.c'" ) . rows , [ [ 0 ] ] ) ;
976+ assert . deepEqual ( engine . execute ( "select 'abc' glob 'a?c'" ) . rows , [ [ 1 ] ] ) ;
977+ assert . deepEqual ( engine . execute ( "select 'abc' glob '[a-c]*'" ) . rows , [ [ 1 ] ] ) ;
978+ assert . deepEqual ( engine . execute ( "select 'abc' glob '[^abd]*'" ) . rows , [ [ 0 ] ] ) ;
979+ assert . deepEqual ( engine . execute ( "select 'z' glob '[^abd]*'" ) . rows , [ [ 1 ] ] ) ;
980+ assert . deepEqual ( engine . execute ( "select 'abc' glob '[!abd]*'" ) . rows , [ [ 1 ] ] ) ;
981+ assert . deepEqual ( engine . execute ( "select 'z' glob '[!abd]*'" ) . rows , [ [ 0 ] ] ) ;
982+ assert . deepEqual ( engine . execute ( "select '[' glob '['" ) . rows , [ [ 0 ] ] ) ;
983+ assert . deepEqual ( engine . execute ( "select '[' glob '[[]'" ) . rows , [ [ 1 ] ] ) ;
984+ assert . deepEqual ( engine . execute ( "select ']' glob '[]]'" ) . rows , [ [ 1 ] ] ) ;
985+ assert . deepEqual ( engine . execute ( "select 'ab' glob 'a[b'" ) . rows , [ [ 0 ] ] ) ;
986+ assert . deepEqual ( engine . execute ( "select 'ab' glob 'a[b]'" ) . rows , [ [ 1 ] ] ) ;
987+ assert . deepEqual ( engine . execute ( "select '-' glob '[a-]'" ) . rows , [ [ 1 ] ] ) ;
988+ assert . deepEqual ( engine . execute ( "select 'abc' glob 'a**c'" ) . rows , [ [ 1 ] ] ) ;
989+ assert . deepEqual ( engine . execute ( "select '' glob '*'" ) . rows , [ [ 1 ] ] ) ;
990+ assert . deepEqual ( engine . execute ( "select 'abc' glob ''" ) . rows , [ [ 0 ] ] ) ;
991+ assert . deepEqual ( engine . execute ( "select 123 glob '1*'" ) . rows , [ [ 1 ] ] ) ;
992+ assert . deepEqual ( engine . execute ( "select 1.5 glob '1*'" ) . rows , [ [ 1 ] ] ) ;
993+ assert . deepEqual ( engine . execute ( "select null glob 'a*'" ) . rows , [ [ null ] ] ) ;
994+ assert . deepEqual ( engine . execute ( "select 'abc' glob null" ) . rows , [ [ null ] ] ) ;
995+ assert . deepEqual ( engine . execute ( "select x'616263' glob 'abc'" ) . rows , [ [ 0 ] ] ) ;
996+ assert . deepEqual ( engine . execute ( "select 'abc' glob x'616263'" ) . rows , [ [ 0 ] ] ) ;
997+ assert . deepEqual ( engine . execute ( "select 'a' glob 'a*' or 0" ) . rows , [ [ 1 ] ] ) ;
998+ } ) ;
999+
1000+ // `NULLS FIRST` / `NULLS LAST` override SQLite's default placement, which sorts
1001+ // `NULL` below every value and therefore puts them first for `ASC` and last for
1002+ // `DESC`. The placement also drives the window sort order used by a frame.
1003+ test ( "order by nulls first and last override the default placement" , ( ) => {
1004+ const engine = createEngine ( 4096 ) ;
1005+ engine . execute ( "create table t (id integer primary key, a text, b text, v integer)" ) ;
1006+ engine . execute ( "insert into t values (1,'x','p',1),(2,'x','q',null),(3,'y','p',2),(4,null,'q',null),(5,'x','q',3)" ) ;
1007+
1008+ assert . deepEqual ( engine . execute ( "select id from t order by v asc nulls first" ) . rows , [ [ 2 ] , [ 4 ] , [ 1 ] , [ 3 ] , [ 5 ] ] ) ;
1009+ assert . deepEqual ( engine . execute ( "select id from t order by v asc nulls last" ) . rows , [ [ 1 ] , [ 3 ] , [ 5 ] , [ 2 ] , [ 4 ] ] ) ;
1010+ assert . deepEqual ( engine . execute ( "select id from t order by v desc nulls first" ) . rows , [ [ 2 ] , [ 4 ] , [ 5 ] , [ 3 ] , [ 1 ] ] ) ;
1011+ assert . deepEqual ( engine . execute ( "select id from t order by v desc nulls last" ) . rows , [ [ 5 ] , [ 3 ] , [ 1 ] , [ 2 ] , [ 4 ] ] ) ;
1012+ assert . deepEqual ( engine . execute ( "select id from t order by v nulls last" ) . rows , [ [ 1 ] , [ 3 ] , [ 5 ] , [ 2 ] , [ 4 ] ] ) ;
1013+ assert . deepEqual (
1014+ engine . execute ( "select id from t order by a asc nulls last, b desc nulls first" ) . rows ,
1015+ [ [ 2 ] , [ 5 ] , [ 1 ] , [ 3 ] , [ 4 ] ] ,
1016+ ) ;
1017+ assert . deepEqual ( engine . execute ( "select id from t order by a asc nulls first, v desc" ) . rows , [ [ 4 ] , [ 5 ] , [ 1 ] , [ 2 ] , [ 3 ] ] ) ;
1018+ assert . deepEqual ( engine . execute ( "select id from t order by v asc nulls first, id desc" ) . rows , [ [ 4 ] , [ 2 ] , [ 1 ] , [ 3 ] , [ 5 ] ] ) ;
1019+ assert . deepEqual ( engine . execute ( "select (select id from t order by v nulls last limit 1)" ) . rows , [ [ 1 ] ] ) ;
1020+
1021+ assert . deepEqual (
1022+ engine
1023+ . execute (
1024+ "select id, sum(v) over (order by v nulls first rows between 1 preceding and current row) from t order by id" ,
1025+ )
1026+ . rows ,
1027+ [
1028+ [ 1 , 1 ] ,
1029+ [ 2 , null ] ,
1030+ [ 3 , 3 ] ,
1031+ [ 4 , null ] ,
1032+ [ 5 , 5 ] ,
1033+ ] ,
1034+ ) ;
1035+ assert . deepEqual (
1036+ engine
1037+ . execute (
1038+ "select id, sum(v) over (order by v nulls last rows between 1 preceding and current row) from t order by id" ,
1039+ )
1040+ . rows ,
1041+ [
1042+ [ 1 , 1 ] ,
1043+ [ 2 , 3 ] ,
1044+ [ 3 , 3 ] ,
1045+ [ 4 , null ] ,
1046+ [ 5 , 5 ] ,
1047+ ] ,
1048+ ) ;
1049+
1050+ const middle = engine . tryExecute ( "select id from t order by v nulls middle" ) ;
1051+ assert . equal ( middle . ok , false ) ;
1052+ assert . equal ( middle . error ?. code , "syntax" ) ;
1053+ } ) ;
1054+
9311055// `IS TRUE` is the truthiness test, not `= 1`, and it never yields `NULL`.
9321056test ( "is true and is false follow sqlite truthiness" , ( ) => {
9331057 const engine = createEngine ( 4096 ) ;
0 commit comments