1 /*Copyright (c) Brian B.
3 This library is free software; you can redistribute it and/or
4 modify it under the terms of the GNU Lesser General Public
5 License as published by the Free Software Foundation; either
6 version 3 of the License, or (at your option) any later version.
7 See the file LICENSE included with this distribution for more
11 #include "libpgcli/pgconn.h"
13 #define PG_STACKSIZE 1024 * 8
14 #define PG_DEFAULT_PORT 5432
16 static uint32_t pg_addr
= 0;
17 static unsigned short pg_port
= 0;
19 //******************************************************************************
21 //******************************************************************************
23 __attribute ((constructor
))
24 static void pg_init () {
29 inet_aton("127.0.0.1", &addr
);
30 pg_addr
= addr
.s_addr
;
31 pg_port
= htons(PG_DEFAULT_PORT
);
35 __attribute__ ((destructor
))
36 static void _pg_done () {
40 static int _pg_atoport (const char *service
) {
41 struct servent
*servent
= getservbyname(service
, "tcp");
45 return servent
->s_port
;
46 port
= strtol(service
, &tail
, 0);
47 if ('\0' == *tail
|| port
< 1 || port
> USHRT_MAX
)
52 void pgconn_init (const char *pg_srv_addr
, const char *pg_srv_service
) {
55 pg_port
= _pg_atoport(pg_srv_service
);
57 inet_aton(pg_srv_addr
, &addr
);
58 pg_addr
= addr
.s_addr
;
61 //******************************************************************************
63 //******************************************************************************
65 //******************************************************************************
67 const strptr_t
pg_get (pgconn_t
*conn
, int row
, int col
) {
69 strptr_t
pg_get (pgconn_t
*conn
, int row
, int col
) {
71 strptr_t res
= { .ptr
= NULL
, .len
= 0 };
72 assert(CHECK_BOUNDS(conn
, row
, col
));
73 res
.ptr
= (char*)PG_DATA(conn
, row
, col
);
74 res
.len
= PG_FLDLEN(conn
, row
, col
);
78 cstr_t
*pg_getstr (pgconn_t
*conn
, int row
, int col
) {
79 strptr_t s
= pg_get(conn
, row
, col
);
82 res
= mkcstr(s
.ptr
, s
.len
);
86 uint32_t pg_getx32 (pgconn_t
*conn
, int row
, int col
) {
87 assert(CHECK_BOUNDS(conn
, row
, col
));
88 pg_bit32_t
*res
= (pg_bit32_t
*)PG_DATA(conn
, row
, col
);
89 return be32toh(res
->bit
);
92 pg_intv_t
pg_getintv (pgconn_t
*conn
, int row
, int col
) {
93 pg_intv_t
*i
, res
= { .time
= 0, .day
= 0, .month
= 0 };
94 assert(CHECK_BOUNDS(conn
, row
, col
));
95 i
= (pg_intv_t
*)PG_DATA(conn
, row
, col
),
96 res
.time
= be64toh(i
->time
);
97 res
.day
= be32toh(i
->day
);
98 res
.month
= be32toh(i
->month
);
106 int32_t pg_getinet4 (pgconn_t
*conn
, int row
, int col
) {
107 uint8_t *v
= PG_DATA(conn
, row
, col
);
108 if (2 == v
[0] && 32 == v
[1] && 4 == *(unsigned short*)(v
+2)) {
119 //******************************************************************************
121 //******************************************************************************
123 const int pg_error (pgconn_t
*conn
, pgerror_t
*e
) {
125 int pg_error (pgconn_t
*conn
, pgerror_t
*e
) {
130 if ((conn
->intr_error
& EPG_PORT
)) {
131 e
->code
= STR(EPG_PORT
);
133 e
->msg
= "Illegal port";
136 if ((conn
->intr_error
& EPG_ERRNO
)) {
137 e
->code
= STR(EPG_ERRNO
);
139 e
->msg
= strerror(conn
->intr_error
& EPG_HOST
);
142 if ((conn
->intr_error
& EPG_PROTO
)) {
143 e
->code
= STR(EPG_PROTO
);
145 e
->msg
= "Protocol error";
148 switch ((conn
->intr_error
& EPG_HOST
)) {
150 e
->code
= STR(HOST_NOT_FOUND
);
152 e
->msg
= "The specified host is unknown.";
155 e
->code
= STR(NO_DATA
);
157 e
->msg
= "The requested name is valid but does not have an IP address. Another type of request to the name server for this domain may return an answer.";
160 e
->code
= STR(NO_RECOVERY
);
162 e
->msg
= "A nonrecoverable name server error occurred.";
165 e
->code
= STR(TRY_AGAIN
);
167 e
->msg
= "A temporary error occurred on an authoritative name server. Try again later.";
171 e
->code
= conn
->error
->code
;
172 e
->text
= conn
->error
->text
;
173 e
->msg
= conn
->error
->message
;
174 e
->detail
= conn
->error
->detail
;
177 e
->code
= STR(ERRCODE_SUCCESSFUL_COMPLETION
);
183 static void _pg_startup (pgconn_t
*conn
, const char *conn_info
, conninfo_t
*cinfo
) {
184 pgmsg_t
*msg
= pgmsg_create_startup_params(conn_info
);
185 if (-1 == pgmsg_send(conn
->fd
, msg
))
189 while (!conn
->ready
&& !conn
->error
&& 0 == pgmsg_recv(conn
->fd
, &msg
)) {
190 pgmsg_resp_t
*resp
= NULL
;
191 switch (msg
->body
.type
) {
193 pgmsg_list_add(&conn
->msgs
, msg
);
194 resp
= pgmsg_parse(msg
);
195 conn
->ready
= &resp
->msg_ready
;
199 pgmsg_list_add(&conn
->msgs
, msg
);
200 resp
= pgmsg_parse(msg
);
201 conn
->error
= &resp
->msg_error
;
205 resp
= pgmsg_parse(msg
);
206 if (0 == strcmp(resp
->msg_param_status
.name
, "integer_datetimes"))
207 conn
->is_int_datetimes
= 0 == strcmp(resp
->msg_param_status
.value
, "on") ? 1 : 0;
209 if (0 == strcmp(resp
->msg_param_status
.name
, "is_superuser"))
210 conn
->is_superuser
= 0 == strcmp(resp
->msg_param_status
.value
, "on") ? 1 : 0;
212 if (0 == strcmp(resp
->msg_param_status
.name
, "server_version")) {
213 char *ver
= strdup(resp
->msg_param_status
.value
),
214 *p
= strchr(ver
, '.');
217 conn
->minor_ver
= strtol(p
, NULL
, 0);
219 conn
->major_ver
= strtol(ver
, NULL
, 0);
222 if (0 == strcmp(resp
->msg_param_status
.name
, "client_encoding"))
223 conn
->client_encoding
= strdup(resp
->msg_param_status
.value
);
225 if (0 == strcmp(resp
->msg_param_status
.name
, "server_encoding"))
226 conn
->server_encoding
= strdup(resp
->msg_param_status
.value
);
228 if (0 == strcmp(resp
->msg_param_status
.name
, "session_authorization"))
229 conn
->session_auth
= strdup(resp
->msg_param_status
.value
);
231 if (0 == strcmp(resp
->msg_param_status
.name
, "DateStyle"))
232 conn
->date_style
= strdup(resp
->msg_param_status
.value
);
234 if (0 == strcmp(resp
->msg_param_status
.name
, "TimeZone"))
235 conn
->timezone
= strdup(resp
->msg_param_status
.value
);
236 if (resp
) free(resp
);
242 resp
= pgmsg_parse(msg
);
244 conn
->authok
= resp
->msg_auth
.success
;
245 switch (conn
->authok
) {
248 memcpy(cinfo
->salt
, resp
->msg_auth
.kind
.md5_auth
, sizeof(uint8_t) * 4);
251 msg
= pgmsg_create_pass(conn
->authok
, cinfo
->salt
, sizeof(uint8_t) * 4, cinfo
->user
, cinfo
->pass
);
252 pgmsg_send(conn
->fd
, msg
);
257 msg
= pgmsg_create_sasl_init(cinfo
);
258 pgmsg_send(conn
->fd
, msg
);
262 msg
= pgmsg_create_sasl_fin(resp
, cinfo
);
263 pgmsg_send(conn
->fd
, msg
);
269 conn
->intr_error
= EPG_PROTO
;
287 static void *_parse_param (void *data
, const char *begin
, const char *end
, unsigned int *flags
) {
288 conninfo_t
*cinfo
= (conninfo_t
*)data
;
289 const char *p
= begin
, *p1
;
290 while (p
< end
&& '=' != *p
) ++p
;
294 while (p1
> begin
&& isspace(*(p1
- 1))) --p1
;
298 while (p
< end
&& isspace(*p
)) ++p
;
301 if (0 == strncmp(begin
, "host", (uintptr_t)p1
- (uintptr_t)begin
)) {
302 char *addr
= strndup(p
, (uintptr_t)end
- (uintptr_t)p
);
303 struct in_addr in_addr
;
304 memset(&in_addr
, 0, sizeof in_addr
);
305 if (0 == atoaddr(addr
, (struct in_addr
*)&in_addr
))
306 cinfo
->in_addr
.sin_addr
= in_addr
;
309 if (0 == strncmp(begin
, "port", (uintptr_t)p1
- (uintptr_t)begin
)) {
310 char *port_s
= strndup(p
, (uintptr_t)end
- (uintptr_t)p
), *tail
;
311 int port
= strtol(port_s
, &tail
, 0);
312 if ('\0' == *tail
&& ERANGE
!= errno
)
313 cinfo
->in_addr
.sin_port
= htons(port
);
318 if (0 == strncmp(begin
, "user", (uintptr_t)p1
- (uintptr_t)begin
)) {
321 cinfo
->user
= strndup(p
, (uintptr_t)end
- (uintptr_t)p
);
323 if (0 == strncmp(begin
, "password", (uintptr_t)p1
- (uintptr_t)begin
)) {
326 cinfo
->pass
= strndup(p
, (uintptr_t)end
- (uintptr_t)p
);
331 static str_t
*_parse_url (pgconn_t
*conn
, const char *url
) {
332 char *str
= strdup(url
);
333 str_t
*conninfo
= stralloc(64, 64);
334 char *s
= strstr(str
, "://"), *e
, *p
,
349 if ((e
= strchr(s
, '/')))
351 if ((p
= strchr(s
, '@'))) {
355 if ((q
= strchr(s
, ':'))) {
362 if ((p
= strchr(s
, ':'))) {
367 strnadd(&conninfo
, CONST_STR_LEN("host="));
368 strnadd(&conninfo
, host
, strlen(host
));
371 if (conninfo
->len
> 0)
372 strnadd(&conninfo
, CONST_STR_LEN(" "));
373 strnadd(&conninfo
, CONST_STR_LEN("port="));
374 strnadd(&conninfo
, port
, strlen(port
));
377 if (conninfo
->len
> 0)
378 strnadd(&conninfo
, CONST_STR_LEN(" "));
379 strnadd(&conninfo
, CONST_STR_LEN("user="));
380 strnadd(&conninfo
, user
, strlen(user
));
383 if (conninfo
->len
> 0)
384 strnadd(&conninfo
, CONST_STR_LEN(" "));
385 strnadd(&conninfo
, CONST_STR_LEN("password="));
386 strnadd(&conninfo
, password
, strlen(password
));
393 if ((e
= strchr(s
, '?')))
396 if (conn
->dbname
) free(conn
->dbname
);
397 conn
->dbname
= strdup(dbname
);
398 if (conninfo
->len
> 0) {
399 if (conninfo
->len
> 0)
400 strnadd(&conninfo
, CONST_STR_LEN(" "));
401 strnadd(&conninfo
, CONST_STR_LEN("dbname="));
402 strnadd(&conninfo
, dbname
, strlen(dbname
));
409 str_t
*params
= strsplit(s
, strlen(s
), '&');
410 strptr_t entry
= { .ptr
= NULL
, .len
= 0 };
411 while (-1 != strnext(params
, &entry
)) {
412 if ((p
= strnchr(entry
.ptr
, '=', entry
.len
))) {
413 if (conninfo
->len
> 0)
414 strnadd(&conninfo
, CONST_STR_LEN(" "));
415 strnadd(&conninfo
, entry
.ptr
, entry
.len
);
423 pgconn_t
*pg_connect (const char *url
) {
425 conninfo_t cinfo
= { .in_addr
= {
426 .sin_family
= AF_INET
,
428 .sin_addr
.s_addr
= pg_addr
},
429 .user
= NULL
, .pass
= NULL
,
430 .srv_scram_msg
= NULL
, .fmsg_bare
= NULL
,
431 .fmsg_srv
= NULL
, .fmsg_wproof
= NULL
,
435 conn
= calloc(1, sizeof(pgconn_t
));
436 str_t
*conn_info
= _parse_url(conn
, url
);
437 parse_conninfo((void*)&cinfo
, conn_info
->ptr
, _parse_param
, &flags
);
438 if (0 != (conn
->intr_error
= flags
)) {
442 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
443 if (-1 == connect(fd
, (struct sockaddr
*)&cinfo
.in_addr
, sizeof cinfo
.in_addr
)) {
446 conn
->intr_error
= EPG_ERRNO
| errno
;
450 _pg_startup(conn
, conn_info
->ptr
, &cinfo
);
455 if (cinfo
.srv_scram_msg
)
456 free(cinfo
.srv_scram_msg
);
458 free(cinfo
.fmsg_bare
);
460 free(cinfo
.fmsg_srv
);
461 if (cinfo
.fmsg_wproof
)
462 free(cinfo
.fmsg_wproof
);
469 static void _pg_close (pgconn_t
*conn
) {
484 conn
->rowdesc
= NULL
;
486 if (conn
->complete
) {
487 free(conn
->complete
);
488 conn
->complete
= NULL
;
490 conn
->suspended
= NULL
;
491 pgmsg_datarow_list_clear(&conn
->row_list
);
492 pgmsg_list_clear(&conn
->msgs
);
496 void pg_close (pgconn_t
*conn
) {
500 void pg_disconnect (pgconn_t
*conn
) {
501 pgmsg_t
*msg
= pgmsg_create(PG_TERM
);
502 pgmsg_send(conn
->fd
, msg
);
505 if (conn
->date_style
)
506 free(conn
->date_style
);
507 if (conn
->client_encoding
)
508 free(conn
->client_encoding
);
509 if (conn
->server_encoding
)
510 free(conn
->server_encoding
);
511 if (conn
->session_auth
)
512 free(conn
->session_auth
);
514 free(conn
->timezone
);
519 pgmsg_datarow_list_clear(&conn
->row_list
);
520 pgmsg_list_clear(&conn
->msgs
);
526 static void _set_rows (pgconn_t
*conn
) {
528 pgmsg_datarow_list_t
*list
= &conn
->row_list
;
529 pgmsg_datarow_item_t
*item
= list
->head
;
531 conn
->rows
= malloc(conn
->row_list
.len
* sizeof(void*));
533 conn
->rows
[i
++] = item
->datarow
;
535 } while (item
!= list
->head
);
539 static void _wait_ready (pgconn_t
*conn
) {
542 while (!is_ready
&& 0 == pgmsg_recv(conn
->fd
, &msg
)) {
543 is_ready
= PG_READY
== msg
->body
.type
;
548 static int _simple_exec (pgconn_t
*conn
) {
551 while (0 == pgmsg_recv(conn
->fd
, &msg
)) {
553 switch (msg
->body
.type
) {
555 pgmsg_list_add(&conn
->msgs
, msg
);
556 resp
= pgmsg_parse(msg
);
557 conn
->ready
= &resp
->msg_ready
;
558 if (conn
->row_list
.len
> 0)
562 pgmsg_list_add(&conn
->msgs
, msg
);
563 resp
= pgmsg_parse(msg
);
564 conn
->error
= &resp
->msg_error
;
569 pgmsg_list_add(&conn
->msgs
, msg
);
570 resp
= pgmsg_parse(msg
);
571 conn
->rowdesc
= &resp
->msg_rowdesc
;
572 conn
->nflds
= conn
->rowdesc
->nflds
;
575 pgmsg_list_add(&conn
->msgs
, msg
);
576 resp
= pgmsg_parse(msg
);
577 conn
->complete
= &resp
->msg_complete
;
580 pgmsg_list_add(&conn
->msgs
, msg
);
581 resp
= pgmsg_parse(msg
);
582 pgmsg_datarow_list_add(&conn
->row_list
, &resp
->msg_datarow
);
589 pgmsg_list_add(&conn
->msgs
, msg
);
590 resp
= pgmsg_parse(msg
);
591 conn
->cols
= resp
->msg_copyin
.cols
;
592 conn
->fmt
= resp
->msg_copyin
.fmt
;
604 static int _exec (pgconn_t
*conn
) {
608 while (0 == pgmsg_recv(conn
->fd
, &msg
)) {
610 switch (msg
->body
.type
) {
612 pgmsg_list_add(&conn
->msgs
, msg
);
613 resp
= pgmsg_parse(msg
);
614 conn
->ready
= &resp
->msg_ready
;
615 if (conn
->row_list
.len
> 0)
618 case PG_PARSECOMPLETE
:
621 case PG_BINDCOMPLETE
:
625 pgmsg_list_add(&conn
->msgs
, msg
);
626 resp
= pgmsg_parse(msg
);
627 conn
->rowdesc
= &resp
->msg_rowdesc
;
628 conn
->nflds
= conn
->rowdesc
->nflds
;
632 if (0 == pgmsg_recv(conn
->fd
, &msg
)) {
633 switch (msg
->body
.type
) {
635 pgmsg_list_add(&conn
->msgs
, msg
);
636 resp
= pgmsg_parse(msg
);
637 conn
->error
= &resp
->msg_error
;
642 pgmsg_list_add(&conn
->msgs
, msg
);
643 resp
= pgmsg_parse(msg
);
644 conn
->rowdesc
= &resp
->msg_rowdesc
;
645 conn
->nflds
= conn
->rowdesc
->nflds
;
658 pgmsg_list_add(&conn
->msgs
, msg
);
659 resp
= pgmsg_parse(msg
);
660 conn
->error
= &resp
->msg_error
;
665 pgmsg_list_add(&conn
->msgs
, msg
);
666 resp
= pgmsg_parse(msg
);
667 conn
->complete
= &resp
->msg_complete
;
670 pgmsg_list_add(&conn
->msgs
, msg
);
671 resp
= pgmsg_parse(msg
);
672 pgmsg_datarow_list_add(&conn
->row_list
, &resp
->msg_datarow
);
678 case PG_PORTALSUSPENDED
:
679 pgmsg_list_add(&conn
->msgs
, msg
);
680 conn
->suspended
= msg
;
683 pgmsg_list_add(&conn
->msgs
, msg
);
684 conn
->emptyquery
= msg
;
695 int pg_execsql (pgconn_t
*conn
, const char *sql
, size_t sql_len
) {
700 sql_len
= strlen(sql
);
701 msg
= pgmsg_create_simple_query(sql
, sql_len
);
702 rc
= pgmsg_send(conn
->fd
, msg
);
705 rc
= _simple_exec(conn
);
709 int pg_copyin (pgconn_t
*conn
, const char *table_name
) {
710 cstr_t
*sql
= cstrfmt("copy %s from stdin", table_name
);
711 int rc
= pg_execsql(conn
, sql
->ptr
, sql
->len
);
714 if (!rc
) rc
= conn
->cols
;
718 int pg_copyin_sendln (pgconn_t
*conn
, pgfld_t
**flds
) {
719 pgmsg_t
*msg
= pgmsg_copyin_flds(conn
->cols
, flds
);
722 int rc
= pgmsg_send(conn
->fd
, msg
);
727 int pg_copyin_end (pgconn_t
*conn
) {
728 pgmsg_t
*msg
= pgmsg_create(PG_COPYEND
);
730 int rc
= pgmsg_send(conn
->fd
, msg
);
733 rc
= _simple_exec(conn
);
737 static int _pg_sync (pgconn_t
*conn
) {
739 pgmsg_t
*msg
= pgmsg_create(PG_SYNC
);
740 rc
= pgmsg_send(conn
->fd
, msg
);
745 int pg_prepareln (pgconn_t
*conn
, const char *name
, size_t name_len
, const char *sql
, size_t sql_len
, int fld_len
, pgfld_t
**flds
) {
749 msg
= pgmsg_create_parse(name
, name_len
, sql
, sql_len
, fld_len
, flds
);
750 rc
= pgmsg_send(conn
->fd
, msg
);
752 if (0 == rc
&& name
&& 0 == (rc
= _pg_sync(conn
)))
757 DECLARE_ARRAY(pgfld_array
,pgfld_ptr_t
)
758 DECLARE_ARRAY_FUN(pgfld_array
,pgfld_ptr_t
)
760 static int pg_preparevn (pgconn_t
*conn
, const char *name
, size_t name_len
, const char *sql
, size_t sql_len
, pgfld_t
*fld
, va_list ap
) {
762 pgfld_array_t
*flds
= create_pgfld_array(16, 16, 0);
764 add_pgfld_array(&flds
, &fld
);
765 while (NULL
!= (fld
= va_arg(ap
, pgfld_ptr_t
)))
766 add_pgfld_array(&flds
, &fld
);
768 rc
= pg_prepareln(conn
, name
, name_len
, sql
, sql_len
, flds
->len
, flds
->ptr
);
769 free_pgfld_array(&flds
);
773 int pg_preparen (pgconn_t
*conn
, const char *name
, size_t name_len
, const char *sql
, size_t sql_len
, pgfld_t
*fld
, ...) {
777 rc
= pg_preparevn(conn
, name
, name_len
, sql
, sql_len
, fld
, ap
);
782 int pg_prepare (pgconn_t
*conn
, const char *sql
, size_t sql_len
, pgfld_t
*fld
, ...) {
786 rc
= pg_preparevn(conn
, CONST_STR_NULL
, sql
, sql_len
, fld
, ap
);
791 static int _pg_bind (pgconn_t
*conn
, const char *portal
, size_t portal_len
, const char *stmt
, size_t stmt_len
,
792 int fld_len
, pgfld_t
**flds
, int res_fmt_len
, int *res_fmt
) {
796 msg
= pgmsg_create_bind(portal
, portal_len
, stmt
, stmt_len
, fld_len
, flds
, res_fmt_len
, res_fmt
);
797 rc
= pgmsg_send(conn
->fd
, msg
);
802 static int _pg_describe (pgconn_t
*conn
, int8_t op
, const char *portal
, size_t portal_len
) {
805 msg
= pgmsg_create_describe(op
, portal
, portal_len
);
806 rc
= pgmsg_send(conn
->fd
, msg
);
811 static int _pg_execute (pgconn_t
*conn
, const char *portal
, size_t portal_len
, int max_rows
) {
814 msg
= pgmsg_create_execute(portal
, portal_len
, max_rows
);
815 rc
= pgmsg_send(conn
->fd
, msg
);
817 if (0 == rc
&& 0 == (rc
= _pg_sync(conn
)))
822 int pg_execln (pgconn_t
*conn
, const char *portal
, size_t portal_len
, const char *stmt
, size_t stmt_len
,
823 int fld_len
, pgfld_t
**flds
, int res_fmt_len
, int *res_fmt
, int max_data
) {
825 if (0 == (rc
= _pg_bind(conn
, portal
, portal_len
, stmt
, stmt_len
, fld_len
, flds
, res_fmt_len
, res_fmt
)) &&
826 0 == (rc
= _pg_describe(conn
, PG_PREPARED_PORTAL
, portal
, portal_len
)))
827 rc
= _pg_execute(conn
, portal
, portal_len
, max_data
);
831 int pg_nextn (pgconn_t
*conn
, const char *portal
, size_t portal_len
, int max_data
) {
832 return _pg_execute(conn
, portal
, portal_len
, max_data
);
835 int pg_execvn (pgconn_t
*conn
, const char *portal
, size_t portal_len
, const char *stmt
, size_t stmt_len
, int max_data
, int out_fmt
, pgfld_t
*fld
, va_list ap
) {
837 pgfld_array_t
*flds
= create_pgfld_array(16, 16, 0);
839 add_pgfld_array(&flds
, &fld
);
840 while (NULL
!= (fld
= va_arg(ap
, pgfld_ptr_t
)))
841 add_pgfld_array(&flds
, &fld
);
843 rc
= pg_execln(conn
, portal
, portal_len
, stmt
, stmt_len
, flds
->len
, flds
->ptr
, 1, &out_fmt
, max_data
);
844 free_pgfld_array(&flds
);
848 int pg_execn (pgconn_t
*conn
, const char *portal
, size_t portal_len
, const char *stmt
, size_t stmt_len
, int max_data
, int out_fmt
, pgfld_t
*fld
, ...) {
852 rc
= pg_execvn(conn
, portal
, portal_len
, stmt
, stmt_len
, max_data
, out_fmt
, fld
, ap
);
857 int pg_exec (pgconn_t
*conn
, int max_data
, int out_fmt
, pgfld_t
*fld
, ...) {
861 rc
= pg_execvn(conn
, CONST_STR_NULL
, CONST_STR_NULL
, max_data
, out_fmt
, fld
, ap
);
866 int pg_release (pgconn_t
*conn
, const char *name
, size_t name_len
) {
867 pgmsg_t
*msg
= pgmsg_create_close(PG_OPNAME
, name
, 0 == name_len
? strlen(name
) : name_len
);
868 int rc
= pgmsg_send(conn
->fd
, msg
);