diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000..682c1b81 --- /dev/null +++ b/.coderabbit.yaml @@ -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"] diff --git a/CN/modules/ROOT/nav.adoc b/CN/modules/ROOT/nav.adoc index f34da970..e3c8c3c6 100644 --- a/CN/modules/ROOT/nav.adoc +++ b/CN/modules/ROOT/nav.adoc @@ -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、框架设计] @@ -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语法快速参考] diff --git a/CN/modules/ROOT/pages/master/6.3.5.adoc b/CN/modules/ROOT/pages/master/6.3.5.adoc new file mode 100644 index 00000000..9c3bd2a2 --- /dev/null +++ b/CN/modules/ROOT/pages/master/6.3.5.adoc @@ -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()` 函数尚未实现。 +==== \ No newline at end of file diff --git a/CN/modules/ROOT/pages/master/7.17.adoc b/CN/modules/ROOT/pages/master/7.17.adoc new file mode 100644 index 00000000..09e3e297 --- /dev/null +++ b/CN/modules/ROOT/pages/master/7.17.adoc @@ -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` 影响 +==== \ No newline at end of file diff --git a/EN/modules/ROOT/nav.adoc b/EN/modules/ROOT/nav.adoc index ddeaaacb..3d42ed18 100644 --- a/EN/modules/ROOT/nav.adoc +++ b/EN/modules/ROOT/nav.adoc @@ -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] @@ -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] diff --git a/EN/modules/ROOT/pages/master/6.3.5.adoc b/EN/modules/ROOT/pages/master/6.3.5.adoc new file mode 100644 index 00000000..868fae69 --- /dev/null +++ b/EN/modules/ROOT/pages/master/6.3.5.adoc @@ -0,0 +1,151 @@ + +:sectnums: +:sectnumlevels: 5 + + += **Functional Overview** + +IvorySQL provides Oracle-compatible NLS (National Language Support) parameter functionality, including the following parameters: + +[cols="3,7"] +|==== +|Parameter Name|Description +| ivorysql.datetime_ignore_nls_mask | Indicates whether the date format ignores the influence of NLS parameters. Default is 0. +| nls_length_semantics | Oracle-compatible parameter that specifies whether the size unit for CHAR, VARCHAR, and VARCHAR2 type modifiers is bytes or characters. +| nls_date_format | Specifies the default date format, which can be viewed using the SHOW command. Default is 'YYYY-MM-DD'. +| nls_timestamp_format | Oracle-compatible parameter that controls the format of timestamps. +| nls_timestamp_tz_format | Oracle-compatible parameter that controls the format of timestamps with time zones. +| nls_territory | Oracle-compatible parameter that specifies the default region for the database. +| nls_iso_currency | Oracle-compatible parameter that assigns a unique currency symbol to a specified country or region. +| nls_currency | Oracle-compatible parameter that specifies the symbol for the local currency, corresponding to the placeholder L in numeric string formats. +|==== +== Implementation Principles + +=== Parameter `nls_length_semantics` + +In IvorySQL, data types have an attribute modifier called typmod, which provides additional information about the type. For example, in `VARCHAR(n)`, n is the type modifier. When creating or modifying table columns, you can specify the length type, such as: +```c + ivorysql=# create table t1(name varchar2(2 byte)); +``` + +For columns of type `CHAR`, `VARCHAR`, or `VARCHAR2`, if the length type is not explicitly specified, IvorySQL uses the value of the `nls_length_semantics` parameter to determine the length type. The possible values are `BYTE` and `CHAR`, with `BYTE` being the default. + +Note that the `nls_length_semantics` parameter only affects newly created columns and has no impact on existing columns. + +In the syntax parsing file ora_gram.y, the following code converts the original `CHAR`, `VARCHAR`, or `VARCHAR2` types to oracharchar or oracharbyte based on `nls_length_semantics`. +```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 + { + ... + } + } + ; + +``` + +The input/output functions for the oracharchar and oracharbyte data types in IvorySQL include: +```c +oravarcharchartypmodout() +oravarcharbytetypmodout() +oracharbytetypmodout() +oracharchartypmodout() +``` + +These functions call the C-language function `anychar_typmodout()`, which adjusts the output to include `BYTE` or `CHAR` based on the value of `nls_length_semantics`. + +Another role of `nls_length_semantics` is to limit column lengths in tables. If the original `VARCHAR` type is converted to `oracharchar`, the function `oravarcharchar()` is called, and `pg_mbcharcliplen()` calculates the character length instead of the byte length. +```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 Parameter `datetime_ignore_nls_mask` + +This parameter is defined as an int value, where the lower four bits indicate whether to ignore NLS parameter influence on the corresponding date-time formats. The mask definitions are: +```c +#define ORADATE_MASK 0x01 +#define ORATIMESTAMP_MASK 0x02 +#define ORATIMESTAMPTZ_MASK 0x04 +#define ORATIMESTAMPLTZ_MASK 0x08 +``` + +In the source code, this GUC parameter is used in the following functions: + +```c +oradate_in() +oratimestamp_in() +oratimestampltz_in() +oratimestamptz_in() +``` + +If the corresponding mask is set, the native PostgreSQL processing function is called; otherwise, the compatibility code is invoked, and NLS formats are ignored. + +=== GUC Parameters `nls_date_format`/`nls_timestamp_format`/`nls_timestamp_tz_format` + +These three GUC parameters serve as format strings in the function `ora_do_to_timestamp()` for checking and parsing input strings. Their default values are: +```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"; +``` +Setting these values to "pg" disables NLS-specific behavior, reverting to PostgreSQL's default behavior. + +=== GUC Parameters `nls_currency`/`nls_iso_currency`/`nls_territory` + +Currently, `nls_territory` and `nls_iso_currency` support the values CHINA and AMERICA. + +The default values are: +```c +char *nls_territory = "AMERICA"; +char *nls_currency = "$"; +char *nls_iso_currency = "AMERICA"; +``` +These parameters will be used in the Oracle-compatible function `to_number()`. +[NOTE] +==== +The `to_number()` function has not yet been implemented. +==== \ No newline at end of file diff --git a/EN/modules/ROOT/pages/master/7.17.adoc b/EN/modules/ROOT/pages/master/7.17.adoc new file mode 100644 index 00000000..56cf5131 --- /dev/null +++ b/EN/modules/ROOT/pages/master/7.17.adoc @@ -0,0 +1,121 @@ +:sectnums: +:sectnumlevels: 5 + +:imagesdir: ./_images + += NLS Parameters + +== Purpose + +National Language Support(NLS), which refers to the localization support functionality provided by Oracle. +IvorySQL offers Oracle-compatible NLS parameter functionality. + +== Description + +The following parameters are included: + +[cols="3,7"] +|==== +|Parameter Name|Description +| ivorysql.datetime_ignore_nls_mask | Indicates whether the date format ignores the influence of NLS parameters. Default is 0 . +| nls_length_semantics | Oracle-compatible parameter that specifies whether the size unit for CHAR , VARCHAR , and VARCHAR2 type modifiers is bytes or characters. +| nls_date_format | Specifies the default date format, which can be viewed using the SHOW command. Default is 'YYYY-MM-DD' . +| nls_timestamp_format | Oracle-compatible parameter that controls the format of timestamps. +| nls_timestamp_tz_format | Oracle-compatible parameter that controls the format of timestamps with time zones. +| nls_territory | Oracle-compatible parameter that specifies the default region for the database. +| nls_iso_currency | Oracle-compatible parameter that assigns a unique currency symbol to a specified country or region. +| nls_currency | Oracle-compatible parameter that specifies the symbol for the local currency, corresponding to the placeholder L in numeric string formats. +|==== + +=== NLS Date Mask Settings + +Example: +```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) +``` + +=== Disabling NLS Date or Timestamp Format + +Example: +```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 ; + ^ +``` +=== Parameter `nls_length_semantics` Settings + +IvorySQL uses the value of the `nls_length_semantics` parameter to determine the length type, which can be either BYTE or CHAR (default is BYTE )。 + +Example: +```sql +ivorysql=# 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=# +ivorysql=# insert into character_tb values('abcdef', '123456', 'OK'); +INSERT 0 1 +ivorysql=# select * from character_tb ; + char_c | char_b | char_v +--------+--------+-------- + abcdef | 123456 | OK +(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 Currency Symbols Settings + +Example: +```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] +==== +The behavior of `nls_currency` and `nls_iso_currency` is impacted by the Oracle `ivorysql.identifier_case_switch` compatibility feature. +==== \ No newline at end of file