/****************************************************************************

   mod_dav_pgsql (DASL) for apache 2.X
   CopyRight(C) 2005 SolutionBox Inc. All Rights reserved.
   Author : sean kim (sean@solutionbox.co.kr)

   $Id: dbms.c,v 1.5 2007/02/22 08:22:12 elenoa Exp $

   Redistribution and use in source and binary forms, with or with out
   modification, are not permitted in outside of SolutionBox Inc.

 ****************************************************************************/
#include <sys/types.h>
#include <sys/stat.h>
#include <httpd.h>
#include <http_protocol.h>
#include <http_log.h>
#include <http_core.h>		/* for ap_construct_url */

#include <apr.h>
#include <apr_strings.h>
#include <apr_hash.h>
#include <apr_uuid.h>
#include <apr_file_io.h>
#include <stdio.h>
//#include <libpq-fe.h>

#include "dav_repos.h"
#include "lock.h"
#include "dbms.h"
#include "util.h"
#include "share_common.h"
#include "share_fhs_stat.h"


#define DASL_PGSQL_DEBUG

#define STRING_FROM_DBENTRY(pool, element, num)	\
	do {	\
		const char *temp = apr_dbd_get_entry(db->driver, row, num);	\
		element = (temp == NULL ? NULL :	\
					apr_pstrndup(pool, temp, strlen(temp)));	\
	} while (0)

/**
 * Show error message of SQL operations
 * @param conn The database connection
 * @param db_error_message_str An application level error message string
 */
void db_error_message(ap_dbd_t *db, char *message_str, int errnum)
{
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: database: %s: (%d)%s"
			, message_str
			, errnum
			, (errnum == 0 ? "*NONE*" : apr_dbd_error(db->driver, db->handle, errnum))
			);
}


/**
 * r->uri for search key 
 * @param d DB connection struct containing the user, password, and DB name
 * @param r Out variable to hold the result
 * @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
 * @return 1 on success, 0 for no result, -1 on fail
 */
int dbms_get_property(dav_repos_resource *db_r, bool is_uricaseignore )
{
	int iret = 0;
	char *query = NULL;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char tblname[20] = {0};
	
	if (!db_r) 
		return -6;

	char* db_uri = NULL;
	// UTF-8 -> DB client encoding 로 변환 처리
	db_uri = transformDBCharacter(db_r->r, db_r->m_fake_uri, "dbms_get_property");

	// 2014-04-24 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_property: at_split func fail.");
		return -7;
	}
	
	// CHG 2012-03-20 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단하여 
	// 무시일 경우 해당 요청을 소문자로 변환처리하여 조회 결과값을 반환한다.

	char * whereParse = NULL;
	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		whereParse = apr_psprintf(db_r->p, " WHERE sp_svc_tran_id = %s "
					"AND depth = %d AND %s = lower('%s') "
					, user_info.szVolumeID
					, db_r->m_depth
					// 2014.06.12
					// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
					, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
					, db_uri );

	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		whereParse =  apr_psprintf(db_r->p, " WHERE sp_svc_tran_id = %s "
					"AND depth = %d AND uri = '%s' "
					, user_info.szVolumeID, db_r->m_depth, db_uri );
	}

#ifdef __PROPFIND_RANDOM_FILE__
	
	int ntuple = 0;
	int i;
	unsigned int n;
	bool alive = false;

	/*2010.12.07 : dadamin / PROPFIND 처리시 FHS 정보 Random(파일 관련) */
	// NetCache Agent 인지를 확인하기 위해 PROFPIND Header 의 User-Agent 정보 검사.
	const char * userAgent = NULL;
	userAgent = apr_table_get( db_r->r->headers_in, "User-Agent");

	if( db_r->r->method_number == M_PROPFIND && userAgent != NULL && strncmp(userAgent, "NetCache", 8)==0 ) 
	{		
		query = apr_psprintf(db_r->p, 
				"SELECT resource_id, "
				" resource_type, "
#ifdef __OPENDISK_SOLUTION__
				" owner_id, "
#else
				" sp_user_seq, "
#endif
				" uri, "
				" filename_org, "
				" filename_hash, "
				" depth, "
				" creation_date, "
				" display_name, "
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" get_content_length, "
				" get_content_type, "
				" get_lastmodified, "
				" source, "
				" host_name, "
				" file_lastmodified, "    // ADD 2010-02-16 huibong : File 속성 정보 조회
				" is_cache "
				"FROM %s "
				"%s "
				" AND deleted_yn = 'N'"
				, dav_get_target_table(user_info.szVolumeID, tblname), whereParse 
				);

		db = dav_repos_get_db(db_r->r);
		if(db == NULL)
		{
			db_error_message(db, "dbms_get_property: database acquire timeout", 0);
			return -2;
		}

		rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
		if( rv ) 
		{
			db_error_message(db, "dbms_get_property: random select", rv);
			dav_repos_release_db(db_r->r, db, rv);
			
			return -1;
		}

		ntuple = apr_dbd_num_tuples(db->driver, res);
		if( ntuple == 0 ) 
		{
			iret = 0;
		} 
		else 
		{			
			rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, 0);
			if( rv ) 
			{
				db_error_message(db, "dbms_get_property(1): get row", rv);
				dav_repos_release_db(db_r->r, db, rv);

				return -3;
			}

			// 디렉토리 경우 램덤 선택 필요 없음
			if(atoi(apr_dbd_get_entry(db->driver, row, 1)) == 1)
			{
				// dir
				solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, 0);
			}
			else
			{
				// file
				// 해당 서버 정보 random 처리 및 서버 alive 체크
				for (i = 0; i < ntuple; ++i)
				{
					apr_generate_random_bytes((unsigned char *)&n, sizeof(int));

					n = n % ntuple;
					rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, n);
					if( rv ) 
					{
						db_error_message(db, "dbms_get_property(2): get row", rv);
						dav_repos_release_db(db_r->r, db, rv);

						return -3;
					}

					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					if( dav_shared_du_is_alive( apr_dbd_get_entry(db->driver, row, 13) ) != 0 )
					{
						alive = true;
						break;
					}
				}

				// 램덤 실패 시 해당 결과 순회 후 Alive된 서버 선택
				if( alive == false ) 			
				{
					int first = 0;					
					first = n;
					for ( n = first ; n < ntuple; ++n)
					{
						rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, n);
						if( rv ) 
						{
							db_error_message(db, "dbms_get_property(3): get row", rv);
							dav_repos_release_db(db_r->r, db, rv);

							return -3;
						}

						// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
						if(dav_shared_du_is_alive(apr_dbd_get_entry(db->driver, row, 13)) != 0)
						{
							alive = true;
							break;
						}

						if( first > 0 && (n+1) >= ntuple )
						{
							n = -1;
							ntuple = first;
							first = 0;
						}
					}

					if(alive == false)
					{
						// 2012.06.16 : dadamin
						// NetCache 경우 Alive 된 서버가 없을 경우 에러를 리턴
						//dav_repos_release_db( db_r->r, db, rv );
						//return -5;

						// CHG 2019-10-01 huibong 0 파일 관련 CDN 장애 회피를 위한 기능 개선 (#32812 )
						// - 0 파일은 replication 을 하지 않으므로.. 해당 FHS 가 죽는 경우....
						// - soldrive 에서 오류 상태로 인식하여 업로드 실패 등의 문제가 발생
						// - 따라서 이를 개선하기 위해 다음과 같이 처리한다.
						// -- PROPFIND 요청, agent 가 NetCache, 0 파일, 유효한 FHS 가 없는 경우
						// -- 오류 가 아닌 정상 상태로 응답 처리하도록 수정.
						if( atoll( apr_dbd_get_entry( db->driver, row, 9 ) ) != 0 )
						{
							dav_repos_release_db( db_r->r, db, rv );
							return -5;
						}
						else
						{
							// NetCache agent, 파일에 대한 PROPFIND 요청, 0 파일인데.. 원본 FHS 가 죽은 경우...
							// for logging
							ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL
								, "dbms_get_property: size 0 and fhs dead. but return normal status [propfind][%s][%s]"
								, db_uri, apr_dbd_get_entry( db->driver, row, 13 )
							);
						}
					}
				}
			}

			/*Get the data of every column of this row to a link */
			db_r->m_resource_id = atoll(apr_dbd_get_entry(db->driver, row, 0));
#ifdef __OPENDISK_SOLUTION__
			db_r->m_resource_type = dav_repos_change_resource_type(atoi(apr_dbd_get_entry(db->driver, row, 1)));
#else
			db_r->m_resource_type = atoi(apr_dbd_get_entry(db->driver, row, 1));
#endif
			db_r->m_owner_id = atoll(apr_dbd_get_entry(db->driver, row, 2));


			STRING_FROM_DBENTRY(db_r->p, db_r->m_filename_org, 4);
			STRING_FROM_DBENTRY(db_r->p, db_r->m_filename_hash, 5);

			db_r->m_depth = atoi(apr_dbd_get_entry(db->driver, row, 6));
			db_r->m_creation_date = atoll(apr_dbd_get_entry(db->driver, row, 7));


			STRING_FROM_DBENTRY(db_r->p, db_r->m_displayname, 8);

			db_r->m_get_content_length = atoll(apr_dbd_get_entry(db->driver, row, 9));

			STRING_FROM_DBENTRY(db_r->p, db_r->m_get_content_type, 10);

			// ADD 2010-02-16 huibong
			// SBFS Mode : 파일 속성 중 최종 수정일자는 get_lastmodified 필드 대신 file_lastmodified 값을 사용한다.
			db_r->m_get_lastmodified = atoll(apr_dbd_get_entry(db->driver, row, 14));
			
			db_r->m_source = atoi(apr_dbd_get_entry(db->driver, row, 12));

			STRING_FROM_DBENTRY(db_r->p, db_r->m_host_name, 13);

			db_r->next = NULL;

			iret = 1;
		}
	}
	else
#endif //__PROPFIND_RANDOM_FILE__
	{
		query = apr_psprintf(db_r->p, 
				"SELECT resource_id, "
				" resource_type, "
#ifdef __OPENDISK_SOLUTION__
				" owner_id, "
#else
				" sp_user_seq, "
#endif
				" uri, "
				" filename_org, "
				" filename_hash, "
				" depth, "
				" creation_date, "
				" display_name, "
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" get_content_length, "
				" get_content_type, "
				" get_lastmodified, "
				" source, "
				" host_name, "
				" file_lastmodified "    // ADD 2010-02-16 huibong : File 속성 정보 조회
				"FROM %s "
				"%s "
				" AND deleted_yn = 'N' AND is_cache = 'N' "
				, dav_get_target_table(user_info.szVolumeID, tblname), whereParse  );

		db = dav_repos_get_db(db_r->r);
		if(db == NULL)
		{
			db_error_message(db, "dbms_get_property: database acquire timeout", 0);
			return -2;
		}
		
		rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
		if( rv ) 
		{
			db_error_message(db, "dbms_get_property: select", rv);
			dav_repos_release_db(db_r->r, db, rv);
			
			return -1;
		}

		// 해당 정보가 존재하지 않는 경우
		if( apr_dbd_num_tuples(db->driver, res) == 0 ) 
		{
			iret = 0;
		} 
		else 
		{
			// CHG 2012-03-20 huibong
			// 아래 코드는 미사용 코드로 판단되어 우선 주석 처리함.
			//if( apr_dbd_num_cols(db->driver, res) == 1 ) 
			//{
			//	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: Parent? (in2) START ---->");
			//	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: Parent? (in2) %s ", apr_dbd_get_entry(db->driver, row, 0));
			//}
			
			rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, 0);
			if( rv ) 
			{
				db_error_message(db, "dbms_get_property: get row", rv);
				dav_repos_release_db(db_r->r, db, rv);

				return -3;
			}

			/*Get the data of every column of this row to a link */
			db_r->m_resource_id = atoll(apr_dbd_get_entry(db->driver, row, 0));
#ifdef __OPENDISK_SOLUTION__
			db_r->m_resource_type = dav_repos_change_resource_type(atoi(apr_dbd_get_entry(db->driver, row, 1)));
#else
			db_r->m_resource_type = atoi(apr_dbd_get_entry(db->driver, row, 1));
#endif
			db_r->m_owner_id = atoll(apr_dbd_get_entry(db->driver, row, 2));

			STRING_FROM_DBENTRY(db_r->p, db_r->m_filename_org, 4);
			STRING_FROM_DBENTRY(db_r->p, db_r->m_filename_hash, 5);

			db_r->m_depth = atoi(apr_dbd_get_entry(db->driver, row, 6));
			db_r->m_creation_date = atoll(apr_dbd_get_entry(db->driver, row, 7));

			STRING_FROM_DBENTRY(db_r->p, db_r->m_displayname, 8);

			db_r->m_get_content_length = atoll(apr_dbd_get_entry(db->driver, row, 9));

			STRING_FROM_DBENTRY(db_r->p, db_r->m_get_content_type, 10);

			// ADD 2010-02-16 huibong
			// SBFS Mode : 파일 속성 중 최종 수정일자는 get_lastmodified 필드 대신 file_lastmodified 값을 사용한다.
			//db_r->m_get_lastmodified = atoll(apr_dbd_get_entry(db->driver, row, 13));
			db_r->m_get_lastmodified = atoll(apr_dbd_get_entry(db->driver, row, 14));
			
			db_r->m_source = atoi(apr_dbd_get_entry(db->driver, row, 12));

			STRING_FROM_DBENTRY(db_r->p, db_r->m_host_name, 13);

			db_r->next = NULL;

			iret = 1;
		} 
	}

	dav_repos_release_db(db_r->r, db, rv);

    return iret;
} 


