Red Hat Enterprise Linux 4

보안 가이드

ISBN: N/A
차례
머리글
1. 아키텍쳐 관련 정보
2. 문서 약정
3. 레드햇 네트워크에 구입하신 제품을 등록하십시오
3.1. Red Hat 로그인 아이디 입력하기
3.2. 등록 번호를 입력하십시오
3.3. 귀사의 시스템을 연결하십시오
4. 앞으로 추가될 사항
4.1. 여러분의 의견을 기다리고 있습니다
I. 보안에 대한 기본적인 소개
1장. 보안 개요
1.1. 컴퓨터 보안이란 무엇인가?
1.2. 보안 제어
1.3. 결론
2장. 공격자와 취약점
2.1. 간략한 해커 역사
2.2. 네트워크 보안 위협
2.3. 서버 보안 위협
2.4. 워크스테이션과 가정용 PC 보안 위협
II. Red Hat Enterprise Linux를 보안 설정하기
3장. 보안 업데이트
3.1. 패키지 업데이트하기
4장. 워크스테이션 보안
4.1. 워크스테이션 보안 평가하기
4.2. BIOS와 부트로더 보안
4.3. 암호 보안
4.4. 관리 제어
4.5. 사용 가능한 네트워크 서비스
4.6. 개인 방화벽
4.7. 보안 강화된 통신 도구
5장. 서버 보안
5.1. TCP 래퍼와 xinetd를 사용하여 서비스 보안 강화하기
5.2. Portmap 보안 강화
5.3. NIS 보안 강화
5.4. NFS 보안 강화
5.5. Apache HTTP 서버 보안 강화
5.6. FTP 보안 강화
5.7. Sendmail 보안 강화
5.8. 청취 중인 포트 확인하기
6장. 가상 사설 통신망 (Virtual Private Networks)
6.1. VPN과 Red Hat Enterprise Linux
6.2. IPsec
6.3. IPsec 설치
6.4. IPsec 호스트 간 설정
6.5. IPsec 네트워크 간 설정
7장. 방화벽
7.1. Netfilter와 iptables
7.2. iptables 사용법
7.3. 일반 iptables 필터링
7.4. FORWARDNAT 규칙
7.5. 바이러스와 가짜 IP 주소
7.6. iptables와 연결 추적(Connection Tracking)
7.7. ip6tables
7.8. 추가 자료
III. 보안 평가
8장. 취약성 평가
8.1. 적의 마음으로 생각하기
8.2. 평가와 테스팅 정의하기
8.3. 도구를 평가하기
IV. 칩입과 보안 사고 대응
9장. 침입 탐지
9.1. 침입 탐지 시스템이란
9.2. 호스트 기반 IDS
9.3. 네트워크 기반 IDS
10장. 사고 대응
10.1. 사고 대응이란 무엇인가
10.2. 사고 대응 계획 세우기
10.3. 사고 대응 계획을 실행하기
10.4. 사고 조사
10.5. 자원 복구하기
10.6. 사고 보고하기
V. 부록
A. 하드웨어와 네트워크 보안
A.1. 보안 네트워크 구성 방식
A.2. 하드웨어 보안
B. 일반 보안 취약점과 공격
C. 공용 포트
색인
판권

출처 : http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/ko/security-guide/index.html


2장. 공격자와 취약점

좋은 보안 정책을 계획하고 구현하기 위해서는 먼저 공격자가 시스템에 침입하고자 결정하게된 계기와 동기를 알아보아야 합니다. 이를 알아보기에 앞서, 우선 공격자를 지칭하는 여러 용어들에 대하여 설명해 보겠습니다.

2.1. 간략한 해커 역사

오늘날 우리가 알고 있는 해커 (hacker)의 기원은 1960 년대 MIT테크 모델 철도 클럽(TMRC, Tech Model Railroad Club)에서 시작되었습니다. 복잡한 구조의 대형 기차모형을 제작하는 이 동호회에서는 독창적인 요령이나 문제 해결책을 발견한 동호회 회원을 지칭하는데 해커라는 이름을 사용하기시작했습니다.

그 이후 해커라는 용어는 컴퓨터 애호가로부터 능력이 뛰어난 프로그래머까지 모든 것을 포함하는 의미를 갖게 되었습니다.대부분 해커들의 일반적인 특징은 다른 사람의 영향을 받지 않고 스스로 컴퓨터 시스템과 네트워크 기능의 작업 방식에 대하여 자세히알아보고자 하는 정신입니다. 오픈 소스 소프트웨어 개발자들은 종종 자기 자신과 또한 함께 일하는 동료들을 해커로 지칭하며, 해커정신을 존경할 것을 요구합니다.

일반적으로 해커들은 정보와 전문적 기술을 탐구하고 이 정보를 공유하는 것이 사회에 대한 해커의 근본 윤리임을 제시하는 해커 윤리 (hacker ethic)를따릅니다. 이러한 지식을 탐구하는 일환으로 일부 해커들은 컴퓨터 시스템의 보안 침투를 시도하기도 합니다. 이러한 이유로매체에서는 종종 해커라는 용어를 비도덕적이고 악의를 가지고 범죄를 저지를 생각으로 불법으로 시스템과 네트워크에 침입한 사람을지칭하는데 사용합니다. 이러한 유형의 컴퓨터 해커에 대한 보다 적절한 용어는 크래커 (cracker) 입니다 — 1980 년대 중반에 해커들이 해커와 크래커에 차이를 두기 위해 만들어낸 용어입니다.

2.1.1. 다양한 종류의 해커

시스템과 네트워크의 취약점을 분석 파악하고 연구하는 해커들은 여러 다른 그룹으로 나뉘어 집니다. 이 그룹들은 이들이 보안 체계를 연구할 때 "착용하는" 모자의 색으로 구분되며, 이 색은 해킹의 의도를 나타냅니다.

white hat 해커란 네트워크와 시스템을 검사하여 얼마나 외부 침입에취약한지를 연구하는 사람입니다. 일반적으로 white hat 해커는 자신의 시스템이나 보안 검사를 위해 자신을 고용한 고객의시스템에 침투하여 연구합니다. white hat 해커의 대표적인 예로서는 대학 연구원이나 전문 보안 상담자를 들 수 있습니다.

black hat 해커는 크래커와 동일한 의미입니다. 일반적으로 크래커는 연구나 프로그래밍이 보다는 크래킹 프로그램을 사용하거나 잘 알려진 취약점을 이용하여 시스템에 침입한 후 기밀 정보를 빼내거나 목표 시스템이나 네트워크를 손상시킵니다.

반면 grey hat 해커는 대부분의 경우 white hat 해커의 지식과의도를 갖추고 있지만, 가끔씩 자신의 지식을 보다 정당하지 못한 의도로 사용하는 사람을 지칭합니다. grey hat 해커는white hat 해커이지만 가끔씩 자신의 욕심을 채우기 위해 black hat으로 변하는 해커라고 말할 수 있습니다.

Grey hat 해커는 일반적으로 다른 유형의 해커 윤리를 따릅니다. 이 해커 윤리에 따르면 해커가 도둑질이나 기밀을유포하지 않는 한 시스템에 침입하는 것을 허용합니다. 그러나 일부에서는 시스템에 침입하는 것 자체가 윤리적이지 못하다고비난합니다.

침입자의 의도에 상관없이 크래커가 시스템 침입에 사용할 취약점을 알아내는 것이 중요합니다. 이 장의 나머지 부분에서는 이러한 취약점을 중점으로 설명해 보겠습니다.


2.2. 네트워크 보안 위협

다음과 같은 네트워크 설정시 잘못된 설정으로 인해 침입의 위험이 증가될 수 있습니다.

2.2.1. 불안정한 구조

잘못 설정된 네트워크는 허가 없는 사용자들이 시스템에 침입할 수 있게 해주는 주요 시작 지점입니다. 로컬 네트워크를인터넷에 공개해놓는 것은 마치 우범 지역에서 집의 문을 활짝 열어놓는 것과 같습니다 — 얼마 동안은 아무런 일도 일어나지 않을지몰라도, 결국에는 누군가가 그 기회를 이용하여 침입할 것입니다.

2.2.1.1. 브로드캐스트 네트워크

시스템 관리자는 종종 보안 계획을 구상시 네트워크 하드웨어의 중요성을 간과하는 경우가 있습니다. 허브(hub)와 라우터와같이 브로드캐스트나 비교환 원칙에 의존하는 단순 하드웨어; 즉, 네트워크를 통하여 수신자 노드로 데이터를 전송시, 허브나라우터는 수신자 노드가 데이터 패킷을 받아서 프로세스할 때까지 데이터 패킷을 브로드캐스트합니다. 이러한 방법은 지역 네트워크상에서 허가 없는 사용자나 외부 침입자가 주소 결정 프로토콜 (arp)이나 MAC (media access control) 주소 스푸핑을 사용하여 쉽게 침입할 수 있게 해줍니다.

2.2.1.2. 중앙 집중형 서버

또 다른 네트워크 보안 위협으로서 중앙 집중식 컴퓨팅을 들 수 있습니다. 많은 사업체에서 경비를 절감하는 방법으로서 한개의 강력한 컴퓨터에 모든 서비스를 통합하는 경우가 있습니다. 여러 개의 서버를 설정하는 것 보다 상당히 경비도 절감되고관리하기에 편한 이점이 있지만, 이러한 중앙 집중형 서버를 사용하는 경우 만일 중앙 서버가 손상되면 네트워크 전체가 정지되거나또는 데이터 조작이나 도난당하기 쉽습니다. 이러한 경우 침입자는 중앙 서버를 이용하여 전체 네트워크에 접속 가능합니다.


2.3. 서버 보안 위협

서버 보안은 네트워크 보안 만큼 중요합니다. 그 이유는 기업체의 많은 중요한 정보가 서버에 저장되어 있기 때문입니다. 만일서버가 손상되면 크래커가 서버에 저장된 내용물을 모두 빼내거나 마음대로 조작할 가능성이 있습니다. 다음 부분에서는 이와 관련된중요한 문제점에 대하여 설명해 보겠습니다.

2.3.1. 사용되지 않은 서비스와 공개 포트

Red Hat Enterprise Linux를 전체 설치하시면 1000여개에 이르는 응용 프로그램과 라이브러리 패키지가설치됩니다. 그러나 대부분의 서버 관리자 분들은 배포판에 포함된 모든 개별 패키지를 설치하기 보다는, 대신 여러 서버 응용프로그램을 포함한 기본 패키지 설치를 선호합니다.

대부분의 시스템 관리자들은 실제로 어떠한 프로그램이 설치되는지 주시하지 않은 채 운영 체제를 설치하시는 경우가 많습니다.이러한 경우, 기본 설정에 포함된 필요치 않는 서비스가 설치되어 켜질 수 있으므로 문제가 됩니다. Telnet, DHCP,DNS와 같은 원치 않는 서비스가 관리자가 깨닫지 못한채 서버나 워크스테이션에서 실행되어, 서버에 필요없는 트래픽을 야기시키며심지어는 크래커가 시스템에 침입 가능하게 해줍니다. 포트를 닫거나 사용되지 않는 서비스를 비활성화 시키는 방법을 보시려면 5 장을 참조하시기 바랍니다.

2.3.2. 패치가 설치되지 않은 서비스

기본 설치에 포함된 대부분의 서버 응용 프로그램들은 철저하게 테스트와 검증을 거친 소프트웨어입니다. 여러 해를 거쳐 생산 환경에서 사용되면서, 이 소프트웨어의 코드가 보다 개선되고 발견된 다수의 문제점이 수정되었습니다.

그러나 완벽한 소프트웨어란 있을 수 없으며 언제든지 개선할 요소가 있기 마련입니다. 더우기 새로운 소프트웨어는 기대하는만큼 엄격하게 테스트되지 않는 경우가 종종 있습니다. 그 이유는 이 소프트웨어가 제품 환경에 출시된지 얼마되지 않아서 이거나또는 다른 서버 소프트웨어 만큼 많이 사용되지 않기 때문입니다.

개발자와 시스템 관리자는 서버 응용 프로그램에서 문제점을 발견한 경우 그 정보를 Bugtraq 메일링 리스트 (http://www.securityfocus.com)나 컴퓨터 비상 대응팀 (Computer Emergency Response Team: CERT) 웹사이트 (http://www.cert.org)와 같은 버그 추적과 보안 관련 웹사이트에 공개합니다. 이러한 방법은 커뮤니티에 보안 취약점을 빠르게 알려줄 수 있는 효율 적인방법이기는 하지만, 시스템 관리자들은 즉시 시스템을 패치를 설치하셔야 합니다. 크래커는 동일한 취약점 추적 서비스를 볼 수 있기때문에 재빠르게 패치가 설치되지 않은 시스템에 침입하여 정보를 빼내올 가능성이 있기 때문입니다. 훌륭한 시스템 관리자라면 보다안전한 컴퓨팅 환경을 만들기 위하여 항상 경계하며 지속적으로 버그 (문제점)을 추적하고 적절한 시스템 관리 작업을 수행해야합니다.

항상 업데이트된 시스템을 유지하는 방법에 대한 자세한 정보는 3 장을 참조하시기 바랍니다.

2.3.3. 부주의한 관리

관리자가 시스템에 패치를 설치하지 않는 것은 서버 보안에 있어서 가장 큰 위험 요소입니다. SANS (System Administration Network and Security Institute)에 따르면 컴퓨터 보안에 취약점을 가져오는 가장 주된 원인은 "재대로 교육 받지 않는 미숙한 사람을 보안 관리하도록 맡겨놓고 작업을 수행할 수 있는 시간이나 교육도 제공하지 않기 때문이다."[1]라고 합니다. 이러한 상황은 미숙한 관리자 뿐만 아니라 너무 자만하거나 의욕을 상실한 관리자에게도 적용됩니다.

일부 관리자들은 서버와 워크스테이션에 패치를 설치하는 것을 잊어버리는 경우가 있는 반면, 다른 어떤 관리자들은 시스템커널의 로그 메시지나 네트워크 트래픽을 잊고 살펴보지 않는 경우도 있습니다. 또 다른 흔한 실수로 서비스의 기본 암호나 키를변경하지 않고 그대로 사용하는 것을 들 수 있습니다. 예를 들어 일부 데이터베이스에는 시스템 관리자가 설치 후 암호를 즉시변경할 것이라는 가정 하에 기본 관리 암호가 할당됩니다. 만일 데이터베이스 관리자가 이 암호를 변경하지 않으면, 경험이 없는크래커도 잘 알려진 기본 암호를 사용하여 데이터베이스에 관리자 허가를 얻을 수 있습니다. 앞에서 설명된 것은 부주의한 관리가서버 침입에 미치는 영향을 보여주는 몇가지 예시일 뿐입니다.

2.3.4. 본질적으로 안전하지 못한 서비스

가장 경계가 투철한 기업체에서도 선택한 네트워크 서비스가 원래 안전하지 못하다면 공격당하기 쉽습니다. 예를 들어 신뢰하는네트워크 하에서 사용될 것이라는 가정 하에서 개발된 서비스가 많습니다; 그러나 이러한 가정은 서비스가 본질적으로 신뢰할 수 없는— 인터넷 상에서 사용 가능해지면 더 이상 적용되지 않습니다.

이러한 안전하지 못한 네트워크 서비스의 한 예로서 인증을 위해 암호화되지 않는 사용자명과 암호를 요구하는 서비스를 들 수있습니다. Telnet과 FTP가 이러한 서비스의 두 예입니다. 만일 패킷 스니핑 소프트웨어가 원격 사용자와 이러한 서비스사이의 트래픽을 감시 중이라면 서비스 사용자명과 암호를 쉽게 가로챌 수 있습니다.

이러한 서비스는 본질적으로 보안 산업에서 소위 말하는 man-in-the-middle공격의 대상이 되기 쉽습니다. 이러한 유형의 공격은 크래커가 네트워크 상의 이름 서버에 침입하여 자신의 컴퓨터를 원래 수신서버로 오인하게 하여 네트워크 트래픽을 방향 변경시키는 것을 말합니다. 누군가 서버로 원격 세션을 시작하면, 공격자의 컴퓨터는보이지 않는 관로 역할을 하며 원격 서비스와 아무것도 알아채지 못한 사용자 사이에서 조용히 정보를 빼내갑니다. 이러한 방법을사용하여 크래커는 서버나 사용자가 모르게 관리자 암호와 원자료를 가로챌 수 있습니다.

안전하지 못한 서비스에는 네트워크 파일 시스템인 NFS와 NIS 정보 서비스가 포함됩니다. 이러한 서비스는 LAN 사용을위해 만들어졌지만, 불행히도 원격 사용자를 위하여 WAN도 포함하도록 확장되었습니다. NFS는 기본적으로 크래커가 NFS 공유를마운트한 후 그 공유에 속한 자료에 접근하는 것을 방지할 수 있는 어떠한 인증이나 보안 메커니즘도 갖추고 있지 않습니다. NIS또한 평문 ACSII 또는 (ASCII에서 파생된) DBM 데이터베이스에 네트워크 상 모든 컴퓨터에 알려진 중요한 정보 (암호와파일 권한)를 보관합니다. 만일 크래커가 이 데이터베이스에 접속할 수 있게 된다면 네트워크 상 모든 사용자 계정을, 심지어는관리자의 계정까지도 사용 가능하게 됩니다.

Red Hat Enterprise Linux은 이러한 서비스를 사용하지 않도록 기본 설정되어 있습니다. 그러나 관리자들은종종 이러한 서비스를 불가피하게 사용해야할 경우가 있으므로, 신중하게 설정하는 것이 중요합니다. 안전한 방법으로 서비스를설정하는 방법에 대한 자세한 정보를 원하신다면, 5 장을 참조하시기 바랍니다.

주석

[1]

출처: http://www.sans.org/newlook/resources/errors.html


2.4. 워크스테이션과 가정용 PC 보안 위협

워크스테이션과 가정용 PC는 네트워크나 서버 만큼 자주 공격의 대상이 되지는 않습니다. 그러나 신용 카드 정보와 같이 기밀정보가 저장되어 있을 경우가 있으므로, 시스템 크래커들의 공격 대상이 되기도 합니다. 워크스테이션은 사용자가 모르게 공격자에의해서 준비된 공동 공격에 "종속" 컴퓨터로 사용될 수 있습니다. 이러한 이유로 사용자는 워크스테이션의 취약점을 미리 알아두시는 것이 이후 운영 체제를 재설치해야하는 번거러움을 줄일 수 있으며 더 나아가 자료를 도난당하는 사태를 방지할 수 있습니다.

2.4.1. 너무 단순한 암호

너무 단순한 암호는 공격자가 시스템에 접근할 수 있는 가장 쉬운 기회를 제공합니다. 암호 생성시 흔히 발생하는 실수를 피할 수 있는 방법은 4.3 절에 설명되어 있습니다.

2.4.2. 공격 당하기 쉬운 클라이언트 응용 프로그램들

관리자가 아무리 서버를 안전하게 패치를 설치한다고 해도, 그 서버에 접속하는 원격 사용자가 안전하다는 것을 의미하지는않습니다. 예를 들어 서버가 공개 네트워크 상에서 Telnet이나 FTP 서비스를 제공하는 경우, 공격자는 평문으로된 사용자명과암호를 네트워크 상에서 가로챌 수 있으며, 그 가로챈 계정 정보를 사용하여 원격 사용자의 워크스테이션에 접속할 수 있습니다.

SSH와 같이 보안 프로토콜을 사용하는 경우에도, 원격 사용자가 클라이언트 응용 프로그램을 항상 업데이트하지 않는다면이러한 공격을 당할 가능성이 있습니다. 예를 들어 v.1 SSH 클라이언트는 악의를 가진 SSH 서버로부터X-forwarding 공격을 받을 수 있습니다. 클라이언트가 서버에 연결되면, 공격자는 조용히 클라이언트가 네트워크 상에서누르는 키조합이나 마우스 클릭을 캡쳐할 수 있습니다. 이러한 문제점은 v.2 SSH 프로토콜에서는 고쳐졌지만, 이러한 응용프로그램의 취약점을 알고 필요한 경우 업데이트하는 것은 사용자의 몫입니다.

4 장에서는 관리자와 사용자가 컴퓨터 워크스테이션의 취약점을 보완하기 위하여 따르셔야할 단계들을 보다 자세하게 설명하고 있습니다.


....



5장. 서버 보안

시스템이 공중 네트워크에서 서버로 사용될 경우 공격의 대상이 되기 쉽습니다. 이러한 이유로 시스템 보안을 보강하고 서비스를 잠그는 것은 시스템 관리자에게 무엇보다 중요합니다.

특정 사항에 대하여 깊이 파고들기 이전에 서버 보안을 강화시킬 수 있는 일반적인 힌트를 다음에서 간략히 살펴보도록 하겠습니다:

  • 최신 침입 유형에 대비하여 모든 서비스를 항상 업데이트 시키십시오.

  • 가능한 보안 프로토콜을 사용하십시오.

  • 가능한 한 기계당 한가지 유형의 네트워크 서비스를 사용하십시오.

  • 모든 서버에서 수상한 행동이 발견되는지 주의깊게 감시하십시오.

5.1. TCP 래퍼와 xinetd를 사용하여 서비스 보안 강화하기

TCP 래퍼(wrappers)는 다양한 서비스에 접근 제어를 제공합니다. SSH, Telnet, FTP와 같은 대부분의 최신 네트워크 서비스는 들어오는 요청과 요청된 서비스 사이에서 감시 역할을 하는 TCP 래퍼를 사용합니다.

추가 액세스, 기록, 바인딩, 방향 전환 및 자원 활용 제어와 같은 기능을 제공하는 수퍼 서비스인 xinetd를 함께 사용하면 TCP 래퍼가 제공하는 보안 기능이 보다 강화됩니다.

힌트힌트

TCP 래퍼와 xinetd와 함께 IPTables 방화벽 규칙을 사용하여 서비스 접근 제어에 중복성을 갖는 것이 좋습니다. IPTables 명령을 사용하여 방화벽을 구현하는 방법에 대한 자세한 정보는 7 장을 참조하시기 바랍니다.

TCP 래퍼와 xinetd를 설정하는 방법에 대한 추가 정보는 Red Hat Enterprise Linux 참조 가이드TCP 래퍼와 xinetd 장에서 찾으실 수 있습니다.

다음 부분에서는 여러분이 각 주제에 대한 기본적인 지식을 갖추고 계신다고 간주하고 특정 보안 옵션에 중점을 두고 설명해 보겠습니다.

5.1.1. TCP 래퍼를 사용하여 보안 강화하기

TCP 래퍼는 서비스로의 액세스를 거부하는 것 이외에도 다른 많은 기능을 제공합니다. 이 부분에서는 TCP 래퍼를 사용하여연결 배너를 보내고, 특정 호스트에서 침입자에게 경고 메시지를 보내며, 기록 기능을 강화하는 방법에 대하여 설명하고 있습니다.TCP 래퍼의 기능과 제어 언어에 대한 전체적인 목록을 보시려면 hosts_options 메뉴얼 페이지를 참조하시기 바랍니다.

5.1.1.1. TCP 래퍼와 연결 배너

서비스에 접속하는 클라이언트에 경고성 배너를 보내는 것이 서버가 어떠한 시스템을 운영 중인지 보여주지 않으면서 동시에침입자에게 시스템 관리자가 감시 중이라고 알려줄 수 있는 좋은 방법입니다. 서비스에 TCP 래퍼 배너를 구현하시려면 banner 옵션을 사용하십시오.

이 예시는 vsftpd에 배너를 사용합니다. 먼저 배너 파일을 생성하셔야 합니다. 시스템 상 어디에서든 생성하실 수 있지만 이 파일을 사용될 데몬과 동일한 이름을 가져야 합니다. 이 예시에서 파일 이름은 /etc/banners/vsftpd 입니다.

파일의 내용은 다음과 같습니다:

220-Hello, %c 220-All activity on ftp.example.com is logged. 220-Act up and you will be banned.

%c 토큰은 사용자명, 호스트명 또는 연결 메시지에 보다 효과가 있도록 사용자명과 IP 주소와 같은 다양한 클라이언트 정보를 제공합니다. Red Hat Enterprise Linux 참조 가이드에서는 TCP 래퍼에 사용 가능한 다른 토큰 목록을 찾으실 수 있습니다.

이 배너가 들어오는 접속에 보여지도록 하시려면 /etc/hosts.allow 파일에서 다음 줄을 추가하시면 됩니다:

vsftpd : ALL : banners /etc/banners/

5.1.1.2. TCP 래퍼와 침입 경고

만일 특정 호스트나 네트워크가 서버를 침입하는 것이 발견되었다면 TCP 래퍼에 spawn 지시자를 사용하여 침입이 시도된 호스트나 네트워크의 관리자에게 경고 메시지를 보낼 수 있습니다.

예를 들어 206.182.68.0/24 네트워크에서 크래커가 서버에 침입 시도하려는 것이 발견되었다고 가정해봅니다. /etc/hosts.deny 파일에 다음과 같은 줄을 추가하시면, 연결 시도가 거부되며 특별 파일에 기록될 것입니다:

ALL : 206.182.68.0 : spawn /bin/ 'date' %c %d >> /var/log/intruder_alert

%d 토큰은 침입자가 접근하려고 시도한 서비스의 이름을 보여줍니다.

연결을 허용 후 기록하기 위해서는 /etc/hosts.allow 파일에 spawn 지시자를 추가하시기 바랍니다.

알림알림

spawn 지시자는 모든 쉘 명령을 실행하므로, 특정 클라이언트가 서버에 접속을 시도할 경우 관리자에게 알리거나 여러 명령을 수행할 특수 스크립트를 작성하십시오.

5.1.1.3. TCP 래퍼와 향상된 기록 기능

만일 특정 유형의 접속이 다른 유형 보다 중요하다면 severity 옵션을 사용하여 해당 서비스에 대한 여러 다른 기록 수준을 설정하실 수 있습니다.

이 예시에서는 FTP 서버 포트 23 (Telnet 포트)로 접속을 시도하는 사용자를 크래커라고 가정합니다. 크래커가 침입하는 것을 방지하기 위하여 로그 파일에서 기본 플래그(flag)인 info 대신 emerg 플래그를 지정하시고 이 포트로 들어오는 연결을 거부합니다.

연결을 거부하기 위해서는 /etc/hosts.deny 파일에 다음 줄을 추가하시면 됩니다:

in.telnetd : ALL : severity emerg

이러한 설정은 기본 authpriv 기록 기능을 사용하지만 기록 심각성 수준을 기본 값인 info에서 emerg 수준으로 높여서 로그 메시지를 콘솔에 바로 보여줍니다.

5.1.2. xinetd를 사용하여 보안 강화하기

xinetd 수퍼 서버는 종속 서비스로의 접근을 제어하는데 유용하게 사용되는 또 다른 도구입니다. 이 부분에서는 xinetd를 사용하여 트랩(trap) 서비스를 설정하는 방법과 서비스 거부 공격을 좌절시키기 위하여 xinetd 서비스가 사용할 수 있는 자원의 양을 제어하는 방법에 대하여 중점적으로 설명해 보겠습니다. 모든 사용 가능한 옵션의 목록을 보시려면 xinetdxinetd.conf의 메뉴얼 페이지를 참조하시기 바랍니다.

5.1.2.1. 트랩(Trap) 설정하기

xinetd의 중요한 기능 중 하나는 전역 no_access 목록에 호스트를 추가할 수 있는 기능입니다. 이 목록에 포함된 호스트는 xinetd가 관리하는 서비스에 정해진 기간 동안 또는 xinetd가 재시작될 때까지 연결을 거부당합니다. 이 기능은 SENSOR 속성을 통해 실행 가능하며, 서버에서 포트를 스캔하려고 시도하는 호스트를 손쉽게 막을 수 있는 방법입니다.

SENSOR를 설정하기 위한 첫번째 단계는 사용할 계획이 없는 서비스를 선택하는 것입니다. 이 예에서는 Telnet이 사용됩니다.

/etc/xinetd.d/telnet 파일에서 flags 줄을 다음과 같이 수정하시기 바랍니다:

flags = SENSOR

괄호 내에 다음 줄을 추가하십시오:

deny_time = 30

이 설정은 포트로 연결을 시도하는 호스트를 30 분 동안 거부할 것입니다. deny_time 속성에 사용 가능한 다른 값에는 FOREVER와 NEVER가 있습니다. FORVER는 xinetd가 재시작될 때까지 연결을 거부하며, NEVER는 연결을 허용한 후 기록합니다.

마지막 줄을 다음과 같이 수정하십시오:

disable = no

SENSOR를 사용하여 보안을 위협하는 호스트로부터 연결을 검색하여 정지시키는 것이 좋은 방법이기는 하지만, 다음과 같은 두가지 결점이 있습니다:

  • 스텔스 스캔 (쉽게 발견되지 않도록 한 스캔)을 찾아내지 못합니다.

  • 만일 침입자가 SENSOR가 실행 중인 사실을 이미 알고 있다면 자신의 IP 주소를 위장하여 특정 호스트에 서비스 거부 공격을 마운트한 후 금지된 포트에 연결할 수 있습니다.

5.1.2.2. 서버 자원을 제어하기

xinetd의 또 다른 중요한 기능은 서비스가 활용 가능한 자원의 양을 제어할 수 있는 기능입니다.

다음 지시자를 통하여 이 기능을 사용 가능합니다:

  • cps = <number_of_connections> <wait_period> — 1초당 서비스에 허용되는 연결 수를 지정합니다. 이 지시자는 반드시 정수값으로 설정하셔야 합니다.

  • instances = <number_of_connections> — 한 서비스에 허용되는 총 연결 수를 지정합니다.이 지시자는 정수값이나 UNLIMITED 값을 수용합니다.

  • per_source = <number_of_connections> — 각 호스트마다 서비스에 연결할 수 있는 수를 지정합니다. 이 지시자는 정수값이나 UNLIMITED으로 지정하셔야 합니다.

  • rlimit_as = <number[K|M]> — 서비스가 차지할 수 있는 메모리 주소 공간의 용량을 킬로바이트 또는 메가바이트 단위로 지정합니다. 이 지시자는 정수값이나 UNLIMITED로 설정하셔야 합니다.

  • rlimit_cpu = <number_of_seconds> — 서비스가 CPU를 사용할 수 있는 시간을 초 단위로 지정합니다. 이 지시자는 정수값이나 UNLIMITED으로 설정하셔야 합니다.

이러한 지시자를 사용하시면, 서비스 거부 공격을 통해 xinetd 서비스가 시스템을 마비시키는 상황을 방지하는데 도움이 됩니다.


5.2. Portmap 보안 강화

portmap 서비스는 NIS와NFS와 같은 RPC 서비스에 사용되는 동적 포트 할당 데몬입니다. 이 데몬은 허술한 인증 메커니즘을 갖추고 있으며 데몬이제어하는 서비스에 광범위한 포트를 할당 가능합니다. 따라서 보안 관리가 쉽지 않습니다.

알림알림

portmap을 보안 강화하게 되면 NFSv2와 NFSv3만 영향을 받습니다. NFSv4는 더 이상 portmap을 사용하지 않으므로 영향을 받지 않습니다. NFSv2 이나 NFSv3 서버를 구현할 계획이라면, portmap이 사용되므로 다음 부분에서 설명된 내용을 따르십시오.

RPC 서비스를 실행하신다면 다음과 같은 기본 규칙을 따르십시오.

5.2.1. TCP 래퍼를 사용하여 portmap 보호하기

portmap 서비스에는 내장된 인증 방식이 없으므로 TCP 래퍼를 사용하여 이 서비스를 사용할 수 있는 네트워크나 호스트를 제한하는 것이 중요합니다.

또한 서비스로 접근을 제한하실 때는 IP 주소만 사용하셔야 합니다. 호스트명은 DNS poisoning이나 다른 방법으로 위조가 가능하므로 사용하지 마십시오.

5.2.2. IPTables를 사용하여 portmap 보호하기

portmap 서비스로 접근을 더 제한하시려면 서버에 IPTables 규칙을 추가하여 특정 네트워크로 접근하는 것을 제한하시는 것이 좋습니다.

다음은 (포트 111을 청취하는) portmap 서비스로 192.168.0/24 네트워크와 로컬호스트에서 TCP 연결을 허용하는 두가지 IPTables 명령 예시입니다. (Nautilussgi_fam 서비스를 사용하는데 필요한 설정입니다). 모든 다른 패킷은 버립니다(drop).

iptables -A INPUT -p tcp -s! 192.168.0.0/24 --dport 111 -j DROP iptables -A INPUT -p tcp -s 127.0.0.1 --dport 111 -j ACCEPT

이와 유사한 방식으로 UDP 트래픽을 제한하기 위해서는 다음 명령을 사용하십시오.

iptables -A INPUT -p udp -s! 192.168.0.0/24 --dport 111 -j DROP
힌트힌트

IPTables 명령과 함께 방화벽 규칙을 구현하는 방법에 대한 보다 자세한 정보를 원하신다면 7 장을 참조하시기 바랍니다.

, .

DB 에 들어가서..

#create function plpgsql_call_handler() returns opaque as '/usr/local/pgsql/lib/plpgsql.so' language 'C';


#create trusted procedural language 'plpgsql' handler plpgsql_call_handler lancompiler 'PL/pgSQL';


CREATE FUNCTION pgl_get_rows() RETURNS SETOF RECORD AS
'
DECLARE
row RECORD;

BEGIN
FOR row IN EXECUTE ''select field1,field2 from pkg_test ''
LOOP
RETURN next row;

END LOOP;
RETURN;
END;
' LANGUAGE 'plpgsql' ;

CREATE TABLE FUNCTION_NAME([PARAM1,PARAM2,..]) RETURNS [SETOF] DATA_TYPE
AS '
USER_DEFINITION
' LANGUAGE 'lang';

위의 []는 생략 가능함을 의미한다. lang에는 sql,plpgsql,c 등이 올수 있다.


다음은 앞부분(기초편1)에서 언급된 function 예이다. 실행 결과는 앞부분을 참조하라.


CREATE FUNCTION SQL_FUNC_1(int4) RETURNS text AS '
SELECT
CASE
WHEN NOGADA_TAB.id_ngd = $1
THEN ''있군요..''::text
ELSE ''없군요..''::text
END
' LANGUAGE 'sql';

SELECT SQL_FUNC_1(1) ;


다음은 포스트그레스의 function으로 간단한 예와 그 결과이다.


create function func1() returns text
as ' select \'nogadax\'::text \; '
language 'sql';


select func1() as name;

name
-------
nogadax
(1 row)



returns 에 예약어 setof 를 사용하면 한번에 여러 데이타를 받을 수 있다. 리턴형에는 일반 자료형뿐만아니라 테이블이나 뷰도 포함된다.


create table tablex (id_t int4, name text);

insert into tablex values ( 1, 'abc');
insert into tablex values ( 2, 'bcd');
insert into tablex values ( 3, 'cde');
insert into tablex values ( 4, 'def');

create function func2(int4) returns setof tablex
as ' select * from tablex where id_t > $1 '
language 'sql';

select id_t(func2(1)) as id ;

id
---
2
3
4
(3 rows)

select id_t(func2(2)) as id , name(func2(2)) as name;

id | name
-----+--------
3 | cde
4 | def
(2 rows)

Create Function fnBoardMaxNum(text) returns Integer As '
Declare
cQuery text;
cTable ALIAS FOR $1;
nMaxNum Integer;
pResult record;
Begin
cQuery := ''Select max(nBoardNum) as nMaxNum From '' || cTable;
For pResult In Execute cQuery
LOOP
nMaxNum := pResult.nMaxNum;
End Loop;

return nMaxNum;
end;
' Language 'plpgsql';

, .

출처 : 데이터베이스 사랑넷(http://database.sarang.net)

원본 위치 : http://database.sarang.net/?inc=read&aid=5870&criteria=pgsql&subcrit=&id=&limit=20&page=1

질답란에 pg_restore 이야기 있어 잠깐 살펴보았는데,

이 멋진 프로그램이 7.1 버전부터 있었다는 것에 놀랐습니다.

늘 pg_dump mydb > mydb.sql ; psql -f mydb.sql mydb

이런식으로 백업과 복구를 했었는데,

pg_restore 놈이 이렇게 참한 놈이였음을 누군가가 이야기 해주었다면, :(

이야기 시작합니다.

pg_restore 명령은 pg_dump -F{c|t} 명령에 의해서 만들어진 덤프 파일을 사용합니다.

이 명령으로 거의 모든 객체를 개별적으로 복구시킬 수 있습니다.

테이블(-t), 인덱스(-I), 함수(-P), 트리거(-T)는 기본적으로 명령행 옵션으로 지정할 수 있으며, -L 명령을 이용해서 개별적으로 지정한 하나하나씩 섬세하게 복구할 수도 있습니다.

(하나 아쉬운 것은 특정 스키마(namespace) 전체를 쉽게 복구할 방법은 없네요.)

스키마(namespace)단위로 관리된다면, pg_dump 를 사용할 때, -n 옵션으로 개별적으로 덤프받으시길 바랍니다.

pg_dump로 백업하기

pg_dump -h localhost -U postgres -Fc -f mydb.backup mydb

(mydb 데이터베이스 전체를 백업 받습니다)

pg_dump -h localhost -U postgres -Ft -f mydb.backup mydb

(윗 명령과 동일한데, mydb.backup 파일의 사이즈가 커집니다. 대신 복구할 때 압축을 풀지 않으니 좀 더 빠르겠지요)

pg_dump -h localhost -U postgres -Ft -f mydb.backup -n ioseph mydb

(mydb 데이터베이스에서 ioseph schema 영역의 자료만 백업받습니다)

pg_dump -h localhost -U postgres -Ft -f mydb.backup -t zipcode mydb

(mydb 데이터베이스에서 zipcode 테이블만 백업 받습니다)

다음 pg_restore 로 복구하기.

먼저 덤프 받은 backup 파일의 내용을 보려면, -l 옵션을 이용합니다.

pg_restore -l mydb.backup

나중에 이 내용을 기반으로 특정 부분만 따로 복구해야할 상황이면, 이 출력되는 내용을 파일로 저장해두어야합니다.

pg_restore -l mydb.backup > mydb.toc

또는

pg_restore -l -f mydb.toc mydb.backup

다음,

일단 데이터베이스가 없다면, 데이터베이스부터 만들어야합니다.

데이터베이스를 만드려면, 일단 접속할 수 있는 기본 데이터베이스를 지정해야합니다.

pg_restore -h localhost -U postgres -Ft -C -d template1 mydb.backup

(mydb 데이터베이스를 일단 만들고 mydb.backup 안에 있는 내용을 복구합니다.)

기본적으로 -d 옵션을 지정하지 않으면, 표준 출력으로 쿼리를 보냅니다.

pg_restore -Ft mydb.backup > mydb.sql

즉, mydb.sql 의 내용은 pg_dump mydb > mysql.sql 명령에 의해서 만들어지는 내용과 동일합니다.

즉, 이 backup 파일의 내용을 복구시키려면, -d 옵션으로 해당 데이터베이스를 지정해 주어야합니다.

pg_restore -h localhost -U postgres -Ft -d mydb mydb.backup

다음은 toc 파일을 이용한 부분 복구

윗 부분에서 pg_restore -l -f mydb.toc mydb.backup 명령으로 만들어진 mydb.toc 파일을 열어서

복구하지 않아도 되는 부분의 자료 맨 앞에 ; 표시를 해 두거나 그 줄을 삭제 하면 됩니다.

그리고,

pg_restore -h localhost -U postgres -L mydb.toc -d mydb mydb.backup

이런식으로 복구합니다.

마무리하며,

pg_restore 명령을 사용해보면서, 이 명령어를 내부적으로 사용하는 gui 툴을 하나 만들어도 참할 것 같다는 생각이 들더군요.

, .

[펌] Pro*c참고

Oracle 2009. 6. 30. 19:04
출처 대한민국 대표 개발자 [짱가™] | 짱가
원문 http://blog.naver.com/knbawe/100002483290

이글은 초보자를 위한 것으로 Pro*C란 무엇인가를 설명해 놓은 것일 뿐입니다. 그냥 참조만 하시기 바랍니다.

원본 출처 : http://home.bcline.com/hoya1/

1.1 Pro*C란?

ORACLE RDBMS에 준비된 Pro*C툴은, SQL문을 포함한 C 프로그램을, ORACLE 데이터베이스내에 있는 데이터에 접근과, 조작을 할 수 있는 C 프로그램으로 변환하기 위한 것이다. Pro*C는 프리컴파일러이기 때문에, 입력 파일 내에 있는 EXEC SQL 문을 적당한 ORACLE 콜로 변환해서 출력 파일을 작성한다. 그 다음 이 출력 파일을 C 프로그램에 대한 통상의 방법으로 컴파일하고 링크해서 실행모듈을 작성한다.

1.2 개요

Pro*C 툴을 사용하면 프로그램머는 통상의 프로그래밍 과정에 덧붙여서 별도의 처리를 수행 할 필요가 생긴다. 그러나 별도의 처리를 추가함으로써, 프로그램머에 도움이 되는 상당량의 작업을 Pro*C 툴이 수행하게 된다.

 C 프로그램을 작성해서 기동할 때의 통상의 작업 순서는 다음과 같다.       1. C 프로그램을 작성한다.       2. 프로그램을 컴파일해서 오브젝트 파일을 작성한다.       3. 오브젝트 파일을 링크해서 실행 가능한 파일을 작성한다.       4. 프로그램을 실행한다.  프로그래머가 소스프로그램에 Pro*C 문을 짜넣는 경우는, 위에서 기술한 순서에    한가지 처리가 더 추가된다.       1. Pro*C 프로그램을 작성한다.       2. Pro*C를 이용해서 프로그램을 프리컴파일 한다.       3. 프로그램를 컴파일해서 오브젝트 파일을 작성한다.       4. 오브젝트 파일을 링크해서 실행 가능한 파일을 작성한다.       5. 프로그램을 실행한다.

1.2.1 C 커맨드 및 SQL 문의 혼합

올바른 SQL 문이면 C 프로그램에서 실행할 수 있다. Pro*C 프로그램에서는 필수의 구성요소나 문장이 몇 개정도 있는 것 외에 기본적인 "지정순서"가 있지만, C 프로그램내의 어디에 배치해도 좋다

1.2.2 커맨드의 접두사 EXEC SQL

SQL 문을 호스트 언어에 포함시킴으로써, 발생할 수 있는 언어상의 장애를 최소화하기 위해서, 모든 SQL 문에는 EXEC SQL 이라고 하는 접두사를 붙인다.

1.2.3 커맨드의 접두사 EXEC ORACLE

대부분의 Pro*C 문에는 EXEC SQL 이라는 접두사를 붙이지만 EXEC ORACLE 이라는 접두사를 붙인문도 있다. 이들의 문은 SQL과는 호환성이 없고, ORACLE 프리컴파일러 특유의 것이다.

1.2.4 SQL 실행문 및 선언문

Pro*C 프로그램에 포함되어 있는 SQL 문은, 실행문 혹은 선언문중의 하나로 분류할 수 있다. 실행문 혹은 선언문에 관계없이 문에는 모두 EXEC SQL 이라는 접두사가 붙는다.

 실행문

실제로 데이터베이스에 대한 콜을 행성하는 SQL 문이다. 데이터조작문(DML), 데이터정의문(DDL), 데이터제어문(DCL) 등이 있다. SQL 실행문을 실행한 후, SQLCA(SQL 통신영역)에는 일련의 리턴코드가 저장된다.

논리적인 작업단위는, 최초의 SQL 실행문을 실행함으로써 시작된다. 그러므로 CONNECT, COMMIT, ROLLBACK WORK 문의 다음에 첫 번째로 나타나는 SQL 실행문부터 논리적인 작업단위가 새롭게 시작된다.

 선언문

코드를 생성하지 않기 때문에 논리적인 작업단위에 영향은 없다.

1.2.5 Pro*C 프로그램의 구성

Pro*C 프로그램은 2개의 부분으로 구성되어 있고, Pro*C의 처리에는 양자가 모두 필요하다.

 어플리케이션 프롤로그

변수를 정의하고, Pro*C 프로그램을 위한 일반적인 준비를 수행한다.

 어플리케이션 본체

ORACLE 데이터를 조작하기 위한 INSERT나 UPDATE 등의 SQL 문을 포함한다. Pro*C가 처리시 필요로 하는 코드의 전후에 어떠한 C 코드를 지정해도 상관없다.

2.1 어플리케이션 프롤로그

** 선언절

** INCLUDE SQLCA 문

 ** CONNECT 문

2.1.1 선언절 ( 2.0 버젼 이후 없어도 상관 없음 )

C 프로그램내에서 사용된 모든 호스트 변수를 선언한다. 선언절은 아래의 문으로 시작한다.

EXEC SQL BEGIN DECLARE SECTION;

아래의 문으로 종료한다.

EXEC SQL END DECLARE SECTION;

위 두 개의 문 사이에 허용되는 문은 호스트 변수 또는 표지 변수를 선언하는 문 뿐이다.

**호스트 변수

SQL 문 및 프로그램 문의 양쪽으로부터 참조되는 모든 값에 대해서 선언해야 한다. 호스트 변수의 데이터타입은 선언절에서 호스트 언어를 사용해서 선언해야 하며, 이 때 이 데이터타입은 테이블을 정의할 때에 사용되는 ORACLE 데이터타입과 일치할 필요는 없다.

EXEC SQL BEGIN DECLARE SECTION;

int pempno; /* 사번 */

char pname[11]; /* 성명 */

int pdeptno; /* 부서 */

EXEC SQL END DECLARE SECTION;

EXEC SQL SELECT deptno, ename

INTO :pdeptno, :pname

FROM emp

WHERE empno = :pempno;

호스트 변수의 조건

~선언절에서 명시적으로 선언한다.

~선언한 대로 영어 대문자/소문자의 포맷을 사용한다.

~SQL 문에서는 앞에 콜론(:)을 붙인다.

~C 문에서는 앞에 콜론을 붙이지 않는다.

~SQL의 예약어를 사용해서는 안된다.

~상수를 사용할 수 있는 곳에서만 사용한다.

~표지 변수가 붙어 있어도 상관없다.

**표지 변수

선언절에서 선언된 호스트 변수에 1대 1로 대응되는 임의 선택 변수이다. 표지 변수는 주로 널 값을 취급하는데 유효하다

표지 변수의 조건

~선언한 대로 영어 대문자/소문자의 포맷을 사용한다.

~2바이트 정수로써 선언해야 한다.

~SQL 문에서는 앞에 콜론(:)을 붙인다.

~C 문에서는 앞에 콜론을 붙이지 않는다.

~SQL의 예약어를 사용해서는 안된다.

~SQL 문 내에서는 대응하는 입력 호스트 변수를 앞에 붙여 사용한다.

호스트 변수로서의 포인터 선언

포인터 변수는 C에서 통상적으로 행하는 방법으로 선언함으로써, 선언절내에서 사용할 수 있다.

EXEC SQL BEGIN DECLARE SECTION;

int I, j, *intptr;

char *cp;

EXEC SQL END DECLARE SECTION;

SQL 문에서 사용하는 경우는 별표가 아닌 콜론(:)을 변수명 앞에 붙인다.

SELECT intfield INTO :intptr FROM…;

VARCHAR 의사타입의 선언

Proc*C에서는 VARCHAR 의사타입을 사용할 수 있으므로, 가변 길이의 문자열을 처리할 수 있다. VARCHAR 의사타입은 선언절에서 참조할 뿐이고, 확장된 C 타입 또는 사전에 선언된 구조라고 생각할 수 있다.

EXEC SQL BEGIN DECLARE SECTION;

VARCHAR jobDesc[40];

EXEC SQL END DECLARE SECTION;

이 선언은 다음의 구조체로 확장할 수 있다.

struct {    unsigned short int len;    unsigned char arr[40];} jobDesc;

2.1.2 SQL 통신영역의 선언

어플리케이션 프로롤로그내에서 SQL 통신영역(SQLCA)에 대한 참조를 포함시킴으로써 각 Pro*C 프로그램에서 발생하는 상황처리를 가능하게 하는데, 유저는 다음의 문을 지정하면 된다.

EXEC SQL INCLUDE SQLCA;

Pro*C는 프리컴파일시에 SQLCA 파일의 위치를 알아야 한다. 때문에 유저는 다음의 3가지중 하나를 선택해야 한다.

 ~~"INCLUDE = " 커맨드 라인 옵션을 사용한다.

 ~~Pro*C가 파일을 SYS$ORACLE:SQLCA(VMS의 경우)처럼 공동의 OS영역에서 발견할 수 있도록 파일의 정식 명칭을 지정한다.

 ~~PCC를 호출할 디렉토리 또는 디스크에 SQLCA를 카피한다.

SQLCA는 다음과 같은 정보를 포함하고 있다.

 경고 플래그와 처리상황에 관한 정보

 에러 코드

 진단 정보

디폴드 값으로서, Pro*C는 가능한한 에러를 무시하고 처리를 속행시킨다. SQLCA내에 포함된 변수를 사용함으로써, 프로그래머는 각각의 환경에서 실행해야 할 처리를 제어할 수 있다.

2.1.3 ORACA(SQLCA에 대한 확장)

ORACA를 사용하기 위해서는 EXEC SQL INCLUDE을 사용해서 ORACA의 정의를 참조하거나 커맨드라인 옵션 또는 EXEC ORACLE OPTION에서 ORACA = YES 옵션을 선택하여야 한다.

2.1.4 ORACA 내의 정보

  현재의 SQL 문의 텍스트(orastxt)  ORACLE RDBMS가 해석한 문의 내용을 조사할 수 있다. (CONNECT, FETCH, COMMIT등 은 제외)  ~에러가 있는 파일의 이름(orasfnm)  ~에러가 있는 행의 번호(oraslnr)  ~SQL 문 보존 플래그(orastxtf)        이 플래그를 설정함으로써 어느 조건으로 문을 보존할 지를 선택할 수 있다.          0. 디폴트값. SQL 문을 보존하지 않는다.          1. SQLERROR가 있는 SQL 문 만을 보존한다.          2. SQLERROR 및 SQLWARN이 있는 문을 보존한다.          3. SQL 문을 전부 보존한다.    ~DEBUG 처리의 사용 허가 플래그         이 플래그는 0이나 1을 설정할 수 있다.          1을 설정한 경우는 모든 DEBUG 처리를 사용할 수 있다.      커서 캐쉬 검사(orahchf)

2.1.5 ORACLE에의 접속

EXEC SQL CONNECT :oracleid IDENTIFIED BY :oraclepassword;

 CONNECT 문은 Pro*C 프로그램 내에서 실행된 최초의 SQL 실행문이어야 한다. 선언문과 C 코드만을 논리적으로 CONNECT 문 앞에 놓을 수 있다. 패스워드를 따로 지정하는 경우는 ORACLE의 유정명과 ORACLE의 패스워드의 양쪽에 대해 호스트 변수를 사용해야 한다.

 양쪽의 호스트 변수를 고정길이 문자열 또는 가변길이 문자열 중의 하나로 선언해야 한다.

 CONNECT 문을 수행하기 전에 양쪽의 호스트 변수를 초기화켜 놓아야 한다.

 CONNECT는 프로그램의 최초의 실행문이지만 논리적인 작업단위의 시작은 아니다.

2.2 어플리케이션 본체

어플리케이션 본체에는 ORACLE 데이터베이스 내에 보존된 데이터를 쿼리하고 조작하기 위한 SQL 문이 들어 있다. 이러한 문은 데이터 조작문이라고 한다. 또한 어플리케이션 본체에는 데이터 정의문이 포함되며 이것은 테이블, 뷰, 인덱스등의 데이터 구조를 작성하거나 정의하기 위해 사용한다.

 ~DECLARE STATEMENT 문

 ~DECLARE DATABASE 문

 ~EXEC ORACLE 옵션

2.3 Pro*C 예제 프로그램

/* 예제 #1 */ORACLE의 로그온과 로그오프#include /***************************************************************This is sample Pro*C program which will log onto a database asscott/tiger.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    EXEC SQL COMMIT WORK RELEASE;            /* log off database */    exit(0);}/* 예제 #2 */    테이블의 작성#include /***************************************************************This is is a sample Pro*C program which will create a table.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    EXEC SQL CREATE TABLE Emp_TEST        (empno     number        ,ename     char(15)        ,job        char(10)        ,mgr       number        ,hiredate    date        ,sal         number        ,deptno     number);    printf("Table emp_test created. \n");    EXEC SQL COMMIT WORK RELEASE;    exit(0);}/* 예제 #3 */      행을 삽입하기 위한 값의 입력지시#include /***************************************************************This is is a sample Pro*C program which will insert recordsinto the EMP table by prompting the user for values to be entered.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    int empno;    VARCHAR ename[15];    VARCHAR job[10];    float sal;    int deptno;EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    int sret;                                   /* return code from scanf */    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n\n\n", uid.arr);    while(1)    {        printf("Enter employee number(or 0 to end) : ");        sret = scanf("%d", &empno);        if( sret == EOF !! sret == 0 !! empno == 0 )            break;                                      /* terminate loop */        printf("Enter employee name : ");        scanf("%s", ename.arr);        ename.len = strlen(ename.arr);                /* set the name size */        printf("Enter employee's job : ");        scanf("%s", job.arr);        job.len = strlen(job.arr);                     /* set the job size */        printf("Enter employee salary : ");        scanf("%f", &sal);        printf("Enter employee deptno : ");        scanf("%d", &deptno);        EXEC SQL INSERT INTO EMP                  (empno                  ,ename                  ,job                  ,sal                  ,deptno)        VALUES (:empno                  ,:ename                  ,:job                  ,:sal                  ,:deptno);        EXEC SQl COMMIT WORK;        printf("Employee %s added. \n\n", ename.arr);    }    EXEC SQL COMMIT WORK RELEASE;            /* log off database */    exit(0);}/* 예제 #4 */    배열을 이용한 삽입#include /***************************************************************This is is a sample Pro*C program which uses the FOR optionby inserting records into the EMP table.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    int empno[100];    VARCHAR ename[100][15];    VARCHAR job[100][10];    VARCHAR hiredate[100][9];    float sal[100];    int deptno[100];    int loop;EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;FILE *fp;void main(void){    int i;    int fsret;    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                        /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                        /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR GOTO errrpt;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n\n\n", uid.arr);    if((fp = fopen("test.dat", "r")) == NULL)    {        printf("Error opening file test.dat \n");        exit(1);    }    while(1)    {        for(i = 0; i < 100 ; i++)        {            fsret = fscanf(fp, "%d %s %s %s %f %d",                        &empno[i ], ename[i ].arr, job[i ].arr, hiredate[i ].arr,                        &sal[i ], &deptno[i ]);            if(fsret == EOF)                break;            if(fsret == 0)            {                printf("Incompatible field on the line. \n");                exit(1);            }            ename[i ].len = strlen(ename[i ].arr);            job[i ].len = strlen(job[i ].arr);            hiredate[i ].len = strlen(hiredate[i ].arr);        }        loop = i;        EXEC SQL FOR :loop            INSERT INTO EMP(empno, ename, job, hiredate, sal, deptno)            VALUES(:empno, :ename, :job, :hiredate, :sal, :deptno);        EXEC SQL COMMIT WORK;        printf("%d rows inserted. \n", sqlca.sqlerrd[2]);        if(loop < 100)            break;    }    printf("File test.dat loaded. \n");    EXEC SQL WHENEVER SQLERROR CONTINUE;    EXEC SQL COMMIT work RELEASE;    exit(0);    errrpt:        printf("\n %70s \n", sqlca.sqlerrm.sqlerrmc);        EXEC SQL ROLLBACK WORK RELEASE;        exit(1);}   /* 예제 #5 */         갱신에 사용하기 위한 값의 입력지시#include /***************************************************************This is is a sample Pro*C program which will prompt the userfor an employee name and will display thr current sal and commfields for that employee.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    int empno[10];    float sal, comm;    short sali, commi;EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    int sret;    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR STOP;    EXEC SQL WHENEVER NOT FOUND STOP;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    printf("Enter employee name to update : ");    scanf("%s", ename.arr);    ename.len = strlen(ename.arr);    EXEC SQL SELECT SAL, COMM                  INTO  :sal, :comm                  FROM EMP                 WHERE ENAME = :ename;    printf("Employee : %s   sal : %6.2f   comm : %6.2f \n", ename.arr, sal,            comm);    printf("Enter new salary : ");    sret = scanf("%f", &sal);    sali = 0;    if(sret == EOF !! sret == 0) sali = 0;    printf("Enter new commision : ");    sret = scanf("%f", &comm);    commi = 0;    if(sret == EOF !! sret == 0) commi = -1;    EXEC SQL UPDATE EMP                 SET     SAL = :sal:sali,                         COMM = :comm:commi                WHERE ENAME = :ename;    printf("Employee %s updateed. \n", ename.arr);    EXEC SQL COMMIT WORK RELEASE;    exit(0);}   /* 예제 #6 */       배열을 이용한 갱신#include /***************************************************************This is is a sample Pro*C program which updates using hostvariable arrays. The arrays will be loaded with valuesfrom operator input.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    int empno[100];    float sal[100];    int loop;EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    int i, sret;    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR GOTO errrpt;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    while(1)    {        for(i = 0; i < 100; i++)        {            printf("Enter employee number (or 0 to end loop) : ");            sret = scanf("%d", &empno[i ]);            if(sret == EOF !! sret == 0 !! empno[i ] == 0) break;            printf("Enter updated salary : ");            sret = scanf("%f", &sal[i ]);            if(sret == EOF !! sret == 0)            {                printf("Error in entry; terminating at this empno. \n");                break;            }        }        if(i == 0) break;        loop = i;        EXEC SQL FOR :loop            UPDATE EMP SET SAL = :sal            WHERE EMPNO = :empno;        EXEC SQL COMMIT WORK;        printf("%d rows updated. \n", sqlca.sqlerrd[2]);    }    printf("Update program complete. \n");    EXEC SQL WHENEVER SQLERROR CONTINUE;    EXEC SQL COMMIT WORK RELEASE;    exit(0);errrpt:    printf("\n %70s \n", sqlca.sqlerrm.sqlerrmc);    EXEC SQL ROLLBACK WORK RELEASE;    exit(1);}/* 예제 #7 */    배열을 이용한 선택#include /***************************************************************This is is a sample Pro*C program which selects using hostvariable arrays.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    int empno[100];    VARCHAR ename[100][15];    float sal[100];EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    long num_ret;    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR GOTO errrpt;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);     EXEC SQL DECLARE C1 CURSOR FOR        SELECT  EMPNO, ENAME, SAL          FROM   EMP;    EXEC SQL OPEN C1;    EXEC SQL WHENEVER NOT FOUND GOTO endloop;    num_ret = 0;    while(1)    {        EXEC SQL FETCH C1 INTO : empno, :ename, :sal;        print_rows(sqlca.sqlerrd[2] - num_ret);        num_ret = sqlca.sqlerrd[2];    }endloop:    if(sqlca.sqlerrd[2] - num_ret > 0)        print_rows(sqlca.sqlerrd[2] - num_ret);    printf("\n\nProgram complete. \n");    EXEC SQL WHENEVER SQLERROR CONTINUE;    EXEC SQL COMMIT WORK RELEASE;    exit(0);errrpt:    printf("\n %70s \n", sqlca.sqlerrrm.sqlerrmc);    EXEC SQL ROLLBACK WORK RELEASE;    exit(1);}int print_rows(long n){    long i;    printf("\n\nEmployee number\tEmployee Name\tSalary\n");    printf("\n\n---------------\t-------------\t------\n");    for(i = 1; i < n; i++)        printf("%15d\t%13s\t%6.2f\n", empno[i ], ename[i ].arr, sal - i);    return 0;}    /* 예제 #8 */기존의 테이블로부터 행의 삭제#include /***************************************************************This is is a sample Pro*C program which will delete a rowfrom the emp table by prompting for an employee number.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    int empno;EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR STOP;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    scanf("%d", &empno);    EXEC SQL DELETE FROM EMP WHERE EMPNO = :empno;    EXEC SQL COMMIT WORK RELEASE;    printf("Employee number %d dropped. \n", empno);    exit(0);}

3.1 쿼리의 구성

Pro*C를 사용해서 작성하는 쿼리에는 다음에 나타낸 구를 지정한 어떠한 SELECT 문도 사용할 수 있다.

SELECT

INTO

FROM

WHERE

CONNECT

UNION

INTERSECT

MINUS

GROUP BY

HAVING

ORDER BY

3.1.1 입력 호스트 변수

WHERE 구 내의 호스트 변수는 입력 호스트 변수라고 부른다.

3.1.2 출력 호스트 변수

INTO 구 내의 호스트 변수는 출력 호스트 변수라고 부른다.

3.2 1개의 행 만을 리턴하는 쿼리

쿼리가 오직 1개의 행 만을 리턴하는 것을 알고 있는 경우는, SELECT 리스트 내의 항목과 같은 수의 출력 호스트 변수를 지정한 INTO 구를 사용한다.

3.3 복수의 행을 리턴하는 쿼리(커서의 사용법)

쿼리가 복수의 행을 리턴하는 경우, 혹은 몇 개의 행이 리턴됐는지 모를 경우는, SELECT 문과 함께 커서를 사용해야 한다.

커서는 ORACLE 및 Pro*C가 사용하는 작업 영역이고, 이 속에 쿼리의 결과가 포함된다. 커서는 1개의 SELECT문에 대응하고, 쿼리가 변화할 때마다 반복해서 실행된다. 커서는 선언되어야 하며, 데이터의 처리를 위해서 커서를 사용할 때는 아래의 4개의 커서 커맨드를 사용한다.

 DECLARE CURSOR

 OPEN CURSOR

 FETCH

 CLOSE CURSOR

커서를 "OPEN" 한 후 그 커서를 사용해서, 그것에 대응한 쿼리의 결과로서 얻어진 여러 행을 검색 할 수 있다. 쿼리 기준을 만족한 행은 모두 집합의 형식을 취하고, 이것을 커서의 실효집합이라고 부른다. 쿼리가 종료하면 커서를 "CLOSE" 한다.

3.3.1 DECLARE CURSOR 문

DECLARE CURSOR 문에서는 커서를 정의한다.

EXEC SQL DECLARE cursor_name CURSOR FOR

SELECT … FROM …

이 구문은 실제로 다음과 같이 사용한다.

EXEC SQL DECLARE C1 CURSOR FOR

SELECT ename, empno, job, sal

FROM emp

WHERE deptno = :deptno;

커서를 참조한는 SQL 문을 사용하기 전에 반드시 이커서에 대응하는 DECLARE CURSOR 문을 실행해야 하며, Pro*C는 선언되지 않은 커서에 대한 참조를 해석할 수 없다.

프로그램 내에는 복수의 커서를 사용할 수 있으며 동일 프로그램 내에서 같은 이름의 커서를 지정해서는 안된다.

3.3.2 OPEN CURSOR

커서를 OPEN 함으로써 쿼리가 판정되고, 행의 실효집합이 식별된다.

EXEC SQL OPEN cursor_name;

커서는 OPEN 상태에서 실효집합 최초의 행 직전에 위치된다. 그러나, 실제로 이 시점에서는 아직 어느 행의 검색도 행하지 않는다.

커서를 일단 OPEN 하면 다시 OPEN 하지 않는한, 이 커서의 입력 호스트 변수는 새로이 체크되지 않는다.

3.3.3 실효집합의 행의 검색(FETCH 문)

FETCH 문에서는 실효집합의 행을 읽어 들이고, 결과를 얻을 출력 호스트 변수의 이름을 지정한다.

EXEC SQL FETCH cursor_name INTO :hostvar1, :hostvat2 …;

이 구문은 실제로 다음과 같이 사용된다.

EXEC SQL FETCH C1 INTO :pename, :pempno, :pjob, :psal;

처음으로 커서를 실행하면, 커서는 실효집합의 이전 위치에서 실효집합의 첫째 행으로 이동하며 이 행이 현재의 행이 된다. 커서는 실효집합의 다음행으로만 진행하며 앞에 검색한 행으로 되돌아 가기 위해서는 커서를 CLOSE 하고 다시 오픈해야 한다.

3.3.4 CLOSE CURSOR 문

실효집합 행의 검색을 종료한 후 커서를 CLOSE 함으로써, 커서의 OPEN 상태를 유지하기 위해서 사용된 자원을 해제한다.

EXEC SQL CLOSE cursor_name;

CLOSE 한 커서에 대해서는 검색을 행할 수 없다.

3.4 예제 프로그램

/* 예제 #9 */   WHERE 구에 의한 쿼리#include /***************************************************************This is is a sample Pro*C program which will display all thesalesman in the employee table.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    float sal, comm;    char ename[11];EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR STOP;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    EXEC SQL DECLARE C1 CURSOR FOR        SELECT ENAME, SAL, COMM          FROM EMP        WHERE JOB = 'SALESMAN';    EXEC OPEN C1;    EXEC SQL WHENEVER NOT FOUND STOP;    printf("SALESMAN NAME\t\tSALARY\t\tCOMMISSION\n\n");    for( ; ; )    {        EXEC SQL FETCH C1 INTO :ename, :sal, :comm;        printf("%-10s\t\t%6.2f\t\t%6.2f \n", ename, sal, comm);    }    EXEC SQL CLOSE C1;    EXEC SQL WHENEVER SQLERROR CONTINUE;   /* don't trap errors */    EXEC SQL COMMIT WORK RELEASE;    exit(0);}

------------------------------------------------------------------------------------

ORACLE의 기능에 의하여, 유저의 작업 모두가 실행하면 바로 COMMIT되는 것이 아니다. 다시말해서 ORACLE에서는 COMMIT 을 행하기 전에 모든 트랜잭션이 완료되어 있는지를 유저 자신이 확인하도록 되어 있다.

ORACLE은 BEFORE 이미지 파일을 사용하여, 데이터베이스의 일관성을 보호하고 있다. BEFORE 이미지 파일에는 트랜잭션이 시작하기 전의 상태로 데이터베이스의 블록이 보존된다. 상황에 따라 이들의 블록이 데이터베이스 파일에 다시 기록되어, 트랜잭션에 의해 변경된 부분이 변경 전의 상태로 되돌려 진다. 이 같은 처리는 다음과 같은 경우 발생한다.

  유저에 의한 ROLLBACK WORK  유저 프로세스 내의 ORACLE로부터의 이상 종료  프로세스간의 데드록  시스템 장애(H/W OR S/W)

4.1 COMMIT WORK

COMMIT WORK 문은 현재 진행 중인 논리적인 작업단위를 종료하게 하고, 이 작업단위 내에서 행해진 변경을 모두 확정한다.

EXEC SQL COMMIT WORK [RELEASE];

RELEASE 옵션 파라미터는 프로그램이 소유하고 있는 자원을 모두 리턴하고, 데이터베이스에서 로그로프한다.

4.2 ROLLBACK WORK

ROLLBACK WORK 문은 현재 진행 중인 논리적인 작업단위를 종료하게 하고, 이 작업단위에서 행한 변경을 취소한다.

EXEC SQL ROLLBACK WORK [RELEASE];

유저는 RELEASE 옵션을 사용해서 최후의 작업단위를 반드시 명시적으로 COMMIT 또는 ROLLBACK해야 한다.

5.1 표지 변수에 리턴된 값의 사용

어느 호스트 변수에도 임의 선택 변수인 표지 변수를 대응시킬 수 있다. 이 표지 변수를 사용하면 각각의 필드 값이 어떤 경우인지 알 수 있다.

값      의미━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0    리턴되는 값은 호스트 변수에 보존된다. 이것은 널도 아니고,      절사도 되지 않는다.-1   리턴된 값은 널이다. 호스트 변수의 값은 정의되지 않는다.20   리턴된 값은 절사되어 있다. 호스트 변수의 폭이 충분하지 않다.      표지 변수에 설정된 값은 절사되기 전의 폭이다

5.2 SQLCA의 구조

SQLCA 는 프로그램의 실행에 관한 정보를 교환하기 위해, 모든 Pro*C 프로그램에서 사용한다. SQLCA는 각각의 트랜잭션에 대해서 널 값의 무시여부, 쿼리를 시작하고 나서 데이터의 변경 여부등을 표시하므로, 프로그래머는 각각의 데이터 상태를 확인할 수 있다.

5.2.1 SQLCA를 참조하는 타이밍

SQLCA는 각 SQL 실행문의 실행 때마다 갱신된다. 따라서 프로그래머는 각 실행문 뒤에서 SQLCA를 검색해야 한다. 특히 각 DML 문 뒤에서는 SQLCA를 검색할 필요가 있다. 이는 테이블 속의 일부의 행만을 처리한 뒤에 INSERT 및 UPDATE 문이 실패한다면, 유저는 데이터베이스을 어떤 일관성 있는 상태로 회복하기 위해 ROLLBACK WORK 커맨드를 실행해야 하기 때문이다.

WHENEVER 문을 사용하면 이상 상태를 검출하고 그 상태에 따라 적절히 동작을 지정할 수 있다. 다음은 WHENEVER 문의 디폴트 값이다.

EXEC SQL WHENEVER anyerror CONTINUE;

5.2.2 SQLCA의 각 요소의 의미

struct sqlca {      char sqlcaid[8];      long sqlcabc;      long sqlcode;      struct {             unsigned short sqlerrml;             char   sqlerrmc[70];       } sqlerrm;      char sqlerrp[8];      long sqlerrd[6];      char sqlwarn[8];      char sqlext[8];};struct sqlca sqlca;  *sqlca.sqlcode   4바이트 2진 정수이고, SQL 문의 실행결과를 나타낸다.                        0  실행이 정상 종료                        1403  NOT FOUND                         음수  프로그램 또는 시스템 장애     *sqlca.sqlerrm.sqlerrml sqlca.sqlerrm.sqlerrmc의 텍스트의 길이  *sqlca.sqlerrm.sqlerrmc 가변 길이의 문자열이고, sqlca.sqlcode 내에                                              표시된 에러번호에 대응하는 에러메세지  *sqlca.sqlerrd   4바이트 2진 정수 배열 ORACLE RDBMS의 내부상황을                                파악하기 위해 사용. sqlca.sqlerrd[2]는 INSERT나 UPDATE                               처럼 DML 처리에 대해서 몇 개의 행이 처리 됐는지를 나타냄  *sqlca.sqlwarn   프리컴파일 중에 발생한 여러가지 상황에 대한 경고        sqlca.sqlwarn[0]  "W"가 설정된 경우 1개 이상의 경고가 설정되어 있음

5.3 WHENEVER 문

  에러를 검출할 때 어떤 처리를 취해야 할 지를 결정한다.   EXEC SQL WHENEVER [SQLERROR | SQLWARNING | NOT FOUND]                         [STOP | CONTINUE | GOTO stmt-label];  SQLERROR      sqlca.sqlcode가 -1인 경우에 설정  SQLWARNING  sqlca.sqlwarn[0]에 "W"가 설정되어 있는 경우에 설정  NOT FOUND    sqlca.sqlcode가 1403인 경우에 설정  STOP             프로그램을 종료시키고 논리적인 작업단위는 ROLLBACK 된다.  CONTINUE      sqlca의 상태를 무시하고 프로그램을 계속 진행한다.  GOTO             지정한 라벨이 붙은 문으로 제어를 옮긴다.

6. 동적 정의문

6.1 동적 정의문의 정의

동적 정의문이란, 컴파일시에 정의되지 않는 SQL 문이다. 다시 말해서, 동적 정의문은 각각의 실행 때마다 변경이 가능하고, 실제로 많은 경우에 변경된다.

6.2 동적 정의문의 종류

방법 1 : EXECUTE IMMEDIATE 문의 사용  모든 SQL 문(SELECT 문 제외)을 프리컴파일해서 이것을 실행한다.  SQL 문은 상수 또는 호스트 변수 중 어느 것이라도 상관없다.  SQL 문에는 호스트 변수를 포함하지 않는다.방법 2 : PREPARE 문 및 EXECUTE 문의 사용  모든 SQL 문(SELECT 문 제외)을 받아들여 이것을 수행한다.  SQL 문 중에 호스트 변수를 포함할 수 있다.  방법 3 : PREPARE 문 및 FETCH 문의 사용  선택을 행할 수 있다. SQL 문 중에 호스트 변수를 포함할 수 있다.  이 방법은 PREPARE, DECLARE, OPEN, FETCH의 순서로 행한다.방법 4 : 바인드 및 정의 기술자의 사용  1행의 SELECT 및 여러 행의 SELECT를 포함하는 모든 SQL 문을  사용할 수 있다.

6.3 EXECUTE IMMEDIATE 의 사용

/* 예제 #10 */#include /***************************************************************This is is a sample Pro*C program which will prompt for aWHERE clause to be used in an update statement. This is to beused with EXECUTE IMMEDIATE.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    char select[132];EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    char where[80];    int scode;    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR STOP;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    strcpy(select, "UPDATE EMP SET COMM = 100 WHERE");    printf("Please enter where clause for the following : \n");    printf("%s", select);    scode = scanf("%s", where);    if(scode == EOF !! scode == 0)    {        printf("Invalid entry. \n");        exit(1);    }    strcat(select, where);    EXEC SQL EXECUTE IMMEDIATE :select;    printf("%d records updated. \n", sqlca.sqlerrd[2]);    EXEC SQL WHENEVER SQLERROR CONTINUE;    EXEC SQL COMMIT WORK RELEASE;    exit(0);}

6.4 PREPARE 및 EXECUTE 의 사용

/* 예제 #11 */#include /***************************************************************This is is a sample Pro*C program which will prompt for aWHERE clause to be used in an update statement. This is to beused with PREPARE and EXECUTE.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    float comm;    char select[132];EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    char where[80];    int scode;    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR GOTO errrpt;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    strcpy(select, "UPDATE EMP SET COMM = :comm WHERE");    printf("Please enter where clause for the follwing : \n");    printf("%s", select);    scode = scanf("%s", where);    if(scode == EOF !! scode == 0)    {        printf("Invalid entry. \n");        exit(1);    }    strcat(select, where);    EXEC SQL PREPARE S1 FROM :select;    printf("Please enter commission : ");    scanf("%f", &comm);    EXEC SQL EXECUTE S1 USING :comm;    printf("%d records updated. \n", sqlca.sqlerrd[2]);    EXEC SQL WHENEVER SQLERROR CONTINUE;    EXEC SQL COMMIT WORK RELEASE;    exit(0);errrpt:    printf("\n %70s \n", sqlca.sqlerrm.sqlerrmc);    EXEC SQL ROLLBACK WORK RELEASE;    exit(1);}        

6.5 PREPARE, DECLARE, OPEN, FETCH, CLOSE 의 사용

/* 예제 #12 */#include /***************************************************************This is is a sample Pro*C program which will prompt for aWHERE clause to be used in a select statement.This sample uses PREPARE, DECLARE, OPEN, FETCH, CLOSEsince this may be a multi-row select and world require a cursor.***************************************************************/EXEC SQL BEGIN DECLARE SECTION;    VARCHAR uid[20];    VARCHAR pwd[20];    int deptno;    float sal;    char select[132];EXEC SQL END DECLARE SECTION;EXEC SQL INCLUDE SQLCA;void main(void){    char where[80];    int i, scode;    /* log into ORACLE */    strcpy(uid.arr, "SCOTT");                       /* copy the user name */    uid.len = strlen(uid.arr);    strcpy(pwd.arr, "TIGER");                       /* copy the password */    pwd.len = strlen(pwd.arr);    EXEC SQL WHENEVER SQLERROR GOTO errrpt;    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;    printf("Connected to ORACLE user : %s\n", uid.arr);    strcpy(select, "SELECT ENAME, SAL FROM EMP ");    printf("Please enter where clause for the following : \n");    printf("%s", select);    scode = scanf("%s", where);    if(scode == EOF !! scode == 0)    {        printf("Invalid entry. \n");        exit(1);    }    strcat(select, where);    EXEC SQL PREPARE S1 FROM :select;    EXEC SQL DECLARE C1 CURSOR FOR S1;    EXEC SQL OPEN C1;    printf("Employee       \tSalary       \n");    printf("--------------\t-----------\n");    EXEC SQL WHENEVER NOT FOUND GOTO endloop;    for(i = 0; ; i++)    {        EXEC SQL FETCH C1 INTO :ename, :sal;      printf("%10s\t%6.2f\n", ename, sal);    }endloop:    printf("\n\n%d records selected.\n", i);    EXEC SQL WHENEVER SQLERROR CONTINUE;    EXEC SQL COMMIT WORK RELEASE;    exit(0);errrpt:    printf("\n %70s \n", sqlca.sqlerrm.sqlerrmc);    EXEC SQL ROLLBACK WORK RELEASE;    exit(1);}

7.1 STORED PROCEDURE 의 사용

/* calldemo.sql */rem Rem    calldemo.sql - Rem    DESCRIPTIONRem    Rem    RETURNSRem CREATE OR REPLACE PACKAGE calldemo AS   TYPE name_array IS TABLE OF emp.ename%type       INDEX BY BINARY_INTEGER;   TYPE job_array IS TABLE OF emp.job%type       INDEX BY BINARY_INTEGER;   TYPE sal_array IS TABLE OF emp.sal%type       INDEX BY BINARY_INTEGER;   PROCEDURE get_employees(     dept_number IN     number,    -- department to query     batch_size  IN     INTEGER,   -- rows at a time     found       IN OUT INTEGER,   -- rows actually returned     done_fetch  OUT    INTEGER,   -- all done flag     emp_name    OUT    name_array,     job         OUT    job_array,     sal         OUT    sal_array);END calldemo;/CREATE OR REPLACE PACKAGE BODY calldemo AS   CURSOR get_emp (dept_number IN number) IS       SELECT ename, job, sal FROM emp           WHERE deptno = dept_number;   PROCEDURE get_employees(     dept_number IN     number,     batch_size  IN     INTEGER,     found       IN OUT INTEGER,     done_fetch  OUT    INTEGER,     emp_name    OUT    name_array,     job         OUT    job_array,     sal         OUT    sal_array) IS   BEGIN       IF NOT get_emp%ISOPEN THEN      -- open the cursor if           OPEN get_emp(dept_number);  -- not already open       END IF;       done_fetch := 0;  -- set the done flag FALSE       found := 0;       FOR i IN 1..batch_size LOOP           FETCH get_emp INTO emp_name(i), job(i), sal(i);           IF get_emp%NOTFOUND THEN    -- if no row was found               CLOSE get_emp;               done_fetch := 1;   -- indicate all done               EXIT;           ELSE               found := found + 1;  -- count row           END IF;       END LOOP;   END;END;/ /* 예제 #13 */#include #include /***************************************************************This program connects to ORACLE using the SCOTT/TIGER account.  The program declares several host arrays, then calls a PL/SQL stored procedure (GET_EMPLOYEES in the CALLDEMO package) that fills the table OUT parameters. The PL/SQL procedure returns up to ASIZE values.****************************************************************/EXEC SQL INCLUDE sqlca.h;typedef char asciz[20];typedef char vc2_arr[11];EXEC SQL BEGIN DECLARE SECTION;    /* User-defined type for null-terminated strings */    EXEC SQL TYPE asciz IS STRING(20) REFERENCE;    /* User-defined type for a VARCHAR array element. */    EXEC SQL TYPE vc2_arr IS VARCHAR2(11) REFERENCE;    asciz     username;    asciz     password;    int       dept_no;                  /* which department to query? */    vc2_arr   emp_name[10];            /* array of returned names */    vc2_arr   job[10];    float     salary[10];    int       done_flag;    int       array_size;    int       num_ret;                 /* number of rows returned */EXEC SQL END DECLARE SECTION;long      SQLCODE;void print_rows();            /* produces program output      */void sql_error();             /* handles unrecoverable errors */main(){    int   i;    char  temp_buf[32];    /* Connect to ORACLE. */    EXEC SQL WHENEVER SQLERROR DO sql_error();    strcpy(username, "scott");    strcpy(password, "tiger");    EXEC SQL CONNECT :username IDENTIFIED BY :password;    printf("\nConnected to ORACLE as user: %s\n\n", username);    printf("Enter department number: ");    gets(temp_buf);    dept_no = atoi(temp_buf);    /* Print column headers. */    printf("\n\n");    printf("%-10.10s%-10.10s%s\n", "Employee", "Job", "Salary");    printf("%-10.10s%-10.10s%s\n", "--------", "---", "------");    /* Set the array size. */    array_size = 10;    done_flag = 0;    num_ret = 0;    /*  Array fetch loop.     *  The loop continues until the OUT parameter done_flag is set.     *  Pass in the department number, and the array size--     *  get names, jobs, and salaries back. */     for ( ; ; )    {        EXEC SQL EXECUTE             BEGIN calldemo.get_employees                (:dept_no, :array_size, :num_ret, :done_flag,                 :emp_name, :job, :salary);            END;        END-EXEC;        print_rows(num_ret);        if(done_flag) break;    }    /* Disconnect from the database. */    EXEC SQL COMMIT WORK RELEASE;    exit(0);}void print_rows(int n){    int i;    if (n == 0)    {        printf("No rows retrieved.\n");        return;    }    for (i = 0; i < n; i++)        printf("%10.10s%10.10s%6.2f\n", emp_name[i ], job[i ], salary[i ]);} /* Handle errors. Exit on any error. */void sql_error(){    char msg[512];    int buf_len, msg_len;    EXEC SQL WHENEVER SQLERROR CONTINUE;    buf_len = sizeof(msg);    sqlglm(msg, &buf_len, &msg_len);    printf("\nORACLE error detected:");    printf("\n%.*s \n", msg_len, msg);    EXEC SQL ROLLBACK WORK RELEASE;    exit(1);}

8. Pro*C/C++의 사용

8.1 디렉토리 또는 경로의 설정

OS에서 디렉토리 또는 경로를 사용하고 있는 경우 경로 또는 디렉토리의 이름이 장치의 지정과 함께 올바른 지를 확인해야 한다.

8.2 커맨드 구문

일반적인 커맨드 구문은 다음과 같다.

PCC INAME = filename {option=value (option=value …)}

8.2.1 필수 인수

필수 인수는 "INAME = 파일명" 하나뿐이다. 파일의 확장자나 파일타입을 지정하지 않는 경우 "HOST = 언어"를 인수로서 지정해야 한다.

Pro*C에서 입력파일 및 출력파일에 대한 파일타입 및 확장자의 디폴트값은 다음과 같다.

OS          입력파일타입 또는 확장자      출력파일타입 또는 확장자━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━VAX/VMS               .pc                                  .cIBM/VM/CMS         csql                                cUNIX                      .pc                                  .c

8.2.2. Pro*C의 실행시 옵션

  실행시에는 복수의 옵션을 사용할 수 있다.OPTION = value · AREASIZE · ASACC · ERRORS · HOLD_CURSOR/RELEASE_CURSOR · HOST · INCLUDE · IRECLEN · LNAME · LRECLEN · LTYPE · MAXLITERAL · MAXOPENCURSOR · ONAME · ORACA · ORECLEN · PAGELEN · REBIND · SELECT_ERROR · USERID · XREF

[출처] [펌] Pro*c참고|작성자 황제펭귄

, .