SQLite Session 모듈 C/C++ 인터페이스

SQLite Session 모듈 C/C++ 인터페이스 (Session Module C/C++ Interface)

이 페이지는 SQLite 세션 확장에 대한 C 언어 인터페이스를 정의해요. 이것은 튜토리얼이 아니에요. 이 페이지들은 쉽게 읽기보다는 정확하도록 설계됐어요. 튜토리얼은 별도로 제공돼요.

이 페이지는 모든 C 언어 인터페이스 정보를 단일 HTML 파일에 담고 있어요. 같은 정보는 보기 쉽도록 몇 개의 더 작은 페이지로 나누어서도 제공돼요.

이 문서는 소스 코드 파일 sqlite3session.h의 주석을 스캔하는 스크립트로 만들어져요.

출처: 문서

본문

객체 (Objects)

상수 (Constants)

함수 (Functions)

sqlite3changegroup_config()의 옵션 (Options for sqlite3changegroup_config())

#define SQLITE_CHANGEGROUP_CONFIG_PATCHSET 1

다음 값들이 sqlite3changegroup_config()의 2번째 매개변수로 전달될 수 있어요.

SQLITE_CHANGEGROUP_CONFIG_PATCHSET

변경 그룹(changegroup) 객체는 changeset 또는 patchset 중 하나를 생성해요. 보통 이것은 sqlite3changegroup_add()에 대한 첫 호출에 changeset이 전달되는지 patchset이 전달되는지에 의해 결정돼요. 또는 sqlite3changegroup_change_xxx() API를 사용해 첫 변경이 변경 그룹 객체에 추가되면, 이 옵션을 사용해 변경 그룹 객체가 changeset을 생성할지 patchset을 생성할지 구성할 수 있어요.

이 옵션이 호출되면 매개변수 pArg는 int 타입의 값을 가리켜야 해요. 변경 그룹이 현재 0개의 변경을 포함하고 있고, int 변수의 값이 0이거나 0보다 크면, 변경 그룹은 각각 changeset 또는 patchset을 생성하도록 구성돼요. 변경 그룹이 이미 변경을 누적하기 시작해서 구성되지 않는다면 그것은 오류가 아니라 무연산(no-op)이에요.

반환하기 전에, 변경 그룹이 changeset을 생성하도록 구성되면 int 변수는 0으로, patchset을 생성하도록 구성되면 1로 설정돼요.

sqlite3changeset_start_v2의 플래그 (Flags for sqlite3changeset_start_v2)

#define SQLITE_CHANGESETSTART_INVERT        0x0002

다음 플래그들이 sqlite3changeset_start_v2sqlite3changeset_start_v2_strm의 4번째 매개변수로 전달될 수 있어요.

SQLITE_CHANGESETSTART_INVERT

그것을 반복하는 동안 changeset을 뒤집어요. 이것은 changeset을 적용하기 전에 sqlite3changeset_invert()를 사용해 뒤집는 것과 동등해요. patchset과 함께 이 플래그를 지정하면 오류예요.

sqlite3session_config()의 값 (Values for sqlite3session_config())

#define SQLITE_SESSION_CONFIG_STRMSIZE 1

변경 그룹 핸들 (Changegroup Handle)

typedef struct sqlite3_changegroup sqlite3_changegroup;

변경 그룹은 둘 이상의 changeset 또는 patchset을 결합하는 데 사용되는 객체예요.

생성자: sqlite3changegroup_new()

소멸자: sqlite3changegroup_delete()

메서드: sqlite3changegroup_add(), sqlite3changegroup_add_change(), sqlite3changegroup_output()

변경 집합 반복자 핸들 (Changeset Iterator Handle)

typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;

이 객체의 인스턴스는 changeset 또는 patchset의 요소를 반복하기 위한 커서 역할을 해요.

생성자: sqlite3changeset_start(), sqlite3changeset_start_v2()

Changeset 재기반화 (Rebasing changesets)

typedef struct sqlite3_rebaser sqlite3_rebaser;

중요: 이 인터페이스는 실험적(experimental)이며 예고 없이 변경될 수 있어요.

상태 S0의 데이터베이스를 호스팅하는 사이트가 있다고 가정해 보세요. 그리고 그 데이터베이스를 상태 S1로 옮기는 수정이 이루어지고 changeset이 기록된다고 해요(이것을 "local" changeset이라 해요). 그런 다음 다른 사이트에서 S0을 기반으로 한 changeset("remote" changeset)을 받아 데이터베이스에 적용한다고 해요. 데이터베이스는 그 후 상태 (S1+"remote")가 돼요. 정확한 상태는 "remote"를 적용하는 동안 이루어진 충돌 해결 결정(OMIT 또는 REPLACE)에 따라 달라져요. changeset을 재기반화(rebase)한다는 것은 이러한 충돌 해결 결정을 고려하도록 그것을 갱신해서, 같은 충돌이 네트워크의 다른 곳에서 해결될 필요가 없도록 하는 것이에요.

예를 들어 local과 remote changeset 둘 다 "CREATE TABLE t1(a PRIMARY KEY, b)"에서 같은 키의 INSERT를 포함한다면:

  local:  INSERT INTO t1 VALUES(1, 'v1');
  remote: INSERT INTO t1 VALUES(1, 'v2');

충돌 해결이 REPLACE라면, INSERT 변경은 local changeset에서 제거돼요(덮어써졌기 때문). 또는 충돌 해결이 "OMIT"라면, local changeset은 대신 다음을 포함하도록 수정돼요.

          UPDATE t1 SET b = 'v2' WHERE a=1;

local changeset 내의 변경은 다음과 같이 재기반화돼요.

Local INSERT — 이것은 remote INSERT와만 충돌할 수 있어요. 충돌 해결이 OMIT라면, 재기반화된 changeset에 UPDATE 변경을 추가해요. 또는 충돌 해결이 REPLACE라면, 재기반화된 changeset에 아무것도 추가하지 않아요.

Local DELETE — 이것은 remote UPDATE 또는 DELETE와 충돌할 수 있어요. 두 경우 모두 가능한 유일한 해결은 OMIT예요. remote 연산이 DELETE라면 재기반화된 changeset에 변경을 추가하지 않아요. remote 연산이 UPDATE라면, 변경의 old.* 필드가 UPDATE의 new.* 값에 반영되도록 갱신돼요.

Local UPDATE — 이것은 remote UPDATE 또는 DELETE와 충돌할 수 있어요. DELETE와 충돌하고 충돌 해결이 OMIT라면, 업데이트는 INSERT로 바뀌어요. 업데이트 변경의 new.* 레코드에서 정의되지 않은 값은 충돌하는 DELETE의 old.* 값을 사용해 채워져요. 또는 충돌 해결이 REPLACE라면, UPDATE 변경은 재기반화된 changeset에서 단순히 생략돼요.

remote UPDATE와 충돌하고 해결이 OMIT라면, old.* 값은 remote 변경의 new.* 값을 사용해 재기반화돼요. 또는 해결이 REPLACE라면, 변경이 충돌하는 remote UPDATE에 의해서도 갱신된 열에 대한 갱신이 제거된 채로 재기반화된 changeset에 복사돼요. 이것이 갱신될 열이 없다는 것을 의미하면 변경은 생략돼요.

local 변경은 여러 remote 변경에 대해 동시에 재기반화될 수 있어요. 단일 키가 여러 remote changeset에 의해 수정되면, local changeset이 재기반화되기 전에 다음과 같이 결합돼요.

  • 키에 하나 이상의 REPLACE 해결이 있었다면 REPLACE에 따라 재기반화돼요.
  • 키에 REPLACE 해결이 없었다면, local changeset은 OMIT 해결 중 가장 최근의 것에 따라 재기반화돼요.

여러 remote changeset의 충돌 해결은 행 단위가 아니라 필드 단위로 결합된다는 점에 주의하세요. 이것은 여러 remote UPDATE 연산의 경우, 단일 local 변경의 일부 필드는 REPLACE로, 다른 필드는 OMIT로 재기반화될 수 있음을 의미해요.

local changeset을 재기반화하려면, 먼저 sqlite3changeset_apply_v2()를 사용해 remote changeset을 local 데이터베이스에 적용하고 재기반 정보 버퍼를 캡처해야 해요. 그런 다음:

  1. sqlite3rebaser_create()를 호출해 sqlite3_rebaser 객체를 만들어요.
  2. sqlite3rebaser_configure()를 호출해 sqlite3changeset_apply_v2()에서 얻은 재기반 버퍼로 새 객체를 구성해요. local changeset이 여러 remote changeset에 대해 재기반화되어야 한다면, 여러 sqlite3changeset_apply_v2() 호출이 이루어진 것과 같은 순서로 sqlite3rebaser_configure()를 여러 번 호출해야 해요.
  3. sqlite3rebaser_rebase()를 호출해 각 local changeset을 재기반화해요.
  4. sqlite3rebaser_delete()를 호출해 sqlite3_rebaser 객체를 삭제해요.

세션 객체 핸들 (Session Object Handle)

typedef struct sqlite3_session sqlite3_session;

이 객체의 인스턴스는 데이터베이스에 대한 변경을 기록하는 데 사용할 수 있는 세션이에요.

생성자: sqlite3session_create()

소멸자: sqlite3session_delete()

Changeset을 변경 그룹에 추가 (Add A Changeset To A Changegroup)

int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);

버퍼 pData(크기 nData 바이트)의 changeset(또는 patchset) 내의 모든 변경을 변경 그룹에 추가해요.

버퍼가 patchset을 포함하면, 같은 변경 그룹 객체에 대한 이 함수의 모든 이전 호출도 patchset을 지정했어야 해요. 또는 버퍼가 changeset을 포함하면 이전 호출도 changeset을 지정했어야 해요. 그렇지 않으면 SQLITE_ERROR가 반환되고 변경이 변경 그룹에 추가되지 않아요.

changeset과 변경 그룹 내의 행은 PRIMARY KEY 열의 값으로 식별돼요. 두 행이 같은 기본 키를 가지면, changeset의 변경은 변경 그룹에 이미 존재하는 변경과 같은 행에 적용되는 것으로 간주돼요.

변경 그룹에 아직 나타나지 않는 행에 대한 변경은 단순히 그것에 복사돼요. 또는 새 changeset과 변경 그룹 둘 다 단일 행에 적용되는 변경을 포함하면, 변경 그룹의 최종 내용은 각 변경 유형에 따라 다음과 같이 달라져요.

  기존 변경      새 변경      출력 변경
  INSERT        INSERT       새 변경은 무시돼요. 이 경우는 새 changeset이 변경 그룹에 이미 추가된 changeset 직후에 기록된 경우에는 발생하지 않아요.
  INSERT        UPDATE       INSERT 변경이 변경 그룹에 남아요. INSERT 변경의 값은 행이 기존 변경에 의해 삽입된 다음 새 변경에 따라 갱신된 것처럼 수정돼요.
  INSERT        DELETE       기존 INSERT가 변경 그룹에서 제거돼요. DELETE는 추가되지 않아요.
  UPDATE        INSERT       새 변경은 무시돼요. (위 참고)
  UPDATE        UPDATE       기존 UPDATE가 변경 그룹 내에 남아요. 행이 기존 변경에 의해 한 번, 새 변경에 의해 다시 한 번 갱신된 것처럼 수반되는 값이 수정돼요.
  UPDATE        DELETE       기존 UPDATE가 변경 그룹 내에서 새 DELETE로 대체돼요.
  DELETE        INSERT       새 변경이 삽입한 행의 열 값 중 하나 이상이 기존 변경이 삭제한 행의 값과 다르면, 기존 DELETE가 변경 그룹 내에서 UPDATE로 대체돼요. 그렇지 않으면 삽입된 행이 삭제된 행과 정확히 같을 때 기존 DELETE는 단순히 버려져요.
  DELETE        UPDATE       새 변경은 무시돼요. (위 참고)
  DELETE        DELETE       새 변경은 무시돼요. (위 참고)

새 changeset이 변경 그룹에 이미 존재하는 테이블에 대한 변경을 포함하면, 그 테이블의 열 수와 기본 키 열의 위치는 일관되어야 해요. 그렇지 않으면 이 함수는 SQLITE_SCHEMA로 실패해요. 단, 변경 그룹 객체가 sqlite3changegroup_schema() API를 사용해 데이터베이스 스키마로 구성됐다면, 단일 테이블에 대해 다른 열 수의 changeset을 결합하는 것이 가능해요. (그 외에는 호환 가능하다면요.)