// 2018-02-13 CHG huibong
// - redirection 정보 조회시 시스템 오류, RCDB 오류, Flow 오류 등의 상황을 처리하기 위해
// - 기존 dbms_get_redirect() 함수 다시 작성 처리
// return     : NULL 아닌 경우 정상
//              NULL 인 경우 오류 발생했으며.. 인자로 전달한 error_code 내역 참고
// error_code : return 값이 NULL 인 경우
//              내부적으로 발생한 error 상황을 어떤 HTTP Response code 로 처리할지를 나타냄
char * dbms_get_redirect( dav_repos_resource *db_r, bool is_uricaseignore, bool is_random, int *error_code )
{
	// server 설정 정보 추출.
	dav_repos_server_conf *dsc = NULL;
	dsc = dav_repos_get_server_conf( db_r->r->server );
	if( !dsc )
	{
		// 시스템 설정 오류로 인한 처리 불가 상황
		// 이런 경우는 일시적인 장애보다... FHS 설정 장애 또는 apache 재기동 문제 이므로.... 
		// 명시적으로 HTTP 500 internal server 오류로 처리한다. 
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: dav_repos_get_server_conf func fail." );
		*error_code = HTTP_INTERNAL_SERVER_ERROR;
		return NULL;
	}

	// UTF-8 -> DB client encoding 로 변환 처리
	char *db_uri = NULL;
	db_uri = transformDBCharacter( db_r->r, db_r->m_fake_uri, "dbms_get_redirect" );

	// 2014-04-24 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if( at_split( db_r->r->user, &user_info ) < 0 )
	{
		// user 정보에 대한 인식 처리 실패, client 에서 인증 정보를 안 보낼 경우 발생 가능.
		// - HTTP 400 잘못된 요청으로 처리
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: at_split func fail." );
		*error_code = HTTP_BAD_REQUEST;
		return NULL;
	}

	// CHG 2012-03-20 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단하여 
	// 무시일 경우 해당 요청을 소문자로 변환처리하여 조회 결과값을 반환한다.
	char * whereParse = NULL;
	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		whereParse = apr_psprintf( db_r->p, " WHERE sp_svc_tran_id = %s "
								   "AND depth = %d AND %s = lower('%s') "
								   , user_info.szVolumeID
								   , db_r->m_depth
								   // 2014.06.12
								   // 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
								   , dav_use_display_name() == 0 ? "lower(uri)" : "uri"
								   , db_uri );

	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		whereParse = apr_psprintf( db_r->p, " WHERE sp_svc_tran_id = %s "
								   "AND depth = %d AND uri = '%s' "
								   , user_info.szVolumeID, db_r->m_depth, db_uri );
	}

	// 2012.04.19 : dadamin , 해당 쿼리 일부 응답성 개선을 위해서 GROUP BY, ORDER BY 삭제
	// Redirction 처리 대상 장비 선정을 위한 Query 
	char tblname[20] = { 0 };
	char *query;
	query = apr_psprintf( db_r->p,
						  "SELECT host_name, is_cache, filename_hash "
						  "FROM %s "
						  "%s"
						  "AND deleted_yn = 'N' AND get_content_length = %" APR_INT64_T_FMT " "
						  , dav_get_target_table( user_info.szVolumeID, tblname ), whereParse, db_r->m_get_content_length );

	ap_dbd_t *db = dav_repos_get_db( db_r->r );
	if( db == NULL )
	{
		// DB 세션 획득 실패
		// - 요청 수가 많거나... 설정값이 너무 작아 DB 세션 획득 실패 가능
		// - 부하 상황으로 가정하고.. HTTP 503 값을 줘서... NetCache Client 에서 요청이 감소하도록 한다.
		db_error_message( db, "dbms_get_redirect: database acquire timeout", 0 );
		*error_code = HTTP_SERVICE_UNAVAILABLE;
		return NULL;
	}

	apr_dbd_results_t *res = NULL;
	int rv = apr_dbd_select( db->driver, db_r->r->pool, db->handle, &res, query, 1 );
	if( rv )
	{
		// select query 실패시
		// - DB 가 일시적으로 죽은 경우로 판단, HTTP 503 반환.
		db_error_message( db, "dbms_get_redirect: select", rv );
		dav_repos_release_db( db_r->r, db, rv );

		*error_code = HTTP_SERVICE_UNAVAILABLE;
		return NULL;
	}

	int ntuple = apr_dbd_num_tuples( db->driver, res );
	if( ntuple < 1 )
	{
		// DB 조회 결과가 존재하지 않는 경우....
		// - 순간적으로 삭제 처리된 경우로 판단.  HTTP 404 응답
		dav_repos_release_db( db_r->r, db, rv );

		*error_code = HTTP_NOT_FOUND;
		return NULL;
	}

	// NetCache 처리 관련 User-Agent 정보 추출.
	const char *userAgent = NULL;
	userAgent = apr_table_get( db_r->r->headers_in, "User-Agent" );

	// NEW 2011-05-16 huibong
	// GET 요청에 대해 FHS 제외 목록 정보를 HTTP Header 상의 Except-for : <host-name> 형식으로 전달받는 경우
	// 해당 FHS 정보는 Redirect 전달 대상에서 제외시키고
	// 다른 FHS 정보만을 검토하여 전달한다.
	// 단 Except-for 로 전달되는 FHS 정보는 1 대로만 지정처리한다. ( 2012-01-18 다중 FHS 정보 전달 및 탐색으로 수정 처리함 )
	const char * exceptFhsName = NULL;
	exceptFhsName = apr_table_get( db_r->r->headers_in, "Except-for" );
	if( exceptFhsName != NULL )
	{
		ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: Except-for set [%s][%" APR_INT64_T_FMT "][%s]", db_uri, db_r->m_get_content_length, exceptFhsName );
	}


	// 2012.06.13 : dadamin
	// Alive 체크 및 램덤 처리
	apr_dbd_row_t *row = NULL;
	char *retval = NULL;


	// 2010.12.07 C99을 지원하는 컴파일일 경우 배열의 인자로 처리 가능 만약을 대비하여 동적할당 방식으로 수정
	int *host_alive_index;
	int host_alive_count = 0;

	// 메모리 할당
	host_alive_index = ( int * ) calloc( ntuple, sizeof( int ) );

	// 메모리 할당 실패시
	if( host_alive_index == NULL )
	{
		// FHS 목록 구성을 해서 Alive Check 를 할 수 없는 상황, 조회된 첫번째 DB row 는 존재함.
		// - NetCache 인 경우 조회된 첫번째 FHS 가 살아 있는지 체크하여....
		//   살아 있는 경우 해당 FHS 정보 전송, 죽은 경우 HTTP 404 전달
		// - 양방향 기본은 그냥 조회된 첫번째 FHS 정보를 전달한다

		rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, 0 );

		if( userAgent != NULL && strncmp( userAgent, "NetCache", 8 ) == 0 )
		{
			// except-for 에 조회된 FHS 가 존재하는 경우
			if( exceptFhsName != NULL && strcasestr( exceptFhsName, apr_dbd_get_entry( db->driver, row, 0 ) ) != NULL )
			{
				// except-for 에 첫번재 FHS 가 포함된 경우.
				// - 전송할 FHS 가 없는 경우 사전 정의된 대로.. HTTP 410 반환한다.
				*error_code = HTTP_GONE;
				retval = NULL;
				
				ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: memory alloc error & first include Except-for. return code 410 [%s][%" APR_INT64_T_FMT "][%s]"
							  , db_uri, db_r->m_get_content_length, apr_dbd_get_entry( db->driver, row, 0 ) );

			}
			else if( dav_shared_du_is_alive( apr_dbd_get_entry( db->driver, row, 0 ) ) != 0 ) // 해당 장비의 Alive 여부 판단.
			{
				// 해당 장비가 살아 있는 경우...
				*error_code = 0;
				retval = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 0 ) );

				ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: memory alloc error. but first alive. return first [%s][%" APR_INT64_T_FMT "][%s]"
							  , db_uri, db_r->m_get_content_length, retval );
				
			}
			else
			{
				// except-for 에 포함되지 않지만... 해당 장비가 죽은 경우...
				// - 전송할 FHS 가 없는 경우 사전 정의된 대로.. HTTP 410 반환한다.
				*error_code = HTTP_GONE;
				retval = NULL;
				
				ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: memory alloc error & first dead. return code 410 [%s][%" APR_INT64_T_FMT "][%s]"
							  , db_uri, db_r->m_get_content_length, apr_dbd_get_entry( db->driver, row, 0 ) );
			}
		}
		else
		{
			// 양방향 기본인 경우
			// - 조회된 FHS 정보를 그냥 전달한다.
			*error_code = 0;
			retval = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 0 ) );

			ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: memory alloc error. default return first [%s][%" APR_INT64_T_FMT "][%s]", db_uri, db_r->m_get_content_length, retval );
		}
		
	}
	else
	{
		// 메모리 할당 성공시
		// - 조회된 FHS 정보를 해당 메모리에 저장하고...  가장 적합한 FHS 를 전달한다.

		// NEW 2011-05-16 huibong
		// GET 요청에 대해 FHS 제외 목록 정보를 HTTP Header 상의 Except-for : <host-name> 형식으로 전달받는 경우
		// 해당 FHS 정보는 Redirect 전달 대상에서 제외시키고
		// 다른 FHS 정보만을 검토하여 전달한다.
		// 단 Except-for 로 전달되는 FHS 정보는 1 대로만 지정처리한다. ( 2012-01-18 다중 FHS 정보 전달 및 탐색으로 수정 처리함 )

		int i;
		int sourceindex = 0;
		int selfindex = -1;

		// 조회된 DB 결과에서 alive, except 대상이 아닌 FHS 정보를 메모리에 저장 처리
		for( i = 0; i < ntuple; i++ )
		{
			rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, i );
			
			// 원본 FHS 인 경우 해당 row index 저장
			if( strcmp( "N", apr_dbd_get_entry( db->driver, row, 1 ) ) == 0 )
			{
				sourceindex = i;
			}

			// CHG 2012-01-18 huibong
			// 조회된 목록 중 제외 FHS 가 존재하는 경우 해당 장비는 Redirect 전달 대상에서 제외시킨다. ( 대소문자 구문 안함 )
			// 단, 전달된 제외 FHS 목록이 blank 로 구분되는 다중 FHS 목록이므로...
			// DB 조회 값이 제외 FHS 목록상에 존재하는지 기존과 같은 비교가 아니라 검색으로 방식을 변경처리한다.
			// - 현재 장비가 except-for 에 등록된 경우.. 포함되지 않도록 하기 위해.. 아래 현재 장비 check 보다 먼저 수행한다.
			if( exceptFhsName != NULL && strcasestr( exceptFhsName, apr_dbd_get_entry( db->driver, row, 0 ) ) != NULL )
			{
				ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: target excepted by Except-for Header [%s][%" APR_INT64_T_FMT "][%s]"
							  , db_uri, db_r->m_get_content_length, apr_dbd_get_entry( db->driver, row, 0 ) );

				continue;
			}

			// except-for 에 포함 안되고.. 현재 장비인 경우
			if( strcmp( dsc->host_name, apr_dbd_get_entry( db->driver, row, 0 ) ) == 0 )
			{
				selfindex = i;

				// Random 처리 인 경우 해당 장비에 파일이 존재해도 random 처리 한다.
				if( is_random == false )
				{
					// random 처리가 아닌 경우.. 현재 장비 정보 반환 처리하고 끝낸다.
					*error_code = 0;
					retval = apr_pstrdup( db_r->p, dsc->host_name );
					db_r->m_filename_hash = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 2 ) );

					// 메모리 해제
					free( host_alive_index );

					// 마무리 후 반환
					dav_repos_release_db( db_r->r, db, rv );
					return retval;
				}
			}

			// 최종.. 정리할 거 다 하고...해당 장비가 Alive 상태인 경우
			if( dav_shared_du_is_alive( apr_dbd_get_entry( db->driver, row, 0 ) ) != 0 ) 
			{
				// 해당 장비가 alive 상태인 경우.. 메모리에 저장 처리.
				host_alive_index[host_alive_count] = i;
				host_alive_count++;
			}
		}

		// alive 장비가 없는 경우...
		if( host_alive_count <= 0 )
		{
			// NetCache 의 Content 요청인 경우
			if( db_r->m_resource_type == 0 && userAgent != NULL && strncmp( userAgent, "NetCache", 8 ) == 0 )
			{
				// 2012.06.13 : dadamin
				// NetCache 경우 Alive 된 목록이 없을 시 NULL 리턴하며, 
				// 해당 장비에 파일이 존재하면 서비스 될 수 있게 한다.
				
				// 위에서 현재 장비가 except-for 에 등록되어 있는 경우는 selfindex 값이 처리 안되므르....
				// 현재 장비가 except-for 에 포함 안된 경우 임.
				if( selfindex >= 0 )
				{
					// 현재 장비에 파일 존재하는 경우..
					rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, selfindex );
					
					*error_code = 0;
					retval = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 0 ) );
					db_r->m_filename_hash = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 2 ) );

					ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: alive target nothing. return My self [%s][%" APR_INT64_T_FMT "][%s]"
								  , db_uri, db_r->m_get_content_length, retval );
				}
				else
				{
					// alive 장비가 전혀 없는 경우... 
					// - 전송할 FHS 가 없는 경우 사전 정의된 대로.. HTTP 410 반환한다.
					
					// 참고사항
					// - ntuple 값을 이용하여 원본만 존재하는지, 복제본까지 존재하는지 예측 가능
					// - 해당값을 이용하여 원본만 존재하고 FHS 죽은 경우 HTTP 410 반환
					// - 원본, 복제본이 다 죽은 경우 Backup RC 로 넘어가도록 HTTP 500 반환하려 했으나...
					//   관련 담당자들이 해당 발생 확률 낮고.. Backup 이 없는 경우 Backup 으로 넘어가서 서비스 장애를 발생시키는게 더 문제라고 함
					// - Backup failover 는 본 건 아니어도 SolDrive, SolProxy 에서 connection 처리를 이용하여 판단하므로.. 신경 쓸 필요 없다고 함.
					// - 그래서 관련 담당자들과 협의하에 원본, 복제본이 다 죽은 경우에도 HTTP 410 처리하는 것으로 최종 협의함.
					*error_code = HTTP_GONE;
					retval = NULL;

					ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: alive target nothing. return code 410 [%s][%" APR_INT64_T_FMT "]"
								  , db_uri, db_r->m_get_content_length );
				}
			}
			else
			{
				// 양방향 기본
				// - alive 장비가 없는 경우... 원본 장비 정보를 반환한다.
				rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, sourceindex );
				
				*error_code = 0;
				retval = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 0 ) );

				ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: alive target nothing. default return first [%s][%" APR_INT64_T_FMT "][%s]"
							  , db_uri, db_r->m_get_content_length, retval );
			}
		}
		else
		{
			// alive 장비가 존재하는 경우
			// 장비가 다중인 경우 => Random 장비를 선택하여 전송한다.
			unsigned int n;
			apr_generate_random_bytes( ( unsigned char * ) &n, sizeof( int ) );
			n = n % host_alive_count;

			rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, host_alive_index[n] );
			*error_code = 0;
			retval = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 0 ) );

			// 2012.05.03, dadamin
			// 램덤 처리 경우 해당 장비가 원본이 아닌 경우 저장 위치가 다를 수 있으므로 
			// 해당 파일위치를 다시 한 번 더 저장함
			db_r->m_filename_hash = apr_pstrdup( db_r->p, apr_dbd_get_entry( db->driver, row, 2 ) );

			ap_log_error( APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_redirect: alive target random select [%s][%" APR_INT64_T_FMT "][%s]"
						  , db_uri, db_r->m_get_content_length, retval );
		}


		// 메모리 해제
		free( host_alive_index );
	}

	dav_repos_release_db( db_r->r, db, rv );

	// 최종 결과 반환
	return retval;
}


// NEW 2011-03-23 dadamin
// 각 디렉토리 서비스 용량정보( Total, 현재사용량, Used 용량)를 조회하기 위한 함수(단 t_sms_sp_svc_product의 cal_used_depth 기준)
// tran_id [in] 서비스 Tran ID 정보
// is_uricaseignore URI 에 대한 대소문자 구분 여부, true : 대소문자 무시, false: 대소문자 구분
// total_byte      [out] 해당 서비스 Tran ID 의 Total 스토리지 계약 용량 정보
// used_tbyte [out] 해당 uri가 사용하고있는 용량 정보
// return 오류발생시 -1, 해당정보가 존재하지 않는 경우 0, 정상 처리된 경우 1.
int dbms_get_service_quotaex( const dav_repos_resource * db_r, const char * tran_id, bool is_uricaseignore, apr_int64_t * total_byte, apr_int64_t * used_byte )
{
	ap_dbd_t * db = NULL;
	char * query = NULL;
	int rv;
	apr_dbd_results_t * res = NULL;
	apr_dbd_row_t * row = NULL;
	char tblname[20] = {0};

	// 전달받은 인자값 확인, 인자값이 유효하지 않은 경우 -1 을 반환
	if( tran_id == NULL || total_byte == NULL || used_byte == NULL  )
		return -1;

	// 2014-04-24 : dadamin
	// where 구문에 기준값, depth 추가

	// CHG 2012-03-20 유희곤
	// 해당 서비스에 대해 URI 대소문자 구분, 무시 여부를 판단하여 
	// 무시일 경우 해당 요청을 소문자로 변환처리하여 조회 결과값을 반환한다.

	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		// CHG 2013-10-07 dadamin
		// 삭제후 재생성된 디렉토리에 대해서 422 에러 방지를 위해서 삭제 플래그 검사 쿼리 추가
		query = apr_psprintf( db_r->p,
			"SELECT T.*, SUM(R.dir_used) FROM ("
			" SELECT R.max_str_size, R.cal_used_depth, L.uri, L.depth, L.deleted_yn"
			" FROM  %s L"
			" JOIN t_sms_sp_svc_product R"
			" ON L.sp_svc_tran_id = R.sp_svc_tran_id "
			" WHERE L.sp_svc_tran_id = %s AND L.depth = %d "
				"AND %s = lower('%s') AND L.deleted_yn = 'N') T "
			"LEFT JOIN t_dav_use_dir R "
			"ON %s "
			"GROUP BY T.max_str_size, T.cal_used_depth, T.uri, T.deleted_yn, T.depth"
			, dav_get_target_table(tran_id, tblname), tran_id, db_r->m_depth
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
			, dav_use_display_name() == 0 ? "lower(L.uri)" : "L.uri"
			, db_r->m_fake_uri
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
			, dav_use_display_name() == 0 ? "lower(T.uri) = lower(R.path_name)" : "T.uri = R.path_name"
			);
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		// CHG 2013-10-07 dadamin
		// 삭제후 재생성된 디렉토리에 대해서 422 에러 방지를 위해서 삭제 플래그 검사 쿼리 추가
		query = apr_psprintf( db_r->p,
			"SELECT T.*, R.dir_used FROM ("
			" SELECT R.max_str_size, R.cal_used_depth, L.uri, L.depth, L.deleted_yn"
			" FROM  %s L"
			" JOIN t_sms_sp_svc_product R"
			" ON L.sp_svc_tran_id = R.sp_svc_tran_id "
			" WHERE L.sp_svc_tran_id = %s AND L.depth = %d "
				"AND L.uri = '%s' AND L.deleted_yn = 'N') T "
			"LEFT JOIN t_dav_use_dir R "
			"ON T.uri = R.path_name "
			, dav_get_target_table(tran_id, tblname), tran_id, db_r->m_depth, db_r->m_fake_uri
			);
	}

	// Query 실행
	db = dav_repos_get_db( db_r->r );
	if(db == NULL)
	{
		db_error_message(db, "dbms_get_service_quotaex: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);


	// Query 실행 실패시, -1 값을 반환함.
	if( rv != 0 )
	{
		db_error_message(db, "dbms_get_service_quotaex: select", rv );
		dav_repos_release_db( db_r->r, db, rv );
		return -1;
	}

	// Query 결과 데이터가 존재하지 않는 경우, 0 값을 반환함.
	rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, 0);
	if( rv != 0 )
	{
		db_error_message(db, "dbms_get_service_quotaex: get row", rv );
		dav_repos_release_db( db_r->r, db, rv );
		return 0;
	}

	// 2012.09.03 : dadamin
	// 용량 산정이 되지 않을 경우 사용량을 0으로 리턴
	// 요청 경로(depth)에 depth가 용량 산정(cal_used_depth)에 depth 보다 크면 422 리턴

	// 삭제된 경우라면 상위 로직에서 404 에러 리턴되므로 해당 체크 필요 없음

	// error : different depth
	if( strcmp(apr_dbd_get_entry(db->driver, row, 1), apr_dbd_get_entry(db->driver, row, 3)) < 0 )
	{
		dav_repos_release_db( db_r->r, db, rv );
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_service_quotaex: cal_used_depth and depth are different.[%s]", db_r->m_fake_uri);
		return 0;
	}

	// 서비스 계약 용량 및 사용가능용량(Free)을 Byte 단위로 계산처리.
	*total_byte = atoll( apr_dbd_get_entry(db->driver, row, 0));
	*used_byte = atoll( apr_dbd_get_entry(db->driver, row, 5));

	// DB release 처리 및 1 을 반환.
	dav_repos_release_db( db_r->r, db, rv );
	return 1;
}


/**
 * Inserts a resource into the dasl_resource table
 * @param d DB connection struct containing the user, password, and DB name
 * @param r The resource to insert
 * @param lserialno The serial number assigned to the resource when inserted
 * @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
 * @return 0 if success, -1 if fail
 */
