/*
* Emit a PG error or notice, together with any available info about
* the current Python error, previously set by PLy_exception_set().
* This should be used to propagate Python errors into PG. If fmt is
* NULL, the Python error becomes the primary error message, otherwise
* it becomes the detail. If there is a Python traceback, it is put
* in the context.
*/
void
PLy_elog(int elevel, const char *fmt,...)
{
char *xmsg;
char *tbmsg;
int tb_depth;
StringInfoData emsg;
PyObject *exc,
*val,
*tb;
const char *primary = NULL;
int sqlerrcode = 0;
char *detail = NULL;
char *hint = NULL;
char *query = NULL;
int position = 0;
PyErr_Fetch(&exc, &val, &tb);
if (exc != NULL)
{
PyErr_NormalizeException(&exc, &val, &tb);
if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position);
else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
elevel = FATAL;
}
/* this releases our refcount on tb! */
PLy_traceback(exc, val, tb,
&xmsg, &tbmsg, &tb_depth);
if (fmt)
{
initStringInfo(&emsg);
for (;;)
{
va_list ap;
int needed;
va_start(ap, fmt);
needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
va_end(ap);
if (needed == 0)
break;
enlargeStringInfo(&emsg, needed);
}
primary = emsg.data;
/* Since we have a format string, we cannot have a SPI detail. */
Assert(detail == NULL);
/* If there's an exception message, it goes in the detail. */
if (xmsg)
detail = xmsg;
}
else
{
if (xmsg)
primary = xmsg;
}
PG_TRY();
{
ereport(elevel,
(errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
errmsg_internal("%s", primary ? primary : "no exception data"),
(detail) ? errdetail_internal("%s", detail) : 0,
(tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
(hint) ? errhint("%s", hint) : 0,
(query) ? internalerrquery(query) : 0,
(position) ? internalerrposition(position) : 0));
}
PG_CATCH();
{
if (fmt)
pfree(emsg.data);
if (xmsg)
pfree(xmsg);
if (tbmsg)
pfree(tbmsg);
Py_XDECREF(exc);
Py_XDECREF(val);
PG_RE_THROW();
}
PG_END_TRY();
if (fmt)
pfree(emsg.data);
if (xmsg)
//.........这里部分代码省略.........
/*
* ean2isn --- Try to convert an ean13 number to a UPC/ISxN number.
* This doesn't verify for a valid check digit.
*
* If errorOK is false, ereport a useful error message if the ean13 is bad.
* If errorOK is true, just return "false" for bad input.
*/
static bool
ean2isn(ean13 ean, bool errorOK, ean13 *result, enum isn_type accept)
{
enum isn_type type = INVALID;
char buf[MAXEAN13LEN + 1];
char *aux;
unsigned digval;
unsigned search;
ean13 ret = ean;
ean >>= 1;
/* verify it's in the EAN13 range */
if (ean > UINT64CONST(9999999999999))
goto eantoobig;
/* convert the number */
search = 0;
aux = buf + 13;
*aux = '\0'; /* terminate string; aux points to last digit */
do
{
digval = (unsigned) (ean % 10); /* get the decimal value */
ean /= 10; /* get next digit */
*--aux = (char) (digval + '0'); /* convert to ascii and store */
} while (ean && search++ < 12);
while (search++ < 12)
*--aux = '0'; /* fill the remaining EAN13 with '0' */
/* find out the data type: */
if (strncmp("978", buf, 3) == 0)
{ /* ISBN */
type = ISBN;
}
else if (strncmp("977", buf, 3) == 0)
{ /* ISSN */
type = ISSN;
}
else if (strncmp("9790", buf, 4) == 0)
{ /* ISMN */
type = ISMN;
}
else if (strncmp("979", buf, 3) == 0)
{ /* ISBN-13 */
type = ISBN;
}
else if (*buf == '0')
{ /* UPC */
type = UPC;
}
else
{
type = EAN13;
}
if (accept != ANY && accept != EAN13 && accept != type)
goto eanwrongtype;
*result = ret;
return true;
eanwrongtype:
if (!errorOK)
{
if (type != EAN13)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("cannot cast EAN13(%s) to %s for number: \"%s\"",
isn_names[type], isn_names[accept], buf)));
}
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("cannot cast %s to %s for number: \"%s\"",
isn_names[type], isn_names[accept], buf)));
}
}
return false;
eantoobig:
if (!errorOK)
{
char eanbuf[64];
/*
* Format the number separately to keep the machine-dependent format
* code out of the translatable message text
*/
snprintf(eanbuf, sizeof(eanbuf), EAN13_FORMAT, ean);
ereport(ERROR,
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
errmsg("value \"%s\" is out of range for %s type",
//.........这里部分代码省略.........
Datum
hstore_populate_record(PG_FUNCTION_ARGS)
{
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
HStore *hs;
HEntry *entries;
char *ptr;
HeapTupleHeader rec;
Oid tupType;
int32 tupTypmod;
TupleDesc tupdesc;
HeapTupleData tuple;
HeapTuple rettuple;
RecordIOData *my_extra;
int ncolumns;
int i;
Datum *values;
bool *nulls;
if (!type_is_rowtype(argtype))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("first argument must be a rowtype")));
if (PG_ARGISNULL(0))
{
if (PG_ARGISNULL(1))
PG_RETURN_NULL();
rec = NULL;
/*
* have no tuple to look at, so the only source of type info is the
* argtype. The lookup_rowtype_tupdesc call below will error out if we
* don't have a known composite type oid here.
*/
tupType = argtype;
tupTypmod = -1;
}
else
{
rec = PG_GETARG_HEAPTUPLEHEADER(0);
if (PG_ARGISNULL(1))
PG_RETURN_POINTER(rec);
/* Extract type info from the tuple itself */
tupType = HeapTupleHeaderGetTypeId(rec);
tupTypmod = HeapTupleHeaderGetTypMod(rec);
}
hs = PG_GETARG_HS(1);
entries = ARRPTR(hs);
ptr = STRPTR(hs);
/*
* if the input hstore is empty, we can only skip the rest if we were
* passed in a non-null record, since otherwise there may be issues with
* domain nulls.
*/
if (HS_COUNT(hs) == 0 && rec)
PG_RETURN_POINTER(rec);
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
ncolumns = tupdesc->natts;
if (rec)
{
/* Build a temporary HeapTuple control structure */
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
tuple.t_data = rec;
}
/*
* We arrange to look up the needed I/O info just once per series of
* calls, assuming the record type doesn't change underneath us.
*/
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
if (my_extra == NULL ||
my_extra->ncolumns != ncolumns)
{
fcinfo->flinfo->fn_extra =
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
my_extra->record_type = InvalidOid;
my_extra->record_typmod = 0;
}
if (my_extra->record_type != tupType ||
my_extra->record_typmod != tupTypmod)
{
MemSet(my_extra, 0,
sizeof(RecordIOData) - sizeof(ColumnIOData)
+ ncolumns * sizeof(ColumnIOData));
my_extra->record_type = tupType;
//.........这里部分代码省略.........
/*
* Does the supplied GpPolicy support unique indexing on the specified
* attributes?
*
* If the table is distributed randomly, no unique indexing is supported.
* Otherwise, the set of columns being indexed should be a superset of the
* policy.
*
* If the proposed index does not match the distribution policy but the relation
* is empty and does not have a primary key or unique index, update the
* distribution policy to match the index definition (MPP-101), as long as it
* doesn't contain expressions.
*/
void
checkPolicyForUniqueIndex(Relation rel, AttrNumber *indattr, int nidxatts,
bool isprimary, bool has_exprs, bool has_pkey,
bool has_ukey)
{
Bitmapset *polbm = NULL;
Bitmapset *indbm = NULL;
int i;
GpPolicy *pol = rel->rd_cdbpolicy;
/*
* Firstly, unique/primary key indexes aren't supported if we're
* distributing randomly.
*/
if (GpPolicyIsRandomly(pol))
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("%s and DISTRIBUTED RANDOMLY are incompatible",
isprimary ? "PRIMARY KEY" : "UNIQUE")));
}
/*
* We use bitmaps to make intersection tests easier. As noted, order is
* not relevant so looping is just painful.
*/
for (i = 0; i < pol->nattrs; i++)
polbm = bms_add_member(polbm, pol->attrs[i]);
for (i = 0; i < nidxatts; i++)
{
if (indattr[i] < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("cannot create %s on system column",
isprimary ? "primary key" : "unique index")));
indbm = bms_add_member(indbm, indattr[i]);
}
Assert(bms_membership(polbm) != BMS_EMPTY_SET);
Assert(bms_membership(indbm) != BMS_EMPTY_SET);
/*
* If the existing policy is not a subset, we must either error out or
* update the distribution policy. It might be tempting to say that even
* when the policy is a subset, we should update it to match the index
* definition. The problem then is that if the user actually wants to
* distribution on (a, b) but then creates an index on (a, b, c) we'll
* change the policy underneath them.
*
* What is really needed is a new field in gp_distribution_policy telling us
* if the policy has been explicitly set.
*/
if (!bms_is_subset(polbm, indbm))
{
if (cdbRelSize(rel) != 0 || has_pkey || has_ukey || has_exprs)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("%s must contain all columns in the "
"distribution key of relation \"%s\"",
isprimary ? "PRIMARY KEY" : "UNIQUE index",
RelationGetRelationName(rel))));
}
else
{
/* update policy since table is not populated yet. See MPP-101 */
GpPolicy *policy = palloc(sizeof(GpPolicy) +
(sizeof(AttrNumber) * nidxatts));
policy->ptype = POLICYTYPE_PARTITIONED;
policy->nattrs = 0;
for (i = 0; i < nidxatts; i++)
policy->attrs[policy->nattrs++] = indattr[i];
GpPolicyReplace(rel->rd_id, policy);
if (isprimary)
elog(NOTICE, "updating distribution policy to match new primary key");
else
elog(NOTICE, "updating distribution policy to match new unique index");
}
}
}
开发者ID:a320321wb,项目名称:gpdb,代码行数:96,代码来源:cdbcat.c
示例6: handler_internal
/*
* Internal handler function
*/
static Datum
handler_internal(Oid function_oid, FunctionCallInfo fcinfo, bool execute)
{
HeapTuple proctuple;
Form_pg_proc pg_proc_entry;
char *sourcecode;
Datum prosrcdatum;
bool isnull;
const char **xslt_params;
int i;
Oid *argtypes;
char **argnames;
char *argmodes;
int numargs;
xmlDocPtr ssdoc;
xmlDocPtr xmldoc;
xmlDocPtr resdoc;
xsltStylesheetPtr stylesheet;
int reslen;
xmlChar *resstr;
Datum resdatum;
if (CALLED_AS_TRIGGER(fcinfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("trigger functions not supported")));
proctuple = SearchSysCache(PROCOID, ObjectIdGetDatum(function_oid), 0, 0, 0);
if (!HeapTupleIsValid(proctuple))
elog(ERROR, "cache lookup failed for function %u", function_oid);
prosrcdatum = SysCacheGetAttr(PROCOID, proctuple, Anum_pg_proc_prosrc, &isnull);
if (isnull)
elog(ERROR, "null prosrc");
sourcecode = pstrdup(DatumGetCString(DirectFunctionCall1(textout,
prosrcdatum)));
/* allow one blank line at the start */
if (sourcecode[0] == '\n')
sourcecode++;
numargs = get_func_arg_info(proctuple,
&argtypes, &argnames, &argmodes);
if (numargs < 1)
ereport(ERROR,
(errmsg("XSLT function must have at least one argument")));
if (argtypes[0] != XMLOID)
ereport(ERROR,
(errmsg("first argument of XSLT function must have type XML")));
#if 0
xsltSetGenericErrorFunc(NULL, xmlGenericError);
#endif
ssdoc = xmlParseDoc((xmlChar *) sourcecode); /* XXX use backend's xml_parse here() */
stylesheet = xsltParseStylesheetDoc(ssdoc); /* XXX check error handling */
if (!stylesheet)
ereport(ERROR,
(errmsg("could not parse stylesheet")));
pg_proc_entry = (Form_pg_proc) GETSTRUCT(proctuple);
{
char *method;
method = (char *) stylesheet->method;
/*
* TODO: This is strictly speaking not correct because the
* default output method may be "html", but that can only
* detected at run time, so punt for now.
*/
if (!method)
method = "xml";
if (strcmp(method, "xml") == 0 && pg_proc_entry->prorettype != XMLOID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("XSLT stylesheet has output method \"xml\" but return type of function is not xml")));
else if ((strcmp(method, "html") == 0 || strcmp(method, "text") == 0)
&& pg_proc_entry->prorettype != TEXTOID && pg_proc_entry->prorettype != VARCHAROID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("XSLT stylesheet has output method \"%s\" but return type of function is not text or varchar", method)));
}
/* validation stops here */
if (!execute)
{
ReleaseSysCache(proctuple);
PG_RETURN_VOID();
}
/* execution begins here */
xslt_params = palloc(((numargs - 1) * 2 + 1) * sizeof(*xslt_params));
//.........这里部分代码省略.........
开发者ID:petere,项目名称:plxslt,代码行数:101,代码来源:plxslt.c
示例7: hstore_from_arrays
Datum
hstore_from_arrays(PG_FUNCTION_ARGS)
{
int4 buflen;
HStore *out;
Pairs *pairs;
Datum *key_datums;
bool *key_nulls;
int key_count;
Datum *value_datums;
bool *value_nulls;
int value_count;
ArrayType *key_array;
ArrayType *value_array;
int i;
if (PG_ARGISNULL(0))
PG_RETURN_NULL();
key_array = PG_GETARG_ARRAYTYPE_P(0);
Assert(ARR_ELEMTYPE(key_array) == TEXTOID);
/*
* must check >1 rather than != 1 because empty arrays have 0 dimensions,
* not 1
*/
if (ARR_NDIM(key_array) > 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts")));
deconstruct_array(key_array,
TEXTOID, -1, false, 'i',
&key_datums, &key_nulls, &key_count);
/* value_array might be NULL */
if (PG_ARGISNULL(1))
{
value_array = NULL;
value_count = key_count;
value_datums = NULL;
value_nulls = NULL;
}
else
{
value_array = PG_GETARG_ARRAYTYPE_P(1);
Assert(ARR_ELEMTYPE(value_array) == TEXTOID);
if (ARR_NDIM(value_array) > 1)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts")));
if ((ARR_NDIM(key_array) > 0 || ARR_NDIM(value_array) > 0) &&
(ARR_NDIM(key_array) != ARR_NDIM(value_array) ||
ARR_DIMS(key_array)[0] != ARR_DIMS(value_array)[0] ||
ARR_LBOUND(key_array)[0] != ARR_LBOUND(value_array)[0]))
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("arrays must have same bounds")));
deconstruct_array(value_array,
TEXTOID, -1, false, 'i',
&value_datums, &value_nulls, &value_count);
Assert(key_count == value_count);
}
pairs = palloc(key_count * sizeof(Pairs));
for (i = 0; i < key_count; ++i)
{
if (key_nulls[i])
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("null value not allowed for hstore key")));
if (!value_nulls || value_nulls[i])
{
pairs[i].key = VARDATA_ANY(key_datums[i]);
pairs[i].val = NULL;
pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
pairs[i].vallen = 4;
pairs[i].isnull = true;
pairs[i].needfree = false;
}
else
{
pairs[i].key = VARDATA_ANY(key_datums[i]);
pairs[i].val = VARDATA_ANY(value_datums[i]);
pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i]));
pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i]));
pairs[i].isnull = false;
pairs[i].needfree = false;
}
}
//.........这里部分代码省略.........
//.........这里部分代码省略.........
* the same as what freelist.c does now...)
*/
trycounter = NLocBuffer;
for (;;)
{
b = nextFreeLocalBuf;
if (++nextFreeLocalBuf >= NLocBuffer)
nextFreeLocalBuf = 0;
bufHdr = GetLocalBufferDescriptor(b);
if (LocalRefCount[b] == 0)
{
buf_state = pg_atomic_read_u32(&bufHdr->state);
if (BUF_STATE_GET_USAGECOUNT(buf_state) > 0)
{
buf_state -= BUF_USAGECOUNT_ONE;
pg_atomic_write_u32(&bufHdr->state, buf_state);
trycounter = NLocBuffer;
}
else
{
/* Found a usable buffer */
LocalRefCount[b]++;
ResourceOwnerRememberBuffer(CurrentResourceOwner,
BufferDescriptorGetBuffer(bufHdr));
break;
}
}
else if (--trycounter == 0)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("no empty local buffer available")));
}
/*
* this buffer is not referenced but it might still be dirty. if that's
* the case, write it out before reusing it!
*/
if (buf_state & BM_DIRTY)
{
SMgrRelation oreln;
Page localpage = (char *) LocalBufHdrGetBlock(bufHdr);
/* Find smgr relation for buffer */
oreln = smgropen(bufHdr->tag.rnode, MyBackendId);
PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);
/* And write... */
smgrwrite(oreln,
bufHdr->tag.forkNum,
bufHdr->tag.blockNum,
localpage,
false);
/* Mark not-dirty now in case we error out below */
buf_state &= ~BM_DIRTY;
pg_atomic_write_u32(&bufHdr->state, buf_state);
pgBufferUsage.local_blks_written++;
}
/*
/*
* Read a message from the specified connection carrying pid numbers
* of transactions interacting with pooler
*/
int
pool_recvpids(PoolPort *port, int **pids)
{
int r, i;
uint n32;
char buf[SEND_PID_BUFFER_SIZE];
/*
* Buffer size is upper bounded by the maximum number of connections,
* as in the pooler each connection has one Pooler Agent.
*/
r = recv(Socket(*port), &buf, SEND_PID_BUFFER_SIZE, 0);
if (r < 0)
{
/*
* Report broken connection
*/
ereport(ERROR,
(errcode_for_socket_access(),
errmsg("could not receive data from client: %m")));
goto failure;
}
else if (r == 0)
{
goto failure;
}
else if (r != SEND_PID_BUFFER_SIZE)
{
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("incomplete message from client")));
goto failure;
}
/* Verify response */
if (buf[0] != 'p')
{
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("unexpected message code")));
goto failure;
}
memcpy(&n32, buf + 1, 4);
n32 = ntohl(n32);
if (n32 == 0)
{
elog(WARNING, "No transaction to abort");
return n32;
}
*pids = (int *) palloc(sizeof(int) * n32);
for (i = 0; i < n32; i++)
{
int n;
memcpy(&n, buf + 5 + i * sizeof(int), sizeof(int));
*pids[i] = ntohl(n);
}
return n32;
failure:
return 0;
}
/*
* Helper function for pgp_armor. Converts arrays of keys and values into
* plain C arrays, and checks that they don't contain invalid characters.
*/
static int
parse_key_value_arrays(ArrayType *key_array, ArrayType *val_array,
char ***p_keys, char ***p_values)
{
int nkdims = ARR_NDIM(key_array);
int nvdims = ARR_NDIM(val_array);
char **keys,
**values;
Datum *key_datums,
*val_datums;
bool *key_nulls,
*val_nulls;
int key_count,
val_count;
int i;
if (nkdims > 1 || nkdims != nvdims)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("wrong number of array subscripts")));
if (nkdims == 0)
return 0;
deconstruct_array(key_array,
TEXTOID, -1, false, 'i',
&key_datums, &key_nulls, &key_count);
deconstruct_array(val_array,
TEXTOID, -1, false, 'i',
&val_datums, &val_nulls, &val_count);
if (key_count != val_count)
ereport(ERROR,
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
errmsg("mismatched array dimensions")));
keys = (char **) palloc(sizeof(char *) * key_count);
values = (char **) palloc(sizeof(char *) * val_count);
for (i = 0; i < key_count; i++)
{
char *v;
/* Check that the key doesn't contain anything funny */
if (key_nulls[i])
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("null value not allowed for header key")));
v = TextDatumGetCString(key_datums[i]);
if (!string_is_ascii(v))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("header key must not contain non-ASCII characters")));
if (strstr(v, ": "))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("header key must not contain \": \"")));
if (strchr(v, '\n'))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("header key must not contain newlines")));
keys[i] = v;
/* And the same for the value */
if (val_nulls[i])
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("null value not allowed for header value")));
v = TextDatumGetCString(val_datums[i]);
if (!string_is_ascii(v))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("header value must not contain non-ASCII characters")));
if (strchr(v, '\n'))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("header value must not contain newlines")));
values[i] = v;
}
*p_keys = keys;
*p_values = values;
return key_count;
}
/*
* pgfdw_xact_callback --- cleanup at main-transaction end.
*/
static void
pgfdw_xact_callback(XactEvent event, void *arg)
{
HASH_SEQ_STATUS scan;
ConnCacheEntry *entry;
/* Quick exit if no connections were touched in this transaction. */
if (!xact_got_connection)
return;
/*
* Scan all connection cache entries to find open remote transactions, and
* close them.
*/
hash_seq_init(&scan, ConnectionHash);
while ((entry = (ConnCacheEntry *) hash_seq_search(&scan)))
{
Jresult *res;
/* Ignore cache entry if no open connection right now */
if (entry->conn == NULL)
continue;
/* If it has an open remote transaction, try to close it */
if (entry->xact_depth > 0)
{
elog(DEBUG3, "closing remote transaction on connection %p",
entry->conn);
switch (event)
{
case XACT_EVENT_PRE_COMMIT:
/* Commit all remote transactions during pre-commit */
do_sql_command(entry->conn, "COMMIT TRANSACTION");
/*
* If there were any errors in subtransactions, and we
* made prepared statements, do a DEALLOCATE ALL to make
* sure we get rid of all prepared statements. This is
* annoying and not terribly bulletproof, but it's
* probably not worth trying harder.
*
* DEALLOCATE ALL only exists in 8.3 and later, so this
* constrains how old a server jdbc2_fdw can
* communicate with. We intentionally ignore errors in
* the DEALLOCATE, so that we can hobble along to some
* extent with older servers (leaking prepared statements
* as we go; but we don't really support update operations
* pre-8.3 anyway).
*/
if (entry->have_prep_stmt && entry->have_error)
{
res = JQexec(entry->conn, "DEALLOCATE ALL");
JQclear(res);
}
entry->have_prep_stmt = false;
entry->have_error = false;
break;
case XACT_EVENT_PRE_PREPARE:
/*
* We disallow remote transactions that modified anything,
* since it's not very reasonable to hold them open until
* the prepared transaction is committed. For the moment,
* throw error unconditionally; later we might allow
* read-only cases. Note that the error will cause us to
* come right back here with event == XACT_EVENT_ABORT, so
* we'll clean up the connection state at that point.
*/
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot prepare a transaction that modified remote tables")));
break;
case XACT_EVENT_COMMIT:
case XACT_EVENT_PREPARE:
/* Pre-commit should have closed the open transaction */
elog(ERROR, "missed cleaning up connection during pre-commit");
break;
case XACT_EVENT_ABORT:
/* Assume we might have lost track of prepared statements */
entry->have_error = true;
/* If we're aborting, abort all remote transactions too */
res = JQexec(entry->conn, "ABORT TRANSACTION");
/* Note: can't throw ERROR, it would be infinite loop */
if (JQresultStatus(res) != PGRES_COMMAND_OK)
pgfdw_report_error(WARNING, res, entry->conn, true,
"ABORT TRANSACTION");
else
{
JQclear(res);
/* As above, make sure to clear any prepared stmts */
if (entry->have_prep_stmt && entry->have_error)
{
res = JQexec(entry->conn, "DEALLOCATE ALL");
JQclear(res);
}
entry->have_prep_stmt = false;
//.........这里部分代码省略.........
/* This function accepts an array, and returns one item for each entry in the array */
Datum
int_enum(PG_FUNCTION_ARGS)
{
PGARRAY *p = (PGARRAY *) PG_GETARG_POINTER(0);
CTX *pc;
ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
if (!rsi || !IsA(rsi, ReturnSetInfo))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("int_enum called in context that cannot accept a set")));
if (!p)
{
elog(WARNING, "no data sent");
PG_RETURN_NULL();
}
if (!fcinfo->flinfo->fn_extra)
{
/* Allocate working state */
MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
pc = (CTX *) palloc(sizeof(CTX));
/* Don't copy attribute if you don't need to */
if (VARATT_IS_EXTENDED(p))
{
/* Toasted!!! */
pc->p = (PGARRAY *) PG_DETOAST_DATUM_COPY(p);
pc->flags = TOASTED;
}
else
{
/* Untoasted */
pc->p = p;
pc->flags = 0;
}
/* Now that we have a detoasted array, verify dimensions */
if (pc->p->a.ndim != 1)
elog(ERROR, "int_enum only accepts 1-D arrays");
pc->num = 0;
fcinfo->flinfo->fn_extra = (void *) pc;
MemoryContextSwitchTo(oldcontext);
}
else /* use existing working state */
pc = (CTX *) fcinfo->flinfo->fn_extra;
/* Are we done yet? */
if (pc->num >= pc->p->items)
{
/* We are done */
if (pc->flags & TOASTED)
pfree(pc->p);
pfree(pc);
fcinfo->flinfo->fn_extra = NULL;
rsi->isDone = ExprEndResult;
}
else
{
/* nope, return the next value */
int val = pc->p->array[pc->num++];
rsi->isDone = ExprMultipleResult;
PG_RETURN_INT32(val);
}
PG_RETURN_NULL();
}
请发表评论