Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

SQLite 3

2011년 1월 현재 SQLite 최신 버전은 3.7.2 입니다. 이 문서는 SQLite3를 기준으로 합니다. SQLite2 버전 문서를 보기를 원한다면 SQLite 프로그래밍문서를 보시면 됩니다.

달라진 점

이름

SQLite2과 이름규칙이 달라졌습니다. sqlite2에서 sqlite_이 접두사로 사용되던 것이 sqlite3로 넘어오면서 sqlite3_로 바꼈습니다.

DB 형식

데이터 베이스 파일 형식도 바꼈습니다. 그래서 sqlite2에서 만든 데이터베이스 파일은 sqlite3에서 읽을 수 없습니다. 때문에 다음과 같은 방법으로 데이터 베이스를 옮겨야 합니다.
# sqlite OLD.db .dump | sqlite3 NEW.db

헤더파일

헤더파일도 sqlite.h에서 sqlite3.h로 바꼈습니다.

링크

-lsqlite 대신 -lsqlite3를 사용해야 합니다.

64-bit ROWID

sqlite는 엔진에서 insert 레코드에 물리적인 일련 번호를 부여합니다. 일련번호를 부여하기 위한 방법을 고민해 보신적이 있을 겁니다. rowid 덕분에 sqlite에서는 유일한 일련번호를 얻기 위한 고민을 할 필요가 없습니다. 참고로 만약 테이블의 컬럼을 INTEGER PRIMARY KEY형식으로 하면 rowid를 가리킵니다.

이 크기가 32bit에서 64bit로 바꼈습니다.

improved concurrency

sqlite2 버전은 동시에 여러 유저가 읽을 수 있도록 허용했으나 쓰는 것은 허용하지 않았습니다. sqlite3는 읽기와 쓰기 모두에 동시 접근을 허용합니다.

UTF-8과 UTF-16 지원

UTF-8과 UTF-16 모두를 지원한다는 군요. 좋은 세상입니다. ?

프로그래밍 형식

callback 호출 방식

callback 함수를 호출하면 좀더 쉽게 레코드를 다룰 수 있습니다. row와 column단위로 데이터를 처리한다음에 이에 대한 포인터를 callback의 매개 변수로 넘겨주기 때문입니다.

#include <stdio.h>
#include <sqlite3.h>
#include <unistd.h>
#include <stdlib.h>

#include <string>
#include <iostream>

static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
  int i;
  for(i=0; i<argc; i++)
  {
    printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
  }
  printf("\n");
  return 0;
}

int main(int argc, char **argv)
{
  int rc;
  sqlite3 *mdb;
  rc = sqlite3_open("test.db", &mdb);
  if(rc != SQLITE_OK)
  {
    return 1;
  }
  rc = sqlite3_exec(mdb, "select * from session", callback, 0, NULL);
  if(rc != SQLITE_OK)
  {
    return 1;
  }

  sqlite3_close(mdb);
}

직접 다루기

콜백함수를 호출하면 간단하게 데이터를 다룰 수 있습니다. 하지만 static 함수를 호출하기 때문에, c++등에서 추상화 단계를 밟으려면 애로사항이 꽃핍니다.

저는 sqlite 클래스를 만들고, 데이터 베이스를 필요로 하는 하는 클래스는 sqlite 클래스를 상속받아서 사용하길 원했습니다. 이 경우 callback 함수를 사용하는 것은 좋은 선택이 아닙니다.

그래서 낮은 수준에서 직접 다루기 원했습니다.
#include <sqlite3.h>
#include <string.h>
#include <stdio.h>

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Sqlite
{
private:
	sqlite3 *mdb;
	string mdbname;
	sqlite3_stmt *stmt;
	vector<string> mRow;
	bool classCheckFlag;

public:
	Sqlite(string adbname)
	{
		int rc;
		classCheckFlag = false;
		mdbname = adbname;
		// DB 열기
		rc = sqlite3_open(mdbname.c_str(), &mdb);
		if(rc == SQLITE_OK)
		{
			classCheckFlag = true;
		}
	}

	~Sqlite()
	{
		sqlite3_close(mdb); 
	}

	bool ClassCheck()
	{
		return classCheckFlag;
	}
	
	
	int Exec(string sql)
	{
		int rc;

		// sql을 실행한다.
		rc = sqlite3_prepare(mdb, sql.c_str(), sql.length(), &stmt, NULL);
		if(rc != SQLITE_OK)
		{
			return 1;
		}
		return 0;
	}
	
	vector<string> GetRows()
	{
		int rc;
		int n;
		int i;
		char buf[80];
		mRow.clear();

		// sqlite3_step로 row 데이터를 하나씩 가져온다.
		rc = sqlite3_step(stmt);
		if(rc != SQLITE_DONE)
		{
			switch(rc)
			{
				case SQLITE_BUSY:
					break;
				case SQLITE_ERROR:
					break;
				// row 데이터를 가져왔다면
				// 컬럼 데이터를 읽어온다. 
				// 귀찮아서 컬럼 타입에 관계 없이 전부 string으로 넣어버렸다.
				case SQLITE_ROW:
					n = sqlite3_column_count(stmt);
					for(i = 0; i < n ; i++)
					{
						switch(sqlite3_column_type(stmt, i))
						{
							case SQLITE_TEXT:
								mRow.push_back((const char *)sqlite3_column_text(stmt, i));
								break;
							case SQLITE_INTEGER:
								sprintf(buf, "%d", sqlite3_column_int(stmt, i));
								mRow.push_back(buf);
								break;
							case SQLITE_FLOAT:
								sprintf(buf, "%f", sqlite3_column_double(stmt, i));
								mRow.push_back(buf);
								break;
							case SQLITE_NULL:
								mRow.push_back("");
								break;
							default:
								cout << "Unknown Field" << endl;
							break;
						}
					}
					break;
				case SQLITE_MISUSE:
					break;
				default:
					break;
			}
		}
		else
		{
			cout << "SQL Exec Complete" << endl;
		}
		return mRow;
	}
	
	void ExecFree()
	{
		sqlite3_finalize(stmt);
	}
 
	int Exec(string sql, int (*callback)(void*,int,char**,char**))
	{
		int rc;
		rc = sqlite3_exec(mdb, sql.c_str(), callback, 0, NULL);
		if(rc != SQLITE_OK)
		{
			return 1; 
		}
		return 0; 
	}
};

int main()
{
	vector<string> Row;
	Sqlite mySql("test.db");
	if(!mySql.ClassCheck())
	{
		return 1;
	}

	mySql.Exec("select * from address");
	int i;
	for(;;)
	{
		Row = mySql.GetRows();
		if(Row.size() > 0)
		{
			for(i = 0; i < Row.size(); i++)
			{
				cout << Row[i] << "  ";
			}
			cout << endl;
		}
		else
		{
			break;
		}
	}
	mySql.ExecFree();
}