// CHG 2012-03-23 huibong
// 서비스별 대소문자 구분, 무시 기능 추가를 위해 본 함수를 수정 처리
// 그 외 로깅 처리 내역 보강.
static int db_insert_resource(const dav_repos_resource * db_r, apr_int64_t *resource_id, const dav_resource * resource, bool is_uricaseignore )
{
	char *query = NULL;
	acct_info user_info;
	dav_repos_server_conf *dsc;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	int nrows;
	char tblname[20] = {0};

	// 2014-04-28 : dadamin
	// path depth 추출 로직 삭제 : 상위 로직에서 해당값을 추출하여 가지고 있음

	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "db_insert_resource: at_split func fail.");
		return -1;
	}

	dsc = dav_repos_get_server_conf(db_r->r->server);
	if( !dsc ) 
	{
		// conf 정보를 가져오지 못한 경우.
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "db_insert_resource: dav_repos_get_server_conf func fail.");

		// CHG 2012-03-15 huibong
		// 본 함수를 사용하는 코드에서는 -1 을 반환할 경우에만 오류로 처리...
		// 따라서 아래 코드는 잘못되었으므로 수정 처리
		//return -5;
		return -1;
	}

	// CHG 2010-01-19 huibong
	// file_lastmodified column info insert ( equal to creation_date info)
    
	// CHG 2012-03-23 huibong
	// 서비스별 대소문자 구분 무시 기능 추가 	
	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		query = apr_psprintf(db_r->p, 
				"INSERT INTO %s ( "
				" resource_type,"
#ifdef __OPENDISK_SOLUTION__
				" subuser_id, owner_id,"
#else
				" sp_user_seq, sp_svc_tran_id,"
#endif
				" uri,"
				" filename_org,"
				" filename_hash,"
				" depth,"
				" creation_date,"
				" display_name,"
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" get_content_length,"
				" get_content_type,"
				" get_lastmodified,"
				" file_lastmodified,"  // ADD 2010-01-19 huibong
				" source,"
				" host_name,"
				" deleted_yn ) "
				"VALUES "
				" ( %d , %s, %s, lower('%s'), '%s', '%s', %d, %" APR_INT64_T_FMT ", '%s', %" APR_INT64_T_FMT ", '%s', %" APR_INT64_T_FMT ", %" APR_INT64_T_FMT ", %d, '%s', 'N' ) "
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, db_r->m_resource_type
#ifdef __OPENDISK_SOLUTION__
				, user_info.szVolumeID, user_info.szVolumeID
#else
				, user_info.szUserID, user_info.szVolumeID
#endif
				, db_r->m_fake_uri
				, db_r->m_filename_org
				, db_r->m_filename_hash
				, db_r->m_depth
				, db_r->m_creation_date
				, db_r->m_displayname
				, db_r->m_get_content_length
				, db_r->m_get_content_type
				, db_r->m_get_lastmodified
				, db_r->m_get_lastmodified
				, db_r->m_source
				, dsc->host_name
				);
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
				query = apr_psprintf(db_r->p, 
				"INSERT INTO %s ( "
				" resource_type,"
#ifdef __OPENDISK_SOLUTION__
				" subuser_id, owner_id,"
#else
				" sp_user_seq, sp_svc_tran_id,"
#endif
				" uri,"
				" filename_org,"
				" filename_hash,"
				" depth,"
				" creation_date,"
				" display_name,"
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" get_content_length,"
				" get_content_type,"
				" get_lastmodified,"
				" file_lastmodified,"  // ADD 2010-01-19 huibong
				" source,"
				" host_name,"
				" deleted_yn ) "
				"VALUES "
				" ( %d , %s, %s,'%s', '%s', '%s', %d, %"APR_INT64_T_FMT ", '%s', %" APR_INT64_T_FMT ", '%s', %" APR_INT64_T_FMT ", %" APR_INT64_T_FMT ", %d, '%s', 'N' ) "
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, db_r->m_resource_type
#ifdef __OPENDISK_SOLUTION__
				, user_info.szVolumeID, user_info.szVolumeID
#else
				, user_info.szUserID, user_info.szVolumeID
#endif
				, db_r->m_fake_uri
				, db_r->m_filename_org
				, db_r->m_filename_hash
				, db_r->m_depth
				, db_r->m_creation_date
				, db_r->m_displayname
				, db_r->m_get_content_length
				, db_r->m_get_content_type
				, db_r->m_get_lastmodified
				, db_r->m_get_lastmodified
				, db_r->m_source
				, dsc->host_name
				);
	}
	
	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "db_insert_resource: database acquire timeout", 0);
		return -1;
	}

	// insert query 실행
	rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
	if( rv ) 
	{
		// insert query 실패 발생시
		db_error_message(db, "db_insert_resource: insert fail.", rv);
		dav_repos_release_db(db_r->r, db, rv);
	
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "db_insert_resource: insert query fail.");
		return -1;
	}

	// 2012-04-04 dadamin : 세션 해지 기능 삭제 처리
	//	동일 세션에 대해서 해당 시퀀스 마지막 값을 요청해야 하기 때문에 해당 세션을 계속 유지하여 사용

	// INSERT 처리한 row 에 대한 resource_id 값을 조회하여 인자로 반환.
	
	// 2012-03-15 huibong
	// insert 작업 후 resource_id 조회 기능은 현재 pgpool 전환으로 인해 의미가 없지만..
	// 향후 resource_id 정보 재사용을 고려하고 있으므로 현재 방식 그대로 유지한다.
	query = NULL;
	query = apr_psprintf(db_r->p, "SELECT last_value FROM t_dav_resource_resource_id_seq ");

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
	if( rv ) 
	{
		db_error_message(db, "db_insert_resource: select", rv);
		dav_repos_release_db(db_r->r, db, rv);

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "db_insert_resource: sequence select query fail.");
		return -1;
	}
	
	rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, 0);
	if( rv ) 
	{
		db_error_message(db, "db_insert_resource: get row", rv);
		dav_repos_release_db(db_r->r, db, rv);

		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "db_insert_resource: sequence select fetch fail.");
		return -1;
	}

	*resource_id = atoll(apr_dbd_get_entry(db->driver, row, 0)) + 1;
	dav_repos_release_db(db_r->r, db, rv);

	return 0;
}

/**
 * Set a property on a resource
 * @param d DB connection struct containing the user, password, and DB name
 * @param r Identifies the resource to set the property on r->uri. 
 *          Contains the live property and value to set.
 * @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
 * @return The serial number of the resource, or -1 on fail
 */

// CHG 2012.07.12 dadamin
// close 관련 구분 인자 추가
// CHG 2012-03-23 huibong
// 서비스별 대소문자 구분, 무시 기능 추가를 위해 본 함수를 수정 처리
// 그 외에 함수 상에 불필요한 소스 정리 처리
// CHG 2014.07.07 dadamin
// flag 인자 추가(write trunc 경우에 대한 사이즈 오류 방지)
// CHG 2015.06.04 dadamin
// end_pos 인자 추가 (요청 처리 후 file end position 추가)
apr_int64_t  dbms_set_property(dav_repos_resource *db_r, const dav_resource *resource, bool is_uricaseignore, bool is_close, int flag, apr_off_t end_pos)
{
	char *query = NULL;
	apr_int64_t l_resource_id = 0;
	char tmpOrgURI[ MaxSizeOfFileNameLength + MaxSizeOfFileNameLength ];
	
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	
	int nrows;
	bool is_insert = false;
	dav_repos_server_conf *dsc;
	char tblname[20] = {0};

	dsc = dav_repos_get_server_conf( db_r->r->server );
	if( !dsc ) 
	{
		// conf 정보를 가져오지 못한 경우.
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: dav_repos_get_server_conf func fail.");

		// CHG 2012-03-15 huibong
		// 본 함수를 사용하는 코드에서는 -1 을 반환할 경우에만 오류로 처리...
		// 따라서 아래 코드는 잘못되었으므로 수정 처리
		//return -7;
		return -1;
	}

	// 2014-04-24 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: at_split func fail.");
		return -1;
	}

	// CHG 2012-03-23 huibong
	// 서비스별 URI 대소문자 구분/무시 기능 추가
	
	// 요청한 URI 가 기존에 존재하는지 확인
	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		query = apr_psprintf( db_r->p, 
				"SELECT resource_id "
				"FROM %s "
				"WHERE sp_svc_tran_id = %s AND depth = %d AND %s = lower('%s') "
				" AND deleted_yn = 'N' "
				" AND is_cache = 'N'"
				, dav_get_target_table(user_info.szVolumeID, tblname)
				,user_info.szVolumeID
				, db_r->m_depth
				// 2014.06.12
				// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
				, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
				, db_r->m_fake_uri );
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		query = apr_psprintf( db_r->p, 
				"SELECT resource_id "
				"FROM %s "
				"WHERE sp_svc_tran_id = %s AND depth = %d AND uri = '%s' "
				" AND deleted_yn = 'N' "
				" AND is_cache = 'N'"
				, dav_get_target_table(user_info.szVolumeID, tblname), user_info.szVolumeID, db_r->m_depth, db_r->m_fake_uri );
	}

	db = dav_repos_get_db( db_r->r );
	if(db == NULL)
	{
		db_error_message(db, "dbms_set_property: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);	
	if( rv ) 
	{
		db_error_message(db, "dbms_set_property: select query fail.", rv);;
		dav_repos_release_db(db_r->r, db, rv);

		return -1;
	}

	nrows = apr_dbd_num_tuples(db->driver, res);

	// 2012.07.13 dadamin
	// rows == 0 && close 이면 config의 DavProviderCloseInsert On 일때 
	// 해당 rows를 insert 처리 한다.
	if(is_close)
	{
		// 존재하는 경우 update 
		if( nrows > 0 )
		{
			is_insert = false;
		}
		else 
		{
			// 존재 하지 않을 때
			// config 상 DavProviderCloseInsert On 일때 insert 처리 
			if( dsc->close_insertoff )
			{
				// error 리턴
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: Close & Not Fond Mate info.[%s]", db_r->m_fake_uri);
				dav_repos_release_db(db_r->r, db, rv);
				return -1;

			}
			else 
			{
				is_insert = true;
				//단순 logging
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: Close & insert[%s]", db_r->m_fake_uri);
			}
		}
	}
	else 
	{		
		if( nrows == 0)
		{
			// 신규 생성
			is_insert = true;
		}
		else 
		{
			// open 이면서, rows 가 존재하는 경우
			// rows 1개인 경우 : 이어 올리기, range put
			// rows 2개 이상인 경우 : 동일한 시각대 동일한 파일에 대한 요청
			is_insert = false;
		}
	}

	// 요청한 URI 가 Meta 상에 존재하지 않는 경우 -> 신규로 insert 처리
	if( is_insert ) 
	{
		// 기존 RCDB 연결 해제
		dav_repos_release_db(db_r->r, db, rv);

		// RCDB 상에 해당 URI 정보를 INSERT 처리

		// CHG 2012-03-23 huibong
		// db_insert_resource 함수상에 대소문자 구분 여부 정보를 인자로 전달토록 수정..
		// 하지만 NTFS 와 동일하게 나타나도록 하기 위해서는 입력 받은 값을 그대로 저장 처리한다.
		// 따라서 서비스에 상관없이 is_uricaseignore 값을 항상 false 로 처리하여... 대소문자가 구분된 상태로 INSERT 되도록 한다.
		//if( db_insert_resource( db_r, &l_resource_id, resource, false ) != 0 ) 
		// 2014.06.12 dadamin
		// 신규 형상(9.3)에서 대소문자 구분 기능을 display name 컬럼을 연계하여 지원하며,
		// 기존 uri에 lower된 값을 저장하기 위해서 대소문자 구분값을 전달한다.
		// 단, 기존 형상 지원을 위해서 기존 형상인 경우 기존과 동일하게 false 전달
		if( dav_use_display_name() == 0)
			is_uricaseignore = false;
		if( db_insert_resource( db_r, &l_resource_id, resource, is_uricaseignore ) != 0 ) 
		{
			// RCDB INSERT 작업 실패시
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: db_insert_resource func fail.");

			// CHG 2012-03-15 huibong
			// 본 함수를 사용하는 코드에서는 -1 을 반환할 경우에만 오류로 처리...
			// 따라서 아래 코드는 잘못되었으므로 수정 처리
			//return -2;
			return -1;
		} 
		else 
		{
			// RCDB INSERT 작업 성공시 해당 resouce_id 필드 값을 반환.
			return l_resource_id;
		}
	} 
	else // 요청한 URI 정보가 이미 Meta 상에 존재하는 경우 -> 기존 정보를 update 처리
	{
		// 앞의 SELECT Query 에서 나온 결과 중 
		// 첫번째 결과 값의 resource_id 값 정보를 반환 처리
		rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, 0 );
		if( rv ) 
		{
			// DB Fectch 작업시 오류가 발생한 경우
			db_error_message(db, "dbms_set_property: get_row function fail.", rv);
			dav_repos_release_db(db_r->r, db, rv);

			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: solbox_apr_dbd_get_row func fail.");

			// CHG 2012-03-15 huibong
			// 본 함수를 사용하는 코드에서는 -1 을 반환할 경우에만 오류로 처리...
			// 따라서 아래 코드는 잘못되었으므로 수정 처리
			//return -3;
			return -1;
		}

		// 반환값으로 사용할 resource_id 정보 추출.
		l_resource_id = atoll(apr_dbd_get_entry(db->driver, row, 0));

		// RCDB 연결 해제
		dav_repos_release_db(db_r->r, db, rv);


		// NEW 2019-02-20 huibong RCDB deadlock 을 발생시키는 불필요한 폴더 정보 업데이트 상황 발생 회피 (#32499)
		//                - MKCOL 요청에 대해
		//                - 해당 폴더가 존재하지 않는 경우.. 본 함수가 호출되어 INSERT QUERY 가 수행됨.
		//                - 해당 폴더가 존재하는 경우.. Apache 기본 DAV 에서 HTTP 405 Method Not Allowed 응답 처리됨.
		//                - 아래의 폴더 정보 UPDATE 가 호출되는 상황은 전혀 예상치 못한 상황임
		//                  1. MKCOL 요청에 대해 Apache 기본 DAV 에서 1차로 확인시 해당 폴더가 존재하지 않아..
		//                  2. 이에 대한 생성을 위해 dav_repos_create_collection() -> dbms_set_property() 순으로 호출..
		//                  3. 그런데.. 본 함수인 dbms_set_property() 에서 확인시 존재하지 않던 폴더가 갑자기 존재하여...
		//                  4. 전혀 예상치 않은 폴더에 대한 UPDATE query 가 수행됨 ( 아래의 UPDATE query 는 실제로 파일에 대한 크기 갱신을 위한 UPDATE 처리 구문임)
		//                - 폴더에 대한 업데이트 상황은 비상식적인 고객사 user 들이 거의 동시에 폴더에 대한 MKCOL 을 전송하면서
		//                  아주 희박하던 폴더 업데이트가 동시에 발생하여 DB deadlock 발생. 이로 인해 최종 응답은 HTTP 500 이 발생.
		//                - 따라서 이미 생성된 폴더에 대해 정보를 다시 업데이트할 필요 없으므로...
		//                  대상이 폴더인 경우.. 바로 반환 처리하여 아래의 update flow 가 동작하지 않도록 한다.
		if( db_r->m_resource_type == 1 )
		{
			return l_resource_id;
		}


		// 문자열 형식 변경 처리
		dav_repos_replace_unsafe_chars(db_r->m_filename_org, tmpOrgURI);

		// NEW 2012-03-15 huibong
		// 기존 Data 에 대한 UPDATE 처리시..
		// 기존 Data 가 2건 이상 존재할 경우 향후 문제점 분석 등을 위해 로깅 처리한다.
		if( nrows > 1 )
		{
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: update row multiple.[%s][%d][%s]"
					, (is_uricaseignore == true ? "ignore case-sensitive" : "case-sensitive"), nrows, db_r->m_fake_uri );
		}

		apr_int64_t get_content_length = db_r->m_get_content_length;

		// 2014.01.09 dadamin
		// DB Pool 획득 전후 수행하던 로직은 Pool획득 이후로 수정함
		// write close 시 파일 사이즈 확인 필요
		if(db_r->m_resource_type == 0 && is_close)
		{
			struct stat fs;
			int stat_ret = 0;
			memset(&fs , 0x00 , sizeof(struct stat));
			stat_ret = lstat ( db_r->m_filename_hash, &fs ) ;
			if(stat_ret < 0)
			{
				ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: lstat func fail[%d]. uri[%s][%" APR_INT64_T_FMT "] name[%s][%" APR_OFF_T_FMT "] err[%d][%s]"
					, stat_ret
					, db_r->m_fake_uri, get_content_length
					, db_r->m_filename_hash, fs.st_size
					, errno, strerror(errno)
					);
			}
			else
			{
				get_content_length = fs.st_size;
			}

			// 2015.06.04 dadamin
			// 현재 요청에 대한 처리 후 파일크기와 물리적인 파일 크기 동일한지 비교
			// 이를 통해서 멀티 블록 업로드 시 불필요한(?) DB 작업을 줄임
			if (get_content_length != end_pos)
			{
				//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: write end and skip update. uri[%s][%" APR_OFF_T_FMT "] name[%s][%" APR_OFF_T_FMT "]."
				//	, db_r->m_fake_uri, end_pos
				//	, db_r->m_filename_hash, fs.st_size
				//	);

				return l_resource_id;
			}
		}
		// 2014.06.16 dadamin
		// 완료 후 업로드 size 고객사로 보낼 때 해당 값이 '0' 보내지는 버그 수정
		db_r->m_get_content_length = get_content_length;

		// 2014.08.24 dadamin
		// buffer overflow 발생으로 해당 크기 변경함
		// 2014.07.07 dadamin
		// write trunc 경우에 대한 사이즈 오류 개선
		char szLengthSql[255] = {0};
		if((flag & APR_TRUNCATE) != APR_TRUNCATE) 
		{
			// 2014.01.08 dadamin
			// Soldrive 경우 병렬 블록 쓰는 기능 때문에 논리적인, 물리적인 다른 경우가 발생하며,
			// Pool 획득 후 로컬 파일 사이즈 재체크로도 해당 문제를 해결되지 않아서,
			// 파일 크기 줄어드는 경우가 없기 때문에 항상 큰 값을 유지할 수 있게 수정함
			sprintf(szLengthSql, "GREATEST( %" APR_INT64_T_FMT ", get_content_length )", db_r->m_get_content_length);
		}
		else
		{
			sprintf(szLengthSql, "%" APR_INT64_T_FMT, db_r->m_get_content_length );
		}

		// CHG 2010-03-19 webting
		// Meta 정보 업데이트시 get_lastmodified 값을 기존 조회된 값을 사용토록 되어 있음.
		// 이로 인해 replication 생성 등의 여러가지 문제점이 발생하여..
		// 기존 정보 변경 update 시 get_lastmodified 값이 현재값으로 설정되도록 수정 처리함.
		// 변경전/후: db_r->m_get_lastmodified => apr_time_now()

		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET "
					" resource_type = %d, "
					" filename_org = '%s', "
					" filename_hash = '%s', "
					" depth = %d, "
					" creation_date = %" APR_INT64_T_FMT ", "
					// 2015.06.11 : 불필요한 display_name 컬럼 업데이트 삭제
					//" display_name = '%s', "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" get_content_length = %s, "
					" get_content_type = '%s', "
					" get_lastmodified = %" APR_TIME_T_FMT ", "
					" source = %d, "
					" host_name = '%s' "
					"WHERE "
					" sp_svc_tran_id = %s AND depth = %d AND"
					" %s = lower('%s') AND host_name = '%s' AND filename_hash = '%s' AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r->m_resource_type
					, tmpOrgURI
					, db_r->m_filename_hash
					, db_r->m_depth 
					, db_r->m_creation_date
					//, db_r->m_displayname
					, szLengthSql
					, db_r->m_get_content_type
					, apr_time_now()
					, db_r->m_source
					, dsc->host_name
					, user_info.szVolumeID
					, db_r->m_depth
					// 2014.06.12
					// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
					, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
					, db_r->m_fake_uri
					, dsc->host_name
					, db_r->m_filename_hash );
		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET "
					" resource_type = %d, "
					" filename_org = '%s', "
					" filename_hash = '%s', "
					" depth = %d, "
					" creation_date = %" APR_INT64_T_FMT ", "
					// 2015.06.11 : 불필요한 display_name 컬럼 업데이트 삭제
					//" display_name = '%s', "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" get_content_length = %s, "
					" get_content_type = '%s', "
					" get_lastmodified = %" APR_TIME_T_FMT ", "
					" source = %d, "
					" host_name = '%s' "
					"WHERE "
					" sp_svc_tran_id = %s AND depth = %d AND"
					" uri = '%s' AND host_name = '%s' AND filename_hash = '%s' AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r->m_resource_type
					, tmpOrgURI
					, db_r->m_filename_hash
					, db_r->m_depth 
					, db_r->m_creation_date
					//, db_r->m_displayname
					, szLengthSql
					, db_r->m_get_content_type
					, apr_time_now()
					, db_r->m_source
					, dsc->host_name
					, user_info.szVolumeID
					, db_r->m_depth
					, db_r->m_fake_uri
					, dsc->host_name
					, db_r->m_filename_hash);

		}

		/*Execute the update command */
		db = dav_repos_get_db( db_r->r );
		if(db == NULL)
		{
			db_error_message(db, "dbms_set_property: database acquire timeout", 0);
			return -1;
		}

		// Update Query 실행
		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
		if( rv ) 
		{
			// Query 실행 실패시
			db_error_message(db, "dbms_set_property: update", rv);
			dav_repos_release_db(db_r->r, db, rv);
			
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_set_property: update query fail.");
			return -1;
		}

		dav_repos_release_db(db_r->r, db, rv);

		return l_resource_id;
	}

}

