Skip to content

Commit

Permalink
doc: add about function overloading.
Browse files Browse the repository at this point in the history
  • Loading branch information
ashigeru committed Jul 18, 2024
1 parent 954332f commit 2a8d668
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 0 deletions.
150 changes: 150 additions & 0 deletions docs/ja/function-overload-resolution_ja.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# function overload resolution

## この文書について

* この文書は、Tsurugi のSQLコンパイラにおいて、関数のオーバーロードを行う際の挙動について示す

## 基本的な考え方

* Tsurugi の関数は同名であっても仮引数の個数や型が異なる場合、異なる関数として扱われる
* Tsurugi の関数呼び出しは、仮引数の型と実引数の型が完全に一致していなくても、実引数の値を仮引数の型に **自然に変換できる**のであれば、その関数を呼び出せる
* ただし、オーバーロードのあいまいさを低減するため、代入変換 (INSERT/UPDATE時の自動変換等) よりもより制限的な変換のみを行う

## 仮引数の特性

以下は関数定義に含まれる各仮引数の特徴である:

* 仮引数には入力 (IN) と出力 (OUT) を表すものがある
* ただし、実引数には入力のみを指定することになり、出力引数はオーバーロード解決には関係しない
* そのため、本文書の以降では、出力引数について言及しない
* 仮引数の型は、互換性のある上限の型を指定しなければならない
* 以下は、自身の型と互換性のある上限の型が異なるものの一覧 (自身の型 -> 上限の型)
* `INT1` -> `INT4`
* `INT2` -> `INT4`
* `DECIMAL(p, s)` -> `DECIMAL(*, *)`
* `CHAR(s)` -> `VARCHAR(*)`
* `VARCHAR(s)`-> `VARCHAR(*)`
* `BINARY(s)` -> `VARBINARY(*)`
* `VARBINARY(s)` -> `VARBINARY(*)`
* 上記に含まれない型は自由に仮引数の型としてよい
* 妥当でない型を仮引数の型とした場合の挙動は未定義

## パラメータ適用変換

パラメーター適用変換 (*parameter application conversion*) とは、実引数を仮引数のデータ型に変換するための規則である。

関数呼び出しを行う際、関数呼び出し式に含まれる実引数は、対応する仮引数と同じデータ型を有するとは限らない。このとき、当該変換を利用し、実引数の値を仮引数のデータ型のものに変換したうえで、対象の関数を呼び出すことになる。

逆に言えば、いずれかの実引数をパラメータ適用変換によって対象の仮引数のデータ型に変換できない場合、そのような関数は呼び出すことができない。

パラメータ適用変換の可否判断は、以下のステップで行われる。
なお、実引数のデータ型を $A$, 変換対象の仮引数のデータ型を $P$ とおく。

1. $A$ に単項互換昇格 (*unary compatible type promotion*) を適用した結果を $A'$ とおく
2. 以下の表より、 $A'$ から $P$ に変換可能である場合に限り、 $A$ から $P$ へのパラメータ適用変換を適用できる

なお、データ型 $T_1$ をデータ型 $T_2$ にパラメータ適用変換可能である場合、 $T_1 \preceq_p T_2$ のようにかくものとする。

| from \ to | `boolean` | `int4` | `int8` | `decimal` | `float4` | `float8` | `character varying` | `bit varying` | `octet varying` | `date` | `time_of_day` | `time_of_day_tz` | `time_point` | `time_point_tz` | `unknown` |
|--------------------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| `boolean` | v | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| `int4` | - | v | v | v | v | v | - | - | - | - | - | - | - | - | - |
| `int8` | - | - | v | v | v | v | - | - | - | - | - | - | - | - | - |
| `decimal` | - | - | - | v | v | v | - | - | - | - | - | - | - | - | - |
| `float4` | - | - | - | - | v | v | - | - | - | - | - | - | - | - | - |
| `float8` | - | - | - | - | - | v | - | - | - | - | - | - | - | - | - |
| `character varying` | - | - | - | - | - | - | v | - | - | - | - | - | - | - | - |
| `bit varying` | - | - | - | - | - | - | - | v | - | - | - | - | - | - | - |
| `octet varying` | - | - | - | - | - | - | - | - | v | - | - | - | - | - | - |
| `date` | - | - | - | - | - | - | - | - | - | v | - | - | - | - | - |
| `time_of_day` | - | - | - | - | - | - | - | - | - | - | v | - | - | - | - |
| `time_of_day_tz` | - | - | - | - | - | - | - | - | - | - | - | v | - | - | - |
| `time_point` | - | - | - | - | - | - | - | - | - | - | - | - | v | - | - |
| `time_point_tz` | - | - | - | - | - | - | - | - | - | - | - | - | - | v | - |
| `unknown` | v | v | v | v | v | v | v | v | v | v | v | v | v | v | v |

なお、`unknown` 型の仮引数を定義することは不可能であるが、規則の簡単化のために上記の定義には含めている。

----
discussion:

上記は代入変換のよりも以下の点で制限的である。

* 互換性上限のデータ型のみを取り扱っている
* 仮引数は定義上互換性上限でなくてはならず、実引数も単項互換昇格により互換性上限を利用することになる
* 一部の例外を除き、実引数の値の精度が失われることはない
* 例外的に、exact numeric から approximate numeric への変換 (e.g., `int8` to `float8`) は精度が失われる
* 相互に変換可能な型が存在しない
* オーバーロード解決規則の単純化のため
* 関係 $\preceq_p$ の記号通り、自身の型へのパラメータ適用変換は常に成功する

## 呼び出し対象関数の特定

呼び出し先の関数を決定する際、以下の2ステップで行う:

1. 呼び出し可能な関数一覧を探索
2. 呼び出し可能な関数一覧から、実際に呼び出す関数を選別

なお、本節では以下の条件で関数呼び出しを行っているものとする。

* 対象の関数名は $s$
* 実引数を $n$ 個指定し、それぞれの式の型は $A_1$ .. $A_n$

### 呼び出し候補の探索

最初のステップでは、SQL内の関数呼び出しによって呼び出せる可能性のある関数定義の一覧を候補として抽出する。この候補のことを「呼び出し候補集合 (*call candidate set*)」とよぶ。

なお、呼び出し元から利用可能なすべての関数定義の集合を $F$ とおく。

呼び出し候補集合は、 $F$ に含まれる関数定義のうち、以下のすべてを満たすものからなる。

* 関数名が $s$ である
* 入力仮引数の個数が $n$ 個である[^1]
* 入力仮引数の型をそれぞれ $P_1$.. $P_n$ としたとき、 $i = 1..n$ についてそれぞれ $A_i \preceq_p P_i$ が成り立つ

上記の候補集合が空となる場合、呼び出し対象の関数定義が存在しないとして**関数呼び出しに失敗する**

[^1]: n 個以上で n+1 個目以降はデフォルト値を有する、等でも構わないが、ここでは簡単のため省略する

### 呼び出し対象の特定

