!134 Support autonomous transaction

Merge pull request !134 from 江建宇/master
This commit is contained in:
opengauss-bot 2020-08-29 17:26:45 +08:00 committed by Gitee
commit 4728c27ca8
66 changed files with 6407 additions and 85 deletions

View File

@ -59,6 +59,7 @@ void initStringInfo(StringInfo str)
*/
void resetStringInfo(StringInfo str)
{
str->data[0] = '\0';
str->len = 0;
str->cursor = 0;

View File

@ -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

View File

@ -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;

View File

@ -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
* --------------------------------

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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.
*/

View File

@ -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 */

View File

@ -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
*

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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, &param_names, &param_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, &param_names, &param_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, &param_names, &param_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;

View File

@ -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)

View File

@ -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;
/*

View File

@ -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;

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

@ -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)
{

View File

@ -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)

View File

@ -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)));
}

View File

@ -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, &paramTypes, &numParams);
query = parse_analyze_varparams(raw_parse_tree, query_string, &paramTypes, &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;

View File

@ -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);

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -96,3 +96,4 @@ GPCCommitLock 88
GPCClearLock 89
GPCTimelineLock 90
TsTagsCacheLock 91
BackgroundWorkerLock 92

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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. */

View File

@ -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();

View File

@ -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();

View File

@ -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);

View File

@ -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);
/*

25
src/include/libpq/pqmq.h Normal file
View File

@ -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 */

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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 */

View File

@ -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;

View File

@ -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));

View File

@ -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);

View File

@ -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;
/*

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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);