입력 changeset이 손상된 것으로 보이고 손상이 감지되면 SQLITE_CORRUPT가 반환돼요. 또는 처리 중 메모리 부족(out-of-memory) 상태가 발생하면 이 함수는 SQLITE_NOMEM을 반환해요.

모든 경우에, 오류가 발생하면 변경 그룹 최종 내용의 상태는 정의되지 않아요. 오류가 발생하지 않으면 SQLITE_OK가 반환돼요.

Changeset을 변경 그룹에 단일 추가 (Add A Single Change To A Changegroup)

int sqlite3changegroup_add_change(
  sqlite3_changegroup*,
  sqlite3_changeset_iter*
);

이 함수는 두 번째 인수로 전달된 반복자가 현재 가리키는 단일 변경을 변경 그룹 객체에 추가해요. 변경 추가 규칙은 sqlite3changegroup_add()에 대해 설명된 것과 같아요.

변경이 변경 그룹에 성공적으로 추가되면 SQLITE_OK가 반환돼요. 그렇지 않으면 SQLite 오류 코드가 반환돼요.

이 함수가 호출될 때 반복자는 유효한 항목을 가리켜야 해요. 그렇지 않으면 SQLITE_ERROR가 반환되고 변경이 변경 그룹에 추가되지 않아요. 또한 반복자는 SQLITE_CHANGESETAPPLY_INVERT 플래그로 열렸으면 안 돼요. 이 경우에도 SQLITE_ERROR가 반환돼요.

Changeset을 변경 그룹에 하나씩 추가 시작 (Begin adding a change to a changegroup)

int sqlite3changegroup_change_begin(
  sqlite3_changegroup*,
  int eOp,
  const char *zTab,
  int bIndirect,
  char **pzErr
);

이 API는 다른 sqlite3changegroup_change_xxx() API와 함께 사용되어 변경을 변경 그룹 객체에 하나씩 추가해요. 단일 변경을 추가하려면 호출자는 다음을 해야 해요.

  1. sqlite3changegroup_change_begin()을 호출해 변경 유형(INSERT, UPDATE 또는 DELETE), 영향받는 테이블, 그리고 변경이 간접(indirect)으로 표시되어야 하는지 여부를 나타내요.
  2. sqlite3changegroup_change_int64() 또는 다른 네 개의 값 함수(_null(), _double(), _text() 또는 _blob()) 중 하나를 한 번 이상 호출해 구성 중인 변경의 old.* 및 new.* 값을 지정해요.
  3. sqlite3changegroup_change_finish()를 호출해 그룹에 변경 추가를 끝내거나 변경을 완전히 버려요.

이 함수의 첫 번째 인수는 변경이 추가될 기존 변경 그룹 객체에 대한 포인터여야 해요. 두 번째 인수는 SQLITE_INSERT, SQLITE_UPDATE 또는 SQLITE_DELETE여야 해요. 세 번째는 변경이 영향주는 테이블의 이름이고, 네 번째는 변경이 "간접"(bIndirect가 0이 아닌 경우)으로 표시되어야 하는지 아니면 간접이 아닌(bIndirect가 0인 경우) 것으로 표시되어야 하는지를 지정하는 부울 플래그예요.

이 함수에 대한 성공적인 호출 후에는, sqlite3changegroup_change_finish()가 호출될 때까지 같은 변경 그룹 객체에서 이 함수를 다시 호출할 수 없어요. 그렇게 하는 것은 SQLITE_MISUSE 오류예요.

첫 번째 인수로 전달된 변경 그룹 객체는 지정된 테이블에 대한 스키마 데이터로 이미 구성되어야 해요. 테이블을 포함하는 데이터베이스로 sqlite3changegroup_schema()를 호출하거나, 테이블을 포함하는 changeset으로 sqlite3changegroup_add()를 호출해 구성할 수 있어요. 이 함수가 호출될 때 변경 그룹 객체가 지정된 테이블에 대한 스키마로 구성되지 않았다면 SQLITE_ERROR가 반환돼요.

성공하면 SQLITE_OK가 반환돼요. 그렇지 않으면 오류가 발생하면 SQLite 오류 코드가 반환돼요. 이 경우 인수 pzErr가 NULL이 아니면 (*pzErr)는 utf-8 형식, nul 종료, 영어 오류 메시지를 포함하는 버퍼를 가리키도록 설정될 수 있어요. 호출자가 나중에 sqlite3_free()를 사용해 이 버퍼를 해제할 책임이 있어요.

Changeset을 변경 그룹에 blob 추가 (Add a blob to a changegroup)

int sqlite3changegroup_change_blob(
    sqlite3_changegroup*, int, int, const void *pVal, int nVal
);

이 함수는 sqlite3changegroup_change_int64()와 유사해요. 64비트 정수 대신 blob 값으로 현재 누적된 변경을 구성해요. 매개변수 pVal은 blob을 포함하는 버퍼를 가리켜요. 매개변수 nVal은 blob의 크기(바이트)예요.

Changeset을 변경 그룹에 double 추가 (Add an double to a changegroup)

int sqlite3changegroup_change_double(sqlite3_changegroup*, int, int, double);

이 함수는 sqlite3changegroup_change_int64()와 유사해요. 단, 64비트 정수 대신 실수 값으로 현재 구성 중인 변경을 구성해요.

Changeset을 변경 그룹에 하나씩 추가 끝내기 (Finish adding one-at-at-time changes to a changegroup)

int sqlite3changegroup_change_finish(
  sqlite3_changegroup*,
  int bDiscard,
  char **pzErr
);

이 함수는 sqlite3changegroup_change_begin()에 대한 성공적인 호출 후에만 호출될 수 있어요. 그렇지 않으면 SQLITE_MISUSE 오류예요.

매개변수 bDiscard가 0이 아니면 현재 변경은 단순히 버려져요. 이 경우 이 함수는 항상 성공하고 SQLITE_OK가 반환돼요.

매개변수 bDiscard가 0이면 현재 변경을 변경 그룹에 추가하려는 시도가 이루어져요. 변경 그룹이 changeset(patchset 아님)을 생성하도록 구성됐다고 가정하면 다음이 필요해요.

  • 변경이 INSERT 또는 DELETE면, 각각 new.* 또는 old.* 레코드의 모든 열에 대해 값이 지정되어야 해요.
  • 변경이 UPDATE 레코드면, old.* 레코드의 PRIMARY KEY 열에 대해 값이 제공되어야 하지만 new.* 레코드의 PRIMARY KEY 열에 대해서는 제공되어서는 안 돼요.
  • 변경이 UPDATE 레코드면, old.* 레코드에서 값이 제공된 각 비-PRIMARY KEY 열에 대해 new.* 레코드의 같은 열에도 값이 제공되어야 해요. 마찬가지로, old.* 레코드에서 값이 제공되지 않은 각 비-PK 열에 대해 new.* 레코드의 같은 열에도 값이 제공되지 않아야 해요.
  • PRIMARY KEY 열에 대해 지정된 모든 값은 NULL이 아니어야 해요.

그렇지 않으면 오류예요.

변경 그룹이 이미 같은 행(PRIMARY KEY 열로 식별)에 대한 변경을 포함하면, 현재 변경은 sqlite3changegroup_add()에서와 같은 방식으로 기존 변경과 결합돼요.

patchset의 경우 위의 모든 규칙이 적용되지만, UPDATE 또는 DELETE 변경에 대해 비-PK old.* 레코드 열에 값이 제공되는지 여부는 중요하지 않아요. 이것은 sqlite3changegroup_change_xxx() API를 사용해 changeset을 생성하는 데 사용된 코드가 patchset을 생성하는 데도 사용될 수 있음을 의미해요.

호출이 성공하면 SQLITE_OK가 반환돼요. 그렇지 않으면 오류가 발생하면 SQLite 오류 코드가 반환돼요. 오류가 반환되고 매개변수 pzErr가 NULL이 아니면 (*pzErr)는 nul 종료, utf-8 인코딩, 영어 오류 메시지를 포함하는 버퍼를 가리키도록 설정될 수 있어요. 호출자가 나중에 sqlite3_free()를 사용해 그런 오류 메시지 버퍼를 해제할 책임이 있어요.

Changeset을 변경 그룹에 64비트 정수 추가 (Add a 64-bit integer to a changegroup)

int sqlite3changegroup_change_int64(
  sqlite3_changegroup*,
  int bNew,
  int iCol,
  sqlite3_int64 iVal
);

이 함수는 sqlite3changegroup_change_begin()에 대한 성공적인 호출과 그에 상응하는 sqlite3changegroup_change_finish() 호출 사이에서만 호출될 수 있어요. 다른 시간에 호출하면 SQLITE_MISUSE 오류예요. 이 함수를 호출하면 첫 번째 인수로 전달된 변경 그룹 객체에 현재 추가 중인 변경에 사용될 64비트 정수 값을 지정해요.

두 번째 매개변수 bNew는 그 값이 구성 중인 변경의 new.(bNew가 0이 아닌 경우) 또는 old.(bNew가 0인 경우) 레코드의 일부가 될지 지정해요. 이것이 앞선 sqlite3changegroup_change_begin() 호출이 지정한 변경 유형과 일치하지 않으면(즉 SQLITE_INSERT 변경에 대한 old.* 값, 또는 SQLITE_DELETE에 대한 new.* 값) SQLITE_ERROR가 반환돼요.

세 번째 매개변수는 그 값이 일부가 될 old.* 또는 new.* 레코드의 열을 지정해요. 지정된 테이블에 명시적 기본 키가 있으면 이것은 CREATE TABLE 문 내에 지정된 순서로 0부터 번호가 매겨진 테이블 열의 인덱스예요. 또는 테이블이 암시적 rowid 키를 사용하면 열 0은 rowid이고 명시적 열은 1부터 번호가 매겨져요. iCol 매개변수가 0보다 작거나 테이블의 마지막 열 인덱스보다 크면 SQLITE_RANGE가 반환돼요.

네 번째 매개변수는 old.* 또는 new.* 레코드의 일부로 사용할 정수 값이에요.

이 호출이 성공하면 SQLITE_OK가 반환돼요. 그렇지 않으면 오류가 발생하면 SQLite 오류 코드가 반환돼요.

Changeset을 변경 그룹에 NULL 추가 (Add a NULL to a changegroup)

int sqlite3changegroup_change_null(sqlite3_changegroup*, int, int);

이 함수는 sqlite3changegroup_change_int64()와 유사해요. 단, 64비트 정수 대신 NULL 값으로 현재 구성 중인 변경을 구성해요.

Changeset을 변경 그룹에 텍스트 값 추가 (Add a text value to a changegroup)

int sqlite3changegroup_change_text(
  sqlite3_changegroup*, int, int, const char *pVal, int nVal
);

이 함수는 sqlite3changegroup_change_int64()와 유사해요. 64비트 정수 대신 텍스트 값으로 현재 누적된 변경을 구성해요. 매개변수 pVal은 utf-8로 인코딩된 텍스트를 포함하는 버퍼를 가리켜요. 매개변수 nVal은 텍스트 값의 크기(바이트)이거나 음수일 수 있으며, 그 경우 pVal이 가리키는 버퍼는 nul 종료로 가정돼요.

변경 그룹 객체 구성 (Configure a changegroup object)

int sqlite3changegroup_config(sqlite3_changegroup*, int, void *pArg);

첫 번째 인수로 전달된 변경 그룹 객체를 구성해요. 현재 두 번째 매개변수의 유일한 유효한 값은 SQLITE_CHANGEGROUP_CONFIG_PATCHSET예요.

변경 그룹 객체 삭제 (Delete A Changegroup Object)

void sqlite3changegroup_delete(sqlite3_changegroup*);

새 변경 그룹 객체 생성 (Create A New Changegroup Object)

int sqlite3changegroup_new(sqlite3_changegroup **pp);

sqlite3_changegroup 객체는 둘 이상의 changeset(또는 patchset)을 단일 changeset(또는 patchset)으로 결합하는 데 사용돼요. 단일 변경 그룹 객체는 changeset 또는 patchset을 결합할 수 있지만 둘 다는 아니에요. 출력은 항상 입력과 같은 형식이에요.

성공하면 이 함수는 SQLITE_OK를 반환하고 반환하기 전에 (*pp)에 새 sqlite3_changegroup 객체에 대한 포인터를 채워요. 호출자는 나중에 sqlite3changegroup_delete() 호출로 반환된 객체를 해제해야 해요. 오류가 발생하면 SQLite 오류 코드(예: SQLITE_NOMEM)가 반환되고 *pp는 NULL로 설정돼요.

