forked from huawei/openGauss-server
!134 Support autonomous transaction
Merge pull request !134 from 江建宇/master
This commit is contained in:
commit
4728c27ca8
|
|
@ -59,6 +59,7 @@ void initStringInfo(StringInfo str)
|
|||
*/
|
||||
void resetStringInfo(StringInfo str)
|
||||
{
|
||||
|
||||
str->data[0] = '\0';
|
||||
str->len = 0;
|
||||
str->cursor = 0;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,6 @@ ifneq "$(MAKECMDGOALS)" "clean"
|
|||
endif
|
||||
endif
|
||||
OBJS = be-fsstubs.o be-secure.o auth.o crypt.o hba.o ip.o md5.o sha2.o pqcomm.o \
|
||||
pqformat.o pqsignal.o
|
||||
pqformat.o pqsignal.o pqmq.o
|
||||
|
||||
include $(top_srcdir)/src/gausskernel/common.mk
|
||||
|
|
|
|||
|
|
@ -145,6 +145,28 @@ static int Lock_AF_UNIX(unsigned short portNumber, const char* unixSocketName, b
|
|||
static int Setup_AF_UNIX(bool is_create_psql_sock);
|
||||
#endif /* HAVE_UNIX_SOCKETS */
|
||||
|
||||
static void socket_comm_reset(void);
|
||||
static int socket_flush(void);
|
||||
static int socket_flush_if_writable(void);
|
||||
static bool socket_is_send_pending(void);
|
||||
static int socket_putmessage(char msgtype, const char *s, size_t len);
|
||||
static int socket_putmessage_noblock(char msgtype, const char *s, size_t len);
|
||||
static void socket_startcopyout(void);
|
||||
static void socket_endcopyout(bool errorAbort);
|
||||
|
||||
static PQcommMethods PqCommSocketMethods = {
|
||||
socket_comm_reset,
|
||||
socket_flush,
|
||||
socket_flush_if_writable,
|
||||
socket_is_send_pending,
|
||||
socket_putmessage,
|
||||
socket_putmessage_noblock,
|
||||
socket_startcopyout,
|
||||
socket_endcopyout
|
||||
};
|
||||
|
||||
THR_LOCAL PQcommMethods *PqCommMethods = &PqCommSocketMethods;
|
||||
|
||||
extern bool FencedUDFMasterMode;
|
||||
|
||||
/* --------------------------------
|
||||
|
|
@ -458,7 +480,7 @@ void pq_init(void)
|
|||
* inside a pqcomm.c routine (which ideally will never happen, but...)
|
||||
* --------------------------------
|
||||
*/
|
||||
void pq_comm_reset(void)
|
||||
static void socket_comm_reset(void)
|
||||
{
|
||||
/* Do not throw away pending data, but do reset the busy flag */
|
||||
t_thrd.libpq_cxt.PqCommBusy = false;
|
||||
|
|
@ -1612,7 +1634,7 @@ static int internal_putbytes(const char* s, size_t len)
|
|||
* returns 0 if OK, EOF if trouble
|
||||
* --------------------------------
|
||||
*/
|
||||
int pq_flush(void)
|
||||
static int socket_flush(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
|
|
@ -1769,7 +1791,7 @@ static int internal_flush(void)
|
|||
* Returns 0 if OK, or EOF if trouble.
|
||||
* --------------------------------
|
||||
*/
|
||||
int pq_flush_if_writable(void)
|
||||
static int socket_flush_if_writable(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
|
|
@ -1868,7 +1890,7 @@ void pq_flush_timedwait(int timeout)
|
|||
* pq_is_send_pending - is there any pending data in the output buffer?
|
||||
* --------------------------------
|
||||
*/
|
||||
bool pq_is_send_pending(void)
|
||||
static bool socket_is_send_pending(void)
|
||||
{
|
||||
return (t_thrd.libpq_cxt.PqSendStart < t_thrd.libpq_cxt.PqSendPointer);
|
||||
}
|
||||
|
|
@ -1905,7 +1927,7 @@ bool pq_is_send_pending(void)
|
|||
* returns 0 if OK, EOF if trouble
|
||||
* --------------------------------
|
||||
*/
|
||||
int pq_putmessage(char msgtype, const char* s, size_t len)
|
||||
static int socket_putmessage(char msgtype, const char* s, size_t len)
|
||||
{
|
||||
if (t_thrd.libpq_cxt.DoingCopyOut || t_thrd.libpq_cxt.PqCommBusy) {
|
||||
return 0;
|
||||
|
|
@ -1941,7 +1963,7 @@ fail:
|
|||
* If the output buffer is too small to hold the message, the buffer
|
||||
* is enlarged.
|
||||
*/
|
||||
int pq_putmessage_noblock(char msgtype, const char* s, size_t len)
|
||||
static int socket_putmessage_noblock(char msgtype, const char* s, size_t len)
|
||||
{
|
||||
int res;
|
||||
int required;
|
||||
|
|
@ -1967,7 +1989,7 @@ int pq_putmessage_noblock(char msgtype, const char* s, size_t len)
|
|||
* is beginning
|
||||
* --------------------------------
|
||||
*/
|
||||
void pq_startcopyout(void)
|
||||
static void socket_startcopyout(void)
|
||||
{
|
||||
t_thrd.libpq_cxt.DoingCopyOut = true;
|
||||
}
|
||||
|
|
@ -1982,7 +2004,7 @@ void pq_startcopyout(void)
|
|||
* not allow binary transfers, so a textual terminator is always correct.
|
||||
* --------------------------------
|
||||
*/
|
||||
void pq_endcopyout(bool errorAbort)
|
||||
static void socket_endcopyout(bool errorAbort)
|
||||
{
|
||||
if (!t_thrd.libpq_cxt.DoingCopyOut) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -610,6 +610,34 @@ const char* pq_getmsgstring(StringInfo msg)
|
|||
return pg_client_to_server(str, slen);
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* pq_getmsgrawstring - get a null-terminated text string - NO conversion
|
||||
*
|
||||
* Returns a pointer directly into the message buffer.
|
||||
* --------------------------------
|
||||
*/
|
||||
const char *pq_getmsgrawstring(StringInfo msg)
|
||||
{
|
||||
char *str;
|
||||
int slen;
|
||||
|
||||
str = &msg->data[msg->cursor];
|
||||
|
||||
/*
|
||||
* It's safe to use strlen() here because a StringInfo is guaranteed to
|
||||
* have a trailing null byte. But check we found a null inside the
|
||||
* message.
|
||||
*/
|
||||
slen = strlen(str);
|
||||
if (msg->cursor + slen >= msg->len)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||
errmsg("invalid string in message")));
|
||||
msg->cursor += slen + 1;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
* pq_getmsgend - verify message fully consumed
|
||||
* --------------------------------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,277 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pqmq.cpp
|
||||
* Use the frontend/backend protocol for communication over a shm_mq
|
||||
*
|
||||
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/common/backend/libpq/pqmq.cpp
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "libpq/libpq.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "libpq/pqmq.h"
|
||||
#include "miscadmin.h"
|
||||
#include "pgstat.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
static THR_LOCAL shm_mq *pq_mq;
|
||||
static THR_LOCAL shm_mq_handle *pq_mq_handle;
|
||||
static THR_LOCAL bool pq_mq_busy = false;
|
||||
static THR_LOCAL ThreadId pq_mq_parallel_master_pid = 0;
|
||||
static THR_LOCAL BackendId pq_mq_parallel_master_backend_id = InvalidBackendId;
|
||||
|
||||
static void mq_comm_reset(void);
|
||||
static int mq_flush(void);
|
||||
static int mq_flush_if_writable(void);
|
||||
static bool mq_is_send_pending(void);
|
||||
static int mq_putmessage(char msgtype, const char *s, size_t len);
|
||||
static int mq_putmessage_noblock(char msgtype, const char *s, size_t len);
|
||||
static void mq_startcopyout(void);
|
||||
static void mq_endcopyout(bool errorAbort);
|
||||
|
||||
static THR_LOCAL PQcommMethods PqCommMqMethods = {
|
||||
mq_comm_reset,
|
||||
mq_flush,
|
||||
mq_flush_if_writable,
|
||||
mq_is_send_pending,
|
||||
mq_putmessage,
|
||||
mq_putmessage_noblock,
|
||||
mq_startcopyout,
|
||||
mq_endcopyout
|
||||
};
|
||||
|
||||
static THR_LOCAL PQcommMethods *save_PqCommMethods;
|
||||
static THR_LOCAL CommandDest save_whereToSendOutput;
|
||||
static THR_LOCAL ProtocolVersion save_FrontendProtocol;
|
||||
|
||||
/*
|
||||
* Arrange to redirect frontend/backend protocol messages to a message queue.
|
||||
*/
|
||||
void pq_redirect_to_shm_mq(shm_mq_handle *mqh)
|
||||
{
|
||||
save_PqCommMethods = PqCommMethods;
|
||||
save_whereToSendOutput = CommandDest(t_thrd.postgres_cxt.whereToSendOutput);
|
||||
save_FrontendProtocol = FrontendProtocol;
|
||||
|
||||
PqCommMethods = &PqCommMqMethods;
|
||||
pq_mq_handle = mqh;
|
||||
t_thrd.postgres_cxt.whereToSendOutput = static_cast<int>(DestRemote);
|
||||
FrontendProtocol = PG_PROTOCOL_LATEST;
|
||||
}
|
||||
|
||||
void pq_stop_redirect_to_shm_mq(void)
|
||||
{
|
||||
PqCommMethods = save_PqCommMethods;
|
||||
t_thrd.postgres_cxt.whereToSendOutput = static_cast<int>(save_whereToSendOutput);
|
||||
FrontendProtocol = save_FrontendProtocol;
|
||||
pq_mq = NULL;
|
||||
pq_mq_handle = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arrange to SendProcSignal() to the parallel master each time we transmit
|
||||
* message data via the shm_mq.
|
||||
*/
|
||||
void pq_set_parallel_master(ThreadId pid, BackendId backend_id)
|
||||
{
|
||||
Assert(PqCommMethods == &PqCommMqMethods);
|
||||
pq_mq_parallel_master_pid = pid;
|
||||
pq_mq_parallel_master_backend_id = backend_id;
|
||||
}
|
||||
|
||||
static void mq_comm_reset(void)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
static int mq_flush(void)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mq_flush_if_writable(void)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mq_is_send_pending(void)
|
||||
{
|
||||
/* There's never anything pending. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transmit a libpq protocol message to the shared memory message queue
|
||||
* selected via pq_mq_handle. We don't include a length word, because the
|
||||
* receiver will know the length of the message from shm_mq_receive().
|
||||
*/
|
||||
static int mq_putmessage(char msgtype, const char *s, size_t len)
|
||||
{
|
||||
shm_mq_iovec iov[2];
|
||||
shm_mq_result result;
|
||||
|
||||
/*
|
||||
* If we're sending a message, and we have to wait because the queue is
|
||||
* full, and then we get interrupted, and that interrupt results in trying
|
||||
* to send another message, we respond by detaching the queue. There's no
|
||||
* way to return to the original context, but even if there were, just
|
||||
* queueing the message would amount to indefinitely postponing the
|
||||
* response to the interrupt. So we do this instead.
|
||||
*/
|
||||
if (pq_mq_busy) {
|
||||
if (pq_mq_handle != NULL)
|
||||
shm_mq_detach(pq_mq_handle);
|
||||
pq_mq_handle = NULL;
|
||||
return EOF;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the message queue is already gone, just ignore the message. This
|
||||
* doesn't necessarily indicate a problem; for example, DEBUG messages can
|
||||
* be generated late in the shutdown sequence, after all DSMs have already
|
||||
* been detached.
|
||||
*/
|
||||
if (pq_mq_handle == NULL)
|
||||
return 0;
|
||||
|
||||
pq_mq_busy = true;
|
||||
|
||||
iov[0].data = &msgtype;
|
||||
iov[0].len = 1;
|
||||
iov[1].data = s;
|
||||
iov[1].len = len;
|
||||
|
||||
Assert(pq_mq_handle != NULL);
|
||||
|
||||
for (;;) {
|
||||
result = shm_mq_sendv(pq_mq_handle, iov, 2, true);
|
||||
|
||||
if (pq_mq_parallel_master_pid != 0)
|
||||
(void)SendProcSignal(pq_mq_parallel_master_pid,PROCSIG_PARALLEL_MESSAGE,
|
||||
pq_mq_parallel_master_backend_id);
|
||||
|
||||
if (result != SHM_MQ_WOULD_BLOCK)
|
||||
break;
|
||||
|
||||
(void)WaitLatch(&t_thrd.proc->procLatch, WL_LATCH_SET, 0);
|
||||
ResetLatch(&t_thrd.proc->procLatch);
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
}
|
||||
|
||||
pq_mq_busy = false;
|
||||
|
||||
Assert(result == SHM_MQ_SUCCESS || result == SHM_MQ_DETACHED);
|
||||
if (result != SHM_MQ_SUCCESS)
|
||||
return EOF;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mq_putmessage_noblock(char msgtype, const char *s, size_t len)
|
||||
{
|
||||
/*
|
||||
* While the shm_mq machinery does support sending a message in
|
||||
* non-blocking mode, there's currently no way to try sending beginning to
|
||||
* send the message that doesn't also commit us to completing the
|
||||
* transmission. This could be improved in the future, but for now we
|
||||
* don't need it.
|
||||
*/
|
||||
elog(ERROR, "not currently supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mq_startcopyout(void)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
static void mq_endcopyout(bool errorAbort)
|
||||
{
|
||||
/* Nothing to do. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse an ErrorResponse or NoticeResponse payload and populate an ErrorData
|
||||
* structure with the results.
|
||||
*/
|
||||
void pq_parse_errornotice(StringInfo msg, ErrorData *edata)
|
||||
{
|
||||
/* Initialize edata with reasonable defaults. */
|
||||
errno_t rc = memset_s(edata, sizeof(ErrorData), 0, sizeof(ErrorData));
|
||||
securec_check(rc, "\0", "\0");
|
||||
edata->elevel = ERROR;
|
||||
|
||||
/* Loop over fields and extract each one. */
|
||||
for (;;) {
|
||||
char code = pq_getmsgbyte(msg);
|
||||
const char *value = NULL;
|
||||
|
||||
if (code == '\0') {
|
||||
pq_getmsgend(msg);
|
||||
break;
|
||||
}
|
||||
value = pq_getmsgrawstring(msg);
|
||||
|
||||
switch (code) {
|
||||
case PG_DIAG_SEVERITY:
|
||||
/* ignore, trusting we'll get a nonlocalized version */
|
||||
break;
|
||||
case PG_DIAG_INTERNEL_ERRCODE:
|
||||
/* ignore */
|
||||
break;
|
||||
case PG_DIAG_MODULE_ID:
|
||||
/* It is always MOD_MAX */
|
||||
edata->mod_id = MOD_MAX;
|
||||
break;
|
||||
case PG_DIAG_SQLSTATE:
|
||||
if (strlen(value) != 5) {
|
||||
elog(ERROR, "invalid SQLSTATE: \"%s\"", value);
|
||||
}
|
||||
edata->sqlerrcode = MAKE_SQLSTATE(value[0], value[1], value[2],
|
||||
value[3], value[4]);
|
||||
break;
|
||||
case PG_DIAG_MESSAGE_PRIMARY:
|
||||
edata->message = pstrdup(value);
|
||||
break;
|
||||
case PG_DIAG_MESSAGE_DETAIL:
|
||||
edata->detail = pstrdup(value);
|
||||
break;
|
||||
case PG_DIAG_MESSAGE_HINT:
|
||||
edata->hint = pstrdup(value);
|
||||
break;
|
||||
case PG_DIAG_STATEMENT_POSITION:
|
||||
edata->cursorpos = pg_atoi(const_cast<char*>(value), sizeof(int), '\0');
|
||||
break;
|
||||
case PG_DIAG_INTERNAL_POSITION:
|
||||
edata->internalpos = pg_atoi(const_cast<char*>(value), sizeof(int), '\0');
|
||||
break;
|
||||
case PG_DIAG_INTERNAL_QUERY:
|
||||
edata->internalquery = pstrdup(value);
|
||||
break;
|
||||
case PG_DIAG_CONTEXT:
|
||||
edata->context = pstrdup(value);
|
||||
break;
|
||||
case PG_DIAG_SOURCE_FILE:
|
||||
edata->filename = pstrdup(value);
|
||||
break;
|
||||
case PG_DIAG_SOURCE_LINE:
|
||||
edata->lineno = pg_atoi(const_cast<char*>(value), sizeof(int), '\0');
|
||||
break;
|
||||
case PG_DIAG_SOURCE_FUNCTION:
|
||||
edata->funcname = pstrdup(value);
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "unrecognized error field code: %d", (int) code);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ Query* parse_analyze(
|
|||
* symbol datatypes from context. The passed-in paramTypes[] array can
|
||||
* be modified or enlarged (via repalloc).
|
||||
*/
|
||||
Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** paramTypes, int* numParams)
|
||||
Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** paramTypes, int* numParams, char** paramTypeNames)
|
||||
{
|
||||
ParseState* pstate = make_parsestate(NULL);
|
||||
Query* query = NULL;
|
||||
|
|
@ -167,7 +167,7 @@ Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** pa
|
|||
|
||||
pstate->p_sourcetext = sourceText;
|
||||
|
||||
parse_variable_parameters(pstate, paramTypes, numParams);
|
||||
parse_variable_parameters(pstate, paramTypes, numParams, paramTypeNames);
|
||||
|
||||
query = transformTopLevelStmt(pstate, parseTree);
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ typedef struct FixedParamState {
|
|||
typedef struct VarParamState {
|
||||
Oid** paramTypes; /* array of parameter type OIDs */
|
||||
int* numParams; /* number of array entries */
|
||||
char **paramTypeNames;
|
||||
} VarParamState;
|
||||
|
||||
static Node* fixed_paramref_hook(ParseState* pstate, ParamRef* pref);
|
||||
|
|
@ -54,6 +55,7 @@ static Node* variable_paramref_hook(ParseState* pstate, ParamRef* pref);
|
|||
static Node* variable_coerce_param_hook(
|
||||
ParseState* pstate, Param* param, Oid targetTypeId, int32 targetTypeMod, int location);
|
||||
static bool check_parameter_resolution_walker(Node* node, ParseState* pstate);
|
||||
static Node *variable_post_column_ref_hook(ParseState *pstate, ColumnRef *cref, Node *var);
|
||||
static bool query_contains_extern_params_walker(Node* node, void* context);
|
||||
|
||||
/*
|
||||
|
|
@ -73,17 +75,57 @@ void parse_fixed_parameters(ParseState* pstate, Oid* paramTypes, int numParams)
|
|||
/*
|
||||
* Set up to process a query containing references to variable parameters.
|
||||
*/
|
||||
void parse_variable_parameters(ParseState* pstate, Oid** paramTypes, int* numParams)
|
||||
void parse_variable_parameters(ParseState* pstate, Oid** paramTypes, int* numParams, char** paramTypeNames)
|
||||
{
|
||||
VarParamState* parstate = (VarParamState*)palloc(sizeof(VarParamState));
|
||||
|
||||
parstate->paramTypes = paramTypes;
|
||||
parstate->numParams = numParams;
|
||||
parstate->paramTypeNames = paramTypeNames;
|
||||
pstate->p_post_columnref_hook = variable_post_column_ref_hook;
|
||||
pstate->p_ref_hook_state = (void*)parstate;
|
||||
pstate->p_paramref_hook = variable_paramref_hook;
|
||||
pstate->p_coerce_param_hook = variable_coerce_param_hook;
|
||||
}
|
||||
|
||||
static Node * variable_post_column_ref_hook(ParseState *pstate, ColumnRef *cref, Node *var)
|
||||
{
|
||||
VarParamState *parstate = (VarParamState *) pstate->p_ref_hook_state;
|
||||
|
||||
/* already resolved */
|
||||
if (var != NULL)
|
||||
return NULL;
|
||||
|
||||
/* did not supply parameter names */
|
||||
if (!parstate->paramTypeNames)
|
||||
return NULL;
|
||||
|
||||
if (list_length(cref->fields) == 1)
|
||||
{
|
||||
Node *field1 = (Node *) linitial(cref->fields);
|
||||
char *name1;
|
||||
int i;
|
||||
Param *param;
|
||||
|
||||
Assert(IsA(field1, String));
|
||||
name1 = strVal(field1);
|
||||
for (i = 0; i < *parstate->numParams; i++)
|
||||
if (strcmp(name1, parstate->paramTypeNames[i]) == 0)
|
||||
{
|
||||
param = makeNode(Param);
|
||||
param->paramkind = PARAM_EXTERN;
|
||||
param->paramid = i + 1;
|
||||
param->paramtype = (*parstate->paramTypes)[i];
|
||||
param->paramtypmod = -1;
|
||||
param->paramcollid = InvalidOid;
|
||||
param->location = -1;
|
||||
return (Node *) param;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transform a ParamRef using fixed parameter types.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -526,8 +526,9 @@ void ResetLatch(volatile Latch* latch)
|
|||
*/
|
||||
void latch_sigusr1_handler(void)
|
||||
{
|
||||
if (waiting)
|
||||
if (waiting) {
|
||||
sendSelfPipeByte();
|
||||
}
|
||||
}
|
||||
|
||||
/* Send one byte to the self-pipe, to wake up WaitLatch */
|
||||
|
|
|
|||
|
|
@ -1638,6 +1638,63 @@ void FlushErrorStateWithoutDeleteChildrenContext(void)
|
|||
MemoryContextReset(ErrorContext);
|
||||
}
|
||||
|
||||
/*
|
||||
* ThrowErrorData --- report an error described by an ErrorData structure
|
||||
*
|
||||
* This is somewhat like ReThrowError, but it allows elevels besides ERROR,
|
||||
* and the boolean flags such as output_to_server are computed via the
|
||||
* default rules rather than being copied from the given ErrorData.
|
||||
* This is primarily used to re-report errors originally reported by
|
||||
* background worker processes and then propagated (with or without
|
||||
* modification) to the backend responsible for them.
|
||||
*/
|
||||
void
|
||||
ThrowErrorData(ErrorData *edata)
|
||||
{
|
||||
ErrorData *newedata;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
if (!errstart(edata->elevel, edata->filename, edata->lineno,
|
||||
edata->funcname, NULL))
|
||||
return; /* error is not to be reported at all */
|
||||
|
||||
newedata = &t_thrd.log_cxt.errordata[t_thrd.log_cxt.errordata_stack_depth];
|
||||
t_thrd.log_cxt.recursion_depth++;
|
||||
oldcontext = MemoryContextSwitchTo(ErrorContext);
|
||||
|
||||
/* Copy the supplied fields to the error stack entry. */
|
||||
if (edata->sqlerrcode != 0)
|
||||
newedata->sqlerrcode = edata->sqlerrcode;
|
||||
if (edata->message)
|
||||
newedata->message = pstrdup(edata->message);
|
||||
if (edata->detail)
|
||||
newedata->detail = pstrdup(edata->detail);
|
||||
if (edata->detail_log)
|
||||
newedata->detail_log = pstrdup(edata->detail_log);
|
||||
if (edata->hint)
|
||||
newedata->hint = pstrdup(edata->hint);
|
||||
if (edata->context)
|
||||
newedata->context = pstrdup(edata->context);
|
||||
/* assume message_id is not available */
|
||||
if (newedata->filename)
|
||||
newedata->filename = pstrdup(edata->filename);
|
||||
if (newedata->funcname)
|
||||
newedata->funcname = pstrdup(edata->funcname);
|
||||
if (newedata->backtrace_log)
|
||||
newedata->backtrace_log = pstrdup(edata->backtrace_log);
|
||||
|
||||
newedata->cursorpos = edata->cursorpos;
|
||||
newedata->internalpos = edata->internalpos;
|
||||
if (edata->internalquery)
|
||||
newedata->internalquery = pstrdup(edata->internalquery);
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
t_thrd.log_cxt.recursion_depth--;
|
||||
|
||||
/* Process the error. */
|
||||
errfinish(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ReThrowError --- re-throw a previously copied error
|
||||
*
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ THR_LOCAL object_access_hook_type object_access_hook = NULL;
|
|||
* These are initialized for the bootstrap/standalone case.
|
||||
*/
|
||||
THR_LOCAL bool IsUnderPostmaster = false;
|
||||
THR_LOCAL bool IsBackgroundWorker = false;
|
||||
|
||||
volatile ThreadId PostmasterPid = 0;
|
||||
bool IsPostmasterEnvironment = false;
|
||||
|
|
|
|||
|
|
@ -724,11 +724,11 @@ bool has_rolvcadmin(Oid role_id)
|
|||
/*
|
||||
* Initialize user identity during normal backend startup
|
||||
*/
|
||||
void InitializeSessionUserId(const char* role_name)
|
||||
void InitializeSessionUserId(const char* role_name, Oid role_id)
|
||||
{
|
||||
HeapTuple role_tup;
|
||||
Form_pg_authid rform;
|
||||
Oid role_id;
|
||||
char* rname = NULL;
|
||||
/* Audit user login */
|
||||
char details[PGAUDIT_MAXLENGTH];
|
||||
|
||||
|
|
@ -744,23 +744,33 @@ void InitializeSessionUserId(const char* role_name)
|
|||
AssertState(!OidIsValid(u_sess->misc_cxt.AuthenticatedUserId));
|
||||
}
|
||||
|
||||
role_tup = SearchSysCache1(AUTHNAME, PointerGetDatum(role_name));
|
||||
if (!HeapTupleIsValid(role_tup)) {
|
||||
/* Audit user login */
|
||||
int rcs = snprintf_truncated_s(details,
|
||||
sizeof(details),
|
||||
"login db(%s) failed-the role(%s)does not exist",
|
||||
u_sess->proc_cxt.MyProcPort->database_name,
|
||||
role_name);
|
||||
securec_check_ss(rcs, "", "");
|
||||
pgaudit_user_login(FALSE, u_sess->proc_cxt.MyProcPort->database_name, details);
|
||||
if (role_name != NULL) {
|
||||
role_tup = SearchSysCache1(AUTHNAME, PointerGetDatum(role_name));
|
||||
if (!HeapTupleIsValid(role_tup)) {
|
||||
/* Audit user login */
|
||||
int rcs = snprintf_truncated_s(details,
|
||||
sizeof(details),
|
||||
"login db(%s) failed-the role(%s)does not exist",
|
||||
u_sess->proc_cxt.MyProcPort->database_name,
|
||||
role_name);
|
||||
securec_check_ss(rcs, "", "");
|
||||
pgaudit_user_login(FALSE, u_sess->proc_cxt.MyProcPort->database_name, details);
|
||||
|
||||
ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
||||
errmsg("Invalid username/password,login denied.")));
|
||||
ereport(FATAL, (errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
||||
errmsg("Invalid username/password,login denied.")));
|
||||
}
|
||||
} else {
|
||||
role_tup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(role_id));
|
||||
if (!HeapTupleIsValid(role_tup)) {
|
||||
ereport(FATAL,
|
||||
(errcode(ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION),
|
||||
errmsg("role with OID %u does not exist", role_id)));
|
||||
}
|
||||
}
|
||||
|
||||
rform = (Form_pg_authid)GETSTRUCT(role_tup);
|
||||
role_id = HeapTupleGetOid(role_tup);
|
||||
rname = NameStr(rform->rolname);
|
||||
|
||||
u_sess->misc_cxt.AuthenticatedUserId = role_id;
|
||||
u_sess->misc_cxt.AuthenticatedUserIsSuperuser = rform->rolsuper;
|
||||
|
|
@ -832,10 +842,11 @@ void InitializeSessionUserIdStandalone(void)
|
|||
{
|
||||
/*
|
||||
* This function should only be called in single-user mode and in
|
||||
* autovacuum workers.
|
||||
* autovacuum workers, and in background workers.
|
||||
*/
|
||||
AssertState(!IsUnderPostmaster || IsAutoVacuumWorkerProcess() ||
|
||||
IsJobSchedulerProcess() || IsJobWorkerProcess() || AM_WAL_SENDER);
|
||||
IsJobSchedulerProcess() || IsJobWorkerProcess() || AM_WAL_SENDER ||
|
||||
IsBackgroundWorker);
|
||||
|
||||
/* In pooler stateless reuse mode, to reset session userid */
|
||||
if (!g_instance.attr.attr_network.PoolerStatelessReuse) {
|
||||
|
|
|
|||
|
|
@ -683,7 +683,7 @@ void PostgresResetUsernamePgoption(const char* username)
|
|||
u_sess->proc_cxt.MyProcPort->user_name = (char*)GetSuperUserName((char*)username);
|
||||
}
|
||||
|
||||
InitializeSessionUserId(username);
|
||||
InitializeSessionUserId(username, InvalidOid);
|
||||
am_superuser = superuser();
|
||||
u_sess->misc_cxt.CurrentUserName = u_sess->proc_cxt.MyProcPort->user_name;
|
||||
}
|
||||
|
|
@ -1059,6 +1059,7 @@ PostgresInitializer::PostgresInitializer()
|
|||
m_indbname = NULL;
|
||||
m_dboid = InvalidOid;
|
||||
m_username = NULL;
|
||||
m_useroid = InvalidOid;
|
||||
m_isSuperUser = false;
|
||||
m_fullpath = NULL;
|
||||
memset_s(m_dbname, NAMEDATALEN, 0, NAMEDATALEN);
|
||||
|
|
@ -1074,11 +1075,13 @@ PostgresInitializer::~PostgresInitializer()
|
|||
m_username = NULL;
|
||||
}
|
||||
|
||||
void PostgresInitializer::SetDatabaseAndUser(const char* in_dbname, Oid dboid, const char* username)
|
||||
void PostgresInitializer::SetDatabaseAndUser(
|
||||
const char* in_dbname, Oid dboid, const char* username, Oid useroid)
|
||||
{
|
||||
m_indbname = in_dbname;
|
||||
m_dboid = dboid;
|
||||
m_username = username;
|
||||
m_useroid = useroid;
|
||||
}
|
||||
|
||||
void PostgresInitializer::InitBootstrap()
|
||||
|
|
@ -1489,12 +1492,19 @@ void PostgresInitializer::InitSession()
|
|||
|
||||
StartXact();
|
||||
|
||||
if (IsUnderPostmaster) {
|
||||
CheckAuthentication();
|
||||
InitUser();
|
||||
} else {
|
||||
if (!IsUnderPostmaster) {
|
||||
CheckAtLeastOneRoles();
|
||||
SetSuperUserStandalone();
|
||||
} else if (IsBackgroundWorker) {
|
||||
if (m_username == NULL && !OidIsValid(m_useroid)) {
|
||||
InitializeSessionUserIdStandalone();
|
||||
m_isSuperUser = true;
|
||||
} else {
|
||||
InitUser();
|
||||
}
|
||||
} else {
|
||||
CheckAuthentication();
|
||||
InitUser();
|
||||
}
|
||||
|
||||
CheckConnPermission();
|
||||
|
|
@ -1626,7 +1636,7 @@ void PostgresInitializer::SetSuperUserAndDatabase()
|
|||
|
||||
void PostgresInitializer::InitUser()
|
||||
{
|
||||
InitializeSessionUserId(m_username);
|
||||
InitializeSessionUserId(m_username, m_useroid);
|
||||
m_isSuperUser = superuser();
|
||||
u_sess->misc_cxt.CurrentUserName = u_sess->proc_cxt.MyProcPort->user_name;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -198,6 +198,18 @@
|
|||
#define MAX_PASSWORD_ASSIGNED_CHARACTER 999
|
||||
/* max length of password */
|
||||
#define MAX_PASSWORD_LENGTH 999
|
||||
/*
|
||||
* Precision with which REAL type guc values are to be printed for GUC
|
||||
* serialization.
|
||||
*/
|
||||
static const int REALTYPE_PRECISION = 17;
|
||||
|
||||
static const int TYPICAL_LEN_RANGE_OF_VALUE = 1000;
|
||||
static const int MAX_DISPLAY_LEN_OF_BOOL = 5;
|
||||
static const int TYPICAL_DISPLAY_LEN_OF_INT = 4;
|
||||
static const int MAX_DISPLAY_LEN_OF_INT = 11;
|
||||
static const int MAX_DISPLAY_LEN_OF_INT64 = 20;
|
||||
static const int LEN_OF_REAL_EXCEPT_PRECISION = 8;
|
||||
|
||||
extern volatile int synchronous_commit;
|
||||
extern volatile bool most_available_sync;
|
||||
|
|
@ -459,6 +471,7 @@ static void assign_statistics_memory(int newval, void* extra);
|
|||
static void assign_history_memory(int newval, void* extra);
|
||||
static bool check_history_memory_limit(int* newval, void** extra, GucSource source);
|
||||
static bool check_autovacuum_max_workers(int* newval, void** extra, GucSource source);
|
||||
static bool check_max_worker_processes(int* newval, void** extra, GucSource source);
|
||||
static bool check_job_max_workers(int* newval, void** extra, GucSource source);
|
||||
static bool check_effective_io_concurrency(int* newval, void** extra, GucSource source);
|
||||
static void assign_effective_io_concurrency(int newval, void* extra);
|
||||
|
|
@ -7084,6 +7097,23 @@ static void init_configure_names_int()
|
|||
NULL,
|
||||
NULL
|
||||
},
|
||||
{
|
||||
/* see max_connections */
|
||||
{
|
||||
"max_background_workers",
|
||||
PGC_POSTMASTER,
|
||||
RESOURCES_ASYNCHRONOUS,
|
||||
gettext_noop("Maximum number of concurrent background worker processes."),
|
||||
NULL
|
||||
},
|
||||
&g_instance.attr.attr_storage.max_background_workers,
|
||||
8,
|
||||
0,
|
||||
MAX_BACKENDS,
|
||||
check_max_worker_processes,
|
||||
NULL,
|
||||
NULL
|
||||
},
|
||||
{
|
||||
{
|
||||
"job_queue_processes",
|
||||
|
|
@ -17763,6 +17793,542 @@ ArrayType* GUCArrayReset(ArrayType* array)
|
|||
return newarray;
|
||||
}
|
||||
|
||||
/* GUC serialization */
|
||||
static bool CanSkipGucvar(const struct config_generic* gconf);
|
||||
static Size EstimateVariableSize(const struct config_generic* gconf);
|
||||
static void DoSerialize(char** destptr, Size& maxbytes, const char* fmt, ...);
|
||||
static void DoSerializeBinary(char** destptr, Size& maxbytes, const char* val, Size valsize);
|
||||
static void SerializeVariable(char** destptr, Size& maxbytes, const struct config_generic* gconf);
|
||||
static void InitializeOneGUCOption(struct config_generic& gconf);
|
||||
static char* ReadGucstate(char** srcptr, const char* srcend);
|
||||
static void ReadGucstateBinary(char** srcptr, const char* srcend, char* dest, Size size);
|
||||
|
||||
/*
|
||||
* CanSkipGucvar:
|
||||
* When serializing, determine whether to skip this GUC. When restoring, the
|
||||
* negation of this test determines whether to restore the compiled-in default
|
||||
* value before processing serialized values.
|
||||
*
|
||||
* A PGC_S_DEFAULT setting on the serialize side will typically match new
|
||||
* postmaster children, but that can be false when got_SIGHUP == true and the
|
||||
* pending configuration change modifies this setting. Nonetheless, we omit
|
||||
* PGC_S_DEFAULT settings from serialization and make up for that by restoring
|
||||
* defaults before applying serialized values.
|
||||
*
|
||||
* PGC_POSTMASTER variables always have the same value in every child of a
|
||||
* particular postmaster. Most PGC_INTERNAL variables are compile-time
|
||||
* constants; a few, like server_encoding and lc_ctype, are handled specially
|
||||
* outside the serialize/restore procedure. Therefore, SerializeGUCState()
|
||||
* never sends these, and RestoreGUCState() never changes them.
|
||||
*/
|
||||
static bool CanSkipGucvar(const struct config_generic* gconf)
|
||||
{
|
||||
return gconf->context == PGC_POSTMASTER ||
|
||||
gconf->context == PGC_INTERNAL || gconf->source == PGC_S_DEFAULT ||
|
||||
strcmp(gconf->name, "role") == 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* EstimateVariableSize:
|
||||
* Estimate max size for dumping the given GUC variable.
|
||||
*/
|
||||
static Size EstimateVariableSize(const struct config_generic* gconf)
|
||||
{
|
||||
Size size;
|
||||
Size valsize = 0;
|
||||
|
||||
if (CanSkipGucvar(gconf)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size = strlen(gconf->name) + 1;
|
||||
|
||||
/* Get the maximum display length of the GUC value. */
|
||||
switch (gconf->vartype) {
|
||||
case PGC_BOOL: {
|
||||
valsize = MAX_DISPLAY_LEN_OF_BOOL;
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_INT: {
|
||||
const struct config_int* conf = (const struct config_int*)gconf;
|
||||
|
||||
/*
|
||||
* Instead of getting the exact display length, use max
|
||||
* length. Also reduce the max length for typical ranges of
|
||||
* small values. Maximum value is 2147483647, i.e. 10 chars.
|
||||
* Include one byte for sign.
|
||||
*/
|
||||
if (Abs(*conf->variable) < TYPICAL_LEN_RANGE_OF_VALUE) {
|
||||
valsize = TYPICAL_DISPLAY_LEN_OF_INT;
|
||||
} else {
|
||||
valsize = MAX_DISPLAY_LEN_OF_INT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_INT64: {
|
||||
const struct config_int* conf = (const struct config_int*)gconf;
|
||||
|
||||
if (Abs(*conf->variable) < TYPICAL_LEN_RANGE_OF_VALUE) {
|
||||
valsize = TYPICAL_DISPLAY_LEN_OF_INT;
|
||||
} else {
|
||||
valsize = MAX_DISPLAY_LEN_OF_INT64; /* Maximum value is 9,223,372,036,854,775,807, i.e. 19 chars. */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_REAL: {
|
||||
/*
|
||||
* We are going to print it with %.17g. Account for sign,
|
||||
* decimal point, and e+nnn notation. E.g.
|
||||
* -3.9932904234000002e+110
|
||||
*/
|
||||
valsize = LEN_OF_REAL_EXCEPT_PRECISION + REALTYPE_PRECISION;
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_STRING: {
|
||||
const struct config_string *conf = (const struct config_string*)gconf;
|
||||
/*
|
||||
* If the value is NULL, we transmit it as an empty string.
|
||||
* Although this is not physically the same value, GUC
|
||||
* generally treats a NULL the same as empty string.
|
||||
*/
|
||||
if (*conf->variable) {
|
||||
valsize = strlen(*conf->variable);
|
||||
} else {
|
||||
valsize = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_ENUM: {
|
||||
struct config_enum* conf = (struct config_enum*) gconf;
|
||||
valsize = strlen(config_enum_lookup_by_value(conf, *conf->variable));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Allow space for terminating zero-byte */
|
||||
size = add_size(size, valsize + 1);
|
||||
|
||||
if (gconf->sourcefile) {
|
||||
size = add_size(size, strlen(gconf->sourcefile));
|
||||
}
|
||||
|
||||
/* Allow space for terminating zero-byte */
|
||||
size = add_size(size, 1);
|
||||
|
||||
/* Include line whenever we include file. */
|
||||
if (gconf->sourcefile && gconf->sourcefile[0]) {
|
||||
size = add_size(size, sizeof(gconf->sourceline));
|
||||
}
|
||||
|
||||
size = add_size(size, sizeof(gconf->source));
|
||||
size = add_size(size, sizeof(gconf->scontext));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* EstimateGUCStateSpace:
|
||||
* Returns the size needed to store the GUC state for the current process
|
||||
*/
|
||||
Size EstimateGUCStateSpace(void)
|
||||
{
|
||||
Size size;
|
||||
int i;
|
||||
|
||||
/* Add space reqd for saving the data size of the guc state */
|
||||
size = sizeof(Size);
|
||||
|
||||
/* Add up the space needed for each GUC variable */
|
||||
for (i = 0; i < u_sess->num_guc_variables; i++) {
|
||||
size = add_size(size, EstimateVariableSize(u_sess->guc_variables[i]));
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* DoSerialize:
|
||||
* Copies the formatted string into the destination. Moves ahead the
|
||||
* destination pointer, and decrements the maxbytes by that many bytes. If
|
||||
* maxbytes is not sufficient to copy the string, error out.
|
||||
*/
|
||||
static void DoSerialize(char** destptr, Size& maxbytes, const char* fmt, ...)
|
||||
{
|
||||
va_list vargs;
|
||||
int nRet;
|
||||
|
||||
if (maxbytes == 0) {
|
||||
elog(ERROR, "not enough space to serialize GUC state");
|
||||
}
|
||||
|
||||
va_start(vargs, fmt);
|
||||
nRet = vsnprintf_s(*destptr, maxbytes, maxbytes - 1, fmt, vargs);
|
||||
securec_check_ss(nRet, "\0", "\0");
|
||||
va_end(vargs);
|
||||
|
||||
/*
|
||||
* Cater to portability hazards in the vsnprintf() return value just like
|
||||
* appendPQExpBufferVA() does. Note that this requires an extra byte of
|
||||
* slack at the end of the buffer. Since serialize_variable() ends with a
|
||||
* do_serialize_binary() rather than a do_serialize(), we'll always have
|
||||
* that slack; estimate_variable_size() need not add a byte for it.
|
||||
*/
|
||||
if (nRet < 0) {
|
||||
/* Shouldn't happen. Better show errno description. */
|
||||
elog(ERROR, "vsnprintf failed: %s with format string \"%s\"", strerror(nRet), fmt);
|
||||
}
|
||||
if (nRet >= static_cast<int>(maxbytes)) {
|
||||
/* This shouldn't happen either, really. */
|
||||
elog(ERROR, "not enough space to serialize GUC state");
|
||||
}
|
||||
|
||||
/* Shift the destptr ahead of the null terminator */
|
||||
*destptr += nRet + 1;
|
||||
maxbytes -= static_cast<Size>(nRet) + 1;
|
||||
}
|
||||
|
||||
/* Binary copy version of DoSerialize() */
|
||||
static void DoSerializeBinary(char** destptr, Size& maxbytes, const char* val, Size valsize)
|
||||
{
|
||||
if (valsize > maxbytes) {
|
||||
elog(ERROR, "not enough space to serialize GUC state");
|
||||
}
|
||||
|
||||
errno_t rc = memcpy_s(*destptr, maxbytes, val, valsize);
|
||||
securec_check(rc, "\0", "\0");
|
||||
*destptr += valsize;
|
||||
maxbytes -= valsize;
|
||||
}
|
||||
|
||||
/*
|
||||
* SerializeVariable:
|
||||
* Dumps name, value and other information of a GUC variable into destptr.
|
||||
*/
|
||||
static void SerializeVariable(char** destptr, Size& maxbytes, const struct config_generic* gconf)
|
||||
{
|
||||
if (CanSkipGucvar(gconf)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DoSerialize(destptr, maxbytes, "%s", gconf->name);
|
||||
|
||||
switch (gconf->vartype) {
|
||||
case PGC_BOOL: {
|
||||
const struct config_bool *conf = (const struct config_bool*)gconf;
|
||||
DoSerialize(destptr, maxbytes, (*conf->variable ? "true" : "false"));
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_INT: {
|
||||
const struct config_int *conf = (const struct config_int*)gconf;
|
||||
DoSerialize(destptr, maxbytes, "%d", *conf->variable);
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_INT64: {
|
||||
const struct config_int64 *conf = (const struct config_int64*)gconf;
|
||||
DoSerialize(destptr, maxbytes, "%ld", *conf->variable);
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_REAL: {
|
||||
const struct config_real *conf = (const struct config_real*)gconf;
|
||||
DoSerialize(destptr, maxbytes, "%.*e", REALTYPE_PRECISION, *conf->variable);
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_STRING:{
|
||||
const struct config_string* conf = (const struct config_string*)gconf;
|
||||
DoSerialize(destptr, maxbytes, "%s", *conf->variable ? *conf->variable : "");
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_ENUM:{
|
||||
struct config_enum* conf = (struct config_enum*)gconf;
|
||||
DoSerialize(destptr, maxbytes, "%s", config_enum_lookup_by_value(conf, *conf->variable));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
DoSerialize(destptr, maxbytes, "%s", (gconf->sourcefile ? gconf->sourcefile : ""));
|
||||
|
||||
if (gconf->sourcefile) {
|
||||
DoSerializeBinary(destptr, maxbytes, reinterpret_cast<const char*>(&gconf->sourceline),
|
||||
sizeof(gconf->sourceline));
|
||||
}
|
||||
|
||||
DoSerializeBinary(destptr, maxbytes, reinterpret_cast<const char*>(&gconf->source), sizeof(gconf->source));
|
||||
DoSerializeBinary(destptr, maxbytes, reinterpret_cast<const char*>(&gconf->scontext), sizeof(gconf->scontext));
|
||||
}
|
||||
|
||||
/*
|
||||
* SerializeGUCState:
|
||||
* Dumps the complete GUC state onto the memory location at startAddress.
|
||||
*/
|
||||
void SerializeGUCState(Size maxsize, char* startAddress)
|
||||
{
|
||||
char *curptr;
|
||||
Size actualSize;
|
||||
Size bytesLeft;
|
||||
int i;
|
||||
|
||||
/* Reserve space for saving the actual size of the guc state */
|
||||
Assert(maxsize > sizeof(actualSize));
|
||||
curptr = startAddress + sizeof(actualSize);
|
||||
bytesLeft = maxsize - sizeof(actualSize);
|
||||
|
||||
for (i = 0; i < u_sess->num_guc_variables; i++) {
|
||||
SerializeVariable(&curptr, bytesLeft, u_sess->guc_variables[i]);
|
||||
}
|
||||
|
||||
/* Store actual size without assuming alignment of startAddress. */
|
||||
actualSize = maxsize - bytesLeft - sizeof(actualSize);
|
||||
errno_t rc = memcpy_s(startAddress, maxsize, &actualSize, sizeof(actualSize));
|
||||
securec_check(rc, "\0", "\0");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize one GUC option variable to its compiled-in default.
|
||||
*
|
||||
* Note: the reason for calling check_hooks is not that we think the boot_val
|
||||
* might fail, but that the hooks might wish to compute an "extra" struct.
|
||||
*/
|
||||
static void InitializeOneGUCOption(struct config_generic& gconf)
|
||||
{
|
||||
gconf.status = 0;
|
||||
gconf.source = PGC_S_DEFAULT;
|
||||
gconf.reset_source = PGC_S_DEFAULT;
|
||||
gconf.scontext = PGC_INTERNAL;
|
||||
gconf.reset_scontext = PGC_INTERNAL;
|
||||
gconf.stack = NULL;
|
||||
gconf.extra = NULL;
|
||||
gconf.sourcefile = NULL;
|
||||
gconf.sourceline = 0;
|
||||
|
||||
switch (gconf.vartype) {
|
||||
case PGC_BOOL: {
|
||||
struct config_bool *conf = (struct config_bool*)&gconf;
|
||||
bool newval = conf->boot_val;
|
||||
void* extra = NULL;
|
||||
|
||||
if (!call_bool_check_hook(conf, &newval, &extra, PGC_S_DEFAULT, LOG)) {
|
||||
elog(FATAL, "failed to initialize %s to %d", conf->gen.name, static_cast<int>(newval));
|
||||
}
|
||||
if (conf->assign_hook) {
|
||||
(*conf->assign_hook) (newval, extra);
|
||||
}
|
||||
*conf->variable = conf->reset_val = newval;
|
||||
conf->gen.extra = conf->reset_extra = extra;
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_INT: {
|
||||
struct config_int* conf = (struct config_int*)&gconf;
|
||||
int newval = conf->boot_val;
|
||||
void* extra = NULL;
|
||||
|
||||
Assert(newval >= conf->min);
|
||||
Assert(newval <= conf->max);
|
||||
if (!call_int_check_hook(conf, &newval, &extra, PGC_S_DEFAULT, LOG)) {
|
||||
elog(FATAL, "failed to initialize %s to %d", conf->gen.name, newval);
|
||||
}
|
||||
if (conf->assign_hook) {
|
||||
(*conf->assign_hook) (newval, extra);
|
||||
}
|
||||
*conf->variable = conf->reset_val = newval;
|
||||
conf->gen.extra = conf->reset_extra = extra;
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_INT64: {
|
||||
struct config_int64* conf = (struct config_int64*)&gconf;
|
||||
int64 newval = conf->boot_val;
|
||||
void* extra = NULL;
|
||||
|
||||
Assert(newval >= conf->min);
|
||||
Assert(newval <= conf->max);
|
||||
if (!call_int64_check_hook(conf, &newval, &extra, PGC_S_DEFAULT, LOG)) {
|
||||
elog(FATAL, "failed to initialize %s to %ld", conf->gen.name, newval);
|
||||
}
|
||||
if (conf->assign_hook) {
|
||||
(*conf->assign_hook) (newval, extra);
|
||||
}
|
||||
*conf->variable = conf->reset_val = newval;
|
||||
conf->gen.extra = conf->reset_extra = extra;
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_REAL: {
|
||||
struct config_real* conf = (struct config_real*)&gconf;
|
||||
double newval = conf->boot_val;
|
||||
void* extra = NULL;
|
||||
|
||||
Assert(newval >= conf->min);
|
||||
Assert(newval <= conf->max);
|
||||
if (!call_real_check_hook(conf, &newval, &extra, PGC_S_DEFAULT, LOG)) {
|
||||
elog(FATAL, "failed to initialize %s to %g", conf->gen.name, newval);
|
||||
}
|
||||
if (conf->assign_hook) {
|
||||
(*conf->assign_hook) (newval, extra);
|
||||
}
|
||||
*conf->variable = conf->reset_val = newval;
|
||||
conf->gen.extra = conf->reset_extra = extra;
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_STRING: {
|
||||
struct config_string* conf = (struct config_string*)&gconf;
|
||||
char* newval;
|
||||
void* extra = NULL;
|
||||
|
||||
/* non-NULL boot_val must always get strdup'd */
|
||||
if (conf->boot_val != NULL) {
|
||||
newval = guc_strdup(FATAL, conf->boot_val);
|
||||
} else {
|
||||
newval = NULL;
|
||||
}
|
||||
|
||||
if (!call_string_check_hook(conf, &newval, &extra, PGC_S_DEFAULT, LOG)) {
|
||||
elog(FATAL, "failed to initialize %s to \"%s\"", conf->gen.name, newval ? newval : "");
|
||||
}
|
||||
if (conf->assign_hook) {
|
||||
(*conf->assign_hook) (newval, extra);
|
||||
}
|
||||
*conf->variable = conf->reset_val = newval;
|
||||
conf->gen.extra = conf->reset_extra = extra;
|
||||
break;
|
||||
}
|
||||
|
||||
case PGC_ENUM: {
|
||||
struct config_enum *conf = (struct config_enum*)&gconf;
|
||||
int newval = conf->boot_val;
|
||||
void* extra = NULL;
|
||||
|
||||
if (!call_enum_check_hook(conf, &newval, &extra, PGC_S_DEFAULT, LOG)) {
|
||||
elog(FATAL, "failed to initialize %s to %d", conf->gen.name, newval);
|
||||
}
|
||||
if (conf->assign_hook) {
|
||||
(*conf->assign_hook) (newval, extra);
|
||||
}
|
||||
*conf->variable = conf->reset_val = newval;
|
||||
conf->gen.extra = conf->reset_extra = extra;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ReadGucstate:
|
||||
* Actually it does not read anything, just returns the srcptr. But it does
|
||||
* move the srcptr past the terminating zero byte, so that the caller is ready
|
||||
* to read the next string.
|
||||
*/
|
||||
static char* ReadGucstate(char** srcptr, const char* srcend)
|
||||
{
|
||||
char* retptr = *srcptr;
|
||||
char* ptr;
|
||||
|
||||
if (*srcptr >= srcend) {
|
||||
elog(ERROR, "incomplete GUC state");
|
||||
}
|
||||
|
||||
/* The string variables are all null terminated */
|
||||
for (ptr = *srcptr; ptr < srcend && *ptr != '\0'; ptr++) {}
|
||||
|
||||
if (ptr > srcend) {
|
||||
elog(ERROR, "could not find null terminator in GUC state");
|
||||
}
|
||||
|
||||
/* Set the new position to the byte following the terminating NUL */
|
||||
*srcptr = ptr + 1;
|
||||
|
||||
return retptr;
|
||||
}
|
||||
|
||||
/* Binary read version of ReadGucstate(). Copies into dest */
|
||||
static void ReadGucstateBinary(char** srcptr, const char* srcend, char* dest, Size size)
|
||||
{
|
||||
if (*srcptr + size > srcend) {
|
||||
elog(ERROR, "incomplete GUC state");
|
||||
}
|
||||
|
||||
errno_t rc = memcpy_s(dest, size, *srcptr, size);
|
||||
securec_check(rc, "\0", "\0");
|
||||
*srcptr += size;
|
||||
}
|
||||
|
||||
/*
|
||||
* RestoreGUCState:
|
||||
* Reads the GUC state at the specified address and updates the GUCs with the
|
||||
* values read from the GUC state.
|
||||
*/
|
||||
void RestoreGUCState(char* gucstate)
|
||||
{
|
||||
char* varname;
|
||||
char* varvalue;
|
||||
char* varsourcefile;
|
||||
int varsourceline;
|
||||
GucSource varsource;
|
||||
GucContext varscontext;
|
||||
char* srcptr = gucstate;
|
||||
char* srcend;
|
||||
Size len;
|
||||
int i;
|
||||
|
||||
/* See comment at can_skip_gucvar(). */
|
||||
for (i = 0; i < u_sess->num_guc_variables; i++) {
|
||||
if (!CanSkipGucvar(u_sess->guc_variables[i])) {
|
||||
InitializeOneGUCOption(*u_sess->guc_variables[i]);
|
||||
}
|
||||
}
|
||||
/* First item is the length of the subsequent data */
|
||||
errno_t rc = memcpy_s(&len, sizeof(len), gucstate, sizeof(len));
|
||||
securec_check(rc, "\0", "\0");
|
||||
srcptr += sizeof(len);
|
||||
srcend = srcptr + len;
|
||||
|
||||
while (srcptr < srcend) {
|
||||
int result;
|
||||
varname = ReadGucstate(&srcptr, srcend);
|
||||
varvalue = ReadGucstate(&srcptr, srcend);
|
||||
varsourcefile = ReadGucstate(&srcptr, srcend);
|
||||
|
||||
if (varsourcefile[0]) {
|
||||
ReadGucstateBinary(&srcptr, srcend,
|
||||
reinterpret_cast<char*>(&varsourceline), sizeof(varsourceline));
|
||||
} else {
|
||||
varsourceline = 0;
|
||||
}
|
||||
ReadGucstateBinary(&srcptr, srcend,
|
||||
reinterpret_cast<char*>(&varsource), sizeof(varsource));
|
||||
ReadGucstateBinary(&srcptr, srcend,
|
||||
reinterpret_cast<char*>(&varscontext), sizeof(varscontext));
|
||||
|
||||
result = set_config_option(varname, varvalue, varscontext, varsource,
|
||||
GUC_ACTION_SET, true, ERROR, true);
|
||||
if (result <= 0) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INTERNAL_ERROR),
|
||||
errmsg("parameter \"%s\" could not be set", varname)));
|
||||
}
|
||||
if (varsourcefile[0]) {
|
||||
set_config_sourcefile(varname, varsourcefile, varsourceline);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Validate a proposed option setting for GUCArrayAdd/Delete/Reset.
|
||||
*
|
||||
|
|
@ -18762,6 +19328,7 @@ static bool check_maxconnections(int* newval, void** extra, GucSource source)
|
|||
}
|
||||
#endif
|
||||
if (*newval + g_instance.attr.attr_storage.autovacuum_max_workers + g_instance.attr.attr_sql.job_queue_processes +
|
||||
g_instance.attr.attr_storage.max_background_workers +
|
||||
AUXILIARY_BACKENDS + AV_LAUNCHER_PROCS + g_instance.attr.attr_network.maxInnerToolConnections >
|
||||
MAX_BACKENDS) {
|
||||
return false;
|
||||
|
|
@ -18772,6 +19339,7 @@ static bool check_maxconnections(int* newval, void** extra, GucSource source)
|
|||
static bool CheckMaxInnerToolConnections(int* newval, void** extra, GucSource source)
|
||||
{
|
||||
if (*newval + g_instance.attr.attr_storage.autovacuum_max_workers + g_instance.attr.attr_sql.job_queue_processes +
|
||||
g_instance.attr.attr_storage.max_background_workers +
|
||||
g_instance.attr.attr_network.MaxConnections + AUXILIARY_BACKENDS + AV_LAUNCHER_PROCS > MAX_BACKENDS) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -18781,6 +19349,7 @@ static bool CheckMaxInnerToolConnections(int* newval, void** extra, GucSource so
|
|||
static bool check_autovacuum_max_workers(int* newval, void** extra, GucSource source)
|
||||
{
|
||||
if (g_instance.attr.attr_network.MaxConnections + *newval + g_instance.attr.attr_sql.job_queue_processes +
|
||||
g_instance.attr.attr_storage.max_background_workers +
|
||||
AUXILIARY_BACKENDS + AV_LAUNCHER_PROCS + g_instance.attr.attr_network.maxInnerToolConnections >
|
||||
MAX_BACKENDS) {
|
||||
return false;
|
||||
|
|
@ -18788,6 +19357,18 @@ static bool check_autovacuum_max_workers(int* newval, void** extra, GucSource so
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool check_max_worker_processes(int* newval, void** extra, GucSource source)
|
||||
{
|
||||
if (g_instance.attr.attr_network.MaxConnections + g_instance.attr.attr_storage.autovacuum_max_workers +
|
||||
g_instance.attr.attr_sql.job_queue_processes + *newval +
|
||||
AUXILIARY_BACKENDS + AV_LAUNCHER_PROCS + g_instance.attr.attr_network.maxInnerToolConnections >
|
||||
MAX_BACKENDS) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Description: Check wheth out of max backends after max job worker threads.
|
||||
*
|
||||
|
|
@ -18798,6 +19379,7 @@ static bool check_autovacuum_max_workers(int* newval, void** extra, GucSource so
|
|||
static bool check_job_max_workers(int* newval, void** extra, GucSource source)
|
||||
{
|
||||
if (g_instance.attr.attr_network.MaxConnections + g_instance.attr.attr_storage.autovacuum_max_workers + *newval +
|
||||
g_instance.attr.attr_storage.max_background_workers +
|
||||
AUXILIARY_BACKENDS + AV_LAUNCHER_PROCS + g_instance.attr.attr_network.maxInnerToolConnections >
|
||||
MAX_BACKENDS) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ static void check_labels(const char *start_label,
|
|||
static PLpgSQL_expr *read_cursor_args(PLpgSQL_var *cursor,
|
||||
int until, const char *expected);
|
||||
static List *read_raise_options(void);
|
||||
static bool last_pragma;
|
||||
|
||||
%}
|
||||
|
||||
|
|
@ -213,6 +214,7 @@ static List *read_raise_options(void);
|
|||
char *label;
|
||||
int n_initvars;
|
||||
int *initvarnos;
|
||||
bool autonomous;
|
||||
} declhdr;
|
||||
struct
|
||||
{
|
||||
|
|
@ -399,6 +401,7 @@ static List *read_raise_options(void);
|
|||
%token <keyword> K_PG_EXCEPTION_CONTEXT
|
||||
%token <keyword> K_PG_EXCEPTION_DETAIL
|
||||
%token <keyword> K_PG_EXCEPTION_HINT
|
||||
%token <keyword> K_PRAGMA
|
||||
%token <keyword> K_PRIOR
|
||||
%token <keyword> K_QUERY
|
||||
%token <keyword> K_RAISE
|
||||
|
|
@ -477,6 +480,7 @@ pl_block : decl_sect K_BEGIN proc_sect exception_sect K_END opt_label
|
|||
newp->cmd_type = PLPGSQL_STMT_BLOCK;
|
||||
newp->lineno = plpgsql_location_to_lineno(@2);
|
||||
newp->label = $1.label;
|
||||
newp->autonomous = $1.autonomous;
|
||||
newp->n_initvars = $1.n_initvars;
|
||||
newp->initvarnos = $1.initvarnos;
|
||||
newp->body = $3;
|
||||
|
|
@ -500,6 +504,7 @@ decl_sect : opt_block_label
|
|||
$$.label = $1;
|
||||
$$.n_initvars = 0;
|
||||
$$.initvarnos = NULL;
|
||||
$$.autonomous = false;
|
||||
}
|
||||
| opt_block_label decl_start
|
||||
{
|
||||
|
|
@ -507,6 +512,7 @@ decl_sect : opt_block_label
|
|||
$$.label = $1;
|
||||
$$.n_initvars = 0;
|
||||
$$.initvarnos = NULL;
|
||||
$$.autonomous = false;
|
||||
}
|
||||
| opt_block_label decl_start decl_stmts
|
||||
{
|
||||
|
|
@ -514,6 +520,8 @@ decl_sect : opt_block_label
|
|||
$$.label = $1;
|
||||
/* Remember variables declared in decl_stmts */
|
||||
$$.n_initvars = plpgsql_add_initdatums(&($$.initvarnos));
|
||||
$$.autonomous = last_pragma;
|
||||
last_pragma = false;
|
||||
}
|
||||
;
|
||||
|
||||
|
|
@ -521,6 +529,7 @@ decl_start : K_DECLARE
|
|||
{
|
||||
/* Forget any variables created before block */
|
||||
plpgsql_add_initdatums(NULL);
|
||||
last_pragma = false;
|
||||
/*
|
||||
* Disable scanner lookup of identifiers while
|
||||
* we process the decl_stmts
|
||||
|
|
@ -720,6 +729,13 @@ decl_statement : decl_varname decl_const decl_datatype decl_collate decl_notnull
|
|||
errmsg("build variable failed")));
|
||||
pfree_ext($1.name);
|
||||
}
|
||||
| K_PRAGMA any_identifier ';'
|
||||
{
|
||||
if (pg_strcasecmp($2, "autonomous_transaction") == 0)
|
||||
last_pragma = true;
|
||||
else
|
||||
elog(ERROR, "invalid pragma");
|
||||
}
|
||||
;
|
||||
|
||||
record_attr_list : record_attr
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* pl_exec.c - Executor for the PL/pgSQL
|
||||
* pl_exec.cpp - Executor for the PL/pgSQL
|
||||
* procedural language
|
||||
*
|
||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/pl/plpgsql/src/pl_exec.c
|
||||
* src/pl/plpgsql/src/pl_exec.cpp
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
|
@ -33,6 +33,7 @@
|
|||
#include "pgstat.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "storage/proc.h"
|
||||
#include "tcop/autonomous.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
|
|
@ -1412,6 +1413,20 @@ static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block)
|
|||
bool savedIsStp = u_sess->SPI_cxt.is_stp;
|
||||
TransactionId oldTransactionId = InvalidTransactionId;
|
||||
|
||||
if (block->autonomous) {
|
||||
if (estate->func->fn_is_trigger) {
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Un-support feature"),
|
||||
errdetail("Trigger doesnot support autonomous transaction")));
|
||||
} else if (t_thrd.autonomous_cxt.isnested) {
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Un-support feature : Autonomous transaction doesnot support nesting")));
|
||||
} else {
|
||||
estate->autonomous_session = AutonomousSessionStart();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* First initialize all variables declared in this block
|
||||
*/
|
||||
|
|
@ -1732,6 +1747,8 @@ static int exec_stmt_block(PLpgSQL_execstate* estate, PLpgSQL_stmt_block* block)
|
|||
}
|
||||
|
||||
estate->err_text = NULL;
|
||||
if (block->autonomous)
|
||||
AutonomousSessionEnd(estate->autonomous_session);
|
||||
|
||||
/*
|
||||
* Handle the return code.
|
||||
|
|
@ -3664,6 +3681,7 @@ static void plpgsql_estate_setup(PLpgSQL_execstate* estate, PLpgSQL_function* fu
|
|||
estate->rettupdesc = NULL;
|
||||
estate->exitlabel = NULL;
|
||||
estate->cur_error = NULL;
|
||||
estate->autonomous_session = NULL;
|
||||
|
||||
estate->tuple_store = NULL;
|
||||
estate->cursor_return_data = NULL;
|
||||
|
|
@ -3810,6 +3828,59 @@ static void exec_prepare_plan(PLpgSQL_execstate* estate, PLpgSQL_expr* expr, int
|
|||
exec_simple_check_plan(expr);
|
||||
}
|
||||
|
||||
static void build_symbol_table(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_nsitem *ns_start,
|
||||
int *ret_nitems,
|
||||
const char ***ret_names,
|
||||
Oid **ret_types)
|
||||
{
|
||||
PLpgSQL_nsitem *nsitem = NULL;
|
||||
List *names = NIL;
|
||||
List *types = NIL;
|
||||
ListCell *lc1, *lc2;
|
||||
int i, nitems;
|
||||
const char **names_vector;
|
||||
Oid *types_vector = NULL;
|
||||
|
||||
for (nsitem = ns_start; nsitem; nsitem = nsitem->prev) {
|
||||
if (nsitem->itemtype == PLPGSQL_NSTYPE_VAR) {
|
||||
PLpgSQL_datum *datum;
|
||||
PLpgSQL_var *var;
|
||||
Oid typoid;
|
||||
Value *name;
|
||||
|
||||
if (strcmp(nsitem->name, "found") == 0)
|
||||
continue; // XXX
|
||||
elog(LOG, "namespace item variable itemno %d, name %s",
|
||||
nsitem->itemno, nsitem->name);
|
||||
datum = estate->datums[nsitem->itemno];
|
||||
Assert(datum->dtype == PLPGSQL_DTYPE_VAR);
|
||||
var = (PLpgSQL_var *) datum;
|
||||
name = makeString(nsitem->name);
|
||||
typoid = var->datatype->typoid;
|
||||
if (!list_member(names, name)) {
|
||||
names = lappend(names, name);
|
||||
types = lappend_oid(types, typoid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert(list_length(names) == list_length(types));
|
||||
nitems = list_length(names);
|
||||
names_vector = (const char **)palloc(nitems * sizeof(char *));
|
||||
types_vector = (Oid *)palloc(nitems * sizeof(Oid));
|
||||
i = 0;
|
||||
forboth(lc1, names, lc2, types) {
|
||||
names_vector[i] = pstrdup(strVal(lfirst(lc1)));
|
||||
types_vector[i] = lfirst_oid(lc2);
|
||||
i++;
|
||||
}
|
||||
|
||||
*ret_nitems = nitems;
|
||||
*ret_names = names_vector;
|
||||
*ret_types = types_vector;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
* exec_stmt_execsql Execute an SQL statement (possibly with INTO).
|
||||
* ----------
|
||||
|
|
@ -3827,6 +3898,29 @@ static int exec_stmt_execsql(PLpgSQL_execstate* estate, PLpgSQL_stmt_execsql* st
|
|||
oldTransactionId = GetTopTransactionId();
|
||||
}
|
||||
|
||||
if (estate->autonomous_session) {
|
||||
int nparams = 0;
|
||||
int i;
|
||||
const char **param_names = NULL;
|
||||
Oid *param_types = NULL;
|
||||
AutonomousPreparedStatement *astmt = NULL;
|
||||
Datum *values = NULL;
|
||||
bool *nulls = NULL;
|
||||
AutonomousResult *aresult = NULL;
|
||||
t_thrd.autonomous_cxt.sqlstmt = stmt->sqlstmt;
|
||||
build_symbol_table(estate, stmt->sqlstmt->ns, &nparams, ¶m_names, ¶m_types);
|
||||
astmt = AutonomousSessionPrepare(estate->autonomous_session, stmt->sqlstmt->query, (int16)nparams, param_types, param_names);
|
||||
|
||||
values = (Datum *)palloc(nparams * sizeof(*values));
|
||||
nulls = (bool *)palloc(nparams * sizeof(*nulls));
|
||||
for (i = 0; i < nparams; i++) {
|
||||
nulls[i] = true;
|
||||
}
|
||||
aresult = AutonomousSessionExecutePrepared(astmt, (int16)nparams, values, nulls);
|
||||
exec_set_found(estate, (list_length(aresult->tuples) != 0));
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* On the first call for this statement generate the plan, and detect
|
||||
* whether the statement is INSERT/UPDATE/DELETE/MERGE
|
||||
|
|
@ -4240,6 +4334,12 @@ static int exec_stmt_dynexecute(PLpgSQL_execstate* estate, PLpgSQL_stmt_dynexecu
|
|||
|
||||
exec_eval_cleanup(estate);
|
||||
|
||||
if (estate->autonomous_session)
|
||||
{
|
||||
(void *)AutonomousSessionExecute(estate->autonomous_session, querystr);
|
||||
return PLPGSQL_RC_OK;
|
||||
}
|
||||
|
||||
if (stmt->params != NULL) {
|
||||
stmt->ppd = (void*)exec_eval_using_params(estate, stmt->params);
|
||||
}
|
||||
|
|
@ -4984,6 +5084,36 @@ static int exec_stmt_null(PLpgSQL_execstate* estate, PLpgSQL_stmt* stmt)
|
|||
*/
|
||||
static int exec_stmt_commit(PLpgSQL_execstate* estate, PLpgSQL_stmt_commit* stmt)
|
||||
{
|
||||
if (estate->autonomous_session) {
|
||||
if (t_thrd.autonomous_cxt.sqlstmt) {
|
||||
int nparams = 0;
|
||||
int i;
|
||||
const char **param_names = NULL;
|
||||
Oid *param_types = NULL;
|
||||
AutonomousPreparedStatement *astmt = NULL;
|
||||
Datum *values = NULL;
|
||||
bool *nulls = NULL;
|
||||
AutonomousResult *aresult = NULL;
|
||||
ereport(LOG, (errmsg("query COMMIT")));
|
||||
build_symbol_table(estate, t_thrd.autonomous_cxt.sqlstmt->ns, &nparams, ¶m_names, ¶m_types);
|
||||
astmt = AutonomousSessionPrepare(estate->autonomous_session, "COMMIT", (int16)nparams, param_types, param_names);
|
||||
|
||||
values = (Datum *)palloc(nparams * sizeof(*values));
|
||||
nulls = (bool *)palloc(nparams * sizeof(*nulls));
|
||||
for (i = 0; i < nparams; i++)
|
||||
{
|
||||
nulls[i] = true;
|
||||
}
|
||||
aresult = AutonomousSessionExecutePrepared(astmt, (int16)nparams, values, nulls);
|
||||
exec_set_found(estate, (list_length(aresult->tuples) != 0));
|
||||
t_thrd.autonomous_cxt.sqlstmt = NULL;
|
||||
return PLPGSQL_RC_OK;
|
||||
} else {
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Syntax error: In antonomous transaction, commit/rollback must match start transaction")));
|
||||
}
|
||||
}
|
||||
|
||||
const char* PORTAL = "Portal";
|
||||
int subTransactionCount = u_sess->SPI_cxt.portal_stp_exception_counter;
|
||||
|
||||
|
|
@ -5046,6 +5176,36 @@ static int exec_stmt_commit(PLpgSQL_execstate* estate, PLpgSQL_stmt_commit* stmt
|
|||
*/
|
||||
static int exec_stmt_rollback(PLpgSQL_execstate* estate, PLpgSQL_stmt_rollback* stmt)
|
||||
{
|
||||
if (estate->autonomous_session) {
|
||||
if (t_thrd.autonomous_cxt.sqlstmt) {
|
||||
int nparams = 0;
|
||||
int i;
|
||||
const char **param_names = NULL;
|
||||
Oid *param_types = NULL;
|
||||
AutonomousPreparedStatement *astmt = NULL;
|
||||
Datum *values = NULL;
|
||||
bool *nulls = NULL;
|
||||
AutonomousResult *aresult = NULL;
|
||||
ereport(LOG, (errmsg("query ROLLBACK")));
|
||||
build_symbol_table(estate, t_thrd.autonomous_cxt.sqlstmt->ns, &nparams, ¶m_names, ¶m_types);
|
||||
astmt = AutonomousSessionPrepare(estate->autonomous_session, "ROLLBACK", (int16)nparams, param_types, param_names);
|
||||
|
||||
values = (Datum *)palloc(nparams * sizeof(*values));
|
||||
nulls = (bool *)palloc(nparams * sizeof(*nulls));
|
||||
for (i = 0; i < nparams; i++)
|
||||
{
|
||||
nulls[i] = true;
|
||||
}
|
||||
aresult = AutonomousSessionExecutePrepared(astmt, (int16)nparams, values, nulls);
|
||||
exec_set_found(estate, (list_length(aresult->tuples) != 0));
|
||||
t_thrd.autonomous_cxt.sqlstmt = NULL;
|
||||
return PLPGSQL_RC_OK;
|
||||
} else {
|
||||
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||
errmsg("Syntax error: In antonomous transaction, commit/rollback must match start transaction")));
|
||||
}
|
||||
}
|
||||
|
||||
const char* PORTAL = "Portal";
|
||||
int subTransactionCount = u_sess->SPI_cxt.portal_stp_exception_counter;
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,8 @@ static const ScanKeyword unreserved_keywords[] = {
|
|||
UNRESERVED_KEYWORD) PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD) PG_KEYWORD("option", K_OPTION,
|
||||
UNRESERVED_KEYWORD) PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL, UNRESERVED_KEYWORD) PG_KEYWORD("pg_exception_hint",
|
||||
K_PG_EXCEPTION_HINT, UNRESERVED_KEYWORD) PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
|
||||
K_PG_EXCEPTION_HINT, UNRESERVED_KEYWORD) PG_KEYWORD("pragma", K_PRAGMA, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD) PG_KEYWORD("record", K_RECORD, UNRESERVED_KEYWORD)
|
||||
PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD) PG_KEYWORD("result_oid", K_RESULT_OID,
|
||||
UNRESERVED_KEYWORD) PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE, UNRESERVED_KEYWORD)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "catalog/namespace.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/spi.h"
|
||||
#include "tcop/autonomous.h"
|
||||
|
||||
/**********************************************************************
|
||||
* Definitions
|
||||
|
|
@ -382,6 +383,7 @@ typedef struct PLpgSQL_stmt_block { /* Block of statements */
|
|||
int cmd_type;
|
||||
int lineno;
|
||||
char* label;
|
||||
bool autonomous;
|
||||
List* body; /* List of statements */
|
||||
int n_initvars;
|
||||
int* initvarnos;
|
||||
|
|
@ -775,7 +777,7 @@ typedef struct PLpgSQL_execstate { /* Runtime execution data */
|
|||
MemoryContext tuple_store_cxt;
|
||||
ResourceOwner tuple_store_owner;
|
||||
ReturnSetInfo* rsi;
|
||||
|
||||
AutonomousSession *autonomous_session;
|
||||
int found_varno;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -200,8 +200,6 @@ typedef struct QueueBackendStatus {
|
|||
QueuePosition pos; /* backend has read queue up to here */
|
||||
} QueueBackendStatus;
|
||||
|
||||
#define InvalidPid ((ThreadId)(-1))
|
||||
|
||||
/*
|
||||
* Shared memory state for LISTEN/NOTIFY (excluding its SLRU stuff)
|
||||
*
|
||||
|
|
@ -330,7 +328,6 @@ static void asyncQueueReadAllNotifications(void);
|
|||
static bool asyncQueueProcessPageEntries(QueuePosition* current, const QueuePosition &stop, char* page_buffer);
|
||||
static void asyncQueueAdvanceTail(void);
|
||||
static void ProcessIncomingNotify(void);
|
||||
static void NotifyMyFrontEnd(const char* channel, const char* payload, int32 srcPid);
|
||||
static bool AsyncExistsPendingNotify(const char* channel, const char* payload);
|
||||
static void ClearPendingActionsAndNotifies(void);
|
||||
|
||||
|
|
@ -1837,7 +1834,7 @@ static void ProcessIncomingNotify(void)
|
|||
/*
|
||||
* Send NOTIFY message to my front end.
|
||||
*/
|
||||
static void NotifyMyFrontEnd(const char* channel, const char* payload, int32 srcPid)
|
||||
void NotifyMyFrontEnd(const char* channel, const char* payload, int32 srcPid)
|
||||
{
|
||||
if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) {
|
||||
StringInfoData buf;
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ void PrepareQuery(PrepareStmt* stmt, const char* queryString)
|
|||
* Because parse analysis scribbles on the raw querytree, we must make a
|
||||
* copy to ensure we don't modify the passed-in tree.
|
||||
*/
|
||||
query = parse_analyze_varparams((Node*)copyObject(stmt->query), queryString, &argtypes, &nargs);
|
||||
query = parse_analyze_varparams((Node*)copyObject(stmt->query), queryString, &argtypes, &nargs, NULL);
|
||||
|
||||
/*
|
||||
* Check that all parameter types were determined.
|
||||
|
|
|
|||
|
|
@ -686,11 +686,13 @@ bool check_mix_replication_param(bool* newval, void** extra, GucSource source)
|
|||
/*
|
||||
* SET CLIENT_ENCODING
|
||||
*/
|
||||
void (*check_client_encoding_hook)(void);
|
||||
bool check_client_encoding(char** newval, void** extra, GucSource source)
|
||||
{
|
||||
int encoding;
|
||||
const char* canonical_name = NULL;
|
||||
|
||||
if (check_client_encoding_hook)
|
||||
check_client_encoding_hook();
|
||||
/* Look up the encoding by name */
|
||||
encoding = pg_valid_client_encoding(*newval);
|
||||
if (encoding < 0) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ ifneq "$(MAKECMDGOALS)" "clean"
|
|||
endif
|
||||
endif
|
||||
OBJS = autovacuum.o bgwriter.o fork_process.o pgarch.o pgstat.o postmaster.o gaussdb_version.o\
|
||||
startup.o syslogger.o walwriter.o checkpointer.o pgaudit.o alarmchecker.o \
|
||||
startup.o syslogger.o walwriter.o checkpointer.o pgaudit.o alarmchecker.o bgworker.o\
|
||||
twophasecleaner.o aiocompleter.o fencedudf.o lwlockmonitor.o cbmwriter.o remoteservice.o pagewriter.o\
|
||||
$(top_builddir)/src/lib/config/libconfig.a
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -110,6 +110,7 @@
|
|||
#include "job/job_scheduler.h"
|
||||
#include "job/job_worker.h"
|
||||
#include "postmaster/autovacuum.h"
|
||||
#include "postmaster/bgworker_internals.h"
|
||||
#include "postmaster/pagewriter.h"
|
||||
#include "postmaster/fork_process.h"
|
||||
#include "postmaster/pgarch.h"
|
||||
|
|
@ -311,6 +312,7 @@ static void reaper(SIGNAL_ARGS);
|
|||
static void sigusr1_handler(SIGNAL_ARGS);
|
||||
static void dummy_handler(SIGNAL_ARGS);
|
||||
static void CleanupBackend(ThreadId pid, int exitstatus);
|
||||
static bool CleanupBackgroundWorker(ThreadId pid, int exitstatus);
|
||||
static const char* GetProcName(ThreadId pid);
|
||||
static void LogChildExit(int lev, const char* procname, ThreadId pid, int exitstatus);
|
||||
static void PostmasterStateMachine(void);
|
||||
|
|
@ -366,6 +368,8 @@ static void check_and_reset_ha_listen_port(void);
|
|||
static void* cJSON_internal_malloc(size_t size);
|
||||
static bool NeedHeartbeat();
|
||||
static ServerMode GetHaShmemMode(void);
|
||||
static bool assign_backendlist_entry(RegisteredBgWorker *rw);
|
||||
static void maybe_start_bgworkers(void);
|
||||
|
||||
bool PMstateIsRun(void);
|
||||
|
||||
|
|
@ -380,6 +384,7 @@ bool PMstateIsRun(void);
|
|||
#define BACKEND_TYPE_TEMPBACKEND \
|
||||
0x0010 /* temp thread processing cancel signal \
|
||||
or stream connection */
|
||||
|
||||
#define BACKEND_TYPE_ALL 0x001F /* OR of all the above */
|
||||
|
||||
static int CountChildren(int target);
|
||||
|
|
@ -1019,6 +1024,7 @@ void SetShmemCxt(void)
|
|||
g_instance.shmem_cxt.MaxBackends = g_instance.shmem_cxt.MaxConnections +
|
||||
g_instance.attr.attr_sql.job_queue_processes +
|
||||
g_instance.attr.attr_storage.autovacuum_max_workers +
|
||||
g_instance.attr.attr_storage.max_background_workers +
|
||||
AUXILIARY_BACKENDS +
|
||||
AV_LAUNCHER_PROCS;
|
||||
g_instance.shmem_cxt.MaxReserveBackendId = g_instance.attr.attr_sql.job_queue_processes +
|
||||
|
|
@ -5464,6 +5470,14 @@ static void reaper(SIGNAL_ARGS)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Was it one of our background workers? */
|
||||
if (CleanupBackgroundWorker(pid, (int)exitstatus))
|
||||
{
|
||||
/* have it be restarted */
|
||||
g_instance.bgworker_cxt.have_crashed_worker = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Else do standard backend child cleanup.
|
||||
*/
|
||||
|
|
@ -5566,6 +5580,101 @@ static const char* GetProcName(ThreadId pid)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the bgworkers list and see if the given PID (which has just stopped
|
||||
* or crashed) is in it. Handle its shutdown if so, and return true. If not a
|
||||
* bgworker, return false.
|
||||
*
|
||||
* This is heavily based on CleanupBackend. One important difference is that
|
||||
* we don't know yet that the dying process is a bgworker, so we must be silent
|
||||
* until we're sure it is.
|
||||
*/
|
||||
static bool CleanupBackgroundWorker(ThreadId pid,
|
||||
int exitstatus) /* child's exit status */
|
||||
{
|
||||
char namebuf[MAXPGPATH];
|
||||
slist_mutable_iter iter;
|
||||
|
||||
slist_foreach_modify(iter, &BackgroundWorkerList) {
|
||||
RegisteredBgWorker *rw;
|
||||
|
||||
rw = slist_container(RegisteredBgWorker, rw_lnode, iter.cur);
|
||||
|
||||
if (rw->rw_pid != pid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
/* see CleanupBackend */
|
||||
if (exitstatus == ERROR_WAIT_NO_CHILDREN) {
|
||||
exitstatus = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int rc = snprintf_s(namebuf, MAXPGPATH, MAXPGPATH - 1, _("background worker \"%s\""), rw->rw_worker.bgw_type);
|
||||
securec_check_ss_c(rc, "\0", "\0");
|
||||
|
||||
if (!EXIT_STATUS_0(exitstatus)) {
|
||||
/* Record timestamp, so we know when to restart the worker. */
|
||||
rw->rw_crashed_at = GetCurrentTimestamp();
|
||||
} else {
|
||||
/* Zero exit status means terminate */
|
||||
rw->rw_crashed_at = 0;
|
||||
rw->rw_terminate = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Additionally, for shared-memory-connected workers, just like a
|
||||
* backend, any exit status other than 0 or 1 is considered a crash
|
||||
* and causes a system-wide restart.
|
||||
*/
|
||||
if ((rw->rw_worker.bgw_flags & BGWORKER_SHMEM_ACCESS) != 0) {
|
||||
if (!EXIT_STATUS_0(exitstatus) && !EXIT_STATUS_1(exitstatus)) {
|
||||
HandleChildCrash(pid, exitstatus, namebuf);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We must release the postmaster child slot whether this worker is
|
||||
* connected to shared memory or not, but we only treat it as a crash
|
||||
* if it is in fact connected.
|
||||
*/
|
||||
if (!ReleasePostmasterChildSlot(rw->rw_child_slot) &&
|
||||
(rw->rw_worker.bgw_flags & BGWORKER_SHMEM_ACCESS) != 0) {
|
||||
HandleChildCrash(pid, exitstatus, namebuf);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get it out of the BackendList and clear out remaining data */
|
||||
DLRemove(&rw->rw_backend->elem);
|
||||
|
||||
/*
|
||||
* It's possible that this background worker started some OTHER
|
||||
* background worker and asked to be notified when that worker started
|
||||
* or stopped. If so, cancel any notifications destined for the
|
||||
* now-dead backend.
|
||||
*/
|
||||
if (rw->rw_backend->bgworker_notify) {
|
||||
BackgroundWorkerStopNotifications(rw->rw_pid);
|
||||
}
|
||||
|
||||
BackendArrayRemove(rw->rw_backend);
|
||||
|
||||
rw->rw_backend = NULL;
|
||||
rw->rw_pid = 0;
|
||||
rw->rw_child_slot = 0;
|
||||
ReportBackgroundWorkerExit(&iter); /* report child death */
|
||||
|
||||
LogChildExit(EXIT_STATUS_0(exitstatus) ? DEBUG1 : LOG,
|
||||
namebuf, pid, exitstatus);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* CleanupBackend -- cleanup after terminated backend.
|
||||
*
|
||||
|
|
@ -5629,6 +5738,18 @@ static void CleanupBackend(ThreadId pid, int exitstatus) /* child's exit status.
|
|||
BackendArrayRemove(bp);
|
||||
}
|
||||
|
||||
if (bp->bgworker_notify)
|
||||
{
|
||||
/*
|
||||
* This backend may have been slated to receive SIGUSR1 when
|
||||
* some background worker started or stopped. Cancel those
|
||||
* notifications, as we don't want to signal PIDs that are not
|
||||
* PostgreSQL backends. This gets skipped in the (probably
|
||||
* very common) case where the backend has never requested any
|
||||
* such notifications.
|
||||
*/
|
||||
BackgroundWorkerStopNotifications(bp->pid);
|
||||
}
|
||||
DLRemove(curr);
|
||||
break;
|
||||
}
|
||||
|
|
@ -6881,6 +7002,16 @@ static void sigusr1_handler(SIGNAL_ARGS)
|
|||
|
||||
gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL);
|
||||
|
||||
/* Process background worker state change. */
|
||||
if (CheckPostmasterSignal(PMSIGNAL_BACKGROUND_WORKER_CHANGE))
|
||||
{
|
||||
BackgroundWorkerStateChange();
|
||||
g_instance.bgworker_cxt.start_worker_needed = true;
|
||||
}
|
||||
if (g_instance.bgworker_cxt.start_worker_needed || g_instance.bgworker_cxt.have_crashed_worker) {
|
||||
maybe_start_bgworkers();
|
||||
}
|
||||
|
||||
/*
|
||||
* RECOVERY_STARTED and BEGIN_HOT_STANDBY signals are ignored in
|
||||
* unexpected states. If the startup process quickly starts up, completes
|
||||
|
|
@ -7699,6 +7830,300 @@ int MaxLivePostmasterChildren(void)
|
|||
return 6 * g_instance.shmem_cxt.MaxBackends;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start a new bgworker.
|
||||
* Starting time conditions must have been checked already.
|
||||
*
|
||||
* Returns true on success, false on failure.
|
||||
* In either case, update the RegisteredBgWorker's state appropriately.
|
||||
*
|
||||
* This code is heavily based on autovacuum.c, q.v.
|
||||
*/
|
||||
static bool do_start_bgworker(RegisteredBgWorker *rw)
|
||||
{
|
||||
ThreadId worker_pid = InvalidPid;
|
||||
|
||||
Assert(rw->rw_pid == 0);
|
||||
|
||||
/*
|
||||
* Allocate and assign the Backend element. Note we must do this before
|
||||
* forking, so that we can handle failures (out of memory or child-process
|
||||
* slots) cleanly.
|
||||
*
|
||||
* Treat failure as though the worker had crashed. That way, the
|
||||
* postmaster will wait a bit before attempting to start it again; if we
|
||||
* tried again right away, most likely we'd find ourselves hitting the
|
||||
* same resource-exhaustion condition.
|
||||
*/
|
||||
if (!assign_backendlist_entry(rw)) {
|
||||
rw->rw_crashed_at = GetCurrentTimestamp();
|
||||
return false;
|
||||
}
|
||||
|
||||
ereport(DEBUG1,
|
||||
(errmsg("starting background worker process \"%s\"",
|
||||
rw->rw_worker.bgw_name)));
|
||||
|
||||
Backend* bn = rw->rw_backend;
|
||||
void* bgWorkerShmAddr = GetBackgroundWorkerShmAddr(rw->rw_shmem_slot);
|
||||
switch ((worker_pid = initialize_util_thread(BACKGROUND_WORKER, bgWorkerShmAddr))) {
|
||||
case (ThreadId)-1:
|
||||
/* in postmaster, fork failed ... */
|
||||
ereport(LOG,
|
||||
(errmsg("could not fork worker process: %m")));
|
||||
/* undo what assign_backendlist_entry did */
|
||||
(void)ReleasePostmasterChildSlot(rw->rw_child_slot);
|
||||
bn->pid = 0;
|
||||
rw->rw_child_slot = 0;
|
||||
rw->rw_backend = NULL;
|
||||
/* mark entry as crashed, so we'll try again later */
|
||||
rw->rw_crashed_at = GetCurrentTimestamp();
|
||||
break;
|
||||
|
||||
default:
|
||||
/* in postmaster, fork successful ... */
|
||||
rw->rw_pid = worker_pid;
|
||||
bn->pid = rw->rw_pid;
|
||||
ReportBackgroundWorkerPID(rw);
|
||||
/* add new worker to lists of backends */
|
||||
DLInitElem(&bn->elem, bn);
|
||||
DLAddHead(g_instance.backend_list, &bn->elem);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does the current postmaster state require starting a worker with the
|
||||
* specified start_time?
|
||||
*/
|
||||
static bool
|
||||
bgworker_should_start_now(BgWorkerStartTime start_time)
|
||||
{
|
||||
switch (pmState) {
|
||||
case PM_NO_CHILDREN:
|
||||
case PM_WAIT_DEAD_END:
|
||||
case PM_SHUTDOWN_2:
|
||||
case PM_SHUTDOWN:
|
||||
case PM_WAIT_BACKENDS:
|
||||
case PM_WAIT_READONLY:
|
||||
case PM_WAIT_BACKUP:
|
||||
break;
|
||||
|
||||
case PM_RUN:
|
||||
if (start_time == BgWorkerStart_RecoveryFinished) {
|
||||
return true;
|
||||
}
|
||||
/* fall through */
|
||||
case PM_HOT_STANDBY:
|
||||
if (start_time == BgWorkerStart_ConsistentState) {
|
||||
return true;
|
||||
}
|
||||
/* fall through */
|
||||
case PM_RECOVERY:
|
||||
case PM_STARTUP:
|
||||
case PM_INIT:
|
||||
if (start_time == BgWorkerStart_PostmasterStart) {
|
||||
return true;
|
||||
}
|
||||
/* fall through */
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate the Backend struct for a connected background worker, but don't
|
||||
* add it to the list of backends just yet.
|
||||
*
|
||||
* On failure, return false without changing any worker state.
|
||||
*
|
||||
* Some info from the Backend is copied into the passed rw.
|
||||
*/
|
||||
static bool
|
||||
assign_backendlist_entry(RegisteredBgWorker *rw)
|
||||
{
|
||||
Backend* bn = NULL;
|
||||
|
||||
/*
|
||||
* Check that database state allows another connection. Currently the
|
||||
* only possible failure is CAC_TOOMANY, so we just log an error message
|
||||
* based on that rather than checking the error code precisely.
|
||||
*/
|
||||
if (canAcceptConnections(false) != CAC_OK)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errcode(ERRCODE_CONFIGURATION_LIMIT_EXCEEDED),
|
||||
errmsg("no slot available for new worker process")));
|
||||
return false;
|
||||
}
|
||||
|
||||
int slot = AssignPostmasterChildSlot();
|
||||
|
||||
bn = AssignFreeBackEnd(slot);
|
||||
|
||||
if (bn == NULL) {
|
||||
ereport(LOG, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory")));
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the cancel key that will be assigned to this session. We
|
||||
* probably don't need cancel keys for background workers, but we'd better
|
||||
* have something random in the field to prevent unfriendly people from
|
||||
* sending cancels to them.
|
||||
*/
|
||||
GenerateCancelKey(false);
|
||||
bn->cancel_key = t_thrd.proc_cxt.MyCancelKey;
|
||||
bn->child_slot = t_thrd.proc_cxt.MyPMChildSlot = slot;
|
||||
bn->is_autovacuum = false;
|
||||
bn->dead_end = false;
|
||||
bn->bgworker_notify = false;
|
||||
rw->rw_backend = bn;
|
||||
rw->rw_child_slot = bn->child_slot;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the time is right, start background worker(s).
|
||||
*
|
||||
* As a side effect, the bgworker control variables are set or reset
|
||||
* depending on whether more workers may need to be started.
|
||||
*
|
||||
* We limit the number of workers started per call, to avoid consuming the
|
||||
* postmaster's attention for too long when many such requests are pending.
|
||||
* As long as start_worker_needed is true, ServerLoop will not block and will
|
||||
* call this function again after dealing with any other issues.
|
||||
*/
|
||||
static void maybe_start_bgworkers(void)
|
||||
{
|
||||
#define MAX_BGWORKERS_TO_LAUNCH 100
|
||||
int num_launched = 0;
|
||||
TimestampTz now = 0;
|
||||
slist_mutable_iter iter;
|
||||
|
||||
/*
|
||||
* During crash recovery, we have no need to be called until the state
|
||||
* transition out of recovery.
|
||||
*/
|
||||
if (g_instance.fatal_error) {
|
||||
g_instance.bgworker_cxt.start_worker_needed = false;
|
||||
g_instance.bgworker_cxt.have_crashed_worker = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't need to be called again unless we find a reason for it below */
|
||||
g_instance.bgworker_cxt.start_worker_needed = false;
|
||||
g_instance.bgworker_cxt.have_crashed_worker = false;
|
||||
|
||||
slist_foreach_modify(iter, &BackgroundWorkerList) {
|
||||
RegisteredBgWorker *rw;
|
||||
|
||||
rw = slist_container(RegisteredBgWorker, rw_lnode, iter.cur);
|
||||
|
||||
/* ignore if already running */
|
||||
if (rw->rw_pid != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if marked for death, clean up and remove from list */
|
||||
if (rw->rw_terminate) {
|
||||
ForgetBackgroundWorker(&iter);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this worker has crashed previously, maybe it needs to be
|
||||
* restarted (unless on registration it specified it doesn't want to
|
||||
* be restarted at all). Check how long ago did a crash last happen.
|
||||
* If the last crash is too recent, don't start it right away; let it
|
||||
* be restarted once enough time has passed.
|
||||
*/
|
||||
if (rw->rw_crashed_at != 0) {
|
||||
if (rw->rw_worker.bgw_restart_time == BGW_NEVER_RESTART) {
|
||||
ThreadId notify_pid = rw->rw_worker.bgw_notify_pid;
|
||||
|
||||
ForgetBackgroundWorker(&iter);
|
||||
|
||||
/* Report worker is gone now. */
|
||||
if (notify_pid != 0) {
|
||||
(void)gs_signal_send(notify_pid, SIGUSR1);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* read system time only when needed */
|
||||
if (now == 0) {
|
||||
now = GetCurrentTimestamp();
|
||||
}
|
||||
|
||||
if (!TimestampDifferenceExceeds(rw->rw_crashed_at, now,
|
||||
rw->rw_worker.bgw_restart_time * 1000)) {
|
||||
/* Set flag to remember that we have workers to start later */
|
||||
g_instance.bgworker_cxt.have_crashed_worker = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bgworker_should_start_now(rw->rw_worker.bgw_start_time)) {
|
||||
/* reset crash time before trying to start worker */
|
||||
rw->rw_crashed_at = 0;
|
||||
|
||||
/*
|
||||
* Try to start the worker.
|
||||
*
|
||||
* On failure, give up processing workers for now, but set
|
||||
* start_worker_needed so we'll come back here on the next iteration
|
||||
* of ServerLoop to try again. (We don't want to wait, because
|
||||
* there might be additional ready-to-run workers.) We could set
|
||||
* have_crashed_worker as well, since this worker is now marked
|
||||
* crashed, but there's no need because the next run of this
|
||||
* function will do that.
|
||||
*/
|
||||
if (!do_start_bgworker(rw)) {
|
||||
g_instance.bgworker_cxt.start_worker_needed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we've launched as many workers as allowed, quit, but have
|
||||
* ServerLoop call us again to look for additional ready-to-run
|
||||
* workers. There might not be any, but we'll find out the next
|
||||
* time we run.
|
||||
*/
|
||||
if (++num_launched >= MAX_BGWORKERS_TO_LAUNCH) {
|
||||
g_instance.bgworker_cxt.start_worker_needed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* When a backend asks to be notified about worker state changes, we
|
||||
* set a flag in its backend entry. The background worker machinery needs
|
||||
* to know when such backends exit.
|
||||
*/
|
||||
bool
|
||||
PostmasterMarkPIDForWorkerNotify(ThreadId pid)
|
||||
{
|
||||
int count = MaxLivePostmasterChildren();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
Backend* bp = &g_instance.backend_array[i];
|
||||
if (bp->pid == pid)
|
||||
{
|
||||
bp->bgworker_notify = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
#ifndef WIN32
|
||||
#define write_inheritable_socket(dest, src) ((*(dest) = (src)))
|
||||
|
|
@ -7965,6 +8390,7 @@ Backend* AssignFreeBackEnd(int slot)
|
|||
bn->pid = 0;
|
||||
bn->cancel_key = 0;
|
||||
bn->dead_end = false;
|
||||
bn->bgworker_notify = false;
|
||||
return bn;
|
||||
}
|
||||
|
||||
|
|
@ -9952,7 +10378,7 @@ int GaussDbThreadMain(knl_thread_arg* arg)
|
|||
commAuxiliaryMain();
|
||||
proc_exit(0);
|
||||
} break;
|
||||
|
||||
|
||||
#ifdef ENABLE_MULTIPLE_NODES
|
||||
case COMM_POOLER_CLEAN: {
|
||||
InitProcessAndShareMemory();
|
||||
|
|
@ -9960,6 +10386,14 @@ int GaussDbThreadMain(knl_thread_arg* arg)
|
|||
proc_exit(0);
|
||||
} break;
|
||||
#endif
|
||||
|
||||
case BACKGROUND_WORKER: {
|
||||
IsBackgroundWorker = true;
|
||||
InitProcessAndShareMemory();
|
||||
StartBackgroundWorker(arg->payload);
|
||||
proc_exit(0);
|
||||
} break;
|
||||
|
||||
default:
|
||||
ereport(PANIC, (errmsg("unsupport thread role type %d", arg->role)));
|
||||
break;
|
||||
|
|
@ -10011,7 +10445,8 @@ static GaussdbThreadEntry GaussdbThreadEntryGate[] = {GaussDbThreadMain<MASTER>,
|
|||
GaussDbThreadMain<COMM_RECEIVERFLOWER>,
|
||||
GaussDbThreadMain<COMM_RECEIVER>,
|
||||
GaussDbThreadMain<COMM_AUXILIARY>,
|
||||
GaussDbThreadMain<COMM_POOLER_CLEAN>};
|
||||
GaussDbThreadMain<COMM_POOLER_CLEAN>,
|
||||
GaussDbThreadMain<BACKGROUND_WORKER>};
|
||||
|
||||
const char* GaussdbThreadName[] = {"main",
|
||||
"worker",
|
||||
|
|
@ -10055,7 +10490,8 @@ const char* GaussdbThreadName[] = {"main",
|
|||
"communicator receiver flower",
|
||||
"communicator receiver loop",
|
||||
"communicator auxiliary",
|
||||
"communicator pooler auto cleaner"};
|
||||
"communicator pooler auto cleaner",
|
||||
"background worker"};
|
||||
|
||||
GaussdbThreadEntry GetThreadEntry(knl_thread_role role)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ ifneq "$(MAKECMDGOALS)" "clean"
|
|||
endif
|
||||
endif
|
||||
endif
|
||||
OBJS= stmt_retry.o dest.o fastpath.o postgres.o pquery.o utility.o auditfuncs.o
|
||||
OBJS= autonomous.o stmt_retry.o dest.o fastpath.o postgres.o pquery.o utility.o auditfuncs.o
|
||||
|
||||
ifneq (,$(filter $(PORTNAME),cygwin win32))
|
||||
override CPPFLAGS += -fPIC -DWIN32_STACK_RLIMIT=$(WIN32_STACK_RLIMIT)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,869 @@
|
|||
/*--------------------------------------------------------------------------
|
||||
*
|
||||
* autonomous.cpp
|
||||
* Run SQL commands using a background worker.
|
||||
*
|
||||
* Copyright (C) 2014, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/gausskernel/process/tcop/autonomous.cpp
|
||||
*
|
||||
*
|
||||
* This implements a C API to open an autonomous session and run SQL queries
|
||||
* in it. The session looks much like a normal database connection, but it is
|
||||
* always to the same database, and there is no authentication needed. The
|
||||
* "backend" for that connection is a background worker. The normal backend
|
||||
* and the autonomous session worker communicate over the normal FE/BE
|
||||
* protocol.
|
||||
*
|
||||
* Types:
|
||||
*
|
||||
* AutonomousSession -- opaque connection handle
|
||||
* AutonomousPreparedStatement -- opaque prepared statement handle
|
||||
* AutonomousResult -- query result
|
||||
*
|
||||
* Functions:
|
||||
*
|
||||
* AutonomousSessionStart() -- start a session (launches background worker)
|
||||
* and return a handle
|
||||
*
|
||||
* AutonomousSessionEnd() -- close session and free resources
|
||||
*
|
||||
* AutonomousSessionExecute() -- run SQL string and return result (rows or
|
||||
* status)
|
||||
*
|
||||
* AutonomousSessionPrepare() -- prepare an SQL string for subsequent
|
||||
* execution
|
||||
*
|
||||
* AutonomousSessionExecutePrepared() -- run prepared statement
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
#include "gs_thread.h"
|
||||
|
||||
#include "access/htup.h"
|
||||
#include "access/tupdesc.h"
|
||||
#include "access/xact.h"
|
||||
#include "commands/async.h"
|
||||
#include "commands/variable.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "libpq/libpq.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "libpq/pqmq.h"
|
||||
#include "libpq/pqsignal.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/pg_list.h"
|
||||
#include "pgstat.h"
|
||||
#include "postmaster/bgworker.h"
|
||||
#include "storage/shm_mq.h"
|
||||
#include "storage/shm_toc.h"
|
||||
#include "tcop/autonomous.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/resowner.h"
|
||||
#include "utils/ps_status.h"
|
||||
|
||||
/* Table-of-contents constants for our dynamic shared memory segment. */
|
||||
#define AUTONOMOUS_MAGIC 0x50674267
|
||||
|
||||
#define AUTONOMOUS_KEY_FIXED_DATA 0
|
||||
#define AUTONOMOUS_KEY_GUC 1
|
||||
#define AUTONOMOUS_KEY_COMMAND_QUEUE 2
|
||||
#define AUTONOMOUS_KEY_RESPONSE_QUEUE 3
|
||||
#define AUTONOMOUS_NKEYS 4
|
||||
|
||||
#define AUTONOMOUS_QUEUE_SIZE 16384
|
||||
|
||||
/* Fixed-size data passed via our dynamic shared memory segment. */
|
||||
struct autonomous_session_fixed_data {
|
||||
Oid database_id;
|
||||
Oid authenticated_user_id;
|
||||
Oid current_user_id;
|
||||
int sec_context;
|
||||
};
|
||||
|
||||
struct AutonomousSession {
|
||||
char *seg;
|
||||
BackgroundWorkerHandle *worker_handle;
|
||||
shm_mq_handle *command_qh;
|
||||
shm_mq_handle *response_qh;
|
||||
int transaction_status;
|
||||
};
|
||||
|
||||
struct AutonomousPreparedStatement {
|
||||
AutonomousSession *session;
|
||||
Oid *argtypes;
|
||||
TupleDesc tupdesc;
|
||||
};
|
||||
|
||||
static void shm_mq_receive_stringinfo(shm_mq_handle *qh, StringInfoData *msg);
|
||||
static void autonomous_check_client_encoding_hook(void);
|
||||
static TupleDesc TupleDesc_from_RowDescription(StringInfo msg);
|
||||
static HeapTuple HeapTuple_from_DataRow(TupleDesc tupdesc, StringInfo msg);
|
||||
static void forward_NotifyResponse(StringInfo msg);
|
||||
static void rethrow_errornotice(StringInfo msg);
|
||||
static void invalid_protocol_message(char msgtype);
|
||||
|
||||
AutonomousSession * AutonomousSessionStart(void)
|
||||
{
|
||||
BackgroundWorker worker = {0};
|
||||
ThreadId pid;
|
||||
AutonomousSession *session = NULL;
|
||||
shm_toc_estimator e;
|
||||
Size segsize;
|
||||
Size guc_len;
|
||||
char *gucstate = NULL;
|
||||
char *seg = NULL;
|
||||
shm_toc *toc = NULL;
|
||||
autonomous_session_fixed_data *fdata = NULL;
|
||||
shm_mq *command_mq = NULL;
|
||||
shm_mq *response_mq = NULL;
|
||||
BgwHandleStatus bgwstatus;
|
||||
StringInfoData msg;
|
||||
char msgtype;
|
||||
errno_t rc;
|
||||
|
||||
session = (AutonomousSession *)palloc(sizeof(*session));
|
||||
|
||||
shm_toc_initialize_estimator(&e);
|
||||
shm_toc_estimate_chunk(&e, sizeof(autonomous_session_fixed_data));
|
||||
shm_toc_estimate_chunk(&e, AUTONOMOUS_QUEUE_SIZE);
|
||||
shm_toc_estimate_chunk(&e, AUTONOMOUS_QUEUE_SIZE);
|
||||
guc_len = EstimateGUCStateSpace();
|
||||
shm_toc_estimate_chunk(&e, guc_len);
|
||||
shm_toc_estimate_keys(&e, AUTONOMOUS_NKEYS);
|
||||
segsize = shm_toc_estimate(&e);
|
||||
seg = (char *)palloc(sizeof(char) * segsize);
|
||||
|
||||
session->seg = seg;
|
||||
|
||||
toc = shm_toc_create(AUTONOMOUS_MAGIC, seg, segsize);
|
||||
|
||||
/* Store fixed-size data in dynamic shared memory. */
|
||||
fdata = (autonomous_session_fixed_data *)shm_toc_allocate(toc, sizeof(*fdata));
|
||||
fdata->database_id = u_sess->proc_cxt.MyDatabaseId;
|
||||
fdata->authenticated_user_id = GetAuthenticatedUserId();
|
||||
GetUserIdAndSecContext(&fdata->current_user_id, &fdata->sec_context);
|
||||
shm_toc_insert(toc, AUTONOMOUS_KEY_FIXED_DATA, fdata);
|
||||
|
||||
/* Store GUC state in dynamic shared memory. */
|
||||
gucstate = (char *)shm_toc_allocate(toc, guc_len);
|
||||
SerializeGUCState(guc_len, gucstate);
|
||||
shm_toc_insert(toc, AUTONOMOUS_KEY_GUC, gucstate);
|
||||
|
||||
command_mq = shm_mq_create(shm_toc_allocate(toc, AUTONOMOUS_QUEUE_SIZE),
|
||||
AUTONOMOUS_QUEUE_SIZE);
|
||||
shm_toc_insert(toc, AUTONOMOUS_KEY_COMMAND_QUEUE, command_mq);
|
||||
shm_mq_set_sender(command_mq, t_thrd.proc);
|
||||
|
||||
response_mq = shm_mq_create(shm_toc_allocate(toc, AUTONOMOUS_QUEUE_SIZE),
|
||||
AUTONOMOUS_QUEUE_SIZE);
|
||||
shm_toc_insert(toc, AUTONOMOUS_KEY_RESPONSE_QUEUE, response_mq);
|
||||
shm_mq_set_receiver(response_mq, t_thrd.proc);
|
||||
|
||||
session->command_qh = shm_mq_attach(command_mq, seg, NULL);
|
||||
session->response_qh = shm_mq_attach(response_mq, seg, NULL);
|
||||
|
||||
worker.bgw_flags =
|
||||
BGWORKER_SHMEM_ACCESS | BGWORKER_BACKEND_DATABASE_CONNECTION;
|
||||
worker.bgw_start_time = BgWorkerStart_ConsistentState;
|
||||
worker.bgw_restart_time = BGW_NEVER_RESTART;
|
||||
rc = snprintf_s(worker.bgw_library_name, BGW_MAXLEN, BGW_MAXLEN, "postgres");
|
||||
securec_check_ss(rc, "\0", "\0");
|
||||
rc = snprintf_s(worker.bgw_function_name, BGW_MAXLEN, BGW_MAXLEN, "autonomous_worker_main");
|
||||
securec_check_ss(rc, "\0", "\0");
|
||||
rc = snprintf_s(worker.bgw_name, BGW_MAXLEN, BGW_MAXLEN, "autonomous session by PID %lu",
|
||||
t_thrd.proc_cxt.MyProcPid);
|
||||
securec_check_ss(rc, "\0", "\0");
|
||||
worker.bgw_main_arg = PointerGetDatum(seg);
|
||||
worker.bgw_notify_pid = t_thrd.proc_cxt.MyProcPid;
|
||||
|
||||
if (!RegisterDynamicBackgroundWorker(&worker, &session->worker_handle))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
|
||||
errmsg("could not register background process"),
|
||||
errhint("You might need to increase max_background_workers.")));
|
||||
|
||||
shm_mq_set_handle(session->command_qh, session->worker_handle);
|
||||
shm_mq_set_handle(session->response_qh, session->worker_handle);
|
||||
|
||||
bgwstatus = WaitForBackgroundWorkerStartup(session->worker_handle, &pid);
|
||||
if (bgwstatus != BGWH_STARTED)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
|
||||
errmsg("could not start background worker")));
|
||||
|
||||
do {
|
||||
ereport(LOG, (errmsg("front begin receive msg")));
|
||||
shm_mq_receive_stringinfo(session->response_qh, &msg);
|
||||
ereport(LOG, (errmsg("front end receive msg")));
|
||||
ereport(LOG, (errmsg("front function AutonomousSessionStart receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case 'E':
|
||||
case 'N':
|
||||
rethrow_errornotice(&msg);
|
||||
break;
|
||||
case 'Z':
|
||||
session->transaction_status = pq_getmsgbyte(&msg);
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
default:
|
||||
invalid_protocol_message(msgtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (msgtype != 'Z');
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void AutonomousSessionEnd(AutonomousSession *session)
|
||||
{
|
||||
StringInfoData msg;
|
||||
BgwHandleStatus bgwstatus;
|
||||
if (session->transaction_status == 'T')
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("autonomous session ended with transaction block open")));
|
||||
|
||||
pq_redirect_to_shm_mq(session->command_qh);
|
||||
pq_beginmessage(&msg, 'X');
|
||||
pq_endmessage(&msg);
|
||||
pq_stop_redirect_to_shm_mq();
|
||||
bgwstatus = WaitForBackgroundWorkerShutdown(session->worker_handle);
|
||||
if (bgwstatus != BGWH_STOPPED)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
|
||||
errmsg("could not stop background worker")));
|
||||
pfree(session->worker_handle);
|
||||
pfree(session->seg);
|
||||
pfree(session);
|
||||
}
|
||||
|
||||
AutonomousResult *AutonomousSessionExecute(AutonomousSession *session, const char *sql)
|
||||
{
|
||||
StringInfoData msg;
|
||||
char msgtype;
|
||||
AutonomousResult *result = NULL;
|
||||
|
||||
pq_redirect_to_shm_mq(session->command_qh);
|
||||
pq_beginmessage(&msg, 'Q');
|
||||
pq_sendstring(&msg, sql);
|
||||
pq_endmessage(&msg);
|
||||
pq_stop_redirect_to_shm_mq();
|
||||
|
||||
result = (AutonomousResult *)palloc0(sizeof(*result));
|
||||
|
||||
do {
|
||||
shm_mq_receive_stringinfo(session->response_qh, &msg);
|
||||
ereport(LOG, (errmsg("front function AutonomousSessionExecute receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case 'A':
|
||||
forward_NotifyResponse(&msg);
|
||||
break;
|
||||
case 'C':
|
||||
{
|
||||
const char *tag = pq_getmsgstring(&msg);
|
||||
result->command = pstrdup(tag);
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
if (!result->tupdesc)
|
||||
elog(ERROR, "no T before D");
|
||||
result->tuples = lappend(result->tuples, HeapTuple_from_DataRow(result->tupdesc, &msg));
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
case 'E':
|
||||
case 'N':
|
||||
rethrow_errornotice(&msg);
|
||||
break;
|
||||
case 'T':
|
||||
if (result->tupdesc)
|
||||
elog(ERROR, "already received a T message");
|
||||
result->tupdesc = TupleDesc_from_RowDescription(&msg);
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
case 'Z':
|
||||
session->transaction_status = pq_getmsgbyte(&msg);
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
default:
|
||||
invalid_protocol_message(msgtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (msgtype != 'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
AutonomousPreparedStatement *AutonomousSessionPrepare(AutonomousSession *session, const char *sql, int16 nargs,
|
||||
Oid argtypes[], const char *argnames[])
|
||||
{
|
||||
AutonomousPreparedStatement *result = NULL;
|
||||
StringInfoData msg;
|
||||
int16 i;
|
||||
char msgtype;
|
||||
|
||||
pq_redirect_to_shm_mq(session->command_qh);
|
||||
pq_beginmessage(&msg, 'P');
|
||||
pq_sendstring(&msg, "");
|
||||
pq_sendstring(&msg, sql);
|
||||
pq_sendint16(&msg, (uint16)nargs);
|
||||
for (i = 0; i < nargs; i++)
|
||||
pq_sendint32(&msg, (uint32)argtypes[i]);
|
||||
if (argnames)
|
||||
for (i = 0; i < nargs; i++)
|
||||
pq_sendstring(&msg, argnames[i]);
|
||||
pq_endmessage(&msg);
|
||||
pq_stop_redirect_to_shm_mq();
|
||||
|
||||
result = (AutonomousPreparedStatement *)palloc0(sizeof(*result));
|
||||
result->session = session;
|
||||
result->argtypes = (Oid *)palloc(nargs * sizeof(*result->argtypes));
|
||||
errno_t rc;
|
||||
rc = memcpy_s(result->argtypes, nargs * sizeof(*result->argtypes), argtypes, nargs * sizeof(*result->argtypes));
|
||||
securec_check(rc, "\0", "\0");
|
||||
|
||||
shm_mq_receive_stringinfo(session->response_qh, &msg);
|
||||
ereport(LOG, (errmsg("front function AutonomousSessionPrepare receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case '1':
|
||||
break;
|
||||
case 'E':
|
||||
rethrow_errornotice(&msg);
|
||||
break;
|
||||
default:
|
||||
invalid_protocol_message(msgtype);
|
||||
break;
|
||||
}
|
||||
|
||||
pq_redirect_to_shm_mq(session->command_qh);
|
||||
pq_beginmessage(&msg, 'D');
|
||||
pq_sendbyte(&msg, 'S');
|
||||
pq_sendstring(&msg, "");
|
||||
pq_endmessage(&msg);
|
||||
pq_stop_redirect_to_shm_mq();
|
||||
|
||||
do {
|
||||
shm_mq_receive_stringinfo(session->response_qh, &msg);
|
||||
ereport(LOG, (errmsg("front function AutonomousSessionPrepare receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case 'A':
|
||||
forward_NotifyResponse(&msg);
|
||||
break;
|
||||
case 'E':
|
||||
rethrow_errornotice(&msg);
|
||||
break;
|
||||
case 'n':
|
||||
break;
|
||||
case 't':
|
||||
/* ignore for now */
|
||||
break;
|
||||
case 'T':
|
||||
if (result->tupdesc)
|
||||
elog(ERROR, "already received a T message");
|
||||
result->tupdesc = TupleDesc_from_RowDescription(&msg);
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
default:
|
||||
invalid_protocol_message(msgtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (msgtype != 'n' && msgtype != 'T');
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AutonomousResult *AutonomousSessionExecutePrepared(AutonomousPreparedStatement *stmt, int16 nargs,
|
||||
Datum *values, bool *nulls)
|
||||
{
|
||||
AutonomousSession *session = NULL;
|
||||
StringInfoData msg;
|
||||
AutonomousResult *result = NULL;
|
||||
char msgtype;
|
||||
int16 i;
|
||||
|
||||
session = stmt->session;
|
||||
|
||||
pq_redirect_to_shm_mq(session->command_qh);
|
||||
pq_beginmessage(&msg, 'B');
|
||||
pq_sendstring(&msg, "");
|
||||
pq_sendstring(&msg, "");
|
||||
pq_sendint16(&msg, 1); /* number of parameter format codes */
|
||||
pq_sendint16(&msg, 1);
|
||||
pq_sendint16(&msg, (uint16)nargs); /* number of parameter values */
|
||||
for (i = 0; i < nargs; i++) {
|
||||
if (nulls[i])
|
||||
pq_sendint32(&msg, -1);
|
||||
else {
|
||||
Oid typsend;
|
||||
bool typisvarlena;
|
||||
bytea *outputbytes = NULL;
|
||||
|
||||
getTypeBinaryOutputInfo(stmt->argtypes[i], &typsend, &typisvarlena);
|
||||
outputbytes = OidSendFunctionCall(typsend, values[i]);
|
||||
pq_sendint32(&msg, VARSIZE(outputbytes) - VARHDRSZ);
|
||||
pq_sendbytes(&msg, VARDATA(outputbytes), VARSIZE(outputbytes) - VARHDRSZ);
|
||||
pfree(outputbytes);
|
||||
}
|
||||
}
|
||||
pq_sendint16(&msg, 1); /* number of result column format codes */
|
||||
pq_sendint16(&msg, 1);
|
||||
pq_endmessage(&msg);
|
||||
pq_stop_redirect_to_shm_mq();
|
||||
|
||||
shm_mq_receive_stringinfo(session->response_qh, &msg);
|
||||
ereport(LOG, (errmsg("front function AutonomousSessionExecutePrepared receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case '2':
|
||||
break;
|
||||
case 'E':
|
||||
rethrow_errornotice(&msg);
|
||||
break;
|
||||
default:
|
||||
invalid_protocol_message(msgtype);
|
||||
break;
|
||||
}
|
||||
|
||||
pq_redirect_to_shm_mq(session->command_qh);
|
||||
pq_beginmessage(&msg, 'E');
|
||||
pq_sendstring(&msg, "");
|
||||
pq_sendint32(&msg, 0);
|
||||
pq_endmessage(&msg);
|
||||
pq_stop_redirect_to_shm_mq();
|
||||
|
||||
result = (AutonomousResult *)palloc0(sizeof(*result));
|
||||
result->tupdesc = stmt->tupdesc;
|
||||
|
||||
do {
|
||||
shm_mq_receive_stringinfo(session->response_qh, &msg);
|
||||
ereport(LOG, (errmsg("front function AutonomousSessionExecutePrepared receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case 'A':
|
||||
forward_NotifyResponse(&msg);
|
||||
break;
|
||||
case 'C':
|
||||
{
|
||||
const char *tag = pq_getmsgstring(&msg);
|
||||
result->command = pstrdup(tag);
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
if (!stmt->tupdesc)
|
||||
elog(ERROR, "did not expect any rows");
|
||||
result->tuples = lappend(result->tuples, HeapTuple_from_DataRow(stmt->tupdesc, &msg));
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
case 'E':
|
||||
case 'N':
|
||||
rethrow_errornotice(&msg);
|
||||
break;
|
||||
default:
|
||||
invalid_protocol_message(msgtype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (msgtype != 'C');
|
||||
|
||||
pq_redirect_to_shm_mq(session->command_qh);
|
||||
pq_putemptymessage('S');
|
||||
pq_stop_redirect_to_shm_mq();
|
||||
|
||||
shm_mq_receive_stringinfo(session->response_qh, &msg);
|
||||
ereport(LOG, (errmsg("front function AutonomousSessionExecutePrepared receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case 'A':
|
||||
forward_NotifyResponse(&msg);
|
||||
break;
|
||||
case 'Z':
|
||||
session->transaction_status = pq_getmsgbyte(&msg);
|
||||
pq_getmsgend(&msg);
|
||||
break;
|
||||
default:
|
||||
invalid_protocol_message(msgtype);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void autonomous_worker_main(Datum main_arg)
|
||||
{
|
||||
char *seg = NULL;
|
||||
shm_toc *toc = NULL;
|
||||
autonomous_session_fixed_data *fdata = NULL;
|
||||
char *gucstate = NULL;
|
||||
shm_mq *command_mq = NULL;
|
||||
shm_mq *response_mq = NULL;
|
||||
shm_mq_handle *command_qh = NULL;
|
||||
shm_mq_handle *response_qh = NULL;
|
||||
StringInfoData msg;
|
||||
|
||||
char msgtype;
|
||||
|
||||
(void)gspqsignal(SIGTERM, die);
|
||||
BackgroundWorkerUnblockSignals();
|
||||
|
||||
t_thrd.autonomous_cxt.isnested = true;
|
||||
|
||||
/* Set up a memory context and resource owner. */
|
||||
Assert(t_thrd.utils_cxt.CurrentResourceOwner == NULL);
|
||||
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "autonomous");
|
||||
CurrentMemoryContext = AllocSetContextCreate(t_thrd.top_mem_cxt,
|
||||
"autonomous session",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
seg = (char *)DatumGetPointer(main_arg);
|
||||
if (seg == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("could not map dynamic shared memory segment")));
|
||||
|
||||
toc = shm_toc_attach(AUTONOMOUS_MAGIC, seg);
|
||||
if (toc == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("bad magic number in dynamic shared memory segment")));
|
||||
|
||||
/* Find data structures in dynamic shared memory. */
|
||||
fdata = (autonomous_session_fixed_data *)shm_toc_lookup(toc, AUTONOMOUS_KEY_FIXED_DATA);
|
||||
|
||||
gucstate = (char *)shm_toc_lookup(toc, AUTONOMOUS_KEY_GUC);
|
||||
|
||||
command_mq = (shm_mq *)shm_toc_lookup(toc, AUTONOMOUS_KEY_COMMAND_QUEUE);
|
||||
shm_mq_set_receiver(command_mq, t_thrd.proc);
|
||||
command_qh = shm_mq_attach(command_mq, seg, NULL);
|
||||
|
||||
response_mq = (shm_mq *)shm_toc_lookup(toc, AUTONOMOUS_KEY_RESPONSE_QUEUE);
|
||||
shm_mq_set_sender(response_mq, t_thrd.proc);
|
||||
response_qh = shm_mq_attach(response_mq, seg, NULL);
|
||||
|
||||
pq_redirect_to_shm_mq(response_qh);
|
||||
BackgroundWorkerInitializeConnectionByOid(fdata->database_id,
|
||||
fdata->authenticated_user_id);
|
||||
|
||||
(void)SetClientEncoding(GetDatabaseEncoding());
|
||||
|
||||
StartTransactionCommand();
|
||||
RestoreGUCState(gucstate);
|
||||
CommitTransactionCommand();
|
||||
|
||||
process_local_preload_libraries();
|
||||
|
||||
SetUserIdAndSecContext(fdata->current_user_id, fdata->sec_context);
|
||||
|
||||
t_thrd.postgres_cxt.whereToSendOutput = DestRemote;
|
||||
ReadyForQuery((CommandDest)t_thrd.postgres_cxt.whereToSendOutput);
|
||||
|
||||
t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt,
|
||||
"MessageContext",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
ALLOCSET_DEFAULT_MAXSIZE);
|
||||
|
||||
do {
|
||||
(void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt);
|
||||
MemoryContextResetAndDeleteChildren(t_thrd.mem_cxt.msg_mem_cxt);
|
||||
|
||||
if (IsAbortedTransactionBlockState()) {
|
||||
set_ps_display("idle in transaction (aborted)", false);
|
||||
pgstat_report_activity(STATE_IDLEINTRANSACTION_ABORTED, NULL);
|
||||
} else if (IsTransactionOrTransactionBlock()) {
|
||||
set_ps_display("idle in transaction", false);
|
||||
pgstat_report_activity(STATE_IDLEINTRANSACTION, NULL);
|
||||
} else {
|
||||
ProcessCompletedNotifies();
|
||||
pgstat_report_stat(false);
|
||||
|
||||
set_ps_display("idle", false);
|
||||
pgstat_report_activity(STATE_IDLE, NULL);
|
||||
}
|
||||
|
||||
shm_mq_receive_stringinfo(command_qh, &msg);
|
||||
ereport(LOG, (errmsg("bgworker receive msg %s", msg.data)));
|
||||
msgtype = pq_getmsgbyte(&msg);
|
||||
|
||||
switch (msgtype) {
|
||||
case 'B':
|
||||
{
|
||||
SetCurrentStatementStartTimestamp();
|
||||
exec_bind_message(&msg);
|
||||
break;
|
||||
}
|
||||
case 'D':
|
||||
{
|
||||
int describe_type;
|
||||
const char *describe_target;
|
||||
|
||||
SetCurrentStatementStartTimestamp();
|
||||
|
||||
describe_type = pq_getmsgbyte(&msg);
|
||||
describe_target = pq_getmsgstring(&msg);
|
||||
pq_getmsgend(&msg);
|
||||
|
||||
switch (describe_type) {
|
||||
case 'S':
|
||||
exec_describe_statement_message(describe_target);
|
||||
break;
|
||||
#ifdef TODO
|
||||
case 'P':
|
||||
exec_describe_portal_message(describe_target);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||
errmsg("invalid DESCRIBE message subtype %d",
|
||||
describe_type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'E':
|
||||
{
|
||||
const char *portal_name;
|
||||
int max_rows;
|
||||
|
||||
SetCurrentStatementStartTimestamp();
|
||||
|
||||
portal_name = pq_getmsgstring(&msg);
|
||||
max_rows = (int)pq_getmsgint(&msg, 4);
|
||||
pq_getmsgend(&msg);
|
||||
|
||||
exec_execute_message(portal_name, max_rows);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
{
|
||||
const char *stmt_name;
|
||||
const char *query_string;
|
||||
uint16 numParams;
|
||||
Oid *paramTypes = NULL;
|
||||
char **paramTypeNames = NULL;
|
||||
|
||||
SetCurrentStatementStartTimestamp();
|
||||
|
||||
stmt_name = pq_getmsgstring(&msg);
|
||||
query_string = pq_getmsgstring(&msg);
|
||||
numParams = pq_getmsgint(&msg, 2);
|
||||
if (numParams > 0) {
|
||||
int i;
|
||||
|
||||
paramTypes = (Oid *)palloc(numParams * sizeof(Oid));
|
||||
for (i = 0; i < numParams; i++)
|
||||
paramTypes[i] = pq_getmsgint(&msg, 4);
|
||||
}
|
||||
/* If data left in message, read parameter names. */
|
||||
if (msg.cursor != msg.len) {
|
||||
int i;
|
||||
|
||||
paramTypeNames = (char **)palloc(numParams * sizeof(char *));
|
||||
for (i = 0; i < numParams; i++)
|
||||
paramTypeNames[i] = (char *)pq_getmsgstring(&msg);
|
||||
}
|
||||
pq_getmsgend(&msg);
|
||||
|
||||
exec_parse_message(query_string, stmt_name, paramTypes, paramTypeNames, (int)numParams);
|
||||
break;
|
||||
}
|
||||
case 'Q':
|
||||
{
|
||||
const char *sql;
|
||||
int save_log_statement;
|
||||
bool save_log_duration;
|
||||
int save_log_min_duration_statement;
|
||||
|
||||
sql = pq_getmsgstring(&msg);
|
||||
pq_getmsgend(&msg);
|
||||
|
||||
/* XXX room for improvement */
|
||||
save_log_statement = u_sess->attr.attr_common.log_statement;
|
||||
save_log_duration = u_sess->attr.attr_sql.log_duration;
|
||||
save_log_min_duration_statement = u_sess->attr.attr_storage.log_min_duration_statement;
|
||||
|
||||
check_client_encoding_hook = autonomous_check_client_encoding_hook;
|
||||
u_sess->attr.attr_common.log_statement = LOGSTMT_NONE;
|
||||
u_sess->attr.attr_sql.log_duration = false;
|
||||
u_sess->attr.attr_storage.log_min_duration_statement = -1;
|
||||
|
||||
SetCurrentStatementStartTimestamp();
|
||||
exec_simple_query(sql, QUERY_MESSAGE);
|
||||
|
||||
u_sess->attr.attr_common.log_statement = save_log_statement;
|
||||
u_sess->attr.attr_sql.log_duration = save_log_duration;
|
||||
u_sess->attr.attr_storage.log_min_duration_statement = save_log_min_duration_statement;
|
||||
check_client_encoding_hook = NULL;
|
||||
|
||||
ReadyForQuery((CommandDest)t_thrd.postgres_cxt.whereToSendOutput);
|
||||
break;
|
||||
}
|
||||
case 'S':
|
||||
{
|
||||
pq_getmsgend(&msg);
|
||||
finish_xact_command();
|
||||
ReadyForQuery((CommandDest)t_thrd.postgres_cxt.whereToSendOutput);
|
||||
break;
|
||||
}
|
||||
case 'X':
|
||||
break;
|
||||
default:
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||
errmsg("invalid protocol message type from autonomous session leader: %c",
|
||||
msgtype)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (msgtype != 'X');
|
||||
}
|
||||
|
||||
static void shm_mq_receive_stringinfo(shm_mq_handle *qh, StringInfoData *msg)
|
||||
{
|
||||
shm_mq_result res;
|
||||
Size nbytes = 0;
|
||||
void *data = NULL;
|
||||
|
||||
res = shm_mq_receive(qh, &nbytes, &data, false);
|
||||
if (res != SHM_MQ_SUCCESS)
|
||||
elog(ERROR, "shm_mq_receive failed: %d", res);
|
||||
initStringInfo(msg);
|
||||
appendBinaryStringInfo(msg, (const char*)data, (int)nbytes);
|
||||
}
|
||||
|
||||
static void autonomous_check_client_encoding_hook(void)
|
||||
{
|
||||
elog(ERROR, "cannot set client encoding in autonomous session");
|
||||
}
|
||||
|
||||
static TupleDesc TupleDesc_from_RowDescription(StringInfo msg)
|
||||
{
|
||||
TupleDesc tupdesc;
|
||||
int16 natts = pq_getmsgint(msg, 2);
|
||||
int16 i;
|
||||
|
||||
tupdesc = CreateTemplateTupleDesc(natts, false);
|
||||
for (i = 0; i < natts; i++) {
|
||||
const char *colname;
|
||||
Oid type_oid;
|
||||
uint32 typmod;
|
||||
uint16 format;
|
||||
|
||||
colname = pq_getmsgstring(msg);
|
||||
(void) pq_getmsgint(msg, 4); /* table OID */
|
||||
(void) pq_getmsgint(msg, 2); /* table attnum */
|
||||
type_oid = pq_getmsgint(msg, 4);
|
||||
(void) pq_getmsgint(msg, 2); /* type length */
|
||||
typmod = pq_getmsgint(msg, 4);
|
||||
format = pq_getmsgint(msg, 2);
|
||||
(void) format;
|
||||
#ifdef TODO
|
||||
/* XXX The protocol sometimes sends 0 (text) if the format is not
|
||||
* determined yet. We always use binary, so this check is probably
|
||||
* not useful. */
|
||||
if (format != 1)
|
||||
elog(ERROR, "format must be binary");
|
||||
#endif
|
||||
|
||||
TupleDescInitEntry(tupdesc, i + 1, colname, type_oid, typmod, 0);
|
||||
}
|
||||
return tupdesc;
|
||||
}
|
||||
|
||||
static HeapTuple HeapTuple_from_DataRow(TupleDesc tupdesc, StringInfo msg)
|
||||
{
|
||||
int16 natts = pq_getmsgint(msg, 2);
|
||||
int16 i;
|
||||
Datum *values;
|
||||
bool *nulls;
|
||||
StringInfoData buf;
|
||||
|
||||
Assert(tupdesc);
|
||||
|
||||
if (natts != tupdesc->natts)
|
||||
elog(ERROR, "malformed DataRow");
|
||||
|
||||
values = (Datum *)palloc(natts * sizeof(*values));
|
||||
nulls = (bool *)palloc(natts * sizeof(*nulls));
|
||||
initStringInfo(&buf);
|
||||
|
||||
for (i = 0; i < natts; i++) {
|
||||
int32 len = pq_getmsgint(msg, 4);
|
||||
|
||||
if (len < 0)
|
||||
nulls[i] = true;
|
||||
else {
|
||||
Oid recvid;
|
||||
Oid typioparams;
|
||||
|
||||
nulls[i] = false;
|
||||
|
||||
getTypeBinaryInputInfo(tupdesc->attrs[i]->atttypid,
|
||||
&recvid,
|
||||
&typioparams);
|
||||
resetStringInfo(&buf);
|
||||
appendBinaryStringInfo(&buf, pq_getmsgbytes(msg, len), len);
|
||||
values[i] = OidReceiveFunctionCall(recvid, &buf, typioparams,
|
||||
tupdesc->attrs[i]->atttypmod);
|
||||
}
|
||||
}
|
||||
|
||||
return heap_form_tuple(tupdesc, values, nulls);
|
||||
}
|
||||
|
||||
static void forward_NotifyResponse(StringInfo msg)
|
||||
{
|
||||
int32 pid;
|
||||
const char *channel;
|
||||
const char *payload;
|
||||
|
||||
pid = (int32)pq_getmsgint(msg, 4);
|
||||
channel = pq_getmsgrawstring(msg);
|
||||
payload = pq_getmsgrawstring(msg);
|
||||
pq_endmessage(msg);
|
||||
|
||||
NotifyMyFrontEnd(channel, payload, pid);
|
||||
}
|
||||
|
||||
|
||||
static void rethrow_errornotice(StringInfo msg)
|
||||
{
|
||||
ErrorData edata;
|
||||
|
||||
pq_parse_errornotice(msg, &edata);
|
||||
edata.elevel = Min(edata.elevel, ERROR);
|
||||
ThrowErrorData(&edata);
|
||||
}
|
||||
|
||||
|
||||
static void invalid_protocol_message(char msgtype)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||
errmsg("invalid protocol message type from autonomous session: %c",
|
||||
msgtype)));
|
||||
}
|
||||
|
||||
|
|
@ -203,7 +203,7 @@ static void get_query_result(TupleTableSlot* slot, DestReceiver* self);
|
|||
* @hdfs
|
||||
* Define different mesage type used for exec_simple_query
|
||||
*/
|
||||
typedef enum { QUERY_MESSAGE = 0, HYBRID_MESSAGE } MessageType;
|
||||
//typedef enum { QUERY_MESSAGE = 0, HYBRID_MESSAGE } MessageType;
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* decls for routines only used in this file
|
||||
|
|
@ -235,6 +235,8 @@ extern void CancelAutoAnalyze();
|
|||
extern List* RevalidateCachedQuery(CachedPlanSource* plansource);
|
||||
static void InitRecursiveCTEGlobalVariables(const PlannedStmt* planstmt);
|
||||
|
||||
THR_LOCAL bool needEnd = true;
|
||||
|
||||
bool StreamThreadAmI()
|
||||
{
|
||||
return (t_thrd.role == STREAM_WORKER);
|
||||
|
|
@ -1874,7 +1876,7 @@ void exec_init_poolhandles(void)
|
|||
* hybridmesage, this parameter will be set to 1 to tell us the normal query string
|
||||
* followed by information string. query_string = normal querystring + message.
|
||||
*/
|
||||
static void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL)
|
||||
void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg)
|
||||
{
|
||||
CommandDest dest = (CommandDest)t_thrd.postgres_cxt.whereToSendOutput;
|
||||
MemoryContext oldcontext;
|
||||
|
|
@ -2883,7 +2885,7 @@ static void exec_plan_with_params(StringInfo input_message)
|
|||
* If paramTypeNames is specified, paraTypes is filled with corresponding OIDs.
|
||||
* The caller is expected to allocate space for the paramTypes.
|
||||
*/
|
||||
static void exec_parse_message(const char* query_string, /* string to execute */
|
||||
void exec_parse_message(const char* query_string, /* string to execute */
|
||||
const char* stmt_name, /* name for prepared stmt */
|
||||
Oid* paramTypes, /* parameter types */
|
||||
char** paramTypeNames, /* parameter type names */
|
||||
|
|
@ -3104,7 +3106,7 @@ static void exec_parse_message(const char* query_string, /* string to execute */
|
|||
if (u_sess->attr.attr_common.log_parser_stats)
|
||||
ResetUsage();
|
||||
|
||||
query = parse_analyze_varparams(raw_parse_tree, query_string, ¶mTypes, &numParams);
|
||||
query = parse_analyze_varparams(raw_parse_tree, query_string, ¶mTypes, &numParams, paramTypeNames);
|
||||
|
||||
/* check cross engine queries */
|
||||
StorageEngineType storageEngineType = SE_TYPE_UNSPECIFIED;
|
||||
|
|
@ -3766,7 +3768,7 @@ static void exec_get_ddl_params(StringInfo input_message)
|
|||
*
|
||||
* Process a "Bind" message to create a portal from a prepared statement
|
||||
*/
|
||||
static void exec_bind_message(StringInfo input_message)
|
||||
void exec_bind_message(StringInfo input_message)
|
||||
{
|
||||
const char* portal_name = NULL;
|
||||
const char* stmt_name = NULL;
|
||||
|
|
@ -4336,7 +4338,7 @@ static void exec_bind_message(StringInfo input_message)
|
|||
*
|
||||
* Process an "Execute" message for a portal
|
||||
*/
|
||||
static void exec_execute_message(const char* portal_name, long max_rows)
|
||||
void exec_execute_message(const char* portal_name, long max_rows)
|
||||
{
|
||||
CommandDest dest;
|
||||
DestReceiver* receiver = NULL;
|
||||
|
|
@ -4801,7 +4803,7 @@ static int errdetail_recovery_conflict(void)
|
|||
*
|
||||
* Process a "Describe" message for a prepared statement
|
||||
*/
|
||||
static void exec_describe_statement_message(const char* stmt_name)
|
||||
void exec_describe_statement_message(const char* stmt_name)
|
||||
{
|
||||
CachedPlanSource* psrc = NULL;
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -290,6 +290,7 @@ static void knl_g_wlm_init(knl_g_wlm_context* wlm_cxt)
|
|||
|
||||
static void knl_g_shmem_init(knl_g_shmem_context* shmem_cxt)
|
||||
{
|
||||
shmem_cxt->max_parallel_workers = 8;
|
||||
shmem_cxt->MaxBackends = 100;
|
||||
shmem_cxt->MaxReserveBackendId = (AUXILIARY_BACKENDS + AV_LAUNCHER_PROCS);
|
||||
shmem_cxt->ThreadPoolGroupNum = 0;
|
||||
|
|
@ -316,6 +317,12 @@ static void knl_g_numa_init(knl_g_numa_context* numa_cxt)
|
|||
numa_cxt->allocIndex = 0;
|
||||
}
|
||||
|
||||
static void knl_g_bgworker_init(knl_g_bgworker_context* bgworker_cxt)
|
||||
{
|
||||
bgworker_cxt->start_worker_needed = true;
|
||||
bgworker_cxt->have_crashed_worker = false;
|
||||
}
|
||||
|
||||
void knl_instance_init()
|
||||
{
|
||||
g_instance.binaryupgrade = false;
|
||||
|
|
@ -363,6 +370,7 @@ void knl_instance_init()
|
|||
knl_g_dw_init(&g_instance.dw_cxt);
|
||||
knl_g_xlog_init(&g_instance.xlog_cxt);
|
||||
knl_g_numa_init(&g_instance.numa_cxt);
|
||||
knl_g_bgworker_init(&g_instance.bgworker_cxt);
|
||||
|
||||
MemoryContextSwitchTo(old_cxt);
|
||||
|
||||
|
|
|
|||
|
|
@ -1383,6 +1383,12 @@ static void knl_t_heartbeat_init(knl_t_heartbeat_context* heartbeat_cxt)
|
|||
heartbeat_cxt->state = NULL;
|
||||
}
|
||||
|
||||
static void knl_t_autonomous_init(knl_t_autonomous_context* autonomous_cxt)
|
||||
{
|
||||
autonomous_cxt->isnested = false;
|
||||
autonomous_cxt->sqlstmt = NULL;
|
||||
}
|
||||
|
||||
static void knl_t_mot_init(knl_t_mot_context* mot_cxt)
|
||||
{
|
||||
mot_cxt->last_error_code = 0;
|
||||
|
|
@ -1407,6 +1413,12 @@ void knl_thread_mot_init()
|
|||
knl_t_mot_init(&t_thrd.mot_cxt);
|
||||
}
|
||||
|
||||
void knl_t_bgworker_init(knl_t_bgworker_context* bgworker_cxt)
|
||||
{
|
||||
bgworker_cxt->background_worker_data = NULL;
|
||||
bgworker_cxt->my_bgworker_entry = NULL;
|
||||
}
|
||||
|
||||
void knl_thread_init(knl_thread_role role)
|
||||
{
|
||||
t_thrd.role = role;
|
||||
|
|
@ -1494,6 +1506,7 @@ void knl_thread_init(knl_thread_role role)
|
|||
knl_t_heartbeat_init(&t_thrd.heartbeat_cxt);
|
||||
knl_t_poolcleaner_init(&t_thrd.poolcleaner_cxt);
|
||||
knl_t_mot_init(&t_thrd.mot_cxt);
|
||||
knl_t_autonomous_init(&t_thrd.autonomous_cxt);
|
||||
}
|
||||
|
||||
void knl_thread_set_name(const char* name)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,6 @@ ifneq "$(MAKECMDGOALS)" "clean"
|
|||
endif
|
||||
endif
|
||||
OBJS = ipc.o ipci.o pmsignal.o procarray.o procsignal.o shmem.o shmqueue.o \
|
||||
sinval.o sinvaladt.o standby.o
|
||||
sinval.o sinvaladt.o standby.o shm_mq.o shm_toc.o
|
||||
|
||||
include $(top_srcdir)/src/gausskernel/common.mk
|
||||
include $(top_srcdir)/src/gausskernel/common.mk
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
#include "pgxc/nodemgr.h"
|
||||
#endif
|
||||
#include "postmaster/autovacuum.h"
|
||||
#include "postmaster/bgworker_internals.h"
|
||||
#include "postmaster/bgwriter.h"
|
||||
#include "postmaster/postmaster.h"
|
||||
#include "replication/slot.h"
|
||||
|
|
@ -136,6 +137,7 @@ void CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
|
|||
size = add_size(size, CLOGShmemSize());
|
||||
size = add_size(size, CSNLOGShmemSize());
|
||||
size = add_size(size, TwoPhaseShmemSize());
|
||||
size = add_size(size, BackgroundWorkerShmemSize());
|
||||
size = add_size(size, MultiXactShmemSize());
|
||||
size = add_size(size, LWLockShmemSize());
|
||||
size = add_size(size, ProcArrayShmemSize());
|
||||
|
|
@ -275,6 +277,7 @@ void CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
|
|||
{
|
||||
TwoPhaseShmemInit();
|
||||
}
|
||||
BackgroundWorkerShmemInit();
|
||||
|
||||
/*
|
||||
* Set up shared-inval messaging
|
||||
|
|
|
|||
|
|
@ -306,6 +306,7 @@ void procsignal_sigusr1_handler(SIGNAL_ARGS)
|
|||
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
|
||||
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
|
||||
|
||||
SetLatch(&t_thrd.proc->procLatch);
|
||||
latch_sigusr1_handler();
|
||||
|
||||
errno = save_errno;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,242 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* shm_toc.cpp
|
||||
* shared memory segment table of contents
|
||||
*
|
||||
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/gausskernel/storage/ipc/shm_toc.cpp
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "storage/barrier.h"
|
||||
#include "storage/shm_toc.h"
|
||||
#include "storage/spin.h"
|
||||
|
||||
struct shm_toc_entry
|
||||
{
|
||||
uint64 key; /* Arbitrary identifier */
|
||||
uint64 offset; /* Bytes offset */
|
||||
};
|
||||
|
||||
struct shm_toc
|
||||
{
|
||||
uint64 toc_magic; /* Magic number for this TOC */
|
||||
slock_t toc_mutex; /* Spinlock for mutual exclusion */
|
||||
Size toc_total_bytes; /* Bytes managed by this TOC */
|
||||
Size toc_allocated_bytes; /* Bytes allocated of those managed */
|
||||
Size toc_nentry; /* Number of entries in TOC */
|
||||
shm_toc_entry toc_entry[FLEXIBLE_ARRAY_MEMBER];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize a region of shared memory with a table of contents.
|
||||
*/
|
||||
shm_toc *shm_toc_create(uint64 magic, void *address, Size nbytes)
|
||||
{
|
||||
shm_toc *toc = (shm_toc *) address;
|
||||
|
||||
Assert(nbytes > offsetof(shm_toc, toc_entry));
|
||||
toc->toc_magic = magic;
|
||||
SpinLockInit(&toc->toc_mutex);
|
||||
toc->toc_total_bytes = nbytes;
|
||||
toc->toc_allocated_bytes = 0;
|
||||
toc->toc_nentry = 0;
|
||||
|
||||
return toc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach to an existing table of contents. If the magic number found at
|
||||
* the target address doesn't match our expectations, returns NULL.
|
||||
*/
|
||||
extern shm_toc *shm_toc_attach(uint64 magic, void *address)
|
||||
{
|
||||
shm_toc *toc = (shm_toc *) address;
|
||||
|
||||
if (toc->toc_magic != magic)
|
||||
return NULL;
|
||||
|
||||
Assert(toc->toc_total_bytes >= toc->toc_allocated_bytes);
|
||||
Assert(toc->toc_total_bytes >= offsetof(shm_toc, toc_entry));
|
||||
|
||||
return toc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate shared memory from a segment managed by a table of contents.
|
||||
*
|
||||
* This is not a full-blown allocator; there's no way to free memory. It's
|
||||
* just a way of dividing a single physical shared memory segment into logical
|
||||
* chunks that may be used for different purposes.
|
||||
*
|
||||
* We allocated backwards from the end of the segment, so that the TOC entries
|
||||
* can grow forward from the start of the segment.
|
||||
*/
|
||||
extern void *shm_toc_allocate(shm_toc *toc, Size nbytes)
|
||||
{
|
||||
volatile shm_toc *vtoc = toc;
|
||||
Size total_bytes;
|
||||
Size allocated_bytes;
|
||||
Size nentry;
|
||||
Size toc_bytes;
|
||||
|
||||
/* Make sure request is well-aligned. */
|
||||
nbytes = BUFFERALIGN(nbytes);
|
||||
|
||||
SpinLockAcquire(&toc->toc_mutex);
|
||||
|
||||
total_bytes = vtoc->toc_total_bytes;
|
||||
allocated_bytes = vtoc->toc_allocated_bytes;
|
||||
nentry = vtoc->toc_nentry;
|
||||
toc_bytes = offsetof(shm_toc, toc_entry) +nentry * sizeof(shm_toc_entry)
|
||||
+ allocated_bytes;
|
||||
|
||||
/* Check for memory exhaustion and overflow. */
|
||||
if (toc_bytes + nbytes > total_bytes || toc_bytes + nbytes < toc_bytes)
|
||||
{
|
||||
SpinLockRelease(&toc->toc_mutex);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||
errmsg("out of shared memory")));
|
||||
}
|
||||
vtoc->toc_allocated_bytes += nbytes;
|
||||
|
||||
SpinLockRelease(&toc->toc_mutex);
|
||||
|
||||
return ((char *) toc) + (total_bytes - allocated_bytes - nbytes);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the number of bytes that can still be allocated.
|
||||
*/
|
||||
extern Size shm_toc_freespace(shm_toc *toc)
|
||||
{
|
||||
volatile shm_toc *vtoc = toc;
|
||||
Size total_bytes;
|
||||
Size allocated_bytes;
|
||||
Size nentry;
|
||||
Size toc_bytes;
|
||||
|
||||
SpinLockAcquire(&toc->toc_mutex);
|
||||
total_bytes = vtoc->toc_total_bytes;
|
||||
allocated_bytes = vtoc->toc_allocated_bytes;
|
||||
nentry = vtoc->toc_nentry;
|
||||
SpinLockRelease(&toc->toc_mutex);
|
||||
|
||||
toc_bytes = offsetof(shm_toc, toc_entry) +nentry * sizeof(shm_toc_entry);
|
||||
Assert(allocated_bytes + BUFFERALIGN(toc_bytes) <= total_bytes);
|
||||
return total_bytes - (allocated_bytes + BUFFERALIGN(toc_bytes));
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a TOC entry.
|
||||
*
|
||||
* The idea here is that process setting up the shared memory segment will
|
||||
* register the addresses of data structures within the segment using this
|
||||
* function. Each data structure will be identified using a 64-bit key, which
|
||||
* is assumed to be a well-known or discoverable integer. Other processes
|
||||
* accessing the shared memory segment can pass the same key to
|
||||
* shm_toc_lookup() to discover the addresses of those data structures.
|
||||
*
|
||||
* Since the shared memory segment may be mapped at different addresses within
|
||||
* different backends, we store relative rather than absolute pointers.
|
||||
*
|
||||
* This won't scale well to a large number of keys. Hopefully, that isn't
|
||||
* necessary; if it proves to be, we might need to provide a more sophisticated
|
||||
* data structure here. But the real idea here is just to give someone mapping
|
||||
* a dynamic shared memory the ability to find the bare minimum number of
|
||||
* pointers that they need to bootstrap. If you're storing a lot of stuff in
|
||||
* here, you're doing it wrong.
|
||||
*/
|
||||
void
|
||||
shm_toc_insert(shm_toc *toc, uint64 key, void *address)
|
||||
{
|
||||
volatile shm_toc *vtoc = toc;
|
||||
uint64 total_bytes;
|
||||
uint64 allocated_bytes;
|
||||
uint64 nentry;
|
||||
uint64 toc_bytes;
|
||||
uint64 offset;
|
||||
|
||||
/* Relativize pointer. */
|
||||
Assert(address > (void *) toc);
|
||||
offset = ((char *) address) - (char *) toc;
|
||||
|
||||
SpinLockAcquire(&toc->toc_mutex);
|
||||
|
||||
total_bytes = vtoc->toc_total_bytes;
|
||||
allocated_bytes = vtoc->toc_allocated_bytes;
|
||||
nentry = vtoc->toc_nentry;
|
||||
toc_bytes = offsetof(shm_toc, toc_entry) +nentry * sizeof(shm_toc_entry)
|
||||
+ allocated_bytes;
|
||||
|
||||
/* Check for memory exhaustion and overflow. */
|
||||
if (toc_bytes + sizeof(shm_toc_entry) > total_bytes ||
|
||||
toc_bytes + sizeof(shm_toc_entry) < toc_bytes)
|
||||
{
|
||||
SpinLockRelease(&toc->toc_mutex);
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||
errmsg("out of shared memory")));
|
||||
}
|
||||
|
||||
Assert(offset < total_bytes);
|
||||
vtoc->toc_entry[nentry].key = key;
|
||||
vtoc->toc_entry[nentry].offset = offset;
|
||||
|
||||
/*
|
||||
* By placing a write barrier after filling in the entry and before
|
||||
* updating the number of entries, we make it safe to read the TOC
|
||||
* unlocked.
|
||||
*/
|
||||
pg_write_barrier();
|
||||
|
||||
vtoc->toc_nentry++;
|
||||
|
||||
SpinLockRelease(&toc->toc_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up a TOC entry.
|
||||
*
|
||||
* Unlike the other functions in this file, this operation acquires no lock;
|
||||
* it uses only barriers. It probably wouldn't hurt concurrency very much even
|
||||
* if it did get a lock, but since it's reasonably likely that a group of
|
||||
* worker processes could each read a series of entries from the same TOC
|
||||
* right around the same time, there seems to be some value in avoiding it.
|
||||
*/
|
||||
void *shm_toc_lookup(shm_toc *toc, uint64 key)
|
||||
{
|
||||
uint64 nentry;
|
||||
uint64 i;
|
||||
|
||||
/* Read the number of entries before we examine any entry. */
|
||||
nentry = toc->toc_nentry;
|
||||
pg_read_barrier();
|
||||
|
||||
/* Now search for a matching entry. */
|
||||
for (i = 0; i < nentry; ++i)
|
||||
if (toc->toc_entry[i].key == key)
|
||||
return ((char *) toc) + toc->toc_entry[i].offset;
|
||||
|
||||
/* No matching entry was found. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Estimate how much shared memory will be required to store a TOC and its
|
||||
* dependent data structures.
|
||||
*/
|
||||
Size
|
||||
shm_toc_estimate(shm_toc_estimator *e)
|
||||
{
|
||||
return add_size(offsetof(shm_toc, toc_entry),
|
||||
add_size(mul_size(e->number_of_keys, sizeof(shm_toc_entry)),
|
||||
e->space_for_chunks));
|
||||
}
|
||||
|
||||
|
|
@ -96,3 +96,4 @@ GPCCommitLock 88
|
|||
GPCClearLock 89
|
||||
GPCTimelineLock 90
|
||||
TsTagsCacheLock 91
|
||||
BackgroundWorkerLock 92
|
||||
|
|
@ -212,7 +212,9 @@ static void FiniNuma(int code, Datum arg)
|
|||
* So, now we grab enough semaphores to support the desired max number
|
||||
* of backends immediately at initialization --- if the sysadmin has set
|
||||
* MaxConnections or autovacuum_max_workers higher than his kernel will
|
||||
* support, he'll find out sooner rather than later.
|
||||
* support, he'll find out sooner rather than later. (The number of
|
||||
* background worker processes registered by loadable modules is also taken
|
||||
* into consideration.)
|
||||
*
|
||||
* Another reason for creating semaphores here is that the semaphore
|
||||
* implementation typically requires us to create semaphores in the
|
||||
|
|
@ -240,6 +242,7 @@ void InitProcGlobal(void)
|
|||
#endif
|
||||
g_instance.proc_base->freeProcs = NULL;
|
||||
g_instance.proc_base->autovacFreeProcs = NULL;
|
||||
g_instance.proc_base->bgworkerFreeProcs = NULL;
|
||||
g_instance.proc_base->pgjobfreeProcs = NULL;
|
||||
g_instance.proc_base->startupProc = NULL;
|
||||
g_instance.proc_base->startupProcPid = 0;
|
||||
|
|
@ -252,10 +255,10 @@ void InitProcGlobal(void)
|
|||
|
||||
/*
|
||||
* Create and initialize all the PGPROC structures we'll need. There are
|
||||
* four separate consumers: (1) normal backends, (2) autovacuum workers
|
||||
* and the autovacuum launcher, (3) auxiliary processes, and (4) prepared
|
||||
* transactions. Each PGPROC structure is dedicated to exactly one of
|
||||
* these purposes, and they do not move between groups.
|
||||
* five separate consumers: (1) normal backends, (2) autovacuum workers
|
||||
* and the autovacuum launcher, (3) background workers, (4) auxiliary processes,
|
||||
* and (5) prepared transactions. Each PGPROC structure is dedicated to exactly
|
||||
* one of these purposes, and they do not move between groups.
|
||||
*/
|
||||
PGPROC *initProcs[MAX_NUMA_NODE] = {0};
|
||||
|
||||
|
|
@ -331,7 +334,7 @@ void InitProcGlobal(void)
|
|||
procs[i]->nodeno = i % nNumaNodes;
|
||||
|
||||
/*
|
||||
* Newly created PGPROCs for normal backends or for autovacuum must be
|
||||
* Newly created PGPROCs for normal backends, autovacuum and bgworkers must be
|
||||
* queued up on the appropriate free list. Because there can only
|
||||
* ever be a small, fixed number of auxiliary processes, no free list
|
||||
* is used in that case; InitAuxiliaryProcess() instead uses a linear
|
||||
|
|
@ -347,13 +350,23 @@ void InitProcGlobal(void)
|
|||
/* PGPROC for pg_job backend, add to pgjobfreeProcs list, 1 for Job Schedule Lancher */
|
||||
procs[i]->links.next = (SHM_QUEUE *)g_instance.proc_base->pgjobfreeProcs;
|
||||
g_instance.proc_base->pgjobfreeProcs = procs[i];
|
||||
} else if (i < g_instance.shmem_cxt.MaxBackends) {
|
||||
} else if (i < g_instance.shmem_cxt.MaxConnections + AUXILIARY_BACKENDS +
|
||||
g_instance.attr.attr_sql.job_queue_processes + 1 +
|
||||
g_instance.attr.attr_storage.autovacuum_max_workers +
|
||||
AV_LAUNCHER_PROCS) {
|
||||
/*
|
||||
* PGPROC for AV launcher/worker, add to autovacFreeProcs list
|
||||
* list size is autovacuum_max_workers + AUTOVACUUM_LAUNCHERS
|
||||
* list size is autovacuum_max_workers + AV_LAUNCHER_PROCS
|
||||
*/
|
||||
procs[i]->links.next = (SHM_QUEUE *)g_instance.proc_base->autovacFreeProcs;
|
||||
g_instance.proc_base->autovacFreeProcs = procs[i];
|
||||
} else if (i < g_instance.shmem_cxt.MaxBackends) {
|
||||
/*
|
||||
* PGPROC for bgworker, add to bgworkerFreeProcs list
|
||||
* list size is max_background_workers
|
||||
*/
|
||||
procs[i]->links.next = (SHM_QUEUE *)g_instance.proc_base->bgworkerFreeProcs;
|
||||
g_instance.proc_base->bgworkerFreeProcs = procs[i];
|
||||
}
|
||||
|
||||
/* Initialize myProcLocks[] shared memory queues. */
|
||||
|
|
@ -463,6 +476,8 @@ void InitProcess(void)
|
|||
t_thrd.proc = g_instance.proc_base->autovacFreeProcs;
|
||||
else if (IsJobSchedulerProcess() || IsJobWorkerProcess())
|
||||
t_thrd.proc = g_instance.proc_base->pgjobfreeProcs;
|
||||
else if (IsBackgroundWorker)
|
||||
t_thrd.proc = g_instance.proc_base->bgworkerFreeProcs;
|
||||
else {
|
||||
#ifndef __USE_NUMA
|
||||
t_thrd.proc = g_instance.proc_base->freeProcs;
|
||||
|
|
@ -478,6 +493,8 @@ void InitProcess(void)
|
|||
g_instance.proc_base->autovacFreeProcs = (PGPROC *)t_thrd.proc->links.next;
|
||||
else if (IsJobSchedulerProcess() || IsJobWorkerProcess())
|
||||
g_instance.proc_base->pgjobfreeProcs = (PGPROC *)t_thrd.proc->links.next;
|
||||
else if (IsBackgroundWorker)
|
||||
g_instance.proc_base->bgworkerFreeProcs = (PGPROC *)t_thrd.proc->links.next;
|
||||
else {
|
||||
#ifndef __USE_NUMA
|
||||
g_instance.proc_base->freeProcs = (PGPROC *)t_thrd.proc->links.next;
|
||||
|
|
@ -1036,6 +1053,11 @@ static void ProcKill(int code, Datum arg)
|
|||
} else if (IsJobSchedulerProcess() || IsJobWorkerProcess()) {
|
||||
t_thrd.proc->links.next = (SHM_QUEUE *)g_instance.proc_base->pgjobfreeProcs;
|
||||
g_instance.proc_base->pgjobfreeProcs = t_thrd.proc;
|
||||
}
|
||||
else if (IsBackgroundWorker)
|
||||
{
|
||||
t_thrd.proc->links.next = (SHM_QUEUE *)g_instance.proc_base->bgworkerFreeProcs;
|
||||
g_instance.proc_base->bgworkerFreeProcs = t_thrd.proc;
|
||||
} else {
|
||||
t_thrd.proc->links.next = (SHM_QUEUE *)g_instance.proc_base->freeProcs;
|
||||
g_instance.proc_base->freeProcs = t_thrd.proc;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ extern Size AsyncShmemSize(void);
|
|||
extern void AsyncShmemInit(void);
|
||||
|
||||
/* notify-related SQL statements */
|
||||
extern void NotifyMyFrontEnd(const char* channel, const char* payload, int32 srcPid);
|
||||
extern void Async_Notify(const char* channel, const char* payload);
|
||||
extern void Async_Listen(const char* channel);
|
||||
extern void Async_Unlisten(const char* channel);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ extern bool check_transaction_deferrable(bool* newval, void** extra, GucSource s
|
|||
extern bool check_random_seed(double* newval, void** extra, GucSource source);
|
||||
extern void assign_random_seed(double newval, void* extra);
|
||||
extern const char* show_random_seed(void);
|
||||
extern void (*check_client_encoding_hook)(void);
|
||||
extern bool check_client_encoding(char** newval, void** extra, GucSource source);
|
||||
extern void assign_client_encoding(const char* newval, void* extra);
|
||||
extern bool check_mix_replication_param(bool* newval, void** extra, GucSource source);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ typedef enum knl_thread_role {
|
|||
COMM_RECEIVER,
|
||||
COMM_AUXILIARY,
|
||||
COMM_POOLER_CLEAN,
|
||||
BACKGROUND_WORKER,
|
||||
// should be last valid thread.
|
||||
THREAD_ENTRY_BOUND,
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ typedef struct knl_instance_attr_storage {
|
|||
int max_replication_slots;
|
||||
int replication_type;
|
||||
int autovacuum_max_workers;
|
||||
int max_background_workers;
|
||||
int64 autovacuum_freeze_max_age;
|
||||
int wal_level;
|
||||
/* User specified maximum number of recovery threads. */
|
||||
|
|
|
|||
|
|
@ -485,6 +485,7 @@ typedef struct knl_g_libpq_context {
|
|||
|
||||
typedef struct knl_g_shmem_context {
|
||||
int MaxConnections;
|
||||
int max_parallel_workers;
|
||||
int MaxBackends;
|
||||
int MaxReserveBackendId;
|
||||
int ThreadPoolGroupNum;
|
||||
|
|
@ -511,6 +512,12 @@ typedef struct knl_g_numa_context {
|
|||
size_t allocIndex;
|
||||
} knl_g_numa_context;
|
||||
|
||||
typedef struct knl_g_bgworker_context {
|
||||
/* set when there's a worker that needs to be started up */
|
||||
volatile bool start_worker_needed;
|
||||
volatile bool have_crashed_worker;
|
||||
} knl_g_bgworker_context;
|
||||
|
||||
typedef struct knl_instance_context {
|
||||
knl_virtual_role role;
|
||||
volatile int status;
|
||||
|
|
@ -582,6 +589,7 @@ typedef struct knl_instance_context {
|
|||
knl_g_rto_context rto_cxt;
|
||||
knl_g_xlog_context xlog_cxt;
|
||||
knl_g_numa_context numa_cxt;
|
||||
knl_g_bgworker_context bgworker_cxt;
|
||||
} knl_instance_context;
|
||||
|
||||
extern void knl_instance_init();
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@
|
|||
#include "openssl/ossl_typ.h"
|
||||
#include "workload/qnode.h"
|
||||
#include "tcop/dest.h"
|
||||
#include "postmaster/bgworker.h"
|
||||
|
||||
#define MAX_PATH_LEN 1024
|
||||
|
||||
|
|
@ -2659,6 +2660,13 @@ typedef struct knl_t_heartbeat_context {
|
|||
struct heartbeat_state* state;
|
||||
} knl_t_heartbeat_context;
|
||||
|
||||
/* autonomous_transaction */
|
||||
struct PLpgSQL_expr;
|
||||
typedef struct knl_t_autonomous_context {
|
||||
PLpgSQL_expr* sqlstmt;
|
||||
bool isnested;
|
||||
} knl_t_autonomous_context;
|
||||
|
||||
/* MOT thread attributes */
|
||||
#define MOT_MAX_ERROR_MESSAGE 256
|
||||
#define MOT_MAX_ERROR_FRAMES 32
|
||||
|
|
@ -2705,6 +2713,11 @@ typedef struct knl_t_mot_context {
|
|||
unsigned int mbindFlags;
|
||||
} knl_t_mot_context;
|
||||
|
||||
typedef struct knl_t_bgworker_context {
|
||||
BackgroundWorkerArray *background_worker_data;
|
||||
BackgroundWorker *my_bgworker_entry;
|
||||
} knl_t_bgworker_context;
|
||||
|
||||
/* thread context. */
|
||||
typedef struct knl_thrd_context {
|
||||
knl_thread_role role;
|
||||
|
|
@ -2728,6 +2741,7 @@ typedef struct knl_thrd_context {
|
|||
knl_t_arch_context arch;
|
||||
knl_t_async_context asy_cxt;
|
||||
knl_t_audit_context audit;
|
||||
knl_t_autonomous_context autonomous_cxt;
|
||||
knl_t_autovacuum_context autovacuum_cxt;
|
||||
knl_t_basebackup_context basebackup_cxt;
|
||||
knl_t_bgwriter_context bgwriter_cxt;
|
||||
|
|
@ -2802,6 +2816,7 @@ typedef struct knl_thrd_context {
|
|||
knl_t_heartbeat_context heartbeat_cxt;
|
||||
knl_t_poolcleaner_context poolcleaner_cxt;
|
||||
knl_t_mot_context mot_cxt;
|
||||
knl_t_bgworker_context bgworker_cxt;
|
||||
} knl_thrd_context;
|
||||
|
||||
extern void knl_thread_mot_init();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* libpq.h
|
||||
* POSTGRES LIBPQ buffer structure definitions.
|
||||
* POSTGRES LIBPQ buffer structure definitions.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||
|
|
@ -22,19 +22,43 @@
|
|||
|
||||
/* ----------------
|
||||
* PQArgBlock
|
||||
* Information (pointer to array of this structure) required
|
||||
* for the PQfn() call. (This probably ought to go somewhere else...)
|
||||
* Information (pointer to array of this structure) required
|
||||
* for the PQfn() call. (This probably ought to go somewhere else...)
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct {
|
||||
int len;
|
||||
int isint;
|
||||
union {
|
||||
int* ptr; /* can't use void (dec compiler barfs) */
|
||||
int *ptr; /* can't use void (dec compiler barfs) */
|
||||
int integer;
|
||||
} u;
|
||||
} PQArgBlock;
|
||||
|
||||
typedef struct {
|
||||
void (*comm_reset) (void);
|
||||
int (*flush) (void);
|
||||
int (*flush_if_writable) (void);
|
||||
bool (*is_send_pending) (void);
|
||||
int (*putmessage) (char msgtype, const char* s, size_t len);
|
||||
int (*putmessage_noblock) (char msgtype, const char* s, size_t len);
|
||||
void (*startcopyout) (void);
|
||||
void (*endcopyout) (bool errorAbort);
|
||||
} PQcommMethods;
|
||||
|
||||
extern PGDLLIMPORT THR_LOCAL PQcommMethods *PqCommMethods;
|
||||
|
||||
#define pq_comm_reset() (PqCommMethods->comm_reset())
|
||||
#define pq_flush() (PqCommMethods->flush())
|
||||
#define pq_flush_if_writable() (PqCommMethods->flush_if_writable())
|
||||
#define pq_is_send_pending() (PqCommMethods->is_send_pending())
|
||||
#define pq_putmessage(msgtype, s, len) \
|
||||
(PqCommMethods->putmessage(msgtype, s, len))
|
||||
#define pq_putmessage_noblock(msgtype, s, len) \
|
||||
(PqCommMethods->putmessage_noblock(msgtype, s, len))
|
||||
#define pq_startcopyout() (PqCommMethods->startcopyout())
|
||||
#define pq_endcopyout(errorAbort) (PqCommMethods->endcopyout(errorAbort))
|
||||
|
||||
/*
|
||||
* External functions.
|
||||
*/
|
||||
|
|
@ -49,7 +73,6 @@ extern int StreamConnection(pgsocket server_fd, Port* port);
|
|||
extern void StreamClose(pgsocket sock);
|
||||
extern void TouchSocketFile(void);
|
||||
extern void pq_init(void);
|
||||
extern void pq_comm_reset(void);
|
||||
extern int pq_getbytes(char* s, size_t len);
|
||||
extern int pq_getstring(StringInfo s);
|
||||
extern int pq_getmessage(StringInfo s, int maxlen);
|
||||
|
|
@ -57,14 +80,7 @@ extern int pq_getbyte(void);
|
|||
extern int pq_peekbyte(void);
|
||||
extern int pq_getbyte_if_available(unsigned char* c);
|
||||
extern int pq_putbytes(const char* s, size_t len);
|
||||
extern int pq_flush(void);
|
||||
extern int pq_flush_if_writable(void);
|
||||
extern void pq_flush_timedwait(int timeout);
|
||||
extern bool pq_is_send_pending(void);
|
||||
extern int pq_putmessage(char msgtype, const char* s, size_t len);
|
||||
extern int pq_putmessage_noblock(char msgtype, const char* s, size_t len);
|
||||
extern void pq_startcopyout(void);
|
||||
extern void pq_endcopyout(bool errorAbort);
|
||||
extern bool pq_select(int timeout_ms);
|
||||
extern void pq_abandon_sendbuffer(void);
|
||||
extern void pq_abandon_recvbuffer(void);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ extern const char* pq_getmsgbytes(StringInfo msg, int datalen);
|
|||
extern void pq_copymsgbytes(StringInfo msg, char* buf, int datalen);
|
||||
extern char* pq_getmsgtext(StringInfo msg, int rawbytes, int* nbytes);
|
||||
extern const char* pq_getmsgstring(StringInfo msg);
|
||||
extern const char* pq_getmsgrawstring(StringInfo msg);
|
||||
extern void pq_getmsgend(StringInfo msg);
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* pqmq.h
|
||||
* Use the frontend/backend protocol for communication over a shm_mq
|
||||
*
|
||||
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/include/libpq/pqmq.h
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PQMQ_H
|
||||
#define PQMQ_H
|
||||
|
||||
#include "lib/stringinfo.h"
|
||||
#include "storage/shm_mq.h"
|
||||
|
||||
extern void pq_redirect_to_shm_mq(shm_mq_handle* mqh);
|
||||
extern void pq_stop_redirect_to_shm_mq(void);
|
||||
extern void pq_set_parallel_master(pid_t pid, BackendId backend_id);
|
||||
|
||||
extern void pq_parse_errornotice(StringInfo str, ErrorData* edata);
|
||||
|
||||
#endif /* PQMQ_H */
|
||||
|
|
@ -28,6 +28,8 @@
|
|||
#include "pgtime.h" /* for pg_time_t */
|
||||
#include "libpq/libpq-be.h"
|
||||
|
||||
#define InvalidPid ((ThreadId)(-1))
|
||||
|
||||
#define PG_BACKEND_VERSIONSTR "gaussdb " DEF_GS_VERSION "\n"
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
@ -129,6 +131,7 @@ extern bool InplaceUpgradePrecommit;
|
|||
|
||||
extern THR_LOCAL PGDLLIMPORT bool IsUnderPostmaster;
|
||||
extern THR_LOCAL PGDLLIMPORT char my_exec_path[];
|
||||
extern THR_LOCAL PGDLLIMPORT bool IsBackgroundWorker;
|
||||
|
||||
#define MAX_QUERY_DOP (64)
|
||||
#define MIN_QUERY_DOP -(MAX_QUERY_DOP)
|
||||
|
|
@ -232,7 +235,7 @@ extern bool InLocalUserIdChange(void);
|
|||
extern bool InSecurityRestrictedOperation(void);
|
||||
extern void GetUserIdAndContext(Oid* userid, bool* sec_def_context);
|
||||
extern void SetUserIdAndContext(Oid userid, bool sec_def_context);
|
||||
extern void InitializeSessionUserId(const char* rolename);
|
||||
extern void InitializeSessionUserId(const char* rolename, Oid role_id);
|
||||
extern void InitializeSessionUserIdStandalone(void);
|
||||
extern void SetSessionAuthorization(Oid userid, bool is_superuser);
|
||||
extern Oid GetCurrentRoleId(void);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ extern THR_LOCAL PGDLLIMPORT post_parse_analyze_hook_type post_parse_analyze_hoo
|
|||
|
||||
extern Query* parse_analyze(Node* parseTree, const char* sourceText, Oid* paramTypes, int numParams,
|
||||
bool isFirstNode = true, bool isCreateView = false);
|
||||
extern Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** paramTypes, int* numParams);
|
||||
extern Query* parse_analyze_varparams(Node* parseTree, const char* sourceText, Oid** paramTypes, int* numParams, char** paramTypeNames);
|
||||
|
||||
extern Query* parse_sub_analyze(Node* parseTree, ParseState* parentParseState, CommonTableExpr* parentCTE,
|
||||
bool locked_from_parent, bool resolve_unknowns);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
#include "parser/parse_node.h"
|
||||
|
||||
extern void parse_fixed_parameters(ParseState* pstate, Oid* paramTypes, int numParams);
|
||||
extern void parse_variable_parameters(ParseState* pstate, Oid** paramTypes, int* numParams);
|
||||
extern void parse_variable_parameters(ParseState* pstate, Oid** paramTypes, int* numParams, char** paramTypeNames);
|
||||
extern void check_variable_parameters(ParseState* pstate, Query* query);
|
||||
extern bool query_contains_extern_params(Query* query);
|
||||
|
||||
|
|
|
|||
|
|
@ -241,6 +241,8 @@ typedef enum { SKEW_OPT_OFF, SKEW_OPT_NORMAL, SKEW_OPT_LAZY } SkewStrategy;
|
|||
|
||||
typedef enum { RESOURCE_TRACK_NONE, RESOURCE_TRACK_QUERY, RESOURCE_TRACK_OPERATOR } ResourceTrackOption;
|
||||
|
||||
typedef enum { QUERY_MESSAGE = 0, HYBRID_MESSAGE } MessageType;
|
||||
|
||||
typedef enum {
|
||||
CODEGEN_PARTIAL, /* allow to call c-function in codegen */
|
||||
CODEGEN_PURE /* do not allow to call c-function in codegen */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
/* --------------------------------------------------------------------
|
||||
* bgworker.h
|
||||
* POSTGRES pluggable background workers interface
|
||||
*
|
||||
* A background worker is a process able to run arbitrary, user-supplied code,
|
||||
* including normal transactions.
|
||||
*
|
||||
* Any external module loaded via shared_preload_libraries can register a
|
||||
* worker. Workers can also be registered dynamically at runtime. In either
|
||||
* case, the worker process is forked from the postmaster and runs the
|
||||
* user-supplied "main" function. This code may connect to a database and
|
||||
* run transactions. Workers can remain active indefinitely, but will be
|
||||
* terminated if a shutdown or crash occurs.
|
||||
*
|
||||
* If the fork() call fails in the postmaster, it will try again later. Note
|
||||
* that the failure can only be transient (fork failure due to high load,
|
||||
* memory pressure, too many processes, etc); more permanent problems, like
|
||||
* failure to connect to a database, are detected later in the worker and dealt
|
||||
* with just by having the worker exit normally. A worker which exits with
|
||||
* a return code of 0 will never be restarted and will be removed from worker
|
||||
* list. A worker which exits with a return code of 1 will be restarted after
|
||||
* the configured restart interval (unless that interval is BGW_NEVER_RESTART).
|
||||
* The TerminateBackgroundWorker() function can be used to terminate a
|
||||
* dynamically registered background worker; the worker will be sent a SIGTERM
|
||||
* and will not be restarted after it exits. Whenever the postmaster knows
|
||||
* that a worker will not be restarted, it unregisters the worker, freeing up
|
||||
* that worker's slot for use by a new worker.
|
||||
*
|
||||
* Note that there might be more than one worker in a database concurrently,
|
||||
* and the same module may request more than one worker running the same (or
|
||||
* different) code.
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/include/postmaster/bgworker.h
|
||||
* --------------------------------------------------------------------
|
||||
*/
|
||||
#include "gs_thread.h"
|
||||
#ifndef BGWORKER_H
|
||||
#define BGWORKER_H
|
||||
|
||||
/*---------------------------------------------------------------------
|
||||
* External module API.
|
||||
*---------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Pass this flag to have your worker be able to connect to shared memory.
|
||||
*/
|
||||
#define BGWORKER_SHMEM_ACCESS 0x0001
|
||||
|
||||
/*
|
||||
* This flag means the bgworker requires a database connection. The connection
|
||||
* is not established automatically; the worker must establish it later.
|
||||
* It requires that BGWORKER_SHMEM_ACCESS was passed too.
|
||||
*/
|
||||
#define BGWORKER_BACKEND_DATABASE_CONNECTION 0x0002
|
||||
|
||||
/*
|
||||
* This class is used internally for parallel queries, to keep track of the
|
||||
* number of active parallel workers and make sure we never launch more than
|
||||
* max_parallel_workers parallel workers at the same time. Third party
|
||||
* background workers should not use this class.
|
||||
*/
|
||||
#define BGWORKER_CLASS_PARALLEL 0x0010
|
||||
|
||||
/* add additional bgworker classes here */
|
||||
|
||||
typedef void (*bgworker_main_type) (Datum main_arg);
|
||||
|
||||
/*
|
||||
* Points in time at which a bgworker can request to be started
|
||||
*/
|
||||
typedef enum {
|
||||
BgWorkerStart_PostmasterStart,
|
||||
BgWorkerStart_ConsistentState,
|
||||
BgWorkerStart_RecoveryFinished
|
||||
} BgWorkerStartTime;
|
||||
|
||||
#define BGW_DEFAULT_RESTART_INTERVAL 60
|
||||
#define BGW_NEVER_RESTART -1
|
||||
#define BGW_MAXLEN 96
|
||||
#define BGW_EXTRALEN 128
|
||||
|
||||
typedef struct BackgroundWorker {
|
||||
char bgw_name[BGW_MAXLEN];
|
||||
char bgw_type[BGW_MAXLEN];
|
||||
int bgw_flags;
|
||||
BgWorkerStartTime bgw_start_time;
|
||||
int bgw_restart_time; /* in seconds, or BGW_NEVER_RESTART */
|
||||
char bgw_library_name[BGW_MAXLEN];
|
||||
char bgw_function_name[BGW_MAXLEN];
|
||||
Datum bgw_main_arg;
|
||||
char bgw_extra[BGW_EXTRALEN];
|
||||
ThreadId bgw_notify_pid; /* SIGUSR1 this backend on start/stop */
|
||||
} BackgroundWorker;
|
||||
|
||||
typedef enum BgwHandleStatus {
|
||||
BGWH_STARTED, /* worker is running */
|
||||
BGWH_NOT_YET_STARTED, /* worker hasn't been started yet */
|
||||
BGWH_STOPPED, /* worker has exited */
|
||||
BGWH_POSTMASTER_DIED /* postmaster died; worker status unclear */
|
||||
} BgwHandleStatus;
|
||||
|
||||
struct BackgroundWorkerHandle;
|
||||
typedef struct BackgroundWorkerHandle BackgroundWorkerHandle;
|
||||
struct BackgroundWorkerArray;
|
||||
|
||||
/* Register a new bgworker during shared_preload_libraries */
|
||||
extern void RegisterBackgroundWorker(BackgroundWorker *worker);
|
||||
|
||||
/* Register a new bgworker from a regular backend */
|
||||
extern bool RegisterDynamicBackgroundWorker(BackgroundWorker *worker,
|
||||
BackgroundWorkerHandle **handle);
|
||||
|
||||
/* Query the status of a bgworker */
|
||||
extern BgwHandleStatus GetBackgroundWorkerPid(const BackgroundWorkerHandle *handle,
|
||||
ThreadId *pidp);
|
||||
extern BgwHandleStatus WaitForBackgroundWorkerStartup(const BackgroundWorkerHandle *handle, ThreadId *pid);
|
||||
extern BgwHandleStatus WaitForBackgroundWorkerShutdown(const BackgroundWorkerHandle *handle);
|
||||
extern const char *GetBackgroundWorkerTypeByPid(ThreadId pid);
|
||||
|
||||
/* Terminate a bgworker */
|
||||
extern void TerminateBackgroundWorker(const BackgroundWorkerHandle *handle);
|
||||
|
||||
/*
|
||||
* Connect to the specified database, as the specified user. Only a worker
|
||||
* that passed BGWORKER_BACKEND_DATABASE_CONNECTION during registration may
|
||||
* call this.
|
||||
*
|
||||
* If username is NULL, bootstrapping superuser is used.
|
||||
* If dbname is NULL, connection is made to no specific database;
|
||||
* only shared catalogs can be accessed.
|
||||
*/
|
||||
extern void BackgroundWorkerInitializeConnection(const char *dbname, const char *username, uint32 flags = 1);
|
||||
|
||||
/* Just like the above, but specifying database and user by OID. */
|
||||
extern void BackgroundWorkerInitializeConnectionByOid(Oid dboid, Oid useroid, uint32 flags = 1);
|
||||
|
||||
/*
|
||||
* Flags to BackgroundWorkerInitializeConnection et al
|
||||
*
|
||||
*
|
||||
* Allow bypassing datallowconn restrictions when connecting to database
|
||||
*/
|
||||
#define BGWORKER_BYPASS_ALLOWCONN 1
|
||||
|
||||
|
||||
/* Block/unblock signals in a background worker process */
|
||||
extern void BackgroundWorkerBlockSignals(void);
|
||||
extern void BackgroundWorkerUnblockSignals(void);
|
||||
|
||||
#endif /* BGWORKER_H */
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
/* --------------------------------------------------------------------
|
||||
* bgworker_internals.h
|
||||
* POSTGRES pluggable background workers internals
|
||||
*
|
||||
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/include/postmaster/bgworker_internals.h
|
||||
* --------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef BGWORKER_INTERNALS_H
|
||||
#define BGWORKER_INTERNALS_H
|
||||
|
||||
#include "datatype/timestamp.h"
|
||||
#include "lib/ilist.h"
|
||||
#include "postmaster/bgworker.h"
|
||||
|
||||
/*
|
||||
* Maximum possible value of parallel workers.
|
||||
*/
|
||||
#define MAX_PARALLEL_WORKER_LIMIT 1024
|
||||
|
||||
struct BackgroundWorkerSlot;
|
||||
|
||||
/*
|
||||
* List of background workers, private to postmaster.
|
||||
*
|
||||
* A worker that requests a database connection during registration will have
|
||||
* rw_backend set, and will be present in BackendList. Note: do not rely on
|
||||
* rw_backend being non-NULL for shmem-connected workers!
|
||||
*/
|
||||
typedef struct RegisteredBgWorker {
|
||||
BackgroundWorker rw_worker; /* its registry entry */
|
||||
struct Backend *rw_backend; /* its BackendList entry, or NULL */
|
||||
ThreadId rw_pid; /* 0 if not running */
|
||||
int rw_child_slot;
|
||||
TimestampTz rw_crashed_at; /* if not 0, time it last crashed */
|
||||
int rw_shmem_slot;
|
||||
bool rw_terminate;
|
||||
slist_node rw_lnode; /* list link */
|
||||
} RegisteredBgWorker;
|
||||
|
||||
extern THR_LOCAL slist_head BackgroundWorkerList;
|
||||
|
||||
extern Size BackgroundWorkerShmemSize(void);
|
||||
extern void BackgroundWorkerShmemInit(void);
|
||||
extern void BackgroundWorkerStateChange(void);
|
||||
extern void ForgetBackgroundWorker(slist_mutable_iter *cur);
|
||||
extern void ReportBackgroundWorkerPID(const RegisteredBgWorker *);
|
||||
extern void ReportBackgroundWorkerExit(slist_mutable_iter *cur);
|
||||
extern void BackgroundWorkerStopNotifications(ThreadId pid);
|
||||
extern void ResetBackgroundWorkerCrashTimes(void);
|
||||
|
||||
/* Function to start a background worker, called from postmaster.c */
|
||||
extern void StartBackgroundWorker(void* bgWorkerSlotShmAddr) ;
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
extern void* GetBackgroundWorkerShmAddr(int slotno);
|
||||
extern BackgroundWorker *BackgroundWorkerEntry(const BackgroundWorkerSlot* bgWorkerSlotShmAddr);
|
||||
#endif
|
||||
|
||||
#endif /* BGWORKER_INTERNALS_H */
|
||||
|
||||
|
|
@ -249,4 +249,5 @@ extern void GenerateCancelKey(bool isThreadPoolSession);
|
|||
extern bool SignalCancelAllBackEnd();
|
||||
extern bool IsLocalAddr(Port* port);
|
||||
extern uint64_t mc_timers_us(void);
|
||||
extern bool PostmasterMarkPIDForWorkerNotify(ThreadId);
|
||||
#endif /* _POSTMASTER_H */
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ typedef enum {
|
|||
PMSIGNAL_ROLLBACK_STANDBY_PROMOTE, /* roll back standby promoting */
|
||||
PMSIGNAL_START_PAGE_WRITER, /* start a new page writer thread */
|
||||
PMSIGNAL_START_THREADPOOL_WORKER, /* start thread pool woker */
|
||||
PMSIGNAL_BACKGROUND_WORKER_CHANGE, /* background worker state change */
|
||||
NUM_PMSIGNALS /* Must be last value of enum! */
|
||||
} PMSignalReason;
|
||||
|
||||
|
|
|
|||
|
|
@ -304,6 +304,8 @@ typedef struct PROC_HDR {
|
|||
PGPROC* freeProcs;
|
||||
/* Head of list of autovacuum's free PGPROC structures */
|
||||
PGPROC* autovacFreeProcs;
|
||||
/* Head of list of bgworker free PGPROC structures */
|
||||
PGPROC* bgworkerFreeProcs;
|
||||
/* Head of list of pg_job's free PGPROC structures */
|
||||
PGPROC* pgjobfreeProcs;
|
||||
/* First pgproc waiting for group XID clear */
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ typedef enum {
|
|||
PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
|
||||
PROCSIG_EXECUTOR_FLAG,
|
||||
|
||||
PROCSIG_PARALLEL_MESSAGE, /* message from cooperating parallel backend */
|
||||
NUM_PROCSIGNALS /* Must be last! */
|
||||
} ProcSignalReason;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* shm_mq.h
|
||||
* single-reader, single-writer shared memory message queue
|
||||
*
|
||||
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/include/storage/shm_mq.h
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef SHM_MQ_H
|
||||
#define SHM_MQ_H
|
||||
|
||||
#include "postmaster/bgworker.h"
|
||||
#include "storage/proc.h"
|
||||
|
||||
/* The queue itself, in shared memory. */
|
||||
struct shm_mq;
|
||||
typedef struct shm_mq shm_mq;
|
||||
|
||||
/* Backend-private state. */
|
||||
struct shm_mq_handle;
|
||||
typedef struct shm_mq_handle shm_mq_handle;
|
||||
|
||||
/* Descriptors for a single write spanning multiple locations. */
|
||||
typedef struct {
|
||||
const char *data;
|
||||
Size len;
|
||||
} shm_mq_iovec;
|
||||
|
||||
/* Possible results of a send or receive operation. */
|
||||
typedef enum {
|
||||
SHM_MQ_SUCCESS, /* Sent or received a message. */
|
||||
SHM_MQ_WOULD_BLOCK, /* Not completed; retry later. */
|
||||
SHM_MQ_DETACHED /* Other process has detached queue. */
|
||||
} shm_mq_result;
|
||||
|
||||
/*
|
||||
* Primitives to create a queue and set the sender and receiver.
|
||||
*
|
||||
* Both the sender and the receiver must be set before any messages are read
|
||||
* or written, but they need not be set by the same process. Each must be
|
||||
* set exactly once.
|
||||
*/
|
||||
extern shm_mq *shm_mq_create(void *address, Size size);
|
||||
extern void shm_mq_set_receiver(shm_mq *mq, PGPROC *);
|
||||
extern void shm_mq_set_sender(shm_mq *mq, PGPROC *);
|
||||
|
||||
/* Accessor methods for sender and receiver. */
|
||||
extern PGPROC *shm_mq_get_receiver(shm_mq *);
|
||||
extern PGPROC *shm_mq_get_sender(shm_mq *);
|
||||
|
||||
/* Set up backend-local queue state. */
|
||||
extern shm_mq_handle *shm_mq_attach(shm_mq *mq, char *seg,
|
||||
BackgroundWorkerHandle *handle);
|
||||
|
||||
/* Associate worker handle with shm_mq. */
|
||||
extern void shm_mq_set_handle(shm_mq_handle *, BackgroundWorkerHandle *);
|
||||
|
||||
/* Break connection, release handle resources. */
|
||||
extern void shm_mq_detach(shm_mq_handle *mqh);
|
||||
|
||||
/* Get the shm_mq from handle. */
|
||||
extern shm_mq *shm_mq_get_queue(shm_mq_handle *mqh);
|
||||
|
||||
/* Send or receive messages. */
|
||||
extern shm_mq_result shm_mq_send(shm_mq_handle *mqh,
|
||||
Size nbytes, const void *data, bool nowait);
|
||||
extern shm_mq_result shm_mq_sendv(shm_mq_handle *mqh,
|
||||
shm_mq_iovec *iov, int iovcnt, bool nowait);
|
||||
extern shm_mq_result shm_mq_receive(shm_mq_handle *mqh,
|
||||
Size *nbytesp, void **datap, bool nowait);
|
||||
|
||||
/* Wait for our counterparty to attach to the queue. */
|
||||
extern shm_mq_result shm_mq_wait_for_attach(shm_mq_handle *mqh);
|
||||
|
||||
/* Smallest possible queue. */
|
||||
extern PGDLLIMPORT const Size shm_mq_minimum_size;
|
||||
|
||||
#endif /* SHM_MQ_H */
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* shm_toc.h
|
||||
* shared memory segment table of contents
|
||||
*
|
||||
* This is intended to provide a simple way to divide a chunk of shared
|
||||
* memory (probably dynamic shared memory allocated via dsm_create) into
|
||||
* a number of regions and keep track of the addresses of those regions or
|
||||
* key data structures within those regions. This is not intended to
|
||||
* scale to a large number of keys and will perform poorly if used that
|
||||
* way; if you need a large number of pointers, store them within some
|
||||
* other data structure within the segment and only put the pointer to
|
||||
* the data structure itself in the table of contents.
|
||||
*
|
||||
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* src/include/storage/shm_toc.h
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef SHM_TOC_H
|
||||
#define SHM_TOC_H
|
||||
|
||||
#include "storage/shmem.h"
|
||||
|
||||
struct shm_toc;
|
||||
typedef struct shm_toc shm_toc;
|
||||
struct shm_toc_entry;
|
||||
typedef struct shm_toc_entry shm_toc_entry;
|
||||
|
||||
extern shm_toc *shm_toc_create(uint64 magic, void *address, Size nbytes);
|
||||
extern shm_toc *shm_toc_attach(uint64 magic, void *address);
|
||||
extern void *shm_toc_allocate(shm_toc *toc, Size nbytes);
|
||||
extern Size shm_toc_freespace(shm_toc *toc);
|
||||
extern void shm_toc_insert(shm_toc *toc, uint64 key, void *address);
|
||||
extern void *shm_toc_lookup(shm_toc *toc, uint64 key);
|
||||
|
||||
/*
|
||||
* Tools for estimating how large a chunk of shared memory will be needed
|
||||
* to store a TOC and its dependent objects.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Size space_for_chunks;
|
||||
Size number_of_keys;
|
||||
} shm_toc_estimator;
|
||||
|
||||
#define shm_toc_initialize_estimator(e) \
|
||||
((e)->space_for_chunks = 0, (e)->number_of_keys = 0)
|
||||
#define shm_toc_estimate_chunk(e, sz) \
|
||||
((e)->space_for_chunks = add_size((e)->space_for_chunks, \
|
||||
BUFFERALIGN((sz))))
|
||||
#define shm_toc_estimate_keys(e, cnt) \
|
||||
((e)->number_of_keys = add_size((e)->number_of_keys, (cnt)))
|
||||
|
||||
extern Size shm_toc_estimate(shm_toc_estimator *);
|
||||
|
||||
#endif /* SHM_TOC_H */
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/*--------------------------------------------------------------------------
|
||||
*
|
||||
* autonomous.h
|
||||
* Run SQL commands using a background worker.
|
||||
*
|
||||
* Copyright (C) 2014, PostgreSQL Global Development Group
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/include/tcop/autonomous.h
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef AUTONOMOUS_H
|
||||
#define AUTONOMOUS_H
|
||||
|
||||
#include "access/tupdesc.h"
|
||||
#include "nodes/pg_list.h"
|
||||
|
||||
struct AutonomousSession;
|
||||
typedef struct AutonomousSession AutonomousSession;
|
||||
|
||||
struct AutonomousPreparedStatement;
|
||||
typedef struct AutonomousPreparedStatement AutonomousPreparedStatement;
|
||||
|
||||
struct autonomous_session_fixed_data;
|
||||
typedef struct autonomous_session_fixed_data autonomous_session_fixed_data;
|
||||
|
||||
typedef struct AutonomousResult
|
||||
{
|
||||
TupleDesc tupdesc;
|
||||
List *tuples;
|
||||
char *command;
|
||||
} AutonomousResult;
|
||||
|
||||
AutonomousSession *AutonomousSessionStart(void);
|
||||
void AutonomousSessionEnd(AutonomousSession *session);
|
||||
AutonomousResult *AutonomousSessionExecute(AutonomousSession *session, const char *sql);
|
||||
AutonomousPreparedStatement *AutonomousSessionPrepare(AutonomousSession *session, const char *sql, int16 nargs,
|
||||
Oid argtypes[], const char *argnames[]);
|
||||
AutonomousResult *AutonomousSessionExecutePrepared(AutonomousPreparedStatement *stmt, int16 nargs, Datum *values, bool *nulls);
|
||||
extern void autonomous_worker_main(Datum main_arg);
|
||||
|
||||
#endif /* AUTONOMOUS_H */
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
#include "nodes/parsenodes.h"
|
||||
#include "storage/procsignal.h"
|
||||
#include "utils/guc.h"
|
||||
#include "postgres.h"
|
||||
|
||||
/* Required daylight between max_stack_depth and the kernel limit, in bytes */
|
||||
#define STACK_DEPTH_SLOP (640 * 1024L)
|
||||
|
|
@ -67,5 +68,10 @@ extern int check_log_duration(char* msec_str, bool was_logged);
|
|||
extern void set_debug_options(int debug_flag, GucContext context, GucSource source);
|
||||
extern bool set_plan_disabling_options(const char* arg, GucContext context, GucSource source);
|
||||
extern const char* get_stats_option_name(const char* arg);
|
||||
extern void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL);
|
||||
extern void exec_parse_message(const char* query_string, const char* stmt_name, Oid* paramTypes, char** paramTypeNames, int numParams);
|
||||
extern void exec_bind_message(StringInfo input_message);
|
||||
extern void exec_execute_message(const char *portal_name, long max_rows);
|
||||
extern void exec_describe_statement_message(const char *stmt_name);
|
||||
|
||||
#endif /* TCOPPROT_H */
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ typedef struct Backend {
|
|||
bool is_autovacuum; /* is it an autovacuum process? */
|
||||
volatile bool dead_end; /* is it going to send an quit? */
|
||||
volatile int flag;
|
||||
bool bgworker_notify; /* gets bgworker start/stop notifications */
|
||||
Dlelem elem; /* list link in BackendList */
|
||||
} Backend;
|
||||
|
||||
|
|
|
|||
|
|
@ -495,6 +495,7 @@ extern void UpdateErrorData(ErrorData* edata, ErrorData* newData);
|
|||
extern void FreeErrorData(ErrorData* edata);
|
||||
extern void FlushErrorState(void);
|
||||
extern void FlushErrorStateWithoutDeleteChildrenContext(void);
|
||||
extern void ThrowErrorData(ErrorData *edata);
|
||||
extern void ReThrowError(ErrorData* edata) __attribute__((noreturn));
|
||||
extern void pg_re_throw(void) __attribute__((noreturn));
|
||||
|
||||
|
|
|
|||
|
|
@ -266,6 +266,10 @@ extern ArrayType* GUCArrayAdd(ArrayType* array, const char* name, const char* va
|
|||
extern ArrayType* GUCArrayDelete(ArrayType* array, const char* name);
|
||||
extern ArrayType* GUCArrayReset(ArrayType* array);
|
||||
|
||||
extern Size EstimateGUCStateSpace(void);
|
||||
extern void SerializeGUCState(Size maxsize, char *start_address);
|
||||
extern void RestoreGUCState(char *gucstate);
|
||||
|
||||
#ifdef EXEC_BACKEND
|
||||
extern void write_nondefault_variables(GucContext context);
|
||||
extern void read_nondefault_variables(void);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#include "catalog/namespace.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/spi.h"
|
||||
#include "tcop/autonomous.h"
|
||||
|
||||
/**********************************************************************
|
||||
* Definitions
|
||||
|
|
@ -380,6 +381,7 @@ typedef struct PLpgSQL_stmt_block { /* Block of statements */
|
|||
int cmd_type;
|
||||
int lineno;
|
||||
char* label;
|
||||
bool autonomous;
|
||||
List* body; /* List of statements */
|
||||
int n_initvars;
|
||||
int* initvarnos;
|
||||
|
|
@ -776,7 +778,7 @@ typedef struct PLpgSQL_execstate { /* Runtime execution data */
|
|||
MemoryContext tuple_store_cxt;
|
||||
ResourceOwner tuple_store_owner;
|
||||
ReturnSetInfo* rsi;
|
||||
|
||||
AutonomousSession *autonomous_session;
|
||||
int found_varno;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public:
|
|||
|
||||
~PostgresInitializer();
|
||||
|
||||
void SetDatabaseAndUser(const char* in_dbname, Oid dboid, const char* username);
|
||||
void SetDatabaseAndUser(const char* in_dbname, Oid dboid, const char* username, Oid useroid = InvalidOid);
|
||||
|
||||
void GetDatabaseName(char* out_dbname);
|
||||
|
||||
|
|
@ -91,6 +91,8 @@ public:
|
|||
|
||||
const char* m_username;
|
||||
|
||||
Oid m_useroid;
|
||||
|
||||
private:
|
||||
void InitThread();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,351 @@
|
|||
create table at_tb2(id int, val varchar(20));
|
||||
create or replace function at_test2(i int) returns integer
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
declare
|
||||
pragma autonomous_transaction;
|
||||
begin
|
||||
START TRANSACTION;
|
||||
insert into at_tb2 values(1, 'before s1');
|
||||
if i > 10 then
|
||||
rollback;
|
||||
else
|
||||
commit;
|
||||
end if;
|
||||
return i;
|
||||
end;
|
||||
$$;
|
||||
select at_test2(15);
|
||||
at_test2
|
||||
----------
|
||||
15
|
||||
(1 row)
|
||||
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-----
|
||||
(0 rows)
|
||||
|
||||
select at_test2(5);
|
||||
at_test2
|
||||
----------
|
||||
5
|
||||
(1 row)
|
||||
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-----------
|
||||
1 | before s1
|
||||
(1 row)
|
||||
|
||||
truncate table at_tb2;
|
||||
create or replace procedure at_test3(i int)
|
||||
AS
|
||||
DECLARE
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
START TRANSACTION;
|
||||
insert into at_tb2 values(1, 'before s1');
|
||||
insert into at_tb2 values(2, 'after s1');
|
||||
if i > 10 then
|
||||
rollback;
|
||||
else
|
||||
commit;
|
||||
end if;
|
||||
end;
|
||||
/
|
||||
call at_test3(6);
|
||||
at_test3
|
||||
----------
|
||||
|
||||
(1 row)
|
||||
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-----------
|
||||
1 | before s1
|
||||
2 | after s1
|
||||
(2 rows)
|
||||
|
||||
truncate table at_tb2;
|
||||
create or replace procedure at_test4(i int)
|
||||
AS
|
||||
DECLARE
|
||||
BEGIN
|
||||
insert into at_tb2 values(3, 'klk');
|
||||
PERFORM at_test3(6);
|
||||
insert into at_tb2 values(4, 'klk');
|
||||
PERFORM at_test3(15);
|
||||
end;
|
||||
/
|
||||
select at_test4(6);
|
||||
at_test4
|
||||
----------
|
||||
|
||||
(1 row)
|
||||
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-----------
|
||||
3 | klk
|
||||
1 | before s1
|
||||
2 | after s1
|
||||
4 | klk
|
||||
(4 rows)
|
||||
|
||||
truncate table at_tb2;
|
||||
DECLARE
|
||||
begin
|
||||
insert into at_tb2 values(1, 'begin');
|
||||
PERFORM at_test3(6);
|
||||
end;
|
||||
/
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-----------
|
||||
1 | begin
|
||||
1 | before s1
|
||||
2 | after s1
|
||||
(3 rows)
|
||||
|
||||
truncate table at_tb2;
|
||||
begin;
|
||||
insert into at_tb2 values(1, 'begin');
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-------
|
||||
1 | begin
|
||||
(1 row)
|
||||
|
||||
call at_test3(6);
|
||||
at_test3
|
||||
----------
|
||||
|
||||
(1 row)
|
||||
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-----------
|
||||
1 | begin
|
||||
1 | before s1
|
||||
2 | after s1
|
||||
(3 rows)
|
||||
|
||||
rollback;
|
||||
select * from at_tb2;
|
||||
id | val
|
||||
----+-----------
|
||||
1 | before s1
|
||||
2 | after s1
|
||||
(2 rows)
|
||||
|
||||
create table at_test1 (a int);
|
||||
create or replace procedure autonomous_test()
|
||||
AS
|
||||
declare
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
START TRANSACTION;
|
||||
for i in 0..9 loop
|
||||
if i % 2 = 0 then
|
||||
execute 'insert into at_test1 values ('||i::integer||')';
|
||||
end if;
|
||||
end loop;
|
||||
commit;
|
||||
end;
|
||||
/
|
||||
truncate table at_test1;
|
||||
begin;
|
||||
insert into at_test1 values(1);
|
||||
select * from at_test1;
|
||||
a
|
||||
---
|
||||
1
|
||||
(1 row)
|
||||
|
||||
call autonomous_test();
|
||||
autonomous_test
|
||||
-----------------
|
||||
|
||||
(1 row)
|
||||
|
||||
select * from at_test1;
|
||||
a
|
||||
---
|
||||
1
|
||||
0
|
||||
2
|
||||
4
|
||||
6
|
||||
8
|
||||
(6 rows)
|
||||
|
||||
rollback;
|
||||
select * from at_test1;
|
||||
a
|
||||
---
|
||||
0
|
||||
2
|
||||
4
|
||||
6
|
||||
8
|
||||
(5 rows)
|
||||
|
||||
create or replace function autonomous_test2() returns integer
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
declare
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
begin
|
||||
START TRANSACTION;
|
||||
for i in 0..9 loop
|
||||
if i % 2 = 0 then
|
||||
execute 'insert into at_test1 values ('||i::integer||')';
|
||||
end if;
|
||||
end loop;
|
||||
commit;
|
||||
return 42;
|
||||
end;
|
||||
$$;
|
||||
truncate table at_test1;
|
||||
begin;
|
||||
insert into at_test1 values(20);
|
||||
select * from at_test1;
|
||||
a
|
||||
----
|
||||
20
|
||||
(1 row)
|
||||
|
||||
select autonomous_test2();
|
||||
autonomous_test2
|
||||
------------------
|
||||
42
|
||||
(1 row)
|
||||
|
||||
select * from at_test1;
|
||||
a
|
||||
----
|
||||
20
|
||||
0
|
||||
2
|
||||
4
|
||||
6
|
||||
8
|
||||
(6 rows)
|
||||
|
||||
rollback;
|
||||
select * from at_test1;
|
||||
a
|
||||
---
|
||||
0
|
||||
2
|
||||
4
|
||||
6
|
||||
8
|
||||
(5 rows)
|
||||
|
||||
create or replace function autonomous_test3() returns text
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
declare
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
begin
|
||||
START TRANSACTION;
|
||||
for i in 0..9 loop
|
||||
if i % 2 = 0 then
|
||||
execute 'insert into at_test1 values ('||i::integer||')';
|
||||
end if;
|
||||
end loop;
|
||||
commit;
|
||||
return 'autonomous_test3 end';
|
||||
end;
|
||||
$$;
|
||||
truncate table at_test1;
|
||||
begin;
|
||||
insert into at_test1 values(30);
|
||||
select * from at_test1;
|
||||
a
|
||||
----
|
||||
30
|
||||
(1 row)
|
||||
|
||||
select autonomous_test3();
|
||||
autonomous_test3
|
||||
----------------------
|
||||
autonomous_test3 end
|
||||
(1 row)
|
||||
|
||||
select * from at_test1;
|
||||
a
|
||||
----
|
||||
30
|
||||
0
|
||||
2
|
||||
4
|
||||
6
|
||||
8
|
||||
(6 rows)
|
||||
|
||||
rollback;
|
||||
select * from at_test1;
|
||||
a
|
||||
---
|
||||
0
|
||||
2
|
||||
4
|
||||
6
|
||||
8
|
||||
(5 rows)
|
||||
|
||||
CREATE TABLE cp_test1 (a int, b text);
|
||||
CREATE TABLE cp_test2 (a int, b text);
|
||||
CREATE TABLE cp_test3 (a int, b text);
|
||||
CREATE OR REPLACE FUNCTION autonomous_cp() RETURNS integer
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
START TRANSACTION;
|
||||
insert into cp_test1 values(1,'a'),(2,'b');
|
||||
insert into cp_test2 values(1,'c'),(2,'d');
|
||||
with s1 as (select cp_test1.a, cp_test1.b from cp_test1 left join cp_test2 on cp_test1.a = cp_test2.a) insert into cp_test3 select * from s1;
|
||||
COMMIT;
|
||||
RETURN 42;
|
||||
END;
|
||||
$$;
|
||||
select autonomous_cp();
|
||||
autonomous_cp
|
||||
---------------
|
||||
42
|
||||
(1 row)
|
||||
|
||||
select * from cp_test3;
|
||||
a | b
|
||||
---+---
|
||||
1 | a
|
||||
2 | b
|
||||
(2 rows)
|
||||
|
||||
CREATE TABLE tg_test1 (a int, b varchar(25), c timestamp, d int);
|
||||
CREATE TABLE tg_test2 (a int, b varchar(25), c timestamp, d int);
|
||||
CREATE OR REPLACE FUNCTION tri_insert_test2_func() RETURNS TRIGGER AS
|
||||
$$
|
||||
DECLARE
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
insert into tg_test2 values(new.a,new.b,new.c,new.d);
|
||||
RETURN NEW;
|
||||
commit;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
CREATE TRIGGER TG_TEST2_TEMP
|
||||
before insert
|
||||
ON tg_test1
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE tri_insert_test2_func();
|
||||
insert into tg_test1 values(1,'a','2020-08-13 09:00:00', 1);
|
||||
ERROR: Un-support feature
|
||||
DETAIL: Trigger doesnot support autonomous transaction
|
||||
CONTEXT: PL/pgSQL function tri_insert_test2_func() line 4 at statement block
|
||||
|
||||
|
|
@ -588,3 +588,6 @@ test: gtt_clean
|
|||
|
||||
# procedure, Function Test
|
||||
test: create_procedure create_function pg_compatibility postgres_fdw
|
||||
|
||||
# autonomous transaction Test
|
||||
test: autonomous_transaction
|
||||
|
|
|
|||
|
|
@ -0,0 +1,189 @@
|
|||
create table at_tb2(id int, val varchar(20));
|
||||
create or replace function at_test2(i int) returns integer
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
declare
|
||||
pragma autonomous_transaction;
|
||||
begin
|
||||
START TRANSACTION;
|
||||
insert into at_tb2 values(1, 'before s1');
|
||||
if i > 10 then
|
||||
rollback;
|
||||
else
|
||||
commit;
|
||||
end if;
|
||||
return i;
|
||||
end;
|
||||
$$;
|
||||
select at_test2(15);
|
||||
select * from at_tb2;
|
||||
select at_test2(5);
|
||||
select * from at_tb2;
|
||||
|
||||
truncate table at_tb2;
|
||||
create or replace procedure at_test3(i int)
|
||||
AS
|
||||
DECLARE
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
START TRANSACTION;
|
||||
insert into at_tb2 values(1, 'before s1');
|
||||
insert into at_tb2 values(2, 'after s1');
|
||||
if i > 10 then
|
||||
rollback;
|
||||
else
|
||||
commit;
|
||||
end if;
|
||||
end;
|
||||
/
|
||||
call at_test3(6);
|
||||
select * from at_tb2;
|
||||
|
||||
truncate table at_tb2;
|
||||
create or replace procedure at_test4(i int)
|
||||
AS
|
||||
DECLARE
|
||||
BEGIN
|
||||
insert into at_tb2 values(3, 'klk');
|
||||
PERFORM at_test3(6);
|
||||
insert into at_tb2 values(4, 'klk');
|
||||
PERFORM at_test3(15);
|
||||
end;
|
||||
/
|
||||
select at_test4(6);
|
||||
select * from at_tb2;
|
||||
|
||||
truncate table at_tb2;
|
||||
DECLARE
|
||||
begin
|
||||
insert into at_tb2 values(1, 'begin');
|
||||
PERFORM at_test3(6);
|
||||
end;
|
||||
/
|
||||
select * from at_tb2;
|
||||
|
||||
truncate table at_tb2;
|
||||
begin;
|
||||
insert into at_tb2 values(1, 'begin');
|
||||
select * from at_tb2;
|
||||
call at_test3(6);
|
||||
select * from at_tb2;
|
||||
rollback;
|
||||
select * from at_tb2;
|
||||
|
||||
create table at_test1 (a int);
|
||||
create or replace procedure autonomous_test()
|
||||
AS
|
||||
declare
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
START TRANSACTION;
|
||||
for i in 0..9 loop
|
||||
if i % 2 = 0 then
|
||||
execute 'insert into at_test1 values ('||i::integer||')';
|
||||
end if;
|
||||
end loop;
|
||||
commit;
|
||||
end;
|
||||
/
|
||||
|
||||
truncate table at_test1;
|
||||
begin;
|
||||
insert into at_test1 values(1);
|
||||
select * from at_test1;
|
||||
call autonomous_test();
|
||||
select * from at_test1;
|
||||
rollback;
|
||||
select * from at_test1;
|
||||
|
||||
|
||||
create or replace function autonomous_test2() returns integer
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
declare
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
begin
|
||||
START TRANSACTION;
|
||||
for i in 0..9 loop
|
||||
if i % 2 = 0 then
|
||||
execute 'insert into at_test1 values ('||i::integer||')';
|
||||
end if;
|
||||
end loop;
|
||||
commit;
|
||||
return 42;
|
||||
end;
|
||||
$$;
|
||||
truncate table at_test1;
|
||||
begin;
|
||||
insert into at_test1 values(20);
|
||||
select * from at_test1;
|
||||
select autonomous_test2();
|
||||
select * from at_test1;
|
||||
rollback;
|
||||
select * from at_test1;
|
||||
|
||||
create or replace function autonomous_test3() returns text
|
||||
LANGUAGE plpgsql
|
||||
as $$
|
||||
declare
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
begin
|
||||
START TRANSACTION;
|
||||
for i in 0..9 loop
|
||||
if i % 2 = 0 then
|
||||
execute 'insert into at_test1 values ('||i::integer||')';
|
||||
end if;
|
||||
end loop;
|
||||
commit;
|
||||
return 'autonomous_test3 end';
|
||||
end;
|
||||
$$;
|
||||
truncate table at_test1;
|
||||
begin;
|
||||
insert into at_test1 values(30);
|
||||
select * from at_test1;
|
||||
select autonomous_test3();
|
||||
select * from at_test1;
|
||||
rollback;
|
||||
select * from at_test1;
|
||||
|
||||
CREATE TABLE cp_test1 (a int, b text);
|
||||
CREATE TABLE cp_test2 (a int, b text);
|
||||
CREATE TABLE cp_test3 (a int, b text);
|
||||
CREATE OR REPLACE FUNCTION autonomous_cp() RETURNS integer
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
START TRANSACTION;
|
||||
insert into cp_test1 values(1,'a'),(2,'b');
|
||||
insert into cp_test2 values(1,'c'),(2,'d');
|
||||
with s1 as (select cp_test1.a, cp_test1.b from cp_test1 left join cp_test2 on cp_test1.a = cp_test2.a) insert into cp_test3 select * from s1;
|
||||
COMMIT;
|
||||
RETURN 42;
|
||||
END;
|
||||
$$;
|
||||
select autonomous_cp();
|
||||
select * from cp_test3;
|
||||
|
||||
CREATE TABLE tg_test1 (a int, b varchar(25), c timestamp, d int);
|
||||
CREATE TABLE tg_test2 (a int, b varchar(25), c timestamp, d int);
|
||||
CREATE OR REPLACE FUNCTION tri_insert_test2_func() RETURNS TRIGGER AS
|
||||
$$
|
||||
DECLARE
|
||||
PRAGMA AUTONOMOUS_TRANSACTION;
|
||||
BEGIN
|
||||
insert into tg_test2 values(new.a,new.b,new.c,new.d);
|
||||
RETURN NEW;
|
||||
commit;
|
||||
END
|
||||
$$ LANGUAGE PLPGSQL;
|
||||
|
||||
CREATE TRIGGER TG_TEST2_TEMP
|
||||
before insert
|
||||
ON tg_test1
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE tri_insert_test2_func();
|
||||
insert into tg_test1 values(1,'a','2020-08-13 09:00:00', 1);
|
||||
|
||||
Loading…
Reference in New Issue