/**
 * Checks a resource by its serialno whether it is in the resource link or not if exists, set locate_resource to the pointer of the resource whose serialno is 'serialno'
 * @param bd_r A linked list of resources to check
 * @param serialno The serial number of the resource to look for
 * @param resource_pointer Out variable which points to the resource, if found
 * @return 1 if the resource exists, otherwise 0 
 */
static int resource_in_link(dav_repos_resource * db_r, apr_int64_t resource_id, dav_repos_resource ** locate_resource)
{
	dav_repos_resource *resource_pointer = NULL;
	resource_pointer = db_r;

	while (resource_pointer != NULL) 
	{
		if (resource_pointer->m_resource_id == resource_id) 
		{
			*locate_resource = resource_pointer;
			return 1;
		}

		resource_pointer = resource_pointer->next;
	}

	*locate_resource = NULL;

    return 0;
}


/**
 * Inserts a dead property struct into the dead property list of the current resource node, if name does exist in the dead property list
 * @param db_r The resource to add the property to
 * @param resource_node ???
 * @param name The name of the property to add
 * @param value The value of the property to add
 */
static void add_dead_property_inlink(dav_repos_resource * db_r, dav_repos_resource * resource_node, char *name, char *value)
{
	dav_repos_resource *presource_node = resource_node;
	dav_repos_property *pproperty_node = NULL;
	
	/*Is the previous node of pproperty_node */
	dav_repos_property *pupper_property = NULL;

	dav_repos_property *pnew_property = NULL;

	pproperty_node = presource_node->pr;
	pupper_property = presource_node->pr;

	/*check whether the property with the name 'name' exists
	 *in the property link or not*/

	while ((pproperty_node != NULL) && (apr_strnatcmp(pproperty_node->m_propname, name) != 0)) 
	{
		pupper_property = pproperty_node;
		pproperty_node = pproperty_node->next;
	}

	if( pproperty_node == NULL ) 
	{
		/*'name' does not exist, so add a new property node */
		pnew_property = apr_pcalloc(db_r->p, sizeof(*pnew_property));
		pnew_property->m_propname = apr_pstrndup(db_r->p, name, sizeof(name));
		pnew_property->m_propvalue = apr_pstrndup(db_r->p, value, sizeof(value));
		pnew_property->next = NULL;

		if (presource_node->pr == NULL)	/*Property link is originally empty */
			presource_node->pr = pnew_property;
		else	/*Add the new node to the tail of the link */
			pupper_property->next = pnew_property;
	}
}

/**
 * Execute the search query and return the search result in the link db_r including property link in db_r
 * @param d DB connection struct containing the user, password, and DB name
 * @param search_command SQL query to execute, the serialno field must always appear in fields of the search_command 
 * @param db_r Out variable containing the result of the search. The result starts from db_r->next
 * @return 0 on success, -1 on failure
 */
int dbms_search(char *query, dav_repos_resource *db_r)
{
	int tup = 0;
	int iret = 0;
	int bfields_got = 0;
	unsigned int i = 0;
	unsigned int j = 0;
	unsigned int num_fields = 0;
	char szRealURI[MaxSizeOfPathLength];
	dav_repos_server_conf *dsc;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;

	/* Pointer to the tail of the resource link */
	struct dav_repos_resource *pr_tail = NULL;
	/* Pointer to the new item of resource */
	struct dav_repos_resource *pnew_link_item = NULL;
	/* Pointer to resource in the link currently being set */
	struct dav_repos_resource *presource_current_set = NULL;

	dsc = dav_repos_get_server_conf(db_r->r->server);
	if( !dsc ) 
	{
		return -7;
	}

	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_search: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
	if( rv ) 
	{
		db_error_message(db, "dbms_search: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}

	pr_tail = db_r;
	presource_current_set = db_r;
	pr_tail->next = NULL;
	pr_tail->pr = NULL;

	tup = apr_dbd_num_tuples(db->driver, res);

	/* Deal with every records from the search result */
	for( i = 0; i < tup; i++ ) 
	{
		int resource_exists = 0;	/*resource exists in the resource link */
		apr_int64_t resource_id = 0;	/*resource_id  */

		if( bfields_got == 0 ) 	/*not got the fields yet */
		{
			/*Get the fields of the search command, because the fields in the command are not fixed*/
			num_fields = apr_dbd_num_cols(db->driver, res);
			if (num_fields == 0) 
			{
				db_error_message(db, "dbms_search: field zero", 0);
				dav_repos_release_db(db_r->r, db, rv);
				return -2;
			}

			bfields_got = 1;
		}

		rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, i);
		if( rv ) 
		{
			db_error_message(db, "dbms_search: get row", rv);
			dav_repos_release_db(db_r->r, db, rv);
			return -1;
		}

		/*Check where resource has existed in the resource link */
		resource_id = atoll(apr_dbd_get_entry(db->driver, row, 0));
		resource_exists = resource_in_link(db_r->next, resource_id, &presource_current_set);

		if( resource_exists == 0 ) /*Resource not in the resource link */
		{
			/*Resource not the resource link then create a new struct node Malloc a new structure */
			pnew_link_item = apr_pcalloc(db_r->p, sizeof(*pnew_link_item));
			pr_tail->next = pnew_link_item;
			pr_tail = pnew_link_item;

			/*Set value of the record by every field */
			for( j = 0; j < num_fields; j++ ) 
			{
				/*i is the number of the column starting from 0 */
				/*Get the data of every column of this row to a link */

#define SEARCH_COMPARE_INDEX(index)	\
				apr_strnatcasecmp(apr_dbd_get_name(db->driver, res, j), index)

				if( SEARCH_COMPARE_INDEX("resource_id") == 0) {
					pr_tail->m_resource_id = atoll(apr_dbd_get_entry(db->driver, row, j));
				}
				else if( SEARCH_COMPARE_INDEX("filename_org") == 0) {
					STRING_FROM_DBENTRY(db_r->p, pr_tail->m_filename_org, j);
				}
				else if( SEARCH_COMPARE_INDEX("filename_hash") == 0) {
					STRING_FROM_DBENTRY(db_r->p, pr_tail->m_filename_hash, j);
				}
				else if( SEARCH_COMPARE_INDEX("uri") == 0) 
				{
					STRING_FROM_DBENTRY(db_r->p, pr_tail->m_fake_uri, j);
					remove_user_root_dir_from_fake_uri(pr_tail->m_uri, szRealURI);
					pr_tail->m_uri = apr_psprintf(db_r->p, "%s%s", dsc->base_uri, szRealURI);
				} 
				else if( SEARCH_COMPARE_INDEX("creation_date") == 0) 
				{
					pr_tail->m_creation_date = atoll(apr_dbd_get_entry(db->driver, row, j));
				} 
				else if( SEARCH_COMPARE_INDEX("display_name") == 0) {
					STRING_FROM_DBENTRY(db_r->p, pr_tail->m_displayname, j);
				} 
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				else if( SEARCH_COMPARE_INDEX("get_content_type") == 0) {
					STRING_FROM_DBENTRY(db_r->p, pr_tail->m_get_content_type, j);
				} 
				else if( SEARCH_COMPARE_INDEX("get_content_length") == 0) {
					pr_tail->m_get_content_length =
						atol(apr_dbd_get_entry(db->driver, row, j));
				} 
				else if( SEARCH_COMPARE_INDEX("get_lastmodified") == 0) {
					pr_tail->m_get_lastmodified =
						atoll(apr_dbd_get_entry(db->driver, row, j));
				} 
				else if( SEARCH_COMPARE_INDEX("resource_type") == 0) {
					pr_tail->m_resource_type =
						atoi(apr_dbd_get_entry(db->driver, row, j));
				} 
				else if( SEARCH_COMPARE_INDEX("source") == 0) {
					pr_tail->m_source =
						atoi(apr_dbd_get_entry(db->driver, row, j));
				} 
				else if( SEARCH_COMPARE_INDEX("depth") == 0 ) 
				{
					pr_tail->m_depth = atoi(apr_dbd_get_entry(db->driver, row, j));
				} 
			}			/*End of for every field */

			/*Used when setting dead properties */
			presource_current_set = pr_tail;
		}

		/*deal with the dead property section in this row */
		j = 0;

		/*Locate the dead properties, 'name' and 'value', in the query command */
		while( (j < num_fields) && (SEARCH_COMPARE_INDEX("name") != 0)) 
		{
			j++;
		}

#undef SEARCH_COMPARE_INDEX

		if( j < num_fields) 
		{
			/* Found the name field */
			/* Insert a dead property node to the dead property link
			 * in the current resource node, if name does exist in the
			 * dead property link*/
			add_dead_property_inlink(db_r, presource_current_set,
					(char *)apr_dbd_get_entry(db->driver, row, j) /*name */ ,
					(char *)apr_dbd_get_entry(db->driver, row, j + 1) /*value */ );
		}

		pr_tail->next = NULL;
		pr_tail->pr = NULL;
	}

	dav_repos_release_db(db_r->r, db, rv);

	pr_tail = db_r;
	while (pr_tail != NULL) 
	{
		pr_tail = pr_tail->next;
	}

	return iret;
}


// NEW 2010-03-24 webting
// dbms.h의 dbms_remove_replicate 함수 정의에서 내용 확인 요망.
// CHG 2012-03-23 huibong
// 서비스별 대소문자 구분, 무시 기능 추가를 위해 본 함수를 수정 처리
// @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
// QUERY_AND_RETRY 매크로의 반환값 처리로 인해... 반환값 void -> int 로 수정 처리
// @return 0 on success, -1 on failure
int dbms_remove_replicate( const dav_repos_resource* db_r, bool is_uricaseignore )
{
	char* query = NULL;
	ap_dbd_t* db;
	apr_status_t rv = 0;
	int nrows;
	char tblname[20] = {0};

	// 2014-04-25 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_remove_replicate: at_split func fail.");
		return -1;
	}
	
	db = dav_repos_get_db(db_r->r);
	if( db == NULL )
	{
		db_error_message(db, "dbms_remove_replicate: database acquire timeout", 0);
		return -1;
	}

	if( db_r->m_resource_type == 0)
	{
		/* 2010.12.09 : dadamin / 양방향의 적용된(Revision 0237) 내용 적용 */

		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET deleted_yn = 'Y', get_lastmodified = %" APR_TIME_T_FMT " "
					"WHERE sp_svc_tran_id = %s AND depth = %d AND %s = lower('%s') AND deleted_yn = 'N' AND is_cache != 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, apr_time_now()
					, user_info.szVolumeID, db_r->m_depth
					// 2014.06.12
					// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
					, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
					, db_r->m_fake_uri 
					);
		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET deleted_yn = 'Y', get_lastmodified = %" APR_TIME_T_FMT" "
					"WHERE sp_svc_tran_id = %s AND depth = %d AND uri = '%s' AND deleted_yn = 'N' AND is_cache != 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, apr_time_now()
					, user_info.szVolumeID, db_r->m_depth, db_r->m_fake_uri 
					);
		}
		
		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
		if( rv ) 
		{
			db_error_message(db, "dbms_remove_replicate: update: file", rv);
			ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_remove_replicate query fail [%s]", db_r->m_fake_uri);
		}
	}

	dav_repos_release_db(db_r->r, db, rv);

	return 0;
}


/**
 * Removes a resource from the repository. If the resource is collection, all the resources it contains will also be removed.
 * @param d DB connection struct containing the user, password, and DB name.
 * @param r A pointer to the resource to be removed.
 * @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
 * @return 0 on success, -1 on failure
 */
int dbms_remove_resource( const dav_repos_resource *db_r,  bool is_uricaseignore )
{
	char *query = NULL;
	ap_dbd_t *db;
	apr_status_t rv = 0;
	int nrows;
	int retval = 0;
	char tblname[20] = {0};
	
	// 2014-04-28 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_remove_resource: at_split func fail.");
		return -1;
	}

	// RCDB 연결
	db = dav_repos_get_db(db_r->r);
	if( db == NULL )
	{
		db_error_message(db, "dbms_remove_resource: database acquire timeout", 0);
		return -1;
	}

	// 요청 대상이 file 인지 directory 인지 검사.
	if( db_r->m_resource_type == 0 )	// 삭제 요청 대상이 file 인 경우
	{
		// CHG 2012-03-21 huibong
		// 서비스별 대소문자 무시 기능 추가 
		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET deleted_yn = 'Y', get_lastmodified = %" APR_TIME_T_FMT " "
					"WHERE sp_svc_tran_id = %s AND depth = %d AND "
					"%s = lower('%s') AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, apr_time_now(), user_info.szVolumeID, db_r->m_depth
					// 2014.06.12
					// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
					, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
					, db_r->m_fake_uri
					);
		}
		else  // 양방향 기본 ( URI 대소문자 구분 )
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET deleted_yn = 'Y', get_lastmodified = %" APR_TIME_T_FMT " "
					"WHERE sp_svc_tran_id = %s AND depth = %d AND "
					"uri = '%s' AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, apr_time_now(), user_info.szVolumeID, db_r->m_depth, db_r->m_fake_uri
					);
		}

		// Query 수행
		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 

		// Query 수행 오류 발생시 반환값 조정.
		if( rv ) 
		{
			db_error_message( db, "dbms_remove_resource: file delete fail", rv );;
			retval = -1;
		}
	} 
	else if( db_r->m_resource_type == 1 )	// 삭제 요청 대상이 directory 인 경우 : 해당 Directory 및 하위 directory, content 모두 삭제 처리.
	{
		// 2016.04.23 LIKE 구문 특수 문자 제거(_,%)

		/* 2010.10.13 : dadmain / 8.1.2 -> 8.4.5 변경시 발생한 쿼리 warning 제거 */ 
		
		// CHG 2012-03-21 huibong
		// 서비스별 대소문자 무시 기능 추가 
		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET deleted_yn = 'Y', get_lastmodified = %" APR_TIME_T_FMT " "
					"WHERE sp_svc_tran_id = %s AND depth >= %d "
					"AND ( %s = lower('%s') OR %s LIKE regexp_replace( lower('%s/'), '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, apr_time_now(), user_info.szVolumeID, db_r->m_depth
					// 2014.06.12
					// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
					, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
					, db_r->m_fake_uri
					// 2014.06.12
					// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
					, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
					, db_r->m_fake_uri
					);
		}
		else	// 양방향 기본 ( URI 대소문자 구분 )
		{
			query = apr_psprintf( db_r->p, 
					"UPDATE %s "
					"SET deleted_yn = 'Y', get_lastmodified = %" APR_TIME_T_FMT " "
					"WHERE sp_svc_tran_id = %s AND depth >= %d "
					"AND ( uri = '%s' OR uri LIKE regexp_replace('%s/', '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, apr_time_now(), user_info.szVolumeID, db_r->m_depth, db_r->m_fake_uri, db_r->m_fake_uri
					);
		}

		// Query 수행
		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 

		// Query 수행 오류 발생시 반환값 조정.
		if( rv ) 
		{
			db_error_message( db, "dbms_remove_resource: directory delete fail", rv );;
			retval = -2;
		}
	}

	// Logging 처리 목적
	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_remove_resource completed. uri[%s][%s] rv[%d] retval[%d]"
			, db_r->m_fake_uri
			, ( is_uricaseignore == true ? "uri case ignore" : "uri case sensitive" )
			, rv, retval
			);

	// RCDB 연결 해제
	dav_repos_release_db( db_r->r, db, rv );

	return retval;
}


