공개 데이터베이스 서버 PostgreSQL (4)
-libpq를 이용한 어플리케이션 작성

한동훈 ddoch@hitel.kol.co.kr

1. PostgreSQL의 C 인터페이스 - libpq

    지금까지는 PostgreSQL의 SQL특징을 살펴보았다. 이제부터는 SQL 외의 기능들을 중점으로 살펴보도록 하겠다. 그 첫 번째로서 PostgreSQL의 C 인터페이스인 libpq에 대해서 알아보도록 하자.

    libpq는 말그대로 PostgreSQL의 데이터베이스를 다루는데 사용되는 C 라이브러리이다. 사실 사용자 프로그램인 psql도 libpq를 사용하여 작성한 어플리케이션의 일종이다. 만들기에 따라서 psql 보다 더 뛰어난 사용자 프로그램을 얼마든지 만들 수 있다.

    PostgreSQL을 표준으로 설치하였다면, libpq는 /usr/local/pgsql/lib안에 모여있을 것이다. 이 디렉토리 안의 libpq.a는 정적 라이브러리이고 libpq.so.1은 동적 라이브러리이다. 설치 시에 /ect/ld.so.conf 파일에 /usr/local/pgsql/lib를 추가하고 ldconfig를 수행한 경험이 있을 것이다. 이것은 PostgreSQL 응용 프로그램 수행에 필요한 libpq 동적 라이브러리를 자동적으로 링크하기 위해 필요한 작업이었다. 조금 벗어나는 이야기지만 PHP/FI에서 PostgreSQL을 사용하도록 설정하였는데, httpd를 띄울 때 libpq.so를 찾지 못하겠다는 메시지가 나온다면 libpq.so.1을 libpq.so로 심볼릭 링크하고 /etc/ld.so.conf 파일에 /usr/local/pgsql/lib을 추가한 다음, ldconfig를 수행하면 된다. libpq에 대한 온라인 설명은 'man libpq'로 찾아볼 수 있고, 예제 소스 파일은 PostgreSQL 소스 파일의 압축을 푼 디렉토리를 기준으로 볼 때, src/test/examples 디렉토리에 있다. 이제 본론으로 들어가보자.

2. libpq 의 함수들

    libpq는 PostgreSQL의 어플리케이션 프로그래밍 인터페이스이다. libpq는 클라이언트 프로그램이 PostgreSQL 백엔드 서버에게 질의를 전달하고 결과를 회수하는 역할을 하는 라이브러리이다. libpq를 사용하는 프론트엔드 어플리케이션은 libpq-fe.h 헤더 파일을 포함하고 libpq 라이브러리와 링크되어야 한다는 점을 기억하자.
    먼저 libpq에 포함되어 있는 라이브러리 함수들을 살펴보도록 한다.

1) 데이터베이스 초기화와 제어와 관련한 환경변수

    Linux 환경의 다른 어플리케이션들과 마찬가지로 libpq에서도 초기화와 어플리케이션의 행동을 제어하기 위하여 환경변수를 사용한다. 다음의 환경변수의 값이 libpq에서 기본값으로 사용된다.

    PGHOST 기본 서버명을 지정한다.
    PGOPTIONS 벡엔드 서버를 위한 추가적인 실시간 옵션을 설정한다.
    PGPORT 벡엔드 서버와 통신할 기본 포트를 지정한다.
    PGTTY 벡엔드 서버가 출력하는 디버깅 메시지를
    처리할 파일이나 tty를 지정한다.
    PGDATABASE 기본 데이터베이스 명을 지정한다.
    PGREALM Kerberos 인증 시스템이 사용될 때에만
    설정한다.

2) 데이터베이스 접속 함수

    다음의 함수들은 데이터베이스 접속과 관련된 것들이다.

    PQsetdb
    서버와 새로운 연결을 만들어 준다.

    PGconn *PQsetdb(char *pghost,
    char *pgport,
    char *pgoptions,
    char *pgtty,
    char *dbName);

    인자가 NULL이면, 해당하는 환경변수를 검사하고, 환경변수가 설정되지 않았다면 내부 기본 설정값을 사용한다. 이 함수는 항상 유효한 PGconn 포인터를 반환하는데, PQstatus를 사용하여 질의를 서버로 보내기 전에 연결이 확실히 성립되었는지를 검사할 수 있다. libpq 사용자는 PGconn을 관리하는데 유의해야 한다. PGconn 구조체는 미래에 변경될 수도 있기 때문에 직접 구조체의 필드를 참조하는 것은 피하는게 좋다.

    예)
    PGconn *conn;
    /* 192.168.1.2 호스트의 5432 포트를 통하여 web 데이터베이스에 접속한다. */
    PQsetdb("192.168.1.2", "5432", NULL, NULL, "web");

    PQdb
    현재 연결된 데이터베이스의 이름을 반환한다.

    char *PQdb(PGconn *conn)

    PQhost
    현재 연결되어 있는 서버의 호스트 이름을 반환한다.

    char *PQhost(PGconn *conn)

    PQoptions
    연결시에 사용된 옵션이 무엇이었는지를 알려준다.

    char *PQoptions(PGconn *conn)

    PQport
    연결된 포트 번호를 반환한다.

    char *PQport(PGconn *conn)

    PQtty
    연결된 tty를 반환한다.

    char *PQtty(PGconn *conn)

    PQstatus
    연결된 상태에 대한 정보를 알려준다. 상태값은 CONNECTION_OK나 CONNECTION_BAD가 될 수 있다.

    ConnStatusType *PQstatus(PGconn *conn)


    예)
    /* 접속이 실패하면 에러메시지를 출력하고 종료한다. */
    if (PQstatus(conn) == CONNECTION_BAD) {
    fprintf(stderr, "Connection to database '\s' failed.\n", dbname);
    fprintf(stderr, "\s", PQerrorMessage(conn));
    PQfinish(conn);
    exit(1);
    }

    PQerrorMessage
    연결시에 발생한 에러 메시지를 알려준다.

    char *PQerrorMessage(PGconn *conn);

    PQfinish
    서버와의 접속을 종료한다. 또한 PGconn 구조체에 사용된 메모리를 반환한다. PQfinish를 호출한 이후에는 PGconn 포인터를 사용하지 말아야 한다.

    void PQfinish(PQconn *conn)

    PQreset
    서버와의 접속 포트를 리셋한다. 즉, 서버에 연결된 IPC 소켓을 닫고 동일한 서버에 접속을 새롭게 시도한다.

    void PQreset(PGconn *conn)

    PQtrace
    서버와 프론트엔드 사이에 오가는 메시지를 추적한다. 추적 메시지는 debug_port 파일 스트림으로 출력된다.

    void PQtrace(PGconn *conn, FILE* debug_port);

    PQuntrace
    서버와 프론트엔드 사이에 주고받는 메시지 추적을 취소한다.

    void PQuntrace(PGconn *conn);

3) 질의 실행 함수

    PQexec
    질의를 서버에 전달하는 역할을 한다. 질의가 성공적으로 수행되면 PGresult 포인터를 돌려주고, 그렇지 않으면 NULL을 돌려준다. NULL이 반환되면, PQerrorMessage를 사용하여 해당 에러에 대한 좀 더 자세한 정보를 얻을 수 있다.

    PGresult *PQexec(PGconn *conn, char *query);


    PGresult 구조체에는 서버가 반환한 질의 결과가 들어있다. 프로그래머는 PGresult를 조심스럽게 관리해야 할 필요성이 있다. 질의 결과를 회수하는 데 사용되는 접근 함수를 아래에 설명한다. PGresult 구조체는 PGconn 구조체와 마찬가지로 앞으로 언제든지 변할 가능성이 있기 때문에 직접 구조체의 필드를 참조하는 것은 피하기 바란다.

    PQresultStatus
    질의 결과 상태를 알려준다.

    ExecStatusType PQresultStatus(PGresult *res);

    반환값인 ExecStatusType은 다음 중 하나가 될 수 있다.

    PGRES_EMPTY_QUERY : 질의가 비어 있는 경우
    PGRES_COMMEND_OK : 값을 반환하지 않는 질의 명령인 경우
    PGRES_TUPLES_OK : 질의가 성공적으로 수행되어서 튜플을 반환한 경우
    PGRES_BAD_RESPONSE : 서버로부터 기대하지 않은 응답을 받은 경우
    PGRES_NONFATAL_ERROR : 치명적이지 않은 에러가 발생한 경우
    PGRES_FATAL_ERROR : 치명적인 에러가 발생한 경우
    PGRES_COPY_OUT
    PGRES_COPY_IN


    결과의 상태값이 PGRES_TUPLES_OK이면 반환된 튜플을 회수하기 위해 다음의 함수들을 사용할 수 있다.

    PQntuples
    질의 결과의 튜플(인스턴스 또는 레코드, 로우)의 개수를 반환한다.

    int PQntuples(PGresult *res);

    PQnfields
    필드의 개수를 반환한다.

    int PQnfields(PGresult *res);

    PQfname
    지정하는 필드 인덱스와 관련된 필드(속성) 이름을 돌려준다. 필드 인덱스는 0에서부터 시작한다.

    char *PQfname(PGresult *res, int field_index);

    PQfnumber
    주어진 필드 이름의 인덱스 번호를 리턴한다.

    int PQfnumber(PGresult *res, char *field_name);

    PQftype
    저장하는 필드 인덱스의 필드 타입을 돌려준다. 반환되는 정수값은 내부적으로 정의되어 있는 text, int4 등을 나타내는 값이다.

    Oid PQftype(PGresult *res, int field_num);

    PQfsize
    지정하는 필드 인덱스와 관련된 필드의 크기를 바이트 수로 돌려준다. 반환된 크기가 -1이면 가변 길이의 필드임을 나타낸다.

    int PQfsize(PGresult *res, int field_index);

    PQgetvalue
    필드의 이름을 지정하면 그 필드의 값을 돌려준다. PQgetvalue에서 반환되는 값은 필드의 값을 널로 끝나는 아스키 문자열로 변환한 값이다. 질의가 바이너리(BINARY) 커서일 경우에 반환되는 값은 서버의 내부적인 포맷의 바이너리 타입이다. 이 경우에 해당 데이터를 올바른 C타입으로 변환해야 한다. PQgetvalue가 반환하는 값은 PGresult 구조체의 해당 필드에 대한 포인터이므로 PGresult를 해제하고 난 다음에도 사용하려면 그 값을 복사해 둬야 한다.

    char *PQgetvalue(PGresult *res, int tup_num, int field_num);

    PQgetlength
    필드의 길이를 바이트 수로 돌려준다.

    int PQgetlength(PGresult *res, int tup_num, int field_num);

    PQcmdStatus
    마지막 질의 명령과 관련된 명령어 상태를 돌려준다.

    char *PQcmdStatus(PGresult *res);

    PQoidStatus
    마지막으로 수행한 질의가 INSERT 명령일 때, 삽입된 튜플의 객체 아이디를 문자열로 돌려준다. 그 외에는 빈 문자열을 돌려준다.

    char *PQoidStatus(PGresult *res);

    PQdisplayTuples
    임의의 모든 레코드를 출력한다. 선택적으로 타이틀격인 속성 이름의 출력여부와 출력 스트림을 지정할 수 있다. 대표적으로 psql이 테이블의 내용을 출력하기 위해 PQdisplayTuples 함수를 사용한다.

    void PQprintTuples(
    PGresult *res,
    FILE *fout, /* 출력결과를 보낼 파일 스트림 */
    int fillAlign, /*필드를 정렬하기 위해 빈부분을 공백으로 채움여부*/
    char *fieldSep, /* 필드 구분자로 사용할 문자열, 일반적으로 '|'를 사용 */
    int printHeader, /* 헤더의 출력 여부 */
    int quit
    );

    PQclear
    PGresult와 관련된 내용을 해제하는 역할을 한다. 질의 결과가 더 이상 필요 없을 시에는 반드시 해제하여야 한다. 이렇게 하지 않으면 프론트엔드 응용프로그램에서 메모리가 유출되는 결과를 낳는다.

4) COPY 질의와 관련된 함수

    PQgetline
    이 함수는 뉴라인으로 끝나는 문자열을 읽어서 string 버퍼에

    void PQclear(PQresult *res);


    저장한다. fgets와 비슷하게, length-1 문자를 string으로 복사하고, 마지막의 뉴라인 문자를 널 문자로 변환한다는 것이 gets와는 다른 점이다.

    int PQgetlline(PQconn *conn, char *string, int length);


    PQgetline은 읽어 들이는 도중에 EOF를 만나면 EOF를 반환하고, 전체 라인을 읽어들였다면 0을, 뉴라인을 읽기도 전에 버퍼가 차버리면 1을 반환한다. 어플리케이션에서는 하나의 뉴라인이 "."로 입력되는 지를 검사하여야 한다. 이 문자는 서버가 copy 명령의 결과 전송을 종료한다는 것을 뜻한다. 그리고 length-1 보다 큰 길이의 라인을 읽어들일 수도 있으므로 PQgetline의 반환값을 체크하여야 한다.

    PQputline
    이 함수는 널로 끝나는 string을 서버에 전송한다. 어플리케이션에서는 마지막 문자 "."를 데이터 전송이 완료되었다는 것을 서버에게 정확히 알리기 위해 보내야 한다.

    void PQputline(PGconn *conn, char *string);

    PQendcopy
    서버가 copy를 끝낼 때까지 응용 프로그램이 기다리도록 한다. 이 함수는 PQputline을 사용하여 서버에게 마지막 문자열을 보냈을 경우나 PQgetline을 사용하여 서버에게서 마지막 문자열을 회수하였을 경우에 사용한다. 이 함수의 반환값에 따라, 서버는 다음 질의를 받아들일 준비를 한다. 성공적으로 수행되었다면 0을, 그 외에는 0이 아닌 값을 반환한다.

    int PQendcopy(PGconn *conn);

    참고로 PostgreSQL에서는 질의 버퍼가 8192 바이트 길이이므로, 8K를 넘는 질의의 뒷부분은 잘려나가므로 주의하기 바란다. 물론 그럴 경우는 없겠지만...

    예)
    PQexec(conn, "create table foo (a int4, b char16, d float8)");
    PQexec(conn, "copy foo from stdin");
    /* 여기에서 <TAB>은 실제로 TAB키를 입력함을 이야기한다. */
    PQputline(conn, "3<TAB>hello world<TAB>4.5\n");
    PQputline(conn, "4<TAB>goodbye world<TAB>7.11\n");
    PQputline(conn, ".\n");
    PQendcopy(conn);


3. libpq의 활용

    지금까지 libpq의 라이브러리 루틴을 살펴보았다. 이제 예제 프로그램을 살펴보도록 하자.

1) 예제 프로그램 - testlibpq.c

    여기에서는 먼저 PostgreSQL에서 제공하는 괜찮은 예제 소스를 먼저 살펴본다. 이 파일은 src/test/examples/testlibpq.c 파일이다. 먼저 컴파일을 하여 실행을 해보자.

    $ make
    $ ./testlibpq
    datname datdba datpath

    template1 501 template1
    ddoch 501 ddoch
    test3 501 test3
    mydb 501 mydb
    web 506 web
    test 501 test

    이 프로그램은 임시 데이터베이스로 사용되는 template1 데이터베이스에 접속하여 시스템 카탈로그의 일종인 pg_database 클래스에서 데이터베이스 목록을 축출하여 출력하고 있다. 다음은 소스 프로그램이다. 자세한 주석을 붙여놓았다.

    /*
    * testlibpq.c
    * PostgreSQL 프론트엔드 라이브러리인 LIBPQ를 사용한 테스트 프로그램
    *
    *
    */

    #include <stdio.h>
    #include "libpq-fe.h" /* LIBPQ를 사용하는 프로그램에서 꼭 포함해야 한다. */

    static void
    exit_nicely(PGconn *conn)
    {
    PQfinish(conn);
    exit(1);
    }

    int
    main()
    {
    char *pghost, *pgport, *pgoptions, *pgtty;
    char *dbName;
    int nFields; /* 필드의 갯수를 저장할 변수 */
    int i,j;

    #ifdef DEBUG
    FILE *debug; /* 디버깅을 위한 파일 스트림 */
    #endif /* DEBUG */

    PGconn *conn; /* 데이터베이스 접속 디스크럽트 구조체 */
    PGresult *res; /*질의 결과를 저장할 PGresult 구조체 포인터*/

    /*
    * 먼저, 서버 접속을 위해서 매개인자를 설정한다.
    * 매개인자가 널이면, 환경변수를 검사하고,
    * 환경변수가 설정되어 있지 않으면 시스템 기본 내정값을 사용한다.
    */

    pghost = NULL; /* 서버의 호스트 이름 */
    pgport = NULL; /* 서버 포트 */
    pgoptions = NULL; /* 서버로 전달할 특별한 옵션 */
    pgtty = NULL; /* 서버를 위한 디버깅 tty */
    dbName = "template1"; /* 접속할 임시 데이터베이스 */

    /* 데이터베이스로 접속을 시도한다. */
    conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbName);

    /* 서버와의 접속이 성공적으로 이루어졌는지 검사한다.
    * 만일 실패하였다면 에러 메시지를 출력하고 종료한다. */
    if (PQstatus(conn) == CONNECTION_BAD)
    {
    fprintf(stderr, "Connection to database '\s' failed.\n", dbName);
    fprintf(stderr, "\s", PQerrorMessage(conn));
    exit_nicely(conn);
    }

    #ifdef DEBUG
    /* 디버깅 파일 스트림을 열고 추적을 시작한다. */
    debug = fopen("/tmp/trace.out", "w");
    PQtrace(conn, debug);
    #endif /* DEBUG */

    /* 트랜잭션 블록을 시작한다. 모든 작업은 트랜잭션 구문안에서
    * 이루어져야 한다. */
    res = PQexec(conn, "BEGIN");
    if (PQresultStatus(res) ! = PGRES_COMMAND_OK)
    {
    fprintf(stderr, "BEGIN command failed\n");
    PQclear(res);
    exit_nicely(conn);
    }

    /*
    * 메모리 유출을 막으려면 더 이상 필요하지 않는 PGresult를
    * PQclear 해야 한다.
    */
    PQclear(res);

    /*
    * 데이터베이스의 시스템 카탈로그인 pg_database 클래스에서 모든
    * 데이터베이스 항목을 얻어서 커서를 선언한다. */

    res = PQexec(conn, "DECLARE myportal CURSOR FOR select *from pg_database");
    if (PQresultStatus(res) ! = PGRES_COMMAND_OK)
    {
    fprintf(stderr, "DECLARE CURSOR command failed\n");
    PQclear(res);
    exit_nicely(conn);
    }
    PQclear(res);

    /* 선언한 커서에서 데이터를 모두 불러들인다. */
    res = PQexec(conn, "FETCH ALL in myportal");
    if (PQresultStatus(res) ! = PGRES_TUPLES_OK)
    {
    fprintf(stderr, "FETCH ALL command didn't return tuples properly\n");
    PQclear(res);
    exit_nicely(conn);
    }

    /* 먼저 필드 헤더를 출력한다. */
    nFields = PQnfields(res);
    for (i = 0; i<nFields;i++)
    {
    printf("\-15s", PQfname(res, i));
    }
    printf("\n\n");

    /* 다음으로 인스턴스 전체를 레코드 수와 필드 수만큼 출력한다. */
    for (i = 0; i<PQntuples(res); i++)
    {
    for (j = 0; j<nFields; j++)
    {
    printf("\-15s", PQgetvalue(res, i, j));
    }
    printf("\n");
    }

    PQclear(res);

    /* 커서를 닫는다. 커서가 더 이상 필요없으면 커서를 닫아야 한다. */
    res = PQexec(conn, "CLOSE myportal");
    PQclear(res);

    /* 트랜잭션을 끝낸다. */
    res = PQexec(conn, "END");
    PQclear(res);

    /* 데이터베이스 접속을 종료하고 정리한다. */
    PQfinish(conn);

    #ifdef DEBUG
    fclose(debug); /* 디버깅 추적을 중단한다. */
    #endif /* DEBUG */

    exit(0);
    }

2) 간이 SQL 모니터링 프로그램

    간단한 예제 프로그램을 살펴보았으므로, 이제 조금 더 색다른 프로그램을 작성해보자. 앞서와 별다를 바야 없지만 psql과 비슷한 간이 SQL 모니터링 프로그램을 작성해보겠다. psql도 사실은 libpq를 이용하여 작성한 응용 프로그램이라는 것을 앞서 설명한 바 있다. 여기에 나오는 spsql 프로그램은 psql의 기본적인 기능을 모방하여 작성한 것으로, 내부 구조는 먼저 localhost에 접속하여 pg_database 카탈로그에서 전체 데이터베이스 목록을 축출하여 사용자에게 보여주고, 사용자가 선택한 데이터베이스에 다시 접속하여 사용자의 입력을 받아서 서버에게 전달하고, 서버에서 결과를 돌려받을 경우 그것을 출력한다.

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ctype.h>

    #include "libpq-fe.h" /* libpq 어플리케이션에 필요한 헤더파일 */

    void main() {

    char *pghost = NULL;
    char *pgport = NULL;
    char *pgoptions = NULL;
    char *pgtty = NULL;
    char *dbname = NULL;
    int nTuples;
    int i, j;
    PGconn *conn;
    PGresult *res;

    FILE *debug; /* 디버깅 스트림 */

    char database〔256〕;
    char buf〔2048〕, query〔2048〕;

    /* 기본값으로 데이터베이스에 접속한다. */
    conn = PQsetdb(pghost, pgport, pgoptions, pgtty, dbname);

    if (PQstatus(conn) == CONNECTION_BAD) {
    fprintf(stderr, "Connection to database '\s' failed.\n", dbname);
    fprintf(stderr, "\s", PQerrorMEssage(conn));
    PQfinish(conn);
    exit(1);
    }

    /* 디버깅 정보를 얻기 위해 스트림으로 연결한다. */
    debug = fopen("/tmp/trace.out", "w");
    PQreace(conn, debug);



, .

공개용 데이터베이스 서버 PostgreSQL (3)

한동훈 (하이텔 리눅스동 대표시삽)

1. 들어가면서

    지난 시간에는 PostgreSQL의 설치와 간단한 테스트에 이어, PostgreSQL의 사용을 도와주는 외부 명령어를 살펴보았다. 이번 시간에는 PostgreSQL에서 제공하는 데이터 타입과 데이터베이스 질의어인 SQL에 대해서 살펴보겠다.

    PostgreSQL에서의 데이터 타입은 상당히 많고 폭이 넓다. 생각도 하지 못한 듯한 데이터 타입들이 있으며, 공학용이나 복잡한 기하학 용도로 사용될 법한 데이터 타입들도 준비외어 있다. 물론, 이러한 데이터 타입조차도 사용자 마음대로 정의하여 사용할 수 있다는 장점이 있다.
    그리고 SQL 질의어도 마찬가지로 확장성이 뛰어나다. 사실, PostgreSQL 6.2에서 새롭게 지원하는 필드 제한, 보충 기능들과 트리거 기능은 이전 버전에서도 PostgreSQL의 SQL확장을 사용하여 해결할 수 있었다. 이제 본격적으로 PostgreSQL의 기능들을 마음껏 누려 보도록 하자.

2. 데이터 타입

    PostgreSQL 6.2에서 제공하는 터이터 타입은 다음과 같다.

1) 기본 데이터 타입

    abstime : 절대 날짜와 시간
    aclitem : 엑세스 제어 목록 아이템
    bool : 부울런(논리) 값
    box : 2차원 사각형
    bpchar : 공백 채움 문자
    bytea : 가변길이의 바이트 배열
    char : 문자
    char2 : 2 문자의 배열
    char4 : 4 문자의 배열
    char8 : 8문자의 배열
    char16 : 16문자의 배열
    cid : 명령 식별 타입
    date : ANSI SQL 데이터 타입
    datetime : 범용 날짜와 시간
    filename : 거대 객체의 파일이름
    int2 : 부호있는 2바이트 정수
    int28 : int2의 8 배열
    int4 : 부호있는 2바이트 정수
    float4 : 단정도 부동소수
    float8 : 배정도 부동소수
    lseg : 2차원 선 구간
    money : 고정 정밀도를 가지는 십진수 타입
    name : 저장 시스템 식별자를 위한 다중 문자 타입
    oid : 객체 식별자 타입
    oid8 : oid의 8 배열
    oidchar16 : oid 와 char16의 조합
    oidint2 : oid 와 int2의 조합
    oidint4 : oid 와 int4의 조합
    path : 열렸거나 닫혀진 선 구간
    point : 2차원 기하학 점
    polygon : 2차원 다각형 (닫혀진 path와 동일)
    circle : 2차원 원 (중심점과 반경)
    regproc : 등록된 프로시저
    reltime : 상대 날짜와 시간 간격
    smgr : 저장 관리자
    text : 가변길이의 문자 배열
    tid : 튜플 식별자 타입
    time : ANSI SQL 시간 타입
    timespan : 범용 시간 간격
    timestemp : 제한 범위 ISO형식의 날짜와 시간
    tinterval : 시간 간격(절대시작시각과 절대종료시각)
    varchar : 가변길이의 문자들
    xid : 트랜잭션 식별자 타입

2) SQL/92와 PostgreSQL 의 비교

    SQL/92 타입과 동일한 PostgreSQL 데이터 타입은 다음과 같다.

    PostgreSQL 타입

    SQL/92 타입

    의미

    char(n)

    charater(n)

    고정 길이 문자열

    varchar(n)

    charater varying

    가변 길이 문자열

    float4/8

    float(p)

    정밀도 p를 가지는 부동 소수

    float8

    double precision

    배정도 부동 소수

    float8

    real

    배정도 부동 소수

    int2

    smallint

    부호있는 2바이트 정수

    int4

    int

    부호있는 4바이트 정수

    int4

    intgre

    부호있는 4바이트 정수

    int4

    decimal(p,s)

    p<=9, s=0 인 수치

    int4

    numeric(p,s)

    p==9, s=0 인 수치

    timestemp

    time zone이 있는
    timestemp

    날짜와 시간

    timestemp

    interval

    범용 시간 간격

3) 날짜/시간 데이터 타입

    위에서 나열한 날짜/시간 데이터에 대해서 잠깐 살펴보자

    datetime
    이 데이터 타입은 범용 날짜/시간타입으로 다양한 형태의 입력을 허용한다. 입력 형태는 ISO호환, SQL 호환, PostgreSQL 방식이 될 수 있다. 출력형태는 set 명령을 사용하여 다양하게 조절할 수 있다. datetime 타입은 다음의 문법을 사용한다.

    년-월-일 [시:분:초] [AD, BC] [Timezone]

    년월일 [시:분:초] [AD, BC] [Timezone]

    월일 [시:분:초]년[AD, BC] [Timezone]

    유효한 날짜는 Nov 13 00:00:00 4013 BC GMT에서 미래까지이다. 시간 지대는 GMT, PST, KST 와 같은 세문자짜리이다. 시간은 GMT(Greenwich Mean Time) 로 저장되며, 입출력 함수는 이들 시간을 서버의 지역시간으로 변환한다.
    그리고 값 지정시에 current, infinity, -infinity을 사용할 수 있다.

    current는 값이 계산 될 때의 현재 시간이며, infinity는 유효 시간 이후를 말하며, -infinity는 유효시간 이전을 뜻한다. 그 외에도 now, today, yesterday, tomorrow, epoch를 사용할 수 있다. current는 항상 계산 현재시간으로 변경되지만 now, today 등은 바로 계산되어 상수로 저장된다는 점에서 다르다. epoch는 jan 1 00:00:00 1970 GMT을 이야기한다.

    mydb=> create table test_datetime (dt datrtime);
    CREATE
    mydb=> insert into test_datetime values ('1997-11-24 21:17:00 KST);
    INSERT 535342 1
    mydb=> select *from test_datetime;
    dt
    -----------------
    Mon Nov 24 21:17:00 1997 KAT
    (1 row)

    mydb=> \h set
    Command: set
    Description: set run-time environment
    Syntax:
    set DateStyle to {'ISO' | 'SQL' | 'Postgres' | 'European' | 'US' | 'NonEuropean'}
    set GEQO to {'ON[=#]' | 'OFF'}
    set R_PLANS to {'ON'| 'OFF'}

    mydb=>set DateStyle to 'SQL' ;
    SET VARIABLE
    mydb=> select *from test_datetime;
    dt
    ------------------
    11/24/1997 21:17:00.00 KST
    (1 row)

    timespan
    timespan은 범용 시간간격 데이터 타입이다. datetime과 마찬가지로 출력형식은 set 명령으로 다양하게 설정할 수 있다. 입력형식은 다음과 같다.

    Quantity Unit [Quantity Unit...] [Direction]

    Direction 에는 ago를 사용할 수 있다.

    mydb=> create table test_timespan (tt timespan);
    CREATE
    mydb=> insert into test_timespan values ('-10 hour');
    INSERT 535357 1
    mydb=> select *from test_timespan;
    tt
    ----------------
    @ 10 hours ago
    (1 row)

    abstime
    절대 시간(abstime 포함)은 제한된 범위(+/-68 년)와 초단위까지 저장할 수 있는 날짜 타입이다. 절대 시간은 다음의 형식을 따른다.

    Month Day [Hour : Minute : Second] Year [Timezone]

    유효날짜 범위는 Dec 13 20:45:53 1901 GMT에서 Jan 19 03:14:04 2038 GMT 까지 이며, 입출력 루틴은 기본적으로 지역시간지대를 따른다.

    mydb=> create table test_abstime (ta abstime);
    CREATE
    mydb=> insert into test_abstime values ('11 25 23:33:25 1997 PST');
    INSERT 535372 1
    mydb=> select *from test_abstime;
    tt
    ----------------
    11/26/1997 16:33:25.00 KST
    (1 row)

    reltime

    상대시간도 기본적으로 절대 시간과 유사하다. 시간의 범위는 +/-68년이며, 정밀도는 초단위까지이다. 형식은 다음과 같다.

    @ Quantity Unit [Direction]


    mydb=> create table test_reltime (tr reltime);
    CREATE
    mydb=> insert into test_reltime values ('@10 day ago');
    INSERT 535387 1
    mydb=> select *from test_reltime ;
    tr
    ----------------
    @10 day ago
    (1 row)

4) 기하학 데이터 타입

    point
    평면에서 하나의 점을 나타낼 때 사용되는 데이터 타입이다.
    데이터 형식은 다음과 같다.

    (x, y)
    x, y

    x와 y는 각각 평면에서 x축, y축 값을 나타내는 부동 소수이다.

    mydb=> create table test_point (p point);
    CREATE
    mydb=> insert into test_point values ('128, 215');
    INSERT 535438 1
    mydb=> insert into test_point values ('512, 640');
    INSERT 535439 1
    mydb=> insert into test_point values ('978.25, 1075.103');
    INSERT 535440 1
    mydb=> select *from test_point ;
    p
    ----------------
    (128, 215)
    (512, 640)
    (978.25, 1075.103)
    (3 rows)

    lseg
    선 구간은 점 두 개로 구성된다. lseg 데이터 타입은 다음 형식으로 지정한다.

    ((x1, y1) , (x2, y2))
    (x1, y1) , (x2, y2)
    x1, y1 , x2, y2

    (x1, y1)와 (x2, y2)는 선구간의 끝점이다.

    mydb=> create table test_lseg (line lseg);
    CREATE
    mydb=> insert into test_lseg values ('0,0,480,640');
    INSERT 535455 1
    mydb=> insert into test_lseg values ('(640,0), (0,480)');
    INSERT 535456 1
    mydb=> select *from test_lseg ;
    line
    ----------------
    [(0,0),(480,640)]
    [(640,0),(0,480)]
    (2 rows)

    box
    box 데이터 타입은 두 개의 점으로 구성된다. 즉, 두 점을 반대쪽 꼭지점으로 하는 사각형을 구성하는 것이다.
    box의 형식은 다음과 같다.

    ((x1, y1) , (x2, y2))
    (x1, y1) , (x2, y2)
    x1, y1 , x2, y2


    mydb=> create table test_box (사각형 box);
    CREATE
    mydb=> insert into test_box values ('0,0,640,480');
    INSERT 535471 1
    mydb=> select *from test_box ;
    사각형
    ----------------
    (640,480),(0,0)
    (1 row)

    path
    path 데이터 타입은 점의 집합으로 구성된다. 첫 번째 점과 마지막 점이 이어지지 않는다면 경로(path)는 open 이 되고, 이어진다면 close 가 된다. popen(p) 와 pclose(p)함수는 path데이터형 p를 강제로 open하거나 close 상태로 만든다. isopen(p)와 isclosed(p) 함수는 질의어에서 둘 중 하나를 선택하도록 한다.

    path 데이터의 입력 형식은 다음 중 하나르 사용할 수 있다.

    ((x1, y1) , ... , (xn, yn))
    [(x1, y1) , ... , (xn, yn)]
    (x1, y1),..., (xn, yn)
    (x1, y1 ,..., xn, yn)
    x1, y1 ,..., xn, yn


    (x1, y1),..., (xn, yn)은 1에서 n 까지의 점이다.
    "["는 열려진 경로를 나타낸다.
    "("는 닫혀진 경로를 나타낸다.

    mydb=> create table test_path (길 path);
    CREATE
    mydb=> insert into test_path values ('((10,10),(10,20),(20,30),(30,20),(30,10))');
    INSERT 535486 1
    mydb=> insert into test_path values ('[(10,10),(10,20),(20,30),(30,20),(30,10)]');
    INSERT 535487 1
    mydb=> select *from test_path ;

    ----------------
    ((10,10),(10,20),(20,30),(30,20),(30,10))
    [(10,10),(10,20),(20,30),(30,20),(30,10)]
    (2 row)

    mydb=> select isopen(길) from test_path
    isopen
    ----
    f
    t
    (2 rows)

    polygon
    다각형(polygon)도 path와 마찬가지로 여러 개의 점으로 구성된다. 다각형은 당연히 닫혀지는 것을 기본으로 해야 한다. 이것만 제외하면, 데이터 입력 형식은 path와 동일하다.

    circle
    원은 중심점과 반지름으로 구성된다. 형식은 다음 중 하나를 사용 할 수 있다.

    <(x, y), r >
    ((x, y), r )
    (x, y), r
    x, y, r

    (x, y)는 원의 중심점이고 r은 반지름이다.

    mydb=> create table test_circle (원 circle);
    CREATE
    mydb=> insert into test_circle values ('0,0, 100');
    INSERT 535502 1
    mydb=> select *from test_circle ;

    ----------------
    <(0,0), 100>
    (1 row)

    지금까지 데이터 타입을 살펴 보았다. 이들은 PostgreSQL 6.2 기준이다. path 나 polygon, circle 등은 PostgreSQL 버전에 따라 조금 다른점이 있으므로 주의 하여야 한다.
    이제 PostgreSQL에 내장되어 있는 오퍼레이터를 살펴보자. 조금 지겹더라도 기초를 튼튼히 하다보면 나중에 써멱을 떄가 반드시 있을 것이다. 그리고 자세히 알아두는 과정에서 PostgreSQL가 아주 재미있다는 것을 느낄 수 있을 것이다.

3. 내장 오퍼레이터와 함수

1)오퍼레이터

    PostgreSQL 는 정말 많은 양의 내장 오퍼레이터를 제공한다. 이들 오퍼레이터는 pg_operator 시스템 카탈로그에 해당 오퍼레이터를 구현하는 프로시저의 객체 ID와 함께 정의되어 있다.

    mydb=>select count(*) from pg_operator:
    count
    -----
    488
    (1 row)

    488개의 오퍼레이터가 내장되어 있음을 알 수 있다. 나중에 오퍼레이터를 정의하는 방법을 설명할 때 알 수 있겠지만, PostgreSQL 에서는 대상 객체 타입에 따라 오퍼레이터가 다른 의미를 가진다. 즉 '<' 오퍼레이터라 하더라도 여러 데이터 타입에 작동하도록 할 수 있으며, 이 경우에는 PostgreSQL 에 해당 데이터 타입이 정의되어 있거나, 사용자가 정의하여야 한다. 즉, 형식적으로는 '>'와 같은 오퍼레이터 모양을 띄지만, 실제적으로는 앞뒤의 대상 데이터 타입을 참조하여 이에 맞는 프로시저를 실행하는 것이다.

    일반적으로는 다음과 같이 사용할 것이다.

    select * from emp where salary <40000;

    반대로 오퍼레이터를 구현하는 함수를 직접 호출할 수도 있다.
    이 경우에 위의 질의어는 다음과 같다.

    select * from emp where int4lt(salary, 40000);

    이들 오퍼레이터와 프로시저 목록은 'man pgbuiltin' 명령으로 참고 하기를 바라고, 여기서는 간단한 활용 예를 보이겠다.
    두 점 사이의 거리를 구하려면 다음과 같이 하면 된다.

    mybd=>select '25,25' ::point<-> '0,0' ::point as distance;
    distance
    -------------
    35.3553390593274
    (1 row)

    여기에서 명시적인 형변환을 하기 위해 '::' 기호를 사용하였다. <->오퍼레이터는 A와 B사이의 거리를 구하는 오퍼레이터이다.
    사각형의 박스가 서로 겹치는가도 알아볼 수 있다.

    mybd=>select '0,0,640,480' ::box &&'400,300,1000,800' ::box as 박스겹침여부;
    박스겹침여부
    -------------
    t
    (1 row)

    원의 포함여부도 일상적인 오퍼레이터를 사용하여 쉽게 알 수 있다. 이에 해당하는 프로시저가 이미 만들어져 PostgreSQL 에 내장되어 있음을 다시 상기하자.

    mybd=>select '0,0,50' ::circle @'0,0,100' ::circle as A는B에포함될까;
    a는b에포함될까
    -------------
    t
    (1 row)

    그 외의 유용한 여러 오퍼레이터가 있으므로 맨 페이지를 참고 하기 바란다.

2)오라클 호환 함수

    오퍼레이터와 함수는 밀접한 관련이 있다. 오퍼레이터는 함수로 구현된다. 여기서는 RDBMS 로는 가장 많이 알려져 있는 오라클호환 함수를 잠시 살펴보기로 한다. PostgreSQL 6.2에서 구현된 함수는 다음과 같다.
    이들 함수는 모두 text 데이터 타입에서 동작한다는 것에 주의해야 한다. 즉, varchar 와 같은 데이터형에서는 동작하지 않는다.(varchar 일 경우에는 명시적인 형변환을 하거나, 이들 함수가 varchar를 받아들이도록 조치를 취해야 한다.)

    lower (text)
    소문자로 변환한다.

    mydb=>select lower('Linux');
    lower
    ----
    linux
    (1 row)

    upper (text)
    대문자로 변환한다.

    mydb=>select upper('Linux');
    upper
    ----
    LINUX
    (1 row)

    initcao (text)
    단어의 첫글자를 대문자로 변환하고, 나머지는 소문자로 한다. 단어는 공백문자로 구분한다.

    mydb=>select initcap('linux is not trademark');
    initcap
    ----------------
    Linux is Not Trademark
    (1 row)

    lpad (text1, len [,text2])
    text1 의 왼쪽에 text2 를 전체길이가 len 이 되도록 채운다. text2 가 없다면 기본적으로 공백문자가 사용된다.

    mydb=>select lpad('linux', 30, 'world');
    lpad
    ----------------
    worldworldworldworldworldlinux
    (1 row)

    rpad (text1, len [,text2])
    text1의 오른쪽에 text2 를 전체길이가 len 이 되도록 채운다. text2 가 없다면 기본적으로 공백문자가 사용된다.

    mydb=>select rpad('linux', 30, 'world');
    rpad
    ----------------
    linuxworldworldworldworldworld
    (1 row)

    ltrim (text[,set])
    text 문자열에서 set문자열 중 가장 먼저 나타나는 문자부터 매칭되는 데까지 제거하여 반환한다.

    mydb=>select ltrim('linux world', 'wlinud');
    ltrim
    ----------------
    x world
    (1 row)

    rtrim (text[,set])
    rtrim 은 ltrim 의 반대되는 함수이다.

    mydb=>select rtrim('linux', 'ux');
    rtrim
    ----------------
    lin
    (1 row)

    substr (text, m [,n])
    text 문자열에서 m 번째에서 n 길이 만큼의 문자열을 돌려준다.

    mydb=>select substr('linux world', 1,5);
    substr
    ----------------
    linux

    translate (text, from, to)
    text 문자열에서 from 문자를 찾아서 to 로 대체한다.

    mydb=>select translate('ms', 's', '$');
    translate
    ----------------
    m$

그 외의 함수들

    그 외에 사용되는 일반함수를 살펴보도록 하자.

    정수를 부동소숫점으로 변환

    float8 float8(int)
    float4 float4(int)

    부동소숫점을 정수로 변환

    int4 int4(float)
    int2 int2(float)

    절대시간 관련

    bool isfinite(abstime) abstime 이 finite 시간이면 TRUE를 돌려준다.
    datetime datetime(abstime) abstime 을 datetime 으로 변환한다.

    date 관련

    datetime datetime(date) date 타입을 datetime 타입으로 변환한다.
    datetime datetime(date, time) date 타입과 time 타입을 datetime 으로 변환한다.

    datetime 관련

    timespan age(datetime,datetime) 연과 월의 차이를 구한다.
    float8 date_part(text,datetime) date 필드의 포션을 지정한다.
    datetime date_trunc(text,datetime) date 를 지정한 유닛으로 자른다.
    bool isfinite(datetime) finite 시간이면 TRUE 를 돌려준다.
    abstime abstime(datetime) abstime 으로 변환한다.

    reltime 관련

    timespan timespan(reltime) timespan 으로 변환한다.

    time 관련

    datetime datetime(date,time) datetime 으로 변환한다.

    box 관련

    box box(point,point) 점을 상자로 변환한다.
    float8 area(box) 상자의 넓이를 구한다.

    mydb=>select area(사각형) from test_box;
    area
    -----
    307200
    300000
    (2 rows)

    path 관련

    bool isopen(path) 열려진 경로이면 TRUE를 반환한다.
    bool isclosed(path) 닫혀진 경로이면 TRUE를 반환한다.

    circle 관련

    circle circle(point,float8) 원으로 변환한다.
    polygon polygon(npts,circle) 원을 npts 개의 점을 가지는 다각형으로 변환한다.
    float8 center(circle) 원의 중심점을 구한다.
    float8radius(circle) 원의 반지름을 구한다.
    float8 diameter(circle) 원의 직경을 구한다.
    float8 area(circle) 원의 넓이를 구한다.

    mydb=>select area(원) from test_circle ;
    area
    -------------
    31415.9265358979
    7853.98163397448
    (2 rows)

    기타 텍스트 관련 함수

    text position(subtext in text)
    text 에서 subtext 가 있는 위치를 돌려준다.

    mydb=>select position('world' in 'linux world');
    strpos
    -----
    7

4. PostgreSQL 에서의 SQL

    PostgreSQL 은 표준 SQL 에 나오는 상당히 많은 SQL 명령을 제공하지만 조금의 약점이 있다. PostgreSQL 6.2.1 까지를 기준으로 볼 때, PRIMARY KEY, UNIQU, FOREIGN KEY, REFERENCES, SUBSELECT, HAVING 절 등을 지원하지 못하고 있다. 물론 이러한 것들은 PostgreSQL에서 다른 기능을 사용하여 충족시킬 수 있지만, 불편한 것은 사실이다. PostgreSQL 6.2 에 들어와서 NOT NULL, DEFAULT, CONSTRANT CHECK 등을 새롭게 지원하기 시작하여, 올해 안에 발표될 6.3에서는 여지껏 지원하지 못했던 표준 SQL 의 기능을 새롭게 많이 추가될 것으로 보인다.

    이제, PostgreSQL 의 SQL 기능들을 자세하게 살펴보도록 하자. 질의어별로 나누어 보지 않고 관련 기능별로 구분하여 살펴보겠다.

1) 테이블

    테이블은 DBMS 데이터베이스에서 가장 기본적인 개념으로, 실제 데이터들이 일정한 형태를 띄며 가시적으로 저장되는 곳이다. 따라서 테이블들을 어떻게 만드느냐에 따라 데이터베이스의 성능과 효율이 문제가 된다. 테이블 생성문제를 그냥 '필드 가지고 놀기' 라고 보는 관점은 그리 좋지 않다. 프로그램을 작성할 때 설계가 중요하듯이 데이터베이스 시스템을 구축할 때에도 설계가 중요하다. 한번 설계를 잘못하면 오랜 시간동안 고생을 하고 시스템 비용을 낭비하지만, 한번 설계를 잘 해두면 두고두고 이득을 본다. 즉, 데이터의 성질과 필드의 성격, 인덱스의 활용 여부, 제한 규정 등을 잘 파악하고 활용하여야 한다.

    먼저, 테이블 생성에 대해 알아보자.

    테이블 생성
    테이블은 CREATE TABLE 명령을 사용하여 만든다. 문법은 다음과 같다.

    create table classname (attname type [not null] [default value]
    [, attname type [default value] [not null]])
    [inherits ( classname [, classname])]
    [constraint cname check (test) [, check(test)]]
    [archive = archive_mode]
    [store = "smgr_name"]
    [arch_store = "smgr_name"]

    arch_store 는 새로운 클래스에서 사용할 저장 시스템을 지정한다. store 는 현재의 데이터 배치를 제어하고, arch_store 는 역사적인 데이터 배치를 제어한다. arch_store 는 archive 가 지정될 때에만 지정될 수 있다. store 와 arch_store 는 'magnetic disk' 만 지정할 수 있으며, 기본값도 'magnetic disk' 이다.

    arch_store 는 none, light, heavy 중 하나가 될 수 있다. 쓸 일이 없으니 신경 쓰지 않아도 된다.

    위의 문법이 조금 복잡한 듯이 보이지만 사실은 간단하다. 가장 간단한 형태의 테이블 정의는 다음과 같다.

    CREATE TABLE emppay (
    name text,
    wage float4
    ) ;

    NOT NULL 과 DEFAULT, CONSTRAINT를 추가하여 해당 필드에 대해 추가 제어를 하여 테이블을 만들 수도 있다.

    테이블 정의에서 INHERITS 는 상속관계를 나타낸다.

    CREATE TABLE emppay (
    name text NOT NULL,
    wage float4 DEFAULT 10.00
    ) CONSTRAIN empcon CHECK (wage > 5.30 and wage <=30.00),
    CHECK (name<> ") ;


    deppay 테이블(클래스)은 이제 emppay 테이블(클래스)의 name, wage 필드(속성)을 포함하게 된다. 즉, emppay 로부터 상속을 받게 되는 것이다. 이러한 상속은 단순히 필드(속성)을 쉽고 일관성 있게 만들 수 잇다는 장점뿐만이 아니라, 상속 속성을 이용하여 데이터관리를 조리있게(객체지향적으로!!) 할 수 있다는 것을 의미한다.

    CREATE TABLE deppay (
    department text,
    ) INHERITS (emppay) ;

    데이터 추가
    데이터추가는 INSERT 명령을 사용한다. 문법은 다음과 같다. 여러 형태의 INSERT를 사용하여 데이터를 추가할 수 있다.

    INSERT INTO classname
    [(att.expr-1 [,att_expr.i] )]
    {VALUES (expression 1[, expression-i] )|
    SELECT expression1 [, expression-i]
    [from from-list] [where qual]


    INSERT 구문은 때로는 번거롭다.

    INSERT INTO emppay VALUES ('linux', 25.03) ;
    INSERT INTO emppay (name) values ('hacker') ;
    INSERT INTO emppay
    SELECT user FROM user ;


    데이터를 한꺼번에 입력하고 싶을 때가 자주 있을 것이다. 그럴 때는 copy 명령을 사용하면 된다.

    ','는 입력시에 필드 구분자이다. '\'는 표준입력에서 데이터 입력을 종료할 때 사용한다.

    COPY [binary] <class_name> [with oids]
    {to|from} {<filename>|stdin|stdout} [using delimiters <delim>] ;
    mydb=>COPY emppay from stdin using delimiters ',' ;
    Enter info followed by a newline
    End with a backslash and a period on a line by itself.
    >> beginner, 17.03
    >> power user, 20.88
    >> \.


    copy 는 아스키 모드와 바이너리 모드로 처리된다. 기본은 아스키 모드이며 대부분 별 문제없다. 가끔은 이진 데이터 파일에서 입력을 받고자 하는 경우가 있는 경우라면 binary 예약어를 사용해 볼만 하다.

    파일에서 데이터를 읽어 바로 테이블에 저장하고자 한다면, psql 의 내부 명령어인 \copy 명령을 사용하면 된다. 위의 copy 명령에서 파일을 대상으로 하는 것은 PostgreSQL 슈퍼사용자일 경우에만 해당되므로 psql 명령어 사용 습관들이는 것이 좋다. 내부 문법은 다음과 같다.

    \copy class_name {from|filename
    mydb=> \copy emppay from test.data
    Successfully copied.

    단, 필드 구분자 지정이 없으므로 주의해야 한다. 이들 copy 명령에서 기본 필드 구분자는 스페이스가 아니라 탭문자(!!)라는 사실을 꼭 기억해야 한다.

    * 데이터 갱신
    UPDATE 는 데이터를 갱신하는데 사용된다.

    UPDATE classname SET attname-1 = expression-1
    [, attname-i = expression-i]
    [FROM from-list]
    [WHERE qual]

    UPDATE emppy
    SET name = 'guru', wage = 25.00
    FROM emppay
    WHERE name = 'hacker' ;

    테이블의 변경
    가끔씩 테이블 명을 변경하거나, 테이블의 필드명을 변경해야 할 필요성이 있을 것이다. 또는 테이블의 구조를 변경해야 할 필요도 생긴다. 이럴 때 사용되는 명령이 ALTER 이다. ALTER 는 필드와 테이블 이름을 변경하거나, 테이블에 필드를 추가하는 기능을 한다. 아직 PostgreSQL 6.2 에서는 필드제거 기능은 들어있지 않다.

    위에서 '*' 는 PostgreSQL 의 객체지향적 특성을 드러내는 자그마한 특징이다.

    ALTER TABLE <class_name> [*] ADD COLUMN <attr> <type>
    ALTER TABLE <class_name> [*] RENAME [column] <attr1> to <attr2>
    ALTER TABLE <class_name1> RENAME to <class_name2>
    ALTER TABLE emppay ADD hireday date;
    ALTER TABLE emppay RENAME hireday to empday ;
    ALTER TABLE emppay RENAME to emppay2;


    즉, '*' 를 명시하면 class_name에서 상속받는 모든 클래스에 대해 해당 작업을 확장하는 것이다. 클래스의 상속관계는 클래스(테이블)을 생성할 때 지정할 수 있다. 만일 해당 테이블에서 emppay 필드와 같이 특정 필드를 제거하려고 한다면, 당분간은 번거롭지만 단순작업을 해야 한다.

    SELECT name, wage
    INTO TABLE tmp
    FROM emppay2 ;
    DROP TABLE emppay2 ;
    ALTER TABLE tmp RENAME TO emppay2 ;

    데이터의 삭제와 테이블의 제거
    데이터의 삭제는 DELETE 명령을 사용한다.
    WHERE 절을 생략하면 해당 테이블의 데이터를 모두 지우게 되므로 주의해야 한다. 그렇다 하더라도 테이블까지 제거되는 것은 아니다. 테이블은 drop으로 제거한다.

    DELETE FROM class_name [WHERE qual]
    DELETE FROM emppay2 WHERE wage = 10 ;


    만일 emppay2 로부터 상속을 받은 테이블(클래스)가 있다면 경고가 떨어지고 제거되지 않는다. 이때에는 상속받은 테이블(클래스)부터 제거해야 한다.

    DROP table emppay2 ;

    데이터 검색
    데이터 검색(또는 열, 컬럼 회수)은 유명한 SELECT 로 처리한다. SELECT 를 모르고는 SQL 을 안다고 할 수 없다. 문법은 다음과 같다.

    distinct 는 중복되는 데이터를 하나로 보여주는 것이다.

    SELECT [distinct]
    expression1 [as attr_name-1]
    {, expression1 [as attr_name-i]}
    [into table clessname]
    [from from-list]
    [where where-clause]
    [group by attr_name1 {, attr_name-i....}
    [order by attr_name1
    [using op1] {, attr_namei [using opi]}


    as 는 SELECT 의 결과로 나타나는 필드명을 회수시에 임시적으로 변경하는 것이다.

    SELECT 도시, 최저온도, 최고온도
    FROM 날씨
    WHERE 도시 = '서울' ;
    SELECT 도시, 강수량 as 비온량, 날짜
    FROM 날씨
    WHERE 날짜 >= '1997-10-1'
    GROUP BY 날짜, 비온량
    ORDER BY 도시
    USING <;


    따라서 다른 위치에서 사용될 때에는 as 다음의 임시 필드명을 사용해야 한다. '>=' 는 일반적으로 사용하는 비교부호이고, GROUP BY 는 회수하는 데이터를 어떤 덩어리(그룹)로 묶을 것인가를 지정하며, ORDER BY 는 정렬기준을 나타낸다. PostgreSQL 에서는 ASC, DESC 대신에 'USING<'와 'USING>'를 사용하여 오름차순, 내림차순으로 정렬할 수도 있다.

    SELECT에서 클래스의 상속성을 이용하여 데이터를 검색할 수도 있다.
    이 경우에는 SELECT 시에 emppay에서 상속받은 모든 클래스를 포함하여 검색한다.

    SELECT c.name, c.wage
    FROM emppay* c
    WHERE c.wage>= 10 ;


    '*' 가 역시 상속 클래스까지를 포함하는 클래스 오퍼레이터이다. PostgreSQL 의 SQL에서 정규표현식(regex)을 사용하여 데이터를 검색할 수 있다. 정규표현식 검색을 잘 활용하면 복잡한 작업도 쉽게 사용할 수 있다는 것을 잘 알고 있을 것이다. A ~ B는 A가 정규표현식 B를 포함한다는 것이다. 대소문자를 무시하여 검색하려면 A ~ *B를 사용하면 된다. 포함하지 않는 것을 검색할 때에는 !를 앞에 붙이면 된다.

    정규표현식보다 좀더 사용하기 쉬운 와일드카드는 like를 사용할 수 있다.

    ^|][[:alpha:]]{5}/[[:digit:]]{2}' as find
    find
    ---
    f
    (1 row)


    PostgreSQL 의 SELECT 절에서 아직 HAVING 절과 보조 SELECT 를 지원하지 않고 있다. 그 외의 기능은 일반 SQL 과 같다.

2) 뷰

    뷰는 일종의 가상 테이블이다. 즉, 실제로는 하드 디스크에 어떠한 데이터를 물리적으로 저장하고 있지는 않지만, 테이블과 비슷하게 보이는 것이다. 뷰도 하나의 개체이며, 실제로는 하나의 질의어이다. 뷰를 처리할 때는 일반 테이블처럼 하면 된다. 하지만 뷰는 테이블처럼 정적인 것이 아니라, 뷰에 영향을 미치는 테이블이나 객체의 변동에 따라서 민감하게 변화하는 동적인 객체인 것이다. 뷰의 생성은 일반 테이블을 생성할 때 사용하는 방법과는 다르고, SELECT 로 원하는 데이터를 회수한 후 새로운 테이블에 삽입하는 방법과 비슷하다.

    날씨라는 테이블에서 도시가 서울인 것만 선택해서 서울 날씨라는 뷰를 만들었다.

    create view view_name as
    select expression1 [as attr_name1]
    {, expression_i [as attr_namei]}
    [from from.last]
    where qual

    mydb=> CREATE VIEW 서울날씨 as
    mydb-> select*
    mydb-> from 날씨
    mydb-> where 도시 = '서울' ;
    CREATE
    mydb=> select*from 서울날씨 ;

    도시

    최저온도

    최고온도

    강수량

    날짜

    서울

    10

    27

    0

    10-01-1997

    서울

    12

    25

    0.12

    10-02-1997

    (2 rows)


    여기에서 서울날씨 뷰는 날씨 테이블에 의존을 하며, 날씨 테이블에서 서울날씨 뷰의 생성조건을 만족시키는 데이터가 입력되면 서울날씨 뷰에도 입력된 것으로 처리된다. 이것을 다음에서 확인해보자

    날씨 테이블에 입력한 12월2일자 데이터가 서울날씨 뷰에 들어와 있는 것을 알 수 있다.

    mydb=> insert into 날씨 values('서울', -10,8,0.0, '1997-12-2') ;
    INSERT 535891 1
    mydb=> select*from 서울날씨 ;

    도시

    최저온도

    최고온도

    강수량

    날짜

    서울

    10

    27

    0

    10-01-1997

    서울

    12

    25

    0.12

    10-02-1997

    서울

    -10

    8

    0

    12-02-1997

    (3 rows)


    그 역은 성립하지 않는다. 즉, 서울날씨 뷰에 조건을 만족시키는 데이터를 입력한다고 해서 날씨테이블에 나타나지는 않는 다는 것이다. 다음에서 확인할 수 있다.

    mydb=> insert into 서울날씨 values ('서울', -5, 10, 0.0, '1997-12-1') ;
    INSERT 535892 1
    mydb=> select*from 날씨 where 도시 = '서울';

    도시

    최저온도

    최고온도

    강수량

    날짜

    서울

    10

    27

    0

    10-01-1997

    서울

    12

    25

    0.12

    10-02-1997

    서울

    -10

    8

    0

    12-02-1997

    (3 rows)

    만일, 서울날씨 뷰의 생성조건을 만족시키지 않는 데이터가 뷰에 입력되면, 어떤 결과가 나타날까? 한번 확인해보자.

    mydb=> insert into 서울날씨 values('부산', 1, 15, 0.0, '1997-12-2') ;
    INSERT 535893 1
    mydb=> select*from 날씨 where 도시 = '부산';

    도시

    최저온도

    최고온도

    강수량

    날짜

    부산

    13

    28

    0.32

    10-01-1997

    (3 rows)


    서울날씨 뷰에도 뷰의 생성조건을 만족시키지 못하는 데이터는 에러는 생기지 않았지만 받아들이지 않고 있음을 알 수 있다.

    mydb=> select*from 서울날씨 ;

    도시

    최저온도

    최고온도

    강수량

    날짜

    서울

    10

    27

    0

    10-01-1997

    서울

    12

    25

    0.12

    10-02-1997

    서울

    -10

    8

    0

    12-02-1997

    (3 rows)

    mydb=> drop view 서울날씨 ;
    DROP
    mydb=>


    그리고 본래의 날씨 테이블에도 데이터가 추가되지 않았다. 여기에서 보았듯이 뷰는 일반적으로 테이블과 비슷하게 취급되지만, 뷰의 생성 기반이 된 테이블과의 관계와 뷰를 생성시킨 조건에 영향을 받는다는 것이 테이블과는 다른 점이다.

3) 인덱스

    PostgreSQL 에서는 B-tree와 R-tree의 두 가지 인덱싱 방식을 지원하고 있다. 이들 알고리즘은 접근방식(access method)이라고 불리우며, 인덱스 생성시에 사용자가직접 지정할 수 있다. B-tree 는 주로 일반적인 키의 인덱싱에 사용되며, R-tree는 사각형 컴포넌트의 관계를 표현하는데 사용된다. 따라서, 특수한 공학용도가 아니라면 R-tree는 신경쓰지 않아도 되며, 기본 접근방식은 B-tree 이다.

    여기에서 am-name 은 접근방식(access method)이며, funcname은 인덱싱에 사용할 사용자 정의 함수이다.

    create [unique] index index-name
    on classname [using am-name]
    ( attname [type_class], ...)

    create [unique] index index-name
    on classname [using am-name]
    ( funcname (attname-1 {, attname-i}) type_class)


    가끔 특수한 데이터타입에 있어서 사용자가 직접 함수를 작성하여 인덱싱에 이용하는 것이 속도 향상에 상당한 효과를 거둘 수 있다. 인덱스를 만들 때, 하나의 컬럼에만 인덱스를 부여할 수도 있고, 여러 개의 컬럼에 동시에 인덱스를 부여할 수도 있다. 예를 들어, 날씨 테이블의 도시 컬럼에 인덱스를 생성해 보자.

    unique index 는 중복되지 않은 유일한 인덱스를 말한다.

    mydb=> create index weather_index on 날씨 using btree(도시) ;
    CREATE
    mydb=> drop index weather_index ;
    DROP
    mydb=> create index weather_index on 날씨 (도시, 날짜) ;
    CREATE
    mydb=> drop index weather_index ;
    DROP
    mydb=> create index weather_index on 날씨 (도시, 날짜) ;
    CREATE
    mydb=> drop index weather_index ;
    DROP
    mydb=>


    PostgreSQL 6.2 에서는 B-tree방식에서만 다중 컬럼에 대한 인덱스를 지원하며, 총 7개의 컬럼까지 가능하다.

4) 함수

    PostgreSQL 에서 함수는 SQL 함수와 프로그래밍 언어 함수가 있다. 프로그래밍 언어함수는 주로 C로 작성되며, SQL 함수는 SQL 질의어로 간단히 정의한다. 이들 모두 PostgreSQL에서 정한 규칙에 따라서 작성해야 한다.

    이들 함수를 사용하면 subselect 기능을 대신할 수 있다.

    create function function_name
    ([type1 {, type-n}])
    returns type-r
    as {'/full/path/to/objectfile' | 'sql-queries'}
    language {'c' 'sql' 'internal'}


    먼저 SQL 함수를 만드는 예를 들어보겠다. '날씨' 테이블에서 '도시'를 인자로 주면 평균강수량을 구하는 함수를 정의한다.

    여기에서 함수명과 함께 함수인자를 varchar 로 정의하였는데, text, float8 의 정규데이터 타입이나 테이블명도 인자로 줄 수 있다.

    mydb=> \d 날씨 ;

    Table = 날씨

    Field

    Type

    Length

    도시

    varchar

    20

    최저온도

    int4

    4

    최고온도

    int4

    4

    강수량

    float8

    8

    날짜

    date

    4


    mydb=> create function 강수량평균(varchar) returns float8
    mydb-> as 'select avg(강수량) from 날씨 where 도시 = $1 ;' language'sql' ;
    CREATE
    mydb=> select 강수량평균('서울') ;
    강수량평균
    0.04
    (1 row)

    mydb=>select 강수량평균('부산') ;
    강수량평균
    0.32
    (1 row)

    mydb=>


    여기에서 $1은 SQL 함수 내부에서 인자를 참조하는 방법이다. C 언어로 함수를 만드는 방법은 PostgreSQL 소스 디렉토리의 contrib 안의 여러 소스를 참고하면 대충 알 수 있을 것이다. C 소스로 SQL 함수를 작성하였다면, -fpic 옵션으로 일차 컴파일을 한다.
    음, -shared 옵션을 사용하여 so 공유 오브젝트 파일로 만들어야 한다.
    그 다음 create function을 사용하여 함수를 정의한다.

    $ gcc -fpic -c -o datetime_functions.o datetime_functions.c
    -l../../src/include -l../../src/backend
    $ gcc -shared -o datetime_functions.so datetime_functions.o

    $ psql mydb

    mydb=> create function currentdate() returns date
    mydb-> as '/usr/local/pgsql/lib/datetime_function.so' language 'c' ;
    CREATE

    mydb=>select currentdate() ;
    currentdate
    --------
    12-09-1997
    (1 row)

    mydb=>

5) 룰

    룰은 트리거와 유사한 PostgreSQL 의 확장 SQL 부분이다.

6) 트리거

    create rule rule_name
    as on event
    to object [where clause]
    do [instead]
    [action | nothing | [actions...]]

    mydb=> create table 표1 (i int4) ;
    CREATE
    mydb=> create table 표2 (i int4) ;
    CREATE
    mydb=> create rule 규칙1 as on insert to 표1 do [insert into 표2 values(new.i)] ;
    CREATE
    mydb=> insert into 표1 values (10) ;
    INSERT 535999 1
    mydb=> select *from 표1
    i
    --
    10
    (1 row)

    mydb=> select *from 표2
    i
    --
    10
    (1 row)

    mydb=>


    PostgreSQL에서 트리거를 지원하기 전까지 룰 시스템이 대신해왔다. 기본적인 기능은 룰과 비슷하나 트리거는 좀 더 표준이다.

    EventX 는 INSERT, DELETE, UPDATE 중 하나이다.

    create trigger trigname {before | after}
    {Event1 [OR Event2 [OR Event3]]}
    on relname for each {row | statement}
    execute procedure funcname (arguments)


    create trigger 구문은 새로운 트리거를 만들어서 현재의 데이터베이스에 집어 넣는다. 트리거는 relname 객체상에서 EventX 사건이 발생한 전후에, 지정한 함수인 funcname 을 실행한다. 현재 PostgreSQL 에서 트리거는 statement 가 구현되어 있지 않다. 그리고 일반적인 SQL 에는 거의 사용하지 않고, 프로그래밍 인터페이스로 구현되어 있다. 자세한 내용은 PostgreSQL 소스 디렉토리의 doc에 보면 trigger 메뉴얼이 있으니 참고하기 바란다.

7) 시퀀스

    시퀀스는 말그대로 일련번호 발생기이다. 데이터베이스의 특정 테이블의 로우에, 효율적인 관리를 위해 일반적으로 일련번호를 붙일 필요성이 있다.

    seqname 은 일련번호 생성기의 이름이다. increment는 한번에 증가시킬 값으로, 음수와 양수가 될 수 있으며, 기본 값은 1이다.

    create sequence seqname
    [increment incby_value]
    [minvalue min_value]
    [maxvalue max_value]
    [start start_value]
    [cache cache_value]
    [cycle]


    minvalue 는 일련번호의 최소값으로, 증가시에는 1, 감소시에는 -2147483647이다. maxvalue는 일련번호의 최대값으로, 증가시에는 2147483647, 감소시에는 -1이다. start 값은 일련번호의 첫 번째 값으로, 증가시에는 minvalue 가 사용되고, 감소시에는 maxvalue 가 사용된다. cashe 는 캐쉬에 관련되는 것으로 일반적으로 사용할 필요는 없다. cycle 는 일련번호를 다시 처음부터 매기는 것을 말한다. 매 단계의 일련번호의 값을 얻는 함수는 nextval 이다. 일반적으로 사용되는 일련번호는 1부터 시작하여 1씩 증가시키는 경우가 많다. 이 경우에는 다음과 같이 간단하게 사용한다.

    100부터 100씩 증가하는 일련번호를 생성하려면 다음과 같이 하면 된다.

    mydb=> create table t1(num int4, name text) ;
    CREATE
    mydb=> create sequence t1_seq ;
    CREATE
    mydb=> insert into t1 values (nextval ('t1_seq'), '한동훈') ;
    INSERT 536037 1
    mydb=> insert into t1 values (nextval ('t1_seq'), '이규성') ;
    INSERT 536038 1
    mydb=> insert into t1 values (nextval ('t1_seq'), '정용석') ;
    INSERT 536039 1
    mydb=> select *from t1

      num

      name

      1

      한동훈

      2

      이규성

      3

      정용석


    (3 rows)
    mydb=> select currval ('t1_seq')
    currval
    ---
    3
    (1 row)
    mydb=>

8) 트랜잭션

    create sequence seq_100 increment 100 start 100 ;


    SQL 에서 트랜잭션은 없어서는 안되는 중요한 개념이다. 보통 하나의 데이터베이스를 여러명이 동시에 사용하게 된다. 이 경우에 각각의 사용자가 데이터베이스에 가한 일정 작업의 구간을 절정하여, 데이터베이스에 가한 영향을 수용하거나 취소할 수 있도록 하는 기능을 제공하는 것이 트랜잭션의 중요기능이다.
    가령, 어느 데이터베이스 사용자가 오늘은 도서정리를 한다고 가정해 보자. 이 작업이 기존의 작업과 구별되고, 동일한 연장선상에 있지 않다면, 트랜잭션의 시작점(begin)을 설정하고 일정시간 작업을 계속해 나가게 된다. 그러다 일정작업단위가 끝나면, 그간에 데이터베이스에 가한 변화를 승인하려면 commit (또는 end)를, 계산이 잘못 되어서 취소하고 싶으면, rollback (또는 abort)을 사용할 것이다. PostgreSQL 에서 제공하는 트랜잭션 처리기능은 그리 세부적이지 못하다. 트랜잭션 구간 작업처리를 정밀하게 제어할 수 있게 된다. PostgreSQL은 평면적, 1구간 트랜잭션의 승인은 end, 취소는 abort로 한다. commit은 end 와 같고, rollback은 abort와 같다.

    간단한 예를 들어보자

9) 통지와 청취

    mydb=> create table test2 (num int4) ;
    CREATE
    mydb=> insert into test2 values (1)
    INSERT 536270 1
    mydb=> insert into test2 values (2)
    INSERT 5362701
    mydb=> select *from test2 ;
    num
    ----
    1
    2
    (2 rows)

    mydb=> begin ;
    BEGIN
    mydb=> insert into test2 values (3)
    INSERT 5362702 1
    mydb=> select *from test2 ;
    num
    ----
    1
    2
    3
    (3 rows)

    mydb=> abort ;
    ABORT
    mydb=> select *from test2 ;
    num
    ----
    1
    2
    (2 rows)

    mydb=> begin ;
    BEGIN
    mydb=> delete from test2 where num = 2
    DELETE 1
    mydb=> end ;
    END
    mydb=> select *from test2 ;
    num
    ----
    1
    (1 rows)

    mydb=>


    다른 세션에서,

    mydb=> create table 테스트1 (i int4) ;
    CREATE
    mydb=> notify 테스트1 ;
    NOTIFY


    notify 는 동시성에 관련된 통보를 하고, listen 은 그것을 청취한다.

    mydb=> listen 테스트1 ;
    LISTEN
    mydb=>
    mydb=> select *from 테스트1 ;
    i
    -
    (0 rows)

    ASYNC NOTIFY of '테스트' from backend pid '14126' received
    mydb=>


    그냥 listen 만한 상태에서는 아무런 일이 일어나지 않는다.

    식으로 listen 할 테이블(클라스) 이름을 지정하면, 이후에 다른 프로세스(같은 프로세스도 포함하여)가 해당 notify를 하게되면 그때서야 listen 프로세스는 어느 프로세스가 notify를 보냈다는 통보를 받는다.

    listen class_name ;


    그리 유용한 질의어는 아니지만, 같은 테이블에 동시적으로 접근하는 프로세스간에 약간의 정보(프로세스 번호)를 교류하기 위한 것이다. 즉, 누가 지금 어느 테이블을 건드리고 있다...는 정도이다. libpq 에도 이에 상응하는 기본 함수가 있긴 하지만, 거의 쓰이지 않는 듯 하다.

10) 클러스트링

    PostgreSQL에서 클러스트링은 인덱스에 기초하여 데이터를 물리적으로 재정렬하는 것이다. 하나의 클래스인 classname 상의 인덱스인 indexname 에 기초하여 PostgreSQL 가 클러스트링 작업을 수행하도록 지시하는 명령이다. indexname 은 classname 상에 미리 정의되어 있어야 한다. 클래스의 크러스터는 파괴된다. 즉, 클러스트링은 정적(static)이며, 사용자가 원할 경우 언제든지 다시 클러스트링을 수행할 수 있다.

    mydb=> create index 날씨인덱스 on 날씨(도시) ;
    CREATE
    mydb=> cluster 날씨인덱스 on 날씨 ;
    CLUSTER
    mydb=>

11) 권한

    grant
    데이터베이스를 사용하다보면 사용자에게 데이터베이스 접근권한을 넘겨주거나 제한하여야 할 필요가 있다. 이럴 때 사용하는 명령이 grant 이다. 말 그대로 승낙한다는 뜻이다.

    privilege 는 ALL, SELECT, INSERT, UPDATE DELETE, RULE 중의 하나 이상이 될 수 있다.

    grant <privilege[,privilege...]>
    on <rel1>[,...<reln>]
    to [public | group <group> | <username>]


    기본적으로 하나의 테이블은 PostgreSQL 의 모든 사용자가 읽기(select)만 할 수 있도록 되어있다. 이것을 원치 않는다면 권한을 revoke 할 수 있다.

    날씨 테이블에 대한 모든 권한을 공중에 허용한다는 것이다.

    grant all on 날씨 to public ;


    이럴경에 모든 PostgreSQL사용자는 해당 테이블을 읽고 쓸수 있게 된다.

    Linuxer 사용자에게 나씨 테이블에 대한 입력, 갱신, 삭제 권한을 부여하는 것이다. PostgreSQL 의 카탈로그에 보면 pg_group 이라는 객체가 있다. PostgreSQL의 카탈로그는 다른 RDBMS에 비해 상당히 복잡하고, 저급적인 인터페이스를 가지고 있다고 생각한다. 이 pg_group 카탈로그와 grant를 잘 사용하면, 특정 프로젝트를 수행하고 있는 데이터베이스 사용자 그룹에게만 권한을 부여할 수도 있다. 참고로 pg_group 카탈로그는 다음의 컬럼으로 이루어져 있다.

    mydb=> \d pg_group
    Table = pg_group

      Field

      Type

      length

      groname

      name

      32

      grosysid

      int4

      4

      grolist

      int4[]

      var


    해당 그룹이 users 라는 그룹이고, 유닉스 그룹 아이디(/etc/group 에 나타난 번호)가 100이고, 이들 그룹에 포함시키고자 하는 사용자의 UNIX아이디(/etc/passwd 에 나타난 번호)가 501, 510 이라면, 먼저 postgres 계정으로 데이터베이스에 접속하여, 이들에 대한 그룹설정을 해준다.

    이제 501, 510 사용자가 users 라는 그룹명으로 pg_group 카달로그에 포함을 시켰다.

    mydb=>insert into pg_group values ('users', 100, '{501,510}') ;
    INSERT 536064 1


    이제 특정 테이블에 대한 권한을 이들 그룹에게만 부여해보자.

    mydb=>grant all on 날씨 to group users ;
    CHANGE
    mydb=>

    revoke
    revoke는 말 그대로 권한을 취소한다는 의미이다. 문법은 grant와 동일하고, to 와 from 만 다르다.

    역시 privilege 는 ALL, SELECT, INSERT, UPDATE, DELETE, RULE 중 하나 이상이 될 수 있다.

    revoke <privilege[,privilege...]>
    on <rel1>[,...<reln>]
    from [public | group <group> | <username>]


    grant에서 부여한 권한을 차례로 취소해보자.

    revoke all on 날씨 from public ;
    revoke insert, update, delete on 날씨 from linuxer ;
    revoke all on 날씨 from group users ;

12) 질의 분석

    explain 은 질의의 효율성을 분석하는 데 도움을 주는 명령어이다. explain 뒤에 사용할 질의를 명시하면 질의를 실행하는 대신, 옵티마이저가 계획한 질의의 실행경로와 계획단계를 사용자에게 알려준다. 따라서, 대형화된 데이터베이스에서 질의의 최적화를 성립하는 데 도움을 주게 된다.

    explain 에 verbose를 사용하면 더 자세한 질의 실행계획을 알 수 있다.

    mydb=> \h explain ;
    Command : explain
    Description : explain the query execution plan
    Syntax :
    explain [verbose] <query>
    mydb=> explain select *from 날씨 ;
    NOTICE : QUERY PLAN :
    Seq Scan on 날씨 (cost =1.36 size =11 width = 32)
    EXPLAIN
    mydb=> explain verbose select *from 날씨 ;
    NOTICE : QUERY PLAN :
    {SEQSCAN :cost 1.363 :size 11 :width 32 : state nil :qptargetlist
    ({TLE :resdom { RESDOM :resno 1 :restype 1043 :reslen -1 :resname
    "도시" :reskey 0 :reskeyop 0 :resjunk 0} :expr {VAR :varno 1:varattno 1
    :vartype 1043 :varnoold 1 :varoattno 1}} {TLE :resdom {RESDOM
    :resno 2 :restype 23 :resnel 4 :resname "최저온도" :reskey 0 :reskeyop 0
    :resjunk 0} :expr {VAR :varno 1 :varattno 2 :vartype 23 :varnoold 1
    :varoattno 2}} {TLE :resdom {RESDOM :resno 3 :restype 23 :resnel 4
    :resname "최고온도":reskey 0 :reskeyop 0:resjunk 0} :expr {VAR :varno 1:varattno 3 :vartype 23 :varnoold 1 :varoattno 3}} {TLE :resdom
    {RESDOM :resno 4:restype 701 :resnel 8 :resname "강수량" :reskey 0
    :reskeyop 0 :resjunk 0} :expr {VAR :varno 1 :varattno 4:vartype 701
    :varnoold 1:varoattno 4}} {TLE :resdom {RESDOM :resno 5 :restype
    1082 :resnel 4 :resname "날짜":reskey 0 :reskeyop 0:resjunk 0} :expr
    {VAR :varno 1 :varattno 5:vartype 1082 :varnoold 1 :varoattno 5}})
    :qpqual nil :lefttree nil :righttree nil :scanrelid 1}
    Seq Scan on 날씨 (cost =1.36 size =11 width = 32)
    EXPLAIN
    mydb=>

13) 커서 사용하기

    declare
    declare는 커서를 정의한다. SQL 에서 커서는 일종의 파일 스트림과 같은 역할을 하는 것이다. 파일을 열어서 FILE * 스트림으로 연결하여 사용하듯이, SQL 에서는 데이터베이스의 데이터를 SELECT 로 회수하여 커서와 연결하여 사용한다.

    selsct 구문은 앞전과 동일하다. SELECT 구문의 경과를 커서 cursorname 과 연결하여 정의하는 것이다.

    declare cursorname [ binary ] cursor for select statement


    여기에서 binary 에 주목할 필요가 있다. 커서는 일반적으로 아스키 형태로 데이터를 반환한다. 데이터는 자연스럽게 바이너리 형식으로 저장되기 때문에, 시스템은 이것을 아스키 형식으로 변환해야 한다. 아스키 형식은 크기면에서 바이너리 형식보다는 일반적으로 큰 경우가 많다. 데이터가 아스키 형식으로 변환되었을 때, 클라이언트 어플리케이션은 데이터를 어떤 목적으로 처리하기 위해 바이너리 형식으로 변환해야 할 필요가 있을 것이다. 바이너리 커서는 사용자에게 데이터를 바이너리 형식으로 전달한다. 따라서, 바이너리 커서는 변환에 소모되는 오버헤드가 없기 때문에 좀더 빠르다. 하지만, 아스키 형식은 아키텍쳐에 비교적 영향을 받지 않지만, 바이너리 형식은 머쉰에 따라 달라지기 쉽상이다. 따라서, 클라이언트 머쉰과 서버 머쉰이 서로 다른 바이너리 형식을 사용한다면, 원치 않는 결과가 나타날 수도 있다. 주 목적이 데이터를 아스키 형태로 보여주기 위한 것이라면 아스키 형태로 커서를 선언하는 것이 좋다.

    fetch
    fetch 는 커서에서 인스턴스를 적재하는 일을 한다. 파일 스트림(커서)에서 fgets 함수등으로 불러오는 일(fetch) 과 비슷하다고 볼 수 있다.

    number 는 cursor_name 커서에서 불러올 인스턴스의 갯수이다.

    fetch [(forward | backward)] [(number | all)] [in cursor_name]


    해당 커서에 남아있는 인스턴스가 number 보다 적다면 남아있는 것만 적재된다. all 은 현재 커서에 남아있는 모든 인스턴스를 불러온다. forward 와 backward 는 커서에서 앞뒤로 움직이며 적재할 수 있도록 한다. 즉, 파일 스트림에서 정방향(forward)으로, 또는 거꾸로(backward) 돌아가는 것과 비슷하다고 보면 된다. 기본은 물론 forward 이다. fetch 명령어는 트랜잭션 블록 안에서 사용되어야 한다. 그리고, 커서를 사용하고 난 다음에는 커서 cursor_name 과 관련되는 자원을 해제하기 위해 close 해주어야 한다. 트랜잭션 내부에서 declare 와 fetch를 사용하는 순서는 다음과 같다.

    알아보기 쉽게 간단한 테이블을 만들어서 커서를 선언해 보자

    첫 단계에서 5개를 fetch 하였을 경우에는, 커서 내의 위치가 0 에서 시작하여 1, 2, 3, 4, 5 의 5개를 불러와서 5에 위치하게 되고, 거꾸로 3개를 fetch 하였을 경우에는 4, 3, 2 의 3 개를 불러와서 2까지 가게 되며, 여기서 정방향으로 4개를 fetch 하였을 경우에는 3,4 , 5, 6 을 불러왔음을 알 수 있다. fetch all 은 나머지를 모두 보여준다.

    BEGIN -> DECLARE CURSOR -> FETCH INSTANCE IN CURSOR ->
    CLOSE CURSOR -> END


    mydb=> create table test3 (num int4) ;
    CREATE
    mydb=> copy test3 from stdin ;
    Enter info followed by a newline
    End with a backslash and a period on a line by itself.
    >>1
    >>2
    >>3
    >>4
    >>5
    >>6
    >>7
    >>8
    >>9
    >>10
    >>\.
    mydb=> begin ;
    BEGIN
    mydb=> declare mycursor cursor for select *from test3 ;
    SELECT
    mydb=> fetch 5 in mycursor;
    num
    ---
    1
    2
    3
    4
    5
    (5 rows)

    mydb=> fetch backward 3 in mycursor;
    num
    ---
    4
    3
    2
    (3 rows)

    mydb=> fetch forward 4 in mycursor;
    num
    ---
    3
    4
    5
    6
    (4 rows)

    mydb=> fetch all in mycursor;
    num
    ---
    7
    8
    9
    10
    (4 rows)

    mydb=>close mycursor;
    CLOSE
    mydb=>end ;
    END
    mydb=>

14) 제거된 데이터의 물리적인 삭제

    vacuum 은 데이터베이스를 정리하는 것이라 보면 된다. 데이터베이스의 로우를 제거하였다 하더라도 아직 데이터베이스에 남아있다. 무슨 이야긴가 하면,

    즉, 테이블의 데이터를 지웠다고 하더라도, 데이터 자체가 물리적으로 지워진 것이 아니고 PostgreSQL에서 지워진 데이터라는 표시만 하고, 데이터는 그대로 저장되어 있다는 것이다.

    mydb=> select *from 표1 ;
    i
    -
    10
    (1 row)

    mydb=>delete from 표1 ;
    DELETE 1
    mydb=> select *from 표1 ;
    i
    -
    (0 rows)

    mydb=> select *from 표1['epoch', 'now'] ;
    i
    -
    10
    (1 row)

    mydb=> vacuum ;
    VACUUM
    mydb=> select *from 표1['epoch', 'now'] ;
    i
    -
    (0 rows)

    mydb=>


    그래서 데이터 회수시에 시간지정을 해주면 지워진 데이터라 할지라도 검색할 수 있는 것이다. vacuum 은 제거된 데이터를 물리적으로 제거해 버린다. vacuum을 실행하기 전에 지워진 데이터들이 별 필요없는지를 다시 한번 확인해 보는 것이 좋다. 한번씩 vacuum을 실행하는 것은 데이터베이스 처리 속도를 향상시키는 튜닝의 한가지 방법이다.

, .


공개용 데이터베이스 서버 PostgreSQL (2)

    한동훈 (하이텔 리눅스동 대표시삽)

1. 들어가는 말

    얼마 전에 PostgreSQL 6.2가 나왔고, 곧이어 며칠후에 6.2.1이 발표되었다.
    PostgreSQL 6.2부터는 표준SQL 문법을 본격적으로 지원하겠다고 한다.
    PostgreSQL 6.2에서는 많은 부분들이 수정되었는데, SQL 질의어와 관련하여 눈에 띄는 부분은 CREATE TRIGGER구분의 지원과 테이블 생성 시 특정 필드에 대한 NOT NULL과 DEFAULT값 지정, 필드의 값을 제한하고 체크하는 CONSTRAINT정도가 될 것이다. 이전에 이러한 작업은 인덱스나 룰을 통하여 충분히 처리할 수 있었으나, 어찌되었건 한결 편리해진 것만은 틀림없을 것이다. PostgreSQL 6.2.1은 6.2에 대한 패치 정도라고 보면 될 것같다. 특별한 기능 추가보다는 약간의 수정이 주를 이루고 있다. 아울러 PostgreSQL6.2나 6.2.1의 설치는 앞서 설명한 6.1.1의 설치 방법과 동일하다. 올해안에 표준 SQL문법을 완벽히 지원하기를 기대해 보면서 시작하도록 하자.

2.PostgreSQL 관련 문서들

    PostgreSQL 을 배우는 데 도움이 되는 문서는 비교적 적다. 대부분의 문서들은 사용자가 SQL이나 다른 DBMS에 어느 정도 익숙하다고 가정하고 쓰여진 것 같다.
    PostgreSQL에서 제공하는 사용자 매뉴얼은 95년의 POSTGRES95 1.0에 관해 설명하고 있다. 따라서 지금의 PostgreSQL과는 약간의 차이가 있다. 아울러 현재 PostgreSQL에서 새롭게 구현되거나 변경된 특징들은 POSTGRES95 1.0 매뉴얼에는 나와 있지 않다. 이러한 문서 부분의 부족함을 해결하기 위해 PostgreSQL 사이트에서 새롭게 이와 관련된 프로젝트가 진행되고 있으니 반가운 일이 아닐 수 없다.
    PostgreSQL에서 아주 쓸만한 문서는 역시 매뉴얼 페이지이다. 표준 SQL명령 뿐만이 아니라 PostgreSQL에서 제공하는 UNIX 명령어들과 데이터 타입, ORACLE 호환 함수들, 라이브러리 루틴들에 대한 내용을 맨 페이지 형식으로 가장 최신의 정보를 담고 있다. 표준적인 설치가 끝났다면 /usr/local/pgsql/man 에 여러 맨 페이지들이 있으니 맨 페이지 경로를 추가한 이후 다음과 같이 온라인으로 활용할 수 있다.

      $ man l create_sequence

    그 위에도 PostgreSQL에서 제공하는 예제 소스들을 참고 하거나 PostreSQL사이트를 방문하여 메일링 리스트에 가입하면 여러 자세한 정보를 구할 수 있다.

3. PostgreSQL 외부 명령어

    PostgreSQL은 데이터베이스 시스템의 관리를 위한 외부적인 툴을 제공한다.
    이들 툴은 /usr/local/pgsql/bin에 있으며, 일반 UNIX명령어처럼 사용할 수 있다. 이 명령어들을 사용하려면 /usr/local/pgsql.bin이 당연히 검색 경로에 포함하는 것이 편리할 것이다. 이중 데이터베이스 관리와 직접적인 관련이 있는 명령은 대개 접속 포트, 데이터베이스 명, 인증 시스템 등 을 지정할 수 있는 옵션을 가지고 있다. postmaster에서 기본적으로 사용하는 포트는 5432 이지만, postmaster를 여러 개 띄운다던지 다른 특별한 이유 때문에 다른 포트를 사용할 경우에는 PGPORT 환경 변수를 이에 맞게 설정해 주던지, 아니면 일일이 명령행에서 -p옵션을 사용하여 포트를 지정해주어야 한다. 여기에서는 일반적으로 5432포트를 사용하는 것으로 간주한다. 이제 하나씩 살펴보도록 하자.

    Initbd

    이 명령은 직접적으로 사용할 기회가 없을 것이다. PostgreSQL 데이터베이스 시스템을 초기에 구축할 목적으로 설치 시에 한번 사용했을 것이다. 바이너리 패키지를 설치한다면 이미 데이터베이스 시스템이 구축되어 있을 것이기 때문에 이 조차도 사용할 필요가 없다.

    Cleardbdir

    이 명령은 PostgreSQL의 모든 데이터베이스를 파괴하는데 사용된다. 이 역시도 일반 사용자는 사용할 필요도 없고, 사용해서도 안된다. PostgreSQL 슈퍼 유저가 PostgreSQL데이터베이스 시스템을 다시 초기화하기 위해 데이터베이스를 모두 제거할 때 사용한다. 이 명령을 수행할 때에는 postmaster 프로세스가 돌아가지 않도록 하여야 한다.

    pg_dump,pg_dumpall

    이들 명령은 데이터베이스 형식이 다른 현재의 데이터베이스를 다른 버전으로 옮길 때 SQL 스크립트로 갈무리할 목적으로 사용된다. PostgreSQL에서는 버전이 다르면 데이터베이스의 형식이 달라진다. 즉, 몇주일 정도 흐르면 데이터베이스 형식이 달라지기 때문에 업그레이드를 하려면 먼저, 데이터베이스를 이들 프로그램을 사용하여 SQL 스크립트로 갈무리한 다음, 새로운 PostgreSQL에서 실행시켜 줘야 한다. SQL스크립트를 받아낼 때는 반드시 신버젼의 pg_dump, pg_dumpall프로그램을 사용해야 한다. 버전이 달라지면 대개 SQL문법도 약간 달라지게 되므로 구버젼의 dump 프로그램을 사용하여 갈무리하였다면 신버젼의 PostgreSQL에서 SQL스크립트가 제대로 작동하지 않을 것이다. 즉, 신버젼의 PostgreSQL를 설치하기 이전에 dump프로그램만 풀어낸 다음, 이 dump프로그램으로 구버젼의 PostgreSQL데이터베이스에서 SQL을 받아내야 한다. 6.2와 6.2.1에서는 이런 고생을 하지 않고, 그냥 사용하면 된다.

    Pg_dump를 사용하면 특정 테이블이나 데이터베이스만 SQL로 받아낼 수 있다.
    Pg_dumpall은 시스템의 전체 데이터베이스를 한꺼번에 받아낼 때 사용한다.
    모든 데이터베이스를 dump하여 새롭게 설치한다면 다음과 같이 하면 된다.

      $ pg_dumpall-o>유.out
      $ psql-e templatel<유.out

    이들 dump프로그램은 뷰와 룰, 부분 인덱스, 거대 객체는 제대로 처리하지 못한다는 것을 알아두자.

    Createdb, destroydb

    말 그대로 데이터베이스를 생성하고 제거하는 유틸리티이다. 특별히 데이터베이스를 뒷부분에 지정하지 않으면 사용자의 아이디와 동일한 이름의 데이터베이스를 생성하거나 제거한다. 물론, PostgreSQL슈퍼유저가 해당 사용자에게 데이터베이스 생성 권한을 부여 하여야 한다.

      $ createdb test
      $ destroydb test

    만일 데이터베이스 생성 권한이 없는 사용자가 데이터베이스를 만들려고 한다면 다음의 에러가 나타날 것이다.

      $ createdb test
      WARN:user "ddoch" is not allowed to create/destroy databases
      Createdb: database creation failed on test.
      $

    createuser, destroyuser

    데이터베이스를 사용할 사용자를 추가하거나 제거하는 유틸리티이다. 이 명령을 사용하려면 PostgreSQL 슈퍼유저이거나 데이터베이스 사용자를 추가할 권한이 있어야 한다.

      $createuser linuxer
      $destroyuser linuxer

    만일 데이터베이스 사용자 추가 권한이 없는 사용자가 데이터베이스를 사용할 사용자를 추가하려고 한다면 다음의 에러가 나타날 것이다.

      $createuser linuxer
      createuser: ddoch cannot create users.
      $

    사실 지금까지의 프로그램들은 본쉘 스크립트들이다. 살펴보면 알겠지만 psql을 사용하여 templatel 데이터베이스에 접속한 다음, 적절한 질의어를 실행하고 있다. 데이터베이스나 사용자를 추가하는 작업은 SQL 질의어에 기본을 두고 있다는 것을 알 수 있다.

    pg_version

    별 실용성이 없는 프로그램이다. Pg_version에 인자를 주어 실행하면 해당 디렉토리에 PG_VERSION 이라는 파일이 만들어지고, 버전정보가 간단히 들어간다.

    Pg_id

    이 역시도 잘 사용하지 않는 프로그램이다. 실행하면 자신의 계정에 해당하는 uid를 출력한다. /etc/passwd 의 세번째 항목과 동일하다.

    Postmaster

    postmaster는 PostgreSQL 데몬 프로세스이고, postgres는 PostgreSQL 백엔드 프로세스이다. 사실 이들 프로그램은 링크된 같은 프로그램이지만, 보통 postmaster를 실행시키면 postmaster가 postgres를 수행한다. 따라서 postgres를 바로 띄울 수도 있지만, 보통은 postmaster를 사용한다.
    Postmaster는 frontend와 backend프로세스 사이의 통신을 관리한다.
    아울러 공유 메모리 버퍼 풀과(test-and-set 명령을 지원하지 않는 기계에서) 세마포어를 할당한다. postmaster는 그 자체로는 사용자와 상호대화적으로 수행되지 않으며, 후 위 프로세스로 기동되어야 한다.

      명령어 형식:
      postmaster [-B n_buffers] [-D data_dir] [-S] [_a systerm]
      [-b backend_pathname] [-d[debug_level]][-n]
      [-o backend_options][-p port][-s]

    ⊙ -B n_bufferw

    n_buffers는 postmaster가 backend 서버 프로세스를 위해 할당하고 관리할 공유 메모리 버퍼의 개수이다. 기본겂은 64이다.

    ⊙ -D data_dir

    데이터베이스 디렉토리 트리의 루트로 사용할 디렉토리를 명시한다. 이 디렉토리는 PGDATA 환경 변수 값을 사용한다. 만일 PGDATA가 설정되어 있지 않다면, 사용되는 디렉토리는 $POSTGRESQLHOME/data이다. 환경 변수 값도 설정되지 않았고, 이러한 옵션도 지정되지 않았다면, 컴파일 시간에 설정된 기본 데렉토리가 사용될 것이다.

    ⊙ -S

    postmaster 프로세스로 하여금 기동시에 침묵모드(?)가 되도록 지정한다.

    이것은 사용자의 (제어) tty와는 상관없이 그 자신의 프로세스 그룹으로 기동 된다. 이 옵션은 디버깅 옵션과 함께 사용할 수는 없는데, 그 이유는 표준출력과 표준에러출력을 통해서 나오는 메시지는 모두 취소되기 때문이다.

    ⊙ -a system

    frontend 응용프로그램이 postmaster 프로세스로 접속할 때 'system' 인증시스템을 사용할 것인지를 지정한다. 'system'을 명시하여 인증 시스템을 유효화 시킬 수 있으며,'nosystem'을 명시하여 인증 시스템을 취소할 수 있다. 예를 들어, 사용자로 하여금 Kerberos 인증을 사용하도록 허용하려면, '-a kerberos'처럼 사용할 수 있다. 인증을 거치지 않은 모든 접속을 거부하려면, '-a nounauth'처럼 사용할 수 있다.

    ⊙ -b backend_pathname

    backend_pathname은, postmaster가 frontend 응용프로그램의 접속을 요청 받을 때, 호출할 PostgreSQL backend서버의 시행가능한 파일의 전체 경로명이다. 이 옵션이 지정되지 않았다면, postmaster는 이 실행가능한 파일을 postmaster가 호출된 디렉토리에서 찾으려고 할 것이다.(이 것은 postmaster가 호출된 경로명에서 찾는다는 것을 의미한다. 경로명이 지정되지 ㅇ낳았다면, "postgres"라 불리우는 실행가능한 파일을 찾기위해 PATH환경변수를 사용한다).

    ⊙ -d [debug_level]

    debug_level 옵션 인자는 backend 서버가 출력할 디버깅 메시지의 양을 결정한다. Debug_level이 1이라면, postmaster는 모든 접속 트래픽을 추적할 것이며, 그외에는 아무런 일도 하지 않는다. 2 이상의 값이라면, 디버깅작업은 backend 프로세스가 담당하게 되고, postmaster는 backend의 환경과 프로세스 트래픽을 포함한 좀 더 많은 정보를 출력한다. Backend서버가 디버깅 메시지를 출력할 파일을 지정하지 않는다면, 디버깅 정보는 이들의 부모인 postmaster의 제어 tty상에 나타날 것이다.

    ⊙ -n , -s

    -s와 -n은 backend가 비정상적으로 종료할 때, postmaster의 행동을 제어하는 데 사용된다. 이 옵션은 정상적인 작동에는 사용하지 않는 것이 좋다. 비정상적인 종료가 발생할 때 일반적인 전략은, 다른 모든 backend서버에게 종료할 것을 알리고, 공유 메모리와 세마포어를 다시 초기화 하는 것이다. 이렇게 하는 이유는, 잘못된 backend는 종료하기 이전에, 몇몇 공유된 상태를 망가뜨릴 수 있기 때문이다.

    -s 옵션이 지정된다면, postmaster는 다른 모든 backend서버에게 SIGSTOP시그널을 보내서 중도에서 멈추게 할 것이다. 하지만 이 시그널은 프로세스 자체를 종료 시키지는 않을 것이다. 이러한 방법을 사용하면, 시스템 프로그래머는 모든 backend서버로부터 발생한 코아 덤프를 수집할 수 있다.

    -n 옵션이 지정된다면, postmaster는 공유된 데이터 구조를 다시 초기화하지 않는다.

    ⊙ -o backend_options

    postgres(unix)에서 backend_options에 지정된 옵션은 postmaster에 의해 기동된 모든 backend서버 프로세스에게 전달된다. 옵션 중간에 공백 문자가 들어 간다면 인용 부호로 묶어야 한다.

    ⊙ -p port
    postmaster가 frontend 응용프로그램으로부터의 접속요청을 받아들일 인터넷 TCP포트를 지정한다. 기본 포트값은 5432이지만, PGPORT환경변수를 통하여 얼마든지 변경할 수 있다. 기본 포트값과는 다른 포트를 지정한다면, 다른 모든 frontend 응용프로그램의 사용자는, psql을 포함한 libpq를 사용한 응용프로그램을 기동할 때, 반드시 같은 포트를 (PGPORT 환경변수나 명령행 옵션을 사용하여)지정 해야한다.

    Postmaster를 종료시킬 때, 가능하다면 SIGKILL을 사용하지 않는 것이 좋다.
    이것은 비단 PostgreSQL에만 그런건 아니다. Httpd에서도 SIGKILL을 사용해서 httpd 서버를 죽이면 자식들은 살아서 돌아다니게 되므로 귀찮아진다. SIGTERM을 대신 사용하도록 하자.

      $ps-ax|grep postmaster
      3544 p1 S 0:00grep postmaster
      386? S 0:00/usr/local/pgsql/bin/postmaster-S-B 512-o-F-D/usr/l
      $kill-TERM 386

    다시 한번 말하지만, postmaster를 죽이기 위해서 SIGKILL (또는 시그널 번호 9)을 사용하게 되면, postmaster에게 할당되어 있는 공유 메모리와 세마포어등의 시스템 자원을 종료할 때 해제하지 못하게 된다.
    다음 명령은 postmaster를 5432기본 포트 상에 침묵 모드(?)로 띄운다.

      $postmaster-S

    다음 명령은 postmaster를 1234포트상에 띄우고, 공유메모리 버퍼를 4메가(8K*512)를 할당하고, 디스크 캐슁을 켜기 위해 백엔드 옵션으로 '-F'를 전달하고 있다. 데이터베이스 디렉토리로 /usr/local/pgsql/data를 설정하고 있다. psql을 사용하여 이 postmaster에 접속하려면, psql 명령행에서 -p 1234를 지정하거나, PGPORT 환경변수값을 1234로 설정하여야 한다.

      $postmaster-S p 1234-B 512-o-F-d/usr/local/pgsql/data

    postgres

      postgres [-B n_buffers] [-D data_directory] [-E] {-F}
      [-P filedes] [-Q] [-S n_buffers] [-e]
      [-d debug_level] [-o output_file] [-s] [dbname]

    postgres 옵션도 postmaster 와 유사함을 할 수 있다.
    postmaster 에서 -o뒤의 인자는 postgres에게 전달된다.

    Psql

    전월호에서 설명한 바 있다. psql은 정말 유용한 프론트엔드 프로그램이다.
    간간이 중요한 옵션을 실전에 사용해 볼 것이다.

    pg_passwd

    pg_passwd는 PostgreSQL에서 비밀번호 파일을 처리하는 도구이다.
    $PGDATA/pg_hba.conf (일반적으로는, /usr/local/pgsql/data/pg_hba.conf)와 관련이 있다. pg_hba.conf는 뒤에서 설명하겠지만 PostgreSQL의 데이터베이스 접근 제어를 처리하는 파일이다. pg_hba.conf 파일의 중간 쯤에 다음과 같은 줄이 있을 것이다.

      host all 127.0.0.1 255.255.255.255 trust

    이것은 로컬 호스트에서 접속하는 모든 사용자를 일단 믿고, 모든 데이터베이스로의 접속을 허용한다는 뜻이다. 자신의 사이트에 마음에 들지 않는 괴팍한 사용자들이 있어서 비밀번호를 걸어두고 싶다고 하자. 그렇다면 이것을 다음과 같이 바꾸면 된다.

      host all 127.0.0.1 255.255.255.255 password passwd

    여기에서 다섯번째의 'password'는 127.0.0.1(로컬 호스트)에서 접속하는 사용자에게는 비밀전호 매카니즘을 적용한다는 이야기이며, 마지막의 'passwd'는 $PGDATA/passwd (보통, /usr/local/pgsql/data/passwd)파일을 비밀번호 파일로 사용한다는 뜻이다. 즉, 시스템 전체에는 /etc/passwd가 비밀번호 파일로 사용된다면, PostgrreSQL 내부의 기본적인 비밀번호 파일로는 $PGDATA/passwd 파일을 사용한다는 것이다. 이 파일은 pg_passwd프로그램을 사용하여 PostgreSQL 슈퍼유저가 생성할 수 있다. 백업파일은 같은 디렉토리에 passwd.bk로 저장된다.

      $ cd/usr/local/pgsql/data
      $ pg_passwd passwd
      Username:linuxer
      New password: (비밀번호 입력)
      Re-enter new password: (재입력)
      $ cat passwd
      linuxer:XJYDbjVQfFbRs (사용자 아이디와 암호화된 비밀번호)
      $

    이제 로컬 호스트에서 접속하는 linuxer라는 사용자는 비밀번호를 입력하지 않으면 데이터베이스에 접속할 수 없다.

      $ psqlinux
      Connection to database 'linux' failed.
      Failed to authenticate client as Postgres user 'linuxer' using the default authentication type: be_recvauth:host-based authentication failed
      $

    비밀번호를 입력하려면 psql에서 '-u' 옵션을 사용하면 된다.

      $ psql -u linux
      Username: linuxer
      Password:
      Welcome to the POSTGRESQL interactive sql monitor:
      Please read the file COPYRIGHT for copyright terms of POSTGRESQL
      Type\? For help on slash commands
      Type\q to quit
      Type\g or terminate with semicolon to execute query
      You are currently connected to the database: linux
      Linux=>

    로컬 호스트에 굳이 이렇게 비밀번호를 걸어둘 필요는 없을 것이다. 오히려 번거롭기만 하다. 따라서 특정한 호스트에서 접속하는 경우에 사용자의 확인을 거칠 필요가 있다면, pg_passwd 유틸리티를 사용하면 유용하다.

4. 데이터베이스 접근 제어와 원격 접속: pg_hba.conf

    PostreSQL 에서 pg_hba.conf는 아주 유용한 파일이다. 보통 PostgreSQL 슈퍼우저만이 읽을 수 있도록 해두는 것이 좋다.

      $ Is -l pg_hba.conf
      -r--------1 postgres postgres 3776Now 11 08:47 pg_hba.conf

    pg_hba.conf 파일에서 첫번째 항의 'host'는 해당 레코드의 성격을 나타낸다.
    즉, 호스트 접속과 관련된 레코드 임을 알 수 있다.
    두번째 항은 접속을 허용할 데이터베이스이다. 'all'은 모든 데이터베이스를 통칭한다. 세번째 항은 접속을 허용하는 호스트의 IP 주소이다. 접속을 요청하는 호스트의 주소는 먼저 pg_hba.conf의 네번째 항의 마스크와 연산되어 IP주소와 비교된다. 따라서 192.168.1.1과 192.168.1.2등의 호스트에서의 접속을 허용하려면 MASK는 255.255.255.0로, IP 주소는 192.168.1.0으로 하면 된다. 다섯번째 항은 사용자인증 방법을 지정한다.
    'trust'는 인증 절차를 거치지 않음을 나타낸다. 'ident'는 RFC 1413에 따른 ident 프로토콜에 따라 인증 절차를 거친다는 것을 뜻한다. 마지막항은 인증에서 좀 더 세부적인 사항을 지정한다.

      #TYPE DATABASE IP_ADDRESS MASK USERAUTH MAP
      host all 127.0.0.1 255.255.255.255 trust
      host all 192.168.1.0 255.255.255.0 ident sameuser

    만일 허용되지 않는 호스트에서 접속을 하려면 다음과 같은 메시지가 나온다.

      $psql-h free.world.co.kr linux
      Connection to database 'linux' failed.
      Failed to authenticate client as Postgres user 'postgres' using the default
      Authentication type : be_recvauth: host-based authentication failed
      $

    psql에서 -h 옵션은 접속할 호스트 이름을 지정하는 데 사용된다. 만일, 원격 데이터베이스 시스템의 pg_hba.conf에 자신의 호스트가 접속할 수 있도록 되어 있다면, 자신의 시스템에서 원격 데이터베이스에 접속할 수 있다.

      [ddoch@han ddoch]$psql-h free.world.co.kr mydb
      Welcome to the POSTGRESQL interactive sql monitor:
      Please read the file COPYRIGHT for copyright terms of POSTGRESQL
      type\? For help on slash commands
      type\q to quit
      type\g or terminate with semicolon to execute query
      You are currently connected to the database: mydb
      mydb=>\!hostname
      han.world.co.kr
      mydb=>

    물론 psql은 자신의 시스템에서 실행되고, 입력하는 질의는 원격 호스트의 postmaster 포느에 전달되어 그 결과가 다시 자신의 시스템으로 돌아오는 것이다.

    따라서, 이것은 telnet으로 원격 시스템에 로긴하여 psql을 실행하는 것과 같은 결과를 가져오기는 하지만, 그 과정은 전혀 다르다. 원격 데이터베이스 접속 기능을 활용하면, 데잍베이스는 하나의 데이터베이스 서버에 두고, 실행은 원격 클라이언트의 프론트엔드 응용프로그램에서 처리함으로써 여러가지 작업 처리를 분산시키고 체계화할 수 있는 장점을 가질 수 있을 것이다.

5. Kerberos 인증 시스템

    대부분의 사용자는 이 정도의 인증 방법을 사용하면 무난할 것이다. 하지만, 보안상 주용한 데이터를 다룰 경우에는 이것으로도 부족하다. 전달되는 데이터 자체가 암호화되어야 할 필요성이 있다. 이를 위해 PostgreSQL는 kerberos 인증 시스템을 지원하고 있다. 하지만 PostgreSQL 자체에 Kerberos 패키지가 따라오지는 않는다. Kerberos는 ftp://authena-dist.mit.edu 에서 구할 수 있고, 자세한 문의사항은 info-kerberos@authena.mit.edu 에 해보기 바란다. 메일링리스트는 kerberos@athena.mit.edu 이고, 가입은 kerberos-request@athena.mit.edu 에 편지를 보내면 된다.
    USENET뉴스 그룹은 comp.protocols.kerberos 이다.
    미국과 캐나다 이외에 살고 있는 사용자의 Kerberos의 실제 암호화 코드의 사용은 미정부 수출 규제법에 의해 제한되므로 유의하는 것이 좋다.

    원래 이 번호 부터는 데이터베이스의 실질적인 활용측면에 주력할 생각이였으나 궁리 끝에 PostgreSQL에 관해 처음부터 꼼꼼히 챙기는 방향으로 하는 것이 좋을 것 같아 실질적인 응용프로그램의 제작은 조금 늦추어 질 것 같다. 이점 양해를 바란다.

, .

PostgreSql 명령어

PostgreSQL 2008. 7. 17. 15:47

psql to 'postgres'



말입니다 ..



이런 명령어 쓰는 ..



? : 도움말



a : 필드 정렬자 토글



C : html3 캡션 설정



c : 데이터베이스에 접속



d : 현재 데이터베이스의 전체 테이블, 또는 특정 테이블 출력



di : 데이터베이스 내부의 인덱스만 출력



ds : 데이터베이스 내부의 시퀀스만 출력



dt : 데이터베이스 내부의 테이블만 출력



e : 현재 버퍼에 있는 질의어나 파일을 편집



f : 필드 구분자 변경 (보통은 '|')



h : SQL 명령어에 대한 문법적 도움말 출력



H : 질의의 결과를 html3 으로 출력할지의 여부 결정



i : 외부 파일에서 질의를 읽어서 실행함



l : 시스템의 모든 데이터베이스를 출력



p : 현재의 질의 버퍼를 출력



q : 종료



r : 질의 버퍼를 청소



t : 헤더정보와 행의 갯수를 출력할지의 여부 결정



z : 현재의 허용/취소 권한 출력



! : 쉘 명령어 실행







에서 말이죠, \c 를 이용해서 데이터 베이스까지 접속은 합니다.



접속하면 이렇게 뜨죠



testdb-#



여기서 말인데, \i 를 이용하면 외부 질의를 실행하게 된다는데,



외부 파일을 어떻게 읽어 드리게 합니까?



testdb-#\i test.sql



이렇게 하니까 파일 디렉토리를 찾을수 없다고 하는데 .. ;


저, sql 파일은 복구할 dump 파일을 확장자만 바꾼것임.

psql 외부옵션

psql 모니터링 프로그램은 아주 유용한 외부옵션을 많이 제공한다. 이걸 사용하면 쉘스크립트로 PostgreSQL를 사용한 CGI 프로그램을 간단하게 짤 수 있다.

    -c 질의어 : psql 명령행으로 들어가지 않고 질의어만 전달하여 작업할 수 있다.
    간단한 PostgreSQL작업에 유용하다.

    -d 디비이름 : 접속할 데이터 베이스를 지정한다.

    -e : backend로 보낸 질의어를 echo 한다.

    -f 파일이름 : psql 내부에서 \i 명령을 사영하듯이, 외부에서도 SQL 질의어가 담긴
    파일을 지정하여 실행할 수 있다

    -H 호스트 이름 : postmaster 가 수행되고 있는 호스트에 접속한다 기본값은
    localhost 이다.

    -l : 사용가능한 데이테 베이스 목록을 출력한다.

    -n : psql 내부 명령행에서 readline 라이브러리를 사용하지 않는다.
    한글입력에 문제가 있을 때 사용할 수 있다.

    -p 포트 : postmaster 가 돌아가고 있는 인터넷 tcp 포트를 지정한다.
    기본값은 5432이다.

    -q : 여러 가지 부가적인 메시지를 출력하지 않도록 한다.

    -s : 싱글 스텝모드로 psql을 실행한다. 질의어를 실행하기 전에 엔터키를 한번더
    쳐야 한다. 조심해야 할 작업에 사용할 수 있다.

쉘에서 어떠한 목적으로 psql 내부에 들어가지 않고 작업을 할 수 있다.

    $ psql mydb -e -c "select * from 날씨"

다음호에서는 실제적인 업무에서 사용될 법한 좀더 복잡한 데이터 베이스를 PostgreSQL로 다루어 보면서 활용방안을 살펴보겠다.

, .