#include "ReplicateInsert.h"

#include <errno.h>
#include <string.h>

#include "Logger.h"
#include "Database.h"
#include "ServiceConfig.h"
#include "Util.h"


// 처리 loop 간의 sleep time 정의
// - 매 지정된 sec 마다 queue 를 검사하여 RCDB 에 저장 처리.
#define DEFAULT_SLEEP_TIME  3
// RCDB 접속 실패 또는 Query 실패 등의 상황 발생시 
#define MAX_SLEEP_TIME     30

// Query 문장 저장을 위한 Buffer 크기
#define DEFAULT_QUERY_BUFFER_SIZE 2048


// 생성자
// @param [in] pCCompleteData replication 완료 content 정보를 저장하는 queue
CReplicateInsert::CReplicateInsert( CDataQueue<CCompleteData> * pCompleteQueue )
	: m_pCompleteQueue( pCompleteQueue )
{
	m_threadHandle = 0;

	m_uSleep = DEFAULT_SLEEP_TIME;

	// NEW 2014-06-16 신규 멤버 변수 초기화 코드 추가.
	// Init() 에서 RCDB 접속 실패가 발생 가능하므로.. 구형 RCDB 형상을 사용토록 설정.
	m_bServiceTableSeparate = false;
}

// 소멸자
CReplicateInsert::~CReplicateInsert()
{
	// Thread 동작 정지 처리
	// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로 
	//   업무 Flow 수정시 주의할 것
	if( m_threadHandle != 0 )
		pthread_cancel( m_threadHandle );
}

// 초기화 작업 수행
bool CReplicateInsert::Init()
{
	// 생성자를 통해 전달받은 queue 포인터가 NULL 인 경우... 오류 처리
	if( m_pCompleteQueue == NULL )
	{
		LOG( LERR, "replication complete queue is null" );
		return false;
	}

	// Report log 를 처리할 객체 초기화.
	if( m_reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
	{
		std::string strErrorMessage;
		m_reportLog.GetLastError( strErrorMessage );

		LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
		return false;
	}
	
	// RCDB 접속 정보 Load.
	m_rcdbInfo.Load();

	// RCDB 접속 처리를 위한 객체 생성
	DataBase rcdb;

	// RCDB 접속 시도 및 접속 실패시
	if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
		, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
		, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
	{
		LOG( LERR, "RCDB connection failed." );
		return false;
	}
	else
	{
		// 접속 성공한 경우...
		LOG( LINF, "RCDB connection test ok..." );

		// NEW 2014-06-19 huibong
		// 서비스 table 분리된 신규 형상인지.. 구형 형상인지 체크하여
		// m_bServiceTableSeparate 변수 설정 처리.
		// 해당 변수 초기화는 생성자에서 1차로 수행함.
		char szQuery[1024];
		snprintf( szQuery, 1023,
			"SELECT * FROM pg_tables WHERE schemaname = 'public' AND tablename = 't_dav_resource' " );

		rcdb.PgDoExec( szQuery );
		if( rcdb.PgResult( DataBase::NOT_CLEAR ) < 0 )
		{
			// Query 수행 결과 오류 발생시...
			// - pg_tables 가 존재하지 않을 경우 오류 발생 가능...
			// - 이는 RCDB 가 아직 적절하게 구성되지 않았다는 의미이므로... Init()함수를 오류로 처리한다.
			LOG( LERR, "RCDB t_dav_resource exist check failed.[%s][%s]", rcdb.GetErrorMessage().c_str(), szQuery );

			rcdb.PgClear();
			rcdb.PgCloseDB();

			return false;
		}
		else
		{
			// Query 수행이 정상인 경우.
			int  nResult = rcdb.GetNoTuples();
			if( nResult > 0 )
			{
				// t_dav_resource 테이블이 존재하는 경우...
				m_bServiceTableSeparate = false;
				_LOG( LINF, "CReplicateInsert: t_dav_resource table exist.[Old RCDB Type]" );
			}
			else
			{
				// t_dav_resource 테이블이 존재하지 않는 서비스별 테이블 분리 형상인 경우.
				m_bServiceTableSeparate = true;
				_LOG( LINF, "CReplicateInsert: t_dav_resource table not exist.[New RCDB Type]" );
			}
		}

		// RCDB 조회 결과 set 를 clear 처리 후 RCDB 접속 해제.
		rcdb.PgClear();
		rcdb.PgCloseDB();

		return true;
	}    
}

// Thread 를 생성하여 할당된 작업을 수행.
bool CReplicateInsert::Start()
{
	int nRet = ::pthread_create( &m_threadHandle, NULL, CReplicateInsert::threadFunc, this );
	if( nRet != 0 )
	{
		// Thread 생성 실패시
		int errorNum = errno;
		LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}

	return true;
}

// Thread function
void* CReplicateInsert::threadFunc( void* arg )
{
	CReplicateInsert* pObject = reinterpret_cast<CReplicateInsert *>( arg );
	pthread_detach( pthread_self() );

	while( 1 )
	{
		pthread_testcancel();
		pObject->Execute();
		pthread_testcancel();

		// 1회 수행 후 지정된 시간 만큼 대기
		sleep( pObject->m_uSleep );
	}

	// NEW 2014-06-19 huibong
	// Thread 종료시 m_threadHandle 값을 초기화 처리.
	// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
	pObject->m_threadHandle = 0;

	return NULL;
}

// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CReplicateInsert::Execute()
{
	// Queue 상에 처리할 Data 가 존재할 경우...	
	if( m_pCompleteQueue->GetSize() > 0 )
	{
		bool bResult;

		// t_dav_resource 테이블 사용 형상인 경우.
		if( m_bServiceTableSeparate == false )
		{
			bResult = InsertToOldDB();
		}
		else
		{
			// 서비스별 Table 이 분리된 신규 형상인 경우.
			bResult = InsertToDB();
		}

		// 처리 결과에 따라 loop sleep time 조정
		if( bResult == true )
			m_uSleep = DEFAULT_SLEEP_TIME;
		else
		{
			// RCDB 연결 실패, Queury 수행 실패 등의 오류 발생시
			m_uSleep = MAX_SLEEP_TIME;
		}
	}
	else
	{
		// queue 상에 처리할 Data 가 존재하지 않는 경우.
		m_uSleep = DEFAULT_SLEEP_TIME;
	}
}

// RCDB 상에 복제 완료된 content 정보를 insert 처리한다. ( t_dav_resource 사용 )
bool CReplicateInsert::InsertToOldDB()
{
	// 1. RCDB 와 연결 처리.
	DataBase rcdb;
	if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
		, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
		, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
	{
		// RCDB 접속이 실패
		LOG( LERR, "RCDB connection failed." );
		return false;		
	}
	
	// 2. 루프를 돌면서 queue 에 저장된 data 를 처리한다.
	
	// queue 에서 꺼낸 정보를 저장하기 위한 변수
	CCompleteData resultData;	
	// Query 문장 저장을 위한 버퍼 생성
	char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
	// Query 수행 결과값 저장을 위한 변수..	
	int nResult = 0;

	while( m_pCompleteQueue->GetSize() > 0 )	
	{
		// queue 에 저장된 첫번째 data 복사 (queue 에서 삭제된 상태 아님)		
		resultData = m_pCompleteQueue->Front();
		
		// URI의 '(작은따옴표) 문자가 있을 경우 SQL 오류 방지를 위하여 관련 문자를 다르게 변경함
		std::string orgURI = stringreplace(resultData.m_OriginData.m_szURI, "'", "''");
		
		// 원본 조회 서브 쿼리 절에서 분리함 2012.02.10
		// PGPool 경우 INSERT~SELECT 사용 지향
		// 해당 서브절에 SELECT 값이 DB01,DB02 서로 다른 값을 참조될 수 있다.
	
		// 3. 원본 존재 여부 확인
		// Replication 처리가 완료된 Content 에 대해 meta 정보 입력전 실제 원본이 존재하는지 확인한다.
		sprintf( szQuery,
				"SELECT "
				"  sp_user_seq, sp_svc_tran_id, "
				"  '%s', "
				"  resource_type, uri, filename_org, '%s', "
				"  depth, display_name, get_content_language, "
				"  get_content_length, get_content_type, "
				"  get_etag, get_lastmodified, creation_date, "
				"  lock_discovery, source, supported_lock, deleted_yn, 'R', file_lastmodified " // is_cache = R
				"FROM t_dav_resource "
				"WHERE filename_hash = '%s' "
				"AND uri = '%s' "
				"AND get_content_length = %lld "
				"AND is_cache = 'N' AND host_name = '%s' ;",
				resultData.m_szTargetHostName.c_str(),
				resultData.m_szTargetFileNameHash.c_str(),
				resultData.m_OriginData.m_szFileNameHash.c_str(), 
				orgURI.c_str(),
				resultData.m_OriginData.m_nContentLength,
				resultData.m_OriginData.m_szOriginHostName.c_str() );

		// Query 수행 및 결과 확인
		rcdb.PgDoExec( szQuery );
		nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
		if( nResult < 0 )
		{
			// RCDB t_dav_resource 테이블 조회 실패시
			// - RCDB 연결 관련 실패시 : nResult = -4
			// - uri 상의 특수문자로 인해 query 오류 : nResult = -4
			// - 즉 오류 반환값이 동일하기 때문에 어떠한 문제로 query 가 실패했는지 판단 못함.
			
			// 우선 DB 연결 문제일 수 있으므로... 
			// RCDB 재접속 시도.
			rcdb.PgClear();
			rcdb.PgCloseDB();
			sleep( 1 );

			if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
				, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
				, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
			{
				// RCDB 접속 실패 문제인 경우..
				// - 본 처리 함수 종료 처리하여 다음 loop 에서 재시도토록 처리.
				LOG( LERR, "select query fail because RCDB connection error. [%d][%s]"
					, nResult, rcdb.GetErrorMessage().c_str());
				
				rcdb.PgCloseDB();
				return false;
			}
			else
			{
				// uri 상에 특수 문자 문제로 query 실패시
				// - Report 로그에 관련 내역 기록
				// - queue 에서 해당 정보를 삭제 처리한 후 계속 수행한다.
				LOG( LERR, "select query fail. queue forced pop.[%s][%d][%s]"
					, orgURI.c_str(), nResult, rcdb.GetErrorMessage().c_str() );

				m_reportLog.Write( "WAR", "[%s][Replication contents RCDB insert fail. check uri.[%s]]"
					, PROG_NAME, orgURI.c_str());

				rcdb.PgClear();
				m_pCompleteQueue->Pop();
				continue;
			}
		}

		// 결과 Row 수 확인.
		nResult = rcdb.GetNoTuples();
		if( nResult <= 0 )
		{
			// Replication 생성된 content 가 RCDB 에 존재하지 않는 경우...
			// - RCDB 에 저장하지 않고 그냥 버린다.
			// - 이로 인해 FHS 에 meta 기준으로 삭제 불가능한 파일이 남게 된다.			
			_LOG( LINF, "Replication content[%s] not found in RCDB.", orgURI.c_str() );
			
			rcdb.PgClear();
			m_pCompleteQueue->Pop();
			continue;
		}

		// 조회된 원본 정보를 list 에 저장
		std::vector<string> srcinfo;
		std::string strResult;
		for( int j = 0 ; j < 21; j++ )
		{
			strResult = rcdb.GetValue( 0, j );
			strResult = stringreplace( strResult, "'", "''" );
			srcinfo.push_back( strResult );
		}

		// Select Query 의 결과를 Clear 
		rcdb.PgClear();

		// 4. Replication 된 Content 정보를 Meta 정보로 Insert
		// INSERT 작업시 앞에서 조회된 원본 정보를 이용하여 INSERT 한다.
		sprintf( szQuery,
				"INSERT INTO t_dav_resource "
				"( "
				"  sp_user_seq, sp_svc_tran_id, "
				"  host_name, "
				"  resource_type, uri, filename_org, filename_hash, "
				"  depth, display_name, get_content_language, "
				"  get_content_length, get_content_type, "
				"  get_etag, get_lastmodified, creation_date, "
				"  lock_discovery, source, supported_lock, deleted_yn, is_cache ,file_lastmodified) "
				"VALUES "
				"( "
				"  %s, %s, "
				"  '%s', "
				"  %s, '%s', '%s', '%s', "
				"  %s, '%s', '%s', "
				"  %s, '%s', "
				"  '%s', %s, %s, "
				"  '%s', %s, '%s', '%s', '%s', %s );", // is_cache = R
				srcinfo[0].c_str(),srcinfo[1].c_str(),srcinfo[2].c_str(),srcinfo[3].c_str(),
				srcinfo[4].c_str(),srcinfo[5].c_str(),srcinfo[6].c_str(),srcinfo[7].c_str(),
				srcinfo[8].c_str(),srcinfo[9].c_str(),srcinfo[10].c_str(),srcinfo[11].c_str(),
				srcinfo[12].c_str(),srcinfo[13].c_str(),srcinfo[14].c_str(),srcinfo[15].c_str(),
				srcinfo[16].c_str(),srcinfo[17].c_str(),srcinfo[18].c_str(),srcinfo[19].c_str(),srcinfo[20].c_str()
				);

		// INSERT query 수행
		rcdb.PgDoExec( szQuery );
		nResult = rcdb.PgResult( DataBase::CLEAR );
		if( nResult  < 0 )
		{
			// RCDB INSERT 작업시 오류가 발생한 경우...
			// - uri 문제는 앞의 SELECT query 에서 체크했으므로 아니고....
			// - DB 연결 문제 역시 바로 앞에서 체크 했으므로 아닐 가능성이 큼.
			// - 가장 큰 오류는 Query 오류일 가능성이 크므로...
			// - Queue 에 pop 처리한다.
			
			// Report 로그에 관련 내역 기록
			LOG( LERR, "insert query fail. queue forced pop.[%s][%d][%s]"
				, orgURI.c_str(), nResult, rcdb.GetErrorMessage().c_str() );

			m_reportLog.Write( "WAR", "[%s][Replication contents RCDB insert fail. Check query[%s][%s]]"
				, PROG_NAME, orgURI.c_str(), rcdb.GetErrorMessage().c_str() );

			// queue 에서 해당 정보를 삭제 처리한 후 계속 수행한다.
			rcdb.PgClear();
			m_pCompleteQueue->Pop();
			continue;			
		}

		rcdb.PgClear();

		// NEW 2012-09-24 huibong Replication 최종 정합성 검증 기능 추가
		// 원본 존재 여부를 확인한 후 Insert 를 수행하지만...
		// 해당 interval 동안 원본이 MOVE 또는 Rename, Delete 되는 상황이 발생하여  잘못된 Replication이 발생하고..이로 인해 서비스 장애가 발생 가능
		// INSERT 작업 후 원본에 대한 정합성 여부를 다시 한번 확인하여 
		// 원본이 변경된 경우 INSERT 된 Meta 정보를 DELETE 처리한다.

		// 5. INSERT 된 Meta 정보에 대해 원본이 존재하는지 다시 한번 확인하여
		// 원본이 변경된 경우 INSERT 된 정보를 DELETE 한다. (deleted_yn = 'Y', 물리 Content 자동 제거 처리 목적 )
		sprintf( szQuery,
				"SELECT "
				"  uri, get_content_length, deleted_yn "
				"FROM t_dav_resource "
				"WHERE filename_hash = '%s' "
				"AND uri = '%s' "
				"AND get_content_length = %lld "
				"AND is_cache = 'N' AND host_name = '%s' AND deleted_yn = '%s' ;",
				resultData.m_OriginData.m_szFileNameHash.c_str(), 
				orgURI.c_str(),
				resultData.m_OriginData.m_nContentLength,
				resultData.m_OriginData.m_szOriginHostName.c_str(),
				srcinfo[18].c_str()
				);

		rcdb.PgDoExec( szQuery );
		nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
		if( nResult < 0 )
		{
			// 최종 검증을 위한 Check query 가 실패한 경우..
			// query 구문은 앞의 2번 query 를 통해 검증되었으므로.. query 문제가 아닐 듯.
			// 본 오류는 대부분 RCDB 연결 관련 문제인 듯.
			// - 로깅 처리 후
			// - RCDB 에 이미 INSERT 처리 되었으므로... queue pop 처리
			// - DB 세션 종료 처리 후 .. return 하여.. sleep 후 다음 루프가 동작하도록 처리.
			LOG(LERR, "replication last check query fail.[%s][%d][%s][%s]"
				, orgURI.c_str(), nResult, rcdb.GetErrorMessage().c_str(), szQuery );

			rcdb.PgClear();
			rcdb.PgCloseDB();

			m_pCompleteQueue->Pop();
			return false;			
		}

		// 조회된 결과 row 수 확인
		nResult = rcdb.GetNoTuples();
		rcdb.PgClear();

		if( nResult <= 0 )
		{
			// 원본이 변경된 경우......
			// 앞에서 INSERT 된 Replication 정보를 deleted_yn = 'Y' 처리한다.
			LOG( LWAR, "replication check source not found. insert replication value set deleted [%s]"
				, orgURI.c_str() );

			sprintf( szQuery,
					"UPDATE t_dav_resource SET deleted_yn = 'Y', get_lastmodified = %lld "
					"WHERE uri = '%s' "
					"  AND host_name = '%s' "
					"  AND filename_hash = '%s' "
					"  AND get_content_length = %lld "
					"  AND is_cache = 'R' "
					"  AND deleted_yn = 'N' ;",
					longtime_now(),
					orgURI.c_str(),
					resultData.m_szTargetHostName.c_str(),
					resultData.m_szTargetFileNameHash.c_str(),
					resultData.m_OriginData.m_nContentLength );
			
			rcdb.PgDoExec( szQuery );
			nResult = rcdb.PgResult( DataBase::CLEAR );
			if( nResult < 0 )
			{
				// 삭제 처리 작업이 실패한 경우...
				// RCDB 연결 문제로 인한 가능성이 가장 크므로...
				// - 로깅 후..
				// - RCDB 연결 해제...
				// - queue pop 후 다음 루프에서 처리토록 return 처리.
				LOG( LERR, "invalid replication delete fail.[%s][%s]"
					, orgURI.c_str(), rcdb.GetErrorMessage().c_str());

				m_reportLog.Write( "WAR", "[%s][Invalid replication contents exist.[%s]]"
					, PROG_NAME, orgURI.c_str() );

				rcdb.PgClear();
				rcdb.PgCloseDB();

				m_pCompleteQueue->Pop();
				return false;
			}
		}

		// 1 개 replication content 에 대한 RCDB insert 작업이 완료된 경우.
		// - RCDB clear 처리후
		// - queue pop.		
		rcdb.PgClear();
		m_pCompleteQueue->Pop();

		_LOG( LDBG, "replication rcdb Insert [%s|%s]->[%s|%s|%llu]"
			, resultData.m_OriginData.m_szOriginHostName.c_str()
			, resultData.m_OriginData.m_szURI.c_str()
			, resultData.m_szTargetHostName.c_str()
			, resultData.m_szTargetFileNameHash.c_str()
			, resultData.m_OriginData.m_nContentLength 
			);
	}

	// queue 에 저장된 정보 처리 작업이 완료된 경우...
	// - RCDB 연결 해제 처리.
	rcdb.PgClear();
	rcdb.PgCloseDB();

	return true;
}

// RCDB 상에 복제 완료된 content 정보를 insert 처리한다. ( t_meta_xxx 사용 )
// - 신규 RCDB 관련 미사용 Column 항목 제거 처리.
bool CReplicateInsert::InsertToDB()
{
	// 1. RCDB 와 연결 처리.
	DataBase rcdb;
	if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
		, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
		, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
	{
		// RCDB 접속이 실패
		LOG( LERR, "RCDB connection failed." );
		return false;
	}

	// 2. 루프를 돌면서 queue 에 저장된 data 를 처리한다.

	// queue 에서 꺼낸 정보를 저장하기 위한 변수
	CCompleteData resultData;
	// Query 문장 저장을 위한 버퍼 생성
	char szQuery[DEFAULT_QUERY_BUFFER_SIZE];
	// Query 수행 결과값 저장을 위한 변수..	
	int nResult = 0;

	while( m_pCompleteQueue->GetSize() > 0 )	
	{
		// queue 에 저장된 첫번째 data 복사 (queue 에서 삭제된 상태 아님)		
		resultData = m_pCompleteQueue->Front();

		// URI의 '(작은따옴표) 문자가 있을 경우 SQL 오류 방지를 위하여 관련 문자를 다르게 변경함
		std::string orgURI = stringreplace( resultData.m_OriginData.m_szURI, "'", "''" );

		// 원본 조회 서브 쿼리 절에서 분리함 2012.02.10
		// PGPool 경우 INSERT~SELECT 사용 지향
		// 해당 서브절에 SELECT 값이 DB01,DB02 서로 다른 값을 참조될 수 있다.

		// CHG 2015-02-12 huibong 
		// 신규 RCDB 형상에 맞도록 처리 Flow 단순화 (#21354)
		// - RCDB 구조가 pgpool -> streaming replicaton 구조로 변경됨
		// - 이에 따라 기존 SELECT 후 INSERT, 검수, DELETE flow 에 대한 단순화 가능함.

		sprintf( szQuery,
				 "INSERT INTO t_meta_%s "                       // 신형 RCDB 형상
				 "( "
				 "  sp_user_seq, sp_svc_tran_id, "
				 "  host_name, "
				 "  resource_type, uri, filename_org, filename_hash, "
				 "  depth, display_name, "
				 "  get_content_length, get_content_type, "
				 "  get_lastmodified, creation_date, "
				 "  source, deleted_yn, is_cache , file_lastmodified "
				 ") "
				 "SELECT "
				 "  sp_user_seq, sp_svc_tran_id, "
				 "  '%s', "
				 "  resource_type, uri, filename_org, '%s', "
				 "  depth, display_name, "
				 "  get_content_length, get_content_type, "
				 "  get_lastmodified, creation_date, "
				 "  source, deleted_yn, 'R', file_lastmodified " // is_cache = R
				 "FROM t_meta_%s "                               // 신형 RCDB 형상
				 "WHERE filename_hash = '%s' "
				 "AND uri = '%s' "
				 "AND get_content_length = %lld "
				 "AND is_cache = 'N' AND host_name = '%s' ;"
				 , resultData.m_OriginData.m_szServiceSeq.c_str()
				 , resultData.m_szTargetHostName.c_str()
				 , resultData.m_szTargetFileNameHash.c_str()
				 , resultData.m_OriginData.m_szServiceSeq.c_str()
				 , resultData.m_OriginData.m_szFileNameHash.c_str()
				 , orgURI.c_str()
				 , resultData.m_OriginData.m_nContentLength
				 , resultData.m_OriginData.m_szOriginHostName.c_str() 
		);

		// INSERT-SELECT query 수행
		rcdb.PgDoExec( szQuery );
		nResult = rcdb.PgResult( DataBase::NOT_CLEAR );
		if( nResult  < 0 )
		{
			// RCDB SELECT-INSERT 작업시 오류가 발생한 경우...
			// - RCDB 연결 관련 실패시 : nResult = -4
			// - uri 상의 특수문자로 인해 query 오류 : nResult = -4
			// - 그외 Query 오류일 가능성이 존재

			std::string strPreviousErrorMessage = rcdb.GetErrorMessage();
			
			// 우선 DB 연결 문제일 수 있으므로... RCDB 재접속 시도.
			rcdb.PgClear();
			rcdb.PgCloseDB();
			sleep( 1 );

			if( rcdb.PgOpenDB( m_rcdbInfo.m_strRcdbIp, m_rcdbInfo.m_nRcdbPort
				, m_rcdbInfo.m_strRcdbName, m_rcdbInfo.m_strRcdbAcct
				, m_rcdbInfo.m_strRcdbAcctPw ) == NULL )
			{
				// RCDB 접속 실패 문제인 경우..
				// - 본 처리 함수 종료 처리하여 다음 loop 에서 재시도토록 처리.
				LOG( LERR, "Query fail because New RCDB connection error. [%d][%s]"
					 , nResult, rcdb.GetErrorMessage().c_str() );

				rcdb.PgCloseDB();
				return false;
			}
			else
			{
				// uri 상에 특수 문자 또는 Query 문제인 경우...
				// - Report 로그에 관련 내역 기록
				// - queue 에서 해당 정보를 삭제 처리한 후 계속 수행한다.
								
				LOG( LERR, "insert-select query fail. queue forced pop.[%s][%d][%s]"
					 , orgURI.c_str(), nResult, strPreviousErrorMessage.c_str() );

				// Report 로그에 관련 내역 기록 
				m_reportLog.Write( "WAR", "[%s][Replication contents New RCDB insert fail. Check query[%s][%s]]"
								   , PROG_NAME, orgURI.c_str(), strPreviousErrorMessage.c_str() );

				rcdb.PgClear();
				m_pCompleteQueue->Pop();
				continue;
			}
		}

		// PgResult 호출시 NOT_CLEAR 옵션을 사용했으므로....INSERT 수행 row 수 확인을 위해 GetCmdTuples() 함수를 호출가능.
		nResult = rcdb.GetCmdTuples();
		if( nResult > 0 )
		{
			_LOG( LDBG, "replication rcdb Insert [%d] row, [%s|%s]->[%s|%s|%llu]"
				  , nResult
				  , resultData.m_OriginData.m_szOriginHostName.c_str()
				  , resultData.m_OriginData.m_szURI.c_str()
				  , resultData.m_szTargetHostName.c_str()
				  , resultData.m_szTargetFileNameHash.c_str()
				  , resultData.m_OriginData.m_nContentLength
			);
		}
		else
		{
			// INSERT 처리된 row 가 없는 경우....
			// - 생성된 replicaton 파일은 고아 파일이 됨.
			_LOG( LWAR, "replication rcdb Insert origin not found. orgin[%s|%s|%llu|%s] orphan[%s|%s|%llu]"
				  , resultData.m_OriginData.m_szOriginHostName.c_str()
				  , resultData.m_OriginData.m_szFileNameHash.c_str()
				  , resultData.m_OriginData.m_nContentLength
				  , resultData.m_OriginData.m_szURI.c_str()	
				  , resultData.m_szTargetHostName.c_str()
				  , resultData.m_szTargetFileNameHash.c_str()
				  , resultData.m_OriginData.m_nContentLength
			);
		}

		
		// 1 개 replication content 에 대한 RCDB insert 작업이 완료된 경우.
		// - RCDB clear 처리후
		// - queue pop.		
		rcdb.PgClear();
		m_pCompleteQueue->Pop();

	}

	// queue 에 저장된 정보 처리 작업이 완료된 경우...
	// - RCDB 연결 해제 처리.
	rcdb.PgClear();
	rcdb.PgCloseDB();

	return true;
}