sqlite3_changegroup 객체의 일반적인 사용 패턴은 다음과 같아요.

  • sqlite3changegroup_new() 호출로 생성돼요.
  • sqlite3changegroup_add()를 호출해 0개 이상의 changeset(또는 patchset)을 객체에 추가해요.
  • sqlite3changegroup_output() 호출을 통해 모든 입력 changeset을 결합한 결과를 애플리케이션이 얻어요.
  • sqlite3changegroup_delete() 호출로 객체를 삭제해요.

new()와 delete() 호출 사이에 add()와 output()에 대한 호출을 몇 번이든 어떤 순서로든 할 수 있어요.

일반 sqlite3changegroup_add()와 sqlite3changegroup_output() 함수뿐 아니라 스트리밍 버전인 sqlite3changegroup_add_strm()와 sqlite3changegroup_output_strm()도 사용할 수 있어요.

변경 그룹에서 복합 changeset 얻기 (Obtain A Composite Changeset From A Changegroup)

int sqlite3changegroup_output(
  sqlite3_changegroup*,
  int *pnData,                    /* OUT: Size of output buffer in bytes */
  void **ppData                   /* OUT: Pointer to output buffer */
);

변경 그룹의 현재 내용을 나타내는 changeset(또는 patchset)을 포함하는 버퍼를 얻어요. 변경 그룹의 입력이 그 자체로 changeset이면 출력은 changeset이에요. 또는 입력이 patchset이면 출력도 patchset이에요.

sqlite3session_changeset() 및 sqlite3session_patchset() 함수의 출력과 마찬가지로, 단일 테이블과 관련된 모든 변경은 이 함수의 출력에서 함께 그룹화돼요. 테이블은 변경 그룹에 추가된 최초 changeset과 같은 순서로 나타나요. 변경 그룹에 추가된 두 번째 또는 이후 changeset이 첫 changeset에 나타나지 않는 테이블에 대한 변경을 포함하면, 그것들은 출력 changeset의 끝에 처음 발견된 순서대로 추가돼요.

오류가 발생하면 SQLite 오류 코드가 반환되고 출력 변수(*pnData)와 (*ppData)는 0으로 설정돼요. 그렇지 않으면 SQLITE_OK가 반환되고 출력 변수는 각각 출력 버퍼의 크기와 포인터로 설정돼요. 이 경우 호출자가 나중에 sqlite3_free() 호출로 버퍼를 해제할 책임이 있어요.

변경 그룹에 스키마 추가 (Add a Schema to a Changegroup)

int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);

이 메서드는 변경 그룹 핸들에 추가된 changeset이 데이터베이스 zDb("main", "temp" 또는 첨부된 데이터베이스의 이름)의 스키마와 일치해야 한다는 규칙을 선택적으로 강제하는 데 사용될 수 있어요. 구성된 스키마와 호환되지 않는 changeset을 추가하려고 sqlite3changegroup_add()가 호출되면 SQLITE_SCHEMA가 반환되고 변경 그룹 객체는 정의되지 않은 상태로 남아요.

changeset 스키마는 sqlite3changeset_apply()에서와 같은 방식으로 데이터베이스 스키마와 호환되는 것으로 간주돼요. 구체적으로, changeset의 각 테이블에 대해 다음을 가진 데이터베이스 테이블이 존재해요.

  • changeset이 식별한 이름, 그리고
  • changeset에 기록된 것 이상의 충분한 열 수, 그리고
  • changeset에 기록된 것과 같은 위치의 기본 키 열

변경 그룹 객체의 출력은 항상 이 함수로 지명된 데이터베이스와 같은 스키마를 가져요. sqlite3changegroup_add()에 전달된 changeset이 데이터베이스 스키마의 해당 테이블보다 더 적은 열을 가진 경우, 그것들은 데이터베이스 스키마의 기본 열 값으로 채워져요. 이것은 그 외에는 호환된다면 단일 테이블에 대해 다른 열 수를 가진 changeset을 변경 그룹 내에서 결합할 수 있게 해줘요.

두 Changeset 객체 연결 (Concatenate Two Changeset Objects)

int sqlite3changeset_concat(
  int nA,                         /* Number of bytes in buffer pA */
  void *pA,                       /* Pointer to buffer containing changeset A */
  int nB,                         /* Number of bytes in buffer pB */
  void *pB,                       /* Pointer to buffer containing changeset B */
  int *pnOut,                     /* OUT: Number of bytes in output changeset */
  void **ppOut                    /* OUT: Buffer containing output changeset */
);

이 함수는 두 changeset A와 B를 단일 changeset으로 연결하는 데 사용돼요. 결과는 changeset A를 적용한 다음 changeset B를 적용한 것과 동등한 changeset이에요.

이 함수는 sqlite3_changegroup 객체를 사용해 두 입력 changeset을 결합해요. 호출하면 다음 코드 조각과 유사한 결과를 만들어요.

  sqlite3_changegroup *pGrp;
  rc = sqlite3changegroup_new(&pGrp);
  if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
  if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
  if( rc==SQLITE_OK ){
    rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
  }else{
    *ppOut = 0;
    *pnOut = 0;
  }

자세한 내용은 아래 sqlite3_changegroup 문서를 참조하세요.

Changeset 반복자에서 충돌하는 행 값 얻기 (Obtain Conflicting Row Values From A Changeset Iterator)

int sqlite3changeset_conflict(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int iVal,                       /* Column number */
  sqlite3_value **ppValue         /* OUT: Value from conflicting row */
);

이 함수는 sqlite3changeset_apply()SQLITE_CHANGESET_DATA 또는 SQLITE_CHANGESET_CONFLICT로 충돌 처리기 콜백에 전달한 반복자 객체에만 사용해야 해요. 다른 반복자에서 이 함수를 호출하면 SQLITE_MISUSE가 반환되고 *ppValue는 NULL로 설정돼요.

인수 iVal은 0보다 크거나 같아야 하고, 현재 변경이 영향주는 테이블의 열 수보다 작아야 해요. 그렇지 않으면 SQLITE_RANGE가 반환되고 *ppValue는 NULL로 설정돼요.

성공하면 이 함수는 *ppValue를 현재 충돌 처리기 콜백과 연관된 "충돌하는 행"의 iVal 번째 값을 포함하는 보호된(protected) sqlite3_value 객체를 가리키도록 설정하고 SQLITE_OK를 반환해요.

다른 오류(예: OOM 상태)가 발생하면 SQLite 오류 코드가 반환되고 *ppValue는 NULL로 설정돼요.

Changeset 반복자 종료 (Finalize A Changeset Iterator)

int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);

이 함수는 sqlite3changeset_start()로 할당된 반복자를 종료하는 데 사용돼요.

이 함수는 sqlite3changeset_start() 함수로 만든 반복자에만 호출해야 해요. 애플리케이션이 sqlite3changeset_apply()가 충돌 처리기에 전달한 반복자로 이 함수를 호출하면 SQLITE_MISUSE가 즉시 반환되고 호출은 효과가 없어요.

sqlite3changeset_xxx() 함수 호출 내에서 오류가 발생했다면(예: sqlite3changeset_next()SQLITE_CORRUPT 또는 sqlite3changeset_new()SQLITE_NOMEM) 이 함수는 그 오류에 해당하는 오류 코드를 반환해요. 그렇지 않으면 SQLITE_OK가 반환돼요. 이것은 다음 패턴(의사 코드)을 허용하기 위한 것이에요.

  sqlite3changeset_start(&pIter, nChangeset, pChangeset);
  while( SQLITE_ROW==sqlite3changeset_next(pIter) ){
    // Do something with change.
  }
  rc = sqlite3changeset_finalize(pIter);
  if( rc!=SQLITE_OK ){
    // An error has occurred
  }

외래 키 제약 위반 수 결정 (Determine The Number Of Foreign Key Constraint Violations)

int sqlite3changeset_fk_conflicts(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int *pnOut                      /* OUT: Number of FK violations */
);

이 함수는 SQLITE_CHANGESET_FOREIGN_KEY 충돌 처리기 콜백에 전달된 반복자로만 호출할 수 있어요. 이 경우 대상 데이터베이스의 알려진 총 외래 키 위반 수로 출력 변수를 설정하고 SQLITE_OK를 반환해요.

다른 모든 경우에 이 함수는 SQLITE_MISUSE를 반환해요.

Changeset 뒤집기 (Invert A Changeset)

int sqlite3changeset_invert(
  int nIn, const void *pIn,       /* Input changeset */
  int *pnOut, void **ppOut        /* OUT: Inverse of input */
);

이 함수는 changeset 객체를 "뒤집는" 데 사용돼요. 뒤집힌 changeset을 데이터베이스에 적용하면 뒤집히지 않은 changeset을 적용한 효과가 반대가 돼요. 구체적으로:

  • 각 DELETE 변경은 INSERT로 바뀌고,
  • 각 INSERT 변경은 DELETE로 바뀌고,
  • 각 UPDATE 변경에 대해 old.와 new. 값이 교환돼요.

이 함수는 changeset 내에서 변경이 나타나는 순서를 바꾸지 않아요. 단지 각 개별 변경의 의미를 반대로 할 뿐이에요.

성공하면 뒤집힌 changeset을 포함하는 버퍼에 대한 포인터가 *ppOut에 저장되고, 같은 버퍼의 크기가 *pnOut에 저장되며, SQLITE_OK가 반환돼요. 오류가 발생하면 *pnOut와 *ppOut 모두 0으로 설정되고 SQLite 오류 코드가 반환돼요.

이 함수에 대한 성공적인 호출 후 버퍼 할당을 해제하기 위해 호출자가 나중에 *ppOut 포인터에 대해 sqlite3_free()를 호출할 책임이 있어요.

경고/TODO: 이 함수는 현재 입력이 유효한 changeset이라고 가정해요. 그렇지 않으면 결과가 정의되지 않아요.

Changeset 반복자에서 new.* 값 얻기 (Obtain new.* Values From A Changeset Iterator)

int sqlite3changeset_new(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int iVal,                       /* Column number */
  sqlite3_value **ppValue         /* OUT: New value (or NULL pointer) */
);

이 함수에 전달된 pIter 인수는 sqlite3changeset_apply()가 충돌 처리기에 전달한 반복자이거나 sqlite3changeset_start()로 만든 반복자일 수 있어요. 후자의 경우 sqlite3changeset_next()에 대한 가장 최근 호출이 SQLITE_ROW를 반환했어야 해요. 또한 반복자가 현재 가리키는 변경 유형이 SQLITE_UPDATE 또는 SQLITE_INSERT인 경우에만 호출할 수 있어요. 그렇지 않으면 이 함수는 SQLITE_MISUSE를 반환하고 *ppValue를 NULL로 설정해요.

인수 iVal은 0보다 크거나 같아야 하고, 현재 변경이 영향주는 테이블의 열 수보다 작아야 해요. 그렇지 않으면 SQLITE_RANGE가 반환되고 *ppValue는 NULL로 설정돼요.

성공하면 이 함수는 *ppValue를 UPDATE 또는 INSERT 변경의 일부로 저장된 새 행 값 벡터의 iVal 번째 값을 포함하는 보호된 sqlite3_value 객체를 가리키도록 설정하고 SQLITE_OK를 반환해요. 변경이 UPDATE이고 요청된 열에 대한 새 값을 포함하지 않으면 ppValue는 NULL로 설정되고 SQLITE_OK가 반환돼요. 함수 이름은 이것이 update나 delete 트리거에 사용할 수 있는 "new." 열과 유사하기 때문에 붙은 것이에요.

다른 오류가 발생하면 SQLite 오류 코드가 반환되고 *ppValue는 NULL로 설정돼요.

Changeset 반복자 전진 (Advance A Changeset Iterator)

int sqlite3changeset_next(sqlite3_changeset_iter *pIter);

이 함수는 sqlite3changeset_start() 함수로 만든 반복자에만 사용할 수 있어요. sqlite3changeset_apply()가 충돌 처리기 콜백에 전달한 반복자에서 호출되면 SQLITE_MISUSE가 반환되고 호출은 효과가 없어요.

