Skip to content

[CBRD-26259] Allow order by/group by skip when another composite index column is NOT NULL - #7828

Open
jihyekim-0 wants to merge 2 commits into
CUBRID:developfrom
jihyekim-0:CBRD-26259
Open

[CBRD-26259] Allow order by/group by skip when another composite index column is NOT NULL#7828
jihyekim-0 wants to merge 2 commits into
CUBRID:developfrom
jihyekim-0:CBRD-26259

Conversation

@jihyekim-0

Copy link
Copy Markdown
Contributor

http://jira.cubrid.org/browse/CBRD-26259

Purpose

ORDER BY skip 최적화에서 복합 인덱스를 사용할 수 있음에도, ORDER BY 컬럼이 nullable이라는 이유로 인덱스를 후보에서 제외하는 문제를 수정합니다.

복합 인덱스에서는 일부 key 컬럼이 NULL이더라도 다른 key 컬럼에 값이 있으면 해당 row가 인덱스에 포함됩니다. 따라서 복합 인덱스의 key 컬럼 중 하나라도 NOT NULL임이 보장되면, ORDER BY 컬럼 자체가 nullable이어도 NULL로 인해 해당 row가 인덱스에서 누락되지 않습니다.

예를 들어 다음과 같은 복합 인덱스가 있을 때,

create table t (a int, b int not null);

create index idx_t_ab on t(a, b);

a는 nullable이지만 bNOT NULL이므로 a가 NULL인 row도 (NULL, <non-null value>) 형태로 인덱스에 포함됩니다.

기존 qo_validate_index_for_orderby()는 첫 번째 index key를 중심으로 NULL 가능성을 검사하기 때문에, 첫 번째 key가 nullable이면 다른 key가 NOT NULL임에도 해당 인덱스를 ORDER BY skip에 사용할 수 없는 경우가 있었습니다.

본 수정에서는 복합 인덱스의 전체 key를 확인하여 하나 이상의 key가 non-null임을 보장할 수 있으면 해당 인덱스를 ORDER BY skip에 사용할 수 있도록 합니다.

Test

아래 테스트는 복합 인덱스 (c1, c2, c3)를 기준으로 수행했습니다.

시나리오 수정 전 수정 후 비고
첫 번째 key c1NOT NULL, ORDER BY c1 skip skip 기존 동작 유지
두 번째 key c2NOT NULL, ORDER BY c1 미스킵 skip 이번 수정으로 해결
세 번째 key c3NOT NULL, ORDER BY c1 미스킵 skip 이번 수정으로 해결
c3NOT NULL이지만 쿼리에서 c3 미참조 미스킵 skip QO_SEGMENT가 없는 key의 schema NOT NULL 확인
WHERE c2 > 0 ORDER BY c1 미스킵 skip 조건에 의해 c2의 non-null 보장
WHERE c1 = 5 ORDER BY c2 미스킵 skip c1이 등치조건으로 고정되어 c2 순서로 index scan 가능
OR 조건 / LEFT OUTER JOIN 미스킵 미스킵 기존 동작 유지 및 오탐 없음
GROUP BY, 세 번째 key c3NOT NULL 미스킵 skip 동일 NULL 판정 로직 적용
GROUP BY WITH ROLLUP 미스킵 미스킵 기존 동작 유지

Implementation

  1. 복합 인덱스의 key 컬럼 중 하나라도 non-null임을 보장할 수 있는지 검사하는 qo_validate_index_key_notnull()을 추가했습니다.

각 index key에 대해 다음을 확인합니다.

  • schema에서 컬럼에 NOT NULL이 지정되어 있는지
  • 해당 key에 IS NOT NULL 조건이 있는지
  • 기존 qo_validate_index_attr_notnull() 검사로 non-null임을 확인할 수 있는지

하나라도 만족하면 모든 index key가 동시에 NULL인 경우가 발생할 수 없으므로 true를 반환합니다.

