39 "PGRES_NONFATAL_ERROR",
60 const Oid *paramTypes,
61 const char *
const * paramValues,
62 const int *paramLengths,
63 const int *paramFormats,
70 const char *desc_target);
127 #define PGRESULT_DATA_BLOCKSIZE 2048
128 #define PGRESULT_ALIGN_BOUNDARY MAXIMUM_ALIGNOF
129 #define PGRESULT_BLOCK_OVERHEAD Max(sizeof(PGresult_data), PGRESULT_ALIGN_BOUNDARY)
130 #define PGRESULT_SEP_ALLOC_THRESHOLD (PGRESULT_DATA_BLOCKSIZE / 2)
235 if (numAttributes <= 0 || !attDescs)
323 for (tup = 0; tup < src->
ntups; tup++)
355 for (i = 0; i < dest->
nEvents; i++)
387 if (!events || count <= 0)
394 for (i = 0; i < count; i++)
400 newEvents[
i].
name = strdup(events[i].
name);
401 if (!newEvents[i].name)
404 free(newEvents[i].name);
429 if (tup_num < 0 || tup_num > res->
ntups)
433 if (tup_num == res->
ntups)
457 attval = &res->
tuples[tup_num][field_num];
476 memcpy(attval->
value, value, len);
477 attval->
value[len] =
'\0';
658 for (i = 0; i < res->
nEvents; i++)
814 msgBuf[
sizeof(msgBuf) - 1] =
'\0';
820 res->noticeHooks = *hooks;
836 sprintf(res->errMsg,
"%s\n", msgBuf);
841 (*res->noticeHooks.noticeRec) (res->noticeHooks.noticeRecArg, res);
917 fprintf(conn->
Pfdebug,
"pqSaveParameterStatus: '%s' = '%s'\n",
925 prev = pstatus, pstatus = pstatus->
next)
927 if (strcmp(pstatus->
name, name) == 0)
942 strlen(name) +strlen(value) + 2);
950 ptr += strlen(name) + 1;
951 pstatus->
value = ptr;
964 if (strcmp(name,
"client_encoding") == 0)
972 else if (strcmp(name,
"standard_conforming_strings") == 0)
977 else if (strcmp(name,
"server_version") == 0)
984 cnt = sscanf(value,
"%d.%d.%d", &vmaj, &vmin, &vrev);
992 conn->
sversion = (100 * vmaj + vmin) * 100 + vrev;
1049 for (i = 0; i < nfields; i++)
1051 int clen = columns[
i].
len;
1069 memcpy(val, columns[i].
value, clen);
1129 pqPuts(query, conn) < 0 ||
1166 const char *command,
1168 const Oid *paramTypes,
1169 const char *
const * paramValues,
1170 const int *paramLengths,
1171 const int *paramFormats,
1184 if (nParams < 0 || nParams > 65535)
1187 libpq_gettext(
"number of parameters must be between 0 and 65535\n"));
1211 const char *stmtName,
const char *query,
1212 int nParams,
const Oid *paramTypes)
1230 if (nParams < 0 || nParams > 65535)
1233 libpq_gettext(
"number of parameters must be between 0 and 65535\n"));
1241 libpq_gettext(
"function requires at least protocol version 3.0\n"));
1247 pqPuts(stmtName, conn) < 0 ||
1251 if (nParams > 0 && paramTypes)
1255 if (
pqPutInt(nParams, 2, conn) < 0)
1257 for (i = 0; i < nParams; i++)
1259 if (
pqPutInt(paramTypes[i], 4, conn) < 0)
1308 const char *stmtName,
1310 const char *
const * paramValues,
1311 const int *paramLengths,
1312 const int *paramFormats,
1325 if (nParams < 0 || nParams > 65535)
1328 libpq_gettext(
"number of parameters must be between 0 and 65535\n"));
1390 const char *command,
1391 const char *stmtName,
1393 const Oid *paramTypes,
1394 const char *
const * paramValues,
1395 const int *paramLengths,
1396 const int *paramFormats,
1405 libpq_gettext(
"function requires at least protocol version 3.0\n"));
1418 pqPuts(stmtName, conn) < 0 ||
1419 pqPuts(command, conn) < 0)
1421 if (nParams > 0 && paramTypes)
1423 if (
pqPutInt(nParams, 2, conn) < 0)
1425 for (i = 0; i < nParams; i++)
1427 if (
pqPutInt(paramTypes[i], 4, conn) < 0)
1443 pqPuts(stmtName, conn) < 0)
1447 if (nParams > 0 && paramFormats)
1449 if (
pqPutInt(nParams, 2, conn) < 0)
1451 for (i = 0; i < nParams; i++)
1453 if (
pqPutInt(paramFormats[i], 2, conn) < 0)
1463 if (
pqPutInt(nParams, 2, conn) < 0)
1467 for (i = 0; i < nParams; i++)
1469 if (paramValues && paramValues[i])
1473 if (paramFormats && paramFormats[i] != 0)
1477 nbytes = paramLengths[
i];
1481 libpq_gettext(
"length must be given for binary parameter\n"));
1488 nbytes = strlen(paramValues[i]);
1490 if (
pqPutInt(nbytes, 4, conn) < 0 ||
1491 pqPutnchar(paramValues[i], nbytes, conn) < 0)
1704 while ((flushResult =
pqFlush(conn)) > 0)
1763 for (i = 0; i < res->
nEvents; i++)
1773 libpq_gettext(
"PGEventProc \"%s\" failed during PGEVT_RESULTCREATE event\n"),
1844 const char *command,
1846 const Oid *paramTypes,
1847 const char *
const * paramValues,
1848 const int *paramLengths,
1849 const int *paramFormats,
1855 nParams, paramTypes, paramValues, paramLengths,
1856 paramFormats, resultFormat))
1874 const char *stmtName,
const char *query,
1875 int nParams,
const Oid *paramTypes)
1879 if (!
PQsendPrepare(conn, stmtName, query, nParams, paramTypes))
1891 const char *stmtName,
1893 const char *
const * paramValues,
1894 const int *paramLengths,
1895 const int *paramFormats,
1901 nParams, paramValues, paramLengths,
1902 paramFormats, resultFormat))
1961 libpq_gettext(
"COPY OUT state must be terminated first\n"));
2010 result = lastResult;
2021 lastResult = result;
2123 libpq_gettext(
"function requires at least protocol version 3.0\n"));
2129 pqPutc(desc_type, conn) < 0 ||
2130 pqPuts(desc_target, conn) < 0 ||
2293 pqPuts(errormsg, conn) < 0 ||
2322 libpq_gettext(
"function requires at least protocol version 3.0\n"));
2404 if (!s || maxlen <= 0)
2565 result_buf, result_len,
2570 result_buf, result_len,
2597 if (!res || !res->
errMsg)
2617 return strdup(
libpq_gettext(
"PGresult is not an error result\n"));
2637 return workBuf.
data;
2649 if (pfield->
code == fieldcode)
2692 "column number %d is out of range 0..%d",
2701 int tup_num,
int field_num)
2705 if (tup_num < 0 || tup_num >= res->
ntups)
2708 "row number %d is out of range 0..%d",
2709 tup_num, res->
ntups - 1);
2715 "column number %d is out of range 0..%d",
2730 "parameter number %d is out of range 0..%d",
2768 bool all_lower =
true;
2780 if (field_name ==
NULL ||
2781 field_name[0] ==
'\0' ||
2789 for (iptr = field_name; *iptr; iptr++)
2793 if (c ==
'"' || c !=
pg_tolower((
unsigned char) c))
2812 field_case = strdup(field_name);
2813 if (field_case ==
NULL)
2818 for (iptr = field_case; *iptr; iptr++)
2946 static char buf[24];
2950 if (!res || strncmp(res->
cmdStatus,
"INSERT ", 7) != 0)
2953 len = strspn(res->
cmdStatus + 7,
"0123456789");
2954 if (len >
sizeof(buf) - 1)
2955 len =
sizeof(
buf) - 1;
2970 char *endptr =
NULL;
2971 unsigned long result;
2974 strncmp(res->
cmdStatus,
"INSERT ", 7) != 0 ||
2979 result = strtoul(res->
cmdStatus + 7, &endptr, 10);
2981 if (!endptr || (*endptr !=
' ' && *endptr !=
'\0'))
2984 return (
Oid) result;
3005 if (strncmp(res->
cmdStatus,
"INSERT ", 7) == 0)
3009 while (*p && *p !=
' ')
3012 goto interpret_error;
3015 else if (strncmp(res->
cmdStatus,
"SELECT ", 7) == 0 ||
3016 strncmp(res->
cmdStatus,
"DELETE ", 7) == 0 ||
3017 strncmp(res->
cmdStatus,
"UPDATE ", 7) == 0)
3019 else if (strncmp(res->
cmdStatus,
"FETCH ", 6) == 0)
3021 else if (strncmp(res->
cmdStatus,
"MOVE ", 5) == 0 ||
3022 strncmp(res->
cmdStatus,
"COPY ", 5) == 0)
3028 for (c = p; *
c; c++)
3030 if (!isdigit((
unsigned char) *c))
3031 goto interpret_error;
3034 goto interpret_error;
3040 "could not interpret result from server: %s",
3066 return res->
tuples[tup_num][field_num].
len;
3160 #ifdef ENABLE_THREAD_SAFETY
3223 char *to,
const char *from,
size_t length,
3227 const char *source = from;
3234 while (remaining > 0 && *source !=
'\0')
3257 for (i = 0; i < len; i++)
3259 if (remaining == 0 || *source ==
'\0')
3261 *target++ = *source++;
3280 for (; i < len; i++)
3282 if (((
size_t) (target - to)) / 2 >= length)
3298 char *to,
const char *from,
size_t length,
3336 int num_backslashes = 0;
3339 char quote_char = as_ident ?
'"' :
'\'';
3346 for (s = str; (s - str) < len && *s !=
'\0'; ++s)
3348 if (*s == quote_char)
3350 else if (*s ==
'\\')
3360 if ((s - str) + charlen > len || memchr(s, 0, charlen) !=
NULL)
3373 input_len = s - str;
3374 result_size = input_len + num_quotes + 3;
3375 if (!as_ident && num_backslashes > 0)
3376 result_size += num_backslashes + 2;
3377 result = rp = (
char *)
malloc(result_size);
3392 if (!as_ident && num_backslashes > 0)
3412 if (num_quotes == 0 && (num_backslashes == 0 || as_ident))
3414 memcpy(rp, str, input_len);
3419 for (s = str; s - str < input_len; ++s)
3421 if (*s == quote_char || (!as_ident && *s ==
'\\'))
3463 static const char hextbl[] =
"0123456789abcdef";
3466 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3467 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3468 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3469 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
3470 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3471 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3472 -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3473 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
3481 if (c > 0 && c < 127)
3503 static unsigned char *
3505 const unsigned char *from,
size_t from_length,
3506 size_t *to_length,
bool std_strings,
bool use_hex)
3508 const unsigned char *vp;
3510 unsigned char *result;
3513 size_t bslash_len = (std_strings ? 1 : 2);
3522 len += bslash_len + 1 + 2 * from_length;
3527 for (i = from_length; i > 0; i--, vp++)
3529 if (*vp < 0x20 || *vp > 0x7e)
3530 len += bslash_len + 3;
3531 else if (*vp ==
'\'')
3533 else if (*vp ==
'\\')
3534 len += bslash_len + bslash_len;
3541 rp = result = (
unsigned char *)
malloc(len);
3559 for (i = from_length; i > 0; i--, vp++)
3561 unsigned char c = *vp;
3565 *rp++ =
hextbl[(c >> 4) & 0xF];
3573 *rp++ = (c >> 6) +
'0';
3574 *rp++ = ((c >> 3) & 07) +
'0';
3575 *rp++ = (c & 07) +
'0';
3602 const unsigned char *from,
size_t from_length,
3621 #define ISFIRSTOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '3')
3622 #define ISOCTDIGIT(CH) ((CH) >= '0' && (CH) <= '7')
3623 #define OCTVAL(CH) ((CH) - '0')
3642 unsigned char *buffer,
3647 if (strtext ==
NULL)
3650 strtextlen = strlen((
const char *) strtext);
3652 if (strtext[0] ==
'\\' && strtext[1] ==
'x')
3654 const unsigned char *s;
3657 buflen = (strtextlen - 2) / 2;
3659 buffer = (
unsigned char *)
malloc(buflen > 0 ? buflen : 1);
3675 if (!*s || v1 == (
char) -1)
3678 if (v2 != (
char) -1)
3679 *p++ = (v1 << 4) | v2;
3682 buflen = p - buffer;
3690 buffer = (
unsigned char *)
malloc(strtextlen + 1);
3694 for (i = j = 0; i < strtextlen;)
3700 if (strtext[i] ==
'\\')
3701 buffer[j++] = strtext[i++];
3710 byte =
OCTVAL(strtext[i++]);
3711 byte = (byte << 3) +
OCTVAL(strtext[i++]);
3712 byte = (byte << 3) +
OCTVAL(strtext[i++]);
3727 buffer[j++] = strtext[i++];
3736 tmpbuf =
realloc(buffer, buflen + 1);
3745 *retbuflen = buflen;
int PQputCopyData(PGconn *conn, const char *buffer, int nbytes)
int pqFlush(PGconn *conn)
int pqRowProcessor(PGconn *conn, const char **errmsgp)
static bool static_std_strings
int PQgetlength(const PGresult *res, int tup_num, int field_num)
int length(const List *list)
PGresult * PQexecPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
int PQnfields(const PGresult *res)
PGresult * PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
PGresult * PQdescribePrepared(PGconn *conn, const char *stmt)
void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
void pqHandleSendFailure(PGconn *conn)
PGMessageField * errFields
static PGEvent * dupEvents(PGEvent *events, int count)
int PQftablecol(const PGresult *res, int field_num)
int pg_char_to_encoding(const char *name)
int PQisnonblocking(const PGconn *conn)
void pqParseInput2(PGconn *conn)
#define PG_DIAG_MESSAGE_PRIMARY
#define ISFIRSTOCTDIGIT(CH)
size_t PQescapeString(char *to, const char *from, size_t length)
int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
int pqGetline3(PGconn *conn, char *s, int maxlen)
PGresult * pqFunctionCall2(PGconn *conn, Oid fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs)
char * PQgetvalue(const PGresult *res, int tup_num, int field_num)
int pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn)
static int check_tuple_field_number(const PGresult *res, int tup_num, int field_num)
int PQfsize(const PGresult *res, int field_num)
PGnotify * PQnotifies(PGconn *conn)
char * PQfname(const PGresult *res, int field_num)
void pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
static int check_param_number(const PGresult *res, int param_num)
void termPQExpBuffer(PQExpBuffer str)
#define pqIsnonblocking(conn)
char * PQcmdTuples(PGresult *res)
PGresult * pqFunctionCall3(PGconn *conn, Oid fnid, int *result_buf, int *actual_result_len, int result_is_int, const PQArgBlock *args, int nargs)
void appendPQExpBufferStr(PQExpBuffer str, const char *data)
int PQputnbytes(PGconn *conn, const char *buffer, int nbytes)
int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len)
Oid PQoidValue(const PGresult *res)
Oid PQftable(const PGresult *res, int field_num)
int PQsendDescribePortal(PGconn *conn, const char *portal)
static bool pqAddTuple(PGresult *res, PGresAttValue *tup)
static int PQsendQueryGuts(PGconn *conn, const char *command, const char *stmtName, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
PGresult * PQcopyResult(const PGresult *src, int flags)
union pgresult_data PGresult_data
char * PQresStatus(ExecStatusType status)
Oid PQparamtype(const PGresult *res, int param_num)
int PQgetlineAsync(PGconn *conn, char *buffer, int bufsize)
int pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
unsigned char pg_tolower(unsigned char ch)
int pqGetlineAsync2(PGconn *conn, char *buffer, int bufsize)
int PQputCopyEnd(PGconn *conn, const char *errormsg)
struct pgMessageField * next
PGresult * PQdescribePortal(PGconn *conn, const char *portal)
int PQbinaryTuples(const PGresult *res)
#define PG_PROTOCOL_MAJOR(v)
char * PQoidStatus(const PGresult *res)
int PQntuples(const PGresult *res)
PGresult * PQfn(PGconn *conn, int fnid, int *result_buf, int *result_len, int result_is_int, const PQArgBlock *args, int nargs)
PGresult * pqPrepareAsyncResult(PGconn *conn)
ExecStatusType PQresultStatus(const PGresult *res)
#define PG_COPYRES_TUPLES
int PQgetCopyData(PGconn *conn, char **buffer, int async)
static int PQsendDescribe(PGconn *conn, char desc_type, const char *desc_target)
int int vsnprintf(char *str, size_t count, const char *fmt, va_list args)
int pqPutInt(int value, size_t bytes, PGconn *conn)
int PQputline(PGconn *conn, const char *s)
int pqGetlineAsync3(PGconn *conn, char *buffer, int bufsize)
static char get_hex(char c)
#define PGRESULT_ALIGN_BOUNDARY
int PQsendQuery(PGconn *conn, const char *query)
char * PQescapeIdentifier(PGconn *conn, const char *str, size_t len)
PGAsyncStatusType asyncStatus
#define IS_HIGHBIT_SET(ch)
#define PG_COPYRES_EVENTS
int pqEndcopy3(PGconn *conn)
void PQfreeNotify(PGnotify *notify)
PGNoticeHooks noticeHooks
#define PG_COPYRES_NOTICEHOOKS
struct pgParameterStatus pgParameterStatus
PGNoticeHooks noticeHooks
int PQflush(PGconn *conn)
Oid PQftype(const PGresult *res, int field_num)
int pqReadData(PGconn *conn)
int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs)
char * PQresultVerboseErrorMessage(const PGresult *res, PGVerbosity verbosity, PGContextVisibility show_context)
static const int8 hexlookup[128]
static int static_client_encoding
int pqWait(int forRead, int forWrite, PGconn *conn)
#define PGRESULT_BLOCK_OVERHEAD
int PQsetSingleRowMode(PGconn *conn)
static PGresult * getCopyResult(PGconn *conn, ExecStatusType copytype)
PQnoticeReceiver noticeRec
int pg_encoding_mblen(int encoding, const char *mbstr)
void pqInternalNotice(const PGNoticeHooks *hooks, const char *fmt,...)
int PQsendDescribePrepared(PGconn *conn, const char *stmt)
size_t PQescapeStringConn(PGconn *conn, char *to, const char *from, size_t length, int *error)
PGresult * PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
int pqPutc(char c, PGconn *conn)
int pqPuts(const char *s, PGconn *conn)
static void parseInput(PGconn *conn)
static bool PQsendQueryStart(PGconn *conn)
int pqGetCopyData3(PGconn *conn, char **buffer, int async)
unsigned char * PQunescapeBytea(const unsigned char *strtext, size_t *retbuflen)
char * PQcmdStatus(PGresult *res)
void pqSaveMessageField(PGresult *res, char code, const char *value)
pgParameterStatus * pstatus
#define PGRESULT_SEP_ALLOC_THRESHOLD
int PQfnumber(const PGresult *res, const char *field_name)
int PQconsumeInput(PGconn *conn)
int PQsetnonblocking(PGconn *conn, int arg)
PQnoticeProcessor noticeProc
int PQgetline(PGconn *conn, char *s, int maxlen)
char * PQescapeLiteral(PGconn *conn, const char *str, size_t len)
PQExpBufferData errorMessage
void pqSetResultError(PGresult *res, const char *msg)
void PQclear(PGresult *res)
static int check_field_number(const PGresult *res, int field_num)
#define PQExpBufferDataBroken(buf)
int PQendcopy(PGconn *conn)
void * pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
PGresParamDesc * paramDescs
char * PQresultErrorField(const PGresult *res, int fieldcode)
int pqPutnchar(const char *s, size_t len, PGconn *conn)
static size_t PQescapeStringInternal(PGconn *conn, char *to, const char *from, size_t length, int *error, int encoding, bool std_strings)
int PQisBusy(PGconn *conn)
int pqGetline2(PGconn *conn, char *s, int maxlen)
struct pgParameterStatus * next
char contents[FLEXIBLE_ARRAY_MEMBER]
char cmdStatus[CMDSTATUS_LEN]
int PQfmod(const PGresult *res, int field_num)
static char * PQescapeInternal(PGconn *conn, const char *str, size_t len, bool as_ident)
int pqPutMsgEnd(PGconn *conn)
void pqCatenateResultError(PGresult *res, const char *msg)
static PGresult * PQexecFinish(PGconn *conn)
int PQsendQueryPrepared(PGconn *conn, const char *stmtName, int nParams, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
static StringInfoData tmpbuf
char * PQresultErrorMessage(const PGresult *res)
#define PGRESULT_DATA_BLOCKSIZE
static bool PQexecStart(PGconn *conn)
PGresult * PQexecParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes, const char *const *paramValues, const int *paramLengths, const int *paramFormats, int resultFormat)
static const char hextbl[]
PGresult * PQexec(PGconn *conn, const char *query)
#define SQL_STR_DOUBLE(ch, escape_backslash)
void pqBuildErrorMessage3(PQExpBuffer msg, const PGresult *res, PGVerbosity verbosity, PGContextVisibility show_context)
char *const pgresStatus[]
unsigned char * PQescapeBytea(const unsigned char *from, size_t from_length, size_t *to_length)
void pqClearAsyncResult(PGconn *conn)
void pqSaveErrorResult(PGconn *conn)
void pqParseInput3(PGconn *conn)
int pqGetCopyData2(PGconn *conn, char **buffer, int async)
void resetPQExpBuffer(PQExpBuffer str)
static void static void status(const char *fmt,...) pg_attribute_printf(1
int PQgetisnull(const PGresult *res, int tup_num, int field_num)
unsigned char * PQescapeByteaConn(PGconn *conn, const unsigned char *from, size_t from_length, size_t *to_length)
static unsigned char * PQescapeByteaInternal(PGconn *conn, const unsigned char *from, size_t from_length, size_t *to_length, bool std_strings, bool use_hex)
int PQfformat(const PGresult *res, int field_num)
ExecStatusType resultStatus
void PQfreemem(void *ptr)
PGresult * PQgetResult(PGconn *conn)
int pqEndcopy2(PGconn *conn)
int PQnparams(const PGresult *res)
void initPQExpBuffer(PQExpBuffer str)
#define offsetof(type, field)
int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams, const Oid *paramTypes)
char * pqResultStrdup(PGresult *res, const char *str)
void * PQresultAlloc(PGresult *res, size_t nBytes)