sqlite3changeset_start()로 반복자가 만들어진 직후에는 changeset의 어떤 변경도 가리키지 않아요. changeset이 비어 있지 않다고 가정하면 이 함수에 대한 첫 호출은 반복자를 changeset의 첫 변경을 가리키도록 전진시켜요. 각 후속 호출은 반복자를 changeset의 다음 변경(있다면)을 가리키도록 전진시켜요. 오류가 발생하지 않고 sqlite3changeset_next()가 반복자를 전진시킨 후 반복자가 유효한 변경을 가리키면 SQLITE_ROW가 반환돼요. 그렇지 않으면 changeset의 모든 변경이 이미 방문됐다면 SQLITE_DONE이 반환돼요.

오류가 발생하면 SQLite 오류 코드가 반환돼요. 가능한 오류 코드에는 SQLITE_CORRUPT(changeset 버퍼가 손상된 경우) 또는 SQLITE_NOMEM이 포함돼요.

Changeset 반복자에서 old.* 값 얻기 (Obtain old.* Values From A Changeset Iterator)

int sqlite3changeset_old(
  sqlite3_changeset_iter *pIter,  /* Changeset iterator */
  int iVal,                       /* Column number */
  sqlite3_value **ppValue         /* OUT: Old value (or NULL pointer) */
);

이 함수에 전달된 pIter 인수는 sqlite3changeset_apply()가 충돌 처리기에 전달한 반복자이거나 sqlite3changeset_start()로 만든 반복자일 수 있어요. 후자의 경우 sqlite3changeset_next()에 대한 가장 최근 호출이 SQLITE_ROW를 반환했어야 해요. 또한 반복자가 현재 가리키는 변경 유형이 SQLITE_DELETE 또는 SQLITE_UPDATE인 경우에만 호출할 수 있어요. 그렇지 않으면 이 함수는 SQLITE_MISUSE를 반환하고 *ppValue를 NULL로 설정해요.

인수 iVal은 0보다 크거나 같아야 하고, 현재 변경이 영향주는 테이블의 열 수보다 작아야 해요. 그렇지 않으면 SQLITE_RANGE가 반환되고 *ppValue는 NULL로 설정돼요.

성공하면 이 함수는 ppValue를 UPDATE 또는 DELETE 변경의 일부로 저장된 원래 행 값 벡터의 iVal 번째 값을 포함하는 보호된 sqlite3_value 객체를 가리키도록 설정하고 SQLITE_OK를 반환해요. 함수 이름은 이것이 update나 delete 트리거에 사용할 수 있는 "old." 열과 유사하기 때문에 붙은 것이에요.

다른 오류가 발생하면 SQLite 오류 코드가 반환되고 *ppValue는 NULL로 설정돼요.

Changeset 반복자에서 현재 연산 얻기 (Obtain The Current Operation From A Changeset Iterator)

int sqlite3changeset_op(
  sqlite3_changeset_iter *pIter,  /* Iterator object */
  const char **pzTab,             /* OUT: Pointer to table name */
  int *pnCol,                     /* OUT: Number of columns in table */
  int *pOp,                       /* OUT: SQLITE_INSERT, DELETE or UPDATE */
  int *pbIndirect                 /* OUT: True for an 'indirect' change */
);

이 함수에 전달된 pIter 인수는 sqlite3changeset_apply()가 충돌 처리기에 전달한 반복자이거나 sqlite3changeset_start()로 만든 반복자일 수 있어요. 후자의 경우 sqlite3changeset_next()에 대한 가장 최근 호출이 SQLITE_ROW를 반환했어야 해요. 그렇지 않으면 이 함수는 SQLITE_MISUSE를 반환해요.

인수 pOp, pnCol, pzTab은 NULL일 수 없어요. 반환 시 이 포인터들을 통해 세 개의 출력이 설정돼요.

*pOp는 반복자가 현재 가리키는 변경 유형에 따라 SQLITE_INSERT, SQLITE_DELETE 또는 SQLITE_UPDATE 중 하나로 설정돼요;

*pnCol은 변경이 영향주는 테이블의 열 수로 설정돼요; 그리고

*pzTab은 현재 변경이 영향주는 테이블 이름을 포함하는 nul 종료 utf-8 인코딩 문자열을 가리키도록 설정돼요. 반복자에서 sqlite3changeset_next()가 호출되거나 충돌 처리기 함수가 반환할 때까지 버퍼는 유효하게 유지돼요.

pbIndirect가 NULL이 아니면 변경이 간접 변경이면 *pbIndirect는 true(1)로, 그렇지 않으면 false(0)로 설정돼요. 직접 및 간접 변경에 대한 설명은 sqlite3session_indirect() 문서를 참조하세요.

오류가 발생하지 않으면 SQLITE_OK가 반환돼요. 오류가 발생하면 SQLite 오류 코드가 반환돼요. 이 경우 출력 변수의 값은 믿을 수 없을 수 있어요.

테이블의 기본 키 정의 얻기 (Obtain The Primary Key Definition Of A Table)

int sqlite3changeset_pk(
  sqlite3_changeset_iter *pIter,  /* Iterator object */
  unsigned char **pabPK,          /* OUT: Array of boolean - true for PK cols */
  int *pnCol                      /* OUT: Number of entries in output array */
);

각 수정된 테이블에 대해 changeset은 다음을 포함해요.

  • 테이블의 열 수, 그리고
  • 그 중 어떤 열이 테이블의 PRIMARY KEY를 구성하는지.

이 함수는 반복자 pIter가 현재 가리키는 변경이 수정한 테이블의 PRIMARY KEY를 구성하는 열을 찾는 데 사용돼요. 성공하면 *pabPK는 nCol개 항목(nCol은 테이블의 열 수)의 배열을 가리키도록 설정돼요. *pabPK의 요소는 해당 열이 테이블의 기본 키의 일부이면 0x01로, 아니면 0x00으로 설정돼요.

인수 pnCol이 NULL이 아니면 *pnCol은 테이블의 열 수로 설정돼요.

반복자가 유효한 항목을 가리키지 않을 때 이 함수가 호출되면 SQLITE_MISUSE가 반환되고 출력 변수가 0으로 설정돼요. 그렇지 않으면 SQLITE_OK가 반환되고 출력 변수가 위에서 설명한 대로 채워져요.

changeset 재기반화 객체 구성 (Configure a changeset rebaser object.)

int sqlite3rebaser_configure(
  sqlite3_rebaser*,
  int nRebase, const void *pRebase
);

중요: 이 인터페이스는 실험적이며 예고 없이 변경될 수 있어요.

sqlite3changeset_apply_v2()에 대한 이전 호출에서 얻어야 하는 버퍼 pRebase(크기 nRebase 바이트)가 설명하는 충돌 해결에 따라 changeset을 재기반화하도록 changeset 재기반화 객체를 구성해요.

changeset 재기반화 객체 생성 (Create a changeset rebaser object.)

int sqlite3rebaser_create(sqlite3_rebaser **ppNew);

중요: 이 인터페이스는 실험적이며 예고 없이 변경될 수 있어요.

새 changeset 재기반화 객체를 할당해요. 성공하면 (*ppNew)를 새 객체를 가리키도록 설정하고 SQLITE_OK를 반환해요. 그렇지 않으면 오류가 발생하면 SQLite 오류 코드(예: SQLITE_NOMEM)를 반환하고 (*ppNew)를 NULL로 설정해요.

changeset 재기반화 객체 삭제 (Delete a changeset rebaser object.)

void sqlite3rebaser_delete(sqlite3_rebaser *p);

중요: 이 인터페이스는 실험적이며 예고 없이 변경될 수 있어요.

changeset 재기반화 객체와 모든 관련 리소스를 삭제해요. sqlite3rebaser_create()의 각 성공적인 호출에 대해 이 함수를 한 번 호출해야 해요.

changeset 재기반화 (Rebase a changeset)

int sqlite3rebaser_rebase(
  sqlite3_rebaser*,
  int nIn, const void *pIn,
  int *pnOut, void **ppOut
);

중요: 이 인터페이스는 실험적이며 예고 없이 변경될 수 있어요.

인수 pIn은 크기가 nIn 바이트인 changeset을 포함하는 버퍼를 가리켜야 해요. 이 함수는 첫 번째 인수로 전달된 재기반화 객체의 구성에 따라 재기반화된 changeset의 복사본으로 버퍼를 할당하고 채워요. 성공하면 (*ppOut)은 재기반화된 changeset을 포함하는 새 버퍼를 가리키도록, (*pnOut)은 그것의 크기(바이트)로 설정되고 SQLITE_OK가 반환돼요. 호출자가 나중에 sqlite3_free()를 사용해 새 버퍼를 해제할 책임이 있어요. 그렇지 않으면 오류가 발생하면 (*ppOut)과 (*pnOut)은 0으로 설정되고 SQLite 오류 코드가 반환돼요.

세션 객체에 테이블 연결 (Attach A Table To A Session Object)

int sqlite3session_attach(
  sqlite3_session *pSession,      /* Session object */
  const char *zTab                /* Table name */
);

인수 zTab이 NULL이 아니면 첫 번째 인수로 전달된 세션 객체에 연결할 테이블의 이름이에요. 세션 객체가 활성화되어 있는 동안 테이블에 이루어진 모든 이후 변경이 기록돼요. 자세한 내용은 sqlite3session_changeset() 문서를 참조하세요.

또는 인수 zTab이 NULL이면 데이터베이스의 모든 테이블에 대한 변경이 기록돼요. 이 호출이 이루어진 후 데이터베이스에 추가 테이블이 추가되면("CREATE TABLE" 문 실행으로) 새 테이블에 대한 변경도 기록돼요.

변경은 CREATE TABLE 문의 일부로 명시적으로 정의된 PRIMARY KEY를 가진 테이블에 대해서만 기록될 수 있어요. PRIMARY KEY가 "INTEGER PRIMARY KEY"(rowid 별칭)인지 여부는 중요하지 않아요. PRIMARY KEY는 단일 열로 구성되거나 복합 키일 수 있어요.

지정된 테이블이 데이터베이스에 존재하지 않거나 PRIMARY KEY를 가지지 않아도 오류는 아니에요. 하지만 두 시나리오 모두에서 변경이 기록되지 않아요.

PRIMARY KEY 열 중 하나 이상에 NULL 값이 저장된 개별 행에 대해서는 변경이 기록되지 않아요.

호출이 오류 없이 완료되면 SQLITE_OK가 반환돼요. 또는 오류가 발생하면 SQLite 오류 코드(예: SQLITE_NOMEM)가 반환돼요.

특수 sqlite_stat1 처리 (Special sqlite_stat1 Handling)

SQLite 버전 3.22.0부터 "sqlite_stat1" 테이블은 위 규칙 중 일부에 대한 예외예요. SQLite에서 sqlite_stat1의 스키마는 다음과 같아요.

      CREATE TABLE sqlite_stat1(tbl,idx,stat)

sqlite_stat1은 PRIMARY KEY가 없지만, PRIMARY KEY가 (tbl,idx)인 것처럼 변경이 기록돼요. 또한 (idx IS NULL)이 참인 행에 대해서도 변경이 기록돼요. 하지만 그런 행에 대해 NULL 값 대신 길이가 0인 blob(SQL 값 X'')이 changeset이나 patchset에 저장돼요. 이것은 그런 changeset이 sqlite3changeset_invert(), concat() 등의 이전 구현에서 조작될 수 있게 해줘요.

sqlite3changeset_apply() 함수는 sqlite_stat1 테이블을 갱신할 때 길이가 0인 blob을 자동으로 NULL 값으로 변환해요. 하지만 애플리케이션이 changeset 반복자에 대해 sqlite3changeset_new(), sqlite3changeset_old() 또는 sqlite3changeset_conflict를 직접 호출하면(충돌 처리기 콜백에 전달된 changeset 반복자 포함) X'' 값이 반환돼요. 필요한 경우 애플리케이션이 X''를 NULL로 직접 변환해야 해요.

세션 모듈의 이전(3.22.0보다 오래된) 버전은 sqlite_stat1 테이블에 이루어진 변경을 캡처할 수 없어요. sqlite3changeset_apply() 함수의 이전 버전은 changeset이나 patchset의 일부인 sqlite_stat1 테이블에 대한 수정을 조용히 무시해요.

세션 객체에서 changeset 생성 (Generate A Changeset From A Session Object)

int sqlite3session_changeset(
  sqlite3_session *pSession,      /* Session object */
  int *pnChangeset,               /* OUT: Size of buffer at *ppChangeset */
  void **ppChangeset              /* OUT: Buffer containing changeset */
);

첫 번째 인수로 전달된 세션 객체에 연결된 테이블에 대한 변경을 포함하는 changeset을 얻어요. 성공하면 SQLITE_OK를 반환하기 전에 *ppChangeset을 changeset을 포함하는 버퍼를 가리키도록, *pnChangeset을 changeset의 크기(바이트)로 설정해요. 오류가 발생하면 *ppChangeset과 *pnChangeset을 모두 0으로 설정하고 SQLite 오류 코드를 반환해요.

changeset은 각각 연결된 테이블의 단일 행에 대한 변경을 나타내는 0개 이상의 INSERT, UPDATE 및/또는 DELETE 변경으로 구성돼요. INSERT 변경은 새 데이터베이스 행의 각 필드 값을 포함해요. DELETE는 삭제된 데이터베이스 행의 각 필드의 원래 값을 포함해요. UPDATE 변경은 갱신된 데이터베이스 행의 각 필드의 원래 값과 함께 각 갱신된 비-기본-키 열의 갱신 값을 포함해요. UPDATE 변경이 기본 키 열의 값을 수정하는 변경을 나타내는 것은 불가능해요. 그런 변경이 이루어지면 changeset에서 DELETE 다음에 INSERT로 나타나요.

PRIMARY KEY 열 중 하나 이상에 NULL 값이 저장된 행에 대해서는 변경이 기록되지 않아요. 그런 행이 삽입되거나 삭제되면 이 함수가 반환하는 changeset에 해당 변경이 없어요. PRIMARY KEY 열에 NULL 값이 하나 이상 저장된 기존 행이 갱신되어 모든 PRIMARY KEY 열이 non-NULL이 되면 changeset에 INSERT만 나타나요. 마찬가지로 non-NULL PRIMARY KEY 값을 가진 기존 행이 갱신되어 PRIMARY KEY 열 중 하나 이상이 NULL로 설정되면 결과 changeset은 DELETE 변경만 포함해요.

changeset의 내용은 sqlite3changeset_start() API로 만든 반복자를 사용해 순회할 수 있어요. changeset은 sqlite3changeset_apply() API로 호환 스키마를 가진 데이터베이스에 적용할 수 있어요.

이 함수가 생성한 changeset 내에서 단일 테이블과 관련된 모든 변경은 함께 그룹화돼요. 즉, changeset을 반복하거나 changeset을 데이터베이스에 적용할 때 단일 테이블과 관련된 모든 변경은 다음 테이블로 넘어가기 전에 처리돼요. 테이블은 sqlite3_session 객체에 연결(또는 자동 연결)된 것과 같은 순서로 정렬돼요. 단일 테이블과 관련된 변경이 저장되는 순서는 정의되지 않아요.

이 함수에 대한 성공적인 호출 후, 호출자가 sqlite3_free()를 사용해 *ppChangeset이 가리키는 버퍼를 결국 해제할 책임이 있어요.

Changeset 생성 (Changeset Generation)

일단 테이블이 세션 객체에 연결되면, 세션 객체는 테이블에 삽입된 모든 새 행의 기본 키 값을 기록해요. 또한 삭제되거나 갱신된 행의 원래 기본 키와 다른 열 값도 기록해요. 각 고유한 기본 키 값에 대해 데이터는 한 번만 기록돼요. 세션 수명 동안 그 기본 키를 가진 행이 처음 삽입, 갱신 또는 삭제될 때요.

앞 단락에는 한 가지 예외가 있어요. 행이 삽입, 갱신 또는 삭제될 때 그 기본 키 열 중 하나 이상이 NULL 값을 포함하면 변경 기록이 만들어지지 않아요.

따라서 세션 객체는 두 유형의 기록을 누적해요. 기본 키 값만으로 구성된 기록(사용자가 새 레코드를 삽입할 때 생성)과 기본 키 값과 다른 테이블 열의 원래 값으로 구성된 기록(사용자가 레코드를 삭제하거나 갱신할 때 생성)이에요.

이 함수가 호출되면 요청된 changeset은 누적된 기록과 데이터베이스 파일의 현재 내용을 모두 사용해 만들어져요. 구체적으로:

  • 삽입으로 생성된 각 기록에 대해 데이터베이스에 일치하는 기본 키를 가진 행이 있는지 질의돼요. 발견되면 INSERT 변경이 changeset에 추가돼요. 그런 행이 없으면 changeset에 변경이 추가되지 않아요.
  • 갱신 또는 삭제로 생성된 각 기록에 대해 데이터베이스에 일치하는 기본 키를 가진 행이 있는지 질의돼요. 그런 행이 발견되고 비-기본 키 필드 중 하나 이상이 원래 값에서 수정됐다면 UPDATE 변경이 changeset에 추가돼요. 또는 테이블에서 그런 행이 없으면 DELETE 변경이 changeset에 추가돼요. 데이터베이스에 일치하는 기본 키를 가진 행이 있지만 모든 필드가 원래 값을 포함하면 changeset에 변경이 추가되지 않아요.

이것은 무엇보다도, 세션 객체가 활성화되어 있는 동안 행이 삽입된 후 나중에 삭제되면 삽입과 삭제 모두 changeset에 없게 된다는 것을 의미해요. 또는 세션 객체가 활성화되어 있는 동안 행이 삭제된 후 나중에 같은 기본 키 값을 가진 행이 삽입되면, 결과 changeset은 DELETE와 INSERT 대신 UPDATE 변경을 포함해요.

세션 객체가 비활성화되면(sqlite3session_enable() API 참조) 그것은 행이 삽입, 갱신 또는 삭제될 때 기록을 누적하지 않아요. 세션 중에 단일 행이 두 번 이상 쓰이면 이것은 반직관적인 효과가 있을 수 있어요. 예를 들어 세션 객체가 활성화된 동안 행이 삽입되고, 그 후 같은 세션 객체가 비활성화된 동안 삭제되면, 삭제가 세션이 비활성화된 동안 일어났어도 changeset에 INSERT 기록이 나타나지 않아요. 또는 세션이 활성화된 동안 행의 한 필드가 갱신되고, 그 후 세션이 비활성화된 동안 같은 행의 다른 필드가 갱신되면, 결과 changeset은 두 필드를 모두 갱신하는 UPDATE 변경을 포함해요.

changeset 크기의 상한 반환 (Return An Upper-limit For The Size Of The Changeset)

sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession);

기본적으로 이 함수는 항상 0을 반환해요. 유용한 결과를 반환하려면 sqlite3_session 객체가 SQLITE_SESSION_OBJCONFIG_SIZE 동사로 sqlite3session_object_config()를 사용해 이 API를 활성화하도록 구성되어 있어야 해요.

활성화되면 이 함수는 sqlite3session_changeset()이 호출될 경우 생성될 수 있는 changeset 크기에 대한 상한(바이트)을 반환해요. 최종 changeset 크기는 이 함수가 반환하는 크기(바이트)와 같거나 더 작을 수 있어요.

전역 매개변수 구성 (Configure global parameters)

int sqlite3session_config(int op, void *pArg);

sqlite3session_config() 인터페이스는 세션 모듈을 애플리케이션의 특정 요구에 맞게 조정하기 위해 전역 구성 변경을 하는 데 사용돼요.

sqlite3session_config() 인터페이스는 스레드 안전하지 않아요. 다른 스레드가 다른 세션 메서드 내부에 있는 동안 호출되면 결과가 정의되지 않아요. 또한 세션 관련 객체가 만들어진 후에 호출되면 결과도 정의되지 않아요.

sqlite3session_config() 함수의 첫 번째 인수는 아래 정의된 SQLITE_SESSION_CONFIG_XXX 상수 중 하나여야 해요. 두 번째 매개변수로 전달된 (void*) 값의 해석과 이 함수 호출의 효과는 첫 번째 매개변수의 값에 따라 달라져요.

SQLITE_SESSION_CONFIG_STRMSIZE

기본적으로 세션 모듈 스트리밍 인터페이스는 약 1 KiB 청크로 데이터를 입력·출력하려고 시도해요. 이 피연산자는 이 구성 설정의 값을 설정하고 질의하는 데 사용될 수 있어요. 두 번째 인수로 전달된 포인터는 (int) 타입의 값을 가리켜야 해요. 이 값이 0보다 크면 입력과 출력 모두에 대한 새 스트리밍 데이터 청크 크기로 사용돼요. 반환하기 전에 pArg가 가리키는 (int) 값은 스트리밍 인터페이스 청크 크기의 최종 값으로 설정돼요.

이 함수는 성공하면 SQLITE_OK를, 그렇지 않으면 SQLite 오류 코드를 반환해요.

새 세션 객체 생성 (Create A New Session Object)

int sqlite3session_create(
  sqlite3 *db,                    /* Database handle */
  const char *zDb,                /* Name of db (e.g. "main") */
  sqlite3_session **ppSession     /* OUT: New session object */
);

데이터베이스 핸들 db에 연결된 새 세션 객체를 만들어요. 성공하면 새 객체에 대한 포인터가 *ppSession에 기록되고 SQLITE_OK가 반환돼요. 오류가 발생하면 *ppSession은 NULL로 설정되고 SQLite 오류 코드(예: SQLITE_NOMEM)가 반환돼요.

단일 데이터베이스 핸들에 연결된 여러 세션 객체를 만들 수 있어요.

이 함수로 만든 세션 객체는 그들이 연결된 데이터베이스 핸들이 닫히기 전에 sqlite3session_delete() 함수로 삭제해야 해요. 세션 객체가 삭제되기 전에 데이터베이스 핸들이 닫히면, 세션 객체에 대한 세션 모듈 함수( sqlite3session_delete() 포함) 호출의 결과는 정의되지 않아요.

세션 모듈은 sqlite3_preupdate_hook() API를 사용하므로, 하나 이상의 세션 객체가 연결된 데이터베이스 핸들에 애플리케이션이 pre-update 훅을 등록하는 것은 불가능해요. 또한 pre-update 훅이 이미 정의된 데이터베이스 핸들에 연결된 세션 객체를 만드는 것도 불가능해요. 이 둘 중 하나를 시도한 결과는 정의되지 않아요.

세션 객체는 데이터베이스 zDb의 테이블에 대한 changeset을 만드는 데 사용될 거예요. 여기서 zDb는 "main", "temp" 또는 첨부된 데이터베이스의 이름이에요. 세션 객체가 만들어질 때 데이터베이스 zDb가 연결되지 않아도 오류는 아니에요.

세션 객체 삭제 (Delete A Session Object)

void sqlite3session_delete(sqlite3_session *pSession);

sqlite3session_create()로 이전에 할당된 세션 객체를 삭제해요. 세션 객체가 삭제된 후 다른 세션 모듈 함수에서 pSession을 사용하려는 시도의 결과는 정의되지 않아요.

세션 객체는 그들이 연결된 데이터베이스 핸들이 닫히기 전에 삭제해야 해요. 자세한 내용은 sqlite3session_create() 문서를 참조하세요.

테이블 차이를 세션에 로드 (Load The Difference Between Tables Into A Session)

int sqlite3session_diff(
  sqlite3_session *pSession,
  const char *zFromDb,
  const char *zTbl,
  char **pzErrMsg
);

이 함수는 첫 번째 인수로 전달된 세션 객체에 아직 연결되지 않았다면 sqlite3session_attach() 함수와 같은 방식으로 테이블 zTbl을 연결해요. zTbl이 존재하지 않거나 기본 키가 없으면 이 함수는 무연산이에요(그러나 오류를 반환하지 않아요).

인수 zFromDb는 이 함수가 세션에 연결한 테이블과 호환되는 테이블을 포함하는, 세션 객체와 같은 데이터베이스 핸들에 연결된 데이터베이스("main", "temp" 등)의 이름이어야 해요. 테이블이 다음과 같으면 호환되는 것으로 간주돼요.

  • 같은 이름을 가지고,
  • 같은 순서로 선언된 같은 열 집합을 가지고, 그리고
  • 같은 PRIMARY KEY 정의를 가진다.

테이블이 호환되지 않으면 SQLITE_SCHEMA가 반환돼요. 테이블이 호환되지만 PRIMARY KEY 열이 없으면 오류는 아니지만 세션 객체에 변경이 추가되지 않아요. 다른 세션 API와 마찬가지로 PRIMARY KEY가 없는 테이블은 단순히 무시돼요.