질의에서 참조되지 않은 index key는 QO_SEGMENT가 생성되지 않을 수 있으므로, segment 존재 여부와 관계없이 확인할 수 있는 schema의 NOT NULL 속성을 먼저 검사합니다.

  1. 기존 qo_validate_index_term_notnull()은 항상 첫 번째 index key (seg_idxs[0])만 검사했습니다. 이를 seg_idx를 인자로 받도록 변경하여 기존 호출에서는 첫 번째 key를 그대로 검사하고, qo_validate_index_key_notnull()에서는 각 index key의 IS NOT NULL 조건을 검사할 수 있도록 했습니다.

Remarks

  1. GROUP BY의 index skip 검사도 qo_validate_index_for_groupby()ORDER BY와 동일하게 group by 컬럼 자신의 NOT NULL 여부만 확인하고 있어 동일한 문제가 발생할 수 있습니다. 두 함수가 완전히 같은 헬퍼(qo_validate_index_term_notnull, qo_validate_index_attr_notnull)를 공유하는 구조라, 본 이슈의 직접 대상은 ORDER BY이지만 GROUP BY에도 같은 검사를 함께 적용했습니다.

  2. 본 수정에서는 일반 B-tree 인덱스만 대상으로 하며 다음 인덱스는 제외합니다.

  • Filter index — filter predicate에 의해 일부 row가 인덱스에서 제외될 수 있으므로, key의 NOT NULL만으로 row가 인덱스에 존재함을 보장할 수 없습니다.
  • Function index — order by/group by skip 후보로 선택되는 경우를 확인하지 못했지만, 만약 선택될 경우 함수식 세그먼트 이름이 실제 attribute와 매칭되지 않아 assert(false)로 이어질 수 있어 명시적으로 제외했습니다.
  • Prefix index — CUBRID는 복합 prefix 인덱스를 지원하지 않습니다.

…roup by skip

A composite index only excludes a row from the B-tree when ALL of its key
columns are null, so the order by/group by column itself does not need to
be the one proven not null -- any other key column in the same index is
enough to guarantee the index is complete. The existing check only looked
at the order by/group by column, so a nullable leading column with a
not-null trailing column (schema constraint or a safe WHERE predicate)
missed the skip optimization even though it was safe.

qo_validate_index_key_notnull() reuses the existing per-column checks
(qo_validate_index_term_notnull, qo_validate_index_attr_notnull) against
each key segment instead of duplicating their logic. Filter, function and
prefix indexes are intentionally excluded and keep relying on the existing
single-column check only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

❌ TC Merge Gate — Merge Blocked

One or more TC PRs are still open. Please merge or close them before merging this PR.

TC Repositories & Branches:

  • cubrid-testcases: TC PR tc/pr-7828 is open (draft) — must be merged or closed first
  • cubrid-testcases-private-ex: TC PR tc/pr-7828 is open (draft) — must be merged or closed first

Steps to unblock:

  1. Merge or close all TC PRs listed above.
  2. Re-run this check: Actions tab → TC Merge Gate → Re-run failed jobs

@github-actions

Copy link
Copy Markdown

🧪 TC Test Environment Ready

CircleCI Testing:

  • CircleCI will automatically test using the branches below.

TC Repositories & Branches:

Next Steps:

  1. Wait for CircleCI tests to complete
  2. If CircleCI tests failed, please check the test results and fix the issues.
  3. When ready to merge this PR, please merge the TC PR first, then merge this PR.

@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Reviews (1): Last reviewed commit: "[CBRD-26259] Consider not-null composite..." | Re-trigger Greptile

Comment thread src/optimizer/query_planner.c Outdated
…check

is_func_index is only true when the function expression is the index's
first key, so a composite function index with the expression in a later
position (e.g. (c1, UPPER(c2))) slipped past the guard. In that case
qo_validate_index_key_notnull() would read constraints->attributes[] at
the function's position, which holds the function's underlying argument
column rather than the function key itself, and could treat that
column's schema NOT NULL flag as proof that the function key can never
be null.

Check constraints->func_index_info instead, which is set whenever the
index has a function component at any position, not just the first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@jihyekim-0

Copy link
Copy Markdown
Contributor Author

@greptile

@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Reviews (2): Last reviewed commit: "[CBRD-26259] Exclude a trailing function..." | Re-trigger Greptile

@jihyekim-0

Copy link
Copy Markdown
Contributor Author

/run all

1 similar comment
@jihyekim-0

Copy link
Copy Markdown
Contributor Author

/run all

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant