exec-ddl
CREATE TABLE a
(
    k INT PRIMARY KEY,
    i INT,
    f FLOAT,
    s STRING,
    j JSON,
    INDEX s_idx (s) STORING (i, f),
    INDEX si_idx (s DESC, i DESC) STORING (j),
    INVERTED INDEX inv_idx_j (j)
)
----
TABLE a
 ├── k int not null
 ├── i int
 ├── f float
 ├── s string
 ├── j jsonb
 ├── INDEX primary
 │    └── k int not null
 ├── INDEX s_idx
 │    ├── s string
 │    ├── k int not null
 │    ├── i int (storing)
 │    └── f float (storing)
 ├── INDEX si_idx
 │    ├── s string desc
 │    ├── i int desc
 │    ├── k int not null
 │    └── j jsonb (storing)
 └── INVERTED INDEX inv_idx_j
      ├── j jsonb
      └── k int not null

# --------------------------------------------------
# GenerateIndexScans
# --------------------------------------------------

# Revscan won't be used here because there is no index with f
# sorted by ASC, k DESC
opt
SELECT k,f FROM a ORDER BY f DESC, k ASC LIMIT 10
----
limit
 ├── columns: k:1(int!null) f:3(float)
 ├── internal-ordering: -3,+1
 ├── cardinality: [0 - 10]
 ├── key: (1)
 ├── fd: (1)-->(3)
 ├── ordering: -3,+1
 ├── sort
 │    ├── columns: k:1(int!null) f:3(float)
 │    ├── key: (1)
 │    ├── fd: (1)-->(3)
 │    ├── ordering: -3,+1
 │    └── scan a@s_idx
 │         ├── columns: k:1(int!null) f:3(float)
 │         ├── key: (1)
 │         └── fd: (1)-->(3)
 └── const: 10 [type=int]

opt
SELECT k,f from a ORDER BY k DESC LIMIT 10
----
scan a,rev
 ├── columns: k:1(int!null) f:3(float)
 ├── limit: 10(rev)
 ├── key: (1)
 ├── fd: (1)-->(3)
 └── ordering: -1

memo
SELECT k,f FROM a ORDER BY k DESC LIMIT 10
----
memo (optimized, ~3KB)
 ├── G1: (limit G2 G3 ordering=-1) (scan a,rev,cols=(1,3),lim=10(rev))
 │    ├── "[presentation: k:1,f:3] [ordering: -1]"
 │    │    ├── best: (scan a,rev,cols=(1,3),lim=10(rev))
 │    │    └── cost: 11.03
 │    └── ""
 │         ├── best: (scan a,rev,cols=(1,3),lim=10(rev))
 │         └── cost: 11.03
 ├── G2: (scan a,cols=(1,3)) (scan a@s_idx,cols=(1,3))
 │    ├── ""
 │    │    ├── best: (scan a@s_idx,cols=(1,3))
 │    │    └── cost: 1060.00
 │    └── "[ordering: -1]"
 │         ├── best: (scan a,rev,cols=(1,3))
 │         └── cost: 1169.66
 └── G3: (const 10)


opt
SELECT s FROM a ORDER BY k DESC
----
scan a,rev
 ├── columns: s:4(string)
 ├── key: (1)
 ├── fd: (1)-->(4)
 └── ordering: -1

opt
SELECT k FROM a ORDER BY k ASC
----
scan a
 ├── columns: k:1(int!null)
 ├── key: (1)
 └── ordering: +1

opt
SELECT k FROM a ORDER BY k DESC
----
scan a,rev
 ├── columns: k:1(int!null)
 ├── key: (1)
 └── ordering: -1

opt
SELECT s,i,k,j FROM a ORDER BY s DESC, i DESC, k ASC
----
scan a@si_idx
 ├── columns: s:4(string) i:2(int) k:1(int!null) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2,4,5)
 └── ordering: -4,-2,+1

# Revscan node won't be used because ordering is
# only partial (reverse) match with existing indices
opt
SELECT s,i,k,j FROM a ORDER BY s DESC, i DESC, k DESC
----
sort
 ├── columns: s:4(string) i:2(int) k:1(int!null) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2,4,5)
 ├── ordering: -4,-2,-1
 └── scan a@si_idx
      ├── columns: k:1(int!null) i:2(int) s:4(string) j:5(jsonb)
      ├── key: (1)
      └── fd: (1)-->(2,4,5)

