# LogicTest: local

statement ok
CREATE TABLE t (k INT PRIMARY KEY, v INT, w INT, INDEX(v))

# There must be no limit at the index scan level.
query T
EXPLAIN (VERBOSE) SELECT * FROM t WHERE v > 4 AND v < 8 AND w > 30 ORDER BY v LIMIT 2
----
distribution: local
vectorized: true
·
• limit
│ columns: (k, v, w)
│ count: 2
│
└── • filter
    │ columns: (k, v, w)
    │ ordering: +v
    │ estimated row count: 28 (missing stats)
    │ filter: w > 30
    │
    └── • index join
        │ columns: (k, v, w)
        │ ordering: +v
        │ estimated row count: 30 (missing stats)
        │ table: t@t_pkey
        │ key columns: k
        │
        └── • scan
              columns: (k, v)
              ordering: +v
              estimated row count: 30 (missing stats)
              table: t@t_v_idx
              spans: /5-/8

# This kind of query can be used to work around memory usage limits. We need to
# choose the "hard" limit of 100 over the "soft" limit of 25 (with the hard
# limit we will only store 100 rows in the sort node). See #19677.
query T
EXPLAIN (VERBOSE) SELECT DISTINCT w FROM (SELECT w FROM t ORDER BY w LIMIT 100) ORDER BY w LIMIT 25
----
distribution: local
vectorized: true
·
• limit
│ columns: (w)
│ count: 25
│
└── • distinct
    │ columns: (w)
    │ ordering: +w
    │ estimated row count: 65 (missing stats)
    │ distinct on: w
    │ order key: w
    │
    └── • top-k
        │ columns: (w)
        │ estimated row count: 100 (missing stats)
        │ order: +w
        │ k: 100
        │
        └── • scan
              columns: (w)
              estimated row count: 1,000 (missing stats)
              table: t@t_pkey
              spans: FULL SCAN

query T
EXPLAIN (VERBOSE) SELECT k, v FROM t ORDER BY k LIMIT 5
----
distribution: local
vectorized: true
·
• scan
  columns: (k, v)
  ordering: +k
  estimated row count: 5 (missing stats)
  table: t@t_pkey
  spans: LIMITED SCAN
  limit: 5

query T
EXPLAIN (VERBOSE) SELECT k, v FROM t ORDER BY k OFFSET 5
----
distribution: local
vectorized: true
·
• limit
│ columns: (k, v)
│ offset: 5
│
└── • scan
      columns: (k, v)
      ordering: +k
      estimated row count: 1,000 (missing stats)
      table: t@t_pkey
      spans: FULL SCAN

query T
EXPLAIN (VERBOSE) SELECT k, v FROM t ORDER BY v LIMIT (1+4) OFFSET 1
----
distribution: local
vectorized: true
·
• limit
│ columns: (k, v)
│ offset: 1
│
└── • scan
      columns: (k, v)
      ordering: +v
      estimated row count: 6 (missing stats)
      table: t@t_v_idx
      spans: LIMITED SCAN
      limit: 6

query T
EXPLAIN (VERBOSE) SELECT k, v FROM t ORDER BY v DESC LIMIT (1+4) OFFSET 1
----
distribution: local
vectorized: true
·
• limit
│ columns: (k, v)
│ offset: 1
│
└── • revscan
      columns: (k, v)
      ordering: -v
      estimated row count: 6 (missing stats)
      table: t@t_v_idx
      spans: LIMITED SCAN
      limit: 6

query T
EXPLAIN (VERBOSE) SELECT sum(w) FROM t GROUP BY k, v ORDER BY v DESC LIMIT 10
----
distribution: local
vectorized: true
·
• project
│ columns: (sum)
│
└── • project
    │ columns: (any_not_null, sum)
    │ ordering: -any_not_null
    │
    └── • top-k
        │ columns: (k, sum, any_not_null)
        │ estimated row count: 10 (missing stats)
        │ order: -any_not_null
        │ k: 10
        │
        └── • group (streaming)
            │ columns: (k, sum, any_not_null)
            │ estimated row count: 1,000 (missing stats)
            │ aggregate 0: sum(w)
            │ aggregate 1: any_not_null(v)
            │ group by: k
            │ ordered: +k
            │
            └── • scan
                  columns: (k, v, w)
                  ordering: +k
                  estimated row count: 1,000 (missing stats)
                  table: t@t_pkey
                  spans: FULL SCAN