이 함수는 세션 객체에 변경 집합을 추가해요. 이 변경 집합은 데이터베이스 zFrom의 테이블("from-table"이라 부름)을 갱신하여 그 내용이 세션 객체에 연결된 테이블("to-table"이라 부름)과 같게 하는 데 사용될 수 있어요. 구체적으로:

  • from-table에는 없지만 to-table에 존재하는 각 행(기본 키)에 대해 INSERT 기록이 세션 객체에 추가돼요.
  • to-table에는 없지만 from-table에 존재하는 각 행(기본 키)에 대해 DELETE 기록이 세션 객체에 추가돼요.
  • 두 테이블 모두에 존재하지만 각각에서 다른 비-PK 값을 가진 각 행(기본 키)에 대해 UPDATE 기록이 세션에 추가돼요.

명확히 하자면, 이 함수가 호출된 다음 sqlite3session_changeset()을 사용해 changeset을 구성하고, 그 changeset을 데이터베이스 zFrom에 적용하면 두 호환 테이블의 내용이 동일해져요.

이 함수에 대한 호출이 위에서 설명한 대로 무연산이 아니면, 데이터베이스 zFrom이 존재하지 않거나 필요한 호환 테이블을 포함하지 않는 것은 오류예요.

연산이 성공하면 SQLITE_OK가 반환돼요. 그렇지 않으면 SQLite 오류 코드예요. 이 경우 인수 pzErrMsg가 NULL이 아니면 *pzErrMsg는 영어 오류 메시지를 포함하는 버퍼를 가리키도록 설정될 수 있어요. 호출자가 sqlite3_free()를 사용해 이 버퍼를 해제할 책임이 있어요.

세션 객체 활성화 또는 비활성화 (Enable Or Disable A Session Object)

int sqlite3session_enable(sqlite3_session *pSession, int bEnable);

세션 객체에 의한 변경 기록을 활성화하거나 비활성화해요. 활성화되면 세션 객체는 데이터베이스에 이루어진 변경을 기록해요. 비활성화되면 기록하지 않아요. 새로 만들어진 세션 객체는 활성화돼요. 세션 객체의 활성화·비활성화가 최종 changeset에 어떻게 영향을 주는지에 대한 자세한 내용은 sqlite3session_changeset() 문서를 참조하세요.

이 함수에 0을 전달하면 세션이 비활성화돼요. 0보다 큰 값을 전달하면 활성화돼요. 0보다 작은 값을 전달하면 무연산이며, 세션의 현재 상태를 질의하는 데 사용될 수 있어요.

반환 값은 세션 객체의 최종 상태를 나타내요. 세션이 비활성화되면 0, 활성화되면 1이에요.

간접 변경 플래그 설정 또는 해제 (Set Or Clear the Indirect Change Flag)

int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);

세션 객체가 기록한 각 변경은 직접(direct) 또는 간접(indirect)으로 표시돼요. 다음 중 하나라도 해당하면 변경은 간접으로 표시돼요.

  • 변경이 이루어질 때 세션 객체 "indirect" 플래그가 설정되어 있거나,
  • 변경이 사용자의 SQL 문의 직접적인 결과로가 아니라 SQL 트리거나 외래 키 동작에 의해 이루어졌거나.

단일 행이 세션 내의 둘 이상의 연산에 의해 영향받으면, 모든 연산이 위의 간접 변경 기준을 충족하면 변경은 간접으로, 그렇지 않으면 직접으로 간주돼요.

이 함수는 세션 객체 indirect 플래그를 설정, 해제 또는 질의하는 데 사용돼요. 이 함수에 전달된 두 번째 인수가 0이면 indirect 플래그가 해제돼요. 0보다 크면 indirect 플래그가 설정돼요. 0보다 작은 값을 전달하면 indirect 플래그의 현재 값을 수정하지 않으며, 지정된 세션 객체에 대한 indirect 플래그의 현재 상태를 질의하는 데 사용될 수 있어요.

반환 값은 indirect 플래그의 최종 상태를 나타내요. 해제되면 0, 설정되면 1이에요.

changeset이 변경을 기록했는지 테스트 (Test if a changeset has recorded any changes.)

int sqlite3session_isempty(sqlite3_session *pSession);

첫 번째 인수로 전달된 세션 객체가 연결된 테이블에 대한 변경을 기록하지 않았으면 0이 아닌 값을 반환해요. 그렇지 않으면 하나 이상의 변경이 기록됐다면 0을 반환해요.

이 함수가 0을 반환하더라도 세션 핸들에서 sqlite3session_changeset()을 호출하면 변경이 없는 changeset이 여전히 반환될 수 있어요. 이것은 연결된 테이블의 행이 수정된 다음 나중에 원래 값이 복원될 때 발생할 수 있어요. 하지만 이 함수가 0이 아닌 값을 반환하면 sqlite3session_changeset() 호출이 0개의 변경을 포함하는 changeset을 반환한다는 것이 보장돼요.

세션 객체가 사용하는 힙 메모리 양 질의 (Query for the amount of heap memory used by a session object.)

sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);

이 API는 유일한 인수로 전달된 세션 객체가 현재 사용하는 힙 메모리 총량(바이트)을 반환해요.

세션 객체 구성 (Configure a Session Object)

int sqlite3session_object_config(sqlite3_session*, int op, void *pArg);

이 메서드는 세션 객체가 만들어진 후 그것을 구성하는 데 사용돼요. 현재 두 번째 매개변수의 유일한 유효한 값은 SQLITE_SESSION_OBJCONFIG_SIZESQLITE_SESSION_OBJCONFIG_ROWID예요.

세션 객체에서 patchset 생성 (Generate A Patchset From A Session Object)

int sqlite3session_patchset(
  sqlite3_session *pSession,      /* Session object */
  int *pnPatchset,                /* OUT: Size of buffer at *ppPatchset */
  void **ppPatchset               /* OUT: Buffer containing patchset */
);

patchset과 changeset의 차이점은 다음과 같아요.

  • DELETE 기록은 기본 키 필드만으로 구성돼요. 다른 필드의 원래 값은 생략돼요.
  • 수정된 필드의 원래 값은 UPDATE 기록에서 생략돼요.

patchset blob은 sqlite3changeset_invert()를 제외한 모든 sqlite3changeset_xxx API 함수의 최신 버전과 함께 사용될 수 있어요. sqlite3changeset_invert()는 patchset이 전달되면 SQLITE_CORRUPT를 반환해요. 마찬가지로 patchset blob을 이전 버전의 sqlite3changeset_xxx API와 함께 사용하려는 시도도 SQLITE_CORRUPT 오류를 유발해요.

비-기본 키 "old.*" 필드가 생략되므로 patchset이 sqlite3changeset_apply() API에 전달되면 SQLITE_CHANGESET_DATA 충돌이 감지되거나 보고될 수 없어요. 다른 충돌 유형은 changeset과 같은 방식으로 동작해요.

patchset 내의 변경은 sqlite3session_changeset() 함수가 생성한 changeset과 같은 방식으로 정렬돼요. (즉, 단일 테이블에 대한 모든 변경이 함께 그룹화되고, 테이블은 세션 객체에 연결된 순서대로 나타나요.)

세션 객체에 테이블 필터 설정 (Set a table filter on a Session Object.)

void sqlite3session_table_filter(
  sqlite3_session *pSession,      /* Session object */
  int(*xFilter)(
    void *pCtx,                   /* Copy of third arg to _filter_table() */
    const char *zTab              /* Table name */
  ),
  void *pCtx                      /* First argument passed to xFilter */
);

두 번째 인수(xFilter)는 "필터 콜백"이에요. 세션 객체에 연결되지 않은 테이블의 행에 대한 변경의 경우, 테이블의 행 변경이 추적되어야 하는지 여부를 결정하기 위해 필터가 호출돼요. xFilter가 0을 반환하면 변경이 추적되지 않아요. 테이블이 연결되면 xFilter가 다시 호출되지 않는다는 점에 주의하세요.

sqlite3changeset_apply_v2의 플래그 (Flags for sqlite3changeset_apply_v2)

#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT   0x0001
#define SQLITE_CHANGESETAPPLY_INVERT        0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP    0x0004
#define SQLITE_CHANGESETAPPLY_FKNOACTION    0x0008
#define SQLITE_CHANGESETAPPLY_NOUPDATELOOP  0x0010

다음 플래그들이 sqlite3changeset_apply_v2sqlite3changeset_apply_v2_strm의 9번째 매개변수로 전달될 수 있어요.

SQLITE_CHANGESETAPPLY_NOSAVEPOINT

보통 세션 모듈은 apply_v2() 또는 apply_v2_strm()에 대한 단일 호출이 수행하는 모든 연산을 SAVEPOINT로 묶어요. SAVEPOINT는 changeset이나 patchset이 성공적으로 적용되면 커밋되고, 오류가 발생하면 롤백돼요. 이 플래그를 지정하면 세션 모듈이 이 savepoint를 생략하게 해요. 이 경우 apply_v2()가 호출될 때 호출자가 열린 트랜잭션이나 savepoint를 가지고 있다면, 그것을 롤백하여 부분적으로 적용된 changeset을 되돌릴 수 있어요.

SQLITE_CHANGESETAPPLY_INVERT

적용하기 전에 changeset을 뒤집어요. 이것은 적용하기 전에 sqlite3changeset_invert()를 사용해 changeset을 뒤집는 것과 동등해요. patchset과 함께 이 플래그를 지정하면 오류예요.

SQLITE_CHANGESETAPPLY_IGNORENOOP

적용되더라도 실제로 데이터베이스를 수정하지 않는 변경에 대해서는 충돌 처리기 콜백을 호출하지 않아요. 구체적으로, 이것은 다음에 대해 충돌 처리기가 호출되지 않음을 의미해요.

  • 삭제되는 행을 찾을 수 없는 delete 변경,
  • 수정된 필드가 충돌하는 행에서 이미 새 값으로 설정된 update 변경, 또는
  • 충돌하는 행의 모든 필드가 삽입되는 행과 일치하는 insert 변경.

SQLITE_CHANGESETAPPLY_FKNOACTION

이 플래그가 설정되면 대상 데이터베이스의 모든 외래 키 제약 조건이 실제로 CASCADE, RESTRICT, SET NULL 또는 SET DEFAULT라고 해도 "ON UPDATE NO ACTION ON DELETE NO ACTION"으로 선언된 것처럼 동작해요.

SQLITE_CHANGESETAPPLY_NOUPDATELOOP

때로 changeset은 모든 업데이트를 적용한 후에는 데이터베이스에 제약 위반이 없지만 개별 업데이트는 다른 것들보다 먼저 적용될 수 없는 둘 이상의 업데이트 문을 포함해요. 가장 단순한 예는 UNIQUE 제약 조건으로 두 열 값을 "교환"(swapped)한 UPDATE 쌍이에요.

보통 sqlite3changeset_apply()와 유사한 함수는 그런 changeset을 적용할 방법을 찾으려고 열심히 노력해요. 하지만 이 플래그가 설정되면 그런 모든 업데이트가 CONSTRAINT 충돌로 간주돼요.

충돌 처리기가 반환하는 상수 (Constants Returned By The Conflict Handler)

#define SQLITE_CHANGESET_OMIT       0
#define SQLITE_CHANGESET_REPLACE    1
#define SQLITE_CHANGESET_ABORT      2

충돌 처리기 콜백은 다음 세 값 중 하나를 반환해야 해요.

SQLITE_CHANGESET_OMIT

충돌 처리기가 이 값을 반환하면 특별한 조치가 취해지지 않아요. 충돌을 일으킨 변경은 적용되지 않아요. 세션 모듈은 changeset의 다음 변경으로 계속해요.

SQLITE_CHANGESET_REPLACE

이 값은 충돌 처리기의 두 번째 인수가 SQLITE_CHANGESET_DATA 또는 SQLITE_CHANGESET_CONFLICT인 경우에만 반환될 수 있어요. 그렇지 않으면 지금까지 적용된 변경이 롤백되고 sqlite3changeset_apply() 호출이 SQLITE_MISUSE를 반환해요.

CHANGESET_REPLACE가 SQLITE_CHANGESET_DATA 충돌 처리기에 의해 반환되면 충돌하는 행은 변경 유형에 따라 갱신되거나 삭제돼요.

CHANGESET_REPLACE가 SQLITE_CHANGESET_CONFLICT 충돌 처리기에 의해 반환되면 충돌하는 행이 데이터베이스에서 제거되고 변경 적용을 두 번째 시도해요. 이 두 번째 시도가 실패하면 계속하기 전에 원래 행이 데이터베이스에 복원돼요.

SQLITE_CHANGESET_ABORT