# Revscan node won't be used because ordering is
# only partial (reverse) match with existing indices
opt
SELECT s,i,k,j FROM a ORDER BY s DESC, i ASC, k DESC
----
sort
 ├── columns: s:4(string) i:2(int) k:1(int!null) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2,4,5)
 ├── ordering: -4,+2,-1
 └── scan a@si_idx
      ├── columns: k:1(int!null) i:2(int) s:4(string) j:5(jsonb)
      ├── key: (1)
      └── fd: (1)-->(2,4,5)

opt
SELECT s,i,k,j FROM a ORDER BY s ASC, i ASC, k DESC
----
scan a@si_idx,rev
 ├── columns: s:4(string) i:2(int) k:1(int!null) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2,4,5)
 └── ordering: +4,+2,-1

memo
SELECT k FROM a ORDER BY k ASC
----
memo (optimized, ~2KB)
 └── G1: (scan a,cols=(1)) (scan a@s_idx,cols=(1)) (scan a@si_idx,cols=(1))
      ├── "[presentation: k:1] [ordering: +1]"
      │    ├── best: (scan a,cols=(1))
      │    └── cost: 1060.00
      └── ""
           ├── best: (scan a@s_idx,cols=(1))
           └── cost: 1050.00

# Scan of secondary index is lowest cost.
opt
SELECT s, i, f FROM a ORDER BY s, k, i
----
scan a@s_idx
 ├── columns: s:4(string) i:2(int) f:3(float)
 ├── key: (1)
 ├── fd: (1)-->(2-4)
 └── ordering: +4,+1

memo
SELECT s, i, f FROM a ORDER BY s, k, i
----
memo (optimized, ~2KB)
 └── G1: (scan a,cols=(1-4)) (scan a@s_idx,cols=(1-4))
      ├── "[presentation: s:4,i:2,f:3] [ordering: +4,+1]"
      │    ├── best: (scan a@s_idx,cols=(1-4))
      │    └── cost: 1080.00
      └── ""
           ├── best: (scan a@s_idx,cols=(1-4))
           └── cost: 1080.00

# No index-join should be generated for a@si_idx, since it is not constrained.
exploretrace rule=GenerateIndexScans
SELECT s, i, f FROM a ORDER BY s, k, i
----
----
================================================================================
GenerateIndexScans
================================================================================
Source expression:
  sort
   ├── columns: s:4(string) i:2(int) f:3(float)
   ├── key: (1)
   ├── fd: (1)-->(2-4)
   ├── ordering: +4,+1
   └── scan a
        ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string)
        ├── key: (1)
        └── fd: (1)-->(2-4)

New expression 1 of 1:
  scan a@s_idx
   ├── columns: s:4(string) i:2(int) f:3(float)
   ├── key: (1)
   ├── fd: (1)-->(2-4)
   └── ordering: +4,+1
----
----

# --------------------------------------------------
# GenerateConstrainedScans
# --------------------------------------------------

# Constrain the a@si_idx so that an index join is generated.
exploretrace rule=GenerateConstrainedScans
SELECT s, i, f FROM a WHERE s='foo' ORDER BY s, k, i
----
----
================================================================================
GenerateConstrainedScans
================================================================================
Source expression:
  select
   ├── columns: s:4(string!null) i:2(int) f:3(float)
   ├── key: (1)
   ├── fd: ()-->(4), (1)-->(2,3)
   ├── ordering: +1 opt(4)
   ├── scan a@s_idx
   │    ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string)
   │    ├── key: (1)
   │    ├── fd: (1)-->(2-4)
   │    └── ordering: +1 opt(4)
   └── filters [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight), fd=()-->(4)]
        └── s = 'foo' [type=bool, outer=(4), constraints=(/4: [/'foo' - /'foo']; tight)]

New expression 1 of 2:
  scan a@s_idx
   ├── columns: s:4(string!null) i:2(int) f:3(float)
   ├── constraint: /4/1: [/'foo' - /'foo']
   ├── key: (1)
   ├── fd: ()-->(4), (1)-->(2,3)
   └── ordering: +1 opt(4)

New expression 2 of 2:
  sort
   ├── columns: s:4(string!null) i:2(int) f:3(float)
   ├── key: (1)
   ├── fd: ()-->(4), (1)-->(2,3)
   ├── ordering: +1 opt(4)
   └── index-join a
        ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string!null)
        ├── key: (1)
        ├── fd: ()-->(4), (1)-->(2,3)
        └── scan a@si_idx
             ├── columns: k:1(int!null) i:2(int) s:4(string!null)
             ├── constraint: /-4/-2/1: [/'foo' - /'foo']
             ├── key: (1)
             └── fd: ()-->(4), (1)-->(2)