query T
EXPLAIN (VERBOSE) SELECT k FROM (SELECT k, v FROM t ORDER BY v LIMIT 4)
----
distribution: local
vectorized: true
·
• project
│ columns: (k)
│
└── • scan
      columns: (k, v)
      estimated row count: 4 (missing stats)
      table: t@t_v_idx
      spans: LIMITED SCAN
      limit: 4

query T
EXPLAIN (VERBOSE) SELECT k FROM (SELECT k, v, w FROM t ORDER BY v LIMIT 4)
----
distribution: local
vectorized: true
·
• project
│ columns: (k)
│
└── • scan
      columns: (k, v)
      estimated row count: 4 (missing stats)
      table: t@t_v_idx
      spans: LIMITED SCAN
      limit: 4

query T
EXPLAIN (VERBOSE) SELECT k FROM (SELECT k FROM t LIMIT 5) WHERE k != 2
----
distribution: local
vectorized: true
·
• filter
│ columns: (k)
│ estimated row count: 2 (missing stats)
│ filter: k != 2
│
└── • scan
      columns: (k)
      estimated row count: 5 (missing stats)
      table: t@t_v_idx
      spans: LIMITED SCAN
      limit: 5

query T
EXPLAIN (VERBOSE) SELECT k, w FROM t WHERE v >= 1 AND v <= 100 LIMIT 10
----
distribution: local
vectorized: true
·
• project
│ columns: (k, w)
│
└── • limit
    │ columns: (k, v, w)
    │ count: 10
    │
    └── • filter
        │ columns: (k, v, w)
        │ estimated row count: 990 (missing stats)
        │ filter: (v >= 1) AND (v <= 100)
        │
        └── • scan
              columns: (k, v, w)
              estimated row count: 1,000 (missing stats)
              table: t@t_pkey
              spans: FULL SCAN (SOFT LIMIT)

query T
EXPLAIN (VERBOSE) SELECT k, w FROM t WHERE v >= 1 AND v <= 100 ORDER BY v LIMIT 10
----
distribution: local
vectorized: true
·
• project
│ columns: (k, w)
│
└── • index join
    │ columns: (k, v, w)
    │ ordering: +v
    │ estimated row count: 10 (missing stats)
    │ table: t@t_pkey
    │ key columns: k
    │
    └── • scan
          columns: (k, v)
          ordering: +v
          estimated row count: 10 (missing stats)
          table: t@t_v_idx
          spans: /1-/101
          limit: 10

query T
EXPLAIN (VERBOSE) SELECT k, w FROM (SELECT * FROM t WHERE v >= 1 AND v <= 100 ORDER BY k LIMIT 10) ORDER BY v
----
distribution: local
vectorized: true
·
• project
│ columns: (k, w)
│
└── • sort
    │ columns: (k, v, w)
    │ estimated row count: 10 (missing stats)
    │ order: +v
    │
    └── • limit
        │ columns: (k, v, w)
        │ count: 10
        │
        └── • filter
            │ columns: (k, v, w)
            │ ordering: +k
            │ estimated row count: 990 (missing stats)
            │ filter: (v >= 1) AND (v <= 100)
            │
            └── • scan
                  columns: (k, v, w)
                  ordering: +k
                  estimated row count: 1,000 (missing stats)
                  table: t@t_pkey
                  spans: FULL SCAN (SOFT LIMIT)

# Regression test for #47283: scan with both hard limit and soft limit.
statement ok
CREATE TABLE t_47283(k INT PRIMARY KEY, a INT)