이 값이 반환되면 지금까지 적용된 변경이 롤백되고 sqlite3changeset_apply() 호출이 SQLITE_ABORT를 반환해요.

충돌 처리기에 전달되는 상수 (Constants Passed To The Conflict Handler)

#define SQLITE_CHANGESET_DATA        1
#define SQLITE_CHANGESET_NOTFOUND    2
#define SQLITE_CHANGESET_CONFLICT    3
#define SQLITE_CHANGESET_CONSTRAINT  4
#define SQLITE_CHANGESET_FOREIGN_KEY 5

충돌 처리기의 두 번째 인수로 전달될 수 있는 값들이에요.

SQLITE_CHANGESET_DATA

DELETE 또는 UPDATE 변경을 처리할 때 데이터베이스에 필요한 PRIMARY KEY 필드를 가진 행이 존재하지만, 업데이트가 수정하는 하나 이상의 다른(비-기본 키) 필드가 예상되는 "이전" 값을 포함하지 않으면 충돌 처리기가 CHANGESET_DATA를 두 번째 인수로 호출돼요.

이 경우 충돌하는 행은 일치하는 기본 키를 가진 데이터베이스 행이에요.

SQLITE_CHANGESET_NOTFOUND

DELETE 또는 UPDATE 변경을 처리할 때 데이터베이스에 필요한 PRIMARY KEY 필드를 가진 행이 존재하지 않으면 충돌 처리기가 CHANGESET_NOTFOUND를 두 번째 인수로 호출돼요.

이 경우 충돌하는 행이 없어요. sqlite3changeset_conflict() API를 호출한 결과는 정의되지 않아요.

SQLITE_CHANGESET_CONFLICT

INSERT 변경을 처리할 때 그 연산이 중복 기본 키 값을 초래한다면 CHANGESET_CONFLICT가 충돌 처리기의 두 번째 인수로 전달돼요.

이 경우 충돌하는 행은 일치하는 기본 키를 가진 데이터베이스 행이에요.

SQLITE_CHANGESET_FOREIGN_KEY

외래 키 처리가 활성화되어 있고, changeset을 적용하면 데이터베이스가 외래 키 위반을 포함하는 상태가 되면, changeset이 커밋되기 전에 정확히 한 번 충돌 처리기가 CHANGESET_FOREIGN_KEY를 두 번째 인수로 호출돼요. 충돌 처리기가 CHANGESET_OMIT를 반환하면 외래 키 제약 위반을 일으킨 것들을 포함한 변경이 커밋돼요. 또는 CHANGESET_ABORT를 반환하면 changeset이 롤백돼요.

현재 또는 충돌하는 행 정보는 제공되지 않아요. 제공된 sqlite3_changeset_iter 핸들에서 호출할 수 있는 유일한 함수는 sqlite3changeset_fk_conflicts()예요.

SQLITE_CHANGESET_CONSTRAINT

변경을 적용하는 동안 다른 제약 위반(예: UNIQUE, CHECK 또는 NOT NULL 제약 조건)이 발생하면 충돌 처리기가 CHANGESET_CONSTRAINT를 두 번째 인수로 호출돼요.

이 경우 충돌하는 행이 없어요. sqlite3changeset_conflict() API를 호출한 결과는 정의되지 않아요.

sqlite3session_object_config의 옵션 (Options for sqlite3session_object_config)

#define SQLITE_SESSION_OBJCONFIG_SIZE  1
#define SQLITE_SESSION_OBJCONFIG_ROWID 2

다음 값들이 sqlite3session_object_config()의 2번째 매개변수로 전달될 수 있어요.

SQLITE_SESSION_OBJCONFIG_SIZE

이 옵션은 sqlite3session_changeset_size() API를 활성화하는 플래그를 설정, 해제 또는 질의하는 데 사용돼요. 약간의 계산 오버헤드를 부과하므로 이 API는 기본적으로 비활성화돼요. 인수 pArg는 (int) 타입의 값을 가리켜야 해요. 값이 처음에 0이면 sqlite3session_changeset_size() API가 비활성화돼요. 0보다 크면 같은 API가 활성화돼요. 또는 처음 값이 0보다 작으면 변경이 없어요. 모든 경우에 현재 호출 다음에 sqlite3session_changeset_size() API가 활성화되면 (int) 변수는 1로, 그렇지 않으면 0으로 설정돼요.

첫 번째 테이블이 세션 객체에 연결된 후 이 설정을 수정하려고 시도하는 것은 오류(SQLITE_MISUSE)예요.

SQLITE_SESSION_OBJCONFIG_ROWID

이 옵션은 명시적 PRIMARY KEY가 없는 테이블에 대한 데이터 수집을 활성화하는 플래그를 설정, 해제 또는 질의하는 데 사용돼요.

보통 명시적 PRIMARY KEY가 없는 테이블은 세션 모듈에 의해 단순히 무시돼요. 하지만 이 플래그가 설정되면 그런 테이블에 가장 왼쪽 열로 삽입된 "rowid INTEGER PRIMARY KEY" 열이 있는 것처럼 동작해요.

첫 번째 테이블이 세션 객체에 연결된 후 이 설정을 수정하려고 시도하는 것은 오류(SQLITE_MISUSE)예요.

API 함수의 스트리밍 버전 (Streaming Versions of API functions.)