次のステップでは、[前項](#呼び出し候補の探索) において計算した呼び出し候補集合から、実際に呼び出す対象の関数定義を特定する。

このステップの基本的な考え方は、「候補の中から**より限定的な関数**を選ぶ」というものである。

これは、関数定義 $f_1$ と $f_2$ について、 $f_1$ を呼び出し可能な任意の式が $f_2$ も呼び出し可能であったとき、 $f_2$ を呼び出し可能な任意の式が $f_1$ を呼び出せるとは限らない場合、 $f_1$ は $f_2$ よりも「より限定的である」とする。また、このことを $f_1 \prec_f f_2$ とかく。

上記の判定は、次のように行う。

* 関数定義 $f_1$ は、入力仮引数 $P_i$ .. $P_n$ を有する
* 関数定義 $f_2$ は、入力仮引数 $Q_i$ .. $Q_n$ を有する
* 以下のすべてが成り立つ場合、 $f_1 \prec_f f_2$ が成り立つ
* すべての $i = 1..n$ について、 $Pi \preceq_p Qi$ が成り立つ
* いずれかの $i = 1..n$ について、 $Qi \preceq_p Pi$ が **成り立たない**

上記を利用して実際に呼び出す対象の関数定義を特定するには、次のステップで行う。
なお、対象の候補集合を $G$ とおく。

1. $G$ に含まれる任意の $f_1$, $f_2$ について、 $f_1 \prec_f f_2$ が成り立つ場合、 $G$ から $f_2$ を取り除く
2. 上記を $G$ から取り除くものがなくなるまで繰り返す

上記の手続きを行った結果の $G$ について、候補が2つ以上残っていた場合には呼び出し対象の関数定義があいまいとして**関数呼び出しに失敗する**

最後に $G$ に残った唯一の関数定義が、呼び出し対象の関数定義として採用されることになる。

----
discussion:

* 結果の集合が空となることは構成上あり得ない
* 結果があいまいとなるケースは以下のような例が挙げられる
* 候補集合に仮引数の列が `(BIGINT, DECIMAL(*, *))``(DECIMAL(*, *), BIGINT)` の2つの関数定義が含まれる場合、実引数 `(1, 2)` の組 (`(BIGINT, BIGINT)`) で呼び出す際に曖昧になってしまう
* 候補集合に仮引数の列が `(VARCHAR(*))``(VARBINARY(*))` の2つの関数定義が含まれる場合、実引数 `(NULL)` の組で呼び出す際に曖昧になってしまう
* 「すべての $i = 1..n$ について、 $Pi \preceq_p Qi$ が成り立つ」 とはつまり、「 $P_i$ を実引数として $Q_i$ の仮引数に適用可能」ということを表している

----
discussion:

実引数に `NULL` を指定した場合はオーバーロード解決が非常に困難になる。

* `NULL` のデータ型 `unknown` は任意のデータ型にパラメータ適用変換可能であるため、オーバーロードの候補を絞り切れなくなる
* この場合、適切にキャスト式を利用することで、あいまいさのない実引数の型を指定してやる必要がある
38 changes: 38 additions & 0 deletions docs/ja/scalar-expressions-and-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,44 @@ notes:

拡大変換は現在のところ関数のオーバーロード解決のみに利用される。

## パラメータ適用変換

パラメーター適用変換 (*parameter application conversion*) とは、実引数を仮引数のデータ型に変換する。

関数呼び出しを行う際、関数呼び出し式に含まれる実引数は、対応する仮引数と同じデータ型を有するとは限らない。このとき、当該変換を利用し、実引数の値を仮引数のデータ型のものに変換したうえで、対象の関数を呼び出すことになる。

逆に言えば、いずれかの実引数をパラメータ適用変換によって対象の仮引数のデータ型に変換できない場合、そのような関数は呼び出すことができない。

パラメータ適用変換の可否判断は、以下のステップで行われる。
なお、実引数のデータ型を `A`, 変換対象の仮引数のデータ型を `P` とおく。

1. `A` に単項互換昇格 (*unary compatible type promotion*) を適用した結果を `A'` とおく
2. 以下の表より、 `A'` から `P` に変換可能である場合に限り、 `A` から `P` へのパラメータ適用変換を適用できる

| from \ to | `boolean` | `int4` | `int8` | `decimal` | `float4` | `float8` | `character varying` | `bit varying` | `octet varying` | `date` | `time_of_day` | `time_of_day_tz` | `time_point` | `time_point_tz` | `unknown` |
|--------------------:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| `boolean` | v | - | - | - | - | - | - | - | - | - | - | - | - | - | - |
| `int4` | - | v | v | v | v | v | - | - | - | - | - | - | - | - | - |
| `int8` | - | - | v | v | v | v | - | - | - | - | - | - | - | - | - |
| `decimal` | - | - | - | v | v | v | - | - | - | - | - | - | - | - | - |
| `float4` | - | - | - | - | v | v | - | - | - | - | - | - | - | - | - |
| `float8` | - | - | - | - | - | v | - | - | - | - | - | - | - | - | - |
| `character varying` | - | - | - | - | - | - | v | - | - | - | - | - | - | - | - |
| `bit varying` | - | - | - | - | - | - | - | v | - | - | - | - | - | - | - |
| `octet varying` | - | - | - | - | - | - | - | - | v | - | - | - | - | - | - |
| `date` | - | - | - | - | - | - | - | - | - | v | - | - | - | - | - |
| `time_of_day` | - | - | - | - | - | - | - | - | - | - | v | - | - | - | - |
| `time_of_day_tz` | - | - | - | - | - | - | - | - | - | - | - | v | - | - | - |
| `time_point` | - | - | - | - | - | - | - | - | - | - | - | - | v | - | - |
| `time_point_tz` | - | - | - | - | - | - | - | - | - | - | - | - | - | v | - |
| `unknown` | v | v | v | v | v | v | v | v | v | v | v | v | v | v | v |

なお、`unknown` 型の仮引数を定義することは不可能であるが、規則の簡単化のために上記の定義には含めている。

上記の凡例は以下のとおりである。

* `v` - 変換可能

## スカラー式の種類

本章では、スカラー式の具体的な種類について紹介する。
Expand Down

0 comments on commit 2a8d668

Please sign in to comment.