Skip to content

[CBRD-27336] Support a remote REPLACE ... SELECT whose source is a local table - #7985

Open
kwangsoochae wants to merge 7 commits into
CUBRID:developfrom
kwangsoochae:CBRD-27336_dblink_replace_local_source
Open

kwangsoochae wants to merge 7 commits into
CUBRID:developfrom
kwangsoochae:CBRD-27336_dblink_replace_local_source

Conversation

@kwangsoochae

@kwangsoochae kwangsoochae commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

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

Purpose

원격 테이블 대상 REPLACE 는 리터럴 형태만 동작하고(문장이 그대로 원격에 전달된다), 소스가 로컬 테이블이면
파서가 거절했다.

REPLACE INTO remote_t@srv (id, name) SELECT id, name FROM local_t;
-- dblink: this combination of local and remote references is not supported (...)

INSERT ... SELECT 는 로컬에서 행을 만들어 원격에 보내는 값 sink 로 로컬 소스를 받는다. 이 sink 는 소스의
행마다 원격에 한 문장씩 보내고, REPLACE 도 행 단위로 처리된다 — 같은 키가 소스에 여러 행 있으면 소스 질의
순서상 마지막 행이 남는다. 따라서 새 실행 모드 없이 sink 가 준비하는 문장을
REPLACE INTO 로 바꾸면
같은 결과에 도달한다.

Implementation

커밋 단위로 리뷰할 수 있도록 세 단계로 나눴다. 네 번째 커밋은 Remarks 첫 항목의 선행 결함
수정이고, 그 뒤는 주석 정리와 develop merge 다.

1. 원격에 보낼 문장을 조립한다

DBLINK_DML_KIND 에 REPLACE 를 더하고 문장 조립 함수가 접두만 가르게 한다. 컬럼 목록·자리표·바인드 값은
INSERT 와 같으므로, 실행기에서 INSERT_PROC 를 읽는 자리들은 두 종류가 case 를 공유한다.

INSERT INTO t (c1, c2) VALUES (?, ?)      -- AS-IS
REPLACE INTO t (c1, c2) VALUES (?, ?)     -- TO-BE (REPLACE 일 때)

문장 머리의 /* DBLINK ... */ 주석은 게이트웨이가 문장 종류로 읽는 값이고, 그 어휘에 REPLACE 가 없다.
그래서 두 종류 모두 INSERT 를 싣는다.

2. 원격이 보고한 행 수를 그대로 누적한다

INSERT sink 는 문장마다 1 을 셌지만, REPLACE 는 교체가 일어난 행에 원격이 2 를 돌려준다. 원격이 돌려주는
수를 누적하도록 바꿨다(원격 DELETE sink 와 같은 방식).

3. 게이트를 연다

sink 자격 조건에서 REPLACE 제외를 걷어내고 문장 종류를 실행 계획에 싣는다. 직렬화 포맷은 바뀌지 않는다 —
그 필드는 이미 전송되고 있었다.

소스가 로컬과 타깃 서버의 원격 테이블을 함께 읽는 형태도 함께 열린다. sink 자격을 넓히면 그대로 닿는 경로라
따로 막지 않았다.

Remarks

  • 같은 실행 경로의 develop 선행 결함을 함께 고친다. 원격 대상 INSERT / REPLACE 의 로컬 소스가 컴파일 시점에
    거짓이면(WHERE 1 = 0, 그런 조건을 정의에 가진 뷰) 클라이언트가 죽었다. develop 에서는 로컬 소스 REPLACE 가
    거절돼 INSERT 에만 드러나던 결함인데, 이 PR 이 REPLACE 를 열면 그대로 상속한다. XASL 이 없는 원격 대상은
    원격 DELETE 와 같이 0 행으로 끝낸다(do_execute_insert).
  • ON DUPLICATE KEY UPDATE 는 범위 밖이며 지금과 같이 거절한다(sink 에 갱신 대입을 실을 자리가 없다). 별도 PR 로 뗀다.
  • 벤더 원격(게이트웨이): Oracle 은 REPLACE 구문이 없어 원격이 거절한다 — 리터럴 형태와 같은 자리, 같은 이유다.

@github-actions

github-actions Bot commented Sep 20, 2026 •

Copy link
Copy Markdown

❌ TC Merge Gate — Merge Blocked

TC changes for this PR are not on TC develop yet.

  • ❌ cubrid-testcases: TC PR tc/pr-7985 is open (draft) — merge it, or close it if it carries no change
  • ❌ cubrid-testcases-private-ex: TC PR tc/pr-7985 is open (draft) — merge it, or close it if it carries no change
    Steps to unblock:
  1. Merge the TC PRs listed above. Close one only when it carries no change.
  2. Re-run this check: Actions tab → TC Merge Gate → Re-run failed jobs

@kwangsoochae

Copy link
Copy Markdown
Contributor Author

@greptileai review

@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.

@kwangsoochae

