PL/pgSQL 는 Postgres 데이터베이스를 위한 적재가능한 절차형 언어입니다.

24.1. 개요

PL/pgSQL의 디자인의 목표는 다음을 위한 function과 trigger procedure를 생성하고 사용할 수 있는 적재가능한 절차형 언어를 만드는데 있다.
function과 trigger procedure 생성
SQL 언어에 제어 구조의 추가
복잡한 계산의 수행
모든 사용자 정의형, 함수, 연산자의 계승
서버에 의해 신뢰받을 수 있는 정의
사용의 용이

PL/pgSQL 호출 처리기(call handler)는 함수의 소스 코드를 분석하고 그 함수가 처음 호출될 때 내부 바이트코드를 생성한다. 생성된 바이트코드는 함수의 오브젝트 ID를 이용해 호출 처리기에 의해서 각각 구분된다. 이 같은 구조는 DROP/CREATE sequence에 의해 함수의 변경이 이루어질 때 데이터베이스에 대한 새로운 연결 요구없이 이루어지도록 한다.

함수에서 사용된 모든 표현과 SQL 문장에 대해, PL/pgSQL 바이트코드 해석기는 SPI manager의 SPI_prepare() 와 SPI_saveplan() 함수를 이용해서 준비된 실행계획(prepared execution plan) 을 생성한다. 이러한 과정은 PL/pgSQL 함수 안에 각각의 함수의 문장이 최초로 처리될 때 이루어진다. 그래서, 함수는(필요한 실행계획을 포함하는 조건 코드를 가지고 있음) 데이터베이스의 연결이 되어있는 동안만 실제 사용될 실행계획들을 준비하고 저장한다.

그러니까 사용자는 사용자 정의 함수를 만들 때 위의 사항을 유념해야한다.
예를 들면

CREATE FUNCTION populate() RETURNS INTEGER AS '
DECLARE
-- Declarations
BEGIN
PERFORM my_function();
END;
' LANGUAGE 'plpgsql';


만약에 사용자가 위의 함수를 만들었다면, 서버는 그 바이트 코드에 my_function()에 대한 OID를 참조할 것입니다. 후에 사용자가 my_function()을 지우고 재생성하였다면, populate()는 my_function()를 더이상 찾지 못하게 된다. 그러니까 my_function()을 지우고 재생성한 후 populate()도 재생성 해야 한다는 뜻이다.

PL/pgSQL이 실행 계획을 이와같이 처리하기 때문에, PL/pgSQL내에 직접적으로 사용되는 쿼리는 매 실행마다 같은 테이블과 필드를 참조해야만 한다. 이 말은 사용자는 함수의 파라메터를 쿼리에서 사용할 테이블 또는 필드명으로 사용할 수 없다는 것이다. 이 제한을 피하기 위해서 사용자는 PL/pgSQL의 EXECUTE 문을 이용하여 동적 쿼리 (dynamic query)를 사용할 수 있다. -- 그렇지만 매 수행시마다 새로운 쿼리 계획을 만드는 댓가를......

사용자 정의형에 대한 입출력 변환과 계산 함수를 제외하고, C언어 함수에 정의될 수 있는 어떤 형이라도 PL/pgSQL과 함께 사용될 수 있다. 복잡한 조건 계산 함수와 나중에 그 형을 이용하여 연산자를 정의하거나 기능형 index(functional index)에 그 형을 사용할 수 있다.
24.1.1. PL/pgSQL 사용의 잇점
더 나은 성능
SQL 지원
이동가능성 또는 이식성 (Portability)
24.1.1.1. 더 나은 성능

SQL은 PostgreSQL (그리고 거의 모든 관계형 데이터베이스)에서 질의 언어(query language)로 사용하는 언어이다. 이 언어는 옮기는 것이 가능하고 배우기 쉽다. 그렇지만 모든 SQL문은 반드시 각각 데이터베이스에 의해 수행되야만 한다.

그 말은 사용자의 클라이언트는 매 쿼리를 데이터베이스 서버로 전송해야 한다는 말이다.
그래서 그것이 처리되는 동안 기다렸다가 결과를 수신하고, 결과를 토대로 좀 계산을 한다음에 서버로 또다른 쿼리를 보낸다. 이러한 모든 과정은 내부 프로세스 통신을 유발하고 또한 사용자 클라이언트가 데이터베이스 서버와 다른 장비에 있다면 네트워크의 부담을 줄 수 있다.

PL/pgSQL로 사용자는 복수의 계산과 데이터베이스 내의 쿼리들을 모음으로써, 그러니까 절차형 언어의 강력함과 SQL의 사용 용이성과 함께 클라이언트/서버의 통신 부담을 유발하지 않으므로 처리 시간을 단축한다. 그렇기 때문에 PL/pgSQL을 이용하여 사용자는 눈에 띄는 성능 향상을 가져 올 수 있다.
24.1.1.2. SQL 지원

PL/pgSQL은 SQL의 유연성과 용이성에 더해 절차형 언어의 강력함도 가지고 있다. PL/pgSQL로 사용자는 SQL의 모든 자료형(datatype), Column, 연산자와 함수를 사용할 수 있다.
24.1.1.3. 이동가능성 또는 이식성?? (Portability)

PL/pgSQL 함수는 PostgreSQL에서 수행되기 때문에, 이 함수들은 PostgreSQL이 있는 다른 어떤 환경에서도 수행된다. 그렇게 해서 사용자는 코드를 재사용할 수 있고 개발비를 절감할 수 있다.
24.1.2. PL/pgSQL 에서 개발

PL/pgSQL에서 개발은 상당히 직선적이고, 특히 사용자가 Oracle의 PL/SQL과 같은 다른 데이터베이스의 절차 언어를 개발해본 경험이 있다면 PL/pgSQL에서 개발을 위한 2가지의 좋은 방안이 있다. 그 것은...
텍스트 편집기를 이용한 화일을 psql을 통해 적재하는 방식
PostgreSQL의 GUI 도구인 pgaccess을 사용하기

첫번째 경우는 사용이 간단한 사용자가 원하는 텍스트 편집기를 사용하여 사용자 함수를 만들고 다른 콘솔에서 psql을 이용하여 그 함수들을 적재하는 방식이다. 만일 사용자가 이 방식 을 사용한다면 (또는 사용자가 초보 또는 디버그 중 이면) 항상 함수를 데이터베이스로 적재하기 전에 이미 데이버베이스 안에 있는 기존 함수를 제거해야한다. 예를 들면

drop function testfunc(integer);
create function testfunc(integer) return integer as '
....
end;
' language 'plpgsql';


사용자가 처음 위의 함수가 있는 화일을 적재할 때, PostgreSQL은 이 함수가 없다고 경고 메세지를 보여주고나서 그 함수를 생성할 것이다. SQL 화일 (filename.sql)을 "dbname"이라는 데이터베이스로 적재할 경우 다음의 명령을 사용한다.

psql -f filename.sql dbname

두번째의 경우는 PostgreSQL의 GUI 도구인 pgaccess를 사용하는 것이다. 이것은 escaping single-quote 와 같이 편리한 점이 있고, 재생성과 함수 디버그에 편리한 장점이 있다.
24.2 세부 사항
24.2.1. PL/pgSQL 구조

PL/pgSQL는 블록 구조의 언어다. 모은 키워드나 식별자는 대.소문자를 섞어 사용할 수 있다. 한 블록은 다음과 같이 정의한다.

[<<label>>]
[DECLARE
변수 선언들 ]
BEGIN
실행할 문장
END;

블록의 '실행할 문장' 부분에는 얼마든지 하위-블록이 올 수 있다. 하위-블록은 외부 블록의 문장으로부터 변수를 숨기는데 사용될 수 있다.

변수는 'DECLARE' 부분에서 선언되고 초기화되는데 그 초기값을 줄 수 있고, 해당 블럭을 진입할 때마다 초기화 되는데 이것은 함수가 호출될 때 단 1번 수행되는 것이 아니다. 예를 들면...

CREATE FUNCTION somefunc() RETURNS INTEGER AS '
DECLARE
quantity INTEGER := 30;
BEGIN
RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 30
quantity := 50;
--
-- 하위-블럭의 생성
--
DECLARE
quantity INTEGER := 80;
BEGIN
RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 80
END;

RAISE NOTICE ''Quantity here is %'',quantity; -- Quantity here is 50
END;
' LANGUAGE 'plpgsql';

위의 내용을 잘 이해해서 BEGIN/END 사용에 혼선이 없기를 바란다. BEGIN/END는 트랜젝션을 제어하는 데이터베이스 명령과 PL/pgSQL의 문장을 한 데 묶는 역할을 한다. PL/pgSQL의 BEGIN/END 는 오로지 문장을 한데 묶는데 사용한다. 그것들은 실제 트랜젝션의 시작과 끝이 아니다. function과 trigger procedure는 항상 외부 질의(outer query)에 의해 제공된 트렌젝션 내에서만 수행된다. 그 말은 Postgres는 중첩된 트렌젝션 기능이 없어서 스스로 트렌젝션을 시작하고, commit할 수 없다는 뜻이기도 하다.
24.2.2. 주석문

PL/pgSQL에는 두가지 형태의 주석문이 있다.
이중 빼기표시 '--'는 현재 위치부터 문장의 끝까지가 주석문이라는 의미이다.
그리고 '/*' 표시는 '*/'을 만날 때까지가 주석문이라는 의미이다.
주석문은 중첩될 수 없으나 이중 빼기표시 '--'는 이중빼기 표시 주석문의 내부나 '/*' '*/' 주석문 사이에 놓일 수 있다.
24.2.3. 변수와 상수

블럭 내에서 혹은 하위-블럭 내에서 사용할 모든 변수, row와 record는 반드시 'DECLARE'부 에서 선언되어야 한다. 그러나 FOR 반복문에서 사용되는 정수범위를 가진 반복용 변수는 예외이다.

PL/pgSQL 변수는 INTEGER, VARCHAR, CHAR와 같은 어떠한 SQL 자료형을 가질 수 있다. 모든 변수는 SQL NULL 값을 초기값으로 갖는다.

아래는 변수 선언의 몇가지 예를 들어본 것이다.

user_id INTEGER;
quantity NUMBER(5);
url VARCHAR;
24.2.3.1. 상수와 초기치를 갖는 변수

선언은 다음과 같은 문법을 가진다.

변수이름 [ CONSTANT ] 변수형 [ NOT NULL ] [ { DEFAULT | := } 값 ];

CONSTANT를 사용하여 정의된 변수의 값은 변경할 수 없다. 만일 변수를 선언할 때 NOT NULL을 사용했을 때 그 변수에 NULL값을 할당하게 되면 실행시간 에러가 발생한다. 변수를 NOT NULL로 선언하였으면 반드시 초기값을 명시해야 한다. 그 이유는 모든 변수의 초기값은 SQL NULL이기 때문이다.

변수에 할당된 초기값은 함수가 호출될 때마다 평가된다. 그래서 timestamp형의 변수에 'now'라는 것을 할당하는 것은 함수가 실제 호출될 순간의 시간이 저장되는 것이지 함수가 바이트코드로 해석될 때의 시간이 저장되는 것이 아니다.

예)

quantity INTEGER := 32;
url varchar := ''http://mysite.com'';
user_id CONSTANT INTEGER := 10;
24.2.3.2. 함수로 넘겨지는 변수

함수로 넘겨지는 변수는 $1, $2 등등으로 이름 지어진다. (최대 16) 몇가지 예를 보자.

CREATE FUNCTION sales_tax(REAL) RETURNS REAL AS '
DECLARE
subtotal ALIAS FOR $1;
BEGIN
return subtotal * 0.06;
END;
' LANGUAGE 'plpgsql';


CREATE FUNCTION instr(VARCHAR,INTEGER) RETURNS INTEGER AS '
DECLARE
v_string ALIAS FOR $1;
index ALIAS FOR $2;
BEGIN
-- 계산식등이 온다.
END;
' LANGUAGE 'plpgsql';
24.2.3.3. 속성


%TYPE 과 %ROWTYPE 이라는 속성을 사용함으로써 사용자는 다른 데이터베이스의 자료형 또는 구조를 사용하여 변수를 정의 할 수 있다.

%TYPE
%TYPE을 이용하여 변수 또는 데이터베이스 칼럼의 자료형을 변수의 선언에 사용할 수 있다. 그래서 이 것을 이용하여 데이터베이스의 값을 갖는 변수를 선언할 수 있다. 예를 들면 사용자가 user_id라는 이름의 칼럼을 가지는 users라는 테이블이 있다고 하자. 이 때 테이블 users와 같은 자료형을 갖는 변수를 선언하려면...

user_id users.user_id%TYPE;

%TYPE을 사용함으로써 사용자가 사용하는 데이터베이스 구조체의 자료형을 알 필요가 없다. 그러나 이를 이용함으로써 얻을 수 있는 가장 큰 중요한 점은 만일 미래에라도 참조하고 있는 요소의 자료형이 바뀐다하더라도 사용자는 함수의 정의부분을 바꾸어줄 필요가 없다는 것이다.

변수이름 테이블이름%ROWTYPE;

위의 문장은 주어진 테이블의 구조와 함께 행(Row)을 선언하는 것이다. "테이블이름"은 반드시 데이터베이스에 존재하는 테이블이거나 Viwe의 이름이다. 그 행의 필드는 점 "."을 이용하여 참조를 할 수 있다. 함수의 매개변수는 복합형이 될 수 있다. (테이블 행 통째의) 이 경우, 연관된 식별자 $n은 행의 형일 것이지만 사용자는 사용할 때 ALIAS를 사용하여 이름을 변경해야 한다.

오로지 테이블 행의 사용자 속성은 그 행에서만 사용할 수 있다. OID 또는 다른 시스텡의 속성은 사용할 수 없다. (왜냐하면 행은 View에서도 올수 있으니까..) 이 행의 자료형(rowtype)의 필드는 테이블의 필드의 크기와 char()의 정밀도 등 자료형을 계승받는다.

DECLARE
users_rec users%ROWTYPE;
user_id users%TYPE;
/* 바로 위의 코드는 잘못된듯.. user_id users.user_id%TYPE;
가 되야 하지 않을까 */
BEGIN
user_id := users_rec.user_id;
...

create function cs_refresh_one_mv(integer) returns integer as '
DECLARE
key ALIAS FOR $1;
table_data cs_materialized_views%ROWTYPE;
BEGIN
SELECT INTO table_data * FROM cs_materialized_views
WHERE sort_key=key;

IF NOT FOUND THEN
RAISE EXCEPTION ''View '' || key || '' not found'';
RETURN 0;
END IF;

-- cs_materialized_views의 mv_name
-- 칼럼은 view의 이름을 저장한다.

TRUNCATE TABLE table_data.mv_name;
INSERT INTO table_data.mv_name || '' '' || table_data.mv_query;

return 1;
end;
' LANGUAGE 'plpgsql';
24.2.3.4. 변수이름 변경

RENAME을 사용하여 사용자는 변수, 레코드 또는 행의 이름을 바꿀 수 있다. 이것은 NEW 또는 OLD가 trigger procedure 내에서 다른 이름에 의해 참조될 때 유용하다.

문법과 예:

RENAME oldname TO newname;

RENAME id TO user_id;
RENAME this_var TO that_var;

24.2.4. 표현

PL/pgSQL 문장에서 사용하는 모든 표현은 다른 배후 실행기(backend executor)에 의해 처리된다. 상수를 포함한 표현은 실제로 실행시간 중에 평가가 필요하다. (예: timestamp형의 'now'와 같은 경우) 그래서 PL/pgSQL 분석기(parser)로서는 NULL 키워드와 real의 상수를 구분할 방법이 없다.
모든 표현은 내부적으로 SPI 관리자를 통하여 다음과 같은 쿼리를 실행함으로써 평가된다.

SELECT 표현식

위의 '표현식'에서 사용되는 변수는 매개변수로 치환이 되고 매개변수 배열에 있는 실제 값이 실행기로 넘겨진다. PL/pgSQL 함수에서 사용되는 표현식은 오직 한번 번역되고 저장된다. 오직 예외가 있다면 쿼리가 매번 바뀔경우에 사용되는 EXECUTE 명령어를 사용할 때이다.

형의 검사는 Postgres의 주 분석기에 의해 이루어지는데 이에는 상수값의 해석에 영향을 끼친다. 자세하게 설명하자면 아래의 두 함수에는 차이점이 있다.

CREATE FUNCTION logfunc1 (text) RETURNS timestamp AS '
DECLARE
logtxt ALIAS FOR $1;
BEGIN
INSERT INTO logtable VALUES (logtxt, ''now'');
RETURN ''now'';
END;
' LANGUAGE 'plpgsql';
and
CREATE FUNCTION logfunc2 (text) RETURNS timestamp AS '
DECLARE
logtxt ALIAS FOR $1;
curtime timestamp;
BEGIN
curtime := ''now'';
INSERT INTO logtable VALUES (logtxt, curtime);
RETURN curtime;
END;
' LANGUAGE 'plpgsql';

logfunc1()의 경우, Postgres 주 분석기는 INSERT를 해석할 때, logtable의 해당 필드가 timestamp형이므로 문자열 'now'가 timestamp형으로서 해석되어야 한다는 것을 안다. 그래서 주 분석기는 처음 logfunc1()이 호출되는 시점에서 이를 상수로 치환하고 이렇게 해석된 값이 이 함수가 살아있는 동안 유지가 된다.
말할 필요도 없이 이것은 프로그래머가 원하는 방향이 아니다.

logfunc2()의 경우, Postgres 주 분석기는 'now'가 무엇이 되어야 하는지 알 수 없다.
그래서 주 분석기는 'now'를 문자열로 인식하고 있다가 지역변수인 curtime에 할당될 때 PL/pgSQL 해석기는 'now'를 text_out() 과 timestamp_in() 을 호출하여 timestamp 형으로 변경한다.

이러한 Postgres 주 분석기의 형검사는 PL/pgSQL가 거의 완성된 후에 구현이 되었다. 그래서 6.3과 6.4 버젼 사이에 차이가 있으며 SPI 관리자를 사용하여 해석기능을 이용하는 모든 함수들에 영향을 끼친다. 현재의 PL/pgSQL에서는 지역변수를 사용하는 위의 경우에서만 정확하게 해석할 수 있는 유일한 방법이다.

만일 표현식이나 문장 내에서 레코드의 필드가 사용될 경우, 각각의 호출이 이루어지는 동안 필드의 자료형은 변경되어서는 안된다. 만일 trigger procedures를 만들 때와 같이 복수개의 테이블을 위한 이벤트를 다루는 경우에는 이점을 유념해야 할 것이다.
24.2.5. 문장

다음에 나오는 경우와 같이 PL/pgSQL 분석기에 의해 해석되지 않는 부분은 쿼리에 놓여져 그대로 데이터베이스로 실행을 위해 보내진다. 쿼리의 결과는 어떤 값도 반환하지 않는다.
24.2.5.1. 값의 대입(할당)

변수 또는 행/레코드 필드로 값을 대입하는 것은 다음과 같이 쓴다.

식별자 := 표현식;

만일 표현식의 결과값이 변수의 자료형과 일치하지 않으면 또는 그 변수에 크기나 정밀도가 명시되어 있다면 (예를 들어 char(20)), 결과 값은 PL/pgSQL 바이트코드 번역기에 의해 강제적으로 변경된다. 이러한 경우는 내제적으로 실행시간 오류를 야기할 수 있다.

user_id := 20;
tax := subtotal * 0.06;
24.2.5.2. 다른 함수의 호출

Postgres 데이터베이스에 정의된 모든 함수는 값을 반환한다. 그렇기에 일반적으로 함수를 호출하는 것은 SELECT 쿼리를 실행하거나 값의 대입이다.

그렇지만 몇몇 사람은 함수의 결과를 필요로 하지 않는 경우도 있다. 이 경우엔 PERFORM 문을 사용한다.

PERFORM 쿼리문

위의 문장은 SPI manager를 통해 SELECT 쿼리를 실행하고 반환값을 삭제한다. 지역변수와 같은 식별자는 여전히 함수 파라메터로 치환된다.

PERFORM create_mv(''cs_session_page_requests_mv'',''
select session_id, page_id, count(*) as n_hits,
sum(dwell_time) as dwell_time, count(dwell_time) as dwell_count
from cs_fact_table
group by session_id, page_id '');
24.2.5.3. 동적 쿼리의 실행

때때로 PL/pgSQL함수 내에서 동적 쿼리를 생성하고자 할 때가 있다. 또는 다른 함수를 생성하는 함수를 가질 수도 있다. PL/pgSQL는 이러한 경우를 위해 EXECUTE 문을 제공한다.

EXECUTE query-string

위에서 "query-string"은 실행할 쿼리를 포함하는 문자열이다.

동적 쿼리를 이용하여 작업할 때에, PL/pgSQL에서 작은 따옴표에 대한 문제에 직면하게 되는데 이에 대한 수고를 덜어주기 위해 자세한 설명이 있는 "Oracle PL/SQL에서 PL/pgSQL로의 변환"에 있는 표를 참조하기 바란다.

PL/pgSQL의 다른 쿼리와는 다르게 EXECUTE문에 의해 수행되는 쿼리는 다른 일반 쿼리와 같이 처음 실행될 때 번역과 저장 작업이 이루어져 서버가 살아 있는 내내 존재하는 것이 아니다. 쿼리 문자열은 다양한 테이블과 필드에서 작업을 수행할 수 있도록 프로시져 내에서 생성된다.

SELECT 쿼리의 결과는 EXECUTE에 의해 버려진다. 그리고 현재까지는 EXECUTE내에서 SELECT INTO는 사용할 수 없다 그래서 동적으로 생성된 SELECT에서 결과는 뽑는 유일한 방법은 FOR...EXECUTE 형식을 사용하는 것인데 이는 추후에 설령을 하겠다.

예)

EXECUTE ''UPDATE tbl SET ''
|| quote_ident(fieldname)
|| '' = ''
|| quote_literal(newvalue)
|| '' WHERE ...'';

이 예에서 quote_ident(TEXT) 함수와 quote_literal(TEXT) 함수를 사용하였다. 문자열 변수가 필드나 테이블의 이름을 포함하고 있으면 반드시 그 내용을 quote_ident()로 넘겨야 한다. 또한 변수가 동적 쿼리의 문자열의 문자요소를 포함하고 있으면 반드시 그 내용을 quote_literal()로 넘겨야 한다. 앞의 두경우 모두 작은 따옴표 또는 큰 따옴표에 둘러싸인 문자열 그리고 특수문자와 함께 반환하기 위해서는 적절한 절차를 밟아야 한다.

다음은 좀 더 큰 동적쿼리와 EXECUTE를 사용한 예이다.

CREATE FUNCTION cs_update_referrer_type_proc() RETURNS INTEGER AS '
DECLARE
referrer_keys RECORD; -- FOR에서 사용하기 위한 일반적인 record를 선언
a_output varchar(4000);
BEGIN
a_output := ''CREATE FUNCTION cs_find_referrer_type(varchar,varchar,varchar)
RETURNS varchar AS ''''
DECLARE
v_host ALIAS FOR $1;
v_domain ALIAS FOR $2;
v_url ALIAS FOR $3; '';

--
-- 어떻게 FOR 반복문을 통해 쿼리의 결과를 훑는지를 주목...
-- 여기서는 FOR <record> 생성을 이용하였다.
--

FOR referrer_keys IN select * from cs_referrer_keys order by try_order LOOP
a_output := a_output || '' if v_'' || referrer_keys.kind || '' like ''''''''''
|| referrer_keys.key_string || '''''''''' then return ''''''
|| referrer_keys.referrer_type || ''''''; end if;'';
END LOOP;

a_output := a_output || '' return null; end; '''' language ''''plpgsql'''';'';

-- 이 방법은 적법하다 왜냐하면 여기서는 어떠한 변수도 치환하지 않았기 때문이다.
-- 아니면 실패를 하게 된다. 함수를 시행하기 위한 다른 방법인 PERFORM을 보라

EXECUTE a_output;
end;
' LANGUAGE 'plpgsql';
24.2.5.4. 다른 결과 상태 포함하기

GET DIAGNOSTICS 변수이름 = item [ , ... ]

이 명령은 시스템 상태를 추출할 때 사용한다. 각 item은 상태값을 나타내는 키워드인데 그 값은 명시된 '변수이름'에 할당된다. (당연히 '변수이름'의 자료형은 item의 자료형과 일치하여야 한다.) 현재 사용가능한 상태 item은 ...

ROW_COUNT: SQL 처리기에 보내진 마지막 SQL 쿼리에 의해 처리된 행의 갯수
RESULT_OID: 가장 최근의 SQL쿼리에 의해 테이블에 삽입된 행의 OID.
주지할 사항은 RESULT_OID는 INSERT 쿼리 이후에 사용가능하다.
24.2.5.5. 함수에서 값의 반환

RETURN 표현식

위의 명령은 함수를 종료하고 '표현식'의 값을 상위 실행기로 반환한다. 함수의 반환 값은 반드시 정의 돼야한다. 만일 프로그램이 함수의 최상위 레벨의 끝에 까지 다다랐는데도 RETURN문이 없으면 실행시간 오류가 발생한다.

'표현식'의 결과는 자동적으로 함수의 반환형으로 변환된다. 이는 할당에서 설명한 것과 같다.
24.2.6. 제어 구조


이 제어 구조가 아마도 PL/SQL에서 가중 유용한 (또 중요한) 부분이 아닐까 한다. PL/pgSQL의 제어구조를 이용하여 사용자는 PostgreSQL의 데이터를 다양하고 강력하게 다룰 수 있다.
24.2.6.1. 조건 제어: IF 문

IF문은 특정 조건에 따라 주어진 일을 하는 문장이다. PL/pgSQL는 세가지 형태를 가지는데 그 것들은 IF: IF-THEN, IF-THEN-ELSE, IF-THEN-ELSE IF 이다.
주목: PL/pgSQL의 IF문은 짝지워진 END IF 가 필요한다. ELSE-IF의 경우는 2개의 END IF가 필요한데 하나는 첫번째 IF를 위해, 그리고 다른 하나는 ELSE IF를 위해서 이다.

IF-THEN
IF-THEN 문은 IF문의 가장 간단한 형태이다. THEN 과 END IF사이의 문장들은 주어진 조건이 true일때 수행된다. 그렇지 않으면 END IF 다음에 나오는 문장들로 실행이 넘어간다.

예)

F v_user_id <> 0 THEN
UPDATE users SET email = v_email WHERE user_id = v_user_id;
END IF;

IF-THEN-ELSE
IF-THEN-ELSE 문은 IF-THEN 문에 해당 조건이 FALSE가 되었을 경우 실행될 문장들을 추가한 형태이다.

예1)

IF parentid IS NULL or parentid = ''''
THEN
return fullname;
ELSE
return hp_true_filename(parentid) || ''/'' || fullname;
END IF;

예2)

IF v_count > 0 THEN
INSERT INTO users_count(count) VALUES(v_count);
return ''t'';
ELSE
return ''f'';
END IF;

다음에 나오는 예처럼 IF문은 중첩되어 사용될 수 있다.

IF demo_row.sex = ''m'' THEN
pretty_sex := ''man'';
ELSE
IF demo_row.sex = ''f'' THEN
pretty_sex := ''woman'';
END IF;
END IF;

IF-THEN-ELSE IF
"ELSE IF"문이 사용되는 것은 실제로 ELSE문 안에 IF문을 중첩하는 것과 같다. 그렇기 때문에 END IF문이 각 중첩된 IF와 상위 IF-ELSE를 위해 필요한 것이다.

예)

IF demo_row.sex = ''m'' THEN
pretty_sex := ''man'';
ELSE IF demo_row.sex = ''f'' THEN
pretty_sex := ''woman'';
END IF; /* <-- 요기를 주목하시라... 요게 일반적인 언어와 다른 부분이다. */
END IF;
24.2.6.2. 반복 제어: LOOP, WHILE, FOR 그리고 EXIT

LOOP, WHILE, FOR 그리고 EXIT 문을 이용하여 PL/pgSQL 프로그램을 반복적으로 실행하는 흐름제어를 할 수 있다.


LOOP 문...

[<<label>>]
LOOP
실행할 문장
END LOOP;

위와같은 조건이 없는 반복문은 반드시 명백하게 EXIT을 사용하여 종료되야 한다. 선택적으로 사용할 수 있는 label은 중복된 반복문에서 빠져나오고자 할 때 EXIT문에 의해 사용된다.

EXIT 문...

EXIT [ label ] [ WHEN 표현식 ];

만약에 label이 명시되지 않으면 중첩된 반복문에서 내부의 반복문이 끝나면 내부 반복문의 END LOOP 다음에 나오는 문장을 수행한다. 그렇지 않고 label이 명시되면 주어진 label이 붙은 반복문 또는 블럭을 빠져나가게 된다. 그리고 반복문 또는 블럭의 해당되는 END 문 다음 문장을 처리한다. 그 label은 중첩된 반복문에서 현재 수행중인 내부 반복문의 label이거나 상위 반복문의 label이어야한다.

예)

LOOP
-- 실행할 계산문
IF count > 0 THEN
EXIT; -- 반복문 탈출
END IF;
END LOOP;

LOOP
-- 실행할 계산문
EXIT WHEN count > 0;
END LOOP;

BEGIN
-- 실행할 계산문
IF stocks > 100000 THEN
EXIT; -- 오류! LOOP 블럭 밖에서 EXIT을 사용할 수 없다.
END IF;
END;

WHILE 문...

WHILE문을 사용하여 주어진 조건을 검사하여 그 조건이 참인 동안 일을 수행하는 반복문을 만들 수 있다.

[<<label>>]
WHILE 표현식 LOOP
실행할 문장
END LOOP;


예)

WHILE amount_owed > 0 AND gift_certificate_balance > 0 LOOP
-- 실행할 계산식
END LOOP;

WHILE NOT boolean_expression! LOOP
-- 실행할 계산식
END LOOP;

FOR 문...

[<<label>>]
FOR 변수이름 IN [ REVERSE ] 표현식 .. 표현식 LOOP
실행할 문장
END LOOP;

이 반복문은 주어진 정수형의 범위 내에서 반복 수행한다. 변수 '변수이름'은 자동적으로 정수형으로 생성이 되고 오로지 반복문 내에서만 존재를 한다. 그리고 두 '표현식'은 최소와 최대값의 범위를 나타내는 표현식이다. 그리고 반복 될 때마다 변수의 값은 항상 1씩 증가/감소된다.

다음은 FOR 문에 대한 예이다. (레코드를 반복하는 FOR에 대한 예는 24.2.7를 참조)

FOR i IN 1..10 LOOP
-- 실행할 표현식

RAISE NOTICE 'i is %',i;
END LOOP;

FOR i IN REVERSE 1..10 LOOP
-- 실행할 표현식
END LOOP;

24.2.7. RECORD를 이용한 작업

RECORD는 간단한 데이터베이스 행의 자료형 (rowtype) 이긴 하지만 사전에 구조가 정의 되어있지 않는다. 이는 선택(selection)과 FOR 반복문에서 SELECT 작업을 통해 실제 데이터베이스의 한 행의 값을 갖는다.
24.2.7.1. 선언

RECORD 형의 변수는 다른 선택(selection)에 사용될 수 있다. 한 레코드를 다루거나 값을 레코드 필드로 대입을 하려고 할 때 만일 실제 행(row)이 없다면 실행시간 오류를 야기한다. 변수선언은 다음과 같이 한다.

변수이름 RECORD;
24.2.7.2. 값의 할당

RECORD 또는 Row으로 완전한 선택(selection)의 할당은 다음과 같이 해서 이루어진다.

SELECT INTO 대입받을_변수 표현식 FROM ...;


'대입받을_변수'는 RECORD 또는 ROW 형의 변수이거나 쉼표 ','를 사용하여 구분된 레코드/행의 필드와 변수의 목록이다. 위의 것은 Postgres가 보통 수행하는 SELECT INTO와는 완전히 다른 것이다. Postgres가 보통 수행하는 SELECT INTO에서는 SELECT결과를 이용하여 새로운 테이블을 만들 때 사용하는 것이다. (만일 PL/pgSQL에서 SELECT의 결과를 이용하여 새로이 테이블을 만들려면 CREATE TABLE AS SELECT를 사용해야 한다.)

만약에 row또는 변수목록(','로 구분된)을 '대입받을_변수'로 사용하고자 한다면 선택된 값들은 반드시 "대입받을_변수'와 구조가 일치되어야 한다. 그렇지 않으면 실행시간 오류가 발생한다. FROM 키워드 다음에는 조건, 그룹, 정렬 같은 것들이 따라올 수 있는데 이것들은 SELECT문에도 쓸 수 있는 것들이다.

레코드 또는 행의 값이 RECORD 변수로 할당이 되면 마침표 '.'를 사용하여 해당 필드로 접근할 수 있다.

DECLARE
users_rec RECORD;
full_name varchar;
BEGIN
SELECT INTO users_rec * FROM users WHERE user_id=3;

full_name := users_rec.first_name || '' '' || users_rec.last_name;

FOUND라고 하는 boolean형의 특별한 변수가 있는데, 이 것은 SELECT INTO 바로 다음에 사용되어 할당이 성공적이었는지를 확인 할 수 있다.

SELECT INTO myrec * FROM EMP WHERE empname = myname;
IF NOT FOUND THEN
RAISE EXCEPTION ''employee % not found'', myname;
END IF;

또한 IS NULL (또는 ISNULL)을 사용하여 RECORD/ROW가 NULL인지 여부를 검사할 수 있다. 만일 선택이 복수개의 행을 반환하였다면 첫번째만이 '대입받을_변수'에 옮겨진다. 나머지 행들은 누락되어져 버린다.

DECLARE
users_rec RECORD;
full_name varchar;
BEGIN
SELECT INTO users_rec * FROM users WHERE user_id=3;

IF users_rec.homepage IS NULL THEN
-- 사용자가 없는 홈페이지에 들어왔다, "http://"를 반환한다.

return ''http://'';
END IF;
END;
24.2.7.3. Record를 통한 반복

특별한 FOR 반복문을 통하여 사용자는 쿼리의 결과를 통해 반복을 할 수 있고, 데이터를 사용할 수 있다. 다음은 그 문법이다.

[<<label>>]
FOR 레코드 | 행 IN select문장 LOOP
실행할 문장
END LOOP;

레코드 또는 행은 'select문장'으로부터 모든 반환된 행을 할당받고 반복문의 문장은 각각의 행을 처리한다. 다음은 그 예이다.

create function cs_refresh_mviews () returns integer as '
DECLARE
mviews RECORD;

-- 위의 선언 대신에...
-- mviews cs_materialized_views%ROWTYPE;
-- 이렇게 했다면 이 변수는 오로지 cs_materialized_views 테이블에서만
-- 사용할 수 있다.

BEGIN
PERFORM cs_log(''Refreshing materialized views...'');

FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP

-- 이제 "mviews" 는 cs_materialized_views의 한 레코드를 받았다.

PERFORM cs_log(''Refreshing materialized view '' || mview.mv_name || ''...'');
TRUNCATE TABLE mview.mv_name;
INSERT INTO mview.mv_name || '' '' || mview.mv_query;
END LOOP;

PERFORM cs_log(''Done refreshing materialized views.'');
return 1;
end;
' language 'plpgsql';

만일 위의 반복문이 EXIT으로 빠져나왔다면 마지막에 할당받은 행은 반복문 밖에서도 여전히 사용가능하다.

FOR-IN EXECUTE 문은 결과 레코드 사이를 반복할 수 있는 다른 방법이다.

[<<label>>]
FOR 레코드 | 행 IN EXECUTE 표현식을_가진_문자열 LOOP
실행할 문장
END LOOP;

이의 형태는 앞의 예와 비슷한데, 이 경우에는 SELECT문이 표현식을 가지는 문자열로 대체되었다. 이것은 FOR문에 진입할 때마다 평가되고 해석된다. 여기서 한가지의 선택을 해야하는데 미리 해석된 쿼리를 통한 나은 성능을 택하느냐 아니면 일반적인 EXECUTE문과 같이 동적 쿼리의 유연성을 택하느냐이다.
24.2.8. 처리 중단과 메세지

RAISE문을 사용하여 Postgres elog 매커니즘으로 출력 메세지를 보낼 수 있다.

RAISE 출력수준 '출력형식' [, identifier [...]];

출력형식에서 뒤 따라오는 identifier의 출력형식을 정할 수 있는 %를 사용할 수 있다. '출력수준'으로 쓸 수 있는 키워드는 다음의 3가지이다.
DEBUG - 제품수준 환경의 데이터베이스에서 사용되는 조용한 수준의 메세지
NOTICE - 데이터베이스 log에 기록이 되고 클라이언트로 보내지는 수준의 메세지
EXCEPTIOM - 데이터베이스 log에 기록이 되고 트랜젝션을 중단하는 수준의 메세지

RAISE NOTICE ''Id number '' || key || '' not found!'';
RAISE NOTICE ''Calling cs_create_job(%)'',v_job_id;

바로 위의 2번째 예에서 v_job_id은 %의 자리에 문자열로 출력된다.

RAISE EXCEPTION ''Inexistent ID --> %'',user_id;

위의 예는 트랜젝션을 중단하고 데이터베이스 log에 기록한다.
24.2.9. 예외

Postgres는 그리 똑똑한 예외처리 모델을 가지고 있지 않다. 분석기(parser), 번역기/최적화 (planner/optimizer) 또는 실행기(executor)에서 더이상 요청된 처리를 수행하지 못할 경우 모든 트랜젝션은 중단되고 main loop로 건너뛰고나서 클라이언트 프로그램에서 다음 쿼리를 수행한다.

이같은 상황을 알리기 위한 메세지를 오류 메커니즘으로 넣는 것이 가능하다. 그러나 현재로는 왜 그러한 상황이 발생하였는지 아는 것은 불가능하다. (입/출력 변환 오류, floating point오류, parse 오류 등등). 그래서 데이터베이스 처리가 오류의 시점에서 자료의 불일치가 발생할 수 있기 때문에 상위 처리부로 귀환한다거나 계속된 명령의 처리는 전체 데이터베이스를 망가뜨릴 수 있다. 그렇기 때문에 처리가 중단이 됐고 이의 사실이 클라이언트에 알려진다 하더라도 계속되는 처리는 무의미하다.

그렇기 때문에 PL/pgSQL이 현재 할 수 있는 것은 함수나 trigger procedure가 처리되는 과정에서 중단(abort)되면 어떤 함수의 어디서 (line number와 문장의 종류) 발생했는지 추가적인 DEBUG lebel의 로그 메세지를 기록해주는 것이 다이다.
24.3. Trigger Procedures

PL/pgSQL는 trigger procedure를 정의 할 수 있다. 이를 생성하는 방법은 일반적인 것과 같이 CREATE FUNCTION를 사용하고 그 함수에 매개변수없이 리턴타입을 OPAQUE로 준다.

trigger procedure로 함수를 사용하는데는 Postgres에서만의 몇가지 세부사항이 있다.

첫째는 최상위 선언부(declare section)에 자동으로 생성되는 몇가지 변수들이 있다.

NEW

자료형이 RECORD인 변수로 ROW level trigger에서 INSERT/UPDATE 작업 할 때 데이터베이스의 새로운 행의 값을 갖는다.

OLD

자료형이 RECORD인 변수로 ROW level trigger에서 INSERT/UPDATE 작업 할 때 데이터베이스의 과거 행의 값을 갖는다.

TG_NAME

자료형이 name인 변수로 실제 작동할 trigger의 이름을 갖고 있다.

TG_WHEN

자료형이 test인 문자열로 trigger의 정의에 따라 "BEFORE" 또는 "AFTER"를 갖는다.


TG_LEVEL

자료형이 test인 문자열로 trigger의 정의에 따라 "ROW" 또는 "STATEMENT"를 갖는다.

TG_OP

자료형이 test인 문자열로 trigger가 실제 수행하는 작업을 나타내는데 "INSERT", "UPDATE" 또는 "DELETE"를 갖는다.

TG_RELID

자료형이 oid인 변수로 trigger를 촉발시킨 테이블의 Object ID를 갖는다.

TG_RELNAME

자료형이 name으로 trigger를 촉발시킨 테이블의 이름을 갖는다.

TG_NARGS

자료형이 integer으로 CREATE TRIGGER 문으로 생성된 trigger procedure로 넘겨진 매개변수의 숫자이다.

TG_ARGV[]

자료형이 text인 배열로 CREATE TRIGGER 문의 실제 매개변수 값이다. 배열의 첫째 요소는 0번재 배열에 있고, 만일 배열의 인덱스 값이 0보다 작거나 tg_nargs보다 크면 해당 배열은 NULL을 반환한다.

두번째는 trigger procedure는 NULL 또는 trigger를 동작시킨 테이블과 동인한 구조의 레코드/행을 반납해야한다. "AFTER"로 동작된 trigger는 항상 NULL값을 반납한다. "BEFORE"로 동작한 trigger는 NULL을 반환하면 이 실제의 행에 대한 동작을 건너 뛰도록 신호를 보낸다. 그렇지 않으면 반환된 레코드/행의 값이 삽입되거나 갱신된 행의 값을 덮어쒸울 것이다. signal값을 직접적으로 "NEW"에 기록해서 반환하거나 완전히 새로운 레코드/행을 만들어서 반환하는 것이 가능하다.

Example 24-1. PL/pgSQL Trigger Procedure 예

이 trigger는 테이블에 행 하나가 삽입되거나 갱신될 때마다 현재 사용자 이름과 행에 있는 시간을 확인한다. 그리고 주어진 종업원의 이름과 월급이 양수값인지 확인한다.

CREATE TABLE emp (
empname text,
salary integer,
last_date timestamp,
last_user text
);

CREATE FUNCTION emp_stamp () RETURNS OPAQUE AS '
BEGIN
-- 주어진 empname과 salary를 확인
IF NEW.empname ISNULL THEN
RAISE EXCEPTION ''empname은 NULL값이 될 수 없다.'';
END IF;
IF NEW.salary ISNULL THEN
RAISE EXCEPTION ''% 는 NULL로 된 salary를 가질 수 없다'', NEW.empname;
END IF;

-- 월급을 받고 있는 사람 중에 종업원인가?
IF NEW.salary < 0 THEN
RAISE EXCEPTION ''% 는 음수의 salary를 가질 수 없다.'', NEW.empname;
END IF;

-- 언제 누가 월급을 조정했나?
NEW.last_date := ''now'';
NEW.last_user := current_user;
RETURN NEW;
END;
' LANGUAGE 'plpgsql';

CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();

24.4. 예제

여기에 PL/pgSQL로 함수를 만드는 것이 얼마나 쉬운지를 보여주는 예가 있다.

PL/pgSQL를 사용하여 함수를 만들 때 한가지 귀찮은 것은 작은 따옴표를 사용하는 것이다. CREATE FUNCTION에 사용하는 함수의 소스는 문자열형이어야 한다. 그렇기 때문에 문자열형 내부의 작은 따옴표는 두번을 사용하던지 ('') 아니면 역슬레쉬에 이은 작은 따옴표 (\')이어야 한다. PL/pgSQL개발자는 여전히 좀더 우아한 방법을 찾고 있고 그동안은 (괴롭겠지만) 아래의 예와 같이 두개의 작은 따옴표를 써야한다. 미래에 어떤 해결점이 나오던지 간에 현재의 사용하는 형태는 미래에도 호환이 될것이다.

다른 상황에서 작은 따옴표에서 벗어나는 방법에 대한 자세한 설명은 24.5.1.1에 있으니 참고 바란다.

Example 24-2. 정수를 증가시키기 위한 간단한 PL/pgSQL 함수

이 함수는 정수를 받아서 1을 증가시키고, 그 증가된 값을 반환한다.

CREATE FUNCTION add_one (integer) RETURNS integer AS '
BEGIN
RETURN $1 + 1;
END;
' LANGUAGE 'plpgsql';



Example 24-3. 문자열을 합치는 간단한 PL/pgSQL 함수

이 함수는 두 문자열을 받아들여서 하나로 합친다음 그 것을 반환한다.

CREATE FUNCTION concat_text (text, text) RETURNS text AS '
BEGIN
RETURN $1 || $2;
END;
' LANGUAGE 'plpgsql';



Example 24-4. 복합형의 PL/pgSQL 함수

이 예에서 EMP라는 테이블과 매개변수로 정수를 받아서 참 또는 거짓을 반환한다. 만약에 EMP테이블의 salary 필드가 NULL이면 "f"를 반한하고 그렇지 않으면 그 필드의 값과 매개변수로 받은 정수값을 비교하여 그 결과값을 반환한다. ( t 또는 f )

CREATE FUNCTION c_overpaid (EMP, integer) RETURNS boolean AS '
DECLARE
emprec ALIAS FOR $1;
sallim ALIAS FOR $2;
BEGIN
IF emprec.salary ISNULL THEN
RETURN ''f'';
END IF;
RETURN emprec.salary > sallim;
END;
' LANGUAGE 'plpgsql';

 


출처 : http://cafe.daum.net/kangitbankbest
, .

LINUX FIREWALL

Security 2009. 9. 1. 11:12

리눅스에서의 방화벽 시스템[1]

    이수준 외 BIT NETWORK 67 기
    VIRUSAWALL & SSL APPLICATION 팀

    본 프로젝트는 리눅스상에서 공개된 방화벽 프로그램인 TIS사의 FWTK 프로그램을 중심으로 진행하였다. 이번 호에서는 FWAK를 설치하기 위해서 먼저 익혀야 할 배경 지식들 즉 방화벽과 리우팅의 개념에 대해 개략적으로 알아 보고 실제적으로 리눅스에서 라우터와 베스천 호스트를 만드는 방법을 자세히 적어보고자 한다. 그리고 다음 호에서는 FWTK 설치와 베스천 호스트를 운영하는 방법, 그리고 본 프로젝트에서 실제로 구축한 시스템에 대해 간략히 소개를 하는 것으로 기사를 마칠까 한다.

방화벽의 개요

    인터넷에 대한 관심이 고조됨에 따라 인터넷에 연결하여 다양한 서비스를 제공받으려는 사용자들이 점점 증가하고 있는 추세이다. 실제로 인터넷에 접속이 가능한 사용자들은 인터넷을 통해 다양한 형태의 정보와 서비스를 제공받고 있다.
    그러나, 이와 함께 인터넷을 경유한 불법 사용자나 해커들의 침입으로 인한 피해도 크게 늘어나고 있고, 그 수법 또한 전문적이고 다양해지고 있다. 인터넷으로부터의 불법 사용자나 해커의 침입을 막기 위한 대책으로 각각의 시스템이 가지고 있는 자체 보안 능력에 의존하는 방법이 현재까지 많이 이용되어 왔으나, 현재와 같이 네트워크에 연결된 시스템의 숫자가 폭발적으로 증가하는 현실에서는 많은 수의 시스템 각각을 일일이 점검하고 거기에 맞는 보안 대책을 마련하여 적용하는 일은 쉬운 일이 아니다.

    특히 내부 네트워크상에 여러 시스템들이 존재할 경우에는 모든 시스템에 동시에 보안 대책을 적용하지 않으면 내부 네트워크는 결코 안전하다고 보기 어려우며, 각각의 시스템 마다 적용된 보안 정책이 다를 수 있으므로 보안 정책을 일괄적으로 적용시켜 관리하기란 매우 어렵다. 이러한 문제점을 해결하기 위해서 내부 네트워크와 외부 네트워크의 연결점에서 내부 네트워크 상의 시스템들을 보호할 수 있는 네트워크 구성 요소가 필요하게 되었고, 이러한 기능을 담당하는 시스템이 바로 방화벽 시스템이다. 따라서 내부 시스템 각각에 대해 적용하던 보안 정책을 네트워크 상의 한 곳에서 적용시켜 관리할 수 있게 되었으며, 내부 시스템에 대한 보안 수준을 높일 수 있게 된다. ==>more

방화벽이란?

    외부 네트워크로부터의 침입에 대해 내부 네트워크를 보호하기 위한 네트워크 구성 요소 중의 하나로, 외부의 불법 사용자의 침입으로부터 내부의 전산 자원을 보호하기 위한 정책 및 이를 지원하는 하드웨어와 소프트웨어를 총칭한다. 내부 네트워크가 외부 네트워크(주로 인터넷을 의미)에 연결되어 있지 않을 경우에는 방화벽은 불필요하다.

방화벽의 주요 기능

    방화벽은, 일반적으로 네트워크 서비스별로 해당 서비스를 요구한 호스트의 IP 주소와 포트 번호, 사용자 인증에 기반을 두고 외부 침입을 차단하게 된다. 허용된 네트워크 사용자에게 원하는 서비스를 제공하면서 허용되지 않은 사용자에게는 서비스를 차단하고, 해당 서비스의 허용 또는 실패에 대한 기록을 남긴다.

    *외부 네트워크와 연결된 유일한 창구 (Gateway)
    *서비스 접속 및 거부
    *사용자 인증 포함
    *내외부 상호 접속된 네트워크에 대한 트래픽 감시, 기록

방화벽의 보안 정책

    내부 자원을 보호하기 위한 정책은 크게 다음의 두 가지로 요약할 수 있다.

    *명백히 금지되지 않은 것은 허용한다.
    *명백히 허용되지 않은 것은 금지한다.

    대부분 방화벽 시스템은 기본 보안 개념으로 "명백히 허용되지 않은 것은 금지한다" 의 정책을 따른다. 즉, 보안 규칙에 명백히 허용(설정)되지 않은 네트워크와 호스트가 내부 자원에 접근하는 것을 금지한다.

방화벽의 종류

    패킷 필터링(Packet Filtering) 방식

    패킷 필터링 방식은 네트워크의 OSI 모델에서 네트워크층(IP 프로토콜)과 전송층(TCP 프로토콜)에서 패킷을 필터링 하는 기능으 ㄹ하면서, 패킷에 대한 경로 배정을 위한 자체 프로토콜을 함께 사용하는 형태의 방화벽 시스템이다. 패킷 필터링 방식의 방화벽은 스크리닝 라우터로 구성할 수도 있으며, 베스천 호스트와 패킷 필터링 소프트웨어로도 구현할 수 있다.

    어플리케이션 프락시(APPlication Proxy) 방식

    어플리케이션 프락시 방식의 방화벽은, OSI 7 계층 네트워크 모델에서 제 7계층인 어플리케이션 계층에 방화벽 기능을 구현하게 된다. 이렇게 구현된 게이트웨이는 각 서비스별로 프락시 데몬이 있기 때문에 프락시 게이트웨이 또는 응용 게이트웨이라고 부르기도 한다. 어플리케이션 프락시 방식의 게이트웨이는 각 서비스별 프락시가 서비스 요구자의 IP 주소 및 포트를 기반으로 네트워크 접근 제어를 수행하며, 아울러 사용자 인증 및 기타 부가적인 서비스를 지원할 수 있다.

    서킷 게이트웨이(Circuit Gateway) 방식

    앞에서 알아 본 어플리케이션 프락시 방식의 방화벽에서는, 각 서비스마다 프락시가 존재하지만, 서킷 게이트웨이 방식의 방화벽에서는 OSI 7계층의 네트워크 모델에서 4계층과 5계층에 해당되는 부분에 TCP Proxy 와 UDP proxy가 존재하게 된다.
    본 프로젝트에서는 패킷 필터링 방식과 어플리케이션 프락시 방식을 연결하여 보안의 강도를 높이고자 하였다. 뒤에 소개될 시스템 구성도에서 보듯이 라우터에서 IP 레벨에서 패킷 ==>more

라우터와 라우팅 테이블

    필터링을 해주고 여길 통과한 패킷은 배스천 호스트에서 어플리케이션 레벨에서 통제하도록 구성하였다.

라우팅과 라우터의 개념

    라우팅 개념을 이해하기 위해서는 TCP/IP 프로토콜에 대한 이해가 있어야만 한다. 라우팅이란 패킷을 전송하기 위한 경로를 선택하는 과정을 말하고 라우터란 다른 어드레스 체계를 가진 네트워크 사이에서 통신을 가능하게 하는 기기를 말한다. 인터넷은 라우터라고 불리는 컴퓨터에 의해 상호 연결된 여러 물리적인 망들로 구성되어 있다. 즉 어떤 망의 컴퓨터(호스트)에서 인터넷에 접속하고자 한다면 하나 이상의 라우터를 거쳐야만 하는 것이다. 한 호스트상의 응용 프로그램이 통신하고자 할 때, TCP/IP 프로토콜은 하나 이상의 IP 데이터그램을 만든다. 호스트는 데이터그램을 어디로 송신하여야 하는지를 선택할 때 자신의 라우팅 테이블을 참조해서 경로 설정을 해야만 한다. 그림으로 나타내면 위와 같다.

라우팅의 종류

    라우팅의 종류는 라우팅 테이블을 어떻게 작성하느냐에 따라서 크게 정적 라우팅과 동적 라우팅으로 나뉜다.

    *정적 라우팅(static routing)
    라우팅 테이블을 직접 작성하는 것이다. 이 방법은 망 구조가 복잡하지 않는 소규모 망에서 주로 쓰는 방법이다. 쉘상에서 route라는 명령으로 작성하면 된다.

    *동적 라우팅(dynamic routing)
    이 방법은 라우터 사이의 망 도달성 정보를 정확하게 자동적으로 교류하는 방식으로 라우터들 간에 라우팅 테이블을 주고 받아 자신의 라우팅 테이블을 수정하는 방식이다. 이 때 쓰

라우터와 네트웍간의 연결

    는 라우팅 프로토콜로는 RIP, OSPE등 여러가지 종류가 있다. 여기서는 이글의 범위를 벗어나므로 더 이상 언급하지 않겠다. PC로 라우터를 만들었을 경우에는 routed라는 프로그램을 실행시키면 동적 라우팅을 할 수가 있다.
    지금까지 방화벽과 라우팅의 개념을 개략적으로 살펴보았다. 지금부터는 실제적으로 어떻게 베스천 호스트와 라우터를 셋팅하는지 살펴보자.

    실제 구현 방법

    *리눅스에서 2개의 랜카드를 셋팅하는 방법

    라우터이든 베스천 호스트이던 간에 일단 먼저 해야 할 일은 PC에 랜카드 2개를 설치하는 일이다. 이것은 랜카드의 종류가 같던 다르던 상관 없다. 중요한 것은 랜카드의 IRQ와 I/O주소가 PC의 IRQ와 I/O 주소가 일치하게 세팅 해 주어야 한다는 것이다. 물론 랜카드끼리의 충돌을 피하기 위해선 랜카드의 IRQ와 I/O 주소는 서로 달라야 한다. 이 문제는 랜카드 공급 업체에서 공급해 준 드라이버 디스켓에 IRQ를 조절해 주는 프로그램이 있을 것이므로 이를 이용해 해결하면 된다. 구체적인 방법을 소개하자면 랜카드를 PC에 꼽은 다음 제일 먼저 해야 할 일은 구입 업체에서 제공한 프로그램을 이용해 PnP기능을 제거하는 것이다. 그런 다음 윈도우95에서 제어판 -> 시스템 -> 장치관리자 -> 컴퓨터의 등록정보를 선택하면 현재 시스템에서 쓰고 있는 IRQ와 I/O주소 상태를 알 수 있을 것이다. 이것을 참고로 시스템에서 사용되지 않는 IRQ와 I/O를 메모한 다음 구입 업체에서 제공한 프로그램을 이용

    [표1] 수정된 lilo.conf

    해 프로그램을 이용해 랜카드의 IRQ와 I/O주소를 거기에 맞게 설정하면 된다. 테스트해서 성공적으로 끝났다면 설정된 각각의 랜카드의 IRQ와 I/O를 메모해 두길 바란다. 이제 리눅스로 부팅해 보자. 우선 커널이 자신의 랜카드를 지원하도록 컴파일 해야 한다. 컴파일 옵션 중에서 Network device support 부분에서 자신의 랜카드를 선택하면 된다. 커널 컴파일이 제대로 끝났다면 부팅될 때 랜카드를 선택하면 된다. 커널 컴파일이 제대로 끝났다면 부팅될 때 랜카드 설정 부분이 나타날 것이다. 쉘상에서는 dmesg라는 명령으로 확인할 수 있다. 다음으로 해야할 작업은 /etc/lilo.conf를 [표1]과 같이 수정해야 한다.
    이제 각각 인터페이스(eth0, eth1)에 IP adress를 할당하고 라우팅 테이블에 추가시키는 방법을 알아보자. ==>more

    dislevel#ifconfig eth0 inet 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255 up
    dislevel#ifconfig eth1 inet 192.168.2.1 netmask 255.255.255.0 broadcast 192.168.2.255 up
    dislevel#route add net 192.168.1.0 eth0
    dislevel#route add net 192.168.2.0 eth1

    지금까지는 리눅스에서 랜카드 2개를 설정하는 방법을 알아보았다. 이젠 베스천 호스트와 라우터를 만드는 방법을 알아보겠다.

    *리눅스를 베스천 호스트로 만드는 방법

    리눅스를 베스천 호스트로 만드려면 커널 컴파일을 다시 한번 해야 한다. Network device support 부분은 앞에서 설명했으므로 생략하고 Network options 부분에서 설정해 주어야 할 부분은 <화면1>과 같다.

    여기서 눈여겨 볼 부분은 IP forwarding을 OFF로 셋팅해야 한다는 것이다. 왜냐하면 베스천 호스트에서는 모든 패킷을 일단 차단한 다음 Application Gateway별로 규칙에 부합하는 패킷만을 통과시키도록 정책을 쓰기 때문이다.

    *리눅스를 라우터로 만드는 방법

    베스천 호스트의 차이점을 말한다면 IP 패킷을 forwarding 하느냐, 하지 않는냐에 있다. 즉 라우터는 IP forwarding 을 해야하고 베스천 호스트는 해서는 안된다. 그렇기 때문에 베스천 호스트 만들 때에는 설정했던 컴파일 옵션 중 IP forwarding 부분을 ON 시켜주면 된다. 그리고 추가로 윗그림에서 IP:optimize as router not host 부분을 y로 하면 된다. 커널 컴파일이 끝났으면 routed라는 프로그램을 실행시키면 된다.

    <화면1> 커널 컴파일 옵션에서 네트웍 부분 세팅

    Router를 위한 IP Packet Filter

    이번에는 라우터에 패킷을 필터링 하는 기능을 구현해 보자. 패킷 필터링은 인터넷에 연결된 사설 네트워크에서 인터넷으로 나가거나 사설 네트워크로 들어오는 패킷들의 IP 주소, 포트 번호 등을 기준으로 패킷을 걸러내 허용된 패킷만 받아들이거나 전달되도록 하는 것을 말하며, 일반적으로 다음과 같은 과정을 따라 패킷을 필터링하게 된다.

    *패킷과 연관되는 필터링 규칙이 있는가를 검사한다.

    *처음 발견된 걸맞는 필터링 규칙에 따라 패킷을 처리한다.

    *다음과 같은 3가지 정책중 규칙에 설정된 정책에 따라 패킷을 처리한다.

    -Accept : 패킷을 통과 시킨다.
    -Deny : 패킷을 그냥 버린다.
    -Reject : 패킷을 버리고 ICMP Destination Unreachable 메시지를 패킷 송신자(sender)에게
    보낸다.

    *각 규칙에 걸맞는 패킷 수와 그 바이트를 셈한다.

    *패킷에 관한 정보를 기록한다.

    *걸맞는 필터링 규칙이 없을 경우에는 기본(default)정책에 따라 패킷을 처리한다.

    리눅스에서는 이러한 패킷 필터링의 기능을 ipfwadm이라는 프로그램을 가지고 쉽게 구현 할 수 있다 알짜 레드햇을 설치 하였다면 아마 이 프로그램이 기본으로 설치되어 있을 것이다. 만약 설치 되어있지 않다면 다음 URL에서 최신 버전을 구할 수 있을 것이다.

    Router와 Packet Filter 개념도

    프로그램을 설치하는 것은 별 어려움이 없으리라 본다. 자 그럼 이제 본론으로 들어가서, 우리가 제일 먼저 해야 할 것은 패킷 필터링에 있어서 기본 정책을 세우는 것이다. 일반적으로 기본 정책을 수립하는데 다음과 같은 두가지 철학이 있다.

    *명시적으로 허가되지 않은 것은 금지한다.
    *명시적으로 금지되지 않은 것은 허가한다.

    즉, 기본 정책으로는 일반 모든 패킷을 허용하고 보안상 문제가 되는 패킷들에 대해서는 금지하는 방법과 보안상 문제가 없다고 생각되는 패킷들만 허용하고 나머지 모든 패킷들을 금지하는 방법이 있다. 일반적으로 두번째 방법을 많이 사용한다. 따라서 여기서도 두번째 방법을 가지고 설명을 하겠다. 패킷 필터링의 기본 정책은 다음과 같은 명령으로 설정할 수 있다.

      Ipfwadm -I -p deny
      Ipfwadm -O -p deny

    여기서 -I는 들어오는(input) 패킷을 의미하고 -O는 나가는(output) 패킷을 의미한다. 전달(forward)되는 패킷의 경우는 -F 을 이용하여 정책을 세울 수 있다. -p는 기본 정책(default policy)을 설정하는 옵션이다. 위 두 명령은 들어오고 나가는 모든 패킷에 대하여 기본적으로 패킷을 버리도록 설정이 되어 있다. 이 상태에서는 어떤 패킷도 현재 호스트에 들어오지도 나가지도 않는다. 따라서 어떤 네트워크 서비스도 이용할 수 없다. localhost에 대한 ping 명령을 내려보면 아래 그림과 같을 것이다. 그럼 다시 ping이 가능하게 하려면 어떻게 하면 될까? 다음과 같이 기본 정책을 다시 바꾸면 될 것이다.

    Localhost에 대한 ping 예제

      ipfwadm -I -p accept
      ipfwadm -O -p accept

    하지만 다른 서비스는 다 무시하고 ping만 가능하게 하라면 이 방법으론 안될 것이다. 다음과 같이 하면 ping이 제대로 동작할 것이다.

      ipfwadm -I -a accept -P icmp
      ipfwadm -O -a accept -P icmp

    여기서 -a 옵션은 새로운 필터링 규칙을 추가한다는 것을 나타내고 뒤에 나오는 accept는 허용 규칙을 추가한다는 뜻이고, -P icmp은 프로토콜(protocol) icmp를 의미한다. ping프로그램이 사용하는 프로토콜이 icmp이므로 icmp를 사용한 패킷을 보내고 받을 수 있도록 하면 된다. 여기서 주의 할 점은 'P'가 대문자 라는 것이다.
    필터링 규칙이 제대로 설정 되었는지 알아보려면, 다음과 같이 하면된다. ==>more

      ipfwadm -I -l --- Input packet Filtering rule list 출력
      ipfwadm -O -l --- Output packet Filtering rule list 출력

    그러면 Input packet과 Output packet에 대한 필터링 규칙을 보여준다.

    그럼 이번에는 Telnet 서비스가 가능하도록 해보자. Telnet 서비스는 밖으로 Telnet을 통해 나가는 경우와 밖에서 Telnet을 통해 들어오는 경우, 두 가지가 있다. 먼저 Telnet 서비스를 통하여 밖으로 나가는 경우를 보자. Telnet 서비스를 이용할 경우 TCP 프로토콜을 사용하며, Telnet 서버는 24번 포트를 사용하여 클라이언트의 접속을 기다린다. Telnet 클라이언트는 1024~65535범위 안에서 임의의 포트를 사용하여 Telnet 서버에 연결을 하게 된다. 따라서 Telnet을 이용할 때 전송되는 패킷들의 Source address와 Port는 다음과 같이 요약할 수 있다.

      -클라이언트에서 서버로 가는 패킷

      Source Destination
      Address Port Address Port
      Cllent IP 1024-65535 Server IP 24

      -서버에서 클라이언트로 가는 패킷

      Source Destination
      Address Port Address Port
      Server IP 24 Cllent IP 1024-65535

    따라서, 아래와 같이 필터링 규칙을 추가 해주면 된다. 대소문자에 유의하기 바란다.

      ipfwadm -O -a accept -P tcp -S 192.168.37.1 1024:65535
      \ D 192.168.22.0/24 telnet
      ipfwadm -I -a accept -k -P tcp -S 192.168.22.0/24
      telnet \-D 192.168.37.1 1024:65535

    여기서 -S 192.168.37.1은 패킷은 발송지 주소(Source Address)를 나타낸다. (호스트 IP는 192.168.37.1로 가정) 1024:65535을 Port 번호의 범위를 나타낸다. -D 192.168.22.0/24는 목적지 주소(Destination Address)를 나타낸다. 여기서 192.168.22.0는 네트워크 주소를 나타내고 /24는 netmask 255.255.255.0을 의미한다. 24라는 수는 netmask가 차지하는 상위 비트수를 나타낸다. 위에서 보는 것처럼 TCP 프로토콜에 대해서는 항상 IP address 와 Port 번호를 함께 설정해 주어야 하는데, 이때 IP address 가 호스트일 때는 IP주소만 적고, 네트워크일 때는 netmask를 함께 써주어야 하며, 포트 번호를 생략하게 되면 모든 포트로 설정된다. -k 옵션은 ACL 비트가 포함된 TCP 패킷을 의미한다.

    위와 같이 설정하면 호스트 192.168.37.1에서 192.168.22.0 네트워크로만 Telnet 서비스를 이용할 수 있다. 만약 모든 네트워크로만 Telnet 서비스를 사용가능 하도록 지정하고 싶을땐 192.168.22.0/24를 any/0으로 수정하면 된다. 수정할 때는 앞의 규칙을 삭제하고 새로운 규칙을 다시 설정해야 한다. 필터링 규칙 삭제는 -d 옵션을 사용하여 삭제하고자 하는 규칙을 설정하면 된다. ==>more

      ipfwadm -I -d accept -P tcp -S 192.168.37.1 1024:65535
      \ -D 192.168.22.0/24 telnet
      ipfwadm -I -d accept -k -P tcp -S 192.168.22.0/24
      telnet \-D 192.168.37.1 1024:65535

    모든 필터링 규칙을 삭제하고 처음부터 다시 설정하고 싶다면 다음 명령어를 사용하면 된다.

      ipfwadm -I -f
      ipfwadm -O -f

    이번에는 외부의 호스트가 호스트 192.168.37.1로 Telnet 접속을 할 수 있게 하여 보자. 명령어는 앞에서 한 것과 매우 유사하다. Source Address와 Destination address 그리고 Port번호를 반대로 설정하면 된다.

      ipfwadm -O -a accept -k -P tcp -S 192.168.37.1 telnet
      \ -D 192.168.22.0/24 1024:65535
      ipfwadm -I -a accept -P tcp -S 192.168.22.0/24 1024:
      65535 \-D 192.168.37.1 telnet

    위와같이 설정하면 192.168.22.0 네트워크에 속하는 호스트들만 호스트 192.168.37.1의 Telneet 서버스를 사용할 수 있게 된다. 이상으로 Telnet 접속의 2가지 경우에 대하여 접속을 제어하는 것에 관하여 알아보았다.

    다음으로 우리가 자주 사용하는 FTP에 대한 필터링 규칙을 설정하여 보자. 한가지 주의할 점은 Telnet은 하나의 연결(connection)을 사용하지만 FTP는 두 개의 연결을 가지고 있다는 것이다. 하나는 FTP 명령을 사용하기 위한 연결이고 다른 하나는 데이터를 전송하기 위한 연결이다. 따라서 FTP에 대한 필터링 규칙은 다음과 같다.

      ipfwadm -I -a accept -k -P tcp -S any/0 ftp \
      -D 192.168.37.1 1024:65535
      ipfwadm -O -a accept -P tcp -S 192.168.37.1
      1024:65535 \-D any/0 ftp
      ipfwadm -I -a accept -P tcp -S any/0 ftp-data \
      -D 192.168.37.1 1024:65535
      ipfwadm -O -a accept -k -P tcp -S 192.168.37.1
      1024:65535 \-D any/0 ftp-data

    그외 다른 서비스들도 Telnet 이나 FTP와 마찬가지로 네트워크상에서의 패킷의 발신지 포트와 목적지 포트에 유념하여 필터링 규칙을 설정한다면 특정 패킷을 허용 또는 차단 할 수 있다. 하지만 모든 서비스에 관하여 패킷 필터링의 기능을 설정하는 것은 쉬운 일이 아니다. 각 서비스가 사용하고 있는 프로토콜과 포트 번호를 정확히 알아야 하며, 필터링 규칙이 매우 복잡해진다. 필터링 규칙을 세우는데 있어서 다음과 같은 점을 유의하기 바란다.

    ⇒ 필터링 규칙의 순서를 정하라.
    ⇒ 현재의 패킷에 적용될 수 있는 규칙이 여러 개 있다면 그중 먼저 설정된 규칙이 적용된다.
    ⇒ 가능한한 여러 개의 포트 번호를 묶어서 설정하라.
    - 패킷에 대한 필터링 규칙을 점검하는데 CPU time이 소요되기 때문이다.
    ⇒ 시스템이 시작될 때 필터링 규칙이 설정되게 하라.
    - 시스템이 시작되고 네트워크 장비가 설정되기 전에 필터링 규칙이 설정되도록 하는 것이
    이상적이다.
    ⇒ 호스트 이름이나 네트워크 이름을 사용하지 마라.
    - ipfwadm에서는 호스트 이름이나 네트워크 이름을 사용할 수 있으나 될 수 있으면
    IP 주소를 사용하는 것이 더 좋다.
    ⇒ 현재 운영 중인 시스템에 필터링 규칙을 설정할 경우 다음과 같은 순서로 하라.
    -기본 필터링 규칙을 deny로 설정하라.
    -모든 필터링 규칙을 제거한다. (ipfwadm의 -f 옵션사용)
    -새로운 필터링 규칙을 설정한다.
    -기본 필터링 규칙을 원하는 값으로 설정하라.

    IP traffic accounting

    리눅스에서 ipfwadm을 이용하면 받은 패킷과 보낸 패킷의 수를 셈하여 IP 트래픽을 계산할 수 있다. 로컬 호스트이 IP주소가 192.168.37.1 이고 WWW 서비스를 제공하고 있다면, 다음 명령은 이부로 부터의 WWW 서비스를 이용하는 사람들의 http 패킷을 계산한다. ==>more

      ipfwadm -A -a -d -W ethl -P tcp -D 192.168.37.1 www

    여기서 -A 옵션은 패캣의 수를 세(Account)는 옵션이고, -b옵션은 양방향(bi-directional)의 패킷 모두를 계산하라는 것이다. 192.168.37.1(80)로 들어오는 패킷이나 나가는 패킷의 수만을 계산하고 싶다면,

      ipfwadm -A in -a -W ethl -P tcp -D 192.168.37.1 www
      ipfwadm -A out -a -W ethl -P tcp -S 192.168.37.1 www

    위 명령과 같이 -A 옵션 다음에 방향(in/out)을 지정하고 목적지 또는 발송지 주소를 적어 줌으로서 원하는 패킷의 수만을 계산할 수 있다. -W ethl 은 인터페이스를 의미한다.

    기타 다른 옵션들에 대해서는 man ipfwadm 해서 찾아보기 바란다. 알짜 래드햇의 경우 번역된 맨 페이지가 나오므로 쉽게 알 수 있을 것이다.

    참고 : 정보보안.네트워크.IT자격증 정보공유사이트


, .
psql에서 \dT+라고 치면 아래 표가 나온다.

Schema

NameInternal nameSizeDescription
pg_catalog"any"any4
pg_catalog"char"char1single character
pg_catalogabstimeabstime4absolute, limited-range date and time (Unix system time)
pg_catalogaclitemaclitem12access control list
pg_cataloganyarrayanyarrayvar
pg_cataloganyelementanyelement4
pg_cataloganyenumanyenum4
pg_cataloganynonarrayanynonarray4
pg_catalogbigintint88~18 digit integer, 8-byte storage
pg_catalogbitbitvarfixed-length bit string
pg_catalogbit varyingvarbitvarvariable-length bit string
pg_catalogbooleanbool1boolean, true/false
pg_catalogboxbox32geometric box (lower left,upper right)
pg_catalogbyteabyteavarvariable-length string, binary values escaped
pg_catalogcharacterbpcharvarchar(length), blank-padded string, fixed storage length
pg_catalogcharacter varyingvarcharvarvarchar(length), non-blank-padded string, variable storage length
pg_catalogcidcid4command identifier type, sequence in transaction id
pg_catalogcidrcidrvarnetwork IP address/netmask, network address
pg_catalogcirclecircle24geometric circle (center,radius)
pg_catalogcstringcstringvar
pg_catalogdatedate4ANSI SQL date
pg_catalogdouble precisionfloat88double-precision floating point number, 8-byte storage
pg_cataloggtsvectorgtsvectorvarGiST index internal text representation for text search
pg_cataloginetinetvarIP address/netmask, host address, netmask optional
pg_catalogint2vectorint2vectorvararray of int2, used in system tables
pg_catalogintegerint44-2 billion to 2 billion integer, 4-byte storage
pg_cataloginternalinternal4
pg_catalogintervalinterval16@ <number> <units>, time interval
pg_cataloglanguage_handlerlanguage_handler4
pg_cataloglineline32geometric line (not implemented)
pg_cataloglseglseg32geometric line segment (pt1,pt2)
pg_catalogmacaddrmacaddr6XX:XX:XX:XX:XX:XX, MAC address
pg_catalogmoneymoney8monetary amounts, $d,ddd.cc
pg_catalognamename6463-character type for storing system identifiers
pg_catalognumericnumericvarnumeric(precision, decimal), arbitrary precision number
pg_catalogoidoid4object identifier(oid), maximum 4 billion
pg_catalogoidvectoroidvectorvararray of oids, used in system tables
pg_catalogopaqueopaque4
pg_catalogpathpathvargeometric path (pt1,...)
pg_catalogpointpoint16geometric point (x, y)
pg_catalogpolygonpolygonvargeometric polygon (pt1,...)
pg_catalogrealfloat44single-precision floating point number, 4-byte storage
pg_catalogrecordrecordvar
pg_catalogrefcursorrefcursorvarreference cursor (portal name)
pg_catalogregclassregclass4registered class
pg_catalogregconfigregconfig4registered text search configuration
pg_catalogregdictionaryregdictionary4registered text search dictionary
pg_catalogregoperregoper4registered operator
pg_catalogregoperatorregoperator4registered operator (with args)
pg_catalogregprocregproc4registered procedure
pg_catalogregprocedureregprocedure4registered procedure (with args)
pg_catalogregtyperegtype4registered type
pg_catalogreltimereltime4relative, limited-range time interval (Unix delta time)
pg_catalogsmallintint22-32 thousand to 32 thousand, 2-byte storage
pg_catalogsmgrsmgr2storage manager
pg_catalogtexttextvarvariable-length string, no limit specified
pg_catalogtidtid6(block, offset), physical location of tuple
pg_catalogtime with time zonetimetz12hh:mm:ss, ANSI SQL time
pg_catalogtime without time zonetime8hh:mm:ss, ANSI SQL time
pg_catalogtimestamp with time zonetimestamptz8date and time with time zone
pg_catalogtimestamp without time zonetimestamp8date and time
pg_catalogtintervaltinterval12(abstime,abstime), time interval
pg_catalogtriggertrigger4
pg_catalogtsquerytsqueryvarquery representation for text search
pg_catalogtsvectortsvectorvartext representation for text search
pg_catalogtxid_snapshottxid_snapshotvartxid snapshot
pg_catalogunknownunknownvar
pg_cataloguuiduuid16UUID datatype
pg_catalogvoidvoid4
pg_catalogxidxid4transaction id
pg_catalogxmlxmlvarXML content

네트워크 주소를 저장하는 용도인 cidr, inet, macaddr 같은 것도 있고, box, circle, line, path, point, polygon 같은 기하학적 값들을 위한 자료형도 존재한다.

눈여겨 볼 것은, regclassregtype 같은 고차 자료형(higher-order types)—자료형을 담는 자료형, Ruby의 Class 객체나 Python의 type 객체 같은 것들—도 존재한다는 사실. regoper, regproc 같은 자료형을 보면 알 수 있겠지만, 연산자나 프로시져를 값으로 사용할 수 있기도 하다. regoperator, regprocedure 자료형은 부분 적용(partial application)된 연산자, 프로시져를 담는다.

이 얼마나 강력하고 유연한 ORDBMS인가.

출처 : http://blog.dahlia.pe.kr/articles/2008/08/29

, .
* 개요
이 문서는 80포트만이 열린 네트워크 환경에서 아파치(80포트)와 톰캣(8080포트)을 동시에 서비스 하기 위한

방법을 설명하고 있습니다.


* 시스템사항
Operating System: Ubuntu 9.04
Webserver: Apache 2.0.54
JDK: JDK 6.0
Servlet Container: Tomcat 6.0.18
Tomcat Connector: Jakarta Tomcat Connector mod_jk (not mod_jk2)


* 기본폴더
apache2 : /etc/apache2
tomcat_home : /usr/local/tomcat
java_home : /usr/lib/jvm/java-6-sun/
mod_jk : /usr/lib/apache2/modules/mod_jk.so


* 설정 순서

1. mod_jk 설치
2. workers.properties 파일 만들기
3. 톰캣의 server.xml 수정
4. apache2.conf 수정
5. VirtualHost 설정 파일 수정
==== 이하 Option ====
6. 톰캣의 context 문제점 해결방법
7. 가상호스팅


* 설정설명
1. mod_jk 설치

$ sudo apt-get install libapache2-mod-jk



2. workeers.properties 파일 만들기


$ sudo vi /etc/apache2/jk/workers.properties


#아래의 두 설정의 경로는 자신에 맞게 수정합니다.
workers.tomcat_home=/usr/local/tomcat
workers.java_home=/usr/lib/jvm/java-6-sun/

ps=/

#아래 반복되는 worker1 대신 다른 이름도 상관없습니다.
worker.list=worker1

worker.worker1.port=8009
worker.worker1.host=localhost
worker.worker1.type=ajp13
worker.worker1.lbfactor=1

worker.loadbalancer.type=lb
worker.loadbalancer.balanced_workers=worker1

worker.inprocess.type=jni
worker.inprocess.class_path=$(workers.tomcat_home)$(ps)lib$(ps)tomcat.jar
worker.inprocess.cmd_line=start
worker.inprocess.jvm_lib=$(workers.java_home)$(ps)jre$(ps)lib$(ps)
i386$(ps)classic$(ps)libjvm.so

worker.inprocess.stdout=$(workers.tomcat_home)$(ps)logs$(ps)inprocess.stdout
worker.inprocess.stderr=$(workers.tomcat_home)$(ps)logs$(ps)inprocess.stderr



3. 톰캣의 server.xml 수정
톰캣/conf/server.xml 에 아래의 코드를 추가합니다.


$ su vi /usr/local/tomcat/conf/server.xml



Server 태그 바로 아래쪽에 Listener 태그가 적힌곳에 추가하면 됩니다.
modjk와 workersConfig의 경로에 주의해서 자신에 맞게 수정합니다.

<Listener className="org.apache.jk.config.ApacheConfig" modJk="/usr/lib/apache2/modules/mod_jk.so" workersConfig="/etc/apache2/workers.properties" />



톰캣을 재시작 합니다.


$ su /etc/init.d/tomcat restart


에러없이 재시작 되면 아래와 같은 파일이 생성됩니다.
/usr/local/tomcat/conf/auto/mod_jk.conf


4. apache2.conf 수정
/etc/apache2/apache2.conf 설정 파일에 아래의 코드를 추가합니다. 항상 경로에 신중하세요.


$vim /etc/apache2/apache2.conf


Include /usr/local/tomcat/conf/auto/mod_jk.conf
# Select the log format
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories

# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"

# Globally deny access to the WEB-INF directory
<LocationMatch '.*WEB-INF.*'>
AllowOverride None
deny from all
</LocationMatch>



위의 코드에서 LocationMatch '.*WEB-INF.*' 부분이 중요합니다.
jsp에서 WEB-INF의 내용은 여러가지 설정을 갖고있는 중요한 부분이므로 볼 수 없도록 막아놓습니다.


5. VirtualHost 설정 파일 수정
이 문서는 /etc/apache2/sites-available/default 파일에 VirtualHost가 설정되어 있습니다.
파일을 열어보면 <VirtualHost *> 부분이 있습니다.


$ vi /etc/apache2/sites-available/default



이 부분의 DocumentRoot를 수정합니다.(경로주의)
DocumentRoot /usr/local/tomcat/webapps/ROOT

그리고 아래의 코드를 VirtualHost 태그사이에 추가해줍니다.(worker1에 주의)

JkMount /*.html worker1
JkMount /*.swf worker1
JkMount /*.jsp worker1



JkMount는 패턴에 해당하는 파일 또는 디렉토리를 톰캣이 처리하도록 하는 설정입니다.

파일을 저장하고 아파치를 재시작 합니다.
브라우저에서 http://127.0.0.1로 접속했을때 톰캣 페이지가 보이면 연동된 것입니다.


6. 톰캣의 context 문제점 해결방법
톰캣의 context가 문제가 될 수 있습니다.
컨텍스트의 경로는 DocumentRoot의 하위디렉토리인 것이 아니기 때문입니다.
아파치의 Alias 설정을 통해 문제를 해결 할 수 있습니다.
톰캣의 admin을 예로 들면 아래와 같습니다.

Alias /admin/ "/opt/tomcat/server/webapps/admin/"

JkMount /admin worker1
JkMount /admin/* worker1



7. 가상호스팅
아파치, 톰캣 각각의 가상호스팅 설정에 대해서 자세히 다루지는 않겠습니다.
아파치, 톰캣 두개의 가상호스팅 설정이 일치해야 연동에 문제가 없습니다.

아파치의 가상호스팅
<VirtualHost test.ani>
DocumentRoot /var/www/test
JkMount /*.jsp worker1
</VirtualHost>

톰캣의 가상호스팅(server.xml)
<Host appBase="/var/www/test" name="test.com">
<Context path="" docBase="" debug="0" reloadable="true"/>
</Host>

, .