Newer
Older
Korry Douglas
committed
/**********************************************************************
Heikki Linnakangas
committed
* plugin_debugger.c - Language-independent parts of debugger
Korry Douglas
committed
*
* Copyright (c) 2004-2024 EnterpriseDB Corporation. All Rights Reserved.
Korry Douglas
committed
*
* Licensed under the Artistic License v2.0, see
* https://opensource.org/licenses/artistic-license-2.0
Korry Douglas
committed
* for full details
*
**********************************************************************/
Dave Page
committed
#include "postgres.h"
Korry Douglas
committed
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <setjmp.h>
#include <signal.h>
#ifdef WIN32
#include<winsock2.h>
#else
#include <netinet/in.h>
Korry Douglas
committed
#include <sys/socket.h>
#include <arpa/inet.h>
#endif
#include "access/xact.h"
Korry Douglas
committed
#include "lib/stringinfo.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
Rushabh Lathia
committed
#if (PG_VERSION_NUM >= 130000)
#include "common/hashfn.h"
#endif
Korry Douglas
committed
#include "parser/parser.h"
#include "parser/parse_func.h"
#include "globalbp.h"
#include "storage/proc.h" /* For MyProc */
#include "storage/procarray.h" /* For BackendPidGetProc */
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
Korry Douglas
committed
Heikki Linnakangas
committed
#include "pldebugger.h"
Korry Douglas
committed
/* Include header for GETSTRUCT */
#if (PG_VERSION_NUM >= 90300)
#include "access/htup_details.h"
#endif
Korry Douglas
committed
#define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
#define TARGET_PROTO_VERSION "1.1"
Korry Douglas
committed
/**********************************************************************
* Type and structure definitions
**********************************************************************/
Korry Douglas
committed
* eConnectType
*
* This enum defines the different ways that we can connect to the
* debugger proxy.
Korry Douglas
committed
*
* CONNECT_AS_SERVER means that we create a socket, bind an address to
* to that socket, send a NOTICE to our client application, and wait for
* a debugger proxy to attach to us. That's what happens when your
* client application sets a local breakpoint and can handle the
Korry Douglas
committed
* NOTICE that we send.
*
* CONNECT_AS_CLIENT means that a proxy has already created a socket
* and is waiting for a target (that's us) to connect to it. We do
* this kind of connection stuff when a debugger client sets a global
* breakpoint and we happen to blunder into that breakpoint.
*
* CONNECT_UNKNOWN indicates a problem, we shouldn't ever see this.
*/
typedef enum
{
CONNECT_AS_SERVER, /* Open a server socket and wait for a proxy to connect to us */
CONNECT_AS_CLIENT, /* Connect to a waiting proxy (global breakpoints do this) */
CONNECT_UNKNOWN /* Must already be connected */
} eConnectType;
/* Global breakpoint data. */
typedef struct
{
#if (PG_VERSION_NUM >= 90600)
int tranche_id;
LWLock lock;
#else
LWLockId lockid;
#endif
} GlobalBreakpointData;
Korry Douglas
committed
/**********************************************************************
* Local (static) variables
**********************************************************************/
Heikki Linnakangas
committed
per_session_ctx_t per_session_ctx;
Korry Douglas
committed
Heikki Linnakangas
committed
errorHandlerCtx client_lost;
Korry Douglas
committed
static debugger_language_t *debugger_languages[] = {
&plpgsql_debugger_lang,
#ifdef INCLUDE_PACKAGE_SUPPORT
&spl_debugger_lang,
#endif
NULL
};
#if (PG_VERSION_NUM >= 150000)
static shmem_request_hook_type prev_shmem_request_hook = NULL;
#endif
Korry Douglas
committed
/**********************************************************************
* Function declarations
**********************************************************************/
void _PG_init( void ); /* initialize this module when we are dynamically loaded */
/**********************************************************************
* Local (hidden) function prototypes
**********************************************************************/
#if (PG_VERSION_NUM >= 150000)
static void pldebugger_shmem_request( void );
#endif
Korry Douglas
committed
static void * writen( int peer, void * src, size_t len );
static bool connectAsServer( void );
static bool connectAsClient( Breakpoint * breakpoint );
static bool handle_socket_error(void);
static bool parseBreakpoint( Oid * funcOID, int * lineNumber, char * breakpointString );
static bool addLocalBreakpoint( Oid funcOID, int lineNo );
static void reserveBreakpoints( void );
static debugger_language_t *language_of_frame(ErrorContextCallback *frame);
static char * findSource( Oid oid, HeapTuple * tup );
static void do_deposit(ErrorContextCallback *frame, debugger_language_t *lang,
char *command);
static void send_breakpoints(Oid funcOid);
static void send_stack(void);
static void select_frame(int frameNo, ErrorContextCallback **frame_p, debugger_language_t **lang_p);
Korry Douglas
committed
/**********************************************************************
* Function definitions
**********************************************************************/
void _PG_init( void )
{
int i;
/* Initialize all the per-language hooks. */
for (i = 0; debugger_languages[i] != NULL; i++)
debugger_languages[i]->initialize();
Korry Douglas
committed
#if (PG_VERSION_NUM >= 150000)
prev_shmem_request_hook = shmem_request_hook;
shmem_request_hook = pldebugger_shmem_request;
#else
reserveBreakpoints();
dbgcomm_reserve();
#endif
}
#if (PG_VERSION_NUM >= 150000)
static void pldebugger_shmem_request( void )
{
if (prev_shmem_request_hook)
prev_shmem_request_hook();
Korry Douglas
committed
reserveBreakpoints();
Korry Douglas
committed
}
Korry Douglas
committed
/*
Heikki Linnakangas
committed
* CREATE OR REPLACE FUNCTION pldbg_oid_debug( functionOID OID ) RETURNS INTEGER AS 'pldbg_oid_debug' LANGUAGE C;
Korry Douglas
committed
*/
PGDLLEXPORT Datum pldbg_oid_debug(PG_FUNCTION_ARGS);
Heikki Linnakangas
committed
PG_FUNCTION_INFO_V1(pldbg_oid_debug);
Korry Douglas
committed
Heikki Linnakangas
committed
Datum pldbg_oid_debug(PG_FUNCTION_ARGS)
Korry Douglas
committed
{
Korry Douglas
committed
if(( funcOid = PG_GETARG_OID( 0 )) == InvalidOid )
ereport( ERROR, ( errcode( ERRCODE_UNDEFINED_FUNCTION ), errmsg( "no target specified" )));
/* get the owner of the function */
tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u",
funcOid);
userid = ((Form_pg_proc) GETSTRUCT(tuple))->proowner;
ReleaseSysCache(tuple);
if( !superuser() && (GetUserId() != userid))
ereport( ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg( "must be owner or superuser to create a breakpoint" )));
Korry Douglas
committed
addLocalBreakpoint( funcOid, -1 );
PG_RETURN_INT32( 0 );
}
/*
* ---------------------------------------------------------------------
* readn()
*
* This function reads exactly 'len' bytes from the given socket or it
* throws an error. readn() will hang until the proper number of bytes
Korry Douglas
committed
* have been read (or an error occurs).
*
* Note: dst must point to a buffer large enough to hold at least 'len'
Korry Douglas
committed
* bytes. readn() returns dst (for convenience).
*/
static void * readn( int peer, void * dst, size_t len )
{
size_t bytesRemaining = len;
char * buffer = (char *)dst;
while( bytesRemaining > 0 )
{
ssize_t bytesRead = recv( peer, buffer, bytesRemaining, 0 );
if( bytesRead <= 0 && errno != EINTR )
handle_socket_error();
/* Ignore if we didn't receive anything. */
if ( bytesRead > 0 )
{
bytesRemaining -= bytesRead;
buffer += bytesRead;
}
Korry Douglas
committed
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
}
return( dst );
}
/*
* ---------------------------------------------------------------------
* readUInt32()
*
* Reads a 32-bit unsigned value from the server (and returns it in the host's
* byte ordering)
*/
static uint32 readUInt32( int channel )
{
uint32 netVal;
readn( channel, &netVal, sizeof( netVal ));
return( ntohl( netVal ));
}
/*
* ---------------------------------------------------------------------
* dbg_read_str()
*
* This function reads a counted string from the given stream
Heikki Linnakangas
committed
* Returns a palloc'd, null-terminated string.
Korry Douglas
committed
*
* NOTE: the server-side of the debugger uses this function to read a
Korry Douglas
committed
* string from the client side
*/
Heikki Linnakangas
committed
char *dbg_read_str( void )
Korry Douglas
committed
{
Heikki Linnakangas
committed
uint32 len;
char *dst;
Heikki Linnakangas
committed
int sock = per_session_ctx.client_r;
Heikki Linnakangas
committed
Korry Douglas
committed
len = readUInt32( sock );
Heikki Linnakangas
committed
dst = palloc(len + 1);
Korry Douglas
committed
readn( sock, dst, len );
Korry Douglas
committed
dst[len] = '\0';
Heikki Linnakangas
committed
return dst;
Korry Douglas
committed
}
/*
* ---------------------------------------------------------------------
Korry Douglas
committed
* writen()
*
* This function writes exactly 'len' bytes to the given socket or it
Korry Douglas
committed
* throws an error. writen() will hang until the proper number of bytes
* have been written (or an error occurs).
*/
static void * writen( int peer, void * src, size_t len )
{
size_t bytesRemaining = len;
char * buffer = (char *)src;
while( bytesRemaining > 0 )
{
ssize_t bytesWritten;
if(( bytesWritten = send( peer, buffer, bytesRemaining, 0 )) <= 0 )
handle_socket_error();
Korry Douglas
committed
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
bytesRemaining -= bytesWritten;
buffer += bytesWritten;
}
return( src );
}
/*
* ---------------------------------------------------------------------
* sendUInt32()
*
* This function sends a uint32 value (val) to the debugger server.
*/
static void sendUInt32( int channel, uint32 val )
{
uint32 netVal = htonl( val );
writen( channel, &netVal, sizeof( netVal ));
}
/*
* ---------------------------------------------------------------------
* dbg_send()
*
* This function writes a formatted, counted string to the
* given stream. The argument list for this function is identical to
* the argument list for the fprintf() function - you provide a socket,
* a format string, and then some number of arguments whose meanings
* are defined by the format string.
Korry Douglas
committed
*
* NOTE: the server-side of the debugger uses this function to send
* data to the client side. If the connection drops, dbg_send()
* will longjmp() back to the debugger top-level so that the
Korry Douglas
committed
* server-side can respond properly.
*/
Heikki Linnakangas
committed
void dbg_send( const char *fmt, ... )
Korry Douglas
committed
{
StringInfoData result;
char *data;
size_t remaining;
Heikki Linnakangas
committed
int sock = per_session_ctx.client_w;
if( !sock )
return;
initStringInfo(&result);
for (;;)
Korry Douglas
committed
{
#if (PG_VERSION_NUM >= 90400)
int needed;
va_start(args, fmt);
needed = appendStringInfoVA(&result, fmt, args);
va_end(args);
if (needed == 0)
break;
enlargeStringInfo(&result, needed);
#else
Korry Douglas
committed
va_start(args, fmt);
success = appendStringInfoVA(&result, fmt, args);
va_end(args);
Korry Douglas
committed
if (success)
break;
Korry Douglas
committed
enlargeStringInfo(&result, result.maxlen);
Korry Douglas
committed
data = result.data;
remaining = strlen(data);
Korry Douglas
committed
sendUInt32(sock, remaining);
while( remaining > 0 )
{
int written = send( sock, data, remaining, 0 );
if(written < 0)
handle_socket_error();
continue;
Korry Douglas
committed
}
remaining -= written;
data += written;
Korry Douglas
committed
}
pfree(result.data);
Korry Douglas
committed
}
/*
* ---------------------------------------------------------------------
* dbg_send_src()
*
* dbg_send_src() sends the source code for a function to the client.
*
* The client caches the source code that we send it and uses xmin/cmin
* to ensure the validity of the cache.
*/
static void dbg_send_src( char * command )
{
Oid targetOid = InvalidOid; /* Initialize to keep compiler happy */
targetOid = atoi( command + 2 );
/* Find the source code for this function */
procSrc = findSource( targetOid, &tup );
/* Found it - now send the source to the client */
dbg_send( "%s", procSrc );
/* Release the process tuple and send a footer to the client so he knows we're finished */
}
Korry Douglas
committed
/*
* ---------------------------------------------------------------------
* findSource()
*
* This function locates and returns a pointer to a null-terminated string
* that contains the source code for the given function (identified by its
* OID).
*
* In addition to returning a pointer to the requested source code, this
* function sets *tup to point to a HeapTuple (that you must release when
Korry Douglas
committed
*/
static char * findSource( Oid oid, HeapTuple * tup )
Korry Douglas
committed
{
bool isNull;
*tup = SearchSysCache( PROCOID, ObjectIdGetDatum( oid ), 0, 0, 0 );
if(!HeapTupleIsValid( *tup ))
elog( ERROR, "pldebugger: cache lookup for proc %u failed", oid );
return( DatumGetCString( DirectFunctionCall1( textout, SysCacheGetAttr( PROCOID, *tup, Anum_pg_proc_prosrc, &isNull ))));
}
/*
* ---------------------------------------------------------------------
* attach_to_proxy()
*
* This function creates a connection to the debugger client (via the
* proxy process). attach_to_proxy() will hang the PostgreSQL backend
* until the debugger client completes the connection.
*
* We start by asking the TCP/IP stack to allocate an unused port, then we
Korry Douglas
committed
* extract the port number from the resulting socket, send the port number to
* the client application (by raising a NOTICE), and finally, we wait for the
* client to connect.
*
* We assume that the client application knows the IP address of the PostgreSQL
* backend process - if that turns out to be a poor assumption, we can include
Korry Douglas
committed
* the IP address in the notification string that we send to the client application.
*/
Heikki Linnakangas
committed
bool attach_to_proxy( Breakpoint * breakpoint )
Korry Douglas
committed
{
bool result;
errorHandlerCtx save;
Korry Douglas
committed
{
/* We're already connected to a live proxy, just go home */
return( TRUE );
}
if( breakpoint == NULL )
{
Korry Douglas
committed
* No breakpoint - that implies that we're 'stepping into'.
* We had better already have a connection to a proxy here
* (how could we be 'stepping into' if we aren't connected
* to a proxy?)
*/
return( FALSE );
}
/*
* When a networking error is detected, we longjmp() to the client_lost
* error handler - that normally points to a location inside of dbg_newstmt()
* but we want to handle any network errors that arise while we are
Korry Douglas
committed
* setting up a link to the proxy. So, we save the original client_lost
* error handler context and push our own context on to the stack.
*/
save = client_lost;
Korry Douglas
committed
if( sigsetjmp( client_lost.m_savepoint, 1 ) != 0 )
{
client_lost = save;
return( FALSE );
}
Korry Douglas
committed
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
{
/*
* proxyPort == -1 implies that this is a local breakpoint,
* create a server socket and wait for the proxy to contact
* us.
*/
result = connectAsServer();
}
else
{
/*
* proxyPort != -1 implies that this is a global breakpoint,
* a debugger proxy is already waiting for us at the given
* port (on this host), connect to that proxy.
*/
result = connectAsClient( breakpoint );
}
/*
* Now restore the original error handler context so that
* dbg_newstmt() can handle any future network errors.
*/
client_lost = save;
return( result );
}
/*
* ---------------------------------------------------------------------
* connectAsServer()
*
* This function creates a socket, asks the TCP/IP stack to bind it to
* an unused port, and then waits for a debugger proxy to connect to
* that port. We send a NOTICE to our client process (on the other
Korry Douglas
committed
* end of the fe/be connection) to let the client know that it should
* fire up a debugger and attach to that port (the NOTICE includes
Korry Douglas
committed
* the port number)
*/
static bool connectAsServer( void )
{
Korry Douglas
committed
client_sock = dbgcomm_listen_for_proxy();
if (client_sock < 0)
Korry Douglas
committed
{
per_session_ctx.client_w = per_session_ctx.client_r = 0;
Korry Douglas
committed
}
Korry Douglas
committed
{
per_session_ctx.client_w = client_sock;
per_session_ctx.client_r = client_sock;
return( TRUE );
Korry Douglas
committed
}
}
/*
* ---------------------------------------------------------------------
* connectAsClient()
*
* This function connects to a waiting proxy process over the given
* port. We got the port number from a global breakpoint (the proxy
* stores it's port number in the breakpoint so we'll know how to
Korry Douglas
committed
* find that proxy).
*/
static bool connectAsClient( Breakpoint * breakpoint )
{
int proxySocket;
proxySocket = dbgcomm_connect_to_proxy(breakpoint->data.proxyPort);
Korry Douglas
committed
if (proxySocket < 0 )
Korry Douglas
committed
{
/* dbgcomm_connect_to_proxy already logged the reason */
return false;
Korry Douglas
committed
}
Korry Douglas
committed
{
per_session_ctx.client_w = proxySocket;
per_session_ctx.client_r = proxySocket;
Korry Douglas
committed
BreakpointBusySession( breakpoint->data.proxyPid );
return true;
}
Korry Douglas
committed
}
Heikki Linnakangas
committed
/*
* ---------------------------------------------------------------------
* parseBreakpoint()
Korry Douglas
committed
*
* Given a string that formatted like "funcOID:linenumber",
* this function parses out the components and returns them to the
* caller. If the string is well-formatted, this function returns
Heikki Linnakangas
committed
* TRUE, otherwise, we return FALSE.
Korry Douglas
committed
*/
Heikki Linnakangas
committed
static bool parseBreakpoint( Oid * funcOID, int * lineNumber, char * breakpointString )
Korry Douglas
committed
{
Heikki Linnakangas
committed
int a, b;
int n;
Korry Douglas
committed
Heikki Linnakangas
committed
n = sscanf(breakpointString, "%d:%d", &a, &b);
if (n == 2)
Korry Douglas
committed
{
Heikki Linnakangas
committed
*funcOID = a;
*lineNumber = b;
Korry Douglas
committed
}
Heikki Linnakangas
committed
else
return false;
Korry Douglas
committed
Heikki Linnakangas
committed
return( TRUE );
Korry Douglas
committed
}
/*
* ---------------------------------------------------------------------
Korry Douglas
committed
* addLocalBreakpoint()
*
* This function adds a local breakpoint for the given function and
Korry Douglas
committed
* line number
*/
static bool addLocalBreakpoint( Oid funcOID, int lineNo )
{
Breakpoint breakpoint;
Korry Douglas
committed
breakpoint.key.databaseId = MyProc->databaseId;
breakpoint.key.functionId = funcOID;
breakpoint.key.lineNumber = lineNo;
breakpoint.key.targetPid = MyProc->pid;
breakpoint.data.isTmp = FALSE;
breakpoint.data.proxyPort = -1;
breakpoint.data.proxyPid = -1;
return( BreakpointInsert( BP_LOCAL, &breakpoint.key, &breakpoint.data ));
}
/*
* ---------------------------------------------------------------------
Korry Douglas
committed
* setBreakpoint()
*
* The debugger client can set a local breakpoint at a given
Korry Douglas
committed
* function/procedure and line number by calling this function
* (through the debugger proxy process).
*/
Heikki Linnakangas
committed
void setBreakpoint( char * command )
Korry Douglas
committed
{
Korry Douglas
committed
* Format is 'b funcOID:lineNumber'
*/
int lineNo;
Oid funcOID;
if( parseBreakpoint( &funcOID, &lineNo, command + 2 ))
{
if( addLocalBreakpoint( funcOID, lineNo ))
Heikki Linnakangas
committed
dbg_send( "%s", "t" );
Korry Douglas
committed
else
Heikki Linnakangas
committed
dbg_send( "%s", "f" );
Korry Douglas
committed
}
else
{
Heikki Linnakangas
committed
dbg_send( "%s", "f" );
Korry Douglas
committed
}
}
/*
* ---------------------------------------------------------------------
Korry Douglas
committed
* clearBreakpoint()
*
* This function deletes the breakpoint at the package,
* function/procedure, and line number indicated by the
* given command.
*
* For now, we maintain our own private list of breakpoints -
* later, we'll use the same list managed by the CREATE/
* DROP BREAKPOINT commands.
*/
Heikki Linnakangas
committed
void clearBreakpoint( char * command )
Korry Douglas
committed
{
Korry Douglas
committed
* Format is 'f funcOID:lineNumber'
*/
int lineNo;
Oid funcOID;
if( parseBreakpoint( &funcOID, &lineNo, command + 2 ))
{
Breakpoint breakpoint;
Korry Douglas
committed
breakpoint.key.databaseId = MyProc->databaseId;
breakpoint.key.functionId = funcOID;
breakpoint.key.lineNumber = lineNo;
breakpoint.key.targetPid = MyProc->pid;
if( BreakpointDelete( BP_LOCAL, &breakpoint.key ))
Heikki Linnakangas
committed
dbg_send( "t" );
Korry Douglas
committed
else
Heikki Linnakangas
committed
dbg_send( "f" );
Korry Douglas
committed
}
else
{
Korry Douglas
committed
}
}
Heikki Linnakangas
committed
bool breakAtThisLine( Breakpoint ** dst, eBreakpointScope * scope, Oid funcOid, int lineNumber )
Korry Douglas
committed
{
BreakpointKey key;
key.databaseId = MyProc->databaseId;
key.functionId = funcOid;
Korry Douglas
committed
if( per_session_ctx.step_into_next_func )
{
*dst = NULL;
*scope = BP_LOCAL;
return( TRUE );
}
* First, we look for a global breakpoint at this line, targeting our
* specific backend process.
*
* Next, we look for a global breakpoint (at this line) that does
* not target a specific backend process.
*
* Finally, we look for a local breakpoint at this line (implicitly
* targeting our specific backend process).
*
* NOTE: We must do the local-breakpoint search last because, when the
* proxy attaches to our process, it marks all of its global
* breakpoints as busy (so other potential targets will ignore
* those breakpoints) and we copy all of those global breakpoints
* into our local breakpoint hash. If the debugger client exits
* and the user starts another debugger session, we want to see the
* new breakpoints instead of our obsolete local breakpoints (we
* don't have a good way to detect that the proxy has disconnected
* until it's inconvenient - we have to read-from or write-to the
* proxy before we can tell that it's died).
*/
Korry Douglas
committed
key.targetPid = MyProc->pid; /* Search for a global breakpoint targeted at our process ID */
Korry Douglas
committed
if((( *dst = BreakpointLookup( BP_GLOBAL, &key )) != NULL ) && ((*dst)->data.busy == FALSE ))
{
*scope = BP_GLOBAL;
return( TRUE );
}
key.targetPid = -1; /* Search for a global breakpoint targeted at any process ID */
if((( *dst = BreakpointLookup( BP_GLOBAL, &key )) != NULL ) && ((*dst)->data.busy == FALSE ))
{
*scope = BP_GLOBAL;
return( TRUE );
}
key.targetPid = MyProc->pid; /* Search for a local breakpoint (targeted at our process ID) */
if(( *dst = BreakpointLookup( BP_LOCAL, &key )) != NULL )
{
*scope = BP_LOCAL;
return( TRUE );
}
return( FALSE );
Korry Douglas
committed
Heikki Linnakangas
committed
bool breakpointsForFunction( Oid funcOid )
Korry Douglas
committed
{
if( BreakpointOnId( BP_LOCAL, funcOid ) || BreakpointOnId( BP_GLOBAL, funcOid ))
return( TRUE );
else
return( FALSE );
}
/* ---------------------------------------------------------------------
* handle_socket_error()
*
* when invoked after a socket operation it would check socket operation's
* last error status and invoke siglongjmp incase the error is fatal.
Korry Douglas
committed
*/
static bool handle_socket_error(void)
{
int err;
Korry Douglas
committed
#ifdef WIN32
err = WSAGetLastError();
Korry Douglas
committed
{
Korry Douglas
committed
case WSAEINTR:
case WSAEBADF:
case WSAEACCES:
case WSAEFAULT:
case WSAEINVAL:
case WSAEMFILE:
Korry Douglas
committed
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
/*
* Windows Sockets definitions of regular Berkeley error constants
*/
case WSAEWOULDBLOCK:
case WSAEINPROGRESS:
case WSAEALREADY:
case WSAENOTSOCK:
case WSAEDESTADDRREQ:
case WSAEMSGSIZE:
case WSAEPROTOTYPE:
case WSAENOPROTOOPT:
case WSAEPROTONOSUPPORT:
case WSAESOCKTNOSUPPORT:
case WSAEOPNOTSUPP:
case WSAEPFNOSUPPORT:
case WSAEAFNOSUPPORT:
case WSAEADDRINUSE:
case WSAEADDRNOTAVAIL:
case WSAENOBUFS:
case WSAEISCONN:
case WSAENOTCONN:
case WSAETOOMANYREFS:
case WSAETIMEDOUT:
case WSAELOOP:
case WSAENAMETOOLONG:
case WSAEHOSTUNREACH:
case WSAENOTEMPTY:
case WSAEPROCLIM:
case WSAEUSERS:
case WSAEDQUOT:
case WSAESTALE:
case WSAEREMOTE:
Korry Douglas
committed
/*
* Extended Windows Sockets error constant definitions
*/
case WSASYSNOTREADY:
case WSAVERNOTSUPPORTED:
case WSANOTINITIALISED:
case WSAEDISCON:
case WSAENOMORE:
case WSAECANCELLED:
case WSAEINVALIDPROCTABLE:
case WSAEINVALIDPROVIDER:
case WSAEPROVIDERFAILEDINIT:
case WSASYSCALLFAILURE:
case WSASERVICE_NOT_FOUND:
case WSATYPE_NOT_FOUND:
case WSA_E_NO_MORE:
case WSA_E_CANCELLED:
case WSAEREFUSED:
break;
/*
* Server should shut down its socket on these errors.
Korry Douglas
committed
case WSAENETDOWN:
case WSAENETUNREACH:
case WSAENETRESET:
case WSAECONNABORTED:
case WSAESHUTDOWN:
case WSAEHOSTDOWN:
case WSAECONNREFUSED:
Korry Douglas
committed
break;
Korry Douglas
committed
default:
;
}
Korry Douglas
committed
{
LPVOID lpMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPTSTR) &lpMsgBuf,0, NULL );
elog(COMMERROR,"%s", (char *)lpMsgBuf);
Korry Douglas
committed
LocalFree(lpMsgBuf);
Korry Douglas
committed
siglongjmp(client_lost.m_savepoint, 1);
}
Korry Douglas
committed
err = errno;
Korry Douglas
committed
{
case EINTR:
case ECONNREFUSED:
case EPIPE:
case ENOTCONN:
Korry Douglas
committed
break;
Korry Douglas
committed
case ENOTSOCK:
case EAGAIN:
case EFAULT:
case ENOMEM:
case EINVAL:
Korry Douglas
committed
break;
}
Korry Douglas
committed
{
if(( err ) && ( err != EPIPE ))
elog(COMMERROR, "%s", strerror(err));
siglongjmp(client_lost.m_savepoint, 1);
}
Korry Douglas
committed
errno = err;
#endif
Korry Douglas
committed
}
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
/*
* Returns true if we continue stepping in this frame. False otherwise.
*/
bool
plugin_debugger_main_loop(void)
{
ErrorContextCallback *frame;
debugger_language_t *lang; /* language of the selected frame */
bool need_more = TRUE;
char *command;
bool retval = TRUE;
/* Initially, set focus on the topmost frame in the stack */
for( frame = error_context_stack; frame; frame = frame->previous )
{
/*
* ignore unrecognized stack frames.
*/
lang = language_of_frame(frame);
if (lang)
break;
}
if (frame == NULL)
{
/*
* Oops, couldn't find a frame that we recognize in the stack. This
* shouldn't happen since we're stopped at a breakpoint.
*/
elog(WARNING, "could not find PL/pgSQL frame at the top of the stack");
return false;
}
/* Report the current location */
lang->send_cur_line(frame);
* Loop through the following chunk of code until we get a command
* from the user that would let us execute this PL/pgSQL statement.
*/
while( need_more )
{
/* Wait for a command from the debugger client */