/*------------------------------------------------------------------------- * * querycache.c * Query Cache for PostgreSQL * * NOTES * * インスパイヤ・ライセンス * * 配布条件: * * 以下に示す条件の下、各自が自由にインスパイヤできる。 * * (1)このヘッダーファイルを必ず添付し、直接的にインスパイヤされたリスペクトファイルと、 * 以下に示すインスパイヤ度を新たに更新する。 * * リスペクト・ファイル www.buena-idea.net/postgresql/querycache/querycache.c * * インスパイヤ度 = max(0, org - dif) / org * * ここで * org => リスペクトファイルの行数 - このヘッダー部分の行数 * dif => 次のスクリプトで得た、追加による変更箇所の行数 * "diff -uibw querycache.c.orig querycache.c | grep "^+[^+]" | wc -l" * * * Ex: 関数名をstore_data からdata_storeに変更すると、以下に示すように5行が変更されたことになる。 * * * # diff -uibw querycache.c querycache.c.orig | grep "^+[^+]" * +static bool store_data (void); * + create_qc_query_space (); * + ret = store_data (); * +store_data (void) * + * This function is called from only store_data (). * # diff -uibw querycache.c.orig querycache.c | grep "^+[^+]" | wc -l * 5 * * dif = 5。 * リスペクトファイルの総行数は3149で、このヘッダーは77行なのでorg = 3149-77 = 3072、 * インスパイヤ度は (3072 - 5)/ 3072 = 0.9983 => 99.8372396%である。 * * * * (2)Aが作ったファイルをリスペクトしたBは、Aのファイルをリスペクトファイルと明記しなければならない。 * BがAをリスペクトして作ったファイルを、Cがさらにリスペクトする場合は、CはBのファイルのみリスペクトすればよい。 * * (3)インスパイヤ度が0となった時点で、そのファイルのインスパイヤ・ライセンスは消失する。 * * * ライセンスの適用地域: * カタカナ文化圏のみ有効。 * 英語を含む他の言語圏へ輸出する場合は、このヘッダファイルを削除した上で、ライセンスフリーとする。 * 理由は、"インスパイヤ"に相当する用語への翻訳が不可能なため。 * * ライセンスの適用における問題点と、現時点での対処: * diff による変更行数の検出には問題があり、例えばindentでファイル整形するとか、 * 関数定義を並び換えるだけで、インスパイヤ度が0%になってしまう。 * このような巧妙なインスパイヤに対して、Ave×ライセンスを策定準備中であるが、商標の問題から * 実現には長い時間がかかると予想されるため、上に示したインスパイヤ方法も、許容することとする。 * * リミックス・ライセンスとの関係: * インスパイヤ・ライセンスは、リミックス・ライセンスに含まれるので、 * リミックス・ライセンスの下でソースをサンプリングするのは自由。 * ここでリミックス・ライセンスとは、適当に部分部分を摘んで新しいファイルにカット&ペイストしたものを * 新たに作品と呼ぶことを保証するライセンス。カット&ペイストは条文中ではサンプリングといわれる。 * * /\ /\ * ( ^ A^) * ( ) * | | | * (___)__) * インスパイヤ・ライセンスのキャラクターは、"モナー"をインスパイヤしたものです。 * リスペクト先 : 2ch.net * インスパイヤ度 = 99% *------------------------------------------------------------------------- */ /****************************************************** TODO: (1)メモリ領域の確認 write_block , read_block の領域確認。高効率化。 ShmemIsValid() CreateContext()とかも。 (2)user名, db名の取得 (3)internal_flush()内でのdown原因究明 LOG: could not send data to client: けっこう不安定。 (4)LOCK: EXCLUSIVE, SHARED の確認 (5)Transaction対応 処理が大味で論理的に正しいか疑問。 savepoint未対応。 (6) ********************************************************/ #define __CONTEXT__ //#define __TRANSACTION_CONTEXT__ #define __QC_TX__ #ifndef _UNIT_TEST_ #include "postgres.h" #include "storage/shmem.h" #include "storage/lwlock.h" #include "libpq/pqcomm.h" #include "utils/memutils.h" #include "utils/guc.h" #endif #include "querycache.h" #include #include #include #ifndef offsetof #define offsetof(type, mem) ((size_t) \ ((char *)&((type *)0)->mem - (char *)(type *)0)) #endif /* --------------------------------------------- * private variables * --------------------------------------------- */ /* * Shared Memory */ static QueryCache_Header *QueryCacheHeader = NULL; static char* QueryCacheOidSpace = NULL; static char* QueryCacheQuerySpace = NULL; /* * Local Memory */ #ifdef __CONTEXT__ #ifndef __TRANSACTION_CONTEXT__ #ifndef _UNIT_TEST_ static MemoryContext query_cache_context = NULL; #endif #endif #endif static query current_query; /* Temporary working area */ static query c_query_initial_template; /* Keep the data for the initialization of current_query. In order to raise the efficiency of initialization processing. */ static struct data_cell d_cell; /* The area where the first stream data is stored */ static struct data_cell d_cell_initial_template; #ifdef __QC_TX__ static Tx_info tx_info; /* */ #endif /* for debug */ #ifdef _UNIT_TEST_ static bool query_cache = true; static bool query_cache_local = true; static bool query_cache_check = true; static int query_cache_check_level = 1; static bid query_cache_oblock_num = MAX_OBLOCKS; static bid query_cache_qblock_num = MAX_QBLOCKS; static int query_cache_max_store_block_num = MAX_TOTAL_DATA; #endif /* --------------------------------------------- * macro * --------------------------------------------- */ #define Increment_AllQueries QueryCacheHeader->qc_all_query++ #define Increment_Store QueryCacheHeader->qc_store++ #define Increment_NotCached QueryCacheHeader->qc_not_cached++ #define Increment_Hit QueryCacheHeader->qc_hits++ /* ================================================================ * declaration for static function * ================================================================ */ static bool store_data (void); static bool delete_cache_data (void); static bool send_cache_data (void); static void create_header (void); static void init_header_data (void); static void init_qc_space_header_data (qc_space_header *ch, bid max); static void init_statistical_data (void); static void init_blocks (bid max_blocks, void *c, void (*set_block)(int *, block_type *, void *, bid *, bid *)); static void create_qc_oid_space (void); static void create_qc_query_space (void); static void set_initial_data_to_c_query_initial_template (char *user, char *db); static void init_current_query (query *c_query); static void set_initial_data_to_d_cell_initial_template (void); static void init_data_cell (data_cell *d_cell); #ifndef _UNIT_TEST_ #ifndef __TRANSACTION_CONTEXT__ static void create_query_cache_context (void); #endif #endif static void free_data_cells (void); static bid lookup_oid (Oid oid); static bid add_oid (Oid oid); static bid add_tqid (bid toid, bid qid); static void del_tqid (bid toid, bid tqid); static bool check_tqid (bid toid); static bool del_toid (bid toid); static bool del_all_qid (bid toid); static bid lookup_query (query *c_query); static bid add_query_info_to_query_block (void); static bid lookup_table_block (Oid oid); static void add_table_block (Oid oid, bid toid); static bool del_table_block (bid toid); static bid get_free_table_block (void); static void free_table_block (bid id); static void add_tqid_to_table_block (bid toid, bid qid, bid tqid); static void get_prev_table_block (bid id, bid *prev); static void get_next_table_block (bid id, bid *next); static void set_table_block (bid id, block_type type, Oid oid, bid prev, bid next); static void set_prev_table_block (bid id, bid prev); static void set_next_table_block (bid id, bid next); static void set_qid_to_table_block (bid toid, bid tqid, bid prev, bid next); static void set_qid_to_table_block_prev (bid id, bid prev); static void set_qid_to_table_block_next (bid id, bid next); static void get_qid_from_tqid (bid tqid, bid *qid); static void get_prev_tqid (bid id, bid *prev); static void get_next_tqid (bid id, bid *next); static void write_table_block (oblock *ob, bid id); static void read_table_block (oblock *ob, bid id); static bool compare_query (const qblock *qb, const query *c_query); static bid lookup_query_block (query *c_query); static void add_query_block (query *c_query, bid qid); static void del_query_block (bid qid); static bid get_free_query_block (void); static void free_query_block (bid qid); static void get_prev_query_block (bid id, bid *prev); static void get_next_query_block (bid id, bid *next); static void get_next_qid_query_block (bid id, bid *next); static void set_query_block (bid qid, block_type type, query *c_query, bid prev, bid next); static void add_did (bid qid, bid did, const unsigned char *data, int len); static void del_all_did (bid qid); static void set_prev_query_block (bid id, bid prev); static void set_next_query_block (bid id, bid next); static void get_oid_from_table_block (bid toid, Oid *oid); static void get_oid_num_from_query_block (bid qid, int *num); static void get_toid_by_index_from_query_block (bid qid, int i, bid *toid); static void get_tqid_by_index_from_query_block (bid qid, int i, bid *tqid); static void get_key_from_query_block (bid qid, bid *key); static void write_query_block (qblock *qb, bid id); static void read_query_block (qblock *qb, bid id); static void set_current_query (const char *commandTag, const char *query_string, int len); static void set_qid_to_current_query (bid qid); static void set_oid_to_current_query (Oid oid); static void convert_oid_to_qid_table_block (bid qid, int i, bid toid, bid tid); static void set_cond_to_current_query (enum query_cond cond); static unsigned char * get_data_from_current_query (int cell, int no, int *len); static bid get_max_necessary_num_of_table_blocks (void); static bid get_necessary_num_of_query_blocks (void); #ifdef __QC_TX__ static void init_tx_info (void); static void add_tx_info (Oid oid); static void free_tx_info (void); #endif static hkey table_hashkey (Oid oid); static hkey query_hashkey (const char *query); static size_t header_size (void); static size_t qc_oid_space_size (void); static size_t qc_query_space_size (void); static bid get_free_table_block_num (void); static bid get_free_query_block_num (void); static void _check_blocks (void); static void _check_qblocks (void); static void _check_header (void); static void _check_statistics (void); /* ================================================================ * dummy function * ================================================================ */ #ifdef _UNIT_TEST_ /* * This is dummy. As for this routine you use just for debugging. * The routine which actually is used is defined with "libpq/pqcomm.c". */ static int qc_pq_flush (const unsigned char *data, int len) { fprintf (stderr, "qc_pq_flush (\"%s\", %d)\n", data, len); return len; } #endif /* ================================================================ * main routines * ================================================================ */ /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ void InitQueryCacheShmem(void) { #ifdef _UNIT_TEST_ query_cache_oblock_num = MAX_OBLOCKS; query_cache_qblock_num = MAX_QBLOCKS; #endif if (query_cache) { create_header (); create_qc_oid_space (); create_qc_query_space (); } } /* * Reset All data of QueryCacheHeader, OidSpace, and QuerySpace */ void qc_reset (void) { query c_query; /* */ if (query_cache == false) return; /* */ init_header_data (); /* */ init_blocks (query_cache_oblock_num, (Oid)0, (void (*)(int *, block_type *, void *, bid *, bid *))(set_table_block)); /* */ init_current_query (&c_query); init_blocks (query_cache_qblock_num, &c_query, (void (*)(int *, block_type *, void *, bid *, bid *))(set_query_block)); } /* * */ int QueryCacheShmemSize (void) { if (query_cache) return (int)(header_size () + qc_oid_space_size () + qc_query_space_size ()); return 0; } /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ void qc_init (void) { if (query_cache) { set_initial_data_to_c_query_initial_template ("postgres", "sample"); set_initial_data_to_d_cell_initial_template (); #ifdef __CONTEXT__ #ifndef __TRANSACTION_CONTEXT__ #ifndef _UNIT_TEST_ create_query_cache_context (); #endif #endif #endif } } /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ bool qc_check_query (const char *commandTag, const char *query_string) { bid qid; int query_string_len; /* * */ init_current_query (¤t_query); /* Check: query_string length check */ query_string_len = strlen (query_string); if (PQ_QUERY_LEN < query_string_len) { set_cond_to_current_query (QC_THROUGH); return false; } /* * Initialized current_query, after that, query_string, hash_key, * and commandTag are set to current_query. */ set_current_query (commandTag, query_string, query_string_len); Increment_AllQueries; /* * */ // SELECT command if (strcmp (commandTag, "SELECT") == 0) { #ifdef __QC_TX__ /* Check: transaction or not */ ////****//// if (tx_info.mode == QC_TX) if (tx_info.next != NULL) { set_cond_to_current_query (QC_NO_CACHE); return false; } #endif #ifndef _UNIT_TEST_ LWLockAcquire(QueryCacheLock, LW_SHARED); // LWLockAcquire(QueryCacheLock, LW_EXCLUSIVE); #endif /* * Check: number of free query block */ if (get_free_query_block_num () < query_cache_max_store_block_num) { Increment_NotCached; set_cond_to_current_query (QC_THROUGH); #ifndef _UNIT_TEST_ LWLockRelease(QueryCacheLock); #endif return false; } /* * */ qid = lookup_query (¤t_query); if (qid != (bid)Nil) { /* query_string has been registered already. */ Increment_Hit; set_cond_to_current_query (QC_SEND_CACHE); set_qid_to_current_query (qid); return true; } else { /* query_string is not registered yet. */ #ifndef _UNIT_TEST_ LWLockRelease(QueryCacheLock); #endif set_cond_to_current_query (QC_STORE_DATA); Increment_Store; return false; } } else if (strcmp (commandTag, "UPDATE") == 0 || strcmp (commandTag, "INSERT") == 0 || strcmp (commandTag, "DELETE") == 0 ) { set_cond_to_current_query (QC_DELETE_CACHE); return false; } #ifdef __QC_TX__ else if (strcmp (commandTag, "BEGIN") == 0 ) { set_cond_to_current_query (QC_NO_CACHE); init_tx_info (); return false; } else if (strcmp (commandTag, "COMMIT") == 0 || strcmp (commandTag, "ROLLBACK") == 0 ) { free_tx_info (); set_cond_to_current_query (QC_COMMIT); return false; } #endif // __QC_TX__ set_cond_to_current_query (QC_THROUGH); return false; } /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ /* ************************************************ * ************** NOT SAFE ! REWRITE ! ************ * ************************************************/ void qc_get_data_from_buffer_to_current_query (const unsigned char *data, int len) { int num, i; struct data_cell *dc, *last; #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContext oldcontext; #endif #endif #ifdef __QC_TX__ ////****//// if (tx_info.mode == QC_TX) if (tx_info.next != NULL) return; #endif /* * */ if (qc_get_cond_from_current_query () != QC_STORE_DATA) return; /* * main proccess */ if (query_cache_max_store_block_num <= (current_query.total_data_num - 1)) { Increment_NotCached; set_cond_to_current_query (QC_INVALID); return; } /* * */ num = current_query.total_data_num++; i = num % MAX_DATA; #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ #ifndef __TRANSACTION_CONTEXT__ oldcontext = MemoryContextSwitchTo(query_cache_context); #else oldcontext = MemoryContextSwitchTo(TopTransactionContext); #endif #endif #endif if (MAX_DATA < current_query.last->data_num) { #ifdef _UNIT_TEST_ if ((dc = (struct data_cell *) malloc (sizeof (struct data_cell))) == NULL) { fprintf (stderr, "ERROR: can't allocate memory.\n"); exit (0); } memset ((void *)dc, 0, sizeof (struct data_cell)); init_data_cell (dc); if (current_query.next == NULL) current_query.next = dc; if ((last = current_query.last) != NULL) last->next = dc; current_query.last = dc; #else #ifdef __CONTEXT__ if ((dc = (struct data_cell *) palloc (sizeof (struct data_cell))) == NULL) #else if ((dc = (struct data_cell *) malloc (sizeof (struct data_cell))) == NULL) #endif { /************************** **************************/ fprintf (stderr, "ERROR: can't allocate memory.\n"); exit (0); } MemSet ((void *)dc, 0, sizeof (struct data_cell)); init_data_cell (dc); if (current_query.next == NULL) current_query.next = dc; if ((last = current_query.last) != NULL) last->next = dc; current_query.last = dc; #endif } memcpy ((void *)current_query.last->data[i], (const void *)data, len); current_query.last->data_len[i] = len; current_query.last->data_num++; #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContextSwitchTo(oldcontext); #endif #endif } /* * */ bool qc_operate_cache (void) { query_cond cond; bool ret = true; /* */ cond = qc_get_cond_from_current_query (); /* * main proccess */ switch (cond) { case QC_STORE_DATA: #ifndef _UNIT_TEST_ LWLockAcquire(QueryCacheLock, LW_SHARED); //// LWLockAcquire(QueryCacheLock, LW_EXCLUSIVE); #endif ret = store_data (); #ifndef _UNIT_TEST_ LWLockRelease(QueryCacheLock); #endif break; case QC_DELETE_CACHE: #ifndef _UNIT_TEST_ LWLockAcquire(QueryCacheLock, LW_SHARED); //// LWLockAcquire(QueryCacheLock, LW_EXCLUSIVE); #endif ret = delete_cache_data (); #ifndef _UNIT_TEST_ LWLockRelease(QueryCacheLock); #endif break; case QC_SEND_CACHE: ret = send_cache_data (); #ifndef _UNIT_TEST_ LWLockRelease(QueryCacheLock); #endif break; #ifdef __QC_TX__ case QC_COMMIT: ret = true; break; #endif // __QC_TX__ case QC_THROUGH: case QC_INVALID: case QC_NO_CACHE: default: ret = true; break; } /* * */ set_cond_to_current_query (QC_END); /* To make sure */ return ret; } /* * TODO: error trap * * */ static bool store_data (void) { int i, max; bid qid, oid, tid, toid; bid freeqid; int len; unsigned char *data; #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContext oldcontext; #endif #endif /* * Check phase: */ /* Check : execute function or not. */ if (current_query.oid_num == 0) { return true; } /* Check : number of free_table_block and free_query_block.(To make sure.) */ if (get_free_table_block_num () <= get_max_necessary_num_of_table_blocks () || get_free_query_block_num () <= get_necessary_num_of_query_blocks ()) { Increment_NotCached; return false; } /* * Add query information from current_query to qc_query_space, and return qid. */ if ((qid = add_query_info_to_query_block ()) == (bid)Nil) { /* From now on, QueryCache is invalid. */ set_cond_to_current_query (QC_INVALID); return false; } /* * add result data to qc_query_space. */ #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ #ifndef __TRANSACTION_CONTEXT__ oldcontext = MemoryContextSwitchTo(query_cache_context); #else oldcontext = MemoryContextSwitchTo(TopTransactionContext); #endif #endif #endif max = current_query.total_data_num; for (i = 0; i < max; i++) { int cell_no, idx; /* */ cell_no = floor ((double)i/MAX_DATA); idx = i % MAX_DATA; /* */ data = get_data_from_current_query (cell_no, idx, &len); /* */ { unsigned char min_buff[QBLOCK_DATA_SIZE]; int min_len; int offset = 0; do { min_len = (QBLOCK_DATA_SIZE <= (len - offset)) ? QBLOCK_DATA_SIZE : (len - offset); memcpy ((void *)min_buff, (const void *)(offset + data), min_len); freeqid = get_free_query_block (); add_did (qid, freeqid, min_buff, min_len); offset += QBLOCK_DATA_SIZE; } while (offset < len); } } /* */ free_data_cells (); #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContextSwitchTo(oldcontext); #endif #endif /* * add qid(s) to qc_oid_space. */ for (i = 0; i < current_query.oid_num; i++) { oid = current_query.oid[i]; /* Still oid is not registered, if is, it registers anew */ if ((toid = lookup_oid (oid)) == (bid)Nil) toid = add_oid (oid); if (toid != (bid)Nil && (tid = add_tqid (toid, qid)) != (bid)Nil) /* */ convert_oid_to_qid_table_block (qid, i, toid, tid); else { return false; } } return true; } /* * All data which is related to table which is updated * by DML(Data Mnipulation Language: UPDATE,DELETE,INSERT,.. etc) are deleted * from the qc_oid_space and the qc_query_space. */ static bool delete_cache_data (void) { int i; Oid oid; bid toid; bool ret = true; /* * main loop. */ for (i = 0; i < current_query.oid_num; i++) { oid = current_query.oid[i]; if ((toid = lookup_oid (oid)) != (bid)Nil) /* * When at the stage which it deletes, error occurs even at one, * this function returns "false". */ if (del_toid (toid) == false) ret = false; } return ret; } /* * Send cached data to client. */ static bool send_cache_data (void) { qblock db; bid qid, next; /* * If current_query.cond is QC_SEND_CACHE, * qid is registered to qc_query_space by all means. */ qid = current_query.qid; #ifdef _UNIT_TEST_ assert (qid != (bid)Nil); #else Assert (qid != (bid)Nil); #endif /* Get first data block */ get_next_qid_query_block (qid, &next); /* * Send cached data. */ /*************** I don't know which is better. *************************/ /* * first version */ while (next != (bid)Nil) { read_query_block (&db, next); qc_pq_flush (db.did.data, db.did.data_len); next = db.did.next; } return true; /* * second version */ /** unsigned char buff[PQ_BUFFER_SIZE]; int offset = 0; int len = 0; while (next != (bid)Nil) { read_query_block (&db, next); len += db.did.data_len; if (len < PQ_BUFFER_SIZE) { memcpy ((void *)(buff + offset), (const void *)db.did.data, db.did.data_len); offset += db.did.data_len; } else { int remain = len - PQ_BUFFER_SIZE; memcpy ((void *)(buff + offset), (const void *)db.did.data, PQ_BUFFER_SIZE); qc_pq_flush (buff, len); if (remain != 0) memcpy ((void *)(buff), (const void *)db.did.data, remain); len = remain; offset = remain; } next = db.did.next; } if (len != 0) qc_pq_flush (buff, len); return true; */ } /* ================================================================ * functions for initialization * ================================================================ */ /* --------------------------------------------- * query cache header * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * The initialization routine of QueryCache Header */ static void create_header (void) { size_t size = header_size (); #ifndef _UNIT_TEST_ /* * Create query cache header */ QueryCacheHeader = (QueryCache_Header *) ShmemAlloc(size); if (QueryCacheHeader == NULL) ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("insufficient shared memory for query cache header"))); MemSet(QueryCacheHeader, 0, size); #else if ((QueryCacheHeader = malloc (size)) == NULL) exit (0); memset((void *)QueryCacheHeader, 0, size); #endif /* * Initialize: header data */ init_header_data (); } /* * The initialization routine of query cache header data */ static void init_header_data (void) { /* Initialize : qc_oid_space */ init_qc_space_header_data (&QueryCacheHeader->qc_oid_space, query_cache_oblock_num); /* Initialize : qc_query_space */ init_qc_space_header_data (&QueryCacheHeader->qc_query_space, query_cache_qblock_num); /* Initialize : Statistical data */ init_statistical_data (); } /* * The initialization routine for data of query cache header. */ static void init_qc_space_header_data (qc_space_header *ch, bid max) { bid i; ch->block_num = 0; ch->max_block_num = max; for (i = 0; i < HASH_SIZE; i++) { ch->ht[i].next = (bid)Nil; ch->ht[i].num = 0; } ch->next_free_block = (bid)1; } /* * The initialization routine of statistical query cache data */ static void init_statistical_data (void) { /* * Incompletion */ QueryCacheHeader->qc_all_query = 0; QueryCacheHeader->qc_store = 0; QueryCacheHeader->qc_not_cached = 0; QueryCacheHeader->qc_hits = 0; } /* --------------------------------------------- * * --------------------------------------------- */ /* * */ static void init_blocks (bid max_blocks, void *c, void (*set_block)(int *, block_type *, void *, bid *, bid *)) { bid i, prev, next; /* */ for (i = 1; i <= max_blocks; i++) { prev = (i == 1) ? (bid) Nil : i - 1; next = (i == max_blocks) ? (bid) Nil : i + 1; /**** **** Need memory check ****/ (*set_block)((int *)i, (block_type *)FREE, (void *)c, (bid *)prev, (bid *)next); } } /* ---------------------- * qc_oid_space * ---------------------- */ /* * */ static void create_qc_oid_space (void) { size_t size; size = qc_oid_space_size (); /* */ #ifndef _UNIT_TEST_ QueryCacheOidSpace = (char *) ShmemAlloc (size); #else QueryCacheOidSpace = malloc (size); #endif /* * */ if (QueryCacheOidSpace == NULL) { #ifndef _UNIT_TEST_ ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("insufficient shared memory for qc_oid_space"))); #else fprintf (stderr, "insufficient memory for qc_oid_space\n"); exit (0); #endif } #ifndef _UNIT_TEST_ MemSet(QueryCacheOidSpace, 0, size); #else memset(QueryCacheOidSpace, 0, size); #endif /* */ /// init_table_blocks (); init_blocks (query_cache_oblock_num, (Oid)0, (void (*)(int *, block_type *, void *, bid *, bid *))(set_table_block)); } /* ---------------------- * qc_query_space * ---------------------- */ /* * */ static void create_qc_query_space (void) { size_t size; query c_query; size = qc_query_space_size (); /* */ #ifndef _UNIT_TEST_ QueryCacheQuerySpace = (char *) ShmemAlloc (size); #else QueryCacheQuerySpace = malloc (size); #endif /* */ if (QueryCacheQuerySpace == NULL) { #ifndef _UNIT_TEST_ ereport(FATAL, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("insufficient shared memory for qc_query_space"))); #else fprintf (stderr, "insufficient memory for qc_query_space\n"); exit (0); #endif } #ifndef _UNIT_TEST_ MemSet(QueryCacheQuerySpace, 0, size); #else memset(QueryCacheQuerySpace, 0, size); #endif /* */ /// init_query_blocks (); init_current_query (&c_query); init_blocks (query_cache_qblock_num, &c_query, (void (*)(int *, block_type *, void *, bid *, bid *))(set_query_block)); } /* --------------------------------------------- * Working area * --------------------------------------------- */ /* ---------------------- * current_query * ---------------------- */ /* * */ static void set_initial_data_to_c_query_initial_template (char *user, char *db) { int i; #ifdef _UNIT_TEST_ memset ((void *) &c_query_initial_template, 0, sizeof (query)); #else MemSet ((query *) &c_query_initial_template, 0, sizeof (query)); #endif c_query_initial_template.qid = (bid)Nil; /* query string section */ c_query_initial_template.key = HASH_SIZE; strcpy (c_query_initial_template.query_string, ""); strcpy (c_query_initial_template.commandTag, ""); strcpy (c_query_initial_template.user_name, user); strcpy (c_query_initial_template.db_name, db); /* condition */ c_query_initial_template.cond = QC_NULL; /* attribute */ c_query_initial_template.attr = QC_QATR_NORMAL; /* related tables */ for (i = 0; i < MAX_OID; i++) c_query_initial_template.oid[i] = (bid)Nil; c_query_initial_template.oid_num = 0; /* result data section */ c_query_initial_template.total_data_num = 0; c_query_initial_template.next = &d_cell; c_query_initial_template.last = &d_cell; } /* * */ static void init_current_query (query *c_query) { memcpy ((void *)c_query, (const void *)&c_query_initial_template, sizeof (query)); } /* ---------------------- * data_cell * ---------------------- */ /* * */ static void set_initial_data_to_d_cell_initial_template (void) { int i; /* */ d_cell_initial_template.data_num = 0; /* */ for (i = 0; i < MAX_DATA; i++) { strcpy (d_cell_initial_template.data[i], ""); d_cell_initial_template.data_len[i] = 0; } /* */ d_cell_initial_template.next = NULL; } /* * */ static void init_data_cell (data_cell *d_cell) { memcpy ((void *)d_cell, (const void *)&d_cell_initial_template, sizeof (data_cell)); } /* --------------------------------------------- * memory context * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ #ifndef _UNIT_TEST_ static void create_query_cache_context (void) { #ifdef __CONTEXT__ #ifndef __TRANSACTION_CONTEXT__ query_cache_context = NULL; /* create context for query cache */ query_cache_context = /* * TODO: CHANGE arg values */ AllocSetContextCreate(TopMemoryContext, "query cached data cell ", ALLOCSET_DEFAULT_MINSIZE, sizeof (struct data_cell), sizeof (struct data_cell) * ceil((double) query_cache_max_store_block_num / MAX_DATA)); // ALLOCSET_DEFAULT_INITSIZE, // ALLOCSET_DEFAULT_MAXSIZE); #endif #endif } #endif /* * */ static void free_data_cells (void) // SIMPLE { struct data_cell *next, *new_next; /* */ next = d_cell.next; /* */ while (next != NULL) { new_next = next->next; #ifdef __CONTEXT__ #ifdef _UNIT_TEST_ free (next); #else pfree (next); #endif #else free (next); #endif next = new_next; } /* */ init_data_cell (&d_cell); } /* ================================================================ * basic operating routines for qc_oid/query_space * ================================================================ */ /* --------------------------------------------- * qc_oid_space * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ static bid lookup_oid (Oid oid) { return lookup_table_block (oid); } /* * */ static bid add_oid (Oid oid) { bid freeid; /* already exist */ if (lookup_oid (oid) != (bid)Nil) { return (bid)Nil; } /* */ if ((freeid = get_free_table_block ()) != (bid)Nil) add_table_block (oid, freeid); return freeid; } /* * */ static bid add_tqid (bid toid, bid qid) { bid tqid; /* */ if ((tqid = get_free_table_block ()) != (bid)Nil) add_tqid_to_table_block (toid, qid, tqid); return tqid; } /* * */ static void del_tqid (bid toid, bid tqid) { bid next; bid ptmp, ntmp; /* */ get_next_table_block (toid, &next); /* */ if (next == (bid)Nil) return; else if (next == tqid) { get_next_tqid (tqid, &ntmp); if (ntmp != (bid)Nil) set_qid_to_table_block_prev (ntmp, (bid)Nil); set_next_table_block (toid, ntmp); free_table_block (tqid); } else { get_prev_tqid (tqid, &ptmp); get_next_tqid (tqid, &ntmp); if (ptmp != (bid)Nil) set_qid_to_table_block_next (ptmp, ntmp); if (ntmp != (bid)Nil) set_qid_to_table_block_prev (ntmp, ptmp); free_table_block (tqid); } } /* * */ static bool check_tqid (bid toid) { bid next; get_next_table_block (toid, &next); if (next == (bid)Nil) return true; return false; } /* * */ static bool del_toid (bid toid) { #ifdef _UNIT_TEST_ assert (toid != (bid)Nil); #else Assert (toid != (bid)Nil); #endif /* delete all qid blocks */ if (del_all_qid (toid) == true) /* delete toid block */ if (del_table_block (toid) == true) { return true; } return false; } /* * */ static bool del_all_qid (bid toid) { bid next, new_next, qid; /* check next oid */ get_next_table_block (toid, &next); if (next == (bid)Nil) /* There isn't a next toid block. */ { return true; } /* * */ while (next != (bid)Nil) { get_next_tqid (next, &new_next); get_qid_from_tqid (next, &qid); if (qid != (bid)Nil) { int i; bid toid, tqid; int num; get_oid_num_from_query_block (qid, &num); for (i = 0; i < num; i++) { get_toid_by_index_from_query_block (qid, i, &toid); get_tqid_by_index_from_query_block (qid, i, &tqid); if (tqid != next) del_tqid (toid, tqid); if (check_tqid (toid) == true) del_table_block (toid); } } /* */ del_query_block (qid); free_table_block (next); next = new_next; /* set next iterator. */ }; return true; } /* --------------------------------------------- * qc_query_space * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ static bid lookup_query (query *c_query) { return lookup_query_block (c_query); } /* * add query infomation from current_query to query block, and return qid. */ static bid add_query_info_to_query_block (void) { bid qid; /* */ if (lookup_query (¤t_query) != (bid)Nil) { /* This query_string already exists in the qc_query_space. */ return (bid)Nil; } /* * This function is called from only store_data (). * Therefore, it is guaranteed that there are free_query_blocks sufficiently. */ qid = get_free_query_block (); #ifdef _UNIT_TEST_ assert (qid != (bid)Nil); #else Assert (qid != (bid)Nil); #endif add_query_block (¤t_query, qid); return qid; } /* ================================================================ * primitive operating routines for qc_oid/query_space * ================================================================ */ /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * qc_oid_space * ---------------------- */ /* * */ static bid lookup_table_block (Oid oid) { Oid coid; bid toid; oblock ob; hkey key; key = table_hashkey (oid); if (QueryCacheHeader->qc_oid_space.ht[key].num == 0) { return (bid) Nil; } else { toid = QueryCacheHeader->qc_oid_space.ht[key].next; do { read_table_block (&ob, toid); coid = ob.tid.oid; if (coid == oid && ob.type == TID) { return toid; } toid = ob.next; } while (toid != (bid)Nil); } return (bid) Nil; } /* * */ static void add_table_block (Oid oid, bid toid) { hkey key; bid next; /* */ key = table_hashkey (oid); next = QueryCacheHeader->qc_oid_space.ht[key].next; QueryCacheHeader->qc_oid_space.ht[key].next = toid; /* */ if (QueryCacheHeader->qc_oid_space.ht[key].num == 0) set_table_block (toid, TID, oid, (bid)Nil, (bid)Nil); else set_table_block (toid, TID, oid, (bid)Nil, next); QueryCacheHeader->qc_oid_space.ht[key].num++; QueryCacheHeader->qc_oid_space.block_num++; } /* * */ static bool del_table_block (bid toid) { Oid oid; hkey key; bid ntmp, ptmp, next; /* */ get_oid_from_table_block (toid, &oid); key = table_hashkey (oid); next = QueryCacheHeader->qc_oid_space.ht[key].next; /* */ if (next == (bid)Nil) { return true; } /* */ get_next_table_block (toid, &ntmp); get_prev_table_block (toid, &ptmp); /* */ if (next == toid) if (QueryCacheHeader->qc_oid_space.ht[key].num == 1) QueryCacheHeader->qc_oid_space.ht[key].next = (bid)Nil; else { QueryCacheHeader->qc_oid_space.ht[key].next = ntmp; set_prev_table_block (ntmp, (bid)Nil); } else { set_prev_table_block (ntmp, ptmp); set_next_table_block (ptmp, ntmp); } free_table_block (toid); QueryCacheHeader->qc_oid_space.ht[key].num--; return true; } /* * */ static bid get_free_table_block (void) { bid freeid, nextid; if (get_free_table_block_num () < 1) { return (bid) Nil; } /* */ freeid = QueryCacheHeader->qc_oid_space.next_free_block; get_next_table_block (freeid, &nextid); /* */ QueryCacheHeader->qc_oid_space.next_free_block = nextid; // set_prev_table_block (nextid, (bid) Nil); /* this operation is unnecessary. */ return freeid; } /* * */ static void free_table_block (bid id) { bid freeid; /* */ freeid = QueryCacheHeader->qc_oid_space.next_free_block; QueryCacheHeader->qc_oid_space.next_free_block = id; /* */ #ifdef _UNIT_TEST_ memset ((void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1)), 0, sizeof (oblock)); #else MemSet ((void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1)), 0, sizeof (oblock)); #endif /* */ set_table_block (id, FREE, (Oid) 0, (bid)Nil, freeid); /* */ QueryCacheHeader->qc_oid_space.block_num--; } /* * */ static void add_tqid_to_table_block (bid toid, bid qid, bid tqid) { bid next; /* */ get_next_table_block (toid, &next); if (next == (bid)Nil) set_qid_to_table_block (tqid, qid, (bid)Nil, (bid)Nil); else { set_qid_to_table_block_prev (next, tqid); set_qid_to_table_block (tqid, qid, (bid)Nil, next); } /* */ set_next_table_block (toid, tqid); QueryCacheHeader->qc_oid_space.block_num++; } /* * */ static void get_prev_table_block (bid id, bid *prev) //SIMPLE { memcpy((void *)prev, (const void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_prev), sizeof(bid)); } /* * */ static void get_next_table_block (bid id, bid *next) //SIMPLE { memcpy((void *)next, (const void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_next), sizeof(bid)); } /* * */ static void set_table_block (bid id, block_type type, Oid oid, bid prev, bid next) { oblock ob; read_table_block (&ob, id); ob.type = type; ob.tid.oid = oid; ob.next = (Oid)Nil; ob.prev = prev; ob.next = next; write_table_block (&ob, id); } /* * */ static void set_prev_table_block (bid id, bid prev) //SIMPLE { memcpy((void *)(QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_prev), (const void *) &prev, sizeof(bid)); } /* * */ static void set_next_table_block (bid id, bid next) //SIMPLE { memcpy((void *)(QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_next), (const void *) &next, sizeof(bid)); } /* * */ static void set_qid_to_table_block (bid toid, bid tqid, bid prev, bid next) { oblock ob; read_table_block (&ob, toid); ob.type = TQID; ob.qid.tqid = tqid; ob.qid.prev = prev; ob.qid.next = next; write_table_block (&ob, toid); } /* * */ static void set_qid_to_table_block_prev (bid id, bid prev) //SIMPLE { memcpy((void *)(QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_qid_prev), (const void *) &prev, sizeof(bid)); } /* * */ static void set_qid_to_table_block_next (bid id, bid next) //SIMPLE { memcpy((void *)(QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_qid_next), (const void *) &next, sizeof(bid)); } /* * */ static void get_qid_from_tqid (bid tqid, bid *qid) //SIMPLE { memcpy((void *)qid, (const void *) (QueryCacheOidSpace + sizeof(oblock) * (tqid - 1) + offset_oblock_qid_tqid), sizeof(bid)); } /* * */ static void get_prev_tqid (bid id, bid *prev) //SIMPLE { memcpy((void *)prev, (const void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_qid_prev), sizeof(bid)); } /* * */ static void get_next_tqid (bid id, bid *next) //SIMPLE { memcpy((void *)next, (const void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1) + offset_oblock_qid_next), sizeof(bid)); } /* * */ static void write_table_block (oblock *ob, bid id) //SIMPLE { memcpy((void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1)), (const void *)ob, sizeof(oblock)); } /* * */ static void read_table_block (oblock *ob, bid id) //SIMPLE { memcpy((void *)ob, (const void *) (QueryCacheOidSpace + sizeof(oblock) * (id - 1)), sizeof(oblock)); } /* --------------------------------------------- * qc_query_space * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ static bool compare_query (const qblock *qb, const query *c_query) { /* * */ if (strcmp (qb->qid.query, c_query->query_string) == 0 && strcmp (qb->qid.user_name, c_query->user_name) == 0 && strcmp (qb->qid.db_name, c_query->db_name) == 0) return true; return false; } /* ---------------------- * * ---------------------- */ /* * */ static bid lookup_query_block (query *c_query) { bid qid; qblock qb; hkey key; if ((key = c_query->key) == HASH_SIZE) { /* c_query->key is initial value. */ return (bid)Nil; } /* */ if (c_query->qid != (bid)Nil) { return c_query->qid; } /* */ if (QueryCacheHeader->qc_query_space.ht[key].num == 0) { return (bid) Nil; } else { /* * */ qid = QueryCacheHeader->qc_query_space.ht[key].next; /* */ do { read_query_block (&qb, qid); if (compare_query (&qb, c_query) == true) { return qid; } qid = qb.next; } while (qid != (bid)Nil); } return (bid) Nil; } /* * */ static void add_query_block (query *c_query, bid qid) { hkey key; bid next; /* */ key = (c_query->key != HASH_SIZE)? c_query->key : query_hashkey (c_query->query_string); c_query->qid = qid; /* */ next = QueryCacheHeader->qc_query_space.ht[key].next; QueryCacheHeader->qc_query_space.ht[key].next = qid; /* */ if (QueryCacheHeader->qc_query_space.ht[key].num == 0) set_query_block (qid, QID, c_query, (bid)Nil, (bid)Nil); else { set_prev_query_block (next, qid); set_query_block (qid, QID, c_query, (bid)Nil, next); } QueryCacheHeader->qc_query_space.ht[key].num++; QueryCacheHeader->qc_query_space.block_num++; } /* * */ static void del_query_block (bid qid) { hkey key; bid ntmp, ptmp, next; /* */ get_key_from_query_block (qid, &key); next = QueryCacheHeader->qc_query_space.ht[key].next; /* */ if (next == (bid)Nil) { return; } /* */ get_next_query_block (qid, &ntmp); get_prev_query_block (qid, &ptmp); /* */ if (next == qid) { if (QueryCacheHeader->qc_query_space.ht[key].num == 1) QueryCacheHeader->qc_query_space.ht[key].next = (bid)Nil; else { QueryCacheHeader->qc_query_space.ht[key].next = ntmp; set_prev_query_block (ntmp, (bid)Nil); } } else { if (ntmp != (bid)Nil) set_prev_query_block (ntmp, ptmp); if (ptmp != (bid)Nil) set_next_query_block (ptmp, ntmp); } /* */ del_all_did (qid); free_query_block (qid); QueryCacheHeader->qc_query_space.ht[key].num--; } /* * */ static bid get_free_query_block (void) { bid freeid, nextid; /* * If free_query_block doesn't exist, return (bid)Nil. */ if ((freeid = QueryCacheHeader->qc_query_space.next_free_block) != (bid)Nil) { get_next_query_block (freeid, &nextid); QueryCacheHeader->qc_query_space.next_free_block = nextid; // set_prev_query_block (nextid, (bid) Nil); /* this operation is unnecessary. */ } return freeid; } /* * */ static void free_query_block (bid qid) { bid freeid; query c_query; // freeid = QueryCacheHeader->qc_query_space.next_free_block; // QueryCacheHeader->qc_query_space.next_free_block = qid; // #ifdef _UNIT_TEST_ memset ((void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1)), 0, sizeof (qblock)); #else MemSet ((void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1)), 0, sizeof (qblock)); #endif init_current_query (&c_query); set_query_block (qid, FREE, &c_query, (bid)Nil, freeid); QueryCacheHeader->qc_query_space.block_num--; return; } /* * */ static void get_prev_query_block (bid id, bid *prev) //SIMPLE { memcpy((void *)prev, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (id - 1) + offset_qblock_prev), sizeof(bid)); } /* * */ static void get_next_query_block (bid id, bid *next) //SIMPLE { memcpy((void *)next, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (id - 1) + offset_qblock_next), sizeof(bid)); } /* * */ static void get_next_qid_query_block (bid id, bid *next) //SIMPLE { memcpy((void *)next, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (id - 1) + offset_qblock_qid_next), sizeof(bid)); } /* * */ static void set_query_block (bid qid, block_type type, query *c_query, bid prev, bid next) { qblock qb; int i; /* */ read_query_block (&qb, qid); /* */ qb.type = type; /* */ qb.qid.key = c_query->key; strcpy (qb.qid.query, c_query->query_string); qb.qid.attr = c_query->attr; strcpy (qb.qid.user_name, c_query->user_name); strcpy (qb.qid.db_name, c_query->db_name); /* */ for (i = 0; i < c_query->oid_num; i++) qb.qid.toid[i] = c_query->oid[i]; qb.qid.tid_num = c_query->oid_num; /* */ qb.qid.data_num = 0; qb.qid.next = (bid)Nil; qb.qid.last = (bid)Nil; /* */ qb.prev = prev; qb.next = next; /* */ write_query_block (&qb, qid); } /* * */ static void add_did (bid qid, bid did, const unsigned char *data, int len) { qblock qb, db, prev_qblock; bid qb_last; /* */ read_query_block (&qb, qid); read_query_block (&db, did); /* */ qb_last = qb.qid.last; /* */ db.type = DID; if (qb.qid.data_num != 0) { read_query_block (&prev_qblock, qb_last); prev_qblock.did.next = did; write_query_block (&prev_qblock, qb_last); } /* */ memcpy ((void *)db.did.data, (const void *)data, len); db.did.data_len = len; db.did.next = (bid)Nil; /* */ write_query_block (&db, did); /* */ if (qb.qid.data_num == 0) qb.qid.next = did; qb.qid.last = did; qb.qid.data_num++; /* */ write_query_block (&qb, qid); /* */ QueryCacheHeader->qc_query_space.block_num++; } /* * */ static void del_all_did (bid qid) { qblock qb, db; bid next; /* */ read_query_block (&qb, qid); next = qb.qid.next; /* * */ while (next != (bid)Nil) { read_query_block (&db, next); free_query_block (next); next = db.did.next; } /* */ qb.qid.data_num = 0; qb.qid.next = (bid)Nil; qb.qid.last = (bid)Nil; /* */ write_query_block (&qb, qid); } /* * */ static void set_prev_query_block (bid id, bid prev) //SIMPLE { memcpy((void *) (QueryCacheQuerySpace + sizeof(qblock) * (id - 1) + offset_qblock_next), (const void *)&next, sizeof(bid)); } /* * */ static void get_oid_from_table_block (bid toid, Oid *oid) //SIMPLE { memcpy((void *)oid, (const void *) (QueryCacheOidSpace + sizeof(oblock) * (toid - 1) + offset_oblock_tid_oid), sizeof(Oid)); } /* * */ static void get_oid_num_from_query_block (bid qid, int *num) //SIMPLE { memcpy((void *)num, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_qid_tid_num), sizeof(int)); } /* * */ static void get_toid_by_index_from_query_block (bid qid, int i, bid *toid) //SIMPLE { memcpy((void *)toid, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_qid_toid + sizeof(bid) * i), sizeof(bid)); } /* * */ static void get_tqid_by_index_from_query_block (bid qid, int i, bid *tqid) //SIMPLE { memcpy((void *)tqid, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_qid_tqid + sizeof(bid) * i), sizeof(bid)); } /* * */ static void get_key_from_query_block (bid qid, bid *key) //SIMPLE { memcpy((void *)key, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_qid_key), sizeof(bid)); } /* * */ static void write_query_block (qblock *qb, bid id) { memcpy((void *) (QueryCacheQuerySpace + sizeof(qblock) * (id - 1)), (const void *)qb, sizeof(qblock)); } /* * */ static void read_query_block (qblock *qb, bid id) { memcpy((void *)qb, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (id - 1)), sizeof(qblock)); } /* ================================================================ * Current_Query section * ================================================================ */ /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ enum query_cond qc_get_cond_from_current_query (void) //SIMPLE { return current_query.cond; } /* ---------------------- * * ---------------------- */ /* * Initialized current_query, after that, * query_string, hash_key, and commandTag are set to current_query. * This function is called from just qc_check_query (). */ static void set_current_query (const char *commandTag, const char *query_string, int len) { /* // this function is called immediately before (qc_check_query()), // here it does not execute. init_current_query (¤t_query); */ current_query.key = query_hashkey (query_string); strcpy (current_query.commandTag, commandTag); /* * With the function qc_check_query (), * it is guaranteed that length is shorter than PQ_QUERY_LEN, */ strncpy (current_query.query_string, query_string, len); } /* * */ static void set_qid_to_current_query (bid qid) //SIMPLE { current_query.qid = qid; } /* * */ static void set_oid_to_current_query (Oid oid) { int num = current_query.oid_num++; current_query.oid[num] = oid; #ifdef __QC_TX__ if (qc_get_cond_from_current_query () == QC_DELETE_CACHE) add_tx_info (oid); #endif // __QC_TX__ } /* * */ static void convert_oid_to_qid_table_block (bid qid, int i, bid toid, bid tid) { qblock qb; read_query_block (&qb, qid); qb.qid.toid[i] = toid; qb.qid.tqid[i] = tid; write_query_block (&qb, qid); } /* * */ static void set_cond_to_current_query (enum query_cond cond) //SIMPLE { current_query.cond = cond; } /* ---------------------- * * ---------------------- */ /* * */ bool qc_set_oid (Oid oid) { if (MAX_OID <= current_query.oid_num) { // From now on, QueryCache is invalid. set_cond_to_current_query (QC_INVALID); Increment_NotCached; return false; } set_oid_to_current_query (oid); return true; } /* * */ static unsigned char * get_data_from_current_query (int cell, int no, int *len) { int i; struct data_cell *next; next = &d_cell; for (i = 0; i < cell; i++) next = next->next; *len = next->data_len[no]; return (unsigned char *)next->data[no]; } /* * */ static bid get_max_necessary_num_of_table_blocks (void) //SIMPLE { /* necessary for toid and tqid.*/ return (current_query.oid_num * 2); } /* * */ static bid get_necessary_num_of_query_blocks (void) //SIMPLE { /* necessary for qid and did(s). */ return (1 + current_query.total_data_num * PQ_BUFFER_PAR_QBLOCK_DATA); } /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ bool qc_set_data (bid qid, const unsigned char *data, int len) //SIMPLE { ///***/// qblock qb; memcpy ((void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_did_data), (const void *)data, len); memcpy ((void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_did_data_len), (const void *)&len, sizeof(int)); return true; } /* * */ int qc_get_data (bid qid, unsigned char *data) //SIMPLE { int len; memcpy ((void *)data, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_did_data), len); memcpy ((void *)&len, (const void *) (QueryCacheQuerySpace + sizeof(qblock) * (qid - 1) + offset_qblock_did_data_len), sizeof(int)); return len; } #ifdef __QC_TX__ /* ================================================================ * Transaction Info Section * ================================================================ */ /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * * ---------------------- */ /* * */ static void init_tx_info (void) { tx_info.next = NULL; } /* * */ static void add_tx_info (Oid oid) { struct oid_cell *oc, *next; #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContext oldcontext; #endif #endif #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ oldcontext = MemoryContextSwitchTo(CurTransactionContext); #endif #endif next = tx_info.next; if (next != NULL) { while (next != NULL) { if (next->oid == oid) { #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContextSwitchTo(oldcontext); #endif #endif return; // do nothing. } next = next->next; } } // there is not "oid" in tx_info.oid_cell list #ifdef _UNIT_TEST_ if ((oc = (struct oid_cell *) malloc (sizeof (struct oid_cell))) == NULL) { fprintf (stderr, "ERROR: can't allocate memory.\n"); exit (0); } memset ((void *)oc, 0, sizeof (struct oid_cell)); #else #ifdef __CONTEXT__ if ((oc = (struct oid_cell *) palloc (sizeof (struct oid_cell))) == NULL) #else if ((oc = (struct oid_cell *) malloc (sizeof (struct oid_cell))) == NULL) #endif { fprintf (stderr, "ERROR: can't allocate memory.\n"); exit (0); } MemSet ((void *)oc, 0, sizeof (struct oid_cell)); #endif oc->oid = oid; oc->next = NULL; if (next != NULL) next->next = oc; else tx_info.next = oc; #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContextSwitchTo(oldcontext); #endif #endif } /* * */ static void free_tx_info (void) { struct oid_cell *next, *new_next; Oid oid; bid toid; #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(CurTransactionContext); #endif #endif /* */ next = tx_info.next; /* */ while (next != NULL) { new_next = next->next; oid = next->oid; if ((toid = lookup_oid (oid)) != (bid)Nil) del_toid (toid); /** #ifdef __CONTEXT__ #ifdef _UNIT_TEST_ free (next); #else pfree (next); #endif #else free (next); #endif **/ next = new_next; } /* */ /* #ifdef __CONTEXT__ #ifdef _UNIT_TEST_ free (tx_info); #else pfree (tx_info); #endif #else free (tx_info); #endif */ #ifdef __CONTEXT__ #ifndef _UNIT_TEST_ MemoryContextSwitchTo(oldcontext); #endif #endif init_tx_info (); } #endif /* ================================================================ * utilities * ================================================================ */ /* --------------------------------------------- * * --------------------------------------------- */ /* ---------------------- * hash key * ---------------------- */ /* * */ static hkey table_hashkey (Oid oid) { return (hkey)oid % HASH_SIZE; } /* * */ static hkey query_hashkey (const char *query) { int i, len; hkey key = 0; len = strlen (query); for (i = 0; i < len; i++) key = (key * 127 + query[i]) % HASH_SIZE; return (hkey)key; } /* ---------------------- * size * ---------------------- */ /* * */ static size_t header_size (void) { return (size_t)(sizeof (QueryCache_Header)); } /* * */ static size_t qc_oid_space_size (void) { return (size_t)((sizeof (oblock) * query_cache_oblock_num)); } /* * */ static size_t qc_query_space_size (void) { return (size_t)((sizeof (qblock) * query_cache_qblock_num)); } /* ---------------------- * * ---------------------- */ /* * */ static bid get_free_table_block_num (void) //SIMPLE { return ((QueryCacheHeader->qc_oid_space.max_block_num) - (QueryCacheHeader->qc_oid_space.block_num)); } /* * */ static bid get_free_query_block_num (void) //SIMPLE { return ((QueryCacheHeader->qc_query_space.max_block_num) - (QueryCacheHeader->qc_query_space.block_num)); } /* --------------------------------------------- * * --------------------------------------------- */ /* ================================================================ * for DEBUG * ================================================================ */ /* * */ static void _check_blocks (void) { bid i; oblock ob; if (3 <= query_cache_check_level) { fprintf (stdout, "============ check_table blocks ===========\n"); fprintf (stdout, "oid\t\toid/qid\t(p)prev\t(p)next\tqid_num\tqid_next\n"); fprintf (stdout, "-------------------------------------------\n"); for (i = 1; i <= query_cache_oblock_num; i++) { read_table_block (&ob, i); if (ob.type == TID) fprintf (stdout, "%u(TABLE)\t%u\t%u\t%u\n", i, ob.tid.oid, ob.prev, ob.next); else if (ob.type == TQID && 4 <= query_cache_check_level) fprintf (stdout, "%u(QUERY)\t%u\t%u\t%u\n", i, ob.qid.tqid, ob.qid.prev, ob.qid.next); } } } /* * */ static void _check_qblocks (void) { bid i; qblock qb; if (3 <= query_cache_check_level) { fprintf (stdout, "============ check_query blocks ===========\n"); fprintf (stdout, "qid\tprev\tnext\thashkey\tnext\tlast\tquery/data(len)\n"); fprintf (stdout, "-------------------------------------------\n"); for (i = 1; i <= query_cache_qblock_num; i++) { read_query_block (&qb, i); if (qb.type == QID) { fprintf (stdout, "%u(QID)\t%u\t%u\t%u\t%u\t%u\t%s\n", i, qb.prev, qb.next, qb.qid.key, qb.qid.next, qb.qid.last, qb.qid.query); } else if (qb.type == DID && 4 <= query_cache_check_level) { #ifdef _UNIT_TEST_ fprintf (stdout, "%u(DID)\t\t\t\t%u\t\t%s(%d)\n", i, qb.did.next, qb.did.data, qb.did.data_len); #else fprintf (stdout, "%u(DID)\t\t\t\t%u\t\t(%d)\n", i, qb.did.next, qb.did.data_len); #endif } } } } /* * */ static void _check_header (void) { int i; if (2 <= query_cache_check_level) { fprintf (stdout, "=============== check_header ==============\n"); printf ("table_block_num = %u, max_table_block_num = %u\nquery_block_num = %u, max_query_block_num = %u\ntable_free = %u\tquery_free = %u\n", QueryCacheHeader->qc_oid_space.block_num, QueryCacheHeader->qc_oid_space.max_block_num, QueryCacheHeader->qc_query_space.block_num, QueryCacheHeader->qc_query_space.max_block_num, QueryCacheHeader->qc_oid_space.next_free_block, QueryCacheHeader->qc_query_space.next_free_block); } if (4 <= query_cache_check_level) { fprintf (stdout, "-- qc_oid_space -----------------------------\n"); for (i = 0; i < HASH_SIZE; i++) { if (QueryCacheHeader->qc_oid_space.ht[i].num != 0) fprintf (stdout, "\t(%u)\t%u\t%u\n", i, QueryCacheHeader->qc_oid_space.ht[i].next, QueryCacheHeader->qc_oid_space.ht[i].num); } fprintf (stdout, "-- qc_query_space -----------------------------\n"); for (i = 0; i < HASH_SIZE; i++) { if (QueryCacheHeader->qc_query_space.ht[i].num != 0) fprintf (stdout, "\t(%u)\t%u\t%u\n", i, QueryCacheHeader->qc_query_space.ht[i].next, QueryCacheHeader->qc_query_space.ht[i].num); } } } /* * */ static void _check_statistics (void) { fprintf (stdout, "=============== statistics ==============\n"); printf ("all queries = %ld\n\tstore = %ld\thits = %ld\tnot_cached = %ld\n", QueryCacheHeader->qc_all_query, QueryCacheHeader->qc_store, QueryCacheHeader->qc_hits, QueryCacheHeader->qc_not_cached); } /* * */ void _check_query_cache (const char *statement) { if (0 == query_cache_check_level) return; fprintf (stdout, "\n*******************************************\n"); fprintf (stdout, " LOG: %s\n", statement); fprintf (stdout, "*******************************************\n"); _check_blocks (); _check_qblocks(); _check_header (); _check_statistics (); fprintf (stdout, ">>>\n\n"); } // EOF