# The scan should have a hard limit.
query T
EXPLAIN (VERBOSE) SELECT * FROM (SELECT * FROM t_47283 ORDER BY k LIMIT 4) WHERE a > 5 LIMIT 1
----
distribution: local
vectorized: true
·
• limit
│ columns: (k, a)
│ count: 1
│
└── • filter
    │ columns: (k, a)
    │ estimated row count: 2 (missing stats)
    │ filter: a > 5
    │
    └── • scan
          columns: (k, a)
          estimated row count: 4 (missing stats)
          table: t_47283@t_47283_pkey
          spans: LIMITED SCAN
          limit: 4

# Regression test for #55156: split hash-sharded index scan.
statement ok
CREATE TABLE IF NOT EXISTS user_checklist_items (
    tenant_id UUID,
    location_id UUID,
    checklist_item_id UUID,
    user_id STRING,
    configuration_maintenance_id UUID NOT NULL,
    configuration_maintenance_item_id UUID NOT NULL,
    date_should_be_completed DATE NOT NULL,
    is_recurrent_assignation BOOL,
    location_name STRING NOT NULL,
    order_item FLOAT NOT NULL,
    title STRING NOT NULL,
    create_date TIMESTAMPTZ NOT NULL,
    PRIMARY KEY (tenant_id, location_id, checklist_item_id, user_id)
);
CREATE INDEX IF NOT EXISTS userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem ON user_checklist_items (tenant_id, user_id, date_should_be_completed, location_name, title, order_item, checklist_item_id) USING HASH WITH (bucket_count=8);
CREATE INDEX IF NOT EXISTS userchecklistitems_tenantid_locationid_configurationmaintenanceid_configurationmaintenanceitemid_dateshouldbecompleted ON user_checklist_items (tenant_id, location_id, configuration_maintenance_id, configuration_maintenance_item_id, date_should_be_completed) USING HASH WITH (bucket_count=8);

statement ok
ALTER TABLE user_checklist_items INJECT STATISTICS
'[
  {
    "columns": ["tenant_id"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 10000,
    "distinct_count": 1,
    "null_count": 0
  },
  {
    "columns": ["user_id"],
    "created_at": "2018-01-01 1:00:00.00000+00:00",
    "row_count": 10000,
    "distinct_count": 1,
    "null_count": 0
  }
]'

# Expect a scan over each shard.
query T
EXPLAIN
  SELECT
    tenant_id, user_id, date_should_be_completed
  FROM
    user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
  WHERE
    tenant_id = 'a2a0dd49-23cf-4cf2-b823-61701c416e60'
    AND user_id = '01603523-c6f0-4e12-a43f-524c76b0fa8f'
    AND date_should_be_completed >= '2020-10-01'
  ORDER BY
    date_should_be_completed
  LIMIT
    5;
