Conversation
❌ TC Merge Gate — Merge BlockedTC changes for this PR are not on TC develop yet.
|
|
@greptile 리뷰해줘 |
🧪 TC Test Environment ReadyCircleCI Testing:
TC Repositories & Branches:
Next Steps:
|
|
결과 정합성을 직접 훼손하는 문제는 확인되지 않아 병합 가능해 보이지만, cross-partition ON dependency가 유발하는 비차단성 최적화 fallback은 개선하는 것이 좋습니다. Reviews (1) · Last reviewed commit: "Join every table an outer join's ON clau..." |
| { | ||
| /* an ON clause only reads nodes that precede the join it belongs to */ | ||
| QO_ASSERT (env, t < QO_NODE_IDX (on_node)); | ||
| QO_ADD_OUTER_DEP_SET (on_node, QO_ENV_NODE (env, t)); |
There was a problem hiding this comment.
FROM a, b LEFT JOIN c ON a.k = b.k에서는 ON term이 QO_TC_OTHER로 강등되고 dummy edge는 b-c만 연결하므로 a가 별도 파티션에 남을 수 있습니다. 이 줄은 c.outer_dep_set에 a를 추가하지만, 파티션 구성은 edge만 사용합니다. 따라서 {b,c} 파티션을 탐색할 때 방문할 수 없는 a가 선행조건으로 남아 파티션 탐색이 실패하고 전체 CBO 계획이 fallback됩니다. 결과 정합성에는 직접적인 영향이 없더라도 합법적인 쿼리가 최적화 경로에서 항상 탈락하므로, 이 outer dependency를 파티션 구성이나 파티션 로컬 선행조건 판정에도 일관되게 반영해 주세요.
Knowledge Base Used: Query optimization
There was a problem hiding this comment.
의존 집합에 담긴 노드가 다른 파티션에 남을 수 있다는 지적은 맞습니다. 다만 예로 드신 질의는 옵티마이저에 도달하지 않으며, 결과의 형태도 다릅니다.
쉼표 조인은 이름 해석에서 거부됩니다
쉼표 조인에서는 ON 절이 쉼표 너머의 테이블을 참조할 수 없습니다. develop 과 이 PR 은 FROM a, b LEFT JOIN c ON a.k = b.k 에 똑같이 아래 오류를 냅니다.
ERROR: '[dba.a].k' in join condition is not defined.
CROSS JOIN 으로는 재현됩니다
CREATE TABLE a (k INT, v INT); CREATE TABLE b (k INT, v INT); CREATE TABLE c (k INT, v INT);
INSERT INTO a VALUES (1,1),(2,2); INSERT INTO b VALUES (1,1),(2,2); INSERT INTO c VALUES (1,1);
UPDATE STATISTICS ON a, b, c;
SELECT /*+ RECOMPILE */ COUNT(*) FROM a CROSS JOIN b LEFT JOIN c ON a.k = b.k;a CROSS JOIN b 의 결과가 4행이고, LEFT OUTER JOIN 이 그 4행을 보존하므로 정답은 4 입니다.
| 빌드 | 결과 |
|---|---|
develop 11.5.0.2608-9ee8bac |
2 (오답) |
이 PR 11.5.0.2603-1f10dba |
Outer join query optimization failed. |
이 PR 에서는 최적화 단계의 fallback 이 아니라 질의가 실패하는 오류가 납니다.
CBRD-27483 에서 다룹니다
원인은 두 가지입니다. CROSS JOIN 과 쉼표 조인이 머지된 뷰가 조인 그래프에 간선을 남기지 않고, qo_discover_partitions() 가 간선 정보를 바탕으로 파티션을 결정합니다. 간선을 잇는 수로 분리했습니다.
There was a problem hiding this comment.
맞습니다. 제가 든 쉼표 조인 예시는 CUBRID의 이름 해석 규칙상 옵티마이저에 도달하지 않으므로 부적절했습니다.
제기한 파티션 분리 시나리오는 CROSS JOIN에서 재현되며, 이 경우 직접 원인은 이 PR의 의존 집합 추가가 아니라 CROSS JOIN/쉼표 조인이 뷰 병합 후 조인 그래프에 간선을 남기지 않는 점입니다. 또한 증상도 단순한 CBO fallback이 아니라 Outer join query optimization failed입니다.
따라서 해당 지적은 이 PR의 결함으로 유지하지 않겠습니다. 간선 연결 문제는 CBRD-27483에서 별도로 다루는 것이 맞습니다.
314d582 to
a3f2f21
Compare
When a LEFT OUTER JOIN's ON clause carries a predicate over a table that is
not the join partner, qo_analyze_term () never recorded that table in the
outer-join node's dependency set. The optimizer was then free to place the
outer join ahead of that table. In that order the predicate cannot be
evaluated where the outer join needs it, so rows that LEFT OUTER JOIN must
preserve with NULL-padded columns were dropped instead.
Whether the optimizer picks that order is decided by cost, so the same query
returns the right answer on one data set and the wrong one on another.
Two predicate shapes reach the defect through different classifications:
- a predicate over two tables is classified QO_TC_JOIN and then demoted to
QO_TC_OTHER, because its tail node differs from the ON-clause owner
- a predicate over one table is classified QO_TC_SARG from the start and
never enters the QO_TC_JOIN handling at all
Both leave the dependency set empty, so record the dependency once the
classification is settled, whatever class the predicate was given.
QO_ENV_NODE () indexes the node array without a bound check and never returns
NULL, and this change widens the access to QO_TC_SARG and QO_TC_OTHER terms,
so assert the location is in range and that it agrees with the node index.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwDdyHWR9thhoJHJtuCNYX
qo_check_skip_term () gathers evidence that an equivalence class is already connected, and counts an equality carried in an outer join's ON clause among it. That equality holds only on the rows the join matched; on a row it preserved with NULL padding the two sides differ, so it says nothing about the class. A later term the class covers is then dropped and the query returns values the join never matched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012jfLrX4jqiAkaPiTdNe5rs
a3f2f21 to
734874b
Compare
|
/run all |
http://jira.cubrid.org/browse/CBRD-27442
Purpose
외부 조인을 그것이 의존하는 테이블보다 앞에 두는 조인 순서가 후보로 남아, 보존되어야 할 행이 사라진다. 원인은 두 곳이다. ON 절이 참조하는 노드를 의존 관계로 기록하지 않거나, 성립하지 않는 등식을 이미 평가된 항의 근거로 삼는다.
옵티마이저는 조인 순서를 정할 때
QO_NODE_OUTER_DEP_SET을 쓴다. 이 집합은 그 노드보다 먼저 조인해야 하는 노드들을 담고,planner_visit_node()가 검사해 만족하지 않는 조합을 버린다. 아래 (1)은 그 집합이 비는 문제이고, (2)는 조인 항을 건너뛸지 판단하는 근거의 문제다.Implementation
(1) ON 절 술어가 조인 상대가 아닌 테이블을 읽는 경우
qo_analyze_term()의 분류 처리가 끝난 자리에 ON 절 조건이 참조하는 노드를 모두QO_NODE_OUTER_DEP_SET에 넣는 블록을 추가한다. 분류를 따지지 않고 적용해, 두 테이블을 참조해QO_TC_OTHER로 강등된 조건과 한 테이블만 참조해QO_TC_SARG로 분류된 조건을 함께 덮는다. 기존QO_TC_JOIN처리 블록은 전자만, 그것도tail_node가 ON 절 소유 노드와 같을 때만 다룬다.QO_ENV_NODE()는 노드 배열을 경계 검사 없이 인덱싱하므로QO_ASSERT세 개를 함께 넣었다. 주소를 만들기 전에location이env->nnodes안에 있는지 확인하고, 꺼낸 노드의 location 이 그 값과 같은지 보고, 반복문 안에서는 ON 절이 자기가 속한 조인보다 앞선 노드만 읽는지 검사한다.(2) 외부 조인 ON 절의 등식을 이미 성립한 근거로 쓰는 경우
qo_check_skip_term()이 동치류가 이미 연결되었다는 근거를 모을 때 외부 조인 ON 절의 등식을 제외한다. 그 등식은 짝이 맞은 행에서만 성립하고 널로 채워진 행에서는 성립하지 않는다.qo_equivalence()에서 외부 조인 ON 항을 빼는 대안은 택하지 않았다. 동치류를 읽는 코드 네 군데 가운데 세 군데는 병합 조인의 정렬 순서로만 쓰고, 외부 조인 ON 등식에도 유효하다. 결함은 동치류 자체가 아니라qo_check_skip_term()이 동치류에 얹은 해석에 있다. 또qo_equivalence()호출이QO_TERM_NOMINAL_SEG대입과 같은if안에 있어, 빼면 홑 동치류가 생겨 외부 조인이 낀 다중 등가 조인이 정렬 순서를 공유하지 못하는 플랜 퇴행이 난다.Remarks
QO_TC_JOIN안의PT_JOIN_LEFT_OUTER,PT_JOIN_RIGHT_OUTER가지에 있는QO_ADD_OUTER_DEP_SET (on_node, head_node)는 (1)의 새 블록과 결과가 겹친다. 그 가지의 항은 노드가 둘이고on_node가tail_node라, 새 블록이 넣는 값이head_node로 같기 때문이다. 두 호출을 지우고 빌드해도 회귀 테스트가 그대로 통과하지만, 변경 범위를 좁게 두려고 남겼다.6522201dab부터 지금까지 같은 형태로 있고, 11.0 부터 드러난다. 10.2 에서는qo_add_dummy_join_term()이PT_JOIN_LEFT_OUTER처리에서 조인 항의 유무를 따지지 않고 의존 집합을 채워 주어 같은 순서가 후보에 오르지 못했다. 결함 (2)는qo_check_skip_term()을 도입한 커밋86063bc164 [CBRD-25214] Remove join predicate already logically evaluated. (#4954)(2024-03-26) 으로 들어왔고, 그 커밋이release/11.4에 있고release/11.3에 없어 11.4 부터 드러난다.develop에서86063bc164하나만 되돌려 빌드하면 결함 (2)의 반례만 정답으로 돌아오고 결함 (1)의 반례는 그대로 오답이다.LEADING으로 위험한 순서를 지정해도 그 순서를 채택하지 않는다. PostgreSQL 은 그 조합에2e10규모의 페널티 비용을 매겨 원래 순서를 유지하고, Oracle 은 힌트를 받아들이되 계획이 바뀌지 않는다. 수정 후 CUBRID 가 두 제품과 일치한다.