#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include "pg_query.h"
#include "pg_query_internal.h"
#include <miscadmin.h>
#include <mb/pg_wchar.h>
#include <utils/memutils.h>
#include <utils/memdebug.h>
#include <utils/guc_hooks.h>
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#if defined(HAVE_PTHREAD) && (defined(__FreeBSD__) || defined(__NetBSD__))
#include <pthread_np.h>
#endif
#if defined(_WIN32)
#include <windows.h>
#endif
#include <signal.h>
const char* progname = "pg_query";
__thread sig_atomic_t pg_query_initialized = 0;
#ifdef HAVE_PTHREAD
static pthread_key_t pg_query_thread_exit_key;
static void pg_query_thread_exit(void *key);
#endif
static __thread char *pg_query_lowest_stackaddr = NULL;
static char *get_lowest_stackaddr(void);
static void set_max_stack_depth(void);
static void pg_query_enter_stack_depth_check(void);
static void pg_query_exit_stack_depth_check(void);
void pg_query_init(void)
{
if (pg_query_initialized != 0) return;
pg_query_initialized = 1;
MemoryContextInit();
SetDatabaseEncoding(PG_UTF8);
#ifdef HAVE_PTHREAD
pthread_key_create(&pg_query_thread_exit_key, pg_query_thread_exit);
pthread_setspecific(pg_query_thread_exit_key, TopMemoryContext);
#endif
}
void pg_query_free_top_memory_context(MemoryContext context)
{
Assert(MemoryContextIsValid(context));
Assert(TopMemoryContext == CurrentMemoryContext);
MemoryContextDeleteChildren(context);
AllocSetDeleteFreeList();
context->methods->delete_context(context);
VALGRIND_DESTROY_MEMPOOL(context);
free(context);
TopMemoryContext = NULL;
CurrentMemoryContext = NULL;
ErrorContext = NULL;
}
#ifdef HAVE_PTHREAD
static void pg_query_thread_exit(void *key)
{
MemoryContext context = (MemoryContext) key;
pg_query_free_top_memory_context(context);
}
#endif
void pg_query_exit(void)
{
pg_query_free_top_memory_context(TopMemoryContext);
}
MemoryContext pg_query_enter_memory_context()
{
MemoryContext ctx = NULL;
pg_query_init();
pg_query_enter_stack_depth_check();
Assert(CurrentMemoryContext == TopMemoryContext);
ctx = AllocSetContextCreate(TopMemoryContext,
"pg_query",
ALLOCSET_DEFAULT_SIZES);
MemoryContextSwitchTo(ctx);
return ctx;
}
void pg_query_exit_memory_context(MemoryContext ctx)
{
MemoryContextSwitchTo(TopMemoryContext);
MemoryContextDelete(ctx);
ctx = NULL;
pg_query_exit_stack_depth_check();
}
void pg_query_free_error(PgQueryError *error)
{
free(error->message);
free(error->funcname);
free(error->filename);
if (error->context) {
free(error->context);
}
free(error);
}
static char *get_lowest_stackaddr(void)
{
if (pg_query_lowest_stackaddr)
return pg_query_lowest_stackaddr;
#if defined(_WIN32)
{
ULONG_PTR low, high;
GetCurrentThreadStackLimits(&low, &high);
if (low == 0)
return NULL;
pg_query_lowest_stackaddr = (char *) low;
}
#elif defined(HAVE_PTHREAD) && defined(__linux__)
{
pthread_attr_t attr;
void *stack_addr = NULL;
size_t stack_size = 0;
if (pthread_getattr_np(pthread_self(), &attr) != 0)
return NULL;
if (pthread_attr_getstack(&attr, &stack_addr, &stack_size) == 0 && stack_addr != NULL)
pg_query_lowest_stackaddr = (char *) stack_addr;
pthread_attr_destroy(&attr);
}
#elif defined(HAVE_PTHREAD) && defined(__APPLE__)
{
char *stack_high = (char *) pthread_get_stackaddr_np(pthread_self());
size_t stack_size = pthread_get_stacksize_np(pthread_self());
if (stack_size == 0)
return NULL;
pg_query_lowest_stackaddr = stack_high - stack_size;
}
#elif defined(HAVE_PTHREAD) && (defined(__FreeBSD__) || defined(__NetBSD__))
{
pthread_attr_t attr;
void *stack_addr = NULL;
size_t stack_size = 0;
if (pthread_attr_init(&attr) != 0)
return NULL;
if (pthread_attr_get_np(pthread_self(), &attr) == 0 &&
pthread_attr_getstack(&attr, &stack_addr, &stack_size) == 0 &&
stack_addr != NULL)
{
pg_query_lowest_stackaddr = (char *) stack_addr;
}
pthread_attr_destroy(&attr);
}
#endif
return pg_query_lowest_stackaddr;
}
static void set_max_stack_depth(void)
{
char stack_here;
char* lowest_stackaddr = get_lowest_stackaddr();
ssize_t usable_stack_depth;
ssize_t new_limit;
if (lowest_stackaddr == NULL)
return;
usable_stack_depth = (ssize_t) (&stack_here - lowest_stackaddr);
if (usable_stack_depth < 0)
return;
new_limit = (usable_stack_depth - STACK_DEPTH_SLOP) / 1024;
if (new_limit <= 100)
return;
if (new_limit > 2048)
new_limit = 2048;
max_stack_depth = (int) new_limit;
assign_max_stack_depth((int) new_limit, NULL);
}
static void pg_query_enter_stack_depth_check(void)
{
set_stack_base();
set_max_stack_depth();
}
static void pg_query_exit_stack_depth_check(void)
{
restore_stack_base(NULL);
}