----
----

memo
SELECT s, i, f FROM a ORDER BY f
----
memo (optimized, ~2KB)
 └── G1: (scan a,cols=(2-4)) (scan a@s_idx,cols=(2-4))
      ├── "[presentation: s:4,i:2,f:3] [ordering: +3]"
      │    ├── best: (sort G1)
      │    └── cost: 1289.32
      └── ""
           ├── best: (scan a@s_idx,cols=(2-4))
           └── cost: 1070.00

memo
SELECT s, i, f FROM a ORDER BY s DESC, i
----
memo (optimized, ~2KB)
 └── G1: (scan a,cols=(2-4)) (scan a@s_idx,cols=(2-4))
      ├── "[presentation: s:4,i:2,f:3] [ordering: -4,+2]"
      │    ├── best: (sort G1)
      │    └── cost: 1300.28
      └── ""
           ├── best: (scan a@s_idx,cols=(2-4))
           └── cost: 1070.00

memo
SELECT s, i, f FROM a WHERE s='foo' ORDER BY s DESC, i
----
memo (optimized, ~8KB)
 ├── G1: (select G2 G3) (scan a@s_idx,cols=(2-4),constrained) (index-join G4 a,cols=(2-4))
 │    ├── "[presentation: s:4,i:2,f:3] [ordering: +2 opt(4)]"
 │    │    ├── best: (sort G1)
 │    │    └── cost: 11.56
 │    └── ""
 │         ├── best: (scan a@s_idx,cols=(2-4),constrained)
 │         └── cost: 10.70
 ├── G2: (scan a,cols=(2-4)) (scan a@s_idx,cols=(2-4))
 │    ├── ""
 │    │    ├── best: (scan a@s_idx,cols=(2-4))
 │    │    └── cost: 1070.00
 │    └── "[ordering: +2 opt(4)]"
 │         ├── best: (sort G2)
 │         └── cost: 1289.32
 ├── G3: (filters G5)
 ├── G4: (scan a@si_idx,cols=(1,2,4),constrained)
 │    ├── ""
 │    │    ├── best: (scan a@si_idx,cols=(1,2,4),constrained)
 │    │    └── cost: 10.70
 │    └── "[ordering: +2 opt(4)]"
 │         ├── best: (scan a@si_idx,rev,cols=(1,2,4),constrained)
 │         └── cost: 11.03
 ├── G5: (eq G6 G7)
 ├── G6: (variable s)
 └── G7: (const 'foo')

# Force an index in order to ensure that an index join is created.
opt
SELECT * FROM a@si_idx
----
index-join a
 ├── columns: k:1(int!null) i:2(int) f:3(float) s:4(string) j:5(jsonb)
 ├── key: (1)
 ├── fd: (1)-->(2-5)
 └── scan a@si_idx
      ├── columns: k:1(int!null) i:2(int) s:4(string) j:5(jsonb)
      ├── flags: force-index=si_idx
      ├── key: (1)
      └── fd: (1)-->(2,4,5)

exec-ddl
CREATE TABLE abc (
  a INT,
  b INT,
  c INT,
  d CHAR,
  PRIMARY KEY (a, b, c),
  UNIQUE INDEX bc (b, c),
  INDEX ba (b, a),
  FAMILY (a, b, c),
  FAMILY (d)
)
----
TABLE abc
 ├── a int not null
 ├── b int not null
 ├── c int not null
 ├── d string
 ├── INDEX primary
 │    ├── a int not null
 │    ├── b int not null
 │    └── c int not null
 ├── INDEX bc
 │    ├── b int not null
 │    ├── c int not null
 │    └── a int not null (storing)
 └── INDEX ba
      ├── b int not null
      ├── a int not null
      └── c int not null

memo
SELECT d FROM abc ORDER BY lower(d)
----
memo (optimized, ~4KB)
 ├── G1: (project G2 G3)
 │    ├── "[presentation: d:4] [ordering: +5]"
 │    │    ├── best: (sort G1)
 │    │    └── cost: 1289.32
 │    └── ""
 │         ├── best: (project G2 G3)
 │         └── cost: 1070.00
 ├── G2: (scan abc,cols=(4))
 │    └── ""
 │         ├── best: (scan abc,cols=(4))
 │         └── cost: 1050.00
 ├── G3: (projections G4 d)
 ├── G4: (function G5 lower)
 └── G5: (variable d)

