41.6. 제어 구조#

제어 구조는 아마도 가장 유용하고 중요한 부분 일 것입니다pl/pgsql. 와 함께pl/pgsql의 제어 구조, 조작 할 수 있습니다postgresql매우 유연하고 강력한 방식으로 데이터.

41.6.1. 함수에서 반환#

함수에서 데이터를 반환 할 수있는 두 가지 명령이 있습니다.returnand다음 반환.

41.6.1.1. return #

return표현;

return표현식과 함께 함수를 종료하고 값을 반환합니다표현발신자에게. 이 양식은에 사용됩니다.pl/pgsql세트를 반환하지 않는 기능.

스칼라 유형을 반환하는 함수에서 표현식 결과는 할당에 대해 설명 된대로 기능의 반환 유형으로 자동으로 표시됩니다. 그러나 합성 (행) 값을 반환하려면 요청 된 열 세트를 정확히 전달하는 표현식을 작성해야합니다.

출력 매개 변수로 함수를 선언 한 경우 JUTEreturn표현이없는. 출력 매개 변수의 현재 값이 반환됩니다.

반환으로 함수를 선언 한 경우void, areturn명령문은 기능을 조기에 종료하는 데 사용될 수 있습니다. 그러나 다음 표현을 작성하지 마십시오return.

함수의 반환 값은 정의되지 않은 상태로 남을 수 없습니다. 제어가 A를 치지 않고 함수의 최상위 블록의 끝에 도달하는 경우return문, 런타임 오류가 발생합니다. 이 제한은 출력 매개 변수 및 기능이 반환되는 함수에 적용되지 않습니다void. 그러한 경우 areturn최상위 블록이 완료되면 명령문이 자동으로 실행됩니다.

일부 예 :

- 스칼라 유형을 반환하는 기능

41.6.1.2. 다음 반환andreturn query #

다음 반환표현;쿼리;Command-string [사용표현 [, ... ] ];

언제pl/pgsql함수는 반환으로 선언 됨setofSomegeype, 따라야 할 절차는 약간 다릅니다. 이 경우 반환 할 개별 항목은 시퀀스로 지정됩니다.다음 반환또는return query명령, 그리고 최종return인수가없는 명령은 함수가 실행 된 것을 나타내는 데 사용됩니다.다음 반환스칼라 및 복합 데이터 유형 모두에서 사용할 수 있습니다. 복합 결과 유형을 사용하여 전체테이블결과가 반환됩니다.return query함수의 결과 세트에 쿼리를 실행 한 결과를 추가합니다.다음 반환andreturn query단일 설정 함수로 자유롭게 혼합 될 수 있으며,이 경우 결과가 연결됩니다.

다음 반환andreturn query실제로 함수에서 돌아 오지 마십시오. 단순히 함수의 결과 세트에 0 이상을 추가합니다. 그런 다음 실행은의 다음 진술로 계속됩니다.pl/pgsql함수. 연속다음 반환또는return query명령이 실행되고 결과 세트가 쌓입니다. 최종return, 인수가 없어야하는 것은 제어가 함수를 종료하도록 유발합니다 (또는 제어가 함수의 끝에 도달하게 할 수 있습니다)..

return query변형이 있습니다return query execute는 동적으로 실행될 쿼리를 지정합니다. 매개 변수 표현식은를 통해 계산 된 쿼리 문자열에 삽입 할 수 있습니다.사용,와 같은 방식으로execute명령.

출력 매개 변수로 함수를 선언 한 경우 JUTE다음 반환표현이없는. 각 실행에서 출력 매개 변수 변수의 현재 값은 결과 행으로 최종 반환을 위해 저장됩니다.Setof Record여러 출력 매개 변수가있을 때 또는setofSomegeype유형의 출력 매개 변수가 하나만있을 때Somegeype, 출력 매개 변수로 설정된 설정 함수를 만들려면

다음을 사용하는 함수의 예는입니다.다음 반환:

테이블 foo (FOOD INT, FOOSUBID int, fooname 텍스트) 만들기;

다음을 사용하는 함수의 예는입니다.return query:

함수 생성 get_available_flightid (날짜) 정수를 설정합니다

Note

현재의 구현다음 반환andreturn query위에서 설명한대로 함수에서 돌아 오기 전에 전체 결과 세트를 저장합니다. 그것은 a이라는 것을 의미합니다.pl/pgsql함수는 매우 큰 결과 세트를 생성하고, 성능이 좋지 않을 수 있습니다 : 데이터는 메모리 소진을 피하기 위해 디스크에 기록되지만 전체 결과 세트가 생성 될 때까지 기능 자체가 반환되지 않습니다. 미래 버전의pl/pgsql사용자는이 제한이없는 설정 회복 함수를 정의 할 수 있습니다. 현재 데이터가 디스크에 기록되기 시작하는 지점은에 의해 제어됩니다.work_mem구성 변수. 메모리에 더 큰 결과 세트를 저장하기에 충분한 메모리를 가진 관리자는이 매개 변수를 증가시키는 것을 고려해야합니다.

41.6.2. 절차에서 돌아온#

프로 시저에는 리턴 값이 없습니다. 그러므로 절차는 a없이 끝날 수 있습니다.return진술. a를 사용하려면return코드를 일찍 종료하려는 명령문, JUTEreturn표현이없는.

프로 시저에 출력 매개 변수가 있으면 출력 매개 변수의 최종 값이 발신자에게 반환됩니다.

41.6.3. 절차 호출#

A pl/pgsql함수, 절차 또는do블록을 사용하여 절차를 호출 할 수 있습니다Call. 출력 매개 변수는 방식과 다르게 처리됩니다.Call일반 SQL에서 작동합니다. 각Out또는inout절차의 매개 변수는의 변수에 해당해야합니다.Call명령문, 그리고 절차가 반환 된 후 해당 변수에 다시 할당됩니다. 예를 들어:

절차 생성 트리플 (inout x int)

출력 매개 변수에 해당하는 변수는 간단한 변수 또는 복합 유형 변수의 필드 일 수 있습니다. 현재 배열의 요소가 될 수 없습니다.

41.6.4. 조건부#

ifandCASE진술은 특정 조건에 따라 대체 명령을 실행할 수 있습니다.pl/pgsql세 가지 형태의if:

  • IF ... 그런 다음 ... IF

  • if ... then ... else ... end if

  • if ... 그런 다음 ... Elsif ... 그런 다음 ... End If

그리고 두 가지 형태의CASE:

  • CASE ... 언제 ... 그런데 ... ELSE ... END CASE

  • 케이스가 ... 그런 다음 ... else ... end case

41.6.4.1. if-then #

if부울-표현the진술END IF;

if-then진술은 가장 간단한 형태의입니다.if. 사이의 진술theand종료 if조건이 참이면 실행됩니다. 그렇지 않으면 건너 뜁니다.

예 :

if v_user_id <> 0입니다

41.6.4.2. if-then-else #

if부울-표현the진술else진술END IF;

if-then-else진술 추가if-then조건이 사실이 아닌 경우 실행 해야하는 대체 명령문 세트를 지정할 수 있습니다. (여기에는 상태가 널로 평가되는 경우가 포함됩니다.)

예 :

Parentid가 null 또는 parentid = ''인 경우 = ''
V_count> 0이면

41.6.4.3. if-then-elsif #

if부울-표현the진술
[elsif부울-표현the진술
[elsif부울-표현the진술
    ...
]
]else진술 ]

때로는 두 가지 이상의 대안이 있습니다.if-then-elsif차례로 몇 가지 대안을 확인하는 편리한 방법을 제공합니다. 그만큼if조건은 참된 첫 번째 조건이 발견 될 때까지 연속적으로 테스트됩니다. 그런 다음 관련 진술이 실행되고 그 후 컨트롤이 다음 문으로 전달됩니다.종료 if. (모든 후속if조건은not테스트.)if조건은 사실입니다.else블록 (있는 경우)이 실행됩니다.

예는 다음과 같습니다.

if number = 0이면

키워드elsif철자도elseif.

동일한 작업을 수행하는 대안적인 방법은 둥지입니다if-then-else다음 예와 같이 진술 :

demo_row.sex = 'm'이면

그러나이 방법은 일치하는 것을 작성해야합니다종료 if각각if, 사용하는 것보다 훨씬 번거 롭습니다elsif많은 대안이있을 때.

41.6.4.4. 단순한CASE #

CASESearch-Expression언제표현 [, 표현 [ ... ]] 그런 다음진술
  [언제표현 [, 표현 [ ... ]] 그런 다음진술
    ... ]else진술 ]

간단한 형태의CASE피연산자의 평등에 따라 조건부 실행을 제공합니다. 그만큼Search-Expression평가 (한 번)가 평가되고 각에 비해 연속적으로표현in언제조항. 일치가 발견되면 해당진술실행 된 다음 컨트롤이 다음 명령문으로 전달됩니다최종 사례. (후속언제표현이 평가되지 않습니다.) 일치가 없으면else 진술실행됩니다. 하지만 ifelse존재하지 않으면 Acase_not_found예외가 제기됩니다.

여기 간단한 예가 있습니다 :

케이스 x

41.6.4.5. 검색CASE #

케이스부울-표현the진술
  [언제부울-표현the진술
    ... ]else진술 ]

검색 된 형태의CASE부울 표현의 진실을 기반으로 조건부 실행을 제공합니다. 각언제부울-표현수율이 발견 될 때까지 차례로 평가됩니다true. 그러면 해당진술실행 된 다음 컨트롤이 다음 명령문으로 전달됩니다최종 사례. (후속언제표현이 평가되지 않습니다.) 진정한 결과가 없으면else 진술실행됩니다. 하지만 ifelse존재하지 않으면 Acase_not_found예외가 제기됩니다.

예는 다음과 같습니다.

케이스

이 형태의CASE전적으로 동일합니다if-then-elsif26479_26525else조항은 아무것도하지 않고 오류가 발생합니다.

41.6.5. 간단한 토토 사이트#

withloop, 출구, 계속, while, forforeach진술, 당신은 당신의 것을 준비 할 수 있습니다pl/pgsql일련의 명령을 반복하는 기능.

41.6.5.1. loop #

[ <<레이블 ]진술엔드 토토 사이트 [ 레이블 ];

loopAN에 의해 ​​종료 될 때까지 무기한 반복되는 무조건 토토 사이트를 정의합니다.출구또는return진술. 선택 사항레이블출구and계속중첩 토토 사이트 내의 진술은 어떤 토토 사이트를 언급하는지.

41.6.5.2. 출구 #

출구 [ 레이블 ] [언제부울-표현 ];

그렇지 않은 경우레이블주어지면, 가장 안쪽 토토 사이트가 종료되고 다음은 다음에엔드 토토 사이트다음에 실행됩니다. 만약에레이블주어지면 전류 또는 일부 외부 레벨의 중첩 토토 사이트 또는 블록의 레이블이어야합니다. 그런 다음 토토 사이트/블록의 해당 후 명명 된 토토 사이트 또는 블록이 종료되고 제어가 계속됩니다END.

if언제지정되어 있습니다. 토토 사이트 출구는 만 발생합니다.부울-표현사실입니다. 그렇지 않으면 제어는 이후에 진술로 전달됩니다.출구.

출구모든 유형의 토토 사이트와 함께 사용할 수 있습니다. 무조건 토토 사이트와 함께 사용하는 것은 국한되지 않습니다.

a시작블록,출구블록 끝 이후 다음 명령문에 제어를 전달합니다. 이 목적으로 라벨을 사용해야합니다.출구A와 일치하는 것으로 간주되지 않습니다시작블록. (이것은 8.4 이전 릴리스에서의 변화입니다postgresql.출구A와 일치하려면시작블록.)

예 :

토토 사이트

41.6.5.3. 계속 #

계속 [ 레이블 ] [언제부울-표현 ];

그렇지 않은 경우레이블가장 안쪽 토토 사이트의 다음 반복이 시작됩니다. 즉, 토토 사이트 본체에 남아있는 모든 진술은 건너 뜁니다. 다른 토토 사이트 반복이 필요한지 여부를 결정하기 위해 토토 사이트 제어 표현식 (있는 경우)으로의 제어 리턴이 반환됩니다.레이블존재하고, 실행이 계속 될 토토 사이트의 레이블을 지정합니다.

if언제지정되어 있습니다. 토토 사이트의 다음 반복은 만 시작됩니다.부울-표현사실입니다. 그렇지 않으면 제어는 이후에 진술로 전달됩니다.계속.

계속모든 유형의 토토 사이트와 함께 사용할 수 있습니다. 무조건 토토 사이트와 함께 사용하는 것은 국한되지 않습니다.

예 :

토토 사이트

41.6.5.4. while #

[ <<레이블 ]부울-표현loop진술엔드 토토 사이트 [ 레이블 ];

thewhile진술은만큼 일련의 진술을 반복합니다.부울-표현true로 평가합니다. 표현식은 토토 사이트 본체에 각 입력 직전에 확인됩니다.

예 :


    

41.6.5.5. for(정수 변형)#

[ <<레이블 ]이름in [Reverse] 표현 .. 표현 [by표현 ] loop진술엔드 토토 사이트 [ 레이블 ];

이 형태의for다양한 정수 값을 반복하는 토토 사이트를 만듭니다. 변수이름자동으로 유형으로 정의정수토토 사이트 내부에만 존재합니다 (변수 이름의 기존 정의는 토토 사이트 내에서 무시됩니다). 범위의 하부 및 상한을 제공하는 두 표현은 토토 사이트에 들어갈 때 한 번 평가됩니다.by조항은 지정되지 않았습니다. 반복 단계는 1입니다. 그렇지 않으면에 지정된 값입니다.by조항. 토토 사이트 항목에서 다시 한 번 평가됩니다. 만약에Reverse지정된 다음 각 반복 후에 추가하지 않고 단계 값을 빼냅니다.

정수의 일부 예for토토 사이트 :

1..10 토토 사이트의 i

하한이 상한보다 큰 경우 (또는ReverseCASE), 토토 사이트 본체는 전혀 실행되지 않습니다. 오류가 발생하지 않습니다.

if a레이블|for토토 사이트 그러면 정수 토토 사이트 변수는 자격있는 이름으로 참조 할 수 있습니다.레이블.

41.6.6. 쿼리 결과를 통한 루핑#

다른 유형의 사용for토토 사이트, 쿼리 결과를 반복하고 그 데이터를 조작 할 수 있습니다. 구문은 다음과 같습니다.

[ <<레이블 ]대상in쿼리loop진술엔드 토토 사이트 [ 레이블 ];

the대상는 레코드 변수, 행 변수 또는 쉼표로 분리 된 스칼라 변수 목록입니다. 그만큼대상|쿼리각 행에 대해 토토 사이트 본체가 실행됩니다. 예는 다음과 같습니다.

함수 생성 refresh_mviews ()는 정수를 $$로 반환합니다

토토 사이트가 an에 의해 종료 된 경우출구문서, 마지막으로 할당 된 행 값은 토토 사이트 후에도 여전히 액세스 할 수 있습니다.

the쿼리이 유형에서 사용for명령문은 발신자에게 행을 반환하는 SQL 명령이 될 수 있습니다 :select가장 일반적인 경우이지만 사용할 수도 있습니다삽입, 업데이트, 삭제또는병합with반환절. 와 같은 일부 유틸리티 명령설명도 작동합니다.

pl/pgsql변수는 쿼리 매개 변수로 대체되며 쿼리 계획은 가능한 재사용을 위해 캐시됩니다.섹션 41.11.1and섹션 41.11.2.

thefor-in-execute진술은 행을 반복하는 또 다른 방법입니다 :

[ <<레이블 ]대상executetext_expression [사용표현 [, ... ] ] loop진술엔드 토토 사이트 [ 레이블 ];

이것은 소스 쿼리가 문자열 표현식으로 지정되며, 이는 각 항목에 대해 평가 및 대체 된 것을 제외하고는 이전 형식과 같습니다.for토토 사이트. 이를 통해 프로그래머는 사전 계획된 쿼리의 속도 또는 일반 쿼리의 유연성을 선택할 수 있습니다.execute진술. 와 마찬가지로execute, 매개 변수 값을 동적 명령에 삽입 할 수 있습니다사용.

결과를 반복 해야하는 쿼리를 지정하는 또 다른 방법은 커서로 선언하는 것입니다. 이것은에 설명되어 있습니다.섹션 41.7.4.

41.6.7. 배열을 통한 루핑#

theforeach토토 사이트는 A와 매우 흡사합니다for토토 사이트이지만 SQL 쿼리로 반환 된 행을 반복하는 대신 배열 값의 요소를 통해 반복됩니다. (일반적으로foreach| 복합 값 표현의 구성 요소를 통한 루핑을위한 것입니다. 배열 이외의 복합재를 통한 루핑의 변형은 향후에 추가 될 수 있습니다.)foreach배열을 통해 토토 사이트를위한 명령문은 다음과 같습니다.

[ <<레이블 ]대상 [슬라이스번호 ] 배열표현loop진술엔드 토토 사이트 [ 레이블 ];

없음슬라이스또는 if슬라이스 0지정되어 있으며, 토토 사이트는 평가하여 생성 된 배열의 개별 요소를 통해 반복됩니다표현. 그만큼대상변수는 각 요소 값이 순서대로 할당되며 각 요소에 대해 토토 사이트 본체가 실행됩니다. 다음은 정수 배열의 요소를 통한 루핑의 예입니다.

함수 SUM 작성 (int [])는 int8을 $$로 반환합니다

요소는 배열 치수 수에 관계없이 스토리지 순서로 방문됩니다. 비록대상는 일반적으로 단일 변수 일 뿐이며 복합 값 (레코드) 배열을 통해 반복 할 때 변수 목록 일 수 있습니다. 이 경우 각 배열 요소에 대해 변수는 복합 값의 연속적인 열에서 할당됩니다.

긍정적 인슬라이스value,foreach단일 요소가 아닌 배열 조각을 통해 반복합니다. 그만큼슬라이스값은 배열의 치수 수보다 더 큰 정수 일정이어야합니다. 그만큼대상변수는 배열이어야하며 배열 값의 연속 슬라이스를 수신해야합니다. 여기서 각 슬라이스는에 의해 지정된 치수 수입니다.슬라이스. 다음은 1 차원 슬라이스를 통한 반복의 예입니다.

함수 생성 Scan_rows (int [])는 void를 $$로 반환합니다

41.6.8. 트래핑 오류#

기본적으로 A에서 발생하는 오류pl/pgsql함수는 함수 및 주변 트랜잭션의 실행을 중단합니다. a를 사용하여 오류를 가두고 복구 할 수 있습니다.시작예외절. 구문은 a에 대한 일반 구문의 확장입니다.시작블록 :

[ <<레이블 ]선언선언 ]진술예외조건 [또는조건 ... ] 그런 다음handler_statements
    [언제조건 [또는조건 ... ] 그런 다음handler_statements
      ... ]

오류가 발생하지 않으면이 형태의 블록은 단순히 모든 것을 실행합니다진술END. 그러나 내에서 오류가 발생하면진술, 추가 처리진술버려졌고 제어가로 통과합니다.예외목록. 목록은 첫 번째를 검색합니다조건발생한 오류와 일치합니다. 일치가 발견되면 해당handler_statements실행 된 다음 컨트롤이 다음 명령문으로 전달됩니다END. 일치가 발견되지 않으면 오류가 마치처럼 전파됩니다.예외조항은 전혀 없었습니다.예외또는 없으면 기능의 처리가 중단됩니다.

the조건이름이 표시된 이름 일 수 있습니다PostgreSQL : 문서 : 17 : 부록 A. PostgreSQL 토토 꽁 머니 코드. 카테고리 이름은 해당 범주 내에서 오류와 일치합니다.기타query_canceledandAssert_Failure. (이 두 가지 오류 유형을 이름별로 붙잡는 것은 가능하지만 종종 현명하지 않습니다.) 조건 이름은 대소 문자에 민감하지 않습니다.sqlstate코드; 예를 들어, 이것들은 동일합니다 :

언제 division_by_zero가 ...

선택한 내에서 새 오류가 발생하면handler_statements, 이에 따라 잡을 수 없습니다예외조항이지만 전파됩니다. 주변예외조항을 잡을 수 있습니다.

오류가 발생할 때예외절의 조항,.pl/pgsql함수는 오류가 발생했을 때와 마찬가지로 유지되지만 블록 내 지속적인 데이터베이스 상태로의 모든 변경 사항이 롤백됩니다. 예를 들어이 조각을 고려하십시오 :

mytab에 삽입 (FirstName, lastName) 값 ( 'Tom', 'Jones');

제어가 할당에 도달하는 경우y, A로 실패합니다.division_by_zero오류. 이것은에 의해 잡힐 것입니다.예외절. 반환 된 값return명령문은 증분 값이 될 것입니다x그러나의 효과업데이트명령이 롤백됩니다. 그만큼삽입앞의 명령은 롤백되지 않으므로 최종 결과에는 데이터베이스에 포함되어 있습니다.Tom JonesnotJoe Jones.

an을 포함하는 블록예외절은 하나가없는 블록보다 입력 및 종료하는 데 훨씬 비싸다. 따라서 사용하지 마십시오예외필요없이.

예 41.2. 예외업데이트/삽입

이 예제는 예외 처리를 사용하여 하나를 수행합니다업데이트또는삽입, 적절합니다. 응용 프로그램이 사용하는 것이 좋습니다삽입with충돌시 업데이트실제로이 패턴을 사용하지 않고. 이 예는 주로의 사용을 설명하는 역할을합니다.pl/pgsql제어 흐름 구조 :

테이블 DB 생성 (int 기본 키, b 텍스트);

이 코딩은를 가정합니다.고유 _violation오류는에 의해 발생합니다.삽입삽입테이블의 트리거 함수에서. 테이블에 고유 한 인덱스가 둘 이상인 경우 오작동 할 수도 있습니다. 어떤 색인이 오류를 일으킨 지에 관계없이 작업을 재 시도하므로 오류가 발생할 수 있습니다.


41.6.8.1. 오류에 대한 정보 얻기#

예외 처리기는 발생한 특정 오류를 자주 식별해야합니다. 현재 예외에 대한 정보를 얻는 두 가지 방법이 있습니다.pl/pgsql: 특수 변수 및스택 진단 받기명령.

예외 핸들러 내에서 특수 변수sqlstate제기 된 예외에 해당하는 오류 코드가 포함되어 있습니다 (참조표 A.1가능한 오류 코드 목록의 경우). 특별 변수sqlerrm예외와 관련된 오류 메시지를 포함합니다. 이 변수는 외부 예외 처리기 외부의 정의되지 않았습니다.

예외 핸들러 내에서를 사용하여 현재 예외에 대한 정보를 검색 할 수도 있습니다.스택 진단 받기COMMAND : 양식이 있습니다.

스택 진단 받기가변= | : =항목 [ , ... ];

항목지정된 상태 값을 식별하는 핵심 단어가변(이를 수신하기에 적합한 데이터 유형이어야 함). 현재 사용 가능한 상태 항목은에 표시됩니다.표 41.2.

표 41.2. 오류 진단 항목

이름 타입 설명
returned_sqlstate 텍스트 예외의 SQLSTATE 오류 코드
column_name 텍스트 예외와 관련된 열의 이름
제약 _name 텍스트 예외와 관련된 제약 조건의 이름
PG_DATATYPE_NAME 텍스트 예외와 관련된 데이터 유형의 이름
Message_Text 텍스트 예외의 기본 메시지의 텍스트
table_name 텍스트 예외와 관련된 테이블의 이름
Schema_Name 텍스트 예외와 관련된 스키마의 이름
pg_exception_detail 텍스트 예외 세부 메시지의 텍스트가있는 경우
pg_exception_hint 텍스트 예외 힌트 메시지의 텍스트가 있다면
pg_exception_context 텍스트 예외 당시 통화 스택을 설명하는 텍스트의 줄 (참조섹션 41.6.9)

예외가 항목의 값을 설정하지 않으면 빈 문자열이 반환됩니다.

예는 다음과 같습니다.

선언

41.6.9. 실행 위치 정보#

the진단 받기명령, 이전에 설명 된섹션 41.5.5는 현재 실행 상태에 대한 정보를 검색합니다 (스택 진단 받기위에서 논의한 명령은 이전 오류로 실행 상태에 대한 정보를보고합니다). 그것은PG_CONTEXT상태 항목은 현재 실행 위치를 식별하는 데 유용합니다.PG_CONTEXT통화 스택을 설명하는 텍스트의 줄이있는 텍스트 문자열을 반환합니다. 첫 번째 줄은 현재 함수와 현재 실행 중입니다진단 받기명령. 두 번째 및 후속 선은 호출 함수를 통화 스택 위로 추가로 나타냅니다.

함수 생성 또는 교체 OUTER_FUNC () 정수를 $$로 반환합니다

스택 진단 받기 ... pg_exception_context동일한 종류의 스택 추적을 반환하지만 현재 위치가 아닌 오류가 감지 된 위치를 설명합니다.

수정 제출

문서에 올바르지 않은 내용이 있으면 일치하지 않습니다.이 양식문서 문제를보고하려면