

# Aurora PostgreSQL에서 사용자 지정 캐스트 관리
<a name="PostgreSQL.CustomCasts"></a>

PostgreSQL의 **유형 캐스팅**은 값을 한 데이터 유형에서 다른 데이터 유형으로 변환하는 프로세스입니다. PostgreSQL은 대다수 일반적인 변환을 위한 내장 캐스트를 제공하지만 사용자 지정 캐스트를 생성하여 특정 유형 변환의 작동 방식을 정의할 수도 있습니다.

캐스트는 한 데이터 유형에서 다른 데이터 유형으로 변환하는 방법을 지정합니다. 예를 들어 텍스트 `'123'`을 정수 `123`으로 변환하거나 숫자 `45.67`을 `'45.67'`로 변환합니다.

PostgreSQL 캐스팅 개념 및 구문에 대한 자세한 내용은 [PostgreSQL CREATE CAST 설명서](https://www.postgresql.org/docs/current/sql-createcast.html)를 참조하세요.

Aurora PostgreSQL 버전 13.23, 14.20, 15.15, 16.11, 17.7 및 18.1부터 rds\$1casts 확장을 사용하여 내장 유형에 대한 추가 캐스트를 설치할 수 있으며, 사용자 지정 유형에 대한 자체 캐스트를 생성할 수도 있습니다.

**Topics**
+ [rds\$1casts 확장 설치 및 사용](#PostgreSQL.CustomCasts.Installing)
+ [지원되는 캐스트](#PostgreSQL.CustomCasts.Supported)
+ [캐스트 생성 또는 삭제](#PostgreSQL.CustomCasts.Creating)
+ [적절한 컨텍스트 전략을 사용하여 사용자 지정 캐스트 생성](#PostgreSQL.CustomCasts.BestPractices)

## rds\$1casts 확장 설치 및 사용
<a name="PostgreSQL.CustomCasts.Installing"></a>

`rds_casts` 확장을 생성하려면 Aurora PostgreSQL DB 클러스터의 라이터 인스턴스에 `rds_superuser`로 연결하고 다음 명령을 실행합니다.

```
CREATE EXTENSION IF NOT EXISTS rds_casts;
```

## 지원되는 캐스트
<a name="PostgreSQL.CustomCasts.Supported"></a>

사용자 지정 캐스트를 사용하려는 각 데이터베이스에서 확장을 생성합니다. 확장을 생성한 후 다음 명령을 사용하여 사용 가능한 모든 캐스트를 봅니다.

```
SELECT * FROM rds_casts.list_supported_casts();
```

이 함수는 사용 가능한 캐스트 조합(소스 유형, 대상 유형, 강제 컨텍스트 및 캐스트 함수)을 나열합니다. 예를 들어 `implicit` 캐스트로 `text`에서 `numeric`으로의 변환을 생성하려는 경우 다음 쿼리를 사용하여 캐스트를 생성할 수 있는지 확인할 수 있습니다.

```
SELECT * FROM rds_casts.list_supported_casts()
WHERE source_type = 'text' AND target_type = 'numeric';
 id | source_type | target_type |          qualified_function          | coercion_context
----+-------------+-------------+--------------------------------------+------------------
 10 | text        | numeric     | rds_casts.rds_text_to_numeric_custom | implicit
 11 | text        | numeric     | rds_casts.rds_text_to_numeric_custom | assignment
 13 | text        | numeric     | rds_casts.rds_text_to_numeric_custom | explicit
 20 | text        | numeric     | rds_casts.rds_text_to_numeric_inout  | implicit
 21 | text        | numeric     | rds_casts.rds_text_to_numeric_inout  | assignment
 23 | text        | numeric     | rds_casts.rds_text_to_numeric_inout  | explicit
```

rds\$1casts 확장은 각 캐스트에 대해 두 가지 유형의 변환 함수를 제공합니다.
+ *\$1inout 함수* - PostgreSQL의 표준 I/O 변환 메커니즘을 사용하며, INOUT 메서드로 생성된 캐스트와 동일하게 동작합니다.
+ *\$1custom 함수* - 변환 오류를 방지하기 위해 빈 문자열을 NULL 값으로 변환하는 등 엣지 사례를 처리하는 향상된 변환 로직을 제공합니다.

`inout` 함수는 PostgreSQL의 기본 캐스팅 동작을 재현하는 한편, `custom` 함수는 빈 문자열을 정수로 변환하는 등 표준 INOUT 캐스팅으로는 불가능한 시나리오를 처리하여 이 기능을 확장합니다.

## 캐스트 생성 또는 삭제
<a name="PostgreSQL.CustomCasts.Creating"></a>

다음 두 가지 방법을 사용하여 지원되는 캐스트를 생성하고 삭제할 수 있습니다.

### 캐스트 생성
<a name="PostgreSQL.CustomCasts.Creating.Methods"></a>

**방법 1: 기본 CREATE CAST 명령 사용**

```
CREATE CAST (text AS numeric)
WITH FUNCTION rds_casts.rds_text_to_numeric_custom
AS IMPLICIT;
```

**방법 2: rds\$1casts.create\$1cast 함수 사용**

```
SELECT rds_casts.create_cast(10);
```

`create_cast` 함수는 `list_supported_casts()` 출력에서 ID를 가져옵니다. 이 방법은 더 간단하며 올바른 함수와 컨텍스트 조합을 사용할 수 있도록 합니다. 이 ID는 여러 postgres 버전에서 동일하게 유지됩니다.

캐스트가 성공적으로 생성되었는지 확인하려면 pg\$1cast 시스템 카탈로그를 쿼리합니다.

```
SELECT oid, castsource::regtype, casttarget::regtype, castfunc::regproc, castcontext, castmethod
FROM pg_cast
WHERE castsource = 'text'::regtype AND casttarget = 'numeric'::regtype;
  oid   | castsource | casttarget |               castfunc               | castcontext | castmethod
--------+------------+------------+--------------------------------------+-------------+------------
 356372 | text       | numeric    | rds_casts.rds_text_to_numeric_custom | i           | f
```

`castcontext` 열에는 EXPLICIT을 나타내는 `e`, ASSIGNMENT를 나타내는 `a`, IMPLICIT을 나타내는 `i`가 표시됩니다.

### 캐스트 삭제
<a name="PostgreSQL.CustomCasts.Dropping"></a>

**방법 1: DROP CAST 명령 사용**

```
DROP CAST IF EXISTS (text AS numeric);
```

**방법 2: rds\$1casts.drop\$1cast 함수 사용**

```
SELECT rds_casts.drop_cast(10);
```

`drop_cast` 함수는 캐스트를 생성할 때 사용된 것과 동일한 ID를 사용합니다. 이 방법을 통해 해당 ID로 생성된 정확한 캐스트를 삭제할 수 있습니다.

## 적절한 컨텍스트 전략을 사용하여 사용자 지정 캐스트 생성
<a name="PostgreSQL.CustomCasts.BestPractices"></a>

정수 유형에 대해 여러 캐스트를 생성할 때 모든 캐스트가 IMPLICIT으로 생성되면 연산자 모호성 오류가 발생할 수 있습니다. 다음 예제에서는 텍스트에서 서로 다른 정수 너비로 두 개의 암시적 캐스트를 생성하여 이 문제를 보여줍니다.

```
-- Creating multiple IMPLICIT casts causes ambiguity
postgres=> CREATE CAST (text AS int4) WITH FUNCTION rds_casts.rds_text_to_int4_custom(text) AS IMPLICIT;
CREATE CAST
postgres=> CREATE CAST (text AS int8) WITH FUNCTION rds_casts.rds_text_to_int8_custom(text) AS IMPLICIT;
CREATE CAST

postgres=> CREATE TABLE test_cast(col int);
CREATE TABLE
postgres=> INSERT INTO test_cast VALUES ('123'::text);
INSERT 0 1
postgres=> SELECT * FROM test_cast WHERE col='123'::text;
ERROR:  operator is not unique: integer = text
LINE 1: SELECT * FROM test_cast WHERE col='123'::text;
                                         ^
HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.
```

이 오류는 PostgreSQL이 정수 열을 텍스트 값과 비교할 때 사용할 암시적 캐스팅을 결정할 수 없기 때문에 발생합니다. int4와 int8 암시적 캐스트가 모두 유효한 후보이므로 모호성이 발생합니다.

이 연산자 모호성을 방지하려면 작은 정수 너비에는 ASSIGNMENT 컨텍스트를 사용하고 큰 정수 너비에는 IMPLICIT 컨텍스트를 사용합니다.

```
-- Use ASSIGNMENT for smaller integer widths
CREATE CAST (text AS int2)
WITH FUNCTION rds_casts.rds_text_to_int2_custom(text)
AS ASSIGNMENT;

CREATE CAST (text AS int4)
WITH FUNCTION rds_casts.rds_text_to_int4_custom(text)
AS ASSIGNMENT;

-- Use IMPLICIT for larger integer widths
CREATE CAST (text AS int8)
WITH FUNCTION rds_casts.rds_text_to_int8_custom(text)
AS IMPLICIT;

postgres=> INSERT INTO test_cast VALUES ('123'::text);
INSERT 0 1
postgres=> SELECT * FROM test_cast WHERE col='123'::text;
 col
-----
 123
(1 row)
```

이 전략에서는 int8 캐스트만 암시적이므로 PostgreSQL이 사용할 캐스트를 명확하게 결정할 수 있습니다.