5.Pro C에서 동적 SQL 사용하기, 내용 EXECUTE IMMEDIATE, DBMS SQL 패키지 활용.

Pro*C는 C 프로그램에서 SQL 문을 포함할 수 있도록 해주는 Oracle의 전처리기입니다. 동적 SQL은 프로그램 실행 중에 SQL 문의 구조를 결정할 수 있는 기능으로, 다양한 데이터베이스 요구 사항에 유연하게 대응하기 위한 매우 강력한 도구입니다. 이 글에서는 EXECUTE IMMEDIATEDBMS_SQL 패키지를 활용하여 Pro*C에서 동적 SQL을 사용하는 방법에 대해 알아보겠습니다.

1. 동적 SQL의 필요성

동적 SQL을 사용하는 주된 이유는 SQL 문을 실행할 때 정적 SQL로는 처리할 수 없는 동적인 데이터베이스 쿼리를 작성할 수 있기 때문입니다. 예를 들어, 사용자 입력에 따라 쿼리의 WHERE 절이 변경되거나, 실행할 SQL 문을 전적으로 프로그램의 로직에 의해 결정해야 하는 경우입니다.

2. EXECUTE IMMEDIATE

EXECUTE IMMEDIATE는 Pro*C에서 동적 SQL을 실행하는 가장 간단한 방법 중 하나입니다. 이 구문을 사용 사용하면, SQL 문을 문자열 형식으로 정의할 수 있습니다. 다음은 EXECUTE IMMEDIATE를 사용하는 기본적인 예제입니다.


EXECUTE IMMEDIATE 'INSERT INTO employees (id, name) VALUES (:1, :2)'
USING :emp_id, :emp_name;

이 코드에서 INSERT 문은 문자형으로 정의된 후 USING 절을 통해 변수를 바인딩합니다. 이는 성능과 보안을 모두 고려한 동적 SQL의 방식입니다. 바인딩 변수의 사용은 SQL 인젝션 공격을 방지하는 데 중요한 역할을 합니다.

2.1. 예제: 동적 INSERT 문 실행하기


#include <stdio.h>
#include <sqlca.h>
#include <sqlcpr.h>
#include <string.h>

void insert_employee(int emp_id, char* emp_name) {
    EXEC SQL BEGIN DECLARE SECTION;
        int id = emp_id;
        char name[50];
    EXEC SQL END DECLARE SECTION;

    strncpy(name, emp_name, sizeof(name) - 1);

    EXEC SQL EXECUTE IMMEDIATE 
        'INSERT INTO employees (id, name) VALUES (:id, :name)';
    
    if (sqlca.sqlcode != 0) {
        printf("Error occurred: %d\n", sqlca.sqlcode);
    } else {
        printf("Employee inserted successfully.\n");
    }
}

위의 예제는 프로시저 안에서 동적으로 SQL INSERT 문을 실행하는 방법을 보여줍니다. 에러 처리를 위해 sqlca.sqlcode를 사용하여 SQL 실행 결과를 확인합니다.

3. DBMS_SQL 패키지

DBMS_SQL은 명시적인 커서를 통해 동적 SQL을 처리하기 위한 Oracle의 패키지입니다. 이 방법은 SQL 문이 동적으로 변경되어야 할 때 유용합니다. DBMS_SQL 패키지는 다소 복잡하지만 더 많은 유연성을 제공합니다.

3.1. DBMS_SQL 기본 사용법

DBMS_SQL 패키지를 사용하여 동적 SQL을 실행하는 홈은 다음과 같은 단계로 나누어집니다:

  1. 커서를 선언하고 초기화한다.
  2. SQL 문을 파싱한다.
  3. 바인딩 변수를 설정한다.
  4. SQL을 실행한다.
  5. 결과를 가져온다.

3.2. 예제: DBMS_SQL을 사용한 동적 쿼리 실행하기


#include <stdio.h>
#include <sqlca.h>
#include <sqlcpr.h>

void run_dynamic_query(char* sql_stmt) {
    int cursor;
    int emp_id;
    char emp_name[50];

    cursor = DBMS_SQL.OPEN_CURSOR();

    DBMS_SQL.PARSE(cursor, sql_stmt, strlen(sql_stmt));
    
    // Variable Binding
    DBMS_SQL.BIND_VARIABLE(cursor, ":1", emp_id);
    DBMS_SQL.BIND_VARIABLE(cursor, ":2", emp_name);
    
    DBMS_SQL.DEFINE_COLUMN(cursor, 1, emp_id, sizeof(emp_id));
    DBMS_SQL.DEFINE_COLUMN(cursor, 2, emp_name, sizeof(emp_name));
    
    int ret_code = DBMS_SQL.EXECUTE(cursor);
    
    if (ret_code != 0) {
        printf("SQL execution error: %d\n", ret_code);
    }
    
    // Fetching results
    while (DBMS_SQL.FETCH_ROWS(cursor) > 0) {
        printf("Employee ID: %d, Name: %s\n", emp_id, emp_name);
    }
    
    DBMS_SQL.CLOSE_CURSOR(cursor);
}

이 예제는 주어진 SQL 문에 대해 DBMS_SQL 패키지를 사용하여 동적 쿼리를 실행합니다. 변수를 바인딩하고 결과를 가져오는 방법을 보여줍니다.

4. 동적 SQL의 장단점

동적 SQL은 많은 장점을 가지고 있지만, 단점도 존재합니다. 아래에서 각각을 살펴보겠습니다.

4.1. 장점

  • 유연성: 프로그램 실행 중에 SQL 문을 수정할 수 있으므로 다양한 데이터베이스 요구 사항에 유연하게 대응할 수 있습니다.
  • 복잡한 쿼리 수행: 고정된 쿼리로는 처리하기 어려운 복잡한 쿼리를 실행할 수 있습니다.
  • SQL 문 동적 생성: 사용자 입력에 따라 SQL 문을 동적으로 생성하여 실행할 수 있습니다.

4.2. 단점

  • 성능 문제: 동적 SQL은 SQL 문을 반복해서 파싱해야 하므로 성능이 저하될 수 있습니다.
  • SQL 인젝션 위험: SQL 문이 동적으로 생성될 때, 사용자 입력이 제대로 검증되지 않으면 SQL 인젝션 공격에 취약해집니다.
  • 디버깅 어려움: 동적 SQL의 경우 오류가 발생할 경우 디버깅이 어렵고, 필요한 로그 정보를 온전히 얻기 힘든 경우가 많습니다.

5. 결론

Pro*C에서 동적 SQL을 사용하는 방법에 대해 알아보았습니다. EXECUTE IMMEDIATEDBMS_SQL 패키지는 각각의 상황에 따라 적절하게 선택하여 사용할 수 있으며, 이들 기능을 통해 더 복잡하고 유연한 데이터베이스 애플리케이션을 개발할 수 있습니다. 적절한 오류 처리와 함께 동적 SQL을 구현하는 것이 중요합니다. 동적 SQL의 장단점을 잘 이해하고 적절히 활용하여, 효율적이고 안전한 데이터베이스 작업을 수행하시기 바랍니다.