memo
SELECT j FROM a WHERE s = 'foo'
----
memo (optimized, ~8KB)
 ├── G1: (project G2 G3)
 │    └── "[presentation: j:5]"
 │         ├── best: (project G2 G3)
 │         └── cost: 10.70
 ├── G2: (select G4 G5) (index-join G6 a,cols=(4,5)) (scan a@si_idx,cols=(4,5),constrained)
 │    └── ""
 │         ├── best: (scan a@si_idx,cols=(4,5),constrained)
 │         └── cost: 10.60
 ├── G3: (projections j)
 ├── G4: (scan a,cols=(4,5)) (scan a@si_idx,cols=(4,5))
 │    └── ""
 │         ├── best: (scan a@si_idx,cols=(4,5))
 │         └── cost: 1060.00
 ├── G5: (filters G7)
 ├── G6: (scan a@s_idx,cols=(1,4),constrained)
 │    └── ""
 │         ├── best: (scan a@s_idx,cols=(1,4),constrained)
 │         └── cost: 10.60
 ├── G7: (eq G8 G9)
 ├── G8: (variable s)
 └── G9: (const 'foo')

# Scan of primary index is lowest cost.
opt
SELECT s, i, f FROM a ORDER BY k, i, s
----
scan a
 ├── columns: s:4(string) i:2(int) f:3(float)
 ├── key: (1)
 ├── fd: (1)-->(2-4)
 └── ordering: +1

memo
SELECT s, i, f FROM a ORDER BY k, i, s
----
memo (optimized, ~2KB)
 └── G1: (scan a,cols=(1-4)) (scan a@s_idx,cols=(1-4))
      ├── "[presentation: s:4,i:2,f:3] [ordering: +1]"
      │    ├── best: (scan a,cols=(1-4))
      │    └── cost: 1090.00
      └── ""
           ├── best: (scan a@s_idx,cols=(1-4))
           └── cost: 1080.00

# Secondary index has right order
opt
SELECT s, j FROM a ORDER BY s
----
scan a@si_idx,rev
 ├── columns: s:4(string) j:5(jsonb)
 └── ordering: +4

memo
SELECT s, j FROM a ORDER BY s
----
memo (optimized, ~2KB)
 └── G1: (scan a,cols=(4,5)) (scan a@si_idx,cols=(4,5))
      ├── "[presentation: s:4,j:5] [ordering: +4]"
      │    ├── best: (scan a@si_idx,rev,cols=(4,5))
      │    └── cost: 1159.66
      └── ""
           ├── best: (scan a@si_idx,cols=(4,5))
           └── cost: 1060.00

# Consider three different indexes, and pick index with multiple keys.
opt
SELECT i, k FROM a ORDER BY s DESC, i, k
----
sort
 ├── columns: i:2(int) k:1(int!null)
 ├── key: (1)
 ├── fd: (1)-->(2,4)
 ├── ordering: -4,+2,+1
 └── scan a@s_idx
      ├── columns: k:1(int!null) i:2(int) s:4(string)
      ├── key: (1)
      └── fd: (1)-->(2,4)

memo
SELECT i, k FROM a ORDER BY s DESC, i, k
----
memo (optimized, ~2KB)
 └── G1: (scan a,cols=(1,2,4)) (scan a@s_idx,cols=(1,2,4)) (scan a@si_idx,cols=(1,2,4))
      ├── "[presentation: i:2,k:1] [ordering: -4,+2,+1]"
      │    ├── best: (sort G1)
      │    └── cost: 1301.38
      └── ""
           ├── best: (scan a@s_idx,cols=(1,2,4))
           └── cost: 1070.00

memo
SELECT i, k FROM a WHERE s >= 'foo'
----
memo (optimized, ~7KB)
 ├── G1: (project G2 G3)
 │    └── "[presentation: i:2,k:1]"
 │         ├── best: (project G2 G3)
 │         └── cost: 360.00
 ├── G2: (select G4 G5) (scan a@s_idx,cols=(1,2,4),constrained) (scan a@si_idx,cols=(1,2,4),constrained)
 │    └── ""
 │         ├── best: (scan a@s_idx,cols=(1,2,4),constrained)
 │         └── cost: 356.67
 ├── G3: (projections k i)
 ├── G4: (scan a,cols=(1,2,4)) (scan a@s_idx,cols=(1,2,4)) (scan a@si_idx,cols=(1,2,4))
 │    └── ""
 │         ├── best: (scan a@s_idx,cols=(1,2,4))
 │         └── cost: 1070.00
 ├── G5: (filters G6)
 ├── G6: (ge G7 G8)
 ├── G7: (variable s)
 └── G8: (const 'foo')