----
distribution: local
vectorized: true
·
• limit
│ count: 5
│
└── • union all
    │ estimated row count: 40
    │
    ├── • union all
    │   │ estimated row count: 20
    │   │
    │   ├── • union all
    │   │   │ estimated row count: 10
    │   │   │
    │   │   ├── • scan
    │   │   │     estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
    │   │   │     table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
    │   │   │     spans: [/0/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /0/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
    │   │   │     limit: 5
    │   │   │
    │   │   └── • scan
    │   │         estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
    │   │         table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
    │   │         spans: [/1/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /1/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
    │   │         limit: 5
    │   │
    │   └── • union all
    │       │ estimated row count: 10
    │       │
    │       ├── • scan
    │       │     estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
    │       │     table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
    │       │     spans: [/2/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /2/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
    │       │     limit: 5
    │       │
    │       └── • scan
    │             estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
    │             table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
    │             spans: [/3/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /3/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
    │             limit: 5
    │
    └── • union all
        │ estimated row count: 20
        │
        ├── • union all
        │   │ estimated row count: 10
        │   │
        │   ├── • scan
        │   │     estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
        │   │     table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
        │   │     spans: [/4/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /4/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
        │   │     limit: 5
        │   │
        │   └── • scan
        │         estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
        │         table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
        │         spans: [/5/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /5/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
        │         limit: 5
        │
        └── • union all
            │ estimated row count: 10
            │
            ├── • scan
            │     estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
            │     table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
            │     spans: [/6/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /6/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
            │     limit: 5
            │
            └── • scan
                  estimated row count: 5 (0.05% of the table; stats collected <hidden> ago)
                  table: user_checklist_items@userchecklistitems_tenantid_userid_dateshouldbecompleted_locationname_orderitem
                  spans: [/7/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f'/'2020-10-01' - /7/'a2a0dd49-23cf-4cf2-b823-61701c416e60'/'01603523-c6f0-4e12-a43f-524c76b0fa8f']
                  limit: 5

query T
EXPLAIN (VERBOSE) SELECT * FROM t ORDER BY v, w LIMIT 5
----
distribution: local
vectorized: true
·
• top-k
│ columns: (k, v, w)
│ estimated row count: 5 (missing stats)
│ order: +v,+w
│ k: 5
│
└── • scan
      columns: (k, v, w)
      estimated row count: 1,000 (missing stats)
      table: t@t_pkey
      spans: FULL SCAN



statement ok
CREATE TABLE a AS (SELECT i, i AS j FROM generate_series(0, 1000) AS gen(i));

statement ok
CREATE INDEX ON a (i, j);

statement ok
CREATE TABLE b AS SELECT k, k::STRING AS s FROM generate_series(0, 99) AS gen(k);

statement ok
CREATE INDEX ON b (k, s);

statement ok
ANALYZE a;

statement ok
ANALYZE b;

query T retry
EXPLAIN SELECT * FROM a INNER LOOKUP JOIN b ON k = j ORDER BY i LIMIT 5;
----
distribution: local
vectorized: true
·
• limit
│ count: 5
│
└── • lookup join
    │ estimated row count: 100
    │ table: b@b_k_s_idx
    │ equality: (j) = (k)
    │
    └── • scan
          estimated row count: 100 - 1,001 (100% of the table; stats collected <hidden> ago)
          table: a@a_i_j_idx
          spans: FULL SCAN (SOFT LIMIT)

# A limit cannot be pushed into the scan of a virtual table with ORDER BY.
query T
EXPLAIN SELECT oid, typname FROM pg_type ORDER BY oid LIMIT 10
----
distribution: local
vectorized: true
·
• limit
│ count: 10
│
└── • virtual table
      table: pg_type@pg_type_oid_idx

# A limit can be pushed into the scan of a virtual table without ORDER BY.
query T
EXPLAIN SELECT oid, typname FROM pg_type LIMIT 10
----
distribution: local
vectorized: true
·
• virtual table
  table: pg_type@primary
  limit: 10

# A limit cannot be pushed into the constrained scan of a virtual table with
# ORDER BY.
query T
EXPLAIN SELECT oid, typname FROM pg_type WHERE OID BETWEEN 1 AND 1000 ORDER BY oid LIMIT 10
----
distribution: local
vectorized: true
·
• limit
│ count: 10
│
└── • virtual table
      table: pg_type@primary
      virtual table filter

# Regression test for #69685 - a limit cannot be pushed below a window function
# if its order-by references the window function.
query T
EXPLAIN SELECT * FROM generate_series(1, 10) ORDER BY row_number() OVER () LIMIT 1;
----
distribution: local
vectorized: true
·
• top-k
│ estimated row count: 1
│ order: +row_number
│ k: 1
│
└── • window
    │ estimated row count: 10
    │
    └── • project set
        │ estimated row count: 10
        │
        └── • emptyrow

# Regression test for not incorporating the OFFSET value into the limit hint.
statement ok
CREATE TABLE t_offset (k INT PRIMARY KEY);

statement ok
INSERT INTO t_offset SELECT generate_series(1, 10)

statement ok
SET tracing = on,kv,results; SELECT * FROM t_offset LIMIT 1 OFFSET 3; SET tracing = off

query T
SELECT message FROM [SHOW KV TRACE FOR SESSION] WITH ORDINALITY
 WHERE message LIKE 'fetched:%'
 ORDER BY message, ordinality ASC
----
fetched: /t_offset/t_offset_pkey/1 -> <undecoded>
fetched: /t_offset/t_offset_pkey/2 -> <undecoded>
fetched: /t_offset/t_offset_pkey/3 -> <undecoded>
fetched: /t_offset/t_offset_pkey/4 -> <undecoded>
