ecpg-variables.md 24.9 KB
Newer Older
李少辉-开发者's avatar
李少辉-开发者 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
## 36.4.使用主机变量

[36.4.1. 概述](ecpg-variables.html#ECPG-VARIABLES-OVERVIEW)

[36.4.2. 申报章节](ecpg-variables.html#ECPG-DECLARE-SECTIONS)

[36.4.3. 检索查询结果](ecpg-variables.html#ECPG-RETRIEVING)

[36.4.4. 类型映射](ecpg-variables.html#ECPG-VARIABLES-TYPE-MAPPING)

[36.4.5. 处理非原始SQL数据类型](ecpg-variables.html#ECPG-VARIABLES-NONPRIMITIVE-SQL)

[36.4.6. 指标](ecpg-variables.html#ECPG-INDICATORS)

在里面[第36.3节](ecpg-commands.html)您了解了如何从嵌入式SQL程序执行SQL语句。其中一些语句只使用固定值,没有提供将用户提供的值插入语句或让程序处理查询返回的值的方法。这些类型的语句在实际应用中并不真正有用。本节详细介绍了如何使用名为*宿主变量*在嵌入式SQL程序中,我们认为SQL语句是*客人*在C程序代码中*主语*因此,C程序的变量被称为*宿主变量*.

PostgreSQL后端和ECPG应用程序之间交换值的另一种方法是使用SQL描述符,如中所述[第36.7节](ecpg-descriptors.html).

### 36.4.1.概述

在嵌入式SQL中,在C程序和SQL语句之间传递数据特别简单。与让程序将数据粘贴到语句中(这会带来各种复杂情况,例如正确引用值)不同,您只需将C变量的名称写入SQL语句中,并以冒号作为前缀。例如:

```
EXEC SQL INSERT INTO sometable VALUES (:v1, 'foo', :v2);
```

此语句引用两个名为`v1``v2`还使用常规的SQL字符串文字,以说明您不受限制地使用一种或另一种数据。

这种在SQL语句中插入C变量的方式适用于SQL语句中需要值表达式的任何地方。

### 36.4.2.申报章节

要将数据从程序传递到数据库,例如作为查询中的参数,或将数据从数据库传递回程序,需要在特别标记的部分中声明用于包含此数据的C变量,以便嵌入式SQL预处理器知道它们。

本节从以下内容开始:

```
EXEC SQL BEGIN DECLARE SECTION;
```

最后是:

```
EXEC SQL END DECLARE SECTION;
```

在这些行之间,必须有正常的C变量声明,例如:

```
int   x = 4;
char  foo[16], bar[16];
```

如您所见,您可以选择为变量指定初始值。变量的作用域由其声明部分在程序中的位置决定。您还可以使用以下语法声明变量,这些语法隐式创建一个declare节:

```
EXEC SQL int i = 4;
```

一个程序中可以有任意多个declare节。

声明也会作为普通的C变量回显到输出文件中,因此无需再次声明它们。不打算在SQL命令中使用的变量通常可以在这些特殊部分之外声明。

结构或联合的定义也必须列在`声明`部分否则,预处理器无法处理这些类型,因为它不知道定义。

### 36.4.3.检索查询结果

现在,您应该能够将程序生成的数据传递到SQL命令中。但是如何检索查询结果呢?为此,嵌入式SQL提供了常用命令的特殊变体`选择``取来`.这些命令有一个特殊的`进入`子句,指定检索到的值要存储在哪些主机变量中。`选择`用于只返回单行的查询,以及`取来`用于使用光标返回多行的查询。

下面是一个例子:

```
/*
 * assume this table:
 * CREATE TABLE test1 (a int, b varchar(50));
 */

EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL SELECT a, b INTO :v1, :v2 FROM test;
```

所以`进入`子句出现在选择列表和`从…起`条款选择列表中的元素数以及之后的列表`进入`(也称为目标列表)必须相等。

下面是一个使用命令的示例`取来`:

```
EXEC SQL BEGIN DECLARE SECTION;
int v1;
VARCHAR v2;
EXEC SQL END DECLARE SECTION;

 ...

EXEC SQL DECLARE foo CURSOR FOR SELECT a, b FROM test;

 ...

do
{
    ...
    EXEC SQL FETCH NEXT FROM foo INTO :v1, :v2;
    ...
} while (...);
```

这是`进入`子句出现在所有普通子句之后。

### 36.4.4.类型映射

当ECPG应用程序在PostgreSQL server和C应用程序之间交换值时,例如从服务器检索查询结果或使用输入参数执行SQL语句时,这些值需要在PostgreSQL数据类型和宿主语言变量类型(具体来说是C语言数据类型)之间转换。ECPG的一个要点是,在大多数情况下,它会自动处理这个问题。

在这方面,有两种数据类型:一些简单的PostgreSQL数据类型,例如`整数``文本`,可由应用程序直接读写。其他PostgreSQL数据类型,例如`时间戳``数字的`只能通过特殊的图书馆功能访问;看见[第36.4.4.2节](ecpg-variables.html#ECPG-SPECIAL-TYPES).

[表36.1](ecpg-variables.html#ECPG-DATATYPE-HOSTVARS-TABLE)显示哪些PostgreSQL数据类型对应于哪些C数据类型。当您希望发送或接收给定PostgreSQL数据类型的值时,应该在declare部分声明相应C数据类型的C变量。

李少辉-开发者's avatar
build  
李少辉-开发者 已提交
122
**表36.1.PostgreSQL数据类型和C变量类型之间的映射**
李少辉-开发者's avatar
李少辉-开发者 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650

| PostgreSQL数据类型 | 主机变量类型 |
| -------------- | ------ |
| `短整型` | `短的` |
| `整数` | `智力` |
| `比基特` | `双长整型` |
| `十进制的` | `十进制的`[<sup class="footnote" id="ECPG-DATATYPE-TABLE-FN">\[a\]</sup>](#ftn.ECPG-DATATYPE-TABLE-FN) |
| `数字的` | `数字的`[<sup class="footnoteref">\[a\]</sup>](ecpg-variables.html#ftn.ECPG-DATATYPE-TABLE-FN) |
| `真实的` | `浮动` |
| `双精度` | `双重的` |
| `smallserial` | `短的` |
| `电视连续剧` | `智力` |
| `大系列` | `双长整型` |
| `老年人` | `无符号整型` |
| `性格(*`n`*)`, `瓦尔查尔(*`n`*)`, `文本` | `炭[*`n`*+1]`, `瓦尔查尔[*`n`*+1]` |
| `名称` | `char[NAMEDATALEN]` |
| `时间戳` | `时间戳`[<sup class="footnoteref">\[a\]</sup>](ecpg-variables.html#ftn.ECPG-DATATYPE-TABLE-FN) |
| `间隔` | `间隔`[<sup class="footnoteref">\[a\]</sup>](ecpg-variables.html#ftn.ECPG-DATATYPE-TABLE-FN) |
| `日期` | `日期`[<sup class="footnoteref">\[a\]</sup>](ecpg-variables.html#ftn.ECPG-DATATYPE-TABLE-FN) |
| `布尔值` | `布尔`[<sup class="footnote" id="id-1.7.5.10.7.5.2.2.17.2.2">\[b\]</sup>](#ftn.id-1.7.5.10.7.5.2.2.17.2.2) |
| `二进制数据` | `炭*`, `拜茶[*`n`*]` |
| [<sup class="para">\[a\] </sup>](#ECPG-DATATYPE-TABLE-FN)这种类型只能通过特殊的库函数访问;看见[第36.4.4.2节](ecpg-variables.html#ECPG-SPECIAL-TYPES).<br/><br/>[<sup class="para">\[b\] </sup>](#id-1.7.5.10.7.5.2.2.17.2.2)声明于`ecpglib。H`如果不是本地人 |  |

#### 36.4.4.1.处理字符串

处理SQL字符串数据类型,例如`瓦尔查尔``文本`,有两种可能的方法来声明主机变量。

一种方法是使用`char[]`,一系列`烧焦`,这是C语言中处理字符数据最常用的方法。

```
EXEC SQL BEGIN DECLARE SECTION;
    char str[50];
EXEC SQL END DECLARE SECTION;
```

注意,你必须自己注意长度。如果将此主机变量用作返回超过49个字符的字符串的查询的目标变量,则会发生缓冲区溢出。

另一种方法是使用`瓦尔查尔`类型,这是ECPG提供的特殊类型。类型数组上的定义`瓦尔查尔`被转换为命名的`结构`对于每个变量。声明如下:

```
VARCHAR var[180];
```

转换为:

```
struct varchar_var { int len; char arr[180]; } var;
```

成员`啊`承载包含终止零字节的字符串。因此,将字符串存储在`瓦尔查尔`主机变量,主机变量的声明长度必须包括零字节终止符。成员`伦恩`保存存储在中的字符串的长度`啊`没有终止零字节。当主机变量用作查询的输入时,如果`斯特伦(arr)``伦恩`如果不同,则使用较短的。

`瓦尔查尔`可以用大写或小写书写,但不能用混合大写。

`烧焦``瓦尔查尔`主机变量还可以保存其他SQL类型的值,这些值将以字符串形式存储。

#### 36.4.4.2.访问特殊数据类型

ECPG包含一些特殊类型,可以帮助您轻松地与PostgreSQL server中的一些特殊数据类型进行交互。特别是,它已经实施了对`数字的`, `十进制的`, `日期`, `时间戳``间隔`类型。这些数据类型无法有效地映射到原始主机变量类型(例如`智力`, `双长整型``char[]`),因为它们有一个复杂的内部结构。应用程序通过在特殊类型中声明主机变量并使用pgtypes库中的函数访问它们来处理这些类型。pgtypes库,在中详细介绍[第36.6节](ecpg-pgtypes.html)包含处理这些类型的基本函数,例如,不需要仅为向时间戳添加间隔而向SQL server发送查询。

以下小节描述了这些特殊的数据类型。有关pgtypes库函数的更多详细信息,请参阅[第36.6节](ecpg-pgtypes.html).

##### 36.4.4.2.1.时间戳,日期

这里有一个处理模式`时间戳`ECPG主机应用程序中的变量。

首先,程序必须包含`时间戳`类型:

```
#include <pgtypes_timestamp.h>
```

接下来,将一个主机变量声明为type`时间戳`在声明部分:

```
EXEC SQL BEGIN DECLARE SECTION;
timestamp ts;
EXEC SQL END DECLARE SECTION;
```

将值读入宿主变量后,使用pgtypes库函数对其进行处理。在下面的示例中`时间戳`将值转换为文本(ASCII)形式,并使用`PGTYPEStimestamp_to_asc()`功能:

```
EXEC SQL SELECT now()::timestamp INTO :ts;

printf("ts = %s\n", PGTYPEStimestamp_to_asc(ts));
```

本例将显示如下结果:

```
ts = 2010-06-27 18:03:56.949343
```

此外,日期类型也可以用同样的方式处理。该计划必须包括`PGU日期。H`,将主机变量声明为日期类型,并使用`PGTYPESdate_to_asc()`作用有关pgtypes库函数的更多详细信息,请参阅[第36.6节](ecpg-pgtypes.html).

##### 36.4.4.2.2.间歇

处理`间隔`类型也类似于`时间戳``日期`类型。然而,需要为一个应用程序分配内存`间隔`显式输入值。换句话说,变量的内存空间必须在堆内存中分配,而不是在堆栈内存中分配。

以下是一个示例程序:

```
#include <stdio.h>
#include <stdlib.h>
#include <pgtypes_interval.h>

int
main(void)
{
EXEC SQL BEGIN DECLARE SECTION;
    interval *in;
EXEC SQL END DECLARE SECTION;

    EXEC SQL CONNECT TO testdb;
    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;

    in = PGTYPESinterval_new();
    EXEC SQL SELECT '1 min'::interval INTO :in;
    printf("interval = %s\n", PGTYPESinterval_to_asc(in));
    PGTYPESinterval_free(in);

    EXEC SQL COMMIT;
    EXEC SQL DISCONNECT ALL;
    return 0;
}
```

##### 36.4.4.2.3.数字的,十进制的

处理`数字的``十进制的`类型与`间隔`类型:它需要定义一个指针,在堆上分配一些内存空间,并使用pgtypes库函数访问变量。有关pgtypes库函数的更多详细信息,请参阅[第36.6节](ecpg-pgtypes.html).

没有专门为`十进制的`类型应用程序必须将其转换为`数字的`变量使用pgtypes库函数进行进一步处理。

下面是一个程序处理示例`数字的``十进制的`类型变量。

```
#include <stdio.h>
#include <stdlib.h>
#include <pgtypes_numeric.h>

EXEC SQL WHENEVER SQLERROR STOP;

int
main(void)
{
EXEC SQL BEGIN DECLARE SECTION;
    numeric *num;
    numeric *num2;
    decimal *dec;
EXEC SQL END DECLARE SECTION;

    EXEC SQL CONNECT TO testdb;
    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;

    num = PGTYPESnumeric_new();
    dec = PGTYPESdecimal_new();

    EXEC SQL SELECT 12.345::numeric(4,2), 23.456::decimal(4,2) INTO :num, :dec;

    printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 0));
    printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 1));
    printf("numeric = %s\n", PGTYPESnumeric_to_asc(num, 2));

    /* Convert decimal to numeric to show a decimal value. */
    num2 = PGTYPESnumeric_new();
    PGTYPESnumeric_from_decimal(dec, num2);

    printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 0));
    printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 1));
    printf("decimal = %s\n", PGTYPESnumeric_to_asc(num2, 2));

    PGTYPESnumeric_free(num2);
    PGTYPESdecimal_free(dec);
    PGTYPESnumeric_free(num);

    EXEC SQL COMMIT;
    EXEC SQL DISCONNECT ALL;
    return 0;
}
```

##### 36.4.4.2.4.拜茶

处理`二进制数据`类型与`瓦尔查尔`.类型数组上的定义`二进制数据`转换为每个变量的命名结构。声明如下:

```
bytea var[180];
```

转换为:

```
struct bytea_var { int len; char arr[180]; } var;
```

成员`啊`托管二进制格式的数据。它还可以处理`'\0'`作为数据的一部分`瓦尔查尔`.数据从/转换为十六进制格式,并由ecpglib发送/接收。

### 笔记

`二进制数据`变量只能在以下情况下使用:[二进制数据\_输出](runtime-config-client.html#GUC-BYTEA-OUTPUT)即将`十六进制`.

#### 36.4.4.3.具有非基本类型的宿主变量

作为宿主变量,还可以使用数组、typedef、结构和指针。

##### 36.4.4.3.1.阵列

数组作为主机变量有两种使用情况。第一种方法是将一些文本字符串存储在`char[]``瓦查尔[]`,如中所述[第36.4.4.1节](ecpg-variables.html#ECPG-CHAR).第二个用例是从查询结果中检索多行,而不使用光标。如果没有数组,要处理由多行组成的查询结果,需要使用游标和`取来`命令但是使用数组主机变量,可以一次接收多行。数组的长度必须定义为能够容纳所有行,否则可能会发生缓冲区溢出。

下面的示例扫描`pg_数据库`系统表,并显示可用数据库的所有OID和名称:

```
int
main(void)
{
EXEC SQL BEGIN DECLARE SECTION;
    int dbid[8];
    char dbname[8][16];
    int i;
EXEC SQL END DECLARE SECTION;

    memset(dbname, 0, sizeof(char)* 16 * 8);
    memset(dbid, 0, sizeof(int) * 8);

    EXEC SQL CONNECT TO testdb;
    EXEC SQL SELECT pg_catalog.set_config('search_path', '', false); EXEC SQL COMMIT;

    /* Retrieve multiple rows into arrays at once. */
    EXEC SQL SELECT oid,datname INTO :dbid, :dbname FROM pg_database;

    for (i = 0; i < 8; i++)
        printf("oid=%d, dbname=%s\n", dbid[i], dbname[i]);

    EXEC SQL COMMIT;
    EXEC SQL DISCONNECT ALL;
    return 0;
}
```

这个例子显示了以下结果。(具体数值取决于当地情况。)

```
oid=1, dbname=template1
oid=11510, dbname=template0
oid=11511, dbname=postgres
oid=313780, dbname=testdb
oid=0, dbname=
oid=0, dbname=
oid=0, dbname=
```

##### 36.4.4.3.2.结构

成员名称与查询结果的列名匹配的结构可用于一次检索多个列。该结构允许在单个主机变量中处理多个列值。

以下示例从中检索可用数据库的OID、名称和大小`pg_数据库`系统表和使用`pg_数据库_大小()`作用在本例中,结构变量`数据库信息`成员的名称与`选择`result用于检索一个结果行,而无需将多个主机变量放入`取来`陈述

```
EXEC SQL BEGIN DECLARE SECTION;
    typedef struct
    {
       int oid;
       char datname[65];
       long long int size;
    } dbinfo_t;

    dbinfo_t dbval;
EXEC SQL END DECLARE SECTION;

    memset(&dbval, 0, sizeof(dbinfo_t));

    EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database;
    EXEC SQL OPEN cur1;

    /* when end of result set reached, break out of while loop */
    EXEC SQL WHENEVER NOT FOUND DO BREAK;

    while (1)
    {
        /* Fetch multiple columns into one structure. */
        EXEC SQL FETCH FROM cur1 INTO :dbval;

        /* Print members of the structure. */
        printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, dbval.size);
    }

    EXEC SQL CLOSE cur1;
```

这个例子显示了以下结果。(具体数值取决于当地情况。)

```
oid=1, datname=template1, size=4324580
oid=11510, datname=template0, size=4243460
oid=11511, datname=postgres, size=4324580
oid=313780, datname=testdb, size=8183012
```

结构宿主变量“吸收”的列数与结构中的字段数一样多。可以将其他列指定给其他主机变量。例如,上面的程序也可以像这样进行重组`大小`结构之外的变量:

```
EXEC SQL BEGIN DECLARE SECTION;
    typedef struct
    {
       int oid;
       char datname[65];
    } dbinfo_t;

    dbinfo_t dbval;
    long long int size;
EXEC SQL END DECLARE SECTION;

    memset(&dbval, 0, sizeof(dbinfo_t));

    EXEC SQL DECLARE cur1 CURSOR FOR SELECT oid, datname, pg_database_size(oid) AS size FROM pg_database;
    EXEC SQL OPEN cur1;

    /* when end of result set reached, break out of while loop */
    EXEC SQL WHENEVER NOT FOUND DO BREAK;

    while (1)
    {
        /* Fetch multiple columns into one structure. */
        EXEC SQL FETCH FROM cur1 INTO :dbval, :size;

        /* Print members of the structure. */
        printf("oid=%d, datname=%s, size=%lld\n", dbval.oid, dbval.datname, size);
    }

    EXEC SQL CLOSE cur1;
```

##### 36.4.4.3.3.Typedefs

使用`类型定义`关键字将新类型映射到现有类型。

```
EXEC SQL BEGIN DECLARE SECTION;
    typedef char mychartype[40];
    typedef long serial_t;
EXEC SQL END DECLARE SECTION;
```

请注意,您还可以使用:

```
EXEC SQL TYPE serial_t IS long;
```

此声明不需要是declare部分的一部分。

##### 36.4.4.3.4.指针

可以声明指向最常见类型的指针。但是请注意,如果没有自动分配,就不能将指针用作查询的目标变量。看见[第36.7节](ecpg-descriptors.html)有关自动分配的更多信息。

```
EXEC SQL BEGIN DECLARE SECTION;
    int   *intp;
    char **charp;
EXEC SQL END DECLARE SECTION;
```

### 36.4.5.处理非原始SQL数据类型

本节包含有关如何在ECPG应用程序中处理非标量和用户定义的SQL级数据类型的信息。请注意,这与前一节中描述的处理非基本类型的主机变量不同。

#### 36.4.5.1.阵列

ECPG不直接支持多维SQL级别数组。一维SQL级别的数组可以映射到C数组主机变量,反之亦然。但是,在创建语句时,ecpg不知道列的类型,因此无法检查C数组是否输入到相应的SQL级别数组中。在处理SQL语句的输出时,ecpg拥有必要的信息,因此会检查两者是否都是数组。

如果查询访问*元素*这样就避免了在ECPG中使用数组。然后,应该使用类型可以映射到元素类型的宿主变量。例如,如果列类型是数组`整数`,类型的宿主变量`智力`可以使用。如果元素类型为`瓦尔查尔``文本`,类型的宿主变量`char[]``瓦查尔[]`可以使用。

下面是一个例子。假设下表:

```
CREATE TABLE t3 (
    ii integer[]
);

testdb=> SELECT * FROM t3;
     ii
#### 36.4.5.2. Composite Types

 Composite types are not directly supported in ECPG, but an easy workaround is possible. The available workarounds are similar to the ones described for arrays above: Either access each attribute separately or use the external string representation.

 For the following examples, assume the following type and table:
```

创建类型comp_t AS(intval integer,textval varchar(32));创建表t4(compval comp_t);插入t4值((256,‘PostgreSQL’));

```
 The most obvious solution is to access each attribute separately. The following program retrieves data from the example table by selecting each attribute of the type `comp_t` separately:
```

EXEC SQL开始声明部分;int intval;瓦查尔[33]; EXEC SQL END DECLARE部分;

/*将复合类型列的每个元素都放入选择列表中。*/EXEC SQL为SELECT(compval)声明cur1游标。intval(compval)。t4的textval;EXEC SQL OPEN cur1;

无论何时未找到EXEC SQL,都不要中断;

而(1){/*将复合类型列的每个元素提取到主机变量中。*/EXEC SQL从cur1获取到:intval,:textval;

```
printf("intval=%d, textval=%s\n", intval, textval.arr);
```

}

EXEC SQL CLOSE cur1;

```
 To enhance this example, the host variables to store values in the `FETCH` command can be gathered into one structure. For more details about the host variable in the structure form, see [Section 36.4.4.3.2](ecpg-variables.html#ECPG-VARIABLES-STRUCT). To switch to the structure, the example can be modified as below. The two host variables, `intval` and `textval`, become members of the `comp_t` structure, and the structure is specified on the `FETCH` command.
```

EXEC SQL开始声明部分;typedef结构{int intval;varchar textval[33]; } 比较;

comp_t compval;EXEC SQL END DECLARE部分;

/*将复合类型列的每个元素都放入选择列表中。*/EXEC SQL为SELECT(compval)声明cur1游标。intval(compval)。t4的textval;EXEC SQL OPEN cur1;

无论何时未找到EXEC SQL,都不要中断;

而(1){/*将选择列表中的所有值放入一个结构中。*/EXEC SQL FETCH FROM cur1 INTO:compval;

```
printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr);
```

}

EXEC SQL CLOSE cur1;

```
 Although a structure is used in the `FETCH` command, the attribute names in the `SELECT` clause are specified one by one. This can be enhanced by using a `*` to ask for all attributes of the composite type value.
```

... EXEC SQL为SELECT(compval)声明cur1游标。\*从t4开始;EXEC SQL OPEN cur1;

无论何时未找到EXEC SQL,都不要中断;

而(1){/*将选择列表中的所有值放入一个结构中。*/EXEC SQL FETCH FROM cur1 INTO:compval;

```
printf("intval=%d, textval=%s\n", compval.intval, compval.textval.arr);
```

} ...

```
 This way, composite types can be mapped into structures almost seamlessly, even though ECPG does not understand the composite type itself.

 Finally, it is also possible to store composite type values in their external string representation in host variables of type `char[]` or `VARCHAR[]`. But that way, it is not easily possible to access the fields of the value from the host program.

#### 36.4.5.3. User-Defined Base Types

 New user-defined base types are not directly supported by ECPG. You can use the external string representation and host variables of type `char[]` or `VARCHAR[]`, and this solution is indeed appropriate and sufficient for many types.

 Here is an example using the data type `complex` from the example in [Section 38.13](xtypes.html). The external string representation of that type is `(%f,%f)`, which is defined in the functions `complex_in()` and `complex_out()` functions in [Section 38.13](xtypes.html). The following example inserts the complex type values `(1,1)` and `(3,3)` into the columns `a` and `b`, and select them from the table after that.
```

EXEC SQL开始声明部分;瓦查尔a[64];     瓦查尔b[64]; EXEC SQL END DECLARE部分;

```
EXEC SQL INSERT INTO test_complex VALUES ('(1,1)', '(3,3)');

EXEC SQL DECLARE cur1 CURSOR FOR SELECT a, b FROM test_complex;
EXEC SQL OPEN cur1;

EXEC SQL WHENEVER NOT FOUND DO BREAK;

while (1)
{
    EXEC SQL FETCH FROM cur1 INTO :a, :b;
    printf("a=%s, b=%s\n", a.arr, b.arr);
}

EXEC SQL CLOSE cur1;
```

```
 This example shows following result:
```

a=(1,1),b=(3,3)

```
 Another workaround is avoiding the direct use of the user-defined types in ECPG and instead create a function or cast that converts between the user-defined type and a primitive type that ECPG can handle. Note, however, that type casts, especially implicit ones, should be introduced into the type system very carefully.

 For example,
```

CREATE函数CREATE_complex(r double,i double)将复杂语言SQL作为$$SELECT$1返回不可变*复杂的“(1,0')”+$2*复合物(0,1)$$;

```
 After this definition, the following
```

EXEC SQL开始声明部分;双a,b,c,d;EXEC SQL END DECLARE部分;

a=1;b=2;c=3;d=4;

EXEC SQL插入测试复杂值(创建复杂值(:a,:b),创建复杂值(:c,:d));

```
 has the same effect as
```

EXEC SQL插入测试\_复杂值(“(1,2)”,“(3,4)”;

```
### 36.4.6. Indicators

 The examples above do not handle null values. In fact, the retrieval examples will raise an error if they fetch a null value from the database. To be able to pass null values to the database or retrieve null values from the database, you need to append a second host variable specification to each host variable that contains data. This second host variable is called the *indicator* and contains a flag that tells whether the datum is null, in which case the value of the real host variable is ignored. Here is an example that handles the retrieval of null values correctly:
```

EXEC SQL开始声明部分;瓦尔查尔·瓦尔;国际货币基金组织;EXEC SQL END DECLARE部分:

 ...

EXEC SQL选择b到:val:val_ind FROM test1;

```
 The indicator variable `val_ind` will be zero if the value was not null, and it will be negative if the value was null.

 The indicator has another function: if the indicator value is positive, it means that the value is not null, but it was truncated when it was stored in the host variable.

 If the argument `-r no_indicator` is passed to the preprocessor `ecpg`, it works in “no-indicator” mode. In no-indicator mode, if no indicator variable is specified, null values are signaled (on input and output) for character string types as empty string and for integer types as the lowest possible value for type (for example, `INT_MIN` for `int`).
```