Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
# Minimal configuration for getting started
# language: "zh-CN"
language: "en-US"
reviews:
profile: "chill"
high_level_summary: true
auto_review:
enabled: true
drafts: false
base_branches: ["master", "v5.0", "v4.6", "v1.17"]
2 changes: 2 additions & 0 deletions CN/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
**** xref:master/6.3.3.adoc[RowID]
**** xref:master/6.3.2.adoc[OUT 参数]
**** xref:master/6.3.4.adoc[%TYPE、%ROWTYPE]
**** xref:master/6.3.5.adoc[NLS 参数]
*** xref:master/6.4.adoc[国标GB18030]
** Oracle兼容功能列表
*** xref:master/7.1.adoc[1、框架设计]
Expand All @@ -43,6 +44,7 @@
*** xref:master/7.14.adoc[14、RowID]
*** xref:master/7.15.adoc[15、OUT 参数]
*** xref:master/7.16.adoc[16、%TYPE、%ROWTYPE]
*** xref:master/7.17.adoc[17、NLS 参数]
** IvorySQL贡献指南
*** xref:master/8.1.adoc[社区贡献指南]
*** xref:master/8.2.adoc[asciidoc语法快速参考]
Expand Down
159 changes: 159 additions & 0 deletions CN/modules/ROOT/pages/master/6.3.5.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@

:sectnums:
:sectnumlevels: 5


= **功能概述**

IvorySQL提供了兼容Oracle的NLS参数功能,包含如下参数。

[cols="3,7"]
|====
|*参数名称*|*功能描述*
|ivorysql.datetime_ignore_nls_mask | 表示日期格式是否忽略NLS参数影响,默认为0。
|nls_length_semantics | 兼容Oracle的同名参数,表示char/varchar/varchar2的类型修饰符的大小单位是字节还是字符。
|nls_date_format | 表示默认的日期格式,可以通过show命令查看,默认为‘YYYY-MM-DD’。
|nls_timestamp_format | 兼容Oracle的同名参数,控制带时间的日期格式。
|nls_timestamp_tz_format | 兼容Oracle的同名参数,控制带时区的日期时间格式。
|nls_territory | 兼容Oracle的同名参数,指定数据库的默认区域。
|nls_iso_currency | 兼容Oracle的同名参数,指定的国家和区域制定唯一的货币符号。
|nls_currency | 兼容Oracle的同名参数,指定显示本地货币的符号,对应数字字符串格式中占位符L。
|====

== 实现原理

=== `nls_length_semantics` 参数

IvorySQL中的数据类型存在一个属性修饰符 `typmod` ,是对类型的补充说明,比如在 `VARCHAR(n)` 类型中,`n` 就是类型修饰符。

在创建或修改表的列时可以指定长度类型,例如:
```c
ivorysql=# create table t1(name varchar2(2 byte));
```

对于列类型为 `CHAR` 、 `VARCHAR` 和 `VARCHAR2` 字符型的列,当没有显式指定列的长度类型时,IvorySQL使用 `nls_length_semantics` 参数的值来决定长度类型,有 `byte` 和 `char` 两种值,默认为 `byte` 。

需要特别注意的是, `nls_length_semantics` 参数的值仅影响新创建的列,对已经存在的列不会产生任何影响。

在语法解析文件 ora_gram.y中,存在如下代码来根据 `nls_length_semantics` 把原本的 `char/varchar/varchar2` 类型改成 `oracharchar` 或者 `oracharbyte` :

```c
CharacterWithLength: character '(' Iconst ')'
{
if (ORA_PARSER == compatible_db)
{
if (strcmp($1, "bpchar"))
{
if (nls_length_semantics == NLS_LENGTH_CHAR)
$1 = "oravarcharchar";
else
$1 = "oravarcharbyte";
}
else
{
if (nls_length_semantics == NLS_LENGTH_CHAR)
$1 = "oracharchar";
else
$1 = "oracharbyte";
}

$$ = OracleSystemTypeName($1);
$$->typmods = list_make1(makeIntConst($3, @3));
$$->location = @1;
}
else
{
...
}
}
;

```

IvorySQL 中数据类型 `oracharchar` 和 `oracharbyte` 的修饰符输入输出函数包括:
```c
oravarcharchartypmodout()
oravarcharbytetypmodout()
oracharbytetypmodout()
oracharchartypmodout()
```

上面这些函数调用C语言实现的函数 `anychar_typmodout()` ,后者根据 `nls_length_semantics` 的值来调整输出的内容是否包含 `byte/char` 的说明。

`nls_length_semantics` 另一个作用是限制表中的列长度:
根据上述代码在语法解析文件 ora_gram.y中,如果原本的 `varchar` 类型被转换成了 `oracharchar` 类型,则函数 `oravarcharchar()` 会被调用,而 `pg_mbcharcliplen()` 函数计算字符长度,而不是字节长度。

```c
Datum
oravarcharchar(PG_FUNCTION_ARGS)
{
VarChar *source = PG_GETARG_VARCHAR_PP(0);
int32 typmod = PG_GETARG_INT32(1);
bool isExplicit = PG_GETARG_BOOL(2);
int32 len,
maxlen;
size_t maxmblen;
char *s_data;

len = VARSIZE_ANY_EXHDR(source);
s_data = VARDATA_ANY(source);
maxlen = typmod - VARHDRSZ;

/* No work if typmod is invalid or supplied data fits it already */
if (maxlen < 0 || len <= maxlen)
PG_RETURN_VARCHAR_P(source);

maxmblen = pg_mbcharcliplen(s_data, len, maxlen);

...
}
```

=== GUC参数 `datetime_ignore_nls_mask`

这个参数被定义为一个int值,低四位分别表示是否在相应的日期时间格式上忽略NLS参数的影响,掩码定义如下:
```c
#define ORADATE_MASK 0x01
#define ORATIMESTAMP_MASK 0x02
#define ORATIMESTAMPTZ_MASK 0x04
#define ORATIMESTAMPLTZ_MASK 0x08
```

在源代码中,这个GUC参数被用于下面这些函数:
```c
oradate_in()
oratimestamp_in()
oratimestampltz_in()
oratimestamptz_in()
```

如果相应的掩码被设置,则调用原生PG的处理函数,否则调用兼容代码并忽略NLS格式。

=== GUC参数 `nls_date_format`/`nls_timestamp_format`/`nls_timestamp_tz_format`

这三个GUC参数,在函数 `ora_do_to_timestamp()` 中做为格式字符串,对输入的字符串进行格式检查与模式识别。

下面是其默认值,可以通过设置其值为"pg"使其失效。"pg"表示禁用NLS特定行为,恢复为PostgreSQL的默认行为。
```c
char *nls_date_format = "YYYY-MM-DD";
char *nls_timestamp_format = "YYYY-MM-DD HH24:MI:SS.FF6";
char *nls_timestamp_tz_format = "YYYY-MM-DD HH24:MI:SS.FF6 TZH:TZM";
```

=== GUC参数 `nls_currency`/`nls_iso_currency`/`nls_territory`

目前,`nls_territory` 和 `nls_iso_currency` 支持CHINA与AMERICA两个值。

默认值如下:

```c
char *nls_territory = "AMERICA";
char *nls_currency = "$";
char *nls_iso_currency = "AMERICA";
```

这三个参数将在oracle兼容函数 `to_number()` 中被使用。
[NOTE]
====
`to_number()` 函数尚未实现。
====
116 changes: 116 additions & 0 deletions CN/modules/ROOT/pages/master/7.17.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
:sectnums:
:sectnumlevels: 5

:imagesdir: ./_images

= NLS 参数

== 目的

NLS 是 National Language Support(国家语言支持)的缩写,指的是 Oracle 提供的本地化支持功能。

IvorySQL提供兼容Oracle的NLS参数功能。

== 功能说明

包含如下参数:

[cols="3,7"]
|====
|*参数名称*|*功能描述*
|ivorysql.datetime_ignore_nls_mask | 表示日期格式是否忽略NLS参数影响,默认为0。
|nls_length_semantics | 兼容Oracle的同名参数,表示char/varchar/varchar2的类型修饰符的大小单位是字节还是字符。
|nls_date_format | 表示默认的日期格式,可以通过show命令查看,默认为‘YYYY-MM-DD’。
|nls_timestamp_format | 兼容Oracle的同名参数,控制带时间的日期格式。
|nls_timestamp_tz_format | 兼容Oracle的同名参数,控制带时区的日期时间格式。
|nls_territory | 兼容Oracle的同名参数,指定数据库的默认区域。
|nls_iso_currency | 兼容Oracle的同名参数,指定的国家和区域制定唯一的货币符号。
|nls_currency | 兼容Oracle的同名参数,指定显示本地货币的符号,对应数字字符串格式中占位符L。
|====

=== NLS 日期掩码设置
示例:
```sql
ivorysql=# set ivorysql.datetime_ignore_nls_mask = 0;
SET
ivorysql=# select '2025-10-15 11:00:00.102030 CST'::oratimestamp ;
ERROR: datetime format picture ends before converting entire input string
LINE 1: select '2025-10-15 11:00:00.102030 CST'::oratimestamp ;
^
ivorysql=# set ivorysql.datetime_ignore_nls_mask = 2;
SET
ivorysql=# select '2025-10-15 11:00:00.102030 CST'::oratimestamp ;
oratimestamp
----------------------------
2025-10-15 11:00:00.102030
(1 row)
```

=== 禁用NLS日期/时间戳参数
示例:
```sql
ivorysql=# select '2025-10-15 11:00:00.102030 '::oratimestamp ;
oratimestamp
----------------------------
2025-10-15 11:00:00.102030
(1 row)

ivorysql=# set nls_timestamp_format="pg";
SET
ivorysql=# select '2025-10-15 11:00:00.102030 '::oratimestamp ;
ERROR: date format not recognized
LINE 1: select '2025-10-15 11:00:00.102030 '::oratimestamp ;
^
```
=== 设置nls_length_semantics
IvorySQL使用nls_length_semantics参数的值来决定长度类型,有byte和char两种值,默认为byte。
示例:
```sql
vorysql=# alter session set nls_length_semantics = char;
SET
ivorysql=# create table character_tb(char_c char(6), char_b varchar2(6 byte), char_v varchar(6));
CREATE TABLE
ivorysql=# insert into character_tb values('中文测试数据', '123456', '成功');
INSERT 0 1
ivorysql=# select * from character_tb ;
char_c | char_b | char_v
--------------+--------+--------
中文测试数据 | 123456 | 成功
(1 row)

ivorysql=# select length(char_b), length(char_c), length(char_v) from character_tb;
length | length | length
--------+--------+--------
6 | 6 | 2
(1 row)

```
=== 设置NLS货币符号
示例:
```sql
ivorysql=# show ivorysql.identifier_case_switch;
ivorysql.identifier_case_switch
---------------------------------
interchange
(1 row)

ivorysql=# set nls_currency to "CHINA";
SET
ivorysql=# show nls_currency;
nls_currency
--------------
CHINA
(1 row)

ivorysql=# set nls_currency to "China";
SET
ivorysql=# show nls_currency;
nls_currency
--------------
China
(1 row)
```
[NOTE]
====
nls_currency和nls_iso_currency的行为,受兼容oracle大小写特性 `identifier_case_switch` 影响
====
2 changes: 2 additions & 0 deletions EN/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*** xref:master/6.3.3.adoc[RowID]
*** xref:master/6.3.2.adoc[OUT Parameter]
*** xref:master/6.3.4.adoc[%Type & %Rowtype]
*** xref:master/6.3.5.adoc[NLS Parameters]
** xref:master/6.4.adoc[GB18030 Character Set]
* List of Oracle compatible features
** xref:master/7.1.adoc[1、Ivorysql frame design]
Expand All @@ -42,6 +43,7 @@
** xref:master/7.14.adoc[14、RowID Column]
** xref:master/7.15.adoc[15、OUT Parameter]
** xref:master/7.16.adoc[16、%Type & %Rowtype]
** xref:master/7.17.adoc[17、NLS Parameters]
* xref:master/8.adoc[Community contribution]
* xref:master/9.adoc[Tool Reference]
* xref:master/10.adoc[FAQ]
Loading