// NEW 2010-01-18 huibong 
// client ModifyTime filed send to dav. This value is File attribute.
// So we saved this value to file_lastmodified column, not get_lastmodified column.
// CHG 2012-03-23
// 서비스별 URI 대소문자 구분/무시 기능 추가를 위해 is_uricaseignore 인자값 추가
// is_uricaseignore true: URI 대소문자 무시, false: URI 대소문자 구분 (default)
int dbms_update_file_lastmodified(const dav_repos_resource *db_r, apr_int64_t mtime, bool is_uricaseignore)
{
	char *query = NULL;
	ap_dbd_t *db;
	apr_status_t rv = 0;
	int nrows;
	int retval = 0;
	char tblname[20] = {0};

	// 2014-04-28 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_update_file_lastmodified: at_split func fail.");
		return -1;
	}

	db = dav_repos_get_db( db_r->r );
	if(db == NULL)
	{
		db_error_message(db, "dbms_update_file_lastmodified: database acquire timeout", 0);
		return -1;
	}

	// CHG 2011-11-29 huibong
	// file_lastmodified 값 변경시 get_lastmodified 값도 현재 시간으로 변경되도록 수정 처리
	// 본 수정은 서비스간 동기화 처리 기능 지원 목적임.
	
	// CHG 2012-03-23 huibong
	// 서비스별 URI 대소문자 구분/무시 기능 추가를 위한 Query 구문 변경
	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		query = apr_psprintf( db_r->p, 
				"UPDATE %s "
				"SET file_lastmodified = %" APR_INT64_T_FMT ", get_lastmodified = %" APR_TIME_T_FMT " "
				"WHERE sp_svc_tran_id = %s AND depth = %d AND %s = lower('%s') AND deleted_yn = 'N'"
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, mtime, apr_time_now(), user_info.szVolumeID, db_r->m_depth
				// 2014.06.12
				// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
				, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
				, db_r->m_fake_uri);
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		query = apr_psprintf( db_r->p, 
				"UPDATE %s "
				"SET file_lastmodified = %" APR_INT64_T_FMT " , get_lastmodified = %" APR_TIME_T_FMT " "
				"WHERE sp_svc_tran_id = %s AND depth = %d AND uri = '%s' AND deleted_yn = 'N'"
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, mtime, apr_time_now(), user_info.szVolumeID, db_r->m_depth, db_r->m_fake_uri);
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
	if( rv ) 
	{
		db_error_message( db, "dbms_update_file_lastmodified: update: mtime", rv );
		retval = -1;
	}

	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_update_file_lastmodified completed. ModifyTime[%s][%"APR_INT64_T_FMT "] rv[%d] retval[%d]"
			, db_r->m_fake_uri, mtime, rv, retval);

	dav_repos_release_db(db_r->r, db, rv);

	return retval;
}

// 2013.08.21 dadamin
// Set hidden  property of DAV:(t_dav_resource)
// hidden : 1, not hidden : 0 
int dbms_update_hidden(const dav_repos_resource *db_r, int hidden , bool is_uricaseignore)
{
	char *query = NULL;
	ap_dbd_t *db;
	apr_status_t rv = 0;
	int nrows;
	int retval = 0;
	char tblname[20] = {0};

	// 2014-04-28 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_update_hidden: at_split func fail.");
		return -1;
	}

	// t_dav_resource 의 source 필드를 hidden 속성값을 의미
	// 팀 협의 완료되었으며, WebDAV source 와는 다름 의미로 사용하게 됨	
	db = dav_repos_get_db( db_r->r );
	if(db == NULL)
	{
		db_error_message(db, "dbms_update_hidden: database acquire timeout", 0);
		return -1;
	}

	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		query = apr_psprintf( db_r->p, 
			"UPDATE %s "
			"SET source = %d , get_lastmodified = %" APR_TIME_T_FMT " "
			"WHERE sp_svc_tran_id = %s AND depth = %d AND %s = lower('%s') AND deleted_yn = 'N'"
			, dav_get_target_table(user_info.szVolumeID, tblname)
			, hidden, apr_time_now(), user_info.szVolumeID, db_r->m_depth
			// 2014.06.12
			// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
			, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
			, db_r->m_fake_uri);
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		query = apr_psprintf( db_r->p, 
			"UPDATE %s "
			"SET source = %d , get_lastmodified = %" APR_TIME_T_FMT " "
			"WHERE sp_svc_tran_id = %s AND depth = %d AND uri = '%s' AND deleted_yn = 'N'"
			, dav_get_target_table(user_info.szVolumeID, tblname)
			, hidden, apr_time_now(), user_info.szVolumeID, db_r->m_depth, db_r->m_fake_uri);
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
	if( rv ) 
	{
		db_error_message( db, "dbms_update_hidden: update: hidden", rv );
		retval = -1;
	}

	ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_update_hidden completed. hidden[%s][%d] rv[%d] retval[%d]"
		, db_r->m_fake_uri, hidden, rv, retval);

	dav_repos_release_db(db_r->r, db, rv);

	return retval;
}

/**
 * Adds a new dead property record the the dead property table in the DB
 * @param d DB connection struct
 * @param r Identifies the resource to associate the property with
 * @param pr The property to insert
 * @return 0 on success, -1 on failure
 */
int db_insert_property( const dav_repos_resource *db_r, const dav_repos_property *pr )
{
    char *query = NULL;
	ap_dbd_t *db;
	apr_status_t rv;
	int nrows;
	char* szfilename_hash = NULL;

	// CHG 2019-08-28 huibong buffer overflow 수정 (#32780)
	// - buffer 크기를 32+1 -> 32 + 8 + 8 + 1 로 변경한다.
	char szTemp[49] = {0};

	// 2013.08.14 dadamin
	// pgpool 사용으로 resource_id가 RCDB01, RCDB02 서로 다른 경우 발생으로
	// 참조키를 filename_hash의 hash 값을 참조키로 변경함
	szfilename_hash = strrchr(db_r->m_filename_hash, '/');
	if(szfilename_hash) 	
	{
		szfilename_hash = szfilename_hash + 1;
	}
	else
	{
		// 디렉토리 경우 hash 없을 수 있으므로 etag rule 과 동일하게 생성
		sprintf( szTemp, "00000000000000000000000000000000%x%x",
			(unsigned int)db_r->m_get_content_length,
			(unsigned int)db_r->m_get_lastmodified );

		szfilename_hash = szTemp;
	}

	/* Create the insert command */
	query = apr_psprintf(db_r->p, 
			"INSERT INTO t_dav_property "
			"(ns_id, filename_hash, propname, propvalue) "
			"VALUES (%ld, '%s', '%s', '%s')"
			, pr->m_ns_id, szfilename_hash, pr->m_propname, pr->m_propvalue
			);

	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "db_insert_property: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query);
	if( rv ) 
	{
		db_error_message(db, "db_insert_property: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}
	
	dav_repos_release_db(db_r->r, db, rv);

    return 0;
}

/**
 * Set the value of a dead property on a resrouce identified by serialno
 * @param d DB connection struct
 * @param r Identified the resource to set the property on
 * @param pr The dead property to set
 * @return 0 on success, -1 on failure
 */
int dbms_set_dead_property(const dav_repos_resource *db_r,
			   const dav_repos_property *pr)
{
    char *query = NULL;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_status_t rv;
	int nrows;
	char *szfilename_hash = NULL;

	// CHG 2019-08-28 huibong buffer overflow 수정 (#32780)
	// - buffer 크기를 32+1 -> 32 + 8 + 8 + 1 로 변경한다.
	char szTemp[49] = {0};

	// 2013.08.14 dadamin
	// pgpool 사용으로 resource_id가 RCDB01, RCDB02 서로 다른 경우 발생으로
	// 참조키를 filename_hash의 hash 값을 참조키로 변경함
	szfilename_hash = strrchr(db_r->m_filename_hash, '/');
	if(szfilename_hash) 
	{
		szfilename_hash = szfilename_hash + 1;
	}
	else
	{
		// 디렉토리 경우 hash 없을 수 있으므로 etag rule 과 동일하게 생성
		sprintf( szTemp, "00000000000000000000000000000000%x%x",
			(unsigned int)db_r->m_get_content_length,
			(unsigned int)db_r->m_get_lastmodified );

		szfilename_hash = szTemp;
	}

	/* Create the select command to get the serialno by serialno */
	query = apr_psprintf(db_r->p, 
			"SELECT filename_hash "
			"FROM t_dav_property "
			"WHERE filename_hash = '%s' "
			" AND ns_id = %ld"
			" AND propname = '%s' "
			, szfilename_hash, pr->m_ns_id, pr->m_propname
			);

	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_set_dead_property: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);	
	if( rv ) 
	{
		db_error_message(db, "dbms_set_dead_property: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}

	if( apr_dbd_num_tuples(db->driver, res) == 0) 
	{
		/* No result */
		dav_repos_release_db(db_r->r, db, rv);

		/* Insert a new property record */
		if( db_insert_property(db_r, pr) != 0) 
		{
			return -2;
		} 
		else 
		{
			return 0;
		}
	}  

	/* Create update command */
	query = apr_psprintf(db_r->p, 
			"UPDATE t_dav_property "
			"SET propvalue='%s' "
			"WHERE filename_hash = '%s' AND ns_id = %ld AND propname='%s'"
			, pr->m_propvalue
			, szfilename_hash, pr->m_ns_id, pr->m_propname
			);

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
	if( rv ) 
	{
		db_error_message(db, "dbms_set_dead_property: update", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -3;
	}

	dav_repos_release_db(db_r->r, db, rv);
	return 0;
}


/**
 *	I'll fill pr->name , r->serialno and you will fill pr->value 
 * @param d DB connection struct
 * @param r Identifies the resource
 * @param pr Identifies the property to retrieve
 * @return 0 on success, 100 if not found, -1 on failure
 * */
int dbms_get_dead_property(const dav_repos_resource * db_r, dav_repos_property * pr)
{
    int iret = 0;
    char *query = NULL;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char * szfilename_hash = NULL;

	// CHG 2019-08-28 huibong buffer overflow 수정 (#32780)
	// - buffer 크기를 32+1 -> 32 + 8 + 8 + 1 로 변경한다.
	char szTemp[49] = {0};

	// 2013.08.14 dadamin
	// pgpool 사용으로 resource_id가 RCDB01, RCDB02 서로 다른 경우 발생으로
	// 참조키를 filename_hash의 hash 값을 참조키로 변경함
	szfilename_hash = strrchr(db_r->m_filename_hash, '/');
	if(szfilename_hash) 
	{
		szfilename_hash = szfilename_hash + 1;
	}
	else 
	{
		// 디렉토리 경우 hash 없을 수 있으므로 etag rule 과 동일하게 생성
		sprintf( szTemp, "00000000000000000000000000000000%x%x",
			(unsigned int)db_r->m_get_content_length,
			(unsigned int)db_r->m_get_lastmodified );

		szfilename_hash = szTemp;
	}

	/* Create the search command */
	query = apr_psprintf(db_r->p, 
			"SELECT ns_id, propvalue FROM t_dav_property "
			"WHERE filename_hash = '%s' "
			" AND ns_id = %ld"
			" AND propname = '%s' "			
			, szfilename_hash, pr->m_ns_id, pr->m_propname);

	/* Execute sequry */
	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_get_dead_property: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
	if( rv ) 
	{
		db_error_message(db, "dbms_get_dead_property: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}

	if( apr_dbd_num_tuples(db->driver, res) == 0 ) 
	{
		/* No result */
		iret = 100;
	} 
	else 
	{

		rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, 0);
		if(rv) 
		{
			iret = -1;
			db_error_message(db, "dbms_get_dead_property: get row", rv);
		}
		else 
		{
			/* One row fetched */
			/* TODO: may be.. */
			/* Get the data of every column of this row to a link */
			/* Fetch value column of the row */
			STRING_FROM_DBENTRY(db_r->p, pr->m_propvalue, 1);
			pr->next = NULL;
		}
	}

	dav_repos_release_db(db_r->r, db, rv);

	return iret;
}

/* Support function. update modified time */
int dbms_modify_resource(const dav_resource *resource)
{
#if 0
	const dav_repos_resource *db_r;
	char *query = NULL;
	ap_dbd_t *db;
	apr_status_t rv;
	int nrows;
	char tblname[20] = {0};

	if (!resource || !resource->info || !resource->info->db_r)
		return -1;
	if (resource->exists == 0)
		return -2;

	db_r = resource->info->db_r;

	// 2014-04-28 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_modify_resource: at_split func fail.");
		return -1;
	}

	//apr_thread_rwlock_wrlock(db_lock);
	query = apr_psprintf(db_r->p,
			"UPDATE %s "
			"SET get_lastmodified = %lld "
			"WHERE sp_svc_tran_id = %s AND depth = %d AND uri = '%s' AND deleted_yn = 'N' "
			, dav_get_target_table(user_info.szVolumeID, tblname)
			, apr_time_now(), user_info.szVolumeID, db_r->m_depth, db_r->m_fake_uri);

	DBG1("dbms_modify_resource: query: %s", query);
	//ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_modify_resource: query: %s", query);

	db = dav_repos_get_db(db_r->r);
	if (db == NULL ) {
		db_error_message(db, "dbms_modify_resource: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query);
	if (rv) {
		db_error_message(db, "dbms_modify_resource: update", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return -1;
	}

	dav_repos_release_db(db_r->r, db, rv);
	//apr_thread_rwlock_unlock(db_lock);
#endif

	return 0;
}

/**
 * Moves a resource. If the source is a regular file, 
 * just change the URI. If the source is collection, 
 * move all files in the collection 
 * @param d DB connection struct
 * @param db_r_src source 
 * @param db_r_des destination ( only contains URI field )
 * @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
 * @return 0 on success -1 on failure
 */
int dbms_move_resource(const dav_repos_resource * db_r_src, const dav_repos_resource * db_r_des, const dav_resource * resource, bool is_uricaseignore )
{
	int depth_diff = 0;
	char *query = NULL;
	ap_dbd_t *db;
	apr_status_t rv = 0;
	int nrows;
	char tblname[20] = {0};

	// 2014-04-28 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r_src->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_move_resource: at_split func fail.");
		return -1;
	}

	// 2014-04-28 : dadamin
	// depth 추출 로직 삭제 : 상위 로직에서 추출됨

	depth_diff = db_r_des->m_depth  - db_r_src->m_depth;

	db = dav_repos_get_db(db_r_src->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_move_resource: database acquire timeout", 0);
		return -1;
	}

	if( db_r_src->m_resource_type == 0 ) 
	{
		/* Is a regular file, not directory Change the URI directly */

		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분 기능을 display_name, uri 컬럼 활용함
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					" uri = lower('%s'), display_name = '%s',"
					" get_lastmodified = %" APR_TIME_T_FMT ", depth = depth + %d "					
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d AND uri = lower('%s') AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri					
					);
			}
			else
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					"  uri = '%s', get_lastmodified = %" APR_TIME_T_FMT ", depth = depth + %d "
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d AND lower(uri) = lower('%s') AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri					
					);

			}
		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분 기능을 display_name, uri 컬럼 활용함
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					" uri = '%s', display_name = '%s', "
					" get_lastmodified = %" APR_TIME_T_FMT" , depth = depth + %d "
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d AND uri = '%s' AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri					
					);
			}
			else 
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					"  uri = '%s', get_lastmodified = %" APR_TIME_T_FMT" , depth = depth + %d "
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d AND uri = '%s' AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri					
					);
			}
		}
		
		/* Execute the move command */
		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
		if( rv ) 
		{
			db_error_message(db, "dbms_move_resource: update", rv);;
			dav_repos_release_db(db_r_src->r, db, rv);
			return -1;
		}
	
	} 
	else if( db_r_src->m_resource_type == 1 )  // 폴더인 경우
	{
		// 2016.04.23 LIKE 구문 특수 문자 제거(_,%)
		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{		
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분 기능을 display_name, uri 컬럼 활용함
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					// 2015.06.10 dadamin
					// 디록토리 변경 시 기존 display_name를 활용해서 replace 시 
					// 변경되지 않을 수 있으므로 uri 필드 값을 사용 한다.
					" uri = replace(uri, lower('%s'), lower('%s')), display_name = replace(uri, lower('%s'), lower('%s')), "
					" get_lastmodified = %" APR_TIME_T_FMT ", depth = depth + %d "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( uri = lower('%s') OR uri LIKE regexp_replace( lower('%s/'), '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri
					, db_r_des->m_fake_uri
					, db_r_src->m_fake_uri
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					, db_r_src->m_fake_uri
					);
			}
			else 
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					"  uri = replace(URI, '%s', '%s'), get_lastmodified = %" APR_TIME_T_FMT ", depth = depth + %d "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( lower(uri) = lower('%s') OR lower(uri) LIKE regexp_replace( lower('%s/'), '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					, db_r_src->m_fake_uri
					);
			}
		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					" uri = replace(URI, '%s', '%s'), display_name = replace(display_name, '%s', '%s'), "
					" get_lastmodified = %" APR_TIME_T_FMT ", depth = depth + %d "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( uri = '%s' OR uri LIKE regexp_replace('%s/', '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri
					, db_r_des->m_fake_uri
					, db_r_src->m_fake_uri
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					, db_r_src->m_fake_uri
					);
			}
			else 
			{
				query = apr_psprintf( db_r_src->p,
					"UPDATE %s "
					"SET "
					"  uri = replace(URI, '%s', '%s'), get_lastmodified = %" APR_TIME_T_FMT ", depth = depth + %d "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( uri = '%s' OR uri LIKE regexp_replace('%s/', '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N'"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri
					, db_r_des->m_fake_uri
					, apr_time_now()
					, depth_diff
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					, db_r_src->m_fake_uri
					);
			}
		}

		/* Execute the update command */
		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
		if( rv ) 
		{
			db_error_message(db, "dbms_move_resource: update", rv);;
			dav_repos_release_db(db_r_src->r, db, rv);
			return -2;
		}

	}

	dav_repos_release_db(db_r_src->r, db, rv);

	return 0;
}

/**
 * Copy the source to the destination. If the source is a regular file, 
 * just copy the record in the database with the new URI. 
 * If the source is collection, duplicate all records in the collection 
 * in the database with the new URIs
 * @param d DB connection struct
 * @param db_r_src The source
 * @param db_r_des The destination ( only contains URI field )
 * @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
 * @return 0 on success, -1 on failure
 */
/* Elenoa CHECK */
// CHG 2012-03-23 huibong
// 서비스별 대소문자 구분, 무시 기능 추가를 위해 본 함수를 수정 처리
int dbms_copy_resource(const dav_repos_resource * db_r_src, const dav_repos_resource * db_r_des, bool is_uricaseignore )
{
    int depth_diff = 0;
    char *query = NULL;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	int nrows;
	char tblname[20] = {0};

	// 2014-04-28 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r_src->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_copy_resource: at_split func fail.");
		return -1;
	}

	// 2014-04-28 : dadamin
	// depth 추출 로직 삭제 : 상위 로직에서 추출됨
	depth_diff = db_r_des->m_depth - db_r_src->m_depth;

	/* Is a regular file, not directory */
	/* Copy the record in the database with the new URI */
	/* Store the source record into a temporary table */

	// CHG 2010-01-19 huibong
	// Copy file_lastmodified info from source to dst when file, directory copy 
	
	// CHG 2011-03-09 huibong
	// 원본에 대한 조회만 수행하도록 is_cache='N' 구문 추가.
	
	// CHG 2012-03-23 huibong
	// 서비스별 URI 대소문자 구분/무시 기능 추가
	if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
	{
		query = apr_psprintf(db_r_src->p,
				"SELECT resource_id, "
#ifdef __OPENDISK_SOLUTION__
				" subuser_id, owner_id, "
#else
				" sp_user_seq, sp_svc_tran_id, "
#endif
				" host_name, "
				" resource_type, uri, filename_org, filename_hash, "
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" depth, creation_date, display_name, "
				" get_content_length, get_content_type, "
				" get_lastmodified, "
				" source, file_lastmodified "  // ADD 2010-01-19 huibong
				"FROM %s "
				"WHERE sp_svc_tran_id = %s AND depth = %d AND %s = lower('%s') "
				"AND deleted_yn = 'N' AND is_cache = 'N' "
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, user_info.szVolumeID, db_r_src->m_depth
				// 2014.06.12
				// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
				, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
				, db_r_src->m_fake_uri);
	}
	else // 양방향 기본 ( URI 대소문자 구분 )
	{
		query = apr_psprintf(db_r_src->p,
				"SELECT resource_id, "
#ifdef __OPENDISK_SOLUTION__
				" subuser_id, owner_id, "
#else
				" sp_user_seq, sp_svc_tran_id, "
#endif
				" host_name, "
				" resource_type, uri, filename_org, filename_hash, "
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" depth, creation_date, display_name, "
				" get_content_length, get_content_type, "
				" get_lastmodified, "
				" source, file_lastmodified "  // ADD 2010-01-19 huibong
				"FROM %s "
				"WHERE sp_svc_tran_id = %s AND depth = %d AND uri = '%s' "
				"AND deleted_yn = 'N' AND is_cache = 'N' "
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, user_info.szVolumeID, db_r_src->m_depth, db_r_src->m_fake_uri);

	}

	// CHG 2011-03-03 huibong
	// resource_id 의 cross 생성 문제로 인해 Copy 시 resource_id 가 아닌 uri 를 기준으로 
	// Copy 작업이 수행되도록 수정처리함.
	/*
	   "WHERE resource_id = %lld "
	   "AND "
	   "deleted_yn = 'N'", db_r_src->m_resource_id);
	   */

	db = dav_repos_get_db(db_r_src->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_copy_resource: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r_src->r->pool, db->handle, &res, query, 1);
	if( rv ) 
	{
		db_error_message(db, "dbms_copy_resource: select", rv);;
		dav_repos_release_db(db_r_src->r, db, rv);
		return -1;
	}

	if( apr_dbd_num_tuples(db->driver, res) == 0 ) 
	{
		dav_repos_release_db(db_r_src->r, db, rv);
		return -2;
	}

	rv = solbox_apr_dbd_get_row(db->driver, db_r_src->r->pool, res, &row, 0);
	if(rv) 
	{
		db_error_message(db, "dbms_copy_resource: get row", rv);
		dav_repos_release_db(db_r_src->r, db, rv);
		return -1;
	}

	// 파일에 대한 COPY 처리시
	if (db_r_src->m_resource_type == 0) 
	{
		/* Is a regular file, not directory */

		//char *tmpOrgURI = NULL;
		//dav_repos_replace_unsafe_chars(apr_dbd_get_entry(db->driver, row, 6), tmpOrgURI);

		// CHG 2010-01-19 huibong
		// Copy file_lastmodified info from source to dst when file, directory copy 

		// CHG 2011-03-09 huibong
		// 파일에 대한 COPY 처리시 Replication 된 파일까지 Copy 되도록 수정처리.

		// CHG 2012-03-23 huibong 
		// 서비스별 대소문자 구분, 무시 기능 추가
		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분 기능을 display_name, uri 컬럼 활용함
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf( db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, display_name, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, lower('%s'), '%s', filename_org, "
					" filename_hash, depth + %d, "
					" get_content_length, get_content_type, %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d "
					"AND uri = lower('%s') AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					);
			}
			else 
			{
				query = apr_psprintf( db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, display_name, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, '%s', filename_org, "
					" filename_hash, depth + %d, "
					" display_name, "
					" get_content_length, get_content_type, %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d "
					"AND lower(uri) = lower('%s') AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					);
			}
		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분 기능을 display_name, uri 컬럼 활용함
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf( db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, display_name, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, '%s', '%s', filename_org, "
					" filename_hash, depth + %d, "
					" get_content_length, get_content_type,  %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d "
					"AND uri = '%s' AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					);
			}
			else 
			{
				query = apr_psprintf( db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, display_name, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, '%s', filename_org, "
					" filename_hash, depth + %d, "
					" display_name, "
					" get_content_length, get_content_type, %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth = %d "
					"AND uri = '%s' AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					);
			}
		}
	} 
	else if (db_r_src->m_resource_type == 1) // 폴더에 COPY 처리시
	{
		// 2016.04.23 LIKE 구문 특수 문자 제거(_,%)

		// CHG 2010-01-19 huibong
		// Copy file_lastmodified info from source to dst when file, directory copy 
		
		// CHG 2011-03-09 huibong
		// 폴더 복사시 is_cache 정보도 복사되도록 수정 처리 

		// CHG 2012-03-23 huibong 
		// 서비스별 대소문자 구분, 무시 기능 추가
		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
		{
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분 기능을 display_name, uri 컬럼 활용함
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf(db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, display_name, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					// 2016.04.28
					// 저장된 display_name 과 요청이 서로 상반된 경우 
					// 해당 디렉토리 복사 시 display_name 변경되지 않기 때문에 uri값을 display_name 사용
					" resource_type, replace(uri, lower('%s'), lower('%s')), replace(uri, lower('%s'), lower('%s')), "
					" filename_org, filename_hash, depth + %d, "
					" get_content_length, get_content_type, %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( uri = lower('%s') OR uri LIKE regexp_replace(lower('%s/'), '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri, db_r_des->m_fake_uri
					, db_r_src->m_fake_uri, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					, db_r_src->m_fake_uri
					);
			}
			else 
			{
				/* 2010.10.13 : dadmain / 8.1.2 -> 8.4.5 변경시 발생한 쿼리 warning 제거 */ 
				query = apr_psprintf(db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, display_name, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, replace(uri, '%s', '%s'), filename_org, "
					" filename_hash, depth + %d, "
					" display_name,  "
					" get_content_length, get_content_type, %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( lower(uri) = lower('%s') OR lower(uri) LIKE regexp_replace(lower('%s/'), '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri
					, db_r_src->m_fake_uri
					);
			}
		}
		else // 양방향 기본 ( URI 대소문자 구분 )
		{
			// 2014.06.13
			// 신규 형상(9.3)에서 대소문자 구분 기능을 display_name, uri 컬럼 활용함
			if( dav_use_display_name() == 1 )
			{
				query = apr_psprintf( db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, display_name, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, replace(uri, '%s', '%s'), replace(display_name, '%s', '%s'), "
					" filename_org, filename_hash, depth + %d, "
					" get_content_length, get_content_type, %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( uri = '%s' OR uri LIKE regexp_replace('%s/', '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri, db_r_des->m_fake_uri
					, db_r_src->m_fake_uri, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri, db_r_src->m_fake_uri
					);
			}
			else 
			{
				/* 2010.10.13 : dadmain / 8.1.2 -> 8.4.5 변경시 발생한 쿼리 warning 제거 */ 
				query = apr_psprintf( db_r_src->p,
					"INSERT INTO %s "
					"( "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, uri, filename_org, filename_hash, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" depth, display_name, "
					" get_content_length, get_content_type, "
					" get_lastmodified, creation_date, "
					" source, deleted_yn, is_cache, file_lastmodified ) "
					"SELECT "
#ifdef __OPENDISK_SOLUTION__
					" subuser_id, owner_id, "
#else
					" sp_user_seq, sp_svc_tran_id, "
#endif
					" host_name, "
					" resource_type, replace(uri, '%s', '%s'), filename_org, "
					" filename_hash, depth + %d, "
					" display_name, "
					" get_content_length, get_content_type, %" APR_TIME_T_FMT ", %" APR_TIME_T_FMT ", "
					" source, 'N', is_cache, file_lastmodified " // ADD 2010-01-19 huibong file_lastmodified 
					"FROM %s "
					"WHERE "
					"sp_svc_tran_id = %s AND depth >= %d "
					"AND ( uri = '%s' OR uri LIKE regexp_replace('%s/', '(%%|_)', '\\\\\\1', 'g') || '%%' ) "
					"AND deleted_yn = 'N' "
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, db_r_src->m_fake_uri, db_r_des->m_fake_uri
					, depth_diff
					, apr_time_now(), apr_time_now()
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, user_info.szVolumeID
					, db_r_src->m_depth
					, db_r_src->m_fake_uri, db_r_src->m_fake_uri
					);
			}
		}
	}

	dav_repos_release_db(db_r_src->r, db, rv);

	/* Execute the command */
	db = dav_repos_get_db(db_r_src->r);
	if( db == NULL)
	{
		db_error_message(db, "dbms_copy_resource: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
	if( rv ) 
	{
		db_error_message(db, "dbms_copy_resource: insert", rv);;
		dav_repos_release_db(db_r_src->r, db, rv);
		return -1;
	}

	dav_repos_release_db(db_r_src->r, db, rv);

    return 0;
}

// 2014-04-28 dadamin
// 사용되지 않는 함수 삭제 dbms_exists

/**
 * Get properties in a form of property link for every resource
 * @param d DB connection struct
 * @param db_r
 * @return 0 on success, -1 on failure
 */
int dbms_fill_dead_property(dav_repos_resource *db_r)
{
	char *query = NULL;
	int tup = 0, lengths = 0;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv = 0;
	char *szfilename_hash = NULL;

	// CHG 2019-08-28 huibong buffer overflow 수정 (#32780)
	// - buffer 크기를 32+1 -> 32 + 8 + 8 + 1 로 변경한다.
	char szTemp[49] = {0};

	struct dav_repos_resource *pr_tail = NULL;
	struct dav_repos_property *pp_tail = NULL;
	struct dav_repos_property *pnew_link_item = NULL;

	pr_tail = db_r;

	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_fill_dead_property: database acquire timeout", 0);
		return -1;
	}
	while (pr_tail != NULL) {
		/*Create the search command */
		DBG1("dbms_fill_dead_property: pr_tail.m_uri: %s", pr_tail->m_uri);
		DBG1("dbms_fill_dead_property: pr_tail.m_fake_uri: %s", pr_tail->m_fake_uri);

		// 2013.08.14 dadamin
		// pgpool 사용으로 resource_id가 RCDB01, RCDB02 서로 다른 경우 발생으로
		// 참조키를 filename_hash의 hash 값을 참조키로 변경함
		szfilename_hash = strrchr(pr_tail->m_filename_hash, '/');
		if(szfilename_hash) 
		{
			szfilename_hash = szfilename_hash + 1;
		}
		else
		{
			// 디렉토리 경우 hash 없을 수 있으므로 etag rule 과 동일하게 생성
			sprintf( szTemp, "00000000000000000000000000000000%x%x",
					(unsigned int)pr_tail->m_get_content_length,
					(unsigned int)pr_tail->m_get_lastmodified );

			szfilename_hash = szTemp;
		}

		query = apr_psprintf(db_r->p,
				"SELECT filename_hash, ns_id, propname, propvalue "
				"FROM t_dav_property "
				"WHERE filename_hash = '%s' "
				"ORDER BY propname ", 
				szfilename_hash);

		rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
		if (rv) 
		{
			db_error_message(db, "dbms_fill_dead_property: select", rv);;
			dav_repos_release_db(db_r->r, db, rv);
			return -1;
		}

		/* Deal with every records from the search result */
		for (tup = 0; tup < apr_dbd_num_tuples(db->driver, res); tup++) {
			/* Get the length of every field */
			rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, tup);
			if (rv) 
			{
				db_error_message(db, "dbms_fill_dead__property: get row", rv);
				dav_repos_release_db(db_r->r, db, rv);
				return -1;
			}
			lengths = (apr_dbd_get_entry(db->driver, row, 1) ?
					strlen(apr_dbd_get_entry(db->driver, row, 1)) : 0);
			if (lengths == 0) 
			{
				db_error_message(db, "dbms_fill_dead_property: lengths: 0", 0);
				dav_repos_release_db(db_r->r, db, rv);
				return -2;
			}

			/* Malloc a new property structure */
			pnew_link_item = apr_pcalloc(db_r->p, sizeof(*pnew_link_item));
			if (pr_tail->pr == NULL) 
			{
				/* It is the first property record */
				pr_tail->pr = pnew_link_item;
				pp_tail = pnew_link_item;
			} 
			else 
			{
				/* It is not the first property record */
				if (pp_tail) 
				{
					pp_tail->next = pnew_link_item;
				}
				pp_tail = pnew_link_item;
			}

			/* Get the data of every column of this row to a link */

			pp_tail->m_resource_id = pr_tail->m_resource_id;
			pp_tail->m_ns_id = atol(apr_dbd_get_entry(db->driver, row, 1));

			STRING_FROM_DBENTRY(db_r->p, pp_tail->m_propname, 2);
			STRING_FROM_DBENTRY(db_r->p, pp_tail->m_propvalue, 3);

			pp_tail->next = NULL;
		} 

		/* Next resource */
		pr_tail = pr_tail->next;
	}

	dav_repos_release_db(db_r->r, db, rv);

	return 0;
}

/**
 * Retrieve all the resources in a collection 
 * @param d DB connection struct
 * @param db_r A pointer to a resource struct which identifies the collection
 * @param depth==1 one depth, other infinity
 * @return 0 on success, -1 on failure
 */
// CHG 2010-03-29 huibong
// 조회시작 시간 정보를 함수의 인자로 추가함
// 본 기능은 연합뉴스 요구사항을 반영하기 위함임.

// CHG 2012-03-23 huibong
// 서비스별 URI 대소문자 구분/무시 기능 추가를 위해 is_uricaseignore 인자 추가.
// @param is_uricaseignore URI 에 대한 대소문자 무시 여부, true : URI 대소문자 무시, false: URI 대소문자 구분
int dbms_get_collection_resource( dav_repos_resource * db_r, int depth, const char * startTime, bool is_uricaseignore )
{
	int tup = 0;
	int iret = 0;
	int infinity = 0;
	char * query = NULL;

	// CHG 2010-03-29 huibong
	// 조회시작 시간 정보 및 각 Where 조건절을 저장하기 위한 변수 생성.
	char * whereParse = NULL;
	
	char szRealURI[MaxSizeOfPathLength];
	struct dav_repos_resource *pnew_link_item = NULL;
	struct dav_repos_resource *pr_tail = NULL;
	dav_repos_server_conf *dsc;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char tblname[20] = {0};

	dsc = dav_repos_get_server_conf(db_r->r->server);
	if( !dsc ) 
		return -8;

	if( depth == DAV_INFINITY ) 
		infinity = 1;
	
	// 2014-04-24 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_collection_resource: at_split func fail.");
		return -9;
	}

	// 2016.04.23 LIKE 구문 특수 문자 제거(_,%)
	if( infinity ) 
	{
		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
			whereParse = apr_psprintf(db_r->p, "sp_svc_tran_id = %s AND depth >= %d "
											"AND %s LIKE regexp_replace( lower('%s/'), '(%%|_)', '\\\\\\1', 'g') || '%%' "
											"AND %s <> lower('%s') "
											,user_info.szVolumeID, (db_r->m_depth+1)
											// 2014.06.12
											// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
											, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
											, db_r->m_fake_uri
											// 2014.06.12
											// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
											, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
											, db_r->m_fake_uri);
		else
			whereParse = apr_psprintf(db_r->p, "sp_svc_tran_id = %s AND depth >= %d "
											"AND uri LIKE regexp_replace('%s/', '(%%|_)', '\\\\\\1', 'g') || '%%' "
											"AND uri <> '%s' "
											,user_info.szVolumeID, (db_r->m_depth+1), db_r->m_fake_uri, db_r->m_fake_uri);
	} 
	else 
	{
		if( is_uricaseignore == true ) // URI 대소문자 무시 서비스인 경우
			whereParse = apr_psprintf(db_r->p, "sp_svc_tran_id = %s AND depth = %d "
											"AND %s LIKE regexp_replace( lower('%s/'), '(%%|_)', '\\\\\\1', 'g') || '%%' "
											"AND %s <> lower('%s') "
											,user_info.szVolumeID, (db_r->m_depth+1)
											// 2014.06.12
											// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
											, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
											, db_r->m_fake_uri
											// 2014.06.12
											// 신규 형상(9.3)에서 대소문자 구분은 인덱스로 지원하지 않음
											, dav_use_display_name() == 0 ? "lower(uri)" : "uri"
											, db_r->m_fake_uri);
		else
			whereParse = apr_psprintf(db_r->p, "sp_svc_tran_id = %s AND depth = %d "
											"AND uri LIKE regexp_replace('%s/', '(%%|_)', '\\\\\\1', 'g') || '%%' "
											"AND uri <> '%s' "
											,user_info.szVolumeID, (db_r->m_depth+1), db_r->m_fake_uri, db_r->m_fake_uri);
	}

	// CHG 2010-01-19 huibong
	// Select file_lastmodified column to send client that file attribute 
	// Not use get_lastmodified

	// CHG 2010-07-08 huibong
	// NetCache PROPFIND 성능 개선을 위해 조회 Query 상에 distinct, ORDER BY 구문 제거처리
	
	// CHG 2011-07-07 huibong
	// HTTP Header 상에 Case-Sensitive: NO 가 설정되어 있으면 소문자로 변환 후 조회
	// 그밖에는 대소문자를 구분하도록 설정

	// CHG 2012-03-23 huibong
	// 서비스별 대소문자 구분/무시 여부 기능 추가.. 
	// 기존 Case-Sensitive 기능은 제거함.

	/* Create the search command */
	// 2010-03-29 huibong
	// 조회조건상에 조회 시작 시간 정보를 추가함.
	// SBFS 는 특성상 조회 시작 시간 정보를 get_lastmodified 값이 아닌 file_lastmodified 값을 사용한다.
	if( startTime == NULL ) 
	{
		query = apr_psprintf(db_r->p, 
				//"SELECT distinct(uri), resource_id, resource_type, "	// 2010-07-08 NetCache 성능 개선을 위한 변경처리
				"SELECT uri, resource_id, resource_type, "
#ifdef __OPENDISK_SOLUTION__
				" owner_id, "
#else
				" sp_user_seq, "
#endif
				" filename_org, filename_hash, depth, "
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" creation_date, display_name, "
				" get_content_length, get_content_type, "
				" get_lastmodified, source, "
				" deleted_yn, host_name, file_lastmodified "	// ADD 2010-01-19 huibong file_lastmodified column
				"FROM %s "
				"WHERE %s "
				" AND deleted_yn = 'N' AND is_cache = 'N' "
				//"ORDER BY uri asc, resource_id asc"							// 2010-07-08 NetCache 성능 개선을 위한 변경처리
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, whereParse
				);

	}
	else 
	{
		query = apr_psprintf(db_r->p, 
				//"SELECT distinct(uri), resource_id, resource_type, "	// 2010-07-08 NetCache 성능 개선을 위한 변경처리
				"SELECT uri, resource_id, resource_type, "
#ifdef __OPENDISK_SOLUTION__
				" owner_id, "
#else
				" sp_user_seq, "
#endif
				" filename_org, filename_hash, depth, "
				// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
				" creation_date, display_name, "
				" get_content_length, get_content_type, "
				" get_lastmodified, source, "
				" deleted_yn, host_name, file_lastmodified "	// ADD 2010-01-19 huibong file_lastmodified column
				"FROM %s "
				"WHERE %s "
				"AND deleted_yn = 'N' AND is_cache = 'N' "
				"AND file_lastmodified >= %s"
				//"ORDER BY uri asc, resource_id asc"							// 2010-07-08 NetCache 성능 개선을 위한 변경처리	
				, dav_get_target_table(user_info.szVolumeID, tblname)
				, whereParse
				, startTime);
	}
	
#ifdef __PROPFIND_RANDOM_DIR__

	/* 2010.12.07 : dadamin / PROPFIND 처리시 FHS 정보 Random(디렉토리 관련) */
	// NetCache Agent 인지를 확인하기 위해 PROFPIND Header 의 User-Agent 정보 검사.
	const char * userAgent = NULL;
	userAgent = apr_table_get( db_r->r->headers_in, "User-Agent");

	if( db_r->r->method_number == M_PROPFIND && userAgent != NULL && strncmp(userAgent, "NetCache", 8)==0 ) 
	{
		// CHG 2011-07-07 huibong
		// HTTP Header 상에 Case-Sensitive: NO 가 설정되어 있으면 소문자로 변환 후 조회
		// 그밖에는 대소문자를 구분하도록 설정

		// CHG 2012-03-23 huibong
		// 서비스별 대소문자 구분/무시 여부 기능 추가.. 
		// 기존 Case-Sensitive 기능은 제거함.

		if( startTime == NULL ) 
		{
			query = apr_psprintf(db_r->p, 
					"SELECT DISTINCT ON (uri) uri, resource_id, resource_type, "
#ifdef __OPENDISK_SOLUTION__
					" owner_id, "
#else
					" sp_user_seq, "
#endif
					" filename_org, filename_hash, depth, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" creation_date, display_name, "
					" get_content_length, get_content_type, "
					" get_lastmodified, source, "
					" deleted_yn, host_name, file_lastmodified "   // ADD 2010-01-19 huibong file_lastmodified column
					"FROM %s "
					"WHERE %s "
					" AND deleted_yn = 'N' "
					"ORDER BY uri, RANDOM()"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, whereParse
					);
		}
		else 
		{
			query = apr_psprintf( db_r->p, 
					"SELECT DISTINCT ON (uri) uri, resource_id, resource_type, "
#ifdef __OPENDISK_SOLUTION__
					" owner_id, "
#else
					" sp_user_seq, "
#endif
					" filename_org, filename_hash, depth, "
					// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
					" creation_date, display_name, "
					" get_content_length, get_content_type, "
					" get_lastmodified, source, "
					" deleted_yn, host_name, file_lastmodified "   // ADD 2010-01-19 huibong file_lastmodified column
					"FROM %s "
					"WHERE %s "
					"AND deleted_yn = 'N' "
					"AND file_lastmodified >= %s "
					"ORDER BY uri, RANDOM()"
					, dav_get_target_table(user_info.szVolumeID, tblname)
					, whereParse
					, startTime);

		}
	}

	/* 2010.12.07 : dadamin / PROPFIND 처리시 FHS 정보 Random / 디렉토리 관련 */
#endif // __PROPFIND_RANDOM_DIR__


	/* Execute sequry */
	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_get_collection_resource: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
	if( rv ) 
	{
		db_error_message(db, "dbms_get_collection_resource: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}

	pr_tail = db_r;
	pr_tail->next = NULL;
	pr_tail->pr = NULL;

	for( tup = 0; tup < apr_dbd_num_tuples(db->driver, res); tup++ ) 
	{
		/* Fetch every row */
		rv = solbox_apr_dbd_get_row( db->driver, db_r->r->pool, res, &row, tup );
		if( rv ) 
		{
			db_error_message(db, "dbms_get_collection_resource: get row", rv);
			dav_repos_release_db(db_r->r, db, rv);
			return -2;
		}

		memset(szRealURI, 0, MaxSizeOfPathLength);

		/* Malloc a new structure */
		pnew_link_item = apr_pcalloc(db_r->p, sizeof(*pnew_link_item));
		pr_tail->next = pnew_link_item;
		pr_tail = pnew_link_item;

		/* Get the data of every column of this row to a link */
		pr_tail->m_resource_id = atoll(apr_dbd_get_entry(db->driver, row, 1));

#ifdef __OPENDISK_SOLUTION__
		pr_tail->m_resource_type = dav_repos_change_resource_type( atoi(apr_dbd_get_entry(db->driver, row, 2)));
#else
		pr_tail->m_resource_type = atoi(apr_dbd_get_entry(db->driver, row, 2));
#endif

		pr_tail->m_owner_id = atoll(apr_dbd_get_entry(db->driver, row, 3));
		STRING_FROM_DBENTRY(db_r->p, pr_tail->m_fake_uri, 0);

		if( pr_tail->m_fake_uri != NULL ) 
		{
			remove_user_root_dir_from_fake_uri(pr_tail->m_fake_uri, szRealURI);
			pr_tail->m_uri = apr_psprintf(db_r->p, "%s%s", dsc->base_uri, szRealURI);
		}

		STRING_FROM_DBENTRY(db_r->p, pr_tail->m_filename_org, 4);
		STRING_FROM_DBENTRY(db_r->p, pr_tail->m_filename_hash, 5);
		pr_tail->m_depth = atoi(apr_dbd_get_entry(db->driver, row, 6));
		pr_tail->m_creation_date = atoll(apr_dbd_get_entry(db->driver, row, 7));
				
		// 2014.11.27 dadamin
		// PROPFIND 응답 시 경로 /dav 로 리턴하도록 수정함
		if( dav_use_display_name() == 1 )
		{
			memset(szRealURI, 0, MaxSizeOfPathLength);
			remove_user_root_dir_from_fake_uri(apr_dbd_get_entry(db->driver, row, 8), szRealURI);
			pr_tail->m_displayname = apr_psprintf(db_r->p, "%s%s", dsc->base_uri, szRealURI);
		}
		else 
		{
			STRING_FROM_DBENTRY(db_r->p, pr_tail->m_displayname, 8);
		}

		// 2014.07.09 : 사용되지 않는 컬럼 정리(get_content_language,get_etag,supported_lock,lock_discovery)
		pr_tail->m_get_content_length = atoll(apr_dbd_get_entry(db->driver, row, 9));
		STRING_FROM_DBENTRY(db_r->p, pr_tail->m_get_content_type, 10);
	
		// CHG 2010-01-19 huibong 
		// PROFIND file list send to client, and last modify time is file_lastmodified column, not get_lastmodified column
		//pr_tail->m_get_lastmodified = atoll(apr_dbd_get_entry(db->driver, row, 13));
		pr_tail->m_get_lastmodified = atoll(apr_dbd_get_entry(db->driver, row, 15));

		pr_tail->m_source = atoi(apr_dbd_get_entry(db->driver, row, 12));
		STRING_FROM_DBENTRY(db_r->p, pr_tail->m_host_name, 14);

		pr_tail->next = NULL;
		pr_tail->pr = NULL;
	}

	dav_repos_release_db(db_r->r, db, rv);

	return iret;
}

/* Deletes a dasl_property by serialno and property name
 * @param d DB connection struct
 * @param r A pointer to a resource struct identifying 
 * the resource to be operated upon
 * @param pr A pointer to a property struct identifying the property to delete
 * @return 0 on success, 1 on failure
 * */
int dbms_del_dead_property(const dav_repos_resource *db_r, const dav_repos_property *pr)
{
    char *query = NULL;
	ap_dbd_t *db;
	apr_status_t rv;
	int nrows;
	char* szfilename_hash = NULL;

	// CHG 2019-08-28 huibong buffer overflow 수정 (#32780)
	// - buffer 크기를 32+1 -> 32 + 8 + 8 + 1 로 변경한다.
	char szTemp[49] = {0};

	// 2013.08.14 dadamin
	// pgpool 사용으로 resource_id가 RCDB01, RCDB02 서로 다른 경우 발생으로
	// 참조키를 filename_hash의 hash 값을 참조키로 변경함
	szfilename_hash = strrchr(db_r->m_filename_hash, '/');
	if(szfilename_hash) 
	{
		szfilename_hash = szfilename_hash + 1;
	}
	else 
	{
		// 디렉토리 경우 hash 없을 수 있으므로 etag rule 과 동일하게 생성
		sprintf( szTemp, "00000000000000000000000000000000%x%x",
			(unsigned int)db_r->m_get_content_length,
			(unsigned int)db_r->m_get_lastmodified );

		szfilename_hash = szTemp;
	}

	query = apr_psprintf(db_r->p, 
			"DELETE FROM t_dav_property "
			"WHERE filename_hash = '%s' AND ns_id = %ld AND propname = '%s' "
			, szfilename_hash, pr->m_ns_id, pr->m_propname);

	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_del_dead_property: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query);
	if( rv ) 
	{
		db_error_message(db, "dbms_del_dead_property: delete", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}

	dav_repos_release_db(db_r->r, db, rv);

    return 0;
}

/* Get resourcetype by uri
 * @param d DB connection struct
 * Return 0: success; 100: not found; -1: failure
 * */
int dbms_get_resourcetype(dav_repos_resource *db_r)
{
	int iret = 0;
	const char *query;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char tblname[20] = {0};

	TRACE();

	// 2014-04-28 : dadamin
	// where 구문에 파티셔닝 기준값, depth  추가
	acct_info user_info;
	if(at_split(db_r->r->user, &user_info ) < 0) 
	{
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "dbms_get_resourcetype: at_split func fail.");
		return -1;
	}

	//apr_thread_rwlock_rdlock(db_lock);
	query = apr_psprintf(db_r->p,
			"SELECT resource_type "
			"FROM %s "
			"WHERE sp_svc_tran_id = %s AND depth = %d AND uri = '%s' "
			"AND is_cache = 'N' "
			, dav_get_target_table(user_info.szVolumeID, tblname)
			, user_info.szVolumeID
			, db_r->m_depth
			, db_r->m_fake_uri);

	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_get_resourcetype: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);	
	if (rv) {
		db_error_message(db, "dbms_get_resourcetype: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return -1;
	}

	if (apr_dbd_num_tuples(db->driver, res) == 0) {
		/* No result */
		iret = 100;
	} else {
		/* One row fetched */
		/* Get the resourcetype data */
		/* Fetch value column of the row */
#ifdef __OPENDISK_SOLUTION__
		db_r->m_resource_type =
			dav_repos_change_resource_type(
					atoi(apr_dbd_get_entry(db->driver, row, 0)));
#else
		db_r->m_resource_type = atoi(apr_dbd_get_entry(db->driver, row, 0));
#endif
		iret = 0;
	}				/*End of else one row fetched */

	dav_repos_release_db(db_r->r, db, rv);
	//apr_thread_rwlock_unlock(db_lock);

	return iret;
}				/*End of dbms_get_resourcetype */

/* Build namespace
 * 
 * Read namespace infor from DB and fill hash
 * db_r must have pool
 * This hash is for PROPFIND
 * */
void dbms_build_ns_id_hash(dav_repos_resource *db_r)
{
	int tup = 0;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char *query = NULL;

	/* id_hash is not clean */
	if (db_r->ns_id_hash != NULL) 
	{
		/* Build hash */
		db_r->ns_id_hash = apr_hash_make(db_r->p);
	}

	/* Execute sequry */
	//apr_thread_rwlock_rdlock(db_lock);
	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_build_ns_id_hash: database acquire timeout", 0);
		return;
	}
	
	query = apr_pstrdup(db_r->p, "SELECT * FROM t_dav_namespace");
	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
	if (rv) {
		db_error_message(db, "dbms_build_ns_id_hash: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return;
	}

	for( tup = 0; tup < apr_dbd_num_tuples(db->driver, res); tup++) 
	{
		rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, tup);
		if (rv) {
			db_error_message(db, "dbms_build_ns_id_hash: get row", 0);
			dav_repos_release_db(db_r->r, db, rv);
			//apr_thread_rwlock_unlock(db_lock);
			return;
		}

		if (apr_dbd_get_entry(db->driver, row, 0) == NULL) {
			dav_repos_release_db(db_r->r, db, rv);
			//apr_thread_rwlock_unlock(db_lock);
			return; 
		}

		apr_hash_set(db_r->ns_id_hash, 
				apr_pstrdup(db_r->p, apr_dbd_get_entry(db->driver, row, 0)),
				APR_HASH_KEY_STRING, 
				apr_pstrdup(db_r->p, apr_dbd_get_entry(db->driver, row, 1)));
	}

	dav_repos_release_db(db_r->r, db, rv);
	//apr_thread_rwlock_unlock(db_lock);
}
// 2013.08.22 dadamin
// set namespace
void dbms_set_namespace(dav_repos_resource * db_r, dav_repos_property *pr, const char *snamespace)
{
	/* id_hash is not clean */
	if (db_r->ns_id_hash == NULL) 
	{
		/* Build hash */
		db_r->ns_id_hash = apr_hash_make(db_r->p);
	}

	char szns_id[10] = {0};
	sprintf(szns_id, "%ld", pr->m_ns_id);

	apr_hash_set(db_r->ns_id_hash, 
		apr_pstrdup(db_r->p, szns_id),
		APR_HASH_KEY_STRING, 
		apr_pstrdup(db_r->p, snamespace));
}

// set ns_id
void dbms_set_ns_id(dav_repos_resource * db_r,  const char *snamespace, dav_repos_property *pr)
{
	/* id_hash is not clean */
	if (db_r->ns_hash == NULL) 
	{
		/* Build hash */
		db_r->ns_hash = apr_hash_make(db_r->p);
	}

	char szns_id[10] = {0};
	sprintf(szns_id, "%ld", pr->m_ns_id);

	apr_hash_set(db_r->ns_hash, 
		apr_pstrdup(db_r->p, snamespace),
		APR_HASH_KEY_STRING, 
		apr_pstrdup(db_r->p, szns_id));
}

/* Get namespace
 * If new namespace, save it and return ns_id
 * 
 * This hash is for PROPPATCH
 * It must be rebuild after adding new namespace
 * */
static void dbms_build_ns_hash(dav_repos_resource *db_r)
{
	int tup = 0;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char *query = NULL;

	TRACE();

	/* Already built */
	if (db_r->ns_hash) return;

	db_r->ns_hash = apr_hash_make(db_r->p);

	/* Execute sequry */
	//apr_thread_rwlock_rdlock(db_lock);
	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_build_ns_hash: database acquire timeout", 0);
		return;
	}
	query = apr_pstrdup(db_r->p, "SELECT * FROM t_dav_namespace");
	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
	if (rv) {
		db_error_message(db, "dbms_build_ns_hash: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return;
	}

	for (tup = 0; tup < apr_dbd_num_tuples(db->driver, res); tup++) {
		rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, tup);
		if (rv) {
			db_error_message(db, "dbms_build_ns_hash: get row", rv);
			dav_repos_release_db(db_r->r, db, rv);
			//apr_thread_rwlock_unlock(db_lock);
			return;
		}

		/* XXX: It's right? */
		apr_hash_set(db_r->ns_hash,
				// 2013.08.12 dadmain
				// 네임스페이스 통해서 해당 ns_id 획득을 위해서 해쉬테이블 설정
				apr_pstrdup(db_r->p, apr_dbd_get_entry(db->driver, row, 1)),
				APR_HASH_KEY_STRING, 
				apr_pstrdup(db_r->p, apr_dbd_get_entry(db->driver, row, 0)));
	}

	dav_repos_release_db(db_r->r, db, rv);
	//apr_thread_rwlock_unlock(db_lock);

	return;
}

/* Insert new namespace
 * Must build hash and try to find namespace first.
 */

static long dbms_select_ns(dav_repos_resource *db_r, const char *namespace)
{
	const char *query = NULL;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;


	query = apr_psprintf(db_r->p, 
						"SELECT * "
						"FROM t_dav_namespace "
						"WHERE ns_name = '%s'", namespace);
	
	db = dav_repos_get_db(db_r->r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_select_ns: database acquire timeout", 0);
		return -1;
	}

	rv = apr_dbd_select(db->driver, db_r->r->pool, db->handle, &res, query, 1);
	if (rv) {
		db_error_message(db, "dbms_select_ns: select", rv);;
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}

	rv = solbox_apr_dbd_get_row(db->driver, db_r->r->pool, res, &row, 0);
	if (rv) {
		db_error_message(db, "dbms_select_ns: get row", 0);
		dav_repos_release_db(db_r->r, db, rv);
		return -1;
	}


	long r = atol(apr_dbd_get_entry(db->driver, row, 0));
	dav_repos_release_db(db_r->r, db, rv);
	return r;
}

static long dbms_insert_ns(dav_repos_resource *db_r, const char *namespace)
{
    long ns_id = -1;
    const char *query = NULL;
	
	// CHG 2019-08-28 huibong buffer overflow 수정 (#32780)
	// - buffer 크기를 10 -> 20 + 1 로 변경한다.
	char szns_id[21] = {0};

	ap_dbd_t *db;
	apr_status_t rv;
	int nrows;

    TRACE();

	// 2013.08.23 dadamin
	// 해당 네임스페이스 RCDB 상 등록되어 있는지 조회
	ns_id = dbms_select_ns(db_r, namespace);
	if(ns_id < 0)
	{
		// 2013.08.12 dadamin
		// ns_id 값을 SEQUENCE 입력된 경우 RCDB01, RCDB02 서로 다름 값을 가질 수 있으므로
		// time 값을 해당값으로 입력하여 RCDB01, RCDB02 서로 같을 가질 수 있게 수정하였으며,
		// 네임스페이스로 최대 1024(DAV_REPOS_MAX_NAMESPACE)까지 가질 수 있게 로직 구성하였으며,
		// DAV: 네임스페이스 경우 ns_id 는 0으로 고정함

		if(strcmp(namespace, "DAV:") == 0)
		{
			ns_id = 0;
		}
		else
		{
			ns_id = (long) time(0);
			if( ns_id > 0 )
				ns_id = ns_id % DAV_REPOS_MAX_NAMESPACE + 1;
		}

		sprintf(szns_id, "%ld", ns_id);

		/* We need to add name space to DB and fill hash */
		//apr_thread_rwlock_wrlock(db_lock);
		query = apr_psprintf(db_r->p,
			"INSERT INTO t_dav_namespace (ns_id, ns_name) VALUES (%s, '%s')", szns_id, namespace);

		db = dav_repos_get_db(db_r->r);
		if(db == NULL)
		{
			db_error_message(db, "dbms_insert_ns: database acquire timeout", 0);
			return -1;
		}

		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
		if (rv) {
			db_error_message(db, "dbms_insert_ns: insert", rv);;
			dav_repos_release_db(db_r->r, db, rv);
			//apr_thread_rwlock_unlock(db_lock);
			return -1;
		}
		dav_repos_release_db(db_r->r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
	}
	else 
	{
		sprintf(szns_id, "%ld", ns_id);
	}

    /* Add to hash_table */
	// 2013.08.12 dadamin
	// 네임스페이스로 ns_id 구하기 위한 설정
	if (db_r->ns_hash) {
		apr_hash_set(db_r->ns_hash, namespace, APR_HASH_KEY_STRING, szns_id);
	}

    return ns_id;
}

/*## FIXME : Make it building hash only once per request
 *
 */
/* Get namespace
 * If new namespace, save it and return ns_idmod_dav 
 * 
 * @param d DB connection struct
 * @param r Pointer to a resource struct identifying the resource in question
 * @param namespace namespace
 * @return name space. Should not return NULL
 * */
const char *dbms_get_ns(dav_repos_resource * db_r, const long ns_id)
{
	const char *namespace;
	const char *ns_id_str;
	const char *ns_default = DEFAULT_NAMESPACE_URI;

	/* Build hash first */
	if( db_r->ns_id_hash == NULL ) 
	{
		return ns_default;
	}

	/* Build key */
	ns_id_str = apr_psprintf(db_r->p, "%ld", ns_id);
	namespace = (const char *) apr_hash_get(db_r->ns_id_hash, ns_id_str, APR_HASH_KEY_STRING);

	/* We can't return NULL, so let's return default NS */
	if( namespace == NULL ) 
	{
		return ns_default;
	}

	return namespace;
}

/* Get namespace
 * If new namespace, save it and return ns_id
 * 
 * @param d DB connection struct
 * @param r Pointer to a resource struct identifying the resource in question
 * @param namespace namespace
 * @return ns_id
 * */
long dbms_get_ns_id(dav_repos_resource * db_r, const char *namespace)
{
	char *ns_id = NULL;

	dbms_build_ns_hash(db_r);

	/* Now, We have Hash table */
	ns_id = apr_hash_get(db_r->ns_hash, namespace, APR_HASH_KEY_STRING);

	if (ns_id) 
	{
		return atol(ns_id);
	} 

	/* name space is not found */
	return dbms_insert_ns(db_r, namespace);
}


/* This function for walker */
dav_error *dbms_load_locknull_list(request_rec *r, const char *path, apr_text_header *phdr, apr_pool_t *pool)
{
	int tup = 0;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
    const char *query = NULL;

	//apr_thread_rwlock_rdlock(db_lock);
	query = apr_psprintf(pool, 
			"SELECT filename_hash "
			"FROM t_dav_locknull "
			"WHERE path = '%s'", path);

	/* Execute sequry */
	db = dav_repos_get_db(r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_load_locknull_list: database acquire timeout", 0);
		return NULL;
	}

	rv = apr_dbd_select(db->driver, r->pool, db->handle, &res, query, 1);
	if (rv) 
	{
		db_error_message(db, "dbms_load_locknull_list: select", rv);;
		dav_repos_release_db(r, db, rv);
		return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "DBMS Error");
	}

	for (tup = 0; tup < apr_dbd_num_tuples(db->driver, res); tup++) 
	{
		rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, tup);
		if (rv) 
		{
			db_error_message(db, "dbms_load_locknull_list: get row", rv);
			dav_repos_release_db(r, db, rv);
			return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0,"DBMS Error");
		}

		/* Add it to the text */
		apr_text_append(pool, phdr, apr_pstrdup(pool, apr_dbd_get_entry(db->driver, row, 0)));
	}

	dav_repos_release_db(r, db, rv);

	return NULL;
}


dav_error *dbms_save_locknull_member(request_rec *r, const char *filename, apr_pool_t * pool)
{
	const char *query = NULL;
	char *path = ap_make_dirstr_parent(pool, filename);
	char tmpOrgURI[MaxSizeOfFileNameLength + MaxSizeOfFileNameLength];
	ap_dbd_t *db;
	apr_status_t rv;
	int nrows;

	dav_repos_replace_unsafe_chars(path, tmpOrgURI);

	/* No trailing */
	dav_repos_no_trail(path);

	/* We need to add name space to DB and fill hash */

	//apr_thread_rwlock_wrlock(db_lock);
	query = apr_psprintf(pool,
			"INSERT INTO t_dav_locknull VALUES ('%s', '%s')",
			tmpOrgURI, filename);

	/* Execute sequry */
	db = dav_repos_get_db(r);
	if(db == NULL)
	{
		return solbox_dav_new_error(pool, HTTP_SERVICE_UNAVAILABLE, 0, "dbms_save_locknull_member: database acquire timeout");
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
	if (rv) {
		db_error_message(db, "dbms_save_locknull_member: select", rv);;
		dav_repos_release_db(r, db, rv);
		return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "DBMS Error");
	}

	dav_repos_release_db(r, db, rv);

	return NULL;
}


dav_error *dbms_remove_locknull_member(request_rec *r, const char *filename, apr_pool_t * pool)
{
    const char *query = NULL;
    char *path = ap_make_dirstr_parent(pool, filename);
	char tmpOrgURI[MaxSizeOfFileNameLength + MaxSizeOfFileNameLength];
	char tmpOrgFile[MaxSizeOfFileNameLength + MaxSizeOfFileNameLength];
	ap_dbd_t *db;
	apr_status_t rv;
	int nrows;

	dav_repos_replace_unsafe_chars(path, tmpOrgURI);
	dav_repos_replace_unsafe_chars(filename, tmpOrgFile);

    /* no trailing */
    dav_repos_no_trail(path);

    /* We need to add name space to DB and fill hash */
	//apr_thread_rwlock_wrlock(db_lock);
	query = apr_psprintf(pool,
			"DELETE FROM t_dav_locknull "
			"WHERE path='%s' AND filename_hash='%s'",
			tmpOrgURI, tmpOrgFile);

	db = dav_repos_get_db(r);
	if (db == NULL) 
	{
		return solbox_dav_new_error(pool, HTTP_SERVICE_UNAVAILABLE, 0, "dbms_remove_locknull_member: database acquire timeout");
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
	if (rv) 
	{
		db_error_message(db, "dbms_remove_locknull_member: delete", rv);;
		dav_repos_release_db(r, db, rv);
		return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "DBMS Error");
	}
	dav_repos_release_db(r, db, rv);

    return NULL;
}

dav_error *dbms_load_lock_record(request_rec *r, const char *lock_key, dav_lock_discovery ** direct, dav_lock_indirect ** indirect, int *need_save, apr_pool_t * pool)
{
    int tup = 0;
    dav_lock_discovery *dp = NULL;
    dav_lock_indirect *ip = NULL;
    const char *query = NULL;
	char tmpOrgURI[MaxSizeOfFileNameLength + MaxSizeOfFileNameLength];
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;

    TRACE();

	dav_repos_replace_unsafe_chars(lock_key, tmpOrgURI);

	//apr_thread_rwlock_rdlock(db_lock);
	query = apr_psprintf(pool, 
			"SELECT lock_type, scope, file_type, depth, "
				"timeout, lock_token, owner, auth_user, lockkey "
			"FROM t_dav_lock "
#ifdef __IGNORE_CASE_DOWNLOAD__
			//"WHERE lower(uri) = lower('%s') "
			"WHERE uri = '%s' "
#else
			"WHERE uri = '%s' "
#endif
			, tmpOrgURI);

	db = dav_repos_get_db(r);
	if(db == NULL)
	{
		return solbox_dav_new_error(pool, HTTP_SERVICE_UNAVAILABLE, 0, "dbms_load_lock_record: database acquire timeout");
	}
	
	rv = apr_dbd_select(db->driver,r->pool, db->handle, &res, query, 1);
	if (rv) {
		db_error_message(db, "dbms_load_lock_record: select", rv);;
		dav_repos_release_db(r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "DBMS Error");
	}

	// CHECK THIS OUT LATER - apr_dbd_get_entry(db->driver, rowN 
    DBG1("dbms_load_lock_record::tuples = %d",
			apr_dbd_num_tuples(db->driver, res));

	for (tup = 0; tup < apr_dbd_num_tuples(db->driver, res); tup++) {
		dav_locktoken *locktoken = NULL;

		rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, tup);
		if (rv) {
			db_error_message(db, "dbms_load_lock_record: get row", rv);
			dav_repos_release_db(r, db, rv);
			//apr_thread_rwlock_unlock(db_lock);
			return
				solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0,"DBMS Error");
		}

		/* Check timeout */
		if (dav_repos_lock_expired(atoi(apr_dbd_get_entry(db->driver, row, 4)))) {
			*need_save = DAV_TRUE;
			DBG0("dbms_load_lock_record: DAV_TRUE!");
			continue;
		}

		locktoken = apr_pcalloc(pool, sizeof(*locktoken));
		locktoken->char_uuid =
			apr_pstrdup(pool, apr_dbd_get_entry(db->driver, row, 5));

		if (atoi(apr_dbd_get_entry(db->driver, row, 0)) == DAV_LOCK_DIRECT) {
			dp = apr_pcalloc(pool, sizeof(*dp));

			/* Fill DP */
			dp->f.scope = atoi(apr_dbd_get_entry(db->driver, row, 1));
			dp->f.type = atoi(apr_dbd_get_entry(db->driver, row, 2));
			dp->f.depth = atoi(apr_dbd_get_entry(db->driver, row, 3));
			dp->f.timeout = atoi(apr_dbd_get_entry(db->driver, row, 4));

			dp->locktoken = locktoken;

			dp->owner =
				apr_pstrdup(pool, apr_dbd_get_entry(db->driver, row, 6));

			if (apr_dbd_get_entry(db->driver, row, 7) &&
					strlen(apr_dbd_get_entry(db->driver, row, 7)))
				dp->auth_user =
					apr_pstrdup(pool, apr_dbd_get_entry(db->driver, row, 7));

			dp->next = *direct;
			*direct = dp;
		} else {
			ip = apr_pcalloc(pool, sizeof(*ip));

			/* Fill IP */
			ip->timeout = atoi(apr_dbd_get_entry(db->driver, row, 4));
			ip->locktoken = locktoken;
			ip->key = apr_pstrdup(pool, apr_dbd_get_entry(db->driver, row, 8));

			/* Put it in the chain */
			ip->next = *indirect;
			*indirect = ip;
		}
	}

	dav_repos_release_db(r, db, rv);
	//apr_thread_rwlock_unlock(db_lock);

	return NULL;
}


/* DIRECT LOCK save
** lock info has linked list data
** #FIXME : Need rollback if we had error
*/
dav_error *dbms_save_lock_record(request_rec *r, const char *key,
		const dav_lock_discovery * direct, const dav_lock_indirect * indirect,
		apr_pool_t * pool)
{
	const char *query = NULL;
	const dav_lock_discovery *dp;
	const dav_lock_indirect *ip;
	ap_dbd_t *db;
	apr_status_t rv = 0;
	int nrows, i;

	TRACE();

	if (direct == NULL && indirect == NULL) { 
		DBG0("dbms_save_lock_record: nothing to save");
		return NULL;
	}

	//apr_thread_rwlock_wrlock(db_lock);
	db = dav_repos_get_db(r);
	if (db == NULL) {
		return solbox_dav_new_error(pool, HTTP_SERVICE_UNAVAILABLE, 0, "dbms_save_lock_record: database acquire timeout");
	}

	i = 0;
	for (dp = direct; dp; dp = dp->next) {
		i++;
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: dplock: (%d) %s",
				i, dp->locktoken->char_uuid);

		query = apr_psprintf(pool, 
				"INSERT INTO t_dav_lock ("
					"uri, lock_type, scope, file_type, depth, "
					"timeout, lock_token, owner, auth_user) "
				"VALUES ('%s', %d, %d, %d, %d, %d, '%s', '%s', '%s')", 
				key, DAV_LOCK_DIRECT, (int)dp->f.scope,
				dp->f.type, dp->f.depth, 
				(int)dp->f.timeout, 
				(dp->locktoken->char_uuid ? dp->locktoken->char_uuid : ""),	
				dp->owner ? dp->owner : "",
				dp->auth_user ? dp->auth_user : "");

		rv = apr_dbd_query(db->driver, db->handle, &nrows, query); 
		if (rv) {
			db_error_message(db, "dbms_save_lock_record: insert(1)", rv);;
			dav_repos_release_db(r, db, rv);
			//apr_thread_rwlock_unlock(db_lock);
			return
				solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0,"DBMS Error");
		}
	}

	i = 0;
	for (ip = indirect; ip; ip = ip->next) {
		i++;
		ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "OPENDAV: iplock: (%d) %s",
				i, ip->locktoken->char_uuid);

		query = apr_psprintf(pool,
				"INSERT INTO t_dav_lock ( "
					"uri, lock_type, timeout, lock_token, lockkey) "
				"VALUES ('%s', %d, %d, '%s', '%s')",
				key, DAV_LOCK_INDIRECT, (int)ip->timeout,
				ip->locktoken->char_uuid ? ip->locktoken->char_uuid : "",
				ip->key ? ip->key : "");

		/* Execute sequry */
		rv = apr_dbd_query(db->driver, db->handle, &nrows, query);		
		if (rv) {
			db_error_message(db, "dbms_save_lock_record: insert(2)", rv);;
			dav_repos_release_db(r, db, rv);
			//apr_thread_rwlock_unlock(db_lock);
			return
				solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0,"DBMS Error");
		}
	}

	dav_repos_release_db(r, db, rv);
	//apr_thread_rwlock_unlock(db_lock);

	return NULL;
}

dav_error *dbms_remove_lock_record(request_rec *r,
		const char *key, apr_pool_t * pool)
{
	const char *query;
	ap_dbd_t *db;
	apr_status_t rv;
	int nrows;
	char tmpOrgURI[MaxSizeOfFileNameLength + MaxSizeOfFileNameLength];

    TRACE();

	dav_repos_replace_unsafe_chars(key, tmpOrgURI);

	/* We need to add name space to DB and fill hash */
	//apr_thread_rwlock_wrlock(db_lock);
    query =
		apr_psprintf(pool, "DELETE FROM t_dav_lock WHERE URI='%s'", tmpOrgURI);

	/* Execute sequry */
	db = dav_repos_get_db(r);
	if (db == NULL) {
		return solbox_dav_new_error(pool, HTTP_SERVICE_UNAVAILABLE, 0, "dbms_remove_lock_record: database acquire timeout");
	}

	rv = apr_dbd_query(db->driver, db->handle, &nrows, query);
	if (rv) {
		db_error_message(db, "dbms_remove_lock_record: delete", rv);;
		dav_repos_release_db(r, db, rv);
		//apr_thread_rwlock_unlock(db_lock);
		return solbox_dav_new_error(pool, HTTP_INTERNAL_SERVER_ERROR, 0, "DBMS Error");
	}

	dav_repos_release_db(r, db, rv);

    return NULL;
}

int dbms_lock_exists(request_rec *r, const char *key, apr_pool_t * pool)
{
    int iret = 0;
	int iselected_attr = 0;
    char *query = NULL;
	ap_dbd_t *db;
	apr_dbd_results_t *res = NULL;
	apr_dbd_row_t *row = NULL;
	apr_status_t rv;
	char tmpOrgURI[MaxSizeOfFileNameLength + MaxSizeOfFileNameLength];

	dav_repos_replace_unsafe_chars(key, tmpOrgURI);

	query = apr_psprintf(pool, 
			"SELECT COUNT(uri) FROM t_dav_lock "
#ifdef __IGNORE_CASE_DOWNLOAD__
			//"WHERE lower(uri) = lower('%s'); "
			"WHERE uri = '%s'; "
#else
			"WHERE uri = '%s'; "
#endif
			, tmpOrgURI);

    /* Execute sequry */
	db = dav_repos_get_db(r);
	if(db == NULL)
	{
		db_error_message(db, "dbms_lock_exists: database acquire timeout", 0);
		return -1;
	}
	
	rv = apr_dbd_select(db->driver, r->pool, db->handle, &res, query, 1);
	if (rv) 
	{
		db_error_message(db, "dbms_lock_exists: select", rv);;
		dav_repos_release_db(r, db, rv);
		return -1;
	}

	rv = solbox_apr_dbd_get_row(db->driver, r->pool, res, &row, 0);
	if (rv) 
	{
		db_error_message(db, "dbms_lock_exists: get row", rv);
		dav_repos_release_db(r, db, rv);
		return -2;
	}

	iselected_attr = atoi(apr_dbd_get_entry(db->driver, row,0));
	if( iselected_attr < 0) 
	{
		iret = -1;
	} 
	else if( iselected_attr == 0) 
	{
		iret = 0;
	} 
	else 
	{
		iret = 1;
	}

	dav_repos_release_db(r, db, rv);

    return iret;
}		