int sqlite3changeset_apply_strm(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
  void *pIn,                                          /* First arg for xInput */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx                      /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2_strm(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
  void *pIn,                                          /* First arg for xInput */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase,
  int flags
);
int sqlite3changeset_apply_v3_strm(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
  void *pIn,                                          /* First arg for xInput */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    sqlite3_changeset_iter *p
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase,
  int flags
);
int sqlite3changeset_concat_strm(
  int (*xInputA)(void *pIn, void *pData, int *pnData),
  void *pInA,
  int (*xInputB)(void *pIn, void *pData, int *pnData),
  void *pInB,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3changeset_invert_strm(
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3changeset_start_strm(
  sqlite3_changeset_iter **pp,
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn
);
int sqlite3changeset_start_v2_strm(
  sqlite3_changeset_iter **pp,
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn,
  int flags
);
int sqlite3session_changeset_strm(
  sqlite3_session *pSession,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3session_patchset_strm(
  sqlite3_session *pSession,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);
int sqlite3changegroup_add_strm(sqlite3_changegroup*,
    int (*xInput)(void *pIn, void *pData, int *pnData),
    void *pIn
);
int sqlite3changegroup_output_strm(sqlite3_changegroup*,
    int (*xOutput)(void *pOut, const void *pData, int nData),
    void *pOut
);
int sqlite3rebaser_rebase_strm(
  sqlite3_rebaser *pRebaser,
  int (*xInput)(void *pIn, void *pData, int *pnData),
  void *pIn,
  int (*xOutput)(void *pOut, const void *pData, int nData),
  void *pOut
);

여섯 개의 스트리밍 API xxx_strm() 함수는 해당 비스트리밍 API 함수와 유사한 목적을 제공해요.

| 스트리밍 함수 | 비스트리밍 등가물 | | sqlite3changeset_apply_strm | sqlite3changeset_apply | | sqlite3changeset_apply_v2_strm | sqlite3changeset_apply_v2 | | sqlite3changeset_concat_strm | sqlite3changeset_concat | | sqlite3changeset_invert_strm | sqlite3changeset_invert | | sqlite3changeset_start_strm | sqlite3changeset_start | | sqlite3session_changeset_strm | sqlite3session_changeset | | sqlite3session_patchset_strm | sqlite3session_patchset |

changeset(또는 patchset)을 입력으로 받아들이는 비스트리밍 함수는 전체 changeset이 메모리의 단일 버퍼에 저장되도록 요구해요. 마찬가지로 changeset이나 patchset을 반환하는 함수는 sqlite3_malloc()으로 할당된 단일 큰 버퍼에 대한 포인터를 반환해 그렇게 해요. 보통 이것은 편리해요. 하지만 저메모리 환경에서 실행되는 애플리케이션이 매우 큰 changeset을 처리해야 한다면, 필요한 큰 연속 메모리 할당이 부담이 될 수 있어요.

이 문제를 피하기 위해, 단일 큰 버퍼 대신 입력은 세션 모듈이 필요할 때 증분적으로 입력 데이터를 요청하기 위해 호출하는 콜백 함수를 통해 스트리밍 API 함수에 전달돼요. 모든 경우에 다음과 같은 API 함수 매개변수 쌍:

      int nChangeset,
      void *pChangeset,

다음으로 대체돼요.

      int (*xInput)(void *pIn, void *pData, int *pnData),
      void *pIn,

세션 모듈이 xInput 콜백을 호출할 때마다 전달되는 첫 번째 인수는 제공된 pIn 컨텍스트 포인터의 복사본이에요. 두 번째 인수 pData는 (*pnData) 바이트 크기의 버퍼를 가리켜요. 오류가 발생하지 않는다고 가정하면 xInput 메서드는 최대 (*pnData) 바이트의 데이터를 버퍼에 복사하고 SQLITE_OK를 반환하기 전에 (*pnData)를 실제 복사된 바이트 수로 설정해야 해요. 입력이 완전히 소진되면 이것을 나타내기 위해 (*pnData)를 0으로 설정해야 해요. 또는 오류가 발생하면 SQLite 오류 코드를 반환해야 해요. 모든 경우에 xInput 콜백이 오류를 반환하면 모든 처리가 중단되고 스트리밍 API 함수는 오류 코드의 복사본을 호출자에게 반환해요.

sqlite3changeset_start_strm()의 경우 xInput 콜백은 반복자의 수명 동안 어느 시점에 세션 모듈에 의해 호출될 수 있어요. 그런 xInput 콜백이 오류를 반환하면 반복자는 오류 상태에 들어가며, 이후 반복자 함수에 대한 모든 호출이 xInput이 반환한 것과 같은 오류 코드로 즉시 실패해요.

마찬가지로 changeset(또는 patchset)을 반환하는 스트리밍 API 함수는 단일 큰 버퍼에 대한 포인터 대신 콜백 함수를 통해 그것들을 청크로 반환해요. 이 경우 다음과 같은 매개변수 쌍:

      int *pnChangeset,
      void **ppChangeset,

다음으로 대체돼요.

      int (*xOutput)(void *pOut, const void *pData, int nData),
      void *pOut

xOutput 콜백은 데이터를 애플리케이션에 반환하기 위해 0회 이상 호출돼요. 각 호출에 전달되는 첫 번째 매개변수는 애플리케이션이 제공한 pOut 포인터의 복사본이에요. 두 번째 매개변수 pData는 반환되는 출력 데이터 청크를 포함하는 nData 바이트 크기의 버퍼를 가리켜요. xOutput 콜백이 제공된 데이터를 성공적으로 처리하면 성공을 나타내기 위해 SQLITE_OK를 반환해야 해요. 그렇지 않으면 다른 SQLite 오류 코드를 반환해야 해요. 이 경우 처리가 즉시 중단되고 스트리밍 API 함수는 xOutput 오류 코드의 복사본을 애플리케이션에 반환해요.

세션 모듈은 세 번째 매개변수가 0보다 작거나 같은 값으로 설정된 xOutput 콜백을 호출하지 않아요. 이것 외에는 반환되는 데이터 청크의 크기에 대해 어떤 보장도 하지 않아요.

Changeset을 데이터베이스에 적용 (Apply A Changeset To A Database)

int sqlite3changeset_apply(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx                      /* First argument passed to xConflict */
);
int sqlite3changeset_apply_v2(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    const char *zTab              /* Table name */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase, /* OUT: Rebase data */
  int flags                       /* SESSION_CHANGESETAPPLY_* flags */
);
int sqlite3changeset_apply_v3(
  sqlite3 *db,                    /* Apply change to "main" db of this handle */
  int nChangeset,                 /* Size of changeset in bytes */
  void *pChangeset,               /* Changeset blob */
  int(*xFilter)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    sqlite3_changeset_iter *p     /* Handle describing change */
  ),
  int(*xConflict)(
    void *pCtx,                   /* Copy of sixth arg to _apply() */
    int eConflict,                /* DATA, MISSING, CONFLICT, CONSTRAINT */
    sqlite3_changeset_iter *p     /* Handle describing change and conflict */
  ),
  void *pCtx,                     /* First argument passed to xConflict */
  void **ppRebase, int *pnRebase, /* OUT: Rebase data */
  int flags                       /* SESSION_CHANGESETAPPLY_* flags */
);

changeset이나 patchset을 데이터베이스에 적용해요. 이 함수들은 두 번째와 세 번째 인수로 전달된 changeset에서 찾은 변경으로 핸들 db에 연결된 "main" 데이터베이스를 갱신하려고 시도해요.

이 함수들이 만든 모든 변경은 savepoint 트랜잭션으로 묶여요. (대상 데이터베이스에 쓰려고 할 때의 제약 실패를 제외한) 다른 오류가 발생하면 savepoint 트랜잭션이 롤백되어 대상 데이터베이스를 원래 상태로 복원하고 SQLite 오류 코드가 반환돼요. 또한 버전 3.51.0부터 sqlite3_errcode()sqlite3_errmsg() API로 접근할 수 있는 오류 코드와 오류 메시지가 데이터베이스 핸들에 남겨져요.

이 함수들에 전달된 네 번째 인수(xFilter)는 "필터 콜백"이에요. 이 인수는 NULL로 전달될 수 있으며, 그 경우 changeset의 모든 변경이 데이터베이스에 적용돼요. sqlite3changeset_apply()와 sqlite3_changeset_apply_v2()의 경우 NULL이 아니면 changeset의 적어도 하나의 변경에 영향받은 각 테이블에 대해 한 번 호출돼요. 이 경우 테이블 이름이 두 번째 인수로, apply() 또는 apply_v2()의 여섯 번째 인수로 전달된 컨텍스트 포인터의 복사본이 첫 번째 인수로 전달돼요. "필터 콜백"이 0을 반환하면 테이블에 어떤 변경도 적용하려는 시도가 이루어지지 않아요. 그렇지 않으면 반환 값이 0이 아닐 때 테이블과 관련된 모든 변경이 시도돼요.

sqlite3_changeset_apply_v3()의 경우 xFilter 콜백은 변경마다 한 번 호출돼요. 이 경우 두 번째 인수는 보통 API를 사용해 현재 변경의 세부 사항을 질의할 수 있는 sqlite3_changeset_iter예요. 이 경우 "필터 콜백"이 0을 반환하면 현재 변경을 적용하려는 시도가 이루어지지 않아요. 0이 아닌 값을 반환하면 변경이 적용돼요.

필터 콜백에 의해 제외되지 않은 각 테이블에 대해 이 함수는 대상 데이터베이스가 호환되는 테이블을 포함하는지 테스트해요. 다음이 모두 참이면 테이블이 호환되는 것으로 간주돼요.

  • 테이블이 changeset에 기록된 이름과 같은 이름을 가지고, 그리고
  • 테이블이 changeset에 기록된 것 이상의 충분한 열을 가지고, 그리고
  • 테이블이 changeset에 기록된 것과 같은 위치에 기본 키 열을 가진다.

호환되는 테이블이 없으면 오류는 아니지만 테이블과 관련된 변경 중 어느 것도 적용되지 않아요. SQLITE_SCHEMA 오류 코드로 sqlite3_log() 메커니즘을 통해 경고 메시지가 발행돼요. changeset의 각 테이블에 대해 많아야 하나의 경고가 발행돼요.

호환되는 테이블이 있는 각 변경에 대해 필터 콜백에 의해 제외되지 않은 각 UPDATE, INSERT 또는 DELETE 변경에 따라 테이블 내용을 수정하려는 시도가 이루어져요. 변경이 깨끗하게 적용될 수 없으면 sqlite3changeset_apply()의 다섯 번째 인수로 전달된 충돌 처리기 함수가 호출될 수 있어요. 각 변경 유형에 대해 충돌 처리기가 정확히 언제 호출되는지에 대한 설명은 아래에 있어요.

xFilter 인수와 달리 xConflict는 NULL로 전달될 수 없어요. xConflict 인수로 유효한 함수 포인터가 아닌 다른 것을 전달한 결과는 정의되지 않아요.

충돌 처리기 함수가 호출될 때마다 SQLITE_CHANGESET_OMIT, SQLITE_CHANGESET_ABORT 또는 SQLITE_CHANGESET_REPLACE 중 하나를 반환해야 해요. SQLITE_CHANGESET_REPLACE는 충돌 처리기에 전달된 두 번째 인수가 SQLITE_CHANGESET_DATA 또는 SQLITE_CHANGESET_CONFLICT인 경우에만 반환될 수 있어요. 충돌 처리기가 불법 값을 반환하면 이미 이루어진 변경이 롤백되고 sqlite3changeset_apply() 호출이 SQLITE_MISUSE를 반환해요. sqlite3changeset_apply()는 충돌 처리기 함수의 각 호출이 반환하는 값에 따라 다른 조치를 취해요. 자세한 내용은 세 개의 가능한 반환 값 문서를 참조하세요.

DELETE 변경 — 각 DELETE 변경에 대해 함수는 대상 데이터베이스가 changeset에 저장된 원래 행 값과 같은 기본 키 값(들)을 가진 행을 포함하는지 확인해요. 있다면, 모든 비-기본 키 열에 저장된 값도 changeset에 저장된 값과 일치하면 행이 대상 데이터베이스에서 삭제돼요.

일치하는 기본 키 값을 가진 행이 발견되지만 비-기본 키 필드 중 하나 이상이 changeset에 저장된 원래 행 값과 다른 값을 포함하면 충돌 처리기 함수가 SQLITE_CHANGESET_DATA를 두 번째 인수로 호출돼요. 데이터베이스 테이블이 changeset에 기록된 것보다 더 많은 열을 가지면 비-기본 키 필드의 값만 현재 데이터베이스 내용과 비교돼요. 끝부분의 데이터베이스 테이블 열은 무시돼요.

데이터베이스에서 일치하는 기본 키 값을 가진 행이 없으면 충돌 처리기 함수가 SQLITE_CHANGESET_NOTFOUND를 두 번째 인수로 전달되어 호출돼요.

DELETE 연산이 시도되지만 SQLite가 SQLITE_CONSTRAINT를 반환하면(외래 키 제약 조건이 위반될 때만 발생할 수 있음) 충돌 처리기 함수가 SQLITE_CHANGESET_CONSTRAINT를 두 번째 인수로 전달되어 호출돼요. 이것은 충돌 처리기 함수에 대한 이전 호출이 SQLITE_CHANGESET_REPLACE를 반환했기 때문에 DELETE 연산이 시도되는 경우를 포함해요.

INSERT 변경 — 각 INSERT 변경에 대해 새 행을 데이터베이스에 삽입하려는 시도가 이루어져요. changeset 행이 데이터베이스 테이블보다 더 적은 필드를 포함하면 끝부분 필드는 기본 값으로 채워져요.

행 삽입 시도가 데이터베이스가 이미 같은 기본 키 값을 가진 행을 포함하기 때문에 실패하면 충돌 처리기 함수가 두 번째 인수를 SQLITE_CHANGESET_CONFLICT로 설정해 호출돼요.

행 삽입 시도가 다른 제약 위반(예: NOT NULL 또는 UNIQUE) 때문에 실패하면 충돌 처리기 함수가 두 번째 인수를 SQLITE_CHANGESET_CONSTRAINT로 설정해 호출돼요. 이것은 충돌 처리기 함수에 대한 이전 호출이 SQLITE_CHANGESET_REPLACE를 반환했기 때문에 INSERT 연산이 다시 시도되는 경우를 포함해요.

UPDATE 변경 — 각 UPDATE 변경에 대해 함수는 대상 데이터베이스가 changeset에 저장된 원래 행 값과 같은 기본 키 값(들)을 가진 행을 포함하는지 확인해요. 있다면, 수정된 모든 비-기본 키 열에 저장된 값도 changeset에 저장된 값과 일치하면 행이 대상 데이터베이스 내에서 갱신돼요.

일치하는 기본 키 값을 가진 행이 발견되지만 수정된 비-기본 키 필드 중 하나 이상이 changeset에 저장된 원래 행 값과 다른 값을 포함하면 충돌 처리기 함수가 SQLITE_CHANGESET_DATA를 두 번째 인수로 호출돼요. UPDATE 변경은 수정될 비-기본 키 필드에 대한 값만 포함하므로, SQLITE_CHANGESET_DATA 충돌 처리기 콜백을 피하려면 그 필드들만 원래 값과 일치하면 돼요.

데이터베이스에서 일치하는 기본 키 값을 가진 행이 없으면 충돌 처리기 함수가 SQLITE_CHANGESET_NOTFOUND를 두 번째 인수로 전달되어 호출돼요.

UPDATE 연산이 시도되지만 SQLite가 SQLITE_CONSTRAINT를 반환하면 충돌 처리기 함수가 SQLITE_CHANGESET_CONSTRAINT를 두 번째 인수로 전달되어 호출돼요. 이것은 충돌 처리기 함수에 대한 이전 호출이 SQLITE_CHANGESET_REPLACE를 반환한 후 UPDATE 연산이 시도되는 경우를 포함해요.

xConflict 콜백 내에서 콜백과 관련된 테이블에 쓰는 것을 포함한 SQL 문을 실행하는 것은 안전해요. 이것은 애플리케이션의 충돌 해결 전략을 추가로 사용자화하는 데 사용될 수 있어요.

출력 매개변수(ppRebase)와 (pnRebase)가 non-NULL이고 입력이 changeset(patchset 아님)이면, sqlite3changeset_apply_v2()는 반환하기 전에 (*ppRebase)를 sqlite3_rebaser API와 함께 사용될 수 있는 "rebase" 버퍼를 가리키도록 설정할 수 있어요. 이 경우 (*pnRebase)는 버퍼의 크기(바이트)로 설정돼요. 호출자가 나중에 sqlite3_free()를 사용해 그런 버퍼를 해제할 책임이 있어요. 버퍼는 patchset을 적용하는 동안 하나 이상의 충돌이 발생한 경우에만 할당되고 채워져요. 자세한 내용은 sqlite3_rebaser API를 둘러싼 주석을 참조하세요.

sqlite3changeset_apply_v2()와 그 스트리밍 등가물의 동작은 9번째 매개변수로 지원되는 플래그의 조합을 전달해 수정될 수 있어요.

sqlite3changeset_apply_v2() API는 여전히 실험적이며 따라서 변경될 수 있다는 점에 주의하세요.

Changeset을 순회하는 반복자 생성 (Create An Iterator To Traverse A Changeset)

int sqlite3changeset_start(
  sqlite3_changeset_iter **pp,    /* OUT: New changeset iterator handle */
  int nChangeset,                 /* Size of changeset blob in bytes */
  void *pChangeset                /* Pointer to blob containing changeset */
);
int sqlite3changeset_start_v2(
  sqlite3_changeset_iter **pp,    /* OUT: New changeset iterator handle */
  int nChangeset,                 /* Size of changeset blob in bytes */
  void *pChangeset,               /* Pointer to blob containing changeset */
  int flags                       /* SESSION_CHANGESETSTART_* flags */
);

changeset의 내용을 순회하는 데 사용되는 반복자를 만들어요. 성공하면 *pp가 반복자 핸들을 가리키도록 설정되고 SQLITE_OK가 반환돼요. 그렇지 않으면 오류가 발생하면 *pp는 0으로 설정되고 SQLite 오류 코드가 반환돼요.

다음 함수를 사용해 이 함수로 만든 changeset 반복자를 전진시키고 질의할 수 있어요.

호출자가 sqlite3changeset_finalize()에 전달해 반복자를 결국 파괴할 책임이 있어요. changeset을 포함하는 버퍼(pChangeset)는 반복자가 파괴된 후까지 유효한 상태로 유지되어야 해요.

changeset blob이 sqlite3session_changeset(), sqlite3changeset_concat() 또는 sqlite3changeset_invert() 함수 중 하나로 만들어졌다고 가정하면, changeset 내에서 단일 테이블에 적용되는 모든 변경이 함께 그룹화돼요. 이것은 애플리케이션이 이 함수로 만든 반복자를 사용해 changeset을 순회할 때 단일 테이블과 관련된 모든 변경이 연속적으로 방문됨을 의미해요. 반복자가 테이블 X에 적용되는 변경을 방문한 다음 테이블 Y에 대한 변경을 방문하고 나중에 테이블 X에 대한 또 다른 변경을 방문할 가능성은 없어요.

sqlite3changeset_start_v2()와 그 스트리밍 등가물의 동작은 4번째 매개변수로 지원되는 플래그의 조합을 전달해 수정될 수 있어요.

sqlite3changeset_start_v2() API는 여전히 실험적이며 따라서 변경될 수 있다는 점에 주의하세요.

더 알아보기 (Learn more)