'Oracle'에 해당되는 글 2건
- 2006/04/06 The Oracle + PHP Cookbook :: Oracle/PHP 환경의 LOB 처리
- 2006/03/13 Linux 및 Windows 환경을 위한 PHP, Oracle 10g Instant Client 설치 (1)
출처: http://www.oracle.com/technology/global/kr/pub/articles/oracle_php_cookbook/fuecks_lobs.html저자 Harry Fuecks
4,000 바이트 제한이 문제가 되십니까? LOB를 활용하십시오...
Downloads for this article:
Oracle Database 10g
Zend Core for Oracle
Apache HTTP Server 1.3 (및 이후 버전)
VARCHAR2와 같은 오라클 데이터 타입은 유용하게 활용됩니다. 하지만 4,000 바이트 이상의 데이터를 저장하려면 어떻게 해야 할까요? 바로 오라클이 지원하는 Long Object (LOB) 데이터 타입이 필요합니다. 또 LOB와 호환하는 PHP API의 사용법을 알고 있어야 합니다. 이에 필요한 지식을 갖고 있지 않은 개발자에게는 무척 까다로운 과제가 될 수 있습니다
이번 “Oracle+PHP Cookbook” 시리즈 연재에서는, LOB 데이터 타입과 PHP에서 LOB를 다루는 방법을 예제를 통해 알아보기로 합니다.
오라클의 Long Object
오라클은 다음과 같은 LOB 데이터 타입을 지원합니다:
BLOB - 바이너리 데이터의 저장에 사용
CLOB - 데이터베이스 문자 셋 인코딩을 이용한 문자 데이터 저장에 사용
NCLOB - 국가별 문자 셋을 이용한 유니코드 문자 데이터 저장에 사용. (본 문서에서 사용되는 PHP OCI8 익스텐션은 NCLOB을 지원하지 않음을 참고하십시오.)
BFILE - 운영체제 파일시스템에 저장된 외부 파일의 참조에 사용
그 밖에도 temporary LOB가 있습니다. temporary LOB는 BLOB, CLOB 또는 NCLOB 등의 형태를 취할 수 있으며, 해제되기 전까지 임시 테이블스페이스에 저장됩니다.
이전 버전의 오라클에서는 문자 및 바이너리 데이터의 저장을 위해 LONG, LONG RAW 타입을 지원했습니다. 이 두 가지 타입은 Oracle9i에서 LOB로 대체되었습니다.
LOB 데이터의 저장. Oracle Database 10g는 BLOB, CLOB, NCLOB 타입에 대해 단일 레코드 당 최대 128TB의 용량을 지원합니다 (실제 최대 용량은 데이터베이스 블록 사이즈와 LOB의 “chunk” 설정에 따라 달라질 수 있습니다.
LOB는 LOB 컨텐트와 LOB 로케이터(locator)의 두 가지 요소로 구성됩니다. LOB 로케이터는 LOB에 대한 “포인터”로 활용됩니다. LOB 로케이터는 LOB 데이터를 효과적으로 저장하고 관리하기 위한 목적에서 설계되었으며, PHP API의 INSERT, UPDATE, SELECT 에도 반영되어 있습니다 (아래 설명 참고)..
오라클은 LOB 레코드의 크기가 4KB를 넘지 않는 경우 LOB 컨텐트를 테이블 내부에 “인라인(in-line)” 형태로 저장합니다. 4KB를 초과하는 LOB는 테이블의 테이블스페이스 내에 아웃오브라인(out-of-line)” 형태로 저장됩니다. 이러한 방법으로 작은 크기의 LOB를 신속하게 조회하는 한편, 큰 LOB 때문에 테이블 스캔 성능이 저하되는 것을 방지할 수 있습니다.
LOB 저장 및 액세스를 위해 메모리 캐싱, 버퍼링과 같은 테크닉을 사용하여 애플리케이션의 성능을 개선할 수 있습니다. 자세한 정보는 오라클 제품문서의 LOB Performance Guidelines와 Oracle Database Application Developer's Guide - Large Objects를 참고하시기 바랍니다.
LOB 관련 제약사항. LOB 타입의 활용에 관련한 몇 가지 제약사항이 존재합니다. 특히 SQL 구문과 관련한 제약사항은 주의할 필요가 있습니다. 아래와 같은 쿼리에서는 LOB 타입을 사용할 수 없습니다.
SELECT DISTINCT
ORDER BY
GROUP BY
또 테이블 JOIN,UNION, INTERSECTION, MINUS 등의 구문에서도 LOB 타입 컬럼을 사용하는 것이 금지되어 있습니다.
그 밖에도 LOB를 프라이머리 키 컬럼으로 사용할 수 없는 등의 여러 가지 제약사항이 존재합니다. 자세한 정보는 Oracle Database Application Developer's Guide - Large Objects를 참고하십시오.
CLOB과 문자 셋
데이터베이스의 디폴트 문자 셋(character set)은 NLS_CHARACTERSET 매개변수를 통해 설정되며, CLOB 타입으로 저장된 텍스트는 이 문자 셋을 이용하여 인코딩 됩니다. 아래 SQL 구문을 이용하여 데이터베이스 문자 셋을 설정하시기 바랍니다:
SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET'
현재로서는 PHP가 NCLOB을 지원하지 않으므로, 아래와 같은 방법으로 UTF-8과 같은 유니코드 인코딩을 기본 데이터베이스 문자 셋으로 설정하는 것을 고려해 볼 수 있습니다:
ALTER DATABASE CHARACTER SET UTF8
참고: 기존 데이터 또는 애플리케이션 코드가 다른 문자 셋을 사용하고 있는 경우, 변경 작업이 전체 환경에 미치는 영향을 미리 감안하여야 합니다. 자세한 정보는 Oracle Globalization Support Guide와 An Overview on Globalizing Oracle PHP Applications를 참고하시기 바랍니다.
LOB 데이터의 처리
본 문서에서는 PHP의 OCI8 익스텐션에 초점을 맞추어 설명합니다. 또 오라클에 포함된 DBMS_LOB 패키지는 PL/SQL을 이용한 LOB 처리를 위한 병렬 프로시저 및 함수를 제공하고 있음을 참고하시기 바랍니다.
PHP OCI8 익스텐션은 글로벌 PHP 네임스페이스에 “OCI-Lob”이라 불리는 PHP 클래스를 등록합니다. LOB 타입의 컬럼이 포함된 SELECT 구문이 실행된 경우, PHP는 이 구문을 OCI-Lob 오브젝트 인스턴스에 자동으로 바인딩합니다. OCI-Lob 오브젝트에 대한 참조가 확보되면 load(), save()등의 메소드를 이용하여 LOB 컨텐트를 조회, 수정할 수 있습니다.
사용 가능한 OCI-Lob 메소드는 PHP 버전에 따라 달라집니다. PHP5는 read(), seek(), 그리고 append()등의 메소드를 지원합니다. 이에 대한 PHP Manual 의 설명이 다소 불충분한 감이 있으므로, 지원되는 버전 넘버가 궁금한 경우 아래와 같은 스크립트를 사용해서 검증해 보시기 바랍니다.
foreach (get_class_methods('OCI-Lob') as $method ) {
print "OCI-Lob::$method()\n";
}
?>
PHP 5.0.5가 실행 중인 필자의 시스템에서는 아래와 같은 메소드 리스트가 출력되었습니다:
OCI-Lob::load()
OCI-Lob::tell()
OCI-Lob::truncate()
OCI-Lob::erase()
OCI-Lob::flush()
OCI-Lob::setbuffering()
OCI-Lob::getbuffering()
OCI-Lob::rewind()
OCI-Lob::read()
OCI-Lob::eof()
OCI-Lob::seek()
OCI-Lob::write()
OCI-Lob::append()
OCI-Lob::size()
OCI-Lob::writetofile()
OCI-Lob::writetemporary()
OCI-Lob::close()
OCI-Lob::save()
OCI-Lob::savefile()
OCI-Lob::free()
PHP 4.x OCI8 익스텐션은 전체 LOB의 일괄적인 읽기 및 쓰기만을 지원합니다. PHP5에서는 LOB의 일부 “청크(chung)”만을 읽거나 쓸 수 있으며 setBuffering(),getBuffering() 메소드를 이용한 LOB 버퍼링을 지원합니다. 또 PHP5는 스탠드얼론 함수로 oci_lob_is_equal(), oci_lob_copy()를 지원합니다.
본 문서에서 사용된 예제들은 새로운 PHP5 OCI 함수명을 사용하고 있습니다 (예: oci_parse 대신 OCIParse 사용)예제에서 공통적으로 사용되는 시퀀스와 테이블이 아래와 같습니다:
CREATE SEQUENCE mylobs_id_seq
NOMINVALUE
NOMAXVALUE
NOCYCLE
CACHE 20
NOORDER
INCREMENT BY 1;
CREATE TABLE mylobs (
id NUMBER PRIMARY KEY,
mylob CLOB
)
본 문서의 예제는 대부분 CLOB을 사용하고 있지만, 동일한 로직이 BLOB에도 적용 가능함을 참고하시기 바랍니다.
LOB의 INSERT
LOB의 INSERT 작업을 수행하려면, 먼저 오라클의 EMPTY_BLOB 또는 EMPTY_CLOB 함수를 이용하여 LOB를 초기화해야 합니다. NULL 값을 포함한 LOB는 업데이트할 수 없습니다.
초기화가 완료되면, 컬럼을 PHP OCI-Lob 오브젝트에 바인딩하고 오브젝트의 save() 메소드를 이용하여 LOB 컨텐트를 업데이트합니다.
아래 스크립트는 LOB 타입 데이터의 INSERT 쿼리 수행을 위한 예를 보여주고 있습니다:
// connect to DB etc...
$sql = "INSERT INTO
mylobs
(
id,
mylob
)
VALUES
(
mylobs_id_seq.NEXTVAL,
--Initialize as an empty CLOB
EMPTY_CLOB()
)
RETURNING
--Return the LOB locator
mylob INTO :mylob_loc";
$stmt = oci_parse($conn, $sql);
// Creates an "empty" OCI-Lob object to bind to the locator
$myLOB = oci_new_descriptor($conn, OCI_D_LOB);
// Bind the returned Oracle LOB locator to the PHP LOB object
oci_bind_by_name($stmt, ":mylob_loc", $myLOB, -1, OCI_B_CLOB);
// Execute the statement using , OCI_DEFAULT - as a transaction
oci_execute($stmt, OCI_DEFAULT)
or die ("Unable to execute query\n");
// Now save a value to the LOB
if ( !$myLOB->save('INSERT: '.date('H:i:s',time())) ) {
// On error, rollback the transaction
oci_rollback($conn);
} else {
// On success, commit the transaction
oci_commit($conn);
}
// Free resources
oci_free_statement($stmt);
$myLOB->free();
// disconnect from DB etc.
?>
위 예제에서 트랜잭션을 사용하고 있으며, oci_execute 의 실행과정에서 OCI_DEFAULT 상수를 통해 an oci_commit 또는 oci_rollback를 대기할 것을 지시하고 있습니다. 이는 INSERT 작업이 두 단계(로우의 생성, LOB의 업데이트)로 실행되기 때문입니다.
참고 위에서는 BLOB 타입이 사용되고 있으므로oci_bind_by_name 함수를 호출하는 작업만이 필요합니다:
oci_bind_by_name($stmt, ":mylob_loc", $myLOB, -1, OCI_B_BLOB);
마찬가지로, LOB 타입을 명시하지 않고 문자열을 바인딩할 수도 있습니다;
// etc.
$sql = "INSERT INTO
mylobs
(
id,
mylob
)
VALUES
(
mylobs_id_seq.NEXTVAL,
:string
)
";
$stmt = oci_parse($conn, $sql);
$string = 'INSERT: '.date('H:i:s',time());
oci_bind_by_name($stmt, ':string', $string);
oci_execute($stmt)
or die ("Unable to execute query\n");
// etc.
?>
위와 같은 방법을 사용하면 코드를 대폭적으로 단순화할 수 있습니다. 이러한 방식은 LOB에 기록되는 데이터의 크기가 비교적 작은 경우에 유용합니다. 큰 용량의 파일 컨텐트를 LOB에 스트리밍하고자 하는 경우에는, PHP LOB 오브젝트에 대해 write(), flush() 함수를 호출하고 (전체 파일을 하나의 인스턴스로 메모리에 보관하는 대신) 작은 청크(chunk) 단위로 루프를 돌려 컨텐트를 처리하는 것이 좋습니다.
LOB의 SELECT
LOB 컬럼을 포함하는 데이터를 SELECT 쿼리로 조회하는 경우, PHP는 해당 컬럼을 OCI-Lob 오브젝트에 자동으로 바인딩합니다. 그 예가 다음과 같습니다:
// etc.
$sql = "SELECT
*
FROM
mylobs
ORDER BY
Id
";
$stmt = oci_parse($conn, $sql);
oci_execute($stmt)
or die ("Unable to execute query\n");
while ( $row = oci_fetch_assoc($stmt) ) {
print "ID: {$row['ID']}, ";
// Call the load() method to get the contents of the LOB
print $row['MYLOB']->load()."\n";
}
// etc.
?>
OCI_RETURN_LOBS 상수와 oci_fetch_array()를 함께 사용하여 LOB 오브젝트를 그 값으로 대치하도록 함으로써 작업을 한층 단순화할 수 있습니다:
while ( $row = oci_fetch_array($stmt, OCI_ASSOC+OCI_RETURN_LOBS) ) {
print "ID: {$row['ID']}, {$row['MYLOB']}\n";
}
LOB의 UPDATE
LOB를 업데이트하는 경우, 위의 INSERT 구문 예와 마찬가지로 "RETURNING" 커맨드를 사용할 수도 있습니다. 하지만 “SELECT ... FOR UPDATE” 구문을 사용하는 편이 훨씬 간단합니다:
// etc.
$sql = "SELECT
mylob
FROM
mylobs
WHERE
id = 3
FOR UPDATE /* locks the row */
";
$stmt = oci_parse($conn, $sql);
// Execute the statement using OCI_DEFAULT (begin a transaction)
oci_execute($stmt, OCI_DEFAULT)
or die ("Unable to execute query\n");
// Fetch the SELECTed row
if ( FALSE === ($row = oci_fetch_assoc($stmt) ) ) {
oci_rollback($conn);
die ("Unable to fetch row\n");
}
// Discard the existing LOB contents
if ( !$row['MYLOB']->truncate() ) {
oci_rollback($conn);
die ("Failed to truncate LOB\n");
}
// Now save a value to the LOB
if ( !$row['MYLOB']->save('UPDATE: '.date('H:i:s',time()) ) ) {
// On error, rollback the transaction
oci_rollback($conn);
} else {
// On success, commit the transaction
oci_commit($conn);
}
// Free resources
oci_free_statement($stmt);
$row['MYLOB']->free();
// etc.
?>
INSERT의 경우와 마찬가지로, 위 코드는 UPDATE 수행을 위해 트랜잭션을 이용하고 있습니다. 특히 주목해야 할 부분이 truncate()함수를 호출하는 부분입니다. save()를 이용해서 LOB를 업데이트하는 경우, save() 함수는 LOB 컨텐트의 시작 부분에서부터 새로운 데이터의 길이에 해당되는 부분까지만을 업데이트합니다. 따라서 이전의 컨텐트가 여전히 LOB에 남아 있을 수도 있습니다.
PHP 4.x, truncate()함수가 지원되지 않습니다. 따라서 새로운 데이터를 업데이트하기 전에 LOB의 기존 컨텐트를 지우려면 오라클의 EMPTY_CLOB() 함수를 사용해야 합니다.
$sql = "UPDATE
mylobs
SET
mylob = EMPTY_CLOB()
WHERE
id = 2403
RETURNING
mylob INTO :mylob
";
$stmt = OCIParse($conn, $sql);
$mylob = OCINewDescriptor($conn,OCI_D_LOB);
OCIBindByName($stmt,':mylob',$mylob, -1, OCI_B_CLOB);
// Execute the statement using OCI_DEFAULT (begin a transaction)
OCIExecute($stmt, OCI_DEFAULT)
or die ("Unable to execute query\n");
if ( !$mylob->save( 'UPDATE: '.date('H:i:s',time()) ) ) {
OCIRollback($conn);
die("Unable to update lob\n");
}
OCICommit($conn);
$mylob->free();
OCIFreeStatement($stmt);
BFILE에 대한 작업 수행
BFILE 타입에 대해 INSERT 또는 UPDATE를 수행하는 경우, 오라클은 (웹 서버가 아닌) 데이터베이스 서버 운영체제의 파일시스템에서 저장된 파일의 위치를 지정하는 작업을 수행하게 됩니다. 또 SELECT 구문을 이용하여 BILE의 컨텐트를 읽어 들이거나, 필요한 경우 DBMS_LOB 패키지의 함수와 프로시저를 호출하여 파일에 대한 정보만을 확인할 수도 있습니다.
BFILE은 파일시스템에 저장된 파일을 직접 액세스하는 한편으로 SQL 구문을 이용하여 파일의 위치를 확인할 수 있게 한다는 장점을 제공합니다. 예를 들어 웹 서버에서 이미지를 직접 제공하면서, BFILE을 포함한 테이블과 다른 테이블과의 관계 정보를 추적하는 것이 가능합니다 (한 예로, 어떤 사용자가 어떤 파일을 업로드했는지 확인할 수 있습니다).
위에서 사용된 테이블 스키마를 먼저 업데이트해 보겠습니다;
ALTER TABLE mylobs ADD( mybfile BFILE )
그런 다음, 오라클에 디렉토리 앨리어스(alias)를 등록하고(이 과정에서 관리자 권한이 필요합니다), 테이블에 대한 읽기 권한을 할당합니다:
CREATE DIRECTORY IMAGES_DIR AS '/home/harryf/public_html/images'
GRANT READ ON DIRECTORY IMAGES_DIR TO scott
이제 아래와 같이 BFILE의 INSERT 작업을 수행할 수 있습니다:
// etc.
// Build an INSERT for the BFILE names
$sql = "INSERT INTO
mylobs
(
id,
mybfile
)
VALUES
(
mylobs_id_seq.NEXTVAL,
/*
Pass the file name using the Oracle directory reference
I created called IMAGES_DIR
*/
BFILENAME('IMAGES_DIR',:filename)
)";
$stmt = oci_parse($conn, $sql);
// Open the directory
$dir = '/home/harryf/public_html/images';
$dh = opendir($dir)
or die("Unable to open $dir");
// Loop through the contents of the directory
while (false !== ( $entry = readdir($dh) ) ) {
// Match only files with the extension .jpg, .gif or .png
if ( is_file($dir.'/'.$entry) && preg_match('/\.(jpg|gif|png)$/',$entry) ) {
// Bind the filename of the statement
oci_bind_by_name($stmt, ":filename", $entry);
// Execute the statement
if ( oci_execute($stmt) ) {
print "$entry added\n";
}
}
}
필요한 경우, CLOB을 조회한 것과 동일한 방법으로 오라클에서 BFILE을 읽어 들일 수 있습니다. 또는 아래와 같이 DBMS_LOB.FILEGETNAME 프로시저를 통해 파일 이름만을 확인한 후 파일시스템에서 직접 액세스하는 방법을 사용할 수도 있습니다:
// etc.
$sql = "SELECT
id
FROM
mylobs
WHERE
-- Select only BFILES which are not null
mybfile IS NOT NULL;
$stmt1 = oci_parse($conn, $sql);
oci_execute($stmt1)
or die ("Unable to execute query\n");
$sql = "DECLARE
locator BFILE;
diralias VARCHAR2(30);
filename VARCHAR2(30);
BEGIN
SELECT
mybfile INTO locator
FROM
mylobs
WHERE
id = :id;
-- Get the filename from the BFILE
DBMS_LOB.FILEGETNAME(locator, diralias, filename);
-- Assign OUT params to bind parameters
:diralias:=diralias;
:filename:=filename;
END;";
$stmt2 = oci_parse($conn, $sql);
while ( $row = oci_fetch_assoc ($stmt1) ) {
oci_bind_by_name($stmt2, ":id", $row['ID']);
oci_bind_by_name ($stmt2, ":diralias", $diralias,30);
oci_bind_by_name ($stmt2, ":filename", $filename,30);
oci_execute($stmt2);
print "{$row['ID']}: $diralias/$filename\n";
}
// etc.
?>
또, DBMS_LOB.FILEEXISTS 함수를 사용하면 운영체제에서 삭제되었음에도 불구하고 여전히 데이터베이스에서 참조되고 있는 파일이 무엇인지 확인할 수 있습니다.
결론
이번 연재에서는 Oracle Database 10g에서 사용되는 다양한 종류의 LOB에 대해 설명했습니다. 이제 데이터베이스에 대용량의 데이터를 효과적으로 저장하기 위해 각각의 LOB 타입이 어떻게 활용되는지 이해하셨을 것입니다. 또 PHP OCI8 API를 이용하여 LOB 데이터를 처리하는 방법과, Oracle/PHP 환경에서 자주 발생하는 개발 관련 이슈에 대해서 설명을 드렸습니다.
--------------------------------------------------------------------------------
Harry Fuecks [http://www.phppatterns.com]는 1999년 이후 PHP 개발자 및 기고가로써 활동해 왔습니다. Harry는 Sitepoint 웹 개발자 네트워크와 The PHP Anthology를 통해 PHP에 관련한 기술문서를 기고하고 있습니다.
여러분의 의견을 보내 주십시오
4,000 바이트 제한이 문제가 되십니까? LOB를 활용하십시오...
Downloads for this article:
Oracle Database 10g
Zend Core for Oracle
Apache HTTP Server 1.3 (및 이후 버전)
VARCHAR2와 같은 오라클 데이터 타입은 유용하게 활용됩니다. 하지만 4,000 바이트 이상의 데이터를 저장하려면 어떻게 해야 할까요? 바로 오라클이 지원하는 Long Object (LOB) 데이터 타입이 필요합니다. 또 LOB와 호환하는 PHP API의 사용법을 알고 있어야 합니다. 이에 필요한 지식을 갖고 있지 않은 개발자에게는 무척 까다로운 과제가 될 수 있습니다
이번 “Oracle+PHP Cookbook” 시리즈 연재에서는, LOB 데이터 타입과 PHP에서 LOB를 다루는 방법을 예제를 통해 알아보기로 합니다.
오라클의 Long Object
오라클은 다음과 같은 LOB 데이터 타입을 지원합니다:
BLOB - 바이너리 데이터의 저장에 사용
CLOB - 데이터베이스 문자 셋 인코딩을 이용한 문자 데이터 저장에 사용
NCLOB - 국가별 문자 셋을 이용한 유니코드 문자 데이터 저장에 사용. (본 문서에서 사용되는 PHP OCI8 익스텐션은 NCLOB을 지원하지 않음을 참고하십시오.)
BFILE - 운영체제 파일시스템에 저장된 외부 파일의 참조에 사용
그 밖에도 temporary LOB가 있습니다. temporary LOB는 BLOB, CLOB 또는 NCLOB 등의 형태를 취할 수 있으며, 해제되기 전까지 임시 테이블스페이스에 저장됩니다.
이전 버전의 오라클에서는 문자 및 바이너리 데이터의 저장을 위해 LONG, LONG RAW 타입을 지원했습니다. 이 두 가지 타입은 Oracle9i에서 LOB로 대체되었습니다.
LOB 데이터의 저장. Oracle Database 10g는 BLOB, CLOB, NCLOB 타입에 대해 단일 레코드 당 최대 128TB의 용량을 지원합니다 (실제 최대 용량은 데이터베이스 블록 사이즈와 LOB의 “chunk” 설정에 따라 달라질 수 있습니다.
LOB는 LOB 컨텐트와 LOB 로케이터(locator)의 두 가지 요소로 구성됩니다. LOB 로케이터는 LOB에 대한 “포인터”로 활용됩니다. LOB 로케이터는 LOB 데이터를 효과적으로 저장하고 관리하기 위한 목적에서 설계되었으며, PHP API의 INSERT, UPDATE, SELECT 에도 반영되어 있습니다 (아래 설명 참고)..
오라클은 LOB 레코드의 크기가 4KB를 넘지 않는 경우 LOB 컨텐트를 테이블 내부에 “인라인(in-line)” 형태로 저장합니다. 4KB를 초과하는 LOB는 테이블의 테이블스페이스 내에 아웃오브라인(out-of-line)” 형태로 저장됩니다. 이러한 방법으로 작은 크기의 LOB를 신속하게 조회하는 한편, 큰 LOB 때문에 테이블 스캔 성능이 저하되는 것을 방지할 수 있습니다.
LOB 저장 및 액세스를 위해 메모리 캐싱, 버퍼링과 같은 테크닉을 사용하여 애플리케이션의 성능을 개선할 수 있습니다. 자세한 정보는 오라클 제품문서의 LOB Performance Guidelines와 Oracle Database Application Developer's Guide - Large Objects를 참고하시기 바랍니다.
LOB 관련 제약사항. LOB 타입의 활용에 관련한 몇 가지 제약사항이 존재합니다. 특히 SQL 구문과 관련한 제약사항은 주의할 필요가 있습니다. 아래와 같은 쿼리에서는 LOB 타입을 사용할 수 없습니다.
SELECT DISTINCT
ORDER BY
GROUP BY
또 테이블 JOIN,UNION, INTERSECTION, MINUS 등의 구문에서도 LOB 타입 컬럼을 사용하는 것이 금지되어 있습니다.
그 밖에도 LOB를 프라이머리 키 컬럼으로 사용할 수 없는 등의 여러 가지 제약사항이 존재합니다. 자세한 정보는 Oracle Database Application Developer's Guide - Large Objects를 참고하십시오.
CLOB과 문자 셋
데이터베이스의 디폴트 문자 셋(character set)은 NLS_CHARACTERSET 매개변수를 통해 설정되며, CLOB 타입으로 저장된 텍스트는 이 문자 셋을 이용하여 인코딩 됩니다. 아래 SQL 구문을 이용하여 데이터베이스 문자 셋을 설정하시기 바랍니다:
SELECT value FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET'
현재로서는 PHP가 NCLOB을 지원하지 않으므로, 아래와 같은 방법으로 UTF-8과 같은 유니코드 인코딩을 기본 데이터베이스 문자 셋으로 설정하는 것을 고려해 볼 수 있습니다:
ALTER DATABASE CHARACTER SET UTF8
참고: 기존 데이터 또는 애플리케이션 코드가 다른 문자 셋을 사용하고 있는 경우, 변경 작업이 전체 환경에 미치는 영향을 미리 감안하여야 합니다. 자세한 정보는 Oracle Globalization Support Guide와 An Overview on Globalizing Oracle PHP Applications를 참고하시기 바랍니다.
LOB 데이터의 처리
본 문서에서는 PHP의 OCI8 익스텐션에 초점을 맞추어 설명합니다. 또 오라클에 포함된 DBMS_LOB 패키지는 PL/SQL을 이용한 LOB 처리를 위한 병렬 프로시저 및 함수를 제공하고 있음을 참고하시기 바랍니다.
PHP OCI8 익스텐션은 글로벌 PHP 네임스페이스에 “OCI-Lob”이라 불리는 PHP 클래스를 등록합니다. LOB 타입의 컬럼이 포함된 SELECT 구문이 실행된 경우, PHP는 이 구문을 OCI-Lob 오브젝트 인스턴스에 자동으로 바인딩합니다. OCI-Lob 오브젝트에 대한 참조가 확보되면 load(), save()등의 메소드를 이용하여 LOB 컨텐트를 조회, 수정할 수 있습니다.
사용 가능한 OCI-Lob 메소드는 PHP 버전에 따라 달라집니다. PHP5는 read(), seek(), 그리고 append()등의 메소드를 지원합니다. 이에 대한 PHP Manual 의 설명이 다소 불충분한 감이 있으므로, 지원되는 버전 넘버가 궁금한 경우 아래와 같은 스크립트를 사용해서 검증해 보시기 바랍니다.
foreach (get_class_methods('OCI-Lob') as $method ) {
print "OCI-Lob::$method()\n";
}
?>
PHP 5.0.5가 실행 중인 필자의 시스템에서는 아래와 같은 메소드 리스트가 출력되었습니다:
OCI-Lob::load()
OCI-Lob::tell()
OCI-Lob::truncate()
OCI-Lob::erase()
OCI-Lob::flush()
OCI-Lob::setbuffering()
OCI-Lob::getbuffering()
OCI-Lob::rewind()
OCI-Lob::read()
OCI-Lob::eof()
OCI-Lob::seek()
OCI-Lob::write()
OCI-Lob::append()
OCI-Lob::size()
OCI-Lob::writetofile()
OCI-Lob::writetemporary()
OCI-Lob::close()
OCI-Lob::save()
OCI-Lob::savefile()
OCI-Lob::free()
PHP 4.x OCI8 익스텐션은 전체 LOB의 일괄적인 읽기 및 쓰기만을 지원합니다. PHP5에서는 LOB의 일부 “청크(chung)”만을 읽거나 쓸 수 있으며 setBuffering(),getBuffering() 메소드를 이용한 LOB 버퍼링을 지원합니다. 또 PHP5는 스탠드얼론 함수로 oci_lob_is_equal(), oci_lob_copy()를 지원합니다.
본 문서에서 사용된 예제들은 새로운 PHP5 OCI 함수명을 사용하고 있습니다 (예: oci_parse 대신 OCIParse 사용)예제에서 공통적으로 사용되는 시퀀스와 테이블이 아래와 같습니다:
CREATE SEQUENCE mylobs_id_seq
NOMINVALUE
NOMAXVALUE
NOCYCLE
CACHE 20
NOORDER
INCREMENT BY 1;
CREATE TABLE mylobs (
id NUMBER PRIMARY KEY,
mylob CLOB
)
본 문서의 예제는 대부분 CLOB을 사용하고 있지만, 동일한 로직이 BLOB에도 적용 가능함을 참고하시기 바랍니다.
LOB의 INSERT
LOB의 INSERT 작업을 수행하려면, 먼저 오라클의 EMPTY_BLOB 또는 EMPTY_CLOB 함수를 이용하여 LOB를 초기화해야 합니다. NULL 값을 포함한 LOB는 업데이트할 수 없습니다.
초기화가 완료되면, 컬럼을 PHP OCI-Lob 오브젝트에 바인딩하고 오브젝트의 save() 메소드를 이용하여 LOB 컨텐트를 업데이트합니다.
아래 스크립트는 LOB 타입 데이터의 INSERT 쿼리 수행을 위한 예를 보여주고 있습니다:
// connect to DB etc...
$sql = "INSERT INTO
mylobs
(
id,
mylob
)
VALUES
(
mylobs_id_seq.NEXTVAL,
--Initialize as an empty CLOB
EMPTY_CLOB()
)
RETURNING
--Return the LOB locator
mylob INTO :mylob_loc";
$stmt = oci_parse($conn, $sql);
// Creates an "empty" OCI-Lob object to bind to the locator
$myLOB = oci_new_descriptor($conn, OCI_D_LOB);
// Bind the returned Oracle LOB locator to the PHP LOB object
oci_bind_by_name($stmt, ":mylob_loc", $myLOB, -1, OCI_B_CLOB);
// Execute the statement using , OCI_DEFAULT - as a transaction
oci_execute($stmt, OCI_DEFAULT)
or die ("Unable to execute query\n");
// Now save a value to the LOB
if ( !$myLOB->save('INSERT: '.date('H:i:s',time())) ) {
// On error, rollback the transaction
oci_rollback($conn);
} else {
// On success, commit the transaction
oci_commit($conn);
}
// Free resources
oci_free_statement($stmt);
$myLOB->free();
// disconnect from DB etc.
?>
위 예제에서 트랜잭션을 사용하고 있으며, oci_execute 의 실행과정에서 OCI_DEFAULT 상수를 통해 an oci_commit 또는 oci_rollback를 대기할 것을 지시하고 있습니다. 이는 INSERT 작업이 두 단계(로우의 생성, LOB의 업데이트)로 실행되기 때문입니다.
참고 위에서는 BLOB 타입이 사용되고 있으므로oci_bind_by_name 함수를 호출하는 작업만이 필요합니다:
oci_bind_by_name($stmt, ":mylob_loc", $myLOB, -1, OCI_B_BLOB);
마찬가지로, LOB 타입을 명시하지 않고 문자열을 바인딩할 수도 있습니다;
// etc.
$sql = "INSERT INTO
mylobs
(
id,
mylob
)
VALUES
(
mylobs_id_seq.NEXTVAL,
:string
)
";
$stmt = oci_parse($conn, $sql);
$string = 'INSERT: '.date('H:i:s',time());
oci_bind_by_name($stmt, ':string', $string);
oci_execute($stmt)
or die ("Unable to execute query\n");
// etc.
?>
위와 같은 방법을 사용하면 코드를 대폭적으로 단순화할 수 있습니다. 이러한 방식은 LOB에 기록되는 데이터의 크기가 비교적 작은 경우에 유용합니다. 큰 용량의 파일 컨텐트를 LOB에 스트리밍하고자 하는 경우에는, PHP LOB 오브젝트에 대해 write(), flush() 함수를 호출하고 (전체 파일을 하나의 인스턴스로 메모리에 보관하는 대신) 작은 청크(chunk) 단위로 루프를 돌려 컨텐트를 처리하는 것이 좋습니다.
LOB의 SELECT
LOB 컬럼을 포함하는 데이터를 SELECT 쿼리로 조회하는 경우, PHP는 해당 컬럼을 OCI-Lob 오브젝트에 자동으로 바인딩합니다. 그 예가 다음과 같습니다:
// etc.
$sql = "SELECT
*
FROM
mylobs
ORDER BY
Id
";
$stmt = oci_parse($conn, $sql);
oci_execute($stmt)
or die ("Unable to execute query\n");
while ( $row = oci_fetch_assoc($stmt) ) {
print "ID: {$row['ID']}, ";
// Call the load() method to get the contents of the LOB
print $row['MYLOB']->load()."\n";
}
// etc.
?>
OCI_RETURN_LOBS 상수와 oci_fetch_array()를 함께 사용하여 LOB 오브젝트를 그 값으로 대치하도록 함으로써 작업을 한층 단순화할 수 있습니다:
while ( $row = oci_fetch_array($stmt, OCI_ASSOC+OCI_RETURN_LOBS) ) {
print "ID: {$row['ID']}, {$row['MYLOB']}\n";
}
LOB의 UPDATE
LOB를 업데이트하는 경우, 위의 INSERT 구문 예와 마찬가지로 "RETURNING" 커맨드를 사용할 수도 있습니다. 하지만 “SELECT ... FOR UPDATE” 구문을 사용하는 편이 훨씬 간단합니다:
// etc.
$sql = "SELECT
mylob
FROM
mylobs
WHERE
id = 3
FOR UPDATE /* locks the row */
";
$stmt = oci_parse($conn, $sql);
// Execute the statement using OCI_DEFAULT (begin a transaction)
oci_execute($stmt, OCI_DEFAULT)
or die ("Unable to execute query\n");
// Fetch the SELECTed row
if ( FALSE === ($row = oci_fetch_assoc($stmt) ) ) {
oci_rollback($conn);
die ("Unable to fetch row\n");
}
// Discard the existing LOB contents
if ( !$row['MYLOB']->truncate() ) {
oci_rollback($conn);
die ("Failed to truncate LOB\n");
}
// Now save a value to the LOB
if ( !$row['MYLOB']->save('UPDATE: '.date('H:i:s',time()) ) ) {
// On error, rollback the transaction
oci_rollback($conn);
} else {
// On success, commit the transaction
oci_commit($conn);
}
// Free resources
oci_free_statement($stmt);
$row['MYLOB']->free();
// etc.
?>
INSERT의 경우와 마찬가지로, 위 코드는 UPDATE 수행을 위해 트랜잭션을 이용하고 있습니다. 특히 주목해야 할 부분이 truncate()함수를 호출하는 부분입니다. save()를 이용해서 LOB를 업데이트하는 경우, save() 함수는 LOB 컨텐트의 시작 부분에서부터 새로운 데이터의 길이에 해당되는 부분까지만을 업데이트합니다. 따라서 이전의 컨텐트가 여전히 LOB에 남아 있을 수도 있습니다.
PHP 4.x, truncate()함수가 지원되지 않습니다. 따라서 새로운 데이터를 업데이트하기 전에 LOB의 기존 컨텐트를 지우려면 오라클의 EMPTY_CLOB() 함수를 사용해야 합니다.
$sql = "UPDATE
mylobs
SET
mylob = EMPTY_CLOB()
WHERE
id = 2403
RETURNING
mylob INTO :mylob
";
$stmt = OCIParse($conn, $sql);
$mylob = OCINewDescriptor($conn,OCI_D_LOB);
OCIBindByName($stmt,':mylob',$mylob, -1, OCI_B_CLOB);
// Execute the statement using OCI_DEFAULT (begin a transaction)
OCIExecute($stmt, OCI_DEFAULT)
or die ("Unable to execute query\n");
if ( !$mylob->save( 'UPDATE: '.date('H:i:s',time()) ) ) {
OCIRollback($conn);
die("Unable to update lob\n");
}
OCICommit($conn);
$mylob->free();
OCIFreeStatement($stmt);
BFILE에 대한 작업 수행
BFILE 타입에 대해 INSERT 또는 UPDATE를 수행하는 경우, 오라클은 (웹 서버가 아닌) 데이터베이스 서버 운영체제의 파일시스템에서 저장된 파일의 위치를 지정하는 작업을 수행하게 됩니다. 또 SELECT 구문을 이용하여 BILE의 컨텐트를 읽어 들이거나, 필요한 경우 DBMS_LOB 패키지의 함수와 프로시저를 호출하여 파일에 대한 정보만을 확인할 수도 있습니다.
BFILE은 파일시스템에 저장된 파일을 직접 액세스하는 한편으로 SQL 구문을 이용하여 파일의 위치를 확인할 수 있게 한다는 장점을 제공합니다. 예를 들어 웹 서버에서 이미지를 직접 제공하면서, BFILE을 포함한 테이블과 다른 테이블과의 관계 정보를 추적하는 것이 가능합니다 (한 예로, 어떤 사용자가 어떤 파일을 업로드했는지 확인할 수 있습니다).
위에서 사용된 테이블 스키마를 먼저 업데이트해 보겠습니다;
ALTER TABLE mylobs ADD( mybfile BFILE )
그런 다음, 오라클에 디렉토리 앨리어스(alias)를 등록하고(이 과정에서 관리자 권한이 필요합니다), 테이블에 대한 읽기 권한을 할당합니다:
CREATE DIRECTORY IMAGES_DIR AS '/home/harryf/public_html/images'
GRANT READ ON DIRECTORY IMAGES_DIR TO scott
이제 아래와 같이 BFILE의 INSERT 작업을 수행할 수 있습니다:
// etc.
// Build an INSERT for the BFILE names
$sql = "INSERT INTO
mylobs
(
id,
mybfile
)
VALUES
(
mylobs_id_seq.NEXTVAL,
/*
Pass the file name using the Oracle directory reference
I created called IMAGES_DIR
*/
BFILENAME('IMAGES_DIR',:filename)
)";
$stmt = oci_parse($conn, $sql);
// Open the directory
$dir = '/home/harryf/public_html/images';
$dh = opendir($dir)
or die("Unable to open $dir");
// Loop through the contents of the directory
while (false !== ( $entry = readdir($dh) ) ) {
// Match only files with the extension .jpg, .gif or .png
if ( is_file($dir.'/'.$entry) && preg_match('/\.(jpg|gif|png)$/',$entry) ) {
// Bind the filename of the statement
oci_bind_by_name($stmt, ":filename", $entry);
// Execute the statement
if ( oci_execute($stmt) ) {
print "$entry added\n";
}
}
}
필요한 경우, CLOB을 조회한 것과 동일한 방법으로 오라클에서 BFILE을 읽어 들일 수 있습니다. 또는 아래와 같이 DBMS_LOB.FILEGETNAME 프로시저를 통해 파일 이름만을 확인한 후 파일시스템에서 직접 액세스하는 방법을 사용할 수도 있습니다:
// etc.
$sql = "SELECT
id
FROM
mylobs
WHERE
-- Select only BFILES which are not null
mybfile IS NOT NULL;
$stmt1 = oci_parse($conn, $sql);
oci_execute($stmt1)
or die ("Unable to execute query\n");
$sql = "DECLARE
locator BFILE;
diralias VARCHAR2(30);
filename VARCHAR2(30);
BEGIN
SELECT
mybfile INTO locator
FROM
mylobs
WHERE
id = :id;
-- Get the filename from the BFILE
DBMS_LOB.FILEGETNAME(locator, diralias, filename);
-- Assign OUT params to bind parameters
:diralias:=diralias;
:filename:=filename;
END;";
$stmt2 = oci_parse($conn, $sql);
while ( $row = oci_fetch_assoc ($stmt1) ) {
oci_bind_by_name($stmt2, ":id", $row['ID']);
oci_bind_by_name ($stmt2, ":diralias", $diralias,30);
oci_bind_by_name ($stmt2, ":filename", $filename,30);
oci_execute($stmt2);
print "{$row['ID']}: $diralias/$filename\n";
}
// etc.
?>
또, DBMS_LOB.FILEEXISTS 함수를 사용하면 운영체제에서 삭제되었음에도 불구하고 여전히 데이터베이스에서 참조되고 있는 파일이 무엇인지 확인할 수 있습니다.
결론
이번 연재에서는 Oracle Database 10g에서 사용되는 다양한 종류의 LOB에 대해 설명했습니다. 이제 데이터베이스에 대용량의 데이터를 효과적으로 저장하기 위해 각각의 LOB 타입이 어떻게 활용되는지 이해하셨을 것입니다. 또 PHP OCI8 API를 이용하여 LOB 데이터를 처리하는 방법과, Oracle/PHP 환경에서 자주 발생하는 개발 관련 이슈에 대해서 설명을 드렸습니다.
--------------------------------------------------------------------------------
Harry Fuecks [http://www.phppatterns.com]는 1999년 이후 PHP 개발자 및 기고가로써 활동해 왔습니다. Harry는 Sitepoint 웹 개발자 네트워크와 The PHP Anthology를 통해 PHP에 관련한 기술문서를 기고하고 있습니다.
여러분의 의견을 보내 주십시오
'Web Dev > PHP' 카테고리의 다른 글
| 오픈소스 프로젝트에 참여하고 싶어요 (11) | 2006/10/23 |
|---|---|
| 젠드 프레임워크 0.1.3 (0) | 2006/04/19 |
| The Oracle + PHP Cookbook :: Oracle/PHP 환경의 LOB 처리 (0) | 2006/04/06 |
| PHP를 Eclipse로 개발하기(PHPEclipse) - 2. 설정편 (7) | 2006/03/21 |
| Linux 및 Windows 환경을 위한 PHP, Oracle 10g Instant Client 설치 (1) | 2006/03/13 |
| PHP를 Eclipse로 개발하기(PHPEclipse) - 1. 설치편 (7) | 2006/02/24 |
2006/03/13 19:59
Linux 및 Windows 환경을 위한 PHP, Oracle 10g Instant Client 설치
2006/03/13 19:59 in Web Dev/PHP

아.. 오랫만에 오라클 설정잡아주느라 애썼다;
출처 : http://www.oracle.com/technology/global/kr/pub/notes/technote_php_instant.html
출처 : http://www.oracle.com/technology/global/kr/pub/notes/technote_php_instant.html
Linux 및 Windows 환경을 위한 PHP, Oracle 10g Instant Client 설치
PHP 5.1.2 업데이트
저자: Christopher Jones, Consulting Technical Staff, Oracle Corporation
업데이트일: 2006년 1월
Oracle 10g Instant Client (무료로 다운로드가 가능합니다)는 PHP를 원격 오라클 데이터베이스에 연결하기 위한 가장 손쉬운 방법으로, 3 개의 라이브러리만 설치하면 사용이 가능합니다.
PHP에서 Oracle API의 액세스에 사용되는 Instant Client 라이브러리는 OCI8이라는 이름으로 불립니다 (이 이름은 Oracle8 버전에서 최초로 개발되면서 붙여진 것입니다). PHP Oracle 8 함수 를 이용하여 Oracle 8.1.7, 9.x, 10.x를 직접 호출할 수 있고, 또는 PEAR MDB2와 ADOdb 등의 추상화 클래스(abstraction class)를 함께 이용할 수도 있습니다.
구 버전의 PHP “oracle” 익스텐션을 Instant Client에서 이용하는 것도 가능하지만, 이 경우에는 (오라클에 의해 더 이상 지원되지 않는) 구 버전의 Oracle API가 사용됩니다. 이러한 이유로, PHP 커뮤니티와 오라클은 새로운 개발작업에서 이 익스텐션을 사용하는 것을 권장하지 않고 있습니다.
Apache 환경에서 PHP 4 또는 PHP 5와 함께 Instant Client를 사용하기 위한 방법이 아래에 설명되어 있습니다. (5.1.2와 OCI8 익스텐션에 관련한 상세한 정보는 이 섹션을 참고하시기 바랍니다.) Instant Client에는 오라클 데이터베이스가 포함되어 있지 않으므로, 별도의 데이터베이스 환경이 미리 구축되어 있어야 합니다.
일반적으로 데이터베이스는 다른 머신에서 운영됩니다. 데이터베이스가 로컬에 설치된 경우에는, 필요한 오라클 컴포넌트가 이미 사용 가능하므로, Instant Client를 따로 설치할 필요가 없습니다.
소프트웨어 요구사항: 소프트웨어 참고
Oracle Instant Client "Instant Client Package - Basic"을 다운로드합니다. Linux 환경의 경우, "Instant Client Package - SDK"를 함께 다운로드합니다.
Apache HTTPD Server PHP 커뮤니티는 Apache 1.3을 권장하고 있습니다.
PHP - PHP Hypertext Processor Version 4.3 또는 이후 버전
Windows 환경에서 PHP OCI8 Extension 활성화하기
Instant Client 바이너리는 PHP에 포함된 Windows용 바이너리를 보완하는 역할을 담당하게 됩니다.
(installer build가 아닌) PHP 바이너리 zip 파일과 Apache를 다운로드합니다. PHP 매뉴얼의 Windows 시스템 설치 가이드를 참고하여 설치 작업을 수행합니다. OTN의 오픈 소스 개발자 센터에서 "Oracle, PHP, and Apache on Windows 2000/XP 환경의 Oracle, PHP, Apache 설치,"와 같은 유용한 기술정보를 참고하실 수 있습니다. 이 문서에는 Oracle 10g를 전체 설치하는 방법이 설명되어 있습니다 (Instant Client 설치 시에는 이 작업이 필요하지 않습니다).
다음 단계로 진행하기 전에 PHP가 제대로 동작하는지 확인합니다. 이 단계에서는 오라클 연동 기능은 지원되지 않습니다.
OTN의 Instant Client 페이지에서 Instant Client Basic package for Windows를 다운로드합니다. zip 파일의 사이즈는 대략 30 MB 정도 됩니다.
서브디렉토리(예: c:\instantclient10_1)를 생성하고 zip 파일로부터 아래 라이브러리를 복사합니다:
oraociei10.dll
orannzsbb10.dll
oci.dll
3개 파일의 총 용량은 약 80MB 정도 됩니다.
PHP의 구 버전 oracle extension을 사용하려는 경우, oci.dll 대신 ociw32.dll을 복사합니다 (이 익스텐션을 활성화하려면 php.ini 파일에서 "extension=php_oracle.dll"로 설정합니다.)
PATH 환경변수를 편집하여 “c:\instantclient10_1”가 다른 오라클 디렉토리의 앞에 위치하게 합니다.
Windows 2000의 경우, 시작 -> 설정 -> 제어판 -> 시스템 -> 고급 -> 환경 변수로 이동하여 시스템 변수 목록의 PATH를 편집합니다.
tnsnames.ora 파일을 이용하여 Oracle Net 서비스 네임을 정의한 경우, tnsnames.ora를 c:\instantclient10_1로 복사하고 사용자 환경변수 TNS_ADMIN을 c:\instantclient10_1로 복사합니다. 디폴트 서비스 네임은 사용자 환경변수 LOCAL에 추가로 설정할 수 있습니다.
NLS_LANG과 같은 Oracle globalization language 환경변수를 필요에 따라 설정합니다. 아무것도 설정되지 않은 경우, 디폴트 로컬 환경이 그대로 사용됩니다. 자세한 정보는 An Overview on Globalizing Oracle PHP Applications 문서를 참고하십시오.
Unset unnecessary Oracle variables such as ORACLE_HOME and ORACLE_SID.
php.ini를 편집하고 OCI8 익스텐션의 커멘트 기호를 삭제합니다:
extension=php_oci8.dll
extension_dir 항목에 PHP 익스텐션 DLL의 전체 경로를 설정합니다. PHP4의 경우 DLL은 PHP 소프트웨어의 “extensions” 서브디렉토리에 위치합니다. PHP5에서는 “ext” 서브디렉토리에 위치합니다.
Apache를 재시작합니다.
익스텐션이 설정되었음을 확인했다면, 웹 서버에서 접근할 수 있는 위치에 간단한 PHP 스크립트(phpinfo.php)를 생성합니다.
phpinfo();
?>
"http://" URL을 사용하여 브라우저에서 스크립트를 로드합니다. 브라우저 페이지의 "oci8" 섹션에서 "OCI8 Support enabled"라는 문구를 확인할 수 있어야 합니다.
Linux에서 PHP OCI8 Extension 활성화하기
Linux 환경에서 오라클 연결을 추가하려면, 먼저 PHP를 다시 컴파일해야 합니다.
OTN의 오픈 소스 개발자 센터에서 " Linux에서 Oracle, PHP, Apache 설치하기",와 같은 유용한 기술정보를 참고하실 수 있습니다. 이 문서에는 Oracle 10g를 전체 설치하는 방법이 설명되어 있습니다 (Instant Client 설치 시에는 이 작업이 필요하지 않습니다.
Apache를 다운로드하여 설치합니다. 사용자의 홈 디렉토리에 Apache를 설치하는 예가 아래와 같습니다:
cd apache_1.3.31
./configure --enable-module=so --prefix=$HOME/apache --with-port=8888
make
make install
$HOME/apache/conf/httpd.con을 편집하여 아래 항목을 추가합니다:
AddType application/x-httpd-php .php
AddType application/x-httpd-php-source .phps
PHP를 다운로드하여 tar 압축을 풉니다.
OTN의 Instant Client 페이지에서 Basic/SDK Instant Client Package를 다운로드합니다. 두 RPM의 전체 크기는 약 30MB 가량 됩니다.
root 사용자로 RPM을 설치합니다.
rpm -Uvh oracle-instantclient-basic-10.1.0.3-1.i386.rpm
rpm -Uvh oracle-instantclient-devel-10.1.0.3-1.i386.rpm
첫 번째 RPM은 오라클 라이브러리를 /usr/lib/oracle/10.1.0.3/client/lib에 설치하고, 두 번째 RPM은 /usr/include/oracle/10.1.0.3/client에 헤더를 생성합니다.
백업 후 이 패치를 PHP의 ext/oci8/config.m4에 적용합니다. 패치 라인 넘버는 PHP 4.3.9를 기준으로 하고 있습니다. (PHP 4.3.11와 5.0.4의 경우처럼) PHP bug 31084가 해결되었다면 이 패치를 적용할 필요는 없습니다.
PHP 4.3.9 또는 4.3.10을 사용하는 경우에는 이 패치를 파일(예: php_oci8ic_buildpatch)에 저장한 후 아래와 같이 설치합니다:
patch -u config.m4 php_oci8ic_buildpatch
위 패치를 실행하면 새로운 PHP 설정 매개변수 --with-oci8-instant-client[=DIR]이 생성됩니다. Linux 운영체제는 기본적으로 RPM을 통해 설치된 Instant Client의 최신 버전을 사용합니다. 오라클 라이브러리의 경로를 지정함으로써 다른 버전을 사용하도록 할 수도 있습니다. 어떤 경우든 버전에 맞는 SDK 헤더가 자동적으로 적용됩니다.
새로운 매개변수는 기존의 --with-oci8 매개변수를 대체합니다.
참고: Linux 이외의 운영체제에서는, 사용자가 선택한 디렉토리에 Instant Client 패키지의 압축을 풀게 됩니다. --with-oci8-instant-client 매개변수를 사용하려면 이 디렉토리를 명시적으로 지정해 주어야 합니다 (예: --with-oci8-instant-client=/home/instantclient10_1) Instant Client SDK는 Basic Package와 같은 디렉토리에 압축을 풀어야만, 재구성된 configure 스크립트에서 헤더 파일의 서브디렉토리를 정확하게 찾을 수 있습니다.
“configure” 스크립트를 최상위 PHP 디렉토리에서 rebuild 합니다.
cd php-4.3.9
rm -rf autom4te.cache config.cache
./buildconf --force
새로운 옵션을 이용하여 configure를 실행합니다. 홈 디렉토리에 Apache를 설치한 환경에서의 실행 예가 아래와 같습니다.
./configure \
--with-oci8-instant-client \
--prefix=$HOME/php --with-apxs=$HOME/apache/bin/apxs \
--enable-sigchild --with-config-file-path=$HOME/apache/conf
PHP를 rebuild합니다.
make
make install
php.ini 파일을 --with-config-file-path에서 지정된 위치에 복사합니다.
cp php.ini-recommended $HOME/apache/conf/php.ini
LD_LIBRARY_PATH를 /usr/lib/oracle/10.1.0.3/client/lib으로 설정하고 Apache를 재시작합니다.
tnsnames.ora 파일을 이용하여 Oracle Net 서비스 네임을 정의한 경우, TNS_ADMIN을 파일이 위치한 디렉토리로 설정해 줍니다.
Apache를 시작하기 전에 모든 오라클 환경 변수를 설정해 주는 것이 중요합니다. 스크립트의 예가 아래와 같습니다:
#!/bin/sh
APACHEHOME=/home/apache
LD_LIBRARY_PATH=/usr/lib/oracle/10.1.0.3/client/lib:${LD_LIBRARY_PATH}
TNS_ADMIN=/home
export LD_LIBRARY_PATH TNS_ADMIN
echo Starting Apache
$APACHEHOME/apachectl start
익스텐션의 설정을 확인한 후, 웹 서버가 접근할 수 있는 위치에 간단한 PHP 스크립트(phpinfo.php)를 생성합니다.
phpinfo();
?>
URL("http://localhost:8888//phpinfo.php”)을 사용하여 스크립트를 브라우저에 로드합니다. 브라우저 페이지의 "oci8" 섹션에서 "OCI8 Support enabled"라는 문구를 확인할 수 있어야 합니다.
오라클로의 연결
오라클 연결을 생성하려면 먼저 오라클 연결 정보를 OCILogon()에 전달해 주어야 합니다. Instant Client와 관련된 모든 툴은 데이터베이스 서버의 “원격”에 위치하며, 따라서 사용자 이름, 암호와 함께 Oracle Net connection identifier가 사용되어야 합니다. 기존에 구성된 오라클 데이터베이스에 대한 연결 정보는 외부에서 “well-known” 정보로 확인이 가능합니다. 새로 구성된 시스템에서는 데이터베이스 셋업 과정에서 오라클 설치 프로그램에 의해 설정됩니다. 인스톨러는 Oracle Net 설정 및 서비스 네임 생성 작업을 수행합니다.
새로운 데이터베이스에서 HR와 같은 샘플 스키마를 사용하려면 unlock 작업을 수행하고 암호를 입력해야 할 수 있습니다. 이를 위해 SQL*Plus에서 SYSTEM 사용자로 연결한 뒤 아래와 같이 실행해 줍니다:
ALTER USER username IDENTIFIED BY new_password ACCOUNT UNLOCK;
연결 정보를 PHP에 전달하는 방법에는 여러 가지가 있습니다. 먼저 Oracle 10g의 Easy Connect 신택스를 이용하여 mymachine에서 실행 중인 the MYDB 데이터베이스 서비스의 HR 스키마에 연결하는 방법이 아래와 같습니다. 이 경우 tnsnames.ora 또는 다른 Oracle Network 파일이 필요하지 않습니다:
$c = OCILogon('hr', 'hr_password', '//mymachine.mydomain/MYDB');
Easy Connect 신택스 관련 오라클 문서(Using the Easy Connect Naming Method)를 참고하시기 바랍니다.
또는, /home/tnsnames.ora에 다음과 같은 내용이 포함된 경우:
MYDB =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = mymachine.mydomain)(PORT = 1521))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = MYDB)
)
)
그리고 TNS_ADMIN 환경변수가 (Apache 시작 이전에) /home으로 설정된 경우, 연결 문자열을 아래와 같이 설정할 수 있습니다:
$c = OCILogon('hr', 'hr_password', 'MYDB');
환경변수 LOCAL(Windows) 또는 TWO_TASK(Linux)가 MYDB로 설정된 경우, MYDB로의 연결을 위한 문자열이 다음과 같습니다:
$c = OCILogon('hr', 'hr_password');
Oracle의 사용
기본적인 연결에 성공했다면, 간단한 스크립트(testoci.php)를 테스트해 볼 차례입니다. 사용자의 데이터베이스 환경에 맞게 연결 설정을 수정한 후 브라우저에서 스크립트를 로드합니다. HR 사용자가 소유한 모든 테이블의 목록을 표시하는 스크립트 예가 아래와 같습니다:
$conn = OCILogon("hr", "hr_password", '//mymachine.mydomain:port/MYDB);
$query = 'select table_name from user_tables';
$stid = OCIParse($conn, $query);
OCIExecute($stid, OCI_DEFAULT);
while ($succ = OCIFetchInto($stid, $row)) {
foreach ($row as $item) {
echo $item." ";
}
echo "
\n";
}
OCILogoff($conn);
?>
트러블슈팅
Oracle PHP Troubleshooting FAQ에서 오라클 연결에 관련한 유용한 정보를 확인하실 수 있습니다.
오라클의 SQL*Plus 커맨드라인 툴을 Instant Client 페이지에서 다운로드하여 환경 및 연결 문제를 해결하는데 이용할 수 있습니다. SQL*Plus Instant Client Release Notes를 참고하시기 바랍니다.
SQL*Plus를 이용하여 환경을 점검하는 방법은 phpino.php 스크립트의 예와 동일합니다.
Windows 도움말
phpinfo.php 스크립트를 실행했을 때 “oci8” 섹션에서 "OCI8 Support enabled" 메시지를 확인할 수 없다면, php.ini 파일에서 "extension=php_oci8.dll" 항목의 주석 처리가 해제되었는지 점검합니다.
PATH가 잘못 설정되어 있거나 오라클 라이브러리를 찾을 수 없는 경우, Apache 시작 시 다음과 같은 경고 메시지가 표시됩니다: "The dynamic link library OCI.dll could not be found in the specified path." phpinfo() 페이지의 Environment 섹션에서, PHP에 의해 실제로 사용되는 PATH와 Oracle 변수의 값을 확인할 수 있습니다.
php.ini에 extension_dir 매개변수가 올바르게 설정되지 않은 경우 Apache 시작 시 다음과 같은 경고 메시지가 표시됩니다: "PHP Startup: Unable to load dynamic library php_oci8.dll."
Linux 도움말
config.m4가 올바르게 패치되었는지 확인합니다. “configure”의 실행에 실패한 경우, config.log 파일을 점검합니다. config.m4를 이전 상태로 되돌리고 캐시 파일을 제거한 후 “./buildconf --force and configure”를 실행한 다음, 발생한 문제가 패치 작업과 관련된 것인지 확인합니다.
“configure”의 타임스탬프가 현재 시점으로 설정되어 있는지 확인합니다. 캐시 파일을 모두 제거하고 필요한 경우 rebuild 작업을 수행합니다.
Apache를 시작하는 쉘에서 필요한 모든 Oracle 환경변수를 설정해 줍니다.
PHP 5.1.2 및 이후 버전
“re-factored” OCI8 익스텐션은 Instant Client를 위한 새로운 신택스를 지원합니다. re-factored 익스텐션은 PHP 5.1.2에 처음으로 포함되었으며, 이전 버전의 PHP의 경우pecl.php.net/package/oci8, pecl4win.php.net/ext.php/php_oci8.dll에서 다운로드할 수 있습니다.
Technical Note에 명시된 대로 Instant Client RPM을 설치했다면, PHP를 아래와 같이 설정해 줍니다:
./configure \
--with-oci8=instantclient,/usr/lib/oracle/10.1.0.3/client/lib \
--prefix=$HOME/php --with-apxs=$HOME/apache/bin/a
pxs \
--enable-sigchild --with-config-file-path=$HOME/apache/conf
Instant Client Basic, SDK zip 파일을 사용하는 경우, 압축을 푼 디렉토리의 --with-oci8 옵션을 변경해 줍니다. 그 예가 아래와 같습니다:
--with-oci8=instantclient,$HOME/instantclient10_1
re-factored 버전이 올바르게 설치되었는지 점검하려면 phpinfo() 실행 결과를 확인하면 됩니다. “oci8.” 접두어로 시작되는 7가지 매개변수를 확인할 수 있을 것입니다 (이전 버전에서는 이 매개변수가 존재하지 않습니다).
결론
이 문서가 여러분에게 도움이 되었기를 바랍니다. 질문이나 의견은 OTN Instant Client 또는 PHP 포럼에 올려 주시기 바랍니다.
'Web Dev > PHP' 카테고리의 다른 글
| The Oracle + PHP Cookbook :: Oracle/PHP 환경의 LOB 처리 (0) | 2006/04/06 |
|---|---|
| PHP를 Eclipse로 개발하기(PHPEclipse) - 2. 설정편 (7) | 2006/03/21 |
| Linux 및 Windows 환경을 위한 PHP, Oracle 10g Instant Client 설치 (1) | 2006/03/13 |
| PHP를 Eclipse로 개발하기(PHPEclipse) - 1. 설치편 (7) | 2006/02/24 |
| Upcoming changes in PHP 6.0 (0) | 2006/01/23 |
| AJAX-based site builder is built with PHP (0) | 2006/01/23 |
Prev

Rss Feed