Copy link
Copy Markdown
Contributor Author

/run all

@greptile-apps

greptile-apps Bot commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

이전 리뷰 이후 변경에서 병합을 막을 새 결함이 확인되지 않아 현재 상태는 병합해도 안전해 보입니다.

Reviews (2) · Last reviewed commit: "Merge remote-tracking branch 'upstream/d..."

@kwangsoochae

Copy link
Copy Markdown
Contributor Author

/run all

@kwangsoochae
kwangsoochae force-pushed the CBRD-27336_dblink_replace_local_source branch from a4eb40f to 6a0cd50 Compare September 22, 2026 00:20
@kwangsoochae

Copy link
Copy Markdown
Contributor Author

/run shell

@kwangsoochae
kwangsoochae force-pushed the CBRD-27336_dblink_replace_local_source branch from 6a0cd50 to fd49b61 Compare September 22, 2026 11:00
The sink builds one statement kind, a plain INSERT. A REPLACE that reached it would
insert instead of replacing, so the parser gate refuses the shape and the statement
falls through to the mixed local/remote rejection.

Give DBLINK_DML_KIND a REPLACE member and let the builder switch the prefix. Nothing
else about the statement changes -- REPLACE takes an INSERT's shape, so the column
list, the placeholders and the values the sink binds are the same, and the two kinds
share a case wherever the executor reads INSERT_PROC. The builder's own diagnostics
name the statement it was asked for rather than always saying INSERT.

The leading /* DBLINK ... */ comment is not a label. A CUBRID gateway reads the word
after DBLINK and maps it to a statement type, refusing the statement when the word is
not one it knows, and that vocabulary has no REPLACE. Both kinds carry INSERT there and
the statement itself says which one it is.

The gate stays shut: do_replace never reaches the plan yet, so the executor still picks
INSERT. The commit that opens it comes last, so no revision in between sends the wrong
statement to a remote.
The INSERT sink counted one row per statement it sent, because a positional INSERT of
one row was expected to affect exactly one. A REPLACE breaks that: the remote counts
the row it removed as well as the one it inserted, so a row that replaces answers 2.

Read affected_rows and add it, the way the DELETE sink already does. The text-push path
reports the remote's number as it stands, so the two paths now answer alike for the same
statement.

This also changes what a plain INSERT sink reports if a remote ever answers with
something other than 1. The old comment named that case and left it out of scope; it was
not reproduced on the remotes the INSERT sink was measured against, so nothing observed
changes here.

The callee's header no longer offers the INSERT sink as the example of a caller that
ignores the count, and the comment naming which statements reach cci_execute is dropped
rather than extended -- what runs there is whatever the prepare built.
The gate admitted only a plain INSERT ... SELECT, so a REPLACE whose source was local
fell through to the mixed local/remote rejection. The sink can send the statement now,
and the plan carries which one it is, so the shape has somewhere to go.

Drop do_replace from the gate's exclusions and set it on the remote sink's plan. ODKU
stays out: the sink has nowhere to carry the update assignments yet.

This also admits a source that mixes a local table with a remote one on the target's own
server. case PT_INSERT counts sub_sel_server_cnt whatever the sink kind, so the
same-server conversion those sources need is already wired -- holding the shape back
would mean adding a guard to refuse what otherwise works.

The comment that deferred both clauses quoted a rejection message CBRD-26921 has since
replaced, and is rewritten to say what the gate now admits.
@kwangsoochae
kwangsoochae force-pushed the CBRD-27336_dblink_replace_local_source branch from fd49b61 to 996e1a0 Compare September 22, 2026 11:00
@kwangsoochae

Copy link
Copy Markdown
Contributor Author

/run all

do_prepare_insert () marks a source query with a compile-time false WHERE by
leaving xasl_id NULL: there is nothing to execute. do_execute_insert () skipped
that check for a remote target, so a remote INSERT or REPLACE with a local
source that reduces to false -- WHERE 1 = 0, or a view defined with such a
condition -- asked the server to run a NULL XASL and the client died.

Return with no rows when a remote target has no XASL, as do_execute_delete ()
already does for false where or a statement that did not prepare correctly.
The do_insert () fallback stays with local targets, as CBRD-24867 left it.
@kwangsoochae

Copy link
Copy Markdown
Contributor Author

/run all

The comment over the remote INSERT gate ended by calling the mixed
local/remote rejection "the same behavior develop gives for it". Once this
lands, develop is this code, so the phrase stops saying anything.

The comment in do_execute_insert () named CBRD-24867 as the origin of the
local-only do_insert () fallback. That ticket gives no reason for it, so the
number tells a reader no more than git blame does.
@kwangsoochae

Copy link
Copy Markdown
Contributor Author

/run all

@kwangsoochae

Copy link
Copy Markdown
Contributor Author

/run rerun 35818139527

@kwangsoochae
kwangsoochae marked this pull request as ready for review September 23, 2026 05:40
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.

2 participants