Code Metaphor

Programming, Writing, Reading, Thoughts…

PostgreSQL 8.3에서 XPath 대신 CSS 셀렉터 사용하기

이전 포스팅에서 PostgreSQL 8.3의 xpath() 함수를 사용했었다. 하지만 요즘에는 Prototype, MooTools의 $$, jQuery의 $ 등 덕분에 CSS 셀렉터에 대부분 더 친숙할 것이다. 나도 그렇고. 그래서 CSS 셀렉터를 사용할 수는 없을까 하고 생각했다.

이미 누군가가 CSS 셀렉터를 XPath로 컴파일해주는 것을 만들었을 것 같았다. 그래서 소스를 참고해서 PL/pgSQL1로 옮겨 쓰려고 생각했다. 근데 웬걸, 검색해보니 CPANHTML::Selector::XPath 모듈이 나온다. Perl 써본지는 얼마 되지 않았지만, CPAN의 모듈은 참 다양하면서도 품질이 좋다는 생각이 든다. 이걸 쓰면 잘 될 것 같다. 게다가 마침 Perl로 작성된 모듈이다! 왜 좋아하냐고? PostgreSQL은 Perl 코드로 프로시져(procedure)를 작성할 수 있기 때문이다. PostgreSQL에는 프로시져 작성을 위한 기본 언어로 PL/pgSQL가 있지만, 그 외에도 PL/Perl, PL/Python, PL/Ruby, PL/Java, PL/PHP 을 지원한다는 사실.

생각난 김에 바로 작업을 시작했다. Ubuntu Linux의 경우 postgresql-plperl-8.3, libhtml-selector-xpath-perl 패키지를 설치하면 된다.

$ sudo apt-get insstall postgresql-plperl-8.3 libhtml-selector-xpath-perl

HTML::Selector::XPath 모듈은 cpan 명령어로 설치해도 된다.

$ sudo cpan install HTML::Selector::XPath

PostgreSQL 내의 오브젝트로는 테이블(table), 뷰(view), 인덱스(index), 시퀀스(sequence), 함수(function) 등이 있는데, 언어(language) 역시 그 중 하나다. PL/Perl을 사용하려면 아래와 같이 등록해야 하다.

CREATE LANGUAGE plperl;

이렇게 해두면 프로시져 언어로 PL/Perl을 사용할 수 있게 된다. 시험삼아 아무 프로시져나 만들어보았다.

CREATE FUNCTION plperl_add(int, int)
    RETURNS int AS $$
        return $_[0] + $_[1];
    $$ LANGUAGE plperl;

코드를 보면 알 수 있듯, 매개변수들은 Perl답게 @_로 들어간다. 호출해보면 다음과 같이 잘 작동한다.

xmltest=# SELECT plperl_add(1, 2), plperl_add(3, 4);
 plperl_add | plperl_add 
------------+------------
          3 |          7
(1 row)

PL/Perl에서 CPAN 모듈도 사용 가능할까? 가능하긴 하다. 기본적으로 PL/Perl 코드에는 입출력이나 use 지시어 사용 따위가 불가능하게 되어 있다. 보안 때문이다. 이러한 안전한 함수를 신뢰 가능한 함수(trusted function)이라고 한다. 반대로 신뢰하기 힘든 함수(untrusted function)도 작성할 수 있다. 언어 이름을 plperl 대신 plperlu로 지정하면 된다. 물론 이것 역시 CREATE LANGUAGE로 등록하고 써야 한다.

CREATE LANGUAGE plperlu;

CREATE OR REPLACE FUNCTION css_selector_to_xpath(text)
    RETURNS text AS $$
        use HTML::Selector::XPath 'selector_to_xpath';
        return selector_to_xpath($_[0]);
    $$ LANGUAGE plperlu IMMUTABLE;

함수 몸통이 딱 두 줄이다. 짧다! 꼭 이렇게 쉽게 되면 작동하지 않을까 겁이 난다.

xmltest=# SELECT css_selector_to_xpath('li#main');
 css_selector_to_xpath 
-----------------------
 //li[@id='main']
(1 row)

xmltest=# SELECT css_selector_to_xpath('body#forum > .main > table td');
                             css_selector_to_xpath                             
-------------------------------------------------------------------------------
 //body[@id='forum']/*[contains(concat(' ', @class, ' '), ' main ')]/table//td
(1 row)

근데 너무 잘된다. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

그럼 xpath() 함수처럼 바로 XML 엘리먼트의 배열을 반환하게 하려면 어떻게 해야 할까? 싱거운 물음이다. 그것도 함수로 만들면 된다.

CREATE FUNCTION css_select(selector text, node xml)
    RETURNS xml[] AS $$
        BEGIN                   
            RETURN xpath(css_selector_to_xpath(selector), node);
        END;
    $$ LANGUAGE plpgsql IMMUTABLE;

진짜 싱겁다. 사용하면 아래처럼 잘 된다.

xmltest=# SELECT css_select('page > revision > contributor > username', page) AS username,
xmltest-#        css_select('page timestamp', page) AS timestamp
xmltest-# FROM wikipedia_pages LIMIT 10;
             username              |                   timestamp                   
-----------------------------------+-----------------------------------------------
 {<username>費勒姆</username>}     | {<timestamp>2007-09-15T00:41:06Z</timestamp>}
 {<username>TXiKiBoT</username>}   | {<timestamp>2008-06-03T12:40:11Z</timestamp>}
 {<username>AlnoktaBOT</username>} | {<timestamp>2008-06-08T23:10:21Z</timestamp>}
 {<username>TXiKiBoT</username>}   | {<timestamp>2008-06-01T17:56:27Z</timestamp>}
 {<username>計算機</username>}     | {<timestamp>2008-05-17T07:39:40Z</timestamp>}
 {<username>SieBot</username>}     | {<timestamp>2008-04-16T04:10:25Z</timestamp>}
 {<username>阿文</username>}       | {<timestamp>2008-03-09T05:53:07Z</timestamp>}
 {<username>Iokseng</username>}    | {<timestamp>2007-09-19T06:05:27Z</timestamp>}
 {<username>今古庸龍</username>}   | {<timestamp>2007-09-15T03:06:34Z</timestamp>}
 {<username>今古庸龍</username>}   | {<timestamp>2007-09-15T03:04:45Z</timestamp>}
(10 rows)

XPath와 달리 CSS 셀렉터는 노드 안쪽의 텍스트 값을 선택하는 text() 함수 같은 기능이 없다. 그래서 노드 단위로 선택되었지만 그건 어쨌든 CSS 셀렉터 스펙의 문제.

PostgreSQL 프로그래밍과 Perl 프로그래밍, 둘 다 너무 재미있는 것 같다.


  1. Oracle DBMS에서의 PL/SQL와 비슷한 PostgreSQL의 프로시저(procedure) 작성용 언어. 

This entry was posted on July 4, 2008 at 1:58 AM. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

3 Responses to “PostgreSQL 8.3에서 XPath 대신 CSS 셀렉터 사용하기”

  1. dahlia's me2DAY Says:

    홍민희의 생각…

    PostgreSQL 실험 2탄: PostgreSQL 8.3에서 XPath 대신 CSS 셀렉터 사용하기…

  2. 이흥섭 Says:

    와 ㅋㅋ 이건 정말 좋네요! 다양한 언어를 지원하는데다 이미 알 법한 언어도 있으니 굉장히 편하고 강력하게 쓸 수 있겠어요. 다음 프로젝트부터는 저도 PgSQL을 잔뜩 써봐야겠어요.

  3. khris Says:

    PostgreSQL 짱인거 같다능… 특히 PL/Python 이요 =3

Powered by WordPress. Styled by Hong, MinHee. XML Feed, Comments XML Feed.