【4】データベース関数ライブラリを自分で書く—レコードを取得する
6685 ワード
関数db_fetchはユーザインタフェースであり、入力されたキー値に基づいて対応するデータを探し、見つかった場合、データを指すポインタを返し、そうでなければNULLを返し、コードは以下の通りである.
この関数の最後にun_を使用しました.ロックマクロ関数はロックを解除します.db_find_and_lock関数でロックが得られた.db_fetch関数の大まかな流れは、受信したkeyに基づいて呼び出すことです.db_find_and_lockは対応するインデックスレコードを検索し、見つかったら呼び出しを続行します.db_readdat対応するデータレコードが読み込まれ、見つからない場合は呼び出されません.db_readdatat関数ではなく、NULLを直接返します.
したがって、ユーザはdb_を呼び出しているfetchの場合は必ず戻り値を判断します.使用中に戻り値を判断せずに戻ったポインタを直接印刷した結果、見つからない場合にSegmentation faultが発生しました.
関数_db_find_and_lockはキーです.次はコードです.
_db_find_and_lock関数は、受信したkeyに基づいてハッシュ値を計算し、ハッシュ・リスト内のチェーン・テーブルにナビゲートし、チェーン・テーブル・ヘッダから各レコードを後方に遍歴し、あるレコードが保存したkeyと受信したkeyが等しいか、チェーン・テーブルの末尾まで.ハッシュ関数は次のとおりです.
以下に紹介します.db_find_and_lockが呼び出した相関関数.まずは_db_readptr、コードは以下の通りです.
すべてのレコードは文字としてファイルに保存されるため、ポインタ値を取得するには文字列タイプを数値タイプに変換する必要があります.上記の関数がこの役割です.オフセット量によってポインタの位置がわかり、この文字列を読み出し、文字列を数値タイプに変換して返します.
次は_db_readidx関数:
からdb_readptrはインデックスレコードオフセット量を知り、呼び出し_db_readidx関数は、ファイルからインデックスを読み込んでbufferに記録します.このbufferは実際にはdb->idxbufです.この関数を呼び出すと、DB構造体の多くのメンバーは、データオフセット量、データ長、インデックス記録長など、ある記録情報を記録します.データオフセット量とデータ長を知ることで、データファイルの実際のデータを読み取ることができます.これが関数です.db_readdatの役割:
ファイルに保存する場合、各データレコードは改行で終わるため、この関数はデータレコードを1つ読み込んだ後、改行を終了文字に変更し、ユーザーコードが戻ってきたデータを直接印刷するのに便利です.
/* */
char *db_fetch(DBHANDLE h, const char *key)
{
DB *db = h;
char *ptr;
/* :0 ,1 , */
if (_db_find_and_lock(db, key, 0) < 0)
{
/* -1 */
ptr = NULL;
db->cnt_fetcherr++;
}
else /* 0 */
{
ptr = _db_readdat(db); /* */
db->cnt_fetchok++;
}
if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
{
printf("%d
", __LINE__);
exit(-1);
}
return ptr;
}
この関数の最後にun_を使用しました.ロックマクロ関数はロックを解除します.db_find_and_lock関数でロックが得られた.db_fetch関数の大まかな流れは、受信したkeyに基づいて呼び出すことです.db_find_and_lockは対応するインデックスレコードを検索し、見つかったら呼び出しを続行します.db_readdat対応するデータレコードが読み込まれ、見つからない場合は呼び出されません.db_readdatat関数ではなく、NULLを直接返します.
したがって、ユーザはdb_を呼び出しているfetchの場合は必ず戻り値を判断します.使用中に戻り値を判断せずに戻ったポインタを直接印刷した結果、見つからない場合にSegmentation faultが発生しました.
関数_db_find_and_lockはキーです.次はコードです.
/* writelock:0 ,1
* :0 ,-1
*/
static int _db_find_and_lock(DB *db, const char *key, int writelock)
{
off_t offset, nextoffset;
/* , */
db->chainoff = (_db_hash(db, key) * PTR_SZ) + db->hashoff;
db->ptroff = db->chainoff; /* */
if (writelock) /* */
{
/* , */
if (writew_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
{
printf("%d
", __LINE__);
exit(-1);
}
}
else /* */
{
/* , */
if (readw_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
{
printf("%d
", __LINE__);
exit(-1);
}
}
/* , ,atol */
offset = _db_readptr(db, db->ptroff); /* , */
/* */
while (offset != 0) /* 0 */
{
nextoffset = _db_readidx(db, offset); /* db->idxbuf */
/* db->idxbuf = '\0' '\0' '\0' */
if (strcmp(db->idxbuf, key) == 0)
break;
db->ptroff = offset; /* ptroff */
offset = nextoffset;
}
return (offset == 0 ? -1 : 0); /* offset = 0 */
}
_db_find_and_lock関数は、受信したkeyに基づいてハッシュ値を計算し、ハッシュ・リスト内のチェーン・テーブルにナビゲートし、チェーン・テーブル・ヘッダから各レコードを後方に遍歴し、あるレコードが保存したkeyと受信したkeyが等しいか、チェーン・テーブルの末尾まで.ハッシュ関数は次のとおりです.
/* */
static DBHASH _db_hash(DB *db, const char *key)
{
DBHASH hval = 0;
char c;
int i;
/* */
for (i = 1; (c = *key++) != 0; i++)
hval += c * i;
return (hval % db->nhash); /* db->nhash = 137 */
}
以下に紹介します.db_find_and_lockが呼び出した相関関数.まずは_db_readptr、コードは以下の通りです.
/* ,
* offset , ,
* : ,
*/
static off_t _db_readptr(DB *db, off_t offset)
{
char asciiptr[PTR_SZ + 1];
if (lseek(db->idxfd, offset, SEEK_SET) == -1)
{
printf("%d
", __LINE__);
exit(-1);
}
if (read(db->idxfd, asciiptr, PTR_SZ) != PTR_SZ) /* PTR_SZ = 6, 6 */
{
printf("%d
", __LINE__);
exit(-1);
}
asciiptr[PTR_SZ] = 0; /* , atol */
/* atol , */
return (atol(asciiptr));
}
すべてのレコードは文字としてファイルに保存されるため、ポインタ値を取得するには文字列タイプを数値タイプに変換する必要があります.上記の関数がこの役割です.オフセット量によってポインタの位置がわかり、この文字列を読み出し、文字列を数値タイプに変換して返します.
次は_db_readidx関数:
/* offset , DB
* :
*/
static off_t _db_readidx(DB *db, off_t offset)
{
ssize_t i;
char *ptr1, *ptr2;
char asciiptr[PTR_SZ + 1], asciilen[IDXLEN_SZ + 1];
struct iovec iov[2];
/*
* offset 0,
*/
if ((db->idxoff = lseek(db->idxfd, offset, offset == 0 ? SEEK_CUR : SEEK_SET)) == -1)
{
printf("%d
", __LINE__);
exit(-1);
}
/* , asciiptr , asciilen */
iov[0].iov_base = asciiptr;
iov[0].iov_len = PTR_SZ; /* 6 */
iov[1].iov_base = asciilen;
iov[1].iov_len = IDXLEN_SZ; /* 4 */
if ((i = readv(db->idxfd, &iov[0], 2)) != PTR_SZ + IDXLEN_SZ)
{
/* , -1 */
if (i == 0 && offset == 0)
return -1; /* db_nextrec ,_db_find_and_lock -1 */
else
{
printf("%d
", __LINE__);
exit(-1);
}
}
/* */
asciiptr[PTR_SZ] = 0;
db->ptrval = atol(asciiptr);
/* */
asciilen[IDXLEN_SZ] = 0;
if ((db->idxlen = atoi(asciilen)) < IDXLEN_MIN || db->idxlen > IDXLEN_MAX)
{
/* 6~1024 */
printf("%d
", __LINE__);
exit(-1);
}
/* , readv , key */
if ((i = read(db->idxfd, db->idxbuf, db->idxlen)) != db->idxlen)
{
printf("%d
", __LINE__);
exit(-1);
}
if (db->idxbuf[db->idxlen -1] != NEWLINE)
{
printf("%d
", __LINE__);
exit(-1);
}
db->idxbuf[db->idxlen - 1] = 0; /* , atol */
/* */
if ((ptr1 = strchr(db->idxbuf, SEP)) == NULL)
{
printf("%d
", __LINE__);
exit(-1);
}
*ptr1++ = 0; /* ,ptr1 */
if ((ptr2 = strchr(ptr1, SEP)) == NULL)
{
printf("%d
", __LINE__);
exit(-1);
}
*ptr2++ = 0; /* ,ptr2 */
if (strchr(ptr2, SEP) != NULL) /* */
{
printf("%d
", __LINE__);
exit(-1);
}
/* */
if ((db->datoff = atol(ptr1)) < 0) /* atol */
{
printf("%d
", __LINE__);
exit(-1);
}
/* , 2~1024 */
if ((db->datlen = atol(ptr2)) <= 0 || db->datlen > DATLEN_MAX)
{
printf("%d
", __LINE__);
exit(-1);
}
return (db->ptrval);
}
からdb_readptrはインデックスレコードオフセット量を知り、呼び出し_db_readidx関数は、ファイルからインデックスを読み込んでbufferに記録します.このbufferは実際にはdb->idxbufです.この関数を呼び出すと、DB構造体の多くのメンバーは、データオフセット量、データ長、インデックス記録長など、ある記録情報を記録します.データオフセット量とデータ長を知ることで、データファイルの実際のデータを読み取ることができます.これが関数です.db_readdatの役割:
/* db->datbuf
* :NULL
*/
static char* _db_readdat(DB *db)
{
if (lseek(db->datfd, db->datoff, SEEK_SET) == -1) /* */
{
printf("%d
", __LINE__);
exit(-1);
}
if (read(db->datfd, db->datbuf, db->datlen) != db->datlen) /* */
{
printf("%d
", __LINE__);
exit(-1);
}
if (db->datbuf[db->datlen - 1] != NEWLINE) /* */
{
printf("%d
", __LINE__);
exit(-1);
}
db->datbuf[db->datlen - 1] = 0; /* */
return (db->datbuf);
}
ファイルに保存する場合、各データレコードは改行で終わるため、この関数はデータレコードを1つ読み込んだ後、改行を終了文字に変更し、ユーザーコードが戻ってきたデータを直接印刷するのに便利です.