SQLite C/C++ 인터페이스 소개

SQLite C/C++ 인터페이스 소개

SQLite에는 225개 이상의 API가 있지만, 대부분은 선택적이고 매우 전문화되어 있어서 초보자가 무시해도 돼요. 핵심 API는 작고 단순하며 배우기 쉬워요. 이 문서는 그 핵심 API를 요약해서 설명합니다. SQLite를 효과적으로 사용하려면 기본 원리만 이해하면 충분하답니다.

출처: 문서

본문

1. 요약

다음 두 객체와 여덟 개의 메서드가 SQLite 인터페이스의 필수 요소를 구성해요:

  • sqlite3 → 데이터베이스 연결 객체. sqlite3_open()으로 생성되고 sqlite3_close()로 파괴돼요.
  • sqlite3_stmt → prepared statement 객체. sqlite3_prepare()로 생성되고 sqlite3_finalize()로 파괴돼요.
  • sqlite3_open() → 새 또는 기존 SQLite 데이터베이스에 대한 연결을 엽니다. sqlite3의 생성자예요.
  • sqlite3_prepare() → SQL 텍스트를 데이터베이스를 쿼리하거나 업데이트하는 작업을 수행할 바이트 코드로 컴파일해요. sqlite3_stmt의 생성자예요.
  • sqlite3_bind() → 원래 SQL의 파라미터에 애플리케이션 데이터를 저장해요.
  • sqlite3_step() → sqlite3_stmt를 다음 결과 행 또는 완료 지점으로 전진시켜요.
  • sqlite3_column() → sqlite3_stmt에 대한 현재 결과 행의 컬럼 값이에요.
  • sqlite3_finalize() → sqlite3_stmt의 소멸자예요.
  • sqlite3_close() → sqlite3의 소멸자예요.
  • sqlite3_exec() → 하나 이상의 SQL 문으로 된 문자열에 대해 sqlite3_prepare(), sqlite3_step(), sqlite3_column(), sqlite3_finalize()를 수행하는 래퍼 함수예요.

2. 소개

SQLite에는 225개 이상의 API가 있어요. 하지만 대부분의 API는 선택적이고 매우 전문화되어 있어서 초보자가 무시해도 돼요. 핵심 API는 작고 단순하며 배우기 쉬워요. 이 문서는 핵심 API를 요약해요.

별도의 문서인 The SQLite C/C++ Interface가 모든 SQLite C/C++ API에 대한 상세한 스펙을 제공해요. SQLite의 기본 동작 원리를 이해하면 그 문서를 참조 가이드로 사용하면 돼요. 이 문서는 소개를 목적으로 하며 SQLite API에 대한 완전하거나 권위 있는 참조는 아니에요.

3. 핵심 객체와 인터페이스

SQL 데이터베이스 엔진의 주요 작업은 SQL 문을 평가하는 것이에요. 이를 위해 개발자는 두 가지 객체가 필요해요:

  • 데이터베이스 연결 객체: sqlite3
  • prepared statement 객체: sqlite3_stmt

엄밀히 말하면 prepared statement 객체는 필수는 아니에요. 편의 래퍼 인터페이스인 sqlite3_execsqlite3_get_table을 사용할 수 있고, 이 편의 래퍼가 prepared statement 객체를 캡슐화하고 숨기기 때문이죠. 그래도 SQLite를 완전히 활용하려면 prepared statement를 이해해야 해요.

데이터베이스 연결과 prepared statement 객체는 아래에 나열된 소수의 C/C++ 인터페이스 루틴으로 제어돼요:

  • sqlite3_open()
  • sqlite3_prepare()
  • sqlite3_step()
  • sqlite3_column()
  • sqlite3_finalize()
  • sqlite3_close()

위 루틴 목록은 실제라기보다 개념적이라는 점에 주의해요. 이 루틴들 중 다수는 여러 버전으로 제공돼요. 예를 들어 위 목록은 sqlite3_open() 단일 루틴을 보여주지만, 실제로는 조금씩 다르게 같은 일을 하는 세 루틴이 있어요: sqlite3_open(), sqlite3_open16(), sqlite3_open_v2(). 목록은 sqlite3_column()을 언급하지만 실제로 그런 루틴은 존재하지 않아요. 목록에 보이는 "sqlite3_column()"은 다양한 데이터타입으로 컬럼 데이터를 추출하는 전체 루틴 계열의 플레이스홀더예요.

핵심 인터페이스가 하는 일에 대한 요약은 다음과 같아요:

  • sqlite3_open() — 이 루틴은 SQLite 데이터베이스 파일에 대한 연결을 열고 데이터베이스 연결 객체를 반환해요. 애플리케이션이 하는 첫 번째 SQLite API 호출인 경우가 많고, 대부분의 다른 SQLite API의 전제 조건이에요. 많은 SQLite 인터페이스는 첫 번째 파라미터로 데이터베이스 연결 객체에 대한 포인터를 요구하며, 데이터베이스 연결 객체의 메서드로 생각할 수 있어요. 이 루틴은 데이터베이스 연결 객체의 생성자예요.

  • sqlite3_prepare() — 이 루틴은 SQL 텍스트를 prepared statement 객체로 변환하고 그 객체에 대한 포인터를 반환해요. 이 인터페이스는 이전 sqlite3_open() 호출로 만들어진 데이터베이스 연결 포인터와, 준비할 SQL 문을 담은 텍스트 문자열을 요구해요. 이 API는 실제로 SQL 문을 평가하지 않아요. 평가를 위해 SQL 문을 준비만 해요.

각 SQL 문을 작은 컴퓨터 프로그램이라 생각해보세요. sqlite3_prepare()의 목적은 그 프로그램을 목적 코드로 컴파일하는 거예요. prepared statement가 목적 코드예요. 그러면 sqlite3_step() 인터페이스가 목적 코드를 실행해 결과를 얻어요.

새 애플리케이션은 항상 sqlite3_prepare() 대신 sqlite3_prepare_v2()를 호출해야 해요. 오래된 sqlite3_prepare()는 하위 호환성을 위해 유지되지만, sqlite3_prepare_v2()가 훨씬 더 나은 인터페이스를 제공해요.

  • sqlite3_step() — 이 루틴은 sqlite3_prepare() 인터페이스로 이전에 만들어진 prepared statement를 평가하는 데 사용돼요. 문은 첫 번째 결과 행이 사용 가능해지는 지점까지 평가돼요. 두 번째 결과 행으로 전진하려면 sqlite3_step()을 다시 호출해요. 문이 완료될 때까지 sqlite3_step()을 계속 호출해요. 결과를 반환하지 않는 문(예: INSERT, UPDATE, DELETE)은 sqlite3_step() 단일 호출로 완료까지 실행돼요.

  • sqlite3_column() — 이 루틴은 sqlite3_step()으로 평가 중인 prepared statement의 결과 집합 현재 행에서 단일 컬럼을 반환해요. sqlite3_step()이 새 결과 집합 행으로 멈출 때마다 이 루틴을 여러 번 호출해서 그 행의 모든 컬럼 값을 찾을 수 있어요.

위에서 언급했듯 SQLite API에는 사실 "sqlite3_column()"이라는 함수는 없어요. 대신 여기서 "sqlite3_column()"이라 부르는 것은 다양한 데이터타입으로 결과 집합에서 값을 반환하는 전체 함수 계열의 플레이스홀더예요. 이 계열에는 (문자열이나 BLOB인 경우) 결과 크기와 결과 집합의 컬럼 수를 반환하는 루틴도 있어요. 그 계열의 루틴들로는 sqlite3_column_blob(), sqlite3_column_bytes(), sqlite3_column_bytes16(), sqlite3_column_count(), sqlite3_column_double(), sqlite3_column_int(), sqlite3_column_int64(), sqlite3_column_text(), sqlite3_column_text16(), sqlite3_column_type(), sqlite3_column_value()가 있어요.

  • sqlite3_finalize() — 이 루틴은 sqlite3_prepare() 호출로 만들어진 prepared statement를 파괴해요. 모든 prepared statement는 메모리 누수를 피하기 위해 이 루틴 호출로 파괴돼야 해요.

  • sqlite3_close() — 이 루틴은 sqlite3_open() 호출로 열린 데이터베이스 연결을 닫아요. 연결과 연관된 모든 prepared statement는 연결을 닫기 전에 finalize되어야 해요.

4. 핵심 루틴과 객체의 전형적인 사용법

애플리케이션은 보통 초기화 중에 sqlite3_open()으로 단일 데이터베이스 연결을 만들게 돼요. sqlite3_open()은 기존 데이터베이스 파일을 열거나 새 데이터베이스 파일을 만들어 열 때 모두 사용할 수 있다는 점에 주의해요. 많은 애플리케이션이 단일 데이터베이스 연결만 사용하지만, 같은 데이터베이스든 다른 데이터베이스든 여러 데이터베이스 연결을 열기 위해 sqlite3_open()을 여러 번 호출하지 못할 이유는 없어요. 때로 멀티스레드 애플리케이션이 스레드마다 별도의 데이터베이스 연결을 만들기도 해요. 단일 데이터베이스 연결은 ATTACH SQL 명령을 사용해 두 개 이상의 데이터베이스에 접근할 수 있으므로, 데이터베이스 파일마다 별도 연결이 필요하지 않다는 점에 주의해요.

많은 애플리케이션은 종료 시 sqlite3_close() 호출로 데이터베이스 연결을 파괴해요. 또는 예를 들어 SQLite를 애플리케이션 파일 형식으로 사용하는 애플리케이션은 File/Open 메뉴 동작에 응답해 데이터베이스 연결을 열고, File/Close 메뉴에 응답해 해당 연결을 파괴할 수 있어요.

SQL 문을 실행하려면 애플리케이션은 다음 단계를 따라요:

  • sqlite3_prepare()로 prepared statement를 만들어요.
  • sqlite3_step()을 한 번 이상 호출해 prepared statement를 평가해요.
  • 쿼리의 경우 sqlite3_step() 두 호출 사이에 sqlite3_column()을 호출해 결과를 추출해요.
  • sqlite3_finalize()로 prepared statement를 파괴해요.

SQLite를 효과적으로 사용하기 위해 실제로 알아야 할 것은 전부 위 내용이에요. 나머지는 모두 최적화와 세부 사항이죠.

5. 핵심 루틴 주변의 편의 래퍼

sqlite3_exec() 인터페이스는 위 네 단계를 모두 단일 함수 호출로 수행하는 편의 래퍼예요. sqlite3_exec()에 전달된 콜백 함수는 결과 집합의 각 행을 처리하는 데 사용돼요. sqlite3_get_table()은 위 네 단계를 모두 수행하는 또 다른 편의 래퍼예요. sqlite3_get_table() 인터페이스는 콜백을 호출하는 대신 쿼리 결과를 힙 메모리에 저장한다는 점에서 sqlite3_exec()와 달라요.

sqlite3_exec()sqlite3_get_table()도 핵심 루틴으로 할 수 없는 일을 하지 않는다는 점을 깨닫는 게 중요해요. 사실 이 래퍼들은 순전히 핵심 루틴들로 구현되어 있어요.

6. 파라미터 바인딩과 prepared statement 재사용

앞선 논의에서는 각 SQL 문이 한 번 준비되고, 평가되고, 파괴된다고 가정했어요. 하지만 SQLite는 같은 prepared statement를 여러 번 평가할 수 있게 해줘요. 이는 다음 루틴들로 이루어져요:

  • sqlite3_reset()
  • sqlite3_bind()

prepared statement가 sqlite3_step() 호출 한 번 이상으로 평가된 후에는 sqlite3_reset() 호출로 다시 평가하기 위해 리셋할 수 있어요. sqlite3_reset()을 prepared statement 프로그램을 시작 지점으로 되감는 것으로 생각해보세요. 기존 prepared statement에 sqlite3_reset()을 사용하는 것은 새 prepared statement를 만드는 것보다 불필요한 sqlite3_prepare() 호출을 피할 수 있어요. 많은 SQL 문에서 sqlite3_prepare() 실행에 필요한 시간은 sqlite3_step()에 필요한 시간과 같거나 더 커요. 그래서 sqlite3_prepare() 호출을 피하면 상당한 성능 향상을 얻을 수 있어요.

정확히 같은 SQL 문을 두 번 이상 평가하는 것은 일반적으로 별로 유용하지 않아요. 더 자주, 비슷한 문을 평가하고 싶을 거예요. 예를 들어 다른 값으로 INSERT 문을 여러 번 평가하고 싶을 수도 있고, WHERE 절의 다른 키로 같은 쿼리를 여러 번 평가하고 싶을 수도 있어요. 이를 지원하기 위해 SQLite는 SQL 문이 평가되기 전에 값에 "바인딩"되는 파라미터를 포함할 수 있게 해줘요. 이 값들은 나중에 바꿀 수 있고, 같은 prepared statement를 새 값으로 두 번째 평가할 수 있어요.

SQLite는 쿼리나 데이터 수정 문(DQL 또는 DML)에서 문자열 리터럴, blob 리터럴, 숫자 상수, NULL이 허용되는 곳 어디든 파라미터를 허용해요. (파라미터는 컬럼이나 테이블 이름, 또는 제약 조건이나 기본값의 값으로는 사용할 수 없어요. DDL이죠.) 파라미터는 다음 형태 중 하나를 가져요:

  • ?
  • ?NNN
  • :AAA
  • $AAA
  • @AAA

위 예제에서 NNN은 정수 값이고 AAA는 식별자예요. 파라미터는 초기에 NULL 값을 가져요. sqlite3_step()을 처음 호출하기 전이나 sqlite3_reset() 직후에, 애플리케이션은 sqlite3_bind() 인터페이스를 호출해 파라미터에 값을 붙일 수 있어요. sqlite3_bind() 호출마다 같은 파라미터의 이전 바인딩을 덮어써요.

애플리케이션은 여러 SQL 문을 미리 준비하고 필요할 때 평가할 수 있어요. 대기 중인 prepared statement 수에 임의 제한은 없어요. 일부 애플리케이션은 시작 시 sqlite3_prepare()를 여러 번 호출해 앞으로 필요할 모든 prepared statement를 만들어요. 다른 애플리케이션은 가장 최근에 사용한 prepared statement의 캐시를 유지하고, 가능하면 캐시에서 prepared statement를 재사용해요. 또 다른 접근법은 루프 안에 있을 때만 prepared statement를 재사용하는 거예요.

7. SQLite 구성

SQLite의 기본 구성은 대부분의 애플리케이션에 아주 잘 동작해요. 하지만 개발자들은 조금 더 성능을 짜내거나 흔하지 않은 기능을 활용하기 위해 설정을 조정하고 싶을 때가 있어요.

sqlite3_config() 인터페이스는 SQLite의 전역적이고 프로세스 전반적인 구성 변경을 하는 데 사용돼요. sqlite3_config() 인터페이스는 데이터베이스 연결을 만들기 전에 호출되어야 해요. sqlite3_config() 인터페이스는 프로그래머가 다음과 같은 일을 할 수 있게 해줘요:

  • 안전이 중요한 실시간 임베디드 시스템에 적합한 대체 메모리 할당자와 애플리케이션 정의 메모리 할당자를 설정하는 것을 포함해, SQLite가 메모리 할당을 하는 방식을 조정해요.
  • 프로세스 전반적인 오류 로그를 설정해요.
  • 애플리케이션 정의 페이지 캐시를 지정해요.
  • 다양한 스레딩 모델에 적합하도록 mutex 사용을 조정하거나, 애플리케이션 정의 mutex 시스템으로 대체해요.

프로세스 전반적인 구성이 완료되고 데이터베이스 연결이 만들어진 후에는 개별 데이터베이스 연결을 sqlite3_limit()sqlite3_db_config() 호출로 구성할 수 있어요.

8. SQLite 확장

SQLite는 기능을 확장하는 데 사용할 수 있는 인터페이스를 포함해요. 그런 루틴으로는 다음이 있어요:

  • sqlite3_create_collation()
  • sqlite3_create_function()
  • sqlite3_create_module()
  • sqlite3_vfs_register()

sqlite3_create_collation() 인터페이스는 텍스트를 정렬하기 위한 새 collating sequence를 만드는 데 사용돼요. sqlite3_create_module() 인터페이스는 새 가상 테이블 구현을 등록하는 데 사용돼요. sqlite3_vfs_register() 인터페이스는 새 VFS를 만들어요.

sqlite3_create_function() 인터페이스는 새 SQL 함수(스칼라 또는 집계)를 만들어요. 새 함수 구현은 보통 다음 추가 인터페이스를 사용해요:

  • sqlite3_aggregate_context()
  • sqlite3_result()
  • sqlite3_user_data()
  • sqlite3_value()

SQLite의 모든 내장 SQL 함수는 정확히 이 동일한 인터페이스로 만들어져요. 예제를 보려면 SQLite 소스 코드, 특히 date.c와 func.c 소스 파일을 참고해요.

공유 라이브러리나 DLL은 SQLite의 로더블 확장으로 사용할 수 있어요.

9. 기타 인터페이스

이 문서는 가장 중요하고 가장 흔히 사용되는 SQLite 인터페이스만 언급해요. SQLite 라이브러리는 여기서 설명하지 않은 유용한 기능을 구현한 많은 다른 API를 포함해요. SQLite 애플리케이션 프로그래밍 인터페이스를 구성하는 함수들의 완전한 목록은 C/C++ Interface Specification에서 찾을 수 있어요. 모든 SQLite 인터페이스에 대한 완전하고 권위 있는 정보는 그 문서를 참고해요.

더 알아보기 (Learn more)