From 438a19c55759bd24071f4b5e835909ca304f95a2 Mon Sep 17 00:00:00 2001 From: pmp-p Date: Tue, 11 Mar 2025 15:13:38 +0100 Subject: [PATCH 01/54] REL_17_4_WASM pglite-wasm + buildtools --- .buildconfig | 5 + ci-17_4_WASM.sh | 136 +++ extra/vector.sh | 40 + .../postgresql-debug/configure.diff | 147 +++ .../src-Makefile.shlib.diff | 49 + .../src-backend-commands-async.c.diff | 15 + .../src-bin-initdb-initdb.c.diff | 211 ++++ pglite-REL_17_4_WASM/build.sh | 144 +++ pglite-REL_17_4_WASM/interactive_one_emsdk.c | 621 +++++++++++ pglite-REL_17_4_WASM/interactive_one_wasi.c | 634 +++++++++++ pglite-REL_17_4_WASM/pg_main.c | 704 +++++++++++++ pglite-REL_17_4_WASM/pg_proto.c | 272 +++++ pglite-REL_17_4_WASM/pgl_initdb.c | 58 + pglite-REL_17_4_WASM/pgl_mains.c | 413 ++++++++ pglite-REL_17_4_WASM/pgl_os.h | 61 ++ pglite-REL_17_4_WASM/pgl_sjlj.c | 62 ++ pglite-REL_17_4_WASM/pgl_stubs.h | 139 +++ pglite-REL_17_4_WASM/pgl_tools.h | 72 ++ pglite-REL_17_4_WASM/repl.html | 987 ++++++++++++++++++ portable/Dockerfile | 18 + portable/functions | 32 + portable/libx86_64.tar.bz2 | Bin 0 -> 2434597 bytes portable/pack_extension.py | 142 +++ portable/portable.sh | 483 +++++++++ portable/proot | Bin 0 -> 831608 bytes runtests.sh | 40 + wasm-build.sh | 471 +++++++++ wasm-build/build-pgcore.sh | 425 ++++++++ wasm-build/build-withdocker.sh | 56 + wasm-build/getsyms.py | 170 +++ wasm-build/linkexport.sh | 41 + wasm-build/linkimports.sh | 93 ++ wasm-build/pack_extension.py | 147 +++ wasm-build/reqsym.py | 27 + wasm-build/sdk.sh | 131 +++ 35 files changed, 7046 insertions(+) create mode 100644 .buildconfig create mode 100755 ci-17_4_WASM.sh create mode 100755 extra/vector.sh create mode 100644 patches-REL_17_4_WASM/postgresql-debug/configure.diff create mode 100644 patches-REL_17_4_WASM/postgresql-emscripten/src-Makefile.shlib.diff create mode 100644 patches-REL_17_4_WASM/postgresql-emscripten/src-backend-commands-async.c.diff create mode 100644 patches-REL_17_4_WASM/postgresql-pglite/src-bin-initdb-initdb.c.diff create mode 100755 pglite-REL_17_4_WASM/build.sh create mode 100644 pglite-REL_17_4_WASM/interactive_one_emsdk.c create mode 100644 pglite-REL_17_4_WASM/interactive_one_wasi.c create mode 100644 pglite-REL_17_4_WASM/pg_main.c create mode 100644 pglite-REL_17_4_WASM/pg_proto.c create mode 100644 pglite-REL_17_4_WASM/pgl_initdb.c create mode 100644 pglite-REL_17_4_WASM/pgl_mains.c create mode 100644 pglite-REL_17_4_WASM/pgl_os.h create mode 100644 pglite-REL_17_4_WASM/pgl_sjlj.c create mode 100644 pglite-REL_17_4_WASM/pgl_stubs.h create mode 100644 pglite-REL_17_4_WASM/pgl_tools.h create mode 100644 pglite-REL_17_4_WASM/repl.html create mode 100755 portable/Dockerfile create mode 100644 portable/functions create mode 100644 portable/libx86_64.tar.bz2 create mode 100644 portable/pack_extension.py create mode 100755 portable/portable.sh create mode 100755 portable/proot create mode 100755 runtests.sh create mode 100755 wasm-build.sh create mode 100755 wasm-build/build-pgcore.sh create mode 100755 wasm-build/build-withdocker.sh create mode 100755 wasm-build/getsyms.py create mode 100755 wasm-build/linkexport.sh create mode 100755 wasm-build/linkimports.sh create mode 100644 wasm-build/pack_extension.py create mode 100644 wasm-build/reqsym.py create mode 100755 wasm-build/sdk.sh diff --git a/.buildconfig b/.buildconfig new file mode 100644 index 0000000000000..92dc69652e8a1 --- /dev/null +++ b/.buildconfig @@ -0,0 +1,5 @@ +PG_VERSION=17.4 +PG_BRANCH=REL_17_4_WASM +SDK_VERSION=3.1.74.6bi +WASI_SDK_VERSION=24.0.4 +SDKROOT=/opt/python-wasm-sdk diff --git a/ci-17_4_WASM.sh b/ci-17_4_WASM.sh new file mode 100755 index 0000000000000..e1d25d18833bc --- /dev/null +++ b/ci-17_4_WASM.sh @@ -0,0 +1,136 @@ +#!/bin/bash +export WORKSPACE=$(pwd) +export PG_VERSION=${PG_VERSION:-17.4} +export PG_BRANCH=${PG_BRANCH:-REL_17_4_WASM} +export CONTAINER_PATH=${CONTAINER_PATH:-/tmp/fs} +export DEBUG=${DEBUG:-false} +export USE_ICU=${USE_ICU:-false} + + +PG_DIST_EXT="${WORKSPACE}/postgresql/dist/extensions-emsdk" +PG_DIST_PGLITE="${WORKSPACE}/postgresql/dist/pglite-sandbox" + + +# +#[ -f postgresql-${PG_BRANCH}/configure ] \ +# || git clone --no-tags --depth 1 --single-branch --branch ${PG_BRANCH} https://github.com/pygame-web/postgres postgresql-${PG_BRANCH} +# + +[ -f postgresql-${PG_BRANCH}/configure ] \ + || git clone --no-tags --depth 1 --single-branch --branch ${PG_BRANCH} https://github.com/electric-sql/postgres-pglite postgresql-${PG_BRANCH} + +chmod +x portable/*.sh wasm-build/*.sh +cp -R wasm-build* extra patches-${PG_BRANCH} postgresql-${PG_BRANCH}/ + +if [ -d postgresql-${PG_BRANCH}/pglite-wasm ] +then + echo "using local pglite files" +else + mkdir -p postgresql-${PG_BRANCH}/pglite-wasm + cp -Rv pglite-${PG_BRANCH}/* postgresql-${PG_BRANCH}/pglite-wasm/ +fi + +pushd postgresql-${PG_BRANCH} + + cat > $CONTAINER_PATH/portable.opts < /tmp/sdk/libpglite-emsdk.tar.gz + popd + + pushd pglite + npm install -g pnpm vitest + pnpm install + popd + fi + + + pushd pglite + if pnpm run ts:build + then + pushd packages/pglite + if $CI + then + pnpm vitest tests/basic.test.js || exit 99 + fi + popd + fi + popd + + + if [ -d /srv/www/html/pglite-web ] + then + git restore src/test/Makefile src/test/isolation/Makefile + + echo "# backup pglite workfiles" + [ -d pglite-wasm ] && cp -R pglite-wasm/* ${WORKSPACE}/pglite-${PG_BRANCH}/ + + echo "# use released files for test" + mkdir -p /srv/www/html/pglite-web/examples + cp -r ${WORKSPACE}/pglite/packages/pglite/dist /srv/www/html/pglite-web/ + cp ${WORKSPACE}/pglite/packages/pglite/examples/{styles.css,utils.js} /srv/www/html/pglite-web/examples/ + cp -f ${WORKSPACE}/pglite/packages/pglite/release/* /srv/www/html/pglite-web/ + du -hs /srv/www/html/pglite-web/pglite.* + else + ./runtests.sh + fi + + else + echo failed to build libpgcore static + exit 125 + fi +popd + diff --git a/extra/vector.sh b/extra/vector.sh new file mode 100755 index 0000000000000..3e6d98538cd02 --- /dev/null +++ b/extra/vector.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +mkdir -p build src + +pushd build + # [ -d pgvector ] || git clone --no-tags --depth 1 --single-branch --branch master https://github.com/pgvector/pgvector + + if [ -d vector ] + then + echo using local pgvector + else + [ -f ../src/pgvector.tar.gz ] || wget -c -q https://github.com/pgvector/pgvector/archive/refs/tags/v0.8.0.tar.gz -O../src/pgvector.tar.gz + tar xvfz ../src/pgvector.tar.gz + mv pgvector-?.?.? vector + fi +popd + + + +if which emcc +then + echo -n +else + reset; + . /opt/python-wasm-sdk/wasm32-bi-emscripten-shell.sh + export PGROOT=${PGROOT:-/tmp/pglite} + export PATH=${PGROOT}/bin:$PATH +fi + + +pushd build/vector + # path for wasm-shared already set to (pwd:pg build dir)/bin + # OPTFLAGS="" turns off arch optim (sse/neon). + PG_CONFIG=${PGROOT}/bin/pg_config emmake make OPTFLAGS="" install || exit 33 + + cp sql/vector.sql sql/vector--0.8.0.sql ${PGROOT}/share/postgresql/extension/ + rm ${PGROOT}/share/postgresql/extension/vector--?.?.?--?.?.?.sql ${PGROOT}/share/postgresql/extension/vector.sql +popd + + diff --git a/patches-REL_17_4_WASM/postgresql-debug/configure.diff b/patches-REL_17_4_WASM/postgresql-debug/configure.diff new file mode 100644 index 0000000000000..d2d620958ea73 --- /dev/null +++ b/patches-REL_17_4_WASM/postgresql-debug/configure.diff @@ -0,0 +1,147 @@ +--- REL_17_4/configure ++++ pglite-REL_17_4/configure +@@ -4328,7 +4328,7 @@ + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no +- CFLAGS="-g" ++ CFLAGS="-g2" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + +@@ -4359,7 +4359,7 @@ + + else + ac_c_werror_flag=$ac_save_c_werror_flag +- CFLAGS="-g" ++ CFLAGS="-g2" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + +@@ -4387,13 +4387,13 @@ + CFLAGS=$ac_save_CFLAGS + elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then +- CFLAGS="-g -O2" ++ CFLAGS="-g2 -Os" + else +- CFLAGS="-g" ++ CFLAGS="-g2" + fi + else + if test "$GCC" = yes; then +- CFLAGS="-O2" ++ CFLAGS="-Os" + else + CFLAGS= + fi +@@ -4859,7 +4859,7 @@ + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no +- CXXFLAGS="-g" ++ CXXFLAGS="-g2" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + +@@ -4890,7 +4890,7 @@ + + else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +- CXXFLAGS="-g" ++ CXXFLAGS="-g2" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + +@@ -4918,13 +4918,13 @@ + CXXFLAGS=$ac_save_CXXFLAGS + elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then +- CXXFLAGS="-g -O2" ++ CXXFLAGS="-g2 -Os" + else +- CXXFLAGS="-g" ++ CXXFLAGS="-g2" + fi + else + if test "$GXX" = yes; then +- CXXFLAGS="-O2" ++ CXXFLAGS="-Os" + else + CXXFLAGS= + fi +@@ -5284,11 +5284,11 @@ + elif test "$enable_coverage" = yes; then + : # no optimization by default + elif test "$GCC" = yes; then +- CFLAGS="-O2" ++ CFLAGS="-Os" + else + # if the user selected debug mode, don't use -O + if test "$enable_debug" != yes; then +- CFLAGS="-O" ++ CFLAGS="-Os" + fi + fi + +@@ -5299,11 +5299,11 @@ + elif test "$enable_coverage" = yes; then + : # no optimization by default + elif test "$GCC" = yes; then +- CXXFLAGS="-O2" ++ CXXFLAGS="-Os" + else + # if the user selected debug mode, don't use -O + if test "$enable_debug" != yes; then +- CXXFLAGS="-O" ++ CXXFLAGS="-Os" + fi + fi + +@@ -5316,12 +5316,12 @@ + if test "$ac_env_BITCODE_CFLAGS_set" = set; then + BITCODE_CFLAGS=$ac_env_BITCODE_CFLAGS_value + else +- BITCODE_CFLAGS="-O2 $BITCODE_CFLAGS" ++ BITCODE_CFLAGS="-Os $BITCODE_CFLAGS" + fi + if test "$ac_env_BITCODE_CXXFLAGS_set" = set; then + BITCODE_CXXFLAGS=$ac_env_BITCODE_CXXFLAGS_value + else +- BITCODE_CXXFLAGS="-O2 $BITCODE_CXXFLAGS" ++ BITCODE_CXXFLAGS="-Os $BITCODE_CXXFLAGS" + fi + + # C[XX]FLAGS we determined above will be added back at the end +@@ -7656,11 +7656,11 @@ + + # supply -g if --enable-debug + if test "$enable_debug" = yes && test "$ac_cv_prog_cc_g" = yes; then +- CFLAGS="$CFLAGS -g" ++ CFLAGS="$CFLAGS -g2" + fi + + if test "$enable_debug" = yes && test "$ac_cv_prog_cxx_g" = yes; then +- CXXFLAGS="$CXXFLAGS -g" ++ CXXFLAGS="$CXXFLAGS -g2" + fi + + # enable code coverage if --enable-coverage +@@ -13313,7 +13313,7 @@ + #ifdef __cplusplus + extern "C" + #endif +-char uuid_export (); ++extern char uuid_export (); + int + main () + { +@@ -13336,7 +13336,7 @@ + if test "x$ac_cv_lib_uuid_uuid_export" = xyes; then : + UUID_LIBS="-luuid" + else +- as_fn_error $? "library 'ossp-uuid' or 'uuid' is required for OSSP UUID" "$LINENO" 5 ++ ac_cv_lib_ossp_uuid_uuid_export=yes + fi + + fi diff --git a/patches-REL_17_4_WASM/postgresql-emscripten/src-Makefile.shlib.diff b/patches-REL_17_4_WASM/postgresql-emscripten/src-Makefile.shlib.diff new file mode 100644 index 0000000000000..06697ff6ade24 --- /dev/null +++ b/patches-REL_17_4_WASM/postgresql-emscripten/src-Makefile.shlib.diff @@ -0,0 +1,49 @@ +--- REL_17_4/src/Makefile.shlib ++++ pglite-REL_17_4/src/Makefile.shlib +@@ -224,6 +224,33 @@ + override CXXFLAGS += $(CXXFLAGS_SL_MODULE) + endif + ++ifeq ($(PORTNAME), emscripten) ++ LINK.shared = emsdk-shared ++ ifdef soname ++ # emscripten uses unversioned shared libraries ++ shlib = $(shlib_bare) ++ soname = $(shlib_bare) ++ endif ++ BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ ++ exports_file = $(SHLIB_EXPORTS:%.txt=%.list) ++# ifneq (,$(exports_file)) ++# LINK.shared += -Wl,--version-script=$(exports_file) ++# endif ++endif ++ ++ifeq ($(PORTNAME), wasi) ++ LINK.shared = wasi-shared ++ ifdef soname ++ # emscripten uses unversioned shared libraries ++ shlib = $(shlib_bare) ++ soname = $(shlib_bare) ++ endif ++ BUILD.exports = ( echo '{ global:'; $(AWK) '/^[^\#]/ {printf "%s;\n",$$1}' $<; echo ' local: *; };' ) >$@ ++ exports_file = $(SHLIB_EXPORTS:%.txt=%.list) ++# ifneq (,$(exports_file)) ++# LINK.shared += -Wl,--version-script=$(exports_file) ++# endif ++endif + + ## + ## BUILD +@@ -239,8 +266,11 @@ + endif + + all-static-lib: $(stlib) +- ++ifdef wasi ++all-shared-lib: all-static-lib ++else + all-shared-lib: $(shlib) ++endif + + # In this rule, "touch $@" works around a problem on some platforms wherein + # ar updates the library file's mod time with a value calculated to diff --git a/patches-REL_17_4_WASM/postgresql-emscripten/src-backend-commands-async.c.diff b/patches-REL_17_4_WASM/postgresql-emscripten/src-backend-commands-async.c.diff new file mode 100644 index 0000000000000..17646ac8cf3f2 --- /dev/null +++ b/patches-REL_17_4_WASM/postgresql-emscripten/src-backend-commands-async.c.diff @@ -0,0 +1,15 @@ +--- REL_17_4/src/backend/commands/async.c ++++ pglite-REL_17_4/src/backend/commands/async.c +@@ -1651,8 +1651,12 @@ + * NotifyQueueLock; which is unlikely but certainly possible. So we + * just log a low-level debug message if it happens. + */ ++#if defined(__EMSCRIPTEN__) || defined(__wasi__) ++ HandleNotifyInterrupt(); ++#else + if (SendProcSignal(pid, PROCSIG_NOTIFY_INTERRUPT, procnos[i]) < 0) + elog(DEBUG3, "could not signal backend with PID %d: %m", pid); ++#endif + } + + pfree(pids); diff --git a/patches-REL_17_4_WASM/postgresql-pglite/src-bin-initdb-initdb.c.diff b/patches-REL_17_4_WASM/postgresql-pglite/src-bin-initdb-initdb.c.diff new file mode 100644 index 0000000000000..cbb044acc6b00 --- /dev/null +++ b/patches-REL_17_4_WASM/postgresql-pglite/src-bin-initdb-initdb.c.diff @@ -0,0 +1,211 @@ +--- REL_17_4/src/bin/initdb/initdb.c ++++ pglite-REL_17_4/src/bin/initdb/initdb.c +@@ -171,7 +171,11 @@ + + + /* internal vars */ ++#if !defined(PGL_MAIN) + static const char *progname; ++#else ++# define dynamic_shared_memory_type initdb_dynamic_shared_memory_type ++#endif + static int encodingid; + static char *bki_file; + static char *hba_file; +@@ -811,6 +815,7 @@ + static char * + get_id(void) + { ++#if !defined(__EMSCRIPTEN__) && !defined(__wasi__) + const char *username; + + #ifndef WIN32 +@@ -825,6 +830,9 @@ + username = get_user_name_or_exit(progname); + + return pg_strdup(username); ++#else ++ return pg_strdup(getenv("PGUSER")); ++#endif /* wasm */ + } + + static char * +@@ -1070,6 +1078,9 @@ + static const char * + choose_dsm_implementation(void) + { ++#if defined(__wasi__) || defined(__EMSCRIPTEN__) ++ return "posix"; ++#endif + #if defined(HAVE_SHM_OPEN) && !defined(__sun__) + int ntries = 10; + pg_prng_state prng_state; +@@ -1608,10 +1619,9 @@ + } + + PG_CMD_CLOSE(); +- +- termPQExpBuffer(&cmd); ++ termPQExpBuffer(&cmd); + free(bki_lines); +- ++PDEBUG("# 1624: BOOT pipe complete"); + check_ok(); + } + +@@ -1711,16 +1721,16 @@ + setup_run_file(FILE *cmdfd, const char *filename) + { + char **lines; +- ++int count=0; + lines = readfile(filename); +- + for (char **line = lines; *line != NULL; line++) + { + PG_CMD_PUTS(*line); + free(*line); ++ count ++ ; + } +- + PG_CMD_PUTS("\n\n"); ++fprintf(stderr, "# 1733: --------------------------------- added %s, %d lines\n", filename, count); + + free(lines); + } +@@ -2636,8 +2646,13 @@ + strlcpy(full_path, progname, sizeof(full_path)); + + if (ret == -1) ++#if defined(__EMSCRIPTEN__) || defined(__wasi__) ++ printf("# WARNING: program \"%s\" is needed by %s but was not found in the same directory as \"%s\"\n", ++ "postgres", progname, full_path); ++#else + pg_fatal("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"", + "postgres", progname, full_path); ++#endif // wasm + else + pg_fatal("program \"%s\" was found by \"%s\" but was not the same version as %s", + "postgres", full_path, progname); +@@ -3096,7 +3111,7 @@ + initPQExpBuffer(&cmd); + printfPQExpBuffer(&cmd, "\"%s\" %s %s template1 >%s", + backend_exec, backend_options, extra_options, DEVNULL); +- ++PDEBUG("# 3115: post-bootstrap sql begin"); + PG_CMD_OPEN(cmd.data); + + setup_auth(cmdfd); +@@ -3134,14 +3149,53 @@ + + PG_CMD_CLOSE(); + termPQExpBuffer(&cmd); +- ++PDEBUG("# 3115: post-bootstrap sql end"); + check_ok(); + } + ++/* pglite entry point */ ++#if defined(PGL_INITDB_MAIN) ++extern void MemoryContextInit(void); ++extern volatile char *PREFIX; ++extern volatile char *PGDATA; ++extern char tmpstr[]; ++char * strcat_alloc(const char *head, const char *tail); ++void strconcat(char*p, const char *head, const char *tail); ++ + + int +-main(int argc, char *argv[]) +-{ ++pgl_initdb_main() { ++ char *pwfile = NULL; ++ char *pgdata = NULL; ++ ++ strconcat(tmpstr, "--pwfile=", PREFIX); ++ pwfile = strcat_alloc(tmpstr, "/password"); ++ ++ ++ strconcat(tmpstr, "--pwfile=", PREFIX); ++ pgdata = strcat_alloc("--pgdata=", PGDATA); ++ ++ char *argv[] = { ++ strcat_alloc(PREFIX,"/bin/initdb"), ++ // "--no-clean", ++ "--wal-segsize=1", ++ "-g", ++ "-E", "UTF8", "--locale=C.UTF-8", "--locale-provider=libc", ++// "-E", "UTF8", "--locale", "C.UTF-8", "--locale-provider=builtin", ++// "--locale", "en_US.UTF-8", ++// "--icu-locale=en-US", "--locale-provider=icu", ++ "-U", WASM_USERNAME, pwfile, //"--pwfile=" WASM_PREFIX "/password", ++ pgdata, // "--pgdata=" WASM_PREFIX "/base", ++ NULL ++ }; ++ ++ int argc = sizeof(argv) / sizeof(char*) - 1; ++ ++ ++#else ++int ++main(int argc, char *argv[]) { ++#endif + static struct option long_options[] = { + {"pgdata", required_argument, NULL, 'D'}, + {"encoding", required_argument, NULL, 'E'}, +@@ -3201,9 +3255,15 @@ + * POSIX says we must do this before any other usage of these files. + */ + setvbuf(stdout, NULL, PG_IOLBF, 0); +- ++#if defined(PGL_INITDB_MAIN) ++ progname = get_progname(argv[0]); ++printf("# 3245:" __FILE__ " calling pg_initdb_main for %s\n", progname); ++ MemoryContextInit(); ++ pg_logging_init(progname); ++#else + pg_logging_init(argv[0]); + progname = get_progname(argv[0]); ++#endif + set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("initdb")); + + if (argc > 1) +@@ -3409,7 +3469,7 @@ + if (icu_rules && locale_provider != COLLPROVIDER_ICU) + pg_fatal("%s cannot be specified unless locale provider \"%s\" is chosen", + "--icu-rules", "icu"); +- ++PDEBUG("# 3463:"__FILE__ " TODO: atexit(cleanup_directories_atexit"); + atexit(cleanup_directories_atexit); + + /* If we only need to sync, just do it and exit */ +@@ -3445,9 +3505,9 @@ + get_restricted_token(); + + setup_pgdata(); +- ++puts("# 3493:pgl_initdb_main " __FILE__); + setup_bin_paths(argv[0]); +- ++puts("# 3495:pgl_initdb_main " __FILE__); + effective_user = get_id(); + if (!username) + username = effective_user; +@@ -3479,7 +3539,7 @@ + get_su_pwd(); + + printf("\n"); +- ++puts("# 3527:" __FILE__); + initialize_data_directory(); + + if (do_sync) +@@ -3535,7 +3595,7 @@ + destroyPQExpBuffer(start_db_cmd); + } + +- ++PDEBUG("# 3583"); + success = true; + return 0; + } diff --git a/pglite-REL_17_4_WASM/build.sh b/pglite-REL_17_4_WASM/build.sh new file mode 100755 index 0000000000000..c1176c8e6ba33 --- /dev/null +++ b/pglite-REL_17_4_WASM/build.sh @@ -0,0 +1,144 @@ +#!/bin/bash +echo "pglite/build: begin" + +WORKSPACE=$(pwd) +PGROOT=/tmp/pglite + +PGSRC=${WORKSPACE} +PGBUILD=${WORKSPACE}/build/postgres + +LIBPGCORE=${PGBUILD}/libpgcore.a + +WEBROOT=${PGBUILD}/web + +PGINC="-I/tmp/pglite/include \ + -I${PGSRC}/src/include -I${PGSRC}/src/interfaces/libpq \ + -I${PGBUILD}/src/include" + + +if $WASI +then + echo TODO + + +else + . ${SDKROOT:-/opt/python-wasm-sdk}/wasm32-bi-emscripten-shell.sh + + touch placeholder + + export PGPRELOAD="\ +--preload-file ${PGROOT}/share/postgresql@${PGROOT}/share/postgresql \ +--preload-file ${PGROOT}/lib/postgresql@${PGROOT}/lib/postgresql \ +--preload-file ${PGROOT}/password@${PGROOT}/password \ +--preload-file ${PGROOT}/PGPASSFILE@/home/web_user/.pgpass \ +--preload-file placeholder@${PGROOT}/bin/postgres \ +--preload-file placeholder@${PGROOT}/bin/initdb\ +" + + export CC=$(which emcc) + + + EXPORTED_FUNCTIONS="_main,_use_wire,_ping,_pgl_initdb,_pgl_backend,_pgl_shutdown,_interactive_write,_interactive_read,_interactive_one" + + EXPORTED_RUNTIME_METHODS="MEMFS,IDBFS,FS,FS_mount,FS_syncfs,FS_analyzePath,setValue,getValue,UTF8ToString,stringToNewUTF8,stringToUTF8OnStack" + EXPORTED_RUNTIME_METHODS="MEMFS,IDBFS,FS,setValue,getValue,UTF8ToString,stringToNewUTF8,stringToUTF8OnStack" + + + + if $DEBUG + then + # FULL + LINKER="-sMAIN_MODULE=1 -sEXPORTED_FUNCTIONS=${EXPORTED_FUNCTIONS}" + else + # min + # LINKER="-sMAIN_MODULE=2" + + # tailored + LINKER="-sMAIN_MODULE=2 -sEXPORTED_FUNCTIONS=@exports" +LINKER="-sMAIN_MODULE=1 -sEXPORTED_FUNCTIONS=${EXPORTED_FUNCTIONS}" + fi + + echo " + +________________________________________________________ + +emscripten : $(which emcc ) $(cat ${SDKROOT}/VERSION) +python : $(which python3) $(python3 -V) +wasmtime : $(which wasmtime) + +CC=${CC:-undefined} + +Linking to libpgcore static from $LIBPGCORE + +Folders : + source : $PGSRC + build : $PGBUILD + target : $WEBROOT + + CPOPTS : $COPTS + DEBUG : $DEBUG + LOPTS : $LOPTS + CMA_MB : $CMA_MB + + CC_PGLITE : $CC_PGLITE + + ICU i18n : $USE_ICU + +$PGPRELOAD +________________________________________________________ + + + +" + + rm pglite.* + + mkdir -p $WEBROOT + + if $USE_ICU + then + LINK_ICU="${PREFIX}/lib/libicui18n.a ${PREFIX}/lib/libicuuc.a ${PREFIX}/lib/libicudata.a" + else + LINK_ICU="" + fi + +# ${CC} ${CC_PGLITE} -DPG_INITDB_MAIN \ +# ${PGINC} \ +# -o ${PGBUILD}/initdb.o -c ${PGSRC}/src/bin/initdb/initdb.c + + ${CC} ${CC_PGLITE} ${PGINC} -o ${PGBUILD}/pglite.o -c ${WORKSPACE}/pglite-wasm/pg_main.c \ + -Wno-incompatible-pointer-types-discards-qualifiers + + COPTS="$LOPTS" ${CC} ${CC_PGLITE} -sGLOBAL_BASE=${CMA_MB}MB -o pglite-rawfs.js -ferror-limit=1 \ + -sFORCE_FILESYSTEM=1 $EMCC_NODE \ + -sALLOW_TABLE_GROWTH -sALLOW_MEMORY_GROWTH -sERROR_ON_UNDEFINED_SYMBOLS \ + -sEXPORTED_RUNTIME_METHODS=${EXPORTED_RUNTIME_METHODS} \ + ${PGINC} ${PGBUILD}/pglite.o \ + $LINKER $LIBPGCORE \ + $LINK_ICU \ + -lnodefs.js -lidbfs.js -lxml2 -lz + + + # some content that does not need to ship into .data + for cleanup in snowball_create.sql psqlrc.sample + do + > ${PREFIX}/${cleanup} + done + + + COPTS="$LOPTS" ${CC} ${CC_PGLITE} -sGLOBAL_BASE=${CMA_MB}MB -o pglite.html -ferror-limit=1 --shell-file ${WORKSPACE}/pglite-wasm/repl.html \ + $PGPRELOAD \ + -sFORCE_FILESYSTEM=1 -sNO_EXIT_RUNTIME=1 -sENVIRONMENT=node,web \ + -sMODULARIZE=1 -sEXPORT_ES6=1 -sEXPORT_NAME=Module \ + -sALLOW_TABLE_GROWTH -sALLOW_MEMORY_GROWTH -sERROR_ON_UNDEFINED_SYMBOLS \ + -sEXPORTED_RUNTIME_METHODS=${EXPORTED_RUNTIME_METHODS} \ + ${PGINC} ${PGBUILD}/pglite.o \ + $LINKER $LIBPGCORE \ + $LINK_ICU \ + -lnodefs.js -lidbfs.js -lxml2 -lz + +fi + +du -hs pglite.* + +echo "pglite/build: end" diff --git a/pglite-REL_17_4_WASM/interactive_one_emsdk.c b/pglite-REL_17_4_WASM/interactive_one_emsdk.c new file mode 100644 index 0000000000000..05d74713982ac --- /dev/null +++ b/pglite-REL_17_4_WASM/interactive_one_emsdk.c @@ -0,0 +1,621 @@ +#define PDEBUG(string) puts(string) +#include // access, unlink +volatile sigjmp_buf local_sigjmp_buf; + + +static void pg_prompt() { + fprintf(stdout,"pg> %c\n", 4); +} + +extern void AbortTransaction(void); +extern void CleanupTransaction(void); +extern void ClientAuthentication(Port *port); +extern FILE* SOCKET_FILE; +extern int SOCKET_DATA; + +/* +init sequence +___________________________________ +SubPostmasterMain / (forkexec) + InitPostmasterChild + shm attach + preload + + BackendInitialize(Port *port) -> collect initial packet + + pq_init(); + whereToSendOutput = DestRemote; + status = ProcessStartupPacket(port, false, false); + pq_startmsgread + pq_getbytes from pq_recvbuf + TODO: place PqRecvBuffer (8K) in lower mem for zero copy + + PerformAuthentication + ClientAuthentication(port) + CheckPasswordAuth SYNC!!!! ( sendAuthRequest flush -> recv_password_packet ) + InitShmemAccess/InitProcess/CreateSharedMemoryAndSemaphores + + BackendRun(port) + PostgresMain + + +-> pq_flush() is synchronous + + +buffer sizes: + + https://github.com/postgres/postgres/blob/master/src/backend/libpq/pqcomm.c#L118 + + https://github.com/postgres/postgres/blob/master/src/common/stringinfo.c#L28 + + + +*/ +extern int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done); +extern void pq_recvbuf_fill(FILE* fp, int packetlen); + +#define PG_MAX_AUTH_TOKEN_LENGTH 65535 +static char * +recv_password_packet(Port *port) { + StringInfoData buf; + int mtype; + + pq_startmsgread(); + + /* Expect 'p' message type */ + mtype = pq_getbyte(); + if (mtype != 'p') + { + /* + * If the client just disconnects without offering a password, don't + * make a log entry. This is legal per protocol spec and in fact + * commonly done by psql, so complaining just clutters the log. + */ + if (mtype != EOF) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("expected password response, got message type %d", + mtype))); + return NULL; /* EOF or bad message type */ + } + + initStringInfo(&buf); + if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH)) /* receive password */ + { + /* EOF - pq_getmessage already logged a suitable message */ + pfree(buf.data); + return NULL; + } + + /* + * Apply sanity check: password packet length should agree with length of + * contained string. Note it is safe to use strlen here because + * StringInfo is guaranteed to have an appended '\0'. + */ + if (strlen(buf.data) + 1 != buf.len) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid password packet size"))); + + /* + * Don't allow an empty password. Libpq treats an empty password the same + * as no password at all, and won't even try to authenticate. But other + * clients might, so allowing it would be confusing. + * + * Note that this only catches an empty password sent by the client in + * plaintext. There's also a check in CREATE/ALTER USER that prevents an + * empty string from being stored as a user's password in the first place. + * We rely on that for MD5 and SCRAM authentication, but we still need + * this check here, to prevent an empty password from being used with + * authentication methods that check the password against an external + * system, like PAM, LDAP and RADIUS. + */ + if (buf.len == 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PASSWORD), + errmsg("empty password returned by client"))); + + /* Do not echo password to logs, for security. */ + elog(DEBUG5, "received password packet"); + return buf.data; +} + + +int md5Salt_len = 4; +char md5Salt[4]; + +ClientSocket dummy_sock; + +static void io_init(bool in_auth, bool out_auth) { + ClientAuthInProgress = in_auth; + MyProcPort = pq_init(&dummy_sock); + if (!MyProcPort) { + PDEBUG("# 133: io_init --------- NO CLIENT (oom) ---------"); + abort(); + } + whereToSendOutput = DestRemote; /* now safe to ereport to client */ +#ifdef PG16 + MyProcPort->canAcceptConnections = CAC_OK; +#endif + ClientAuthInProgress = out_auth; + + SOCKET_FILE = NULL; + SOCKET_DATA = 0; + PDEBUG("\n\n\n\n# 141: io_init --------- Ready for CLIENT ---------"); + + +} + + + +EMSCRIPTEN_KEEPALIVE void +interactive_write(int size) { + cma_rsize = size; + cma_wsize = 0; +} + + +volatile int sf_connected = 0; +volatile bool sockfiles = false; + + +EMSCRIPTEN_KEEPALIVE void +interactive_one() { + int firstchar; + int c; /* character read from getc() */ + StringInfoData input_message; + StringInfoData *inBuf; + FILE *stream ; + FILE *fp; + int packetlen; + bool is_socket = false; + bool is_wire = true; + + if (!is_node && is_repl) { + + + if (!MyProcPort) { + io_init(false, false); + } + + // this could be pg_flush in sync mode. + // but really we are writing socket data that was piled up previous frame async. + if (SOCKET_DATA>0) + goto wire_flush; + + + if (!SOCKET_FILE) { + SOCKET_FILE = fopen(PGS_OLOCK, "w") ; + MyProcPort->sock = fileno(SOCKET_FILE); + } + + } // is_node && is_repl + + + doing_extended_query_message = false; + MemoryContextSwitchTo(MessageContext); + MemoryContextResetAndDeleteChildren(MessageContext); + + initStringInfo(&input_message); + + inBuf = &input_message; + + InvalidateCatalogSnapshotConditionally(); + + if (send_ready_for_query) + { + + // puts("postgres.c 4538-4624 TODO"); + if (IsAbortedTransactionBlockState()) + { + puts("@@@@ TODO 231: idle in transaction (aborted)"); + } + else if (IsTransactionOrTransactionBlock()) + { + puts("@@@@ TODO 235: idle in transaction"); + } + else + { + if (notifyInterruptPending) + ProcessNotifyInterrupt(false); + } + send_ready_for_query = false; + } + + +// postgres.c 4627 + DoingCommandRead = true; + + + #define IO ((char *)(1)) +// #define IO cma_port this would be a temp fix for -O0 but less efficient than a + +/* + * in web mode, client call the wire loop itself waiting synchronously for the results + * in repl mode, the wire loop polls a pseudo socket made from incoming and outgoing files. aka "socketfiles" + * always use "socketfiles" when wasi + * + */ + + +#if 0 //!defined(__wasi__) + if (!is_embed || is_repl) { + // do not try to read when lock/buffer file still there + if (!access(PGS_ILOCK, R_OK)) { +#endif + + packetlen = 0; + + fp = fopen(PGS_IN, "r"); + + // read as a socket. + if (fp) { + fseek(fp, 0L, SEEK_END); + packetlen = ftell(fp); + +//printf("# 250 : wire packetlen = %d\n", packetlen); + if (packetlen) { + sockfiles = true; + whereToSendOutput = DestRemote; + resetStringInfo(inBuf); + rewind(fp); + /* peek on first char */ + firstchar = getc(fp); + rewind(fp); +#define SOCKFILE 1 + pq_recvbuf_fill(fp, packetlen); +#if PGDEBUG + rewind(fp); +#endif + + /* is it startup/auth packet ? */ + if (!firstchar || (firstchar==112)) { + /* code is in handshake/auth domain so read whole msg now */ + //pq_recvbuf_fill(fp, packetlen); + + if (!firstchar) { + if (ProcessStartupPacket(MyProcPort, true, true) != STATUS_OK) { + PDEBUG("# 266: ProcessStartupPacket !OK"); + } else { + PDEBUG("# 267: auth request"); + //ClientAuthentication(MyProcPort); + ClientAuthInProgress = true; + md5Salt[0]=0x01; + md5Salt[1]=0x23; + md5Salt[2]=0x45; + md5Salt[3]=0x56; + { + StringInfoData buf; + pq_beginmessage(&buf, 'R'); + pq_sendint32(&buf, (int32) AUTH_REQ_MD5); + if (md5Salt_len > 0) + pq_sendbytes(&buf, md5Salt, md5Salt_len); + pq_endmessage(&buf); + pq_flush(); + } + } + } // handshake + + if (firstchar==112) { + char *passwd = recv_password_packet(MyProcPort); + printf("auth recv password: %s\n", "md5***" ); + ClientAuthInProgress = false; + /* + // TODO: CheckMD5Auth + if (passwd == NULL) + return STATUS_EOF; + if (shadow_pass) + result = md5_crypt_verify(port->user_name, shadow_pass, passwd, md5Salt, md5Salt_len, logdetail); + else + result = STATUS_ERROR; + */ + pfree(passwd); + { + StringInfoData buf; + pq_beginmessage(&buf, 'R'); + pq_sendint32(&buf, (int32) AUTH_REQ_OK); + pq_endmessage(&buf); + } + + BeginReportingGUCOptions(); + pgstat_report_connect(MyDatabaseId); + { + StringInfoData buf; + pq_beginmessage(&buf, 'K'); + pq_sendint32(&buf, (int32) MyProcPid); + pq_sendint32(&buf, (int32) MyCancelKey); + pq_endmessage(&buf); + } + +PDEBUG("# 324 : TODO: set a pg_main started flag"); + sf_connected++; +// CHECK ME see 538 / 563 + send_ready_for_query = true; + } // auth + } else { +#if PGDEBUG + fprintf(stderr, "# 331: CLI[%d] incoming=%d [%d, ", sf_connected, packetlen, firstchar); + for (int i=0;i4) { + fprintf(stderr, "%d, ", b); + } + } + fprintf(stderr, "]\n"); +#endif + } + // when using locks + // ftruncate(filenum(fp), 0); + } +/* FD CLEANUP */ + fclose(fp); + unlink(PGS_IN); + + // Check if auth bypass work with socketfiles + if (packetlen) { + if (!firstchar || (firstchar==112)) { + PDEBUG("# 351: handshake/auth skip"); + goto wire_flush; + } + + /* else it is wire msg */ +#if PGDEBUG +printf("# 353 : node+repl is_wire/is_socket -> true : %c\n", firstchar); + force_echo = true; +#endif + is_socket = true; + is_wire = true; + whereToSendOutput = DestRemote; + + goto incoming; + } // wire msg + + } // fp data read +#if 0 //!defined(__wasi__) + } // ok lck + } // !is_embed || is_repl +#endif + if (cma_rsize) { + PDEBUG("wire message in cma buffer !"); + is_wire = true; + is_socket = false; + sockfiles = false; + whereToSendOutput = DestRemote; + + if (!MyProcPort) { + io_init(true, false); + } + + if (!SOCKET_FILE) { + SOCKET_FILE = fopen(PGS_OLOCK, "w") ; + MyProcPort->sock = fileno(SOCKET_FILE); + } +#if PGDEBUG + printf("# 391: fd %s: %s fd=%d is_embed=%d\n", PGS_OLOCK, IO, MyProcPort->sock, 1); +#endif + goto incoming; + + } + + c = IO[0]; + + +// TODO: use a msg queue length + if (!c) + return; + + is_repl = true; + + if (is_repl) { + whereToSendOutput = DestNone; + is_wire = false; + is_socket = false; + } else { + is_wire = false; + is_socket = false; + whereToSendOutput = DestRemote; + + if (!MyProcPort) { + ClientAuthInProgress = true; + + MyProcPort = pq_init(&dummy_sock); + if (!MyProcPort) { + PDEBUG(" --------- NO CLIENT (oom) ---------"); + abort(); + } +#ifdef PG16 + MyProcPort->canAcceptConnections = CAC_OK; +#endif + ClientAuthInProgress = false; + } + + if (!SOCKET_FILE) { + SOCKET_FILE = fopen(PGS_OLOCK, "w") ; + MyProcPort->sock = fileno(SOCKET_FILE); + } +#if PGDEBUG + printf("# 430: fd %s: %s fd=%d is_embed=%d\n", PGS_OLOCK, IO, MyProcPort->sock, 1); +#endif + + } + + // zero copy buffer ( lower wasm memory segment ) + packetlen = strlen(IO); + if (packetlen<2) { + pg_prompt(); + // always free kernel buffer !!! + IO[0] = 0; + return; + } + + +// buffer query TODO: direct access ? + resetStringInfo(inBuf); + + for (int i=0; idata); + } + + + if (is_wire) { + /* wire on a socket or cma */ + firstchar = SocketBackend(inBuf); + + } else { + /* nowire */ + if (c == EOF && inBuf->len == 0) { + firstchar = EOF; + + } else { + appendStringInfoChar(inBuf, (char) '\0'); + firstchar = 'Q'; + } + + /* stdio node repl */ + if (is_repl) + whereToSendOutput = DestDebug; + } + while (1) { + if (ignore_till_sync && firstchar != EOF) { + puts("@@@@@@@@@@@@@ 573 TODO: postgres.c 4684 : continue"); + } else { + #include "pg_proto.c" + + /* process notifications */ + ProcessClientReadInterrupt(true); + } + if (is_wire && pq_buffer_remaining_data()) { + firstchar = SocketBackend(inBuf); +#if PGDEBUG + printf("583: PIPELINING [%c]!\n", firstchar); +#endif + } else { + break; + } + } + + if (is_wire) { +wire_flush: + if (!ClientAuthInProgress) { + if (send_ready_for_query) { + PDEBUG("# 594: end packet - sending rfq"); + ReadyForQuery(DestRemote); + //done at postgres.c 4623 send_ready_for_query = false; + } else { + PDEBUG("# 598: end packet - with no rfq"); + } + } else { + PDEBUG("# 601: end packet (ClientAuthInProgress - no rfq) "); + } + + if (SOCKET_DATA>0) { + if (sockfiles) { + if (cma_wsize) + puts("ERROR: cma was not flushed before socketfile interface"); + } else { + /* wsize may have increased with previous rfq so assign here */ + cma_wsize = SOCKET_DATA; + } + if (SOCKET_FILE) { + int outb = SOCKET_DATA; + fclose(SOCKET_FILE); + SOCKET_FILE = NULL; + SOCKET_DATA = 0; + if (cma_wsize) + PDEBUG("# 618: cma and sockfile ???"); + if (sockfiles) { +#if PGDEBUG + printf("# 621: client:ready -> read(%d) " PGS_OLOCK "->" PGS_OUT"\n", outb); +#endif + rename(PGS_OLOCK, PGS_OUT); + } + } + + } else { + cma_wsize = 0; + } + } + + // always free kernel buffer !!! + cma_rsize = 0; + IO[0] = 0; + + #undef IO +} + + + diff --git a/pglite-REL_17_4_WASM/interactive_one_wasi.c b/pglite-REL_17_4_WASM/interactive_one_wasi.c new file mode 100644 index 0000000000000..d5b871f22763b --- /dev/null +++ b/pglite-REL_17_4_WASM/interactive_one_wasi.c @@ -0,0 +1,634 @@ +#ifdef PDEBUG +#undef PDEBUG +#endif + +#define PDEBUG(string) puts(string) +#include // access, unlink + +static void pg_prompt() { + fprintf(stdout,"pg> %c\n", 4); +} + +extern void AbortTransaction(void); +extern void CleanupTransaction(void); +extern void ClientAuthentication(Port *port); +extern FILE* SOCKET_FILE; +extern int SOCKET_DATA; + +/* +init sequence +___________________________________ +SubPostmasterMain / (forkexec) + InitPostmasterChild + shm attach + preload + + BackendInitialize(Port *port) -> collect initial packet + + pq_init(); + whereToSendOutput = DestRemote; + status = ProcessStartupPacket(port, false, false); + pq_startmsgread + pq_getbytes from pq_recvbuf + TODO: place PqRecvBuffer (8K) in lower mem for zero copy + + PerformAuthentication + ClientAuthentication(port) + CheckPasswordAuth SYNC!!!! ( sendAuthRequest flush -> recv_password_packet ) + InitShmemAccess/InitProcess/CreateSharedMemoryAndSemaphores + + BackendRun(port) + PostgresMain + + +-> pq_flush() is synchronous + + +buffer sizes: + + https://github.com/postgres/postgres/blob/master/src/backend/libpq/pqcomm.c#L118 + + https://github.com/postgres/postgres/blob/master/src/common/stringinfo.c#L28 + + +*/ + +extern int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done); +extern void pq_recvbuf_fill(FILE* fp, int packetlen); + +#define PG_MAX_AUTH_TOKEN_LENGTH 65535 +static char * +recv_password_packet(Port *port) { + StringInfoData buf; + int mtype; + + pq_startmsgread(); + + /* Expect 'p' message type */ + mtype = pq_getbyte(); + if (mtype != 'p') + { + /* + * If the client just disconnects without offering a password, don't + * make a log entry. This is legal per protocol spec and in fact + * commonly done by psql, so complaining just clutters the log. + */ + if (mtype != EOF) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("expected password response, got message type %d", + mtype))); + return NULL; /* EOF or bad message type */ + } + + initStringInfo(&buf); + if (pq_getmessage(&buf, PG_MAX_AUTH_TOKEN_LENGTH)) /* receive password */ + { + /* EOF - pq_getmessage already logged a suitable message */ + pfree(buf.data); + return NULL; + } + + /* + * Apply sanity check: password packet length should agree with length of + * contained string. Note it is safe to use strlen here because + * StringInfo is guaranteed to have an appended '\0'. + */ + if (strlen(buf.data) + 1 != buf.len) + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid password packet size"))); + + /* + * Don't allow an empty password. Libpq treats an empty password the same + * as no password at all, and won't even try to authenticate. But other + * clients might, so allowing it would be confusing. + * + * Note that this only catches an empty password sent by the client in + * plaintext. There's also a check in CREATE/ALTER USER that prevents an + * empty string from being stored as a user's password in the first place. + * We rely on that for MD5 and SCRAM authentication, but we still need + * this check here, to prevent an empty password from being used with + * authentication methods that check the password against an external + * system, like PAM, LDAP and RADIUS. + */ + if (buf.len == 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PASSWORD), + errmsg("empty password returned by client"))); + + /* Do not echo password to logs, for security. */ + elog(DEBUG5, "received password packet"); + return buf.data; +} + + +int md5Salt_len = 4; +char md5Salt[4]; +ClientSocket dummy_sock; + +static void io_init(bool in_auth, bool out_auth) { + ClientAuthInProgress = in_auth; +#ifdef PG16 + pq_init(); /* initialize libpq to talk to client */ + MyProcPort = (Port *) calloc(1, sizeof(Port)); +#else + MyProcPort = pq_init(&dummy_sock); +#endif + whereToSendOutput = DestRemote; /* now safe to ereport to client */ + + if (!MyProcPort) { + PDEBUG("# 137: io_init --------- NO CLIENT (oom) ---------"); + abort(); + } +#ifdef PG16 + MyProcPort->canAcceptConnections = CAC_OK; +#endif + ClientAuthInProgress = out_auth; + + SOCKET_FILE = NULL; + SOCKET_DATA = 0; + PDEBUG("\n\n\n\n# 147: io_init --------- Ready for CLIENT ---------"); +} + + + +volatile int sf_connected = 0; +volatile bool sockfiles = false; +volatile bool is_wire = true; +extern char * cma_port; + + +__attribute__((export_name("interactive_write"))) // EMSCRIPTEN_KEEPALIVE +void +interactive_write(int size) { + cma_rsize = size; + cma_wsize = 0; +} + +__attribute__((export_name("ping"))) +void +ping(void) { + puts("pong"); +} + +__attribute__((export_name("use_wire"))) +void +use_wire(int state) { + if (state>0) { + force_echo=true; + printf("176: wire mode, repl off, echo %d\n", force_echo); + is_wire = true; + is_repl = false; + } else { + force_echo=true; + printf("181: repl mode, no wire, echo %d\n", force_echo); + is_wire = false; + is_repl = true; + } +} + +__attribute__((export_name("clear_error"))) +void +clear_error() { + error_context_stack = NULL; + HOLD_INTERRUPTS(); + + disable_all_timeouts(false); /* do first to avoid race condition */ + QueryCancelPending = false; + idle_in_transaction_timeout_enabled = false; + idle_session_timeout_enabled = false; + DoingCommandRead = false; + + pq_comm_reset(); + EmitErrorReport(); + debug_query_string = NULL; + + AbortCurrentTransaction(); + + if (am_walsender) + WalSndErrorCleanup(); + + PortalErrorCleanup(); + if (MyReplicationSlot != NULL) + ReplicationSlotRelease(); +#ifdef PG16 + ReplicationSlotCleanup(); +#else + ReplicationSlotCleanup(false); +#endif + + MemoryContextSwitchTo(TopMemoryContext); + FlushErrorState(); + + if (doing_extended_query_message) + ignore_till_sync = true; + + xact_started = false; + + if (pq_is_reading_msg()) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("terminating connection because protocol synchronization was lost"))); + + RESUME_INTERRUPTS(); + + /* + * If we were handling an extended-query-protocol message, skip till next Sync. + * This also causes us not to issue ReadyForQuery (until we get Sync). + */ + + if (!ignore_till_sync) + send_ready_for_query = true; +} + +void +startup_auth() { + /* code is in handshake/auth domain so read whole msg now */ + + if (ProcessStartupPacket(MyProcPort, true, true) != STATUS_OK) { + PDEBUG("# 246: ProcessStartupPacket !OK"); + } else { + PDEBUG("# 248: sending auth request"); + //ClientAuthentication(MyProcPort); +ClientAuthInProgress = true; + md5Salt[0]=0x01; + md5Salt[1]=0x23; + md5Salt[2]=0x45; + md5Salt[3]=0x56; + { + StringInfoData buf; + pq_beginmessage(&buf, 'R'); + pq_sendint32(&buf, (int32) AUTH_REQ_MD5); + if (md5Salt_len > 0) + pq_sendbytes(&buf, md5Salt, md5Salt_len); + pq_endmessage(&buf); + pq_flush(); + } + } +} + + +void +startup_pass(bool check) { + // auth 'p' + if (check) { + char *passwd = recv_password_packet(MyProcPort); + printf("# 223: auth recv password: %s\n", "md5***" ); + /* + // TODO: CheckMD5Auth + if (passwd == NULL) + return STATUS_EOF; + if (shadow_pass) + result = md5_crypt_verify(port->user_name, shadow_pass, passwd, md5Salt, md5Salt_len, logdetail); + else + result = STATUS_ERROR; + */ + pfree(passwd); + } else { + PDEBUG("# 285: auth skip"); + } + ClientAuthInProgress = false; + + { + StringInfoData buf; + pq_beginmessage(&buf, 'R'); + pq_sendint32(&buf, (int32) AUTH_REQ_OK); + pq_endmessage(&buf); + } + + BeginReportingGUCOptions(); + pgstat_report_connect(MyDatabaseId); + { + StringInfoData buf; + pq_beginmessage(&buf, 'K'); + pq_sendint32(&buf, (int32) MyProcPid); + pq_sendint32(&buf, (int32) MyCancelKey); + pq_endmessage(&buf); + } +PDEBUG("# 305: TODO: set a pg_main started flag"); + sf_connected++; + send_ready_for_query = true; + +} + +extern void pg_startcma(); + +EMSCRIPTEN_KEEPALIVE void +interactive_one() { + int peek = -1; /* preview of firstchar with no pos change */ + int firstchar = 0; /* character read from getc() */ + bool pipelining = true; + StringInfoData input_message; + StringInfoData *inBuf; + FILE *stream ; + FILE *fp; + int packetlen; + + bool had_notification = notifyInterruptPending; + bool notified = false; +if (notifyInterruptPending) + PDEBUG("# 327: has notification !"); + + if (!MyProcPort) { + io_init(is_wire, false); + } + + // this could be pg_flush in sync mode. + // but in fact we are writing socket data that was piled up previous frame async. + if (SOCKET_DATA>0) { + puts("331: ERROR flush after frame"); + goto wire_flush; + } + + if (!cma_rsize) { + // prepare reply queue + if (!SOCKET_FILE) { + SOCKET_FILE = fopen(PGS_OLOCK, "w") ; + MyProcPort->sock = fileno(SOCKET_FILE); + } + } + + doing_extended_query_message = false; + MemoryContextSwitchTo(MessageContext); + MemoryContextResetAndDeleteChildren(MessageContext); + + initStringInfo(&input_message); + + inBuf = &input_message; + + InvalidateCatalogSnapshotConditionally(); + + if (send_ready_for_query) { + + if (IsAbortedTransactionBlockState()) { + puts("@@@@ TODO 356: idle in transaction (aborted)"); + } + else if (IsTransactionOrTransactionBlock()) { + puts("@@@@ TODO 359: idle in transaction"); + } else { + if (notifyInterruptPending) { + ProcessNotifyInterrupt(false); +PDEBUG("# 367: notified ?"); + notified = true; + } + } + send_ready_for_query = false; + } + + +// postgres.c 4627 + DoingCommandRead = true; + +#if defined(EMUL_CMA) + #define IO ((char *)(1+(int)cma_port)) // temp fix for -O0 but less efficient than literal + #error "inefficient" +#else + #define IO ((char *)(1)) +#endif + + +/* + * in cma mode (cma_rsize>0), client call the wire loop itself waiting synchronously for the results + * in socketfiles mode, the wire loop polls a pseudo socket made from incoming and outgoing files. + * in repl mode (cma_rsize==0) output is on stdout not cma/socketfiles wire. repl mode is default. + */ + + peek = IO[0]; + packetlen = cma_rsize; + + if (cma_rsize) { + sockfiles = false; + if (!is_repl) { + whereToSendOutput = DestRemote; + if (!is_wire) + PDEBUG("repl message in cma buffer !"); + } else { + if (is_wire) + PDEBUG("wire message in cma buffer for REPL !"); + whereToSendOutput = DestDebug; + } + } else { + fp = fopen(PGS_IN, "r"); + + // read as a socket. + if (fp) { + fseek(fp, 0L, SEEK_END); + packetlen = ftell(fp); + if (packetlen) { + // always. + is_wire = true; + sockfiles = true; + whereToSendOutput = DestRemote; + resetStringInfo(inBuf); + rewind(fp); + /* peek on first char */ + peek = getc(fp); + rewind(fp); + pq_recvbuf_fill(fp, packetlen); +#if PGDEBUG + rewind(fp); +#endif + /* is it startup/auth packet ? */ + if (!peek) { + startup_auth(); + peek = -1; + } + if (peek==112) { + startup_pass(true); + peek = -1; + } + } + + /* FD CLEANUP, all cases */ + fclose(fp); + unlink(PGS_IN); + + if (packetlen) { + // it was startup/auth , write and return fast. + if (peek<0) { + PDEBUG("# 438: handshake/auth/pass skip"); + goto wire_flush; + } + + /* else it was wire msg */ +#if PGDEBUG + printf("# 444: node+repl is_wire -> true : %c\n", peek); + force_echo = true; +#endif + firstchar = peek; + goto incoming; + } // wire msg + + } // fp data read + + // is it REPL in cma ? + if (!peek) + return; + + firstchar = peek ; + + //REPL mode in zero copy buffer ( lowest wasm memory segment ) + packetlen = strlen(IO); + + } // !cma_rsize -> socketfiles -> repl + +#if PGDEBUG + if (packetlen) + IO[packetlen]=0; + printf("# 479: fd=%d is_embed=%d is_repl=%d is_wire=%d fd %s,len=%d peek=%d [%s]\n", MyProcPort->sock, is_embed, is_repl, is_wire, PGS_OLOCK, packetlen, peek, IO); +#endif + + // buffer query TODO: direct access ? + // CMA wire mode. -> packetlen was set to cma_rsize + resetStringInfo(inBuf); + + for (int i=0; i0; + if (!pipelining) { + printf("# 535: end of wire, rfq=%d\n", send_ready_for_query); + } else { + printf("# 537: no end of wire -> pipelining, rfq=%d\n", send_ready_for_query); + } + } else { + /* nowire */ + pipelining = false; + if (firstchar == EOF && inBuf->len == 0) { + firstchar = EOF; + } else { + appendStringInfoChar(inBuf, (char) '\0'); + firstchar = 'Q'; + } + } + + #if PGDEBUG + if (!pipelining) { + printf("# 552: wire=%d 1stchar=%c Q: %s\n", is_wire, firstchar, inBuf->data); + force_echo = false; + } else { + printf("# 555: PIPELINING [%c]!\n", firstchar); + } + #endif + + if (!ignore_till_sync) + send_ready_for_query = true; + + if (ignore_till_sync && firstchar != EOF) { + puts("@@@@@@@@@@@@@ 562 TODO: postgres.c 4684 : continue"); + } else { /* process notifications (ASYNC) */ + if (notifyInterruptPending) { PDEBUG("# 565: @@@ has notification @@@@\n"); + ProcessClientReadInterrupt(true); + } + } + + #include "pg_proto.c" + } + + if (!is_repl) { +wire_flush: + if (!ClientAuthInProgress) { + /* process notifications (SYNC) */ + if (notifyInterruptPending) + ProcessNotifyInterrupt(false); + + if (send_ready_for_query) { + PDEBUG("# 581: end packet - sending rfq"); + ReadyForQuery(DestRemote); + //done at postgres.c 4623 send_ready_for_query = false; + } else { + PDEBUG("# 585: end packet - with no rfq"); + } + } else { + PDEBUG("# 588: end packet (ClientAuthInProgress - no rfq) "); + } + + if (SOCKET_DATA>0) { + if (sockfiles) { + if (cma_wsize) + puts("ERROR: cma was not flushed before socketfile interface"); + } else { + /* wsize may have increased with previous rfq so assign here */ + cma_wsize = SOCKET_DATA; + } + if (SOCKET_FILE) { + int outb = SOCKET_DATA; + fclose(SOCKET_FILE); + SOCKET_FILE = NULL; + SOCKET_DATA = 0; + if (cma_wsize) + PDEBUG("# 605: cma and sockfile ???"); + if (sockfiles) { +#if PGDEBUG + printf("# 608: client:ready -> read(%d) " PGS_OLOCK "->" PGS_OUT"\n", outb); +#endif + rename(PGS_OLOCK, PGS_OUT); + } + } else { +#if PGDEBUG + printf("# 614: out queue : %d flushed\n", cma_wsize); +#endif + SOCKET_DATA = 0; + } + + } else { + cma_wsize = 0; + } + } else { + pg_prompt(); + } + + + + // always free kernel buffer !!! + cma_rsize = 0; + IO[0] = 0; + + #undef IO +} + diff --git a/pglite-REL_17_4_WASM/pg_main.c b/pglite-REL_17_4_WASM/pg_main.c new file mode 100644 index 0000000000000..0c8baec35188a --- /dev/null +++ b/pglite-REL_17_4_WASM/pg_main.c @@ -0,0 +1,704 @@ +#define PGL_MAIN +#define PGL_INITDB_MAIN +// #define PGDEBUG_STARTUP + +// MEMFS files for os pipe simulation +#define IDB_PIPE_BOOT "/tmp/initdb.boot.txt" +#define IDB_PIPE_SINGLE "/tmp/initdb.single.txt" + +#include "pgl_os.h" + +// ----------------------- pglite ---------------------------- +#include "postgres.h" +#include "utils/memutils.h" +#include "utils/pg_locale.h" +#include "tcop/tcopprot.h" + +#include /* chdir */ +#include /* mkdir */ + +// globals + +#define MemoryContextResetAndDeleteChildren(...) +// #define SpinLockInit(...) + + + +int g_argc; +char **g_argv; +extern char ** environ; + +volatile char *PREFIX; +volatile char *PGDATA; +volatile char *PGUSER; + +const char * progname; + +volatile bool is_repl = true; +volatile bool is_node = true; +volatile bool is_embed = false; +volatile int pgl_idb_status; + +// will backend restart after initdb. defaut is yes. +// TODO: log sync start failures and ask to repair/clean up db. +volatile int async_restart = 1; + +#define IDB_OK 0b11111110 +#define IDB_FAILED 0b0001 +#define IDB_CALLED 0b0010 +#define IDB_HASDB 0b0100 +#define IDB_HASUSER 0b1000 + + +#define WASM_PGDATA WASM_PREFIX "/base" +#define CMA_FD 1 + +extern bool IsPostmasterEnvironment; + +#define help(name) + +#define BREAKV(x) { printf("BREAKV : %d\n",__LINE__);return x; } +#define BREAK { printf("BREAK : %d\n",__LINE__);return; } + + +extern int pgl_initdb_main(void); +extern void pg_proc_exit(int code); +extern int BootstrapModeMain(int, char **, int); + + +// PostgresSingleUserMain / PostgresMain + +#include "miscadmin.h" +#include "access/xlog.h" +#include "storage/ipc.h" +#include "storage/proc.h" +#include "utils/timestamp.h" +#include "utils/guc.h" +#include "pgstat.h" +#include "replication/walsender.h" +#include "libpq/pqformat.h" + + + + +volatile bool send_ready_for_query = true; +volatile bool idle_in_transaction_timeout_enabled = false; +volatile bool idle_session_timeout_enabled = false; +/* +bool quote_all_identifiers = false; +FILE* SOCKET_FILE = NULL; +int SOCKET_DATA = 0; +*/ + +void pg_free(void *ptr) { + free(ptr); +} + +#include "../backend/tcop/postgres.c" + + +// initdb + start on fd (pipe emulation) + + +static bool force_echo = false; + + +#include "pgl_mains.c" + +#include "pgl_stubs.h" + +#include "pgl_tools.h" + +#include "pgl_initdb.c" + + +// interactive_one + + +/* TODO : prevent multiple write and write while reading ? */ +volatile int cma_wsize = 0; +volatile int cma_rsize = 0; // defined in postgres.c + + + +__attribute__((export_name("interactive_read"))) +int +interactive_read() { + /* should cma_rsize should be reset here ? */ + return cma_wsize; +} + + +#if 1 // defined(__wasi__) +# include "./interactive_one_wasi.c" +#else +# include "./interactive_one_emsdk.c" +#endif + + + +static void +main_pre(int argc, char *argv[]) { + + + char key[256]; + int i=0; +// extra env is always after normal args + PDEBUG("# ============= extra argv dump =================="); + { + for (;ienv dump =================="); + { + for (;i255) { + puts("buffer overrun on extra env at:"); + puts(kv); + continue; + } + if (kv[sk]=='=') { + memcpy(key, kv, sk); + key[sk] = 0; +#if PGDEBUG + printf("%s='%s'\n", &(key[0]), &(kv[sk+1])); +#endif + setenv(key, &kv[sk+1], 1); + } + } + } + } + + // get default or set default if not set + PREFIX = setdefault("PREFIX", WASM_PREFIX); + argv[0] = strcat_alloc( PREFIX, "/bin/postgres"); + + + +#if defined(__EMSCRIPTEN__) + EM_ASM({ + Module.is_worker = (typeof WorkerGlobalScope !== 'undefined') && self instanceof WorkerGlobalScope; + Module.FD_BUFFER_MAX = $0; + Module.emscripten_copy_to = console.warn; + }, (CMA_MB*1024*1024) / CMA_FD); /* ( global mem start / num fd max ) */ + + if (is_node) { + setenv("ENVIRONMENT", "node" , 1); + EM_ASM({ +#if defined(PGDEBUG_STARTUP) + console.warn("prerun(C-node) worker=", Module.is_worker); +#endif + Module['postMessage'] = function custom_postMessage(event) { + console.log("# pg_main_emsdk.c:544: onCustomMessage:", event); + }; + }); + + } else { + setenv("ENVIRONMENT", "web" , 1); +#if defined(PGDEBUG_STARTUP) + EM_ASM({ + console.warn("prerun(C-web) worker=", Module.is_worker); + }); +#endif + is_repl = true; + } + + EM_ASM({ + if (Module.is_worker) { +#if defined(PGDEBUG_STARTUP) + console.log("Main: running in a worker, setting onCustomMessage"); +#endif + function onCustomMessage(event) { + console.log("onCustomMessage:", event); + }; + Module['onCustomMessage'] = onCustomMessage; + } else { +#if defined(PGDEBUG_STARTUP) + console.log("Running in main thread, faking onCustomMessage"); +#endif + Module['postMessage'] = function custom_postMessage(event) { + switch (event.type) { + case "raw" : { + //stringToUTF8( event.data, shm_rawinput, Module.FD_BUFFER_MAX); + break; + } + + case "stdin" : { + stringToUTF8( event.data, 1, Module.FD_BUFFER_MAX); + break; + } + case "rcon" : { + //stringToUTF8( event.data, shm_rcon, Module.FD_BUFFER_MAX); + break; + } + default : console.warn("custom_postMessage?", event); + } + }; + //if (!window.vm) + // window.vm = Module; + }; + }); + +#endif // __EMSCRIPTEN__ + chdir("/"); + mkdirp("/tmp"); + mkdirp(PREFIX); + + // postgres does not know where to find the server configuration file. + // also we store the fake locale file there. + // postgres.js:1605 You must specify the --config-file or -D invocation option or set the PGDATA environment variable. + + /* enforce ? */ + setenv("PGSYSCONFDIR", PREFIX, 1); + setenv("PGCLIENTENCODING", "UTF8", 1); + + // default is to run a repl loop + setenv("REPL", "Y", 0); +/* + * we cannot run "locale -a" either from web or node. the file getenv("PGSYSCONFDIR") / "locale" + * serves as popen output + */ + + setenv("LC_CTYPE", "en_US.UTF-8" , 1); + + /* defaults */ + + setenv("TZ", "UTC", 0); + setenv("PGTZ", "UTC", 0); + setenv("PGDATABASE", "template1" , 0); + setenv("PG_COLOR", "always", 0); + + + /* defaults with possible user setup */ + PGUSER = setdefault("PGUSER", WASM_USERNAME); + + /* temp override for inidb */ + setenv("PGUSER", WASM_USERNAME, 1); + + strconcat(tmpstr, PREFIX, "/base" ); + PGDATA = setdefault("PGDATA", tmpstr); + + +#if PGDEBUG + puts("# ============= env dump =================="); + for (char **env = environ; *env != 0; env++) { + char *drefp = *env; + printf("# %s\n", drefp); + } + puts("# ========================================="); +#endif +} // main_pre + + + +void main_post() { +PDEBUG("# 306: main_post()"); + /* + * Fire up essential subsystems: error and memory management + * + * Code after this point is allowed to use elog/ereport, though + * localization of messages may not work right away, and messages won't go + * anywhere but stderr until GUC settings get loaded. + */ + MemoryContextInit(); + + /* + * Set up locale information + */ + set_pglocale_pgservice(g_argv[0], PG_TEXTDOMAIN("postgres")); + + /* + * In the postmaster, absorb the environment values for LC_COLLATE and + * LC_CTYPE. Individual backends will change these later to settings + * taken from pg_database, but the postmaster cannot do that. If we leave + * these set to "C" then message localization might not work well in the + * postmaster. + */ + init_locale("LC_COLLATE", LC_COLLATE, ""); + init_locale("LC_CTYPE", LC_CTYPE, ""); + + /* + * LC_MESSAGES will get set later during GUC option processing, but we set + * it here to allow startup error messages to be localized. + */ + #ifdef LC_MESSAGES + init_locale("LC_MESSAGES", LC_MESSAGES, ""); + #endif + + /* + * We keep these set to "C" always, except transiently in pg_locale.c; see + * that file for explanations. + */ + init_locale("LC_MONETARY", LC_MONETARY, "C"); + init_locale("LC_NUMERIC", LC_NUMERIC, "C"); + init_locale("LC_TIME", LC_TIME, "C"); + + /* + * Now that we have absorbed as much as we wish to from the locale + * environment, remove any LC_ALL setting, so that the environment + * variables installed by pg_perm_setlocale have force. + */ + unsetenv("LC_ALL"); +} // main_post + + +__attribute__((export_name("pgl_backend"))) +void pgl_backend() { +#if PGDEBUG + print_bits(sizeof(pgl_idb_status), &pgl_idb_status); +#endif + if (!(pgl_idb_status&IDB_CALLED)) { + puts("# 349: initdb must be called before starting/resuming backend"); + //abort(); + } + + if (async_restart) { +// old 487 + { +#if PGDEBUG + fprintf(stdout, "\n\n\n# 483: restarting in single mode after initdb with user '%s' instead of %s\n", getenv("PGUSER"), PGUSER); + setenv("PGUSER", PGUSER, 1); +#endif + char *single_argv[] = { + WASM_PREFIX "/bin/postgres", + "--single", + "-d", "1", "-B", "16", "-S", "512", "-f", "siobtnmh", + "-D", PGDATA, + "-F", "-O", "-j", + WASM_PGOPTS, + "template1", + NULL + }; + int single_argc = sizeof(single_argv) / sizeof(char*) - 1; + optind = 1; + RePostgresSingleUserMain(single_argc, single_argv, PGUSER); +PDEBUG("# 384: initdb faking shutdown to complete WAL/OID states in single mode"); + } + goto backend_started; + + } + + main_post(); + + char *single_argv[] = { + g_argv[0], + "--single", + "-d", "1", "-B", "16", "-S", "512", "-f", "siobtnmh", + "-D", PGDATA, + "-F", "-O", "-j", + WASM_PGOPTS, + getenv("PGDATABASE"), + NULL + }; + int single_argc = sizeof(single_argv) / sizeof(char*) - 1; + optind = 1; +#if PGDEBUG + fprintf(stdout, "\n\n\n# 405: resuming db with user '%s' instead of %s\n", PGUSER, getenv("PGUSER")); +#endif + setenv("PGUSER", PGUSER, 1); + + AsyncPostgresSingleUserMain(single_argc, single_argv, PGUSER, async_restart); + + +backend_started:; + IsPostmasterEnvironment = true; + if (TransamVariables->nextOid < ((Oid) FirstNormalObjectId)) { + /* IsPostmasterEnvironment is now true + these will be executed when required in varsup.c/GetNewObjectId + TransamVariables->nextOid = FirstNormalObjectId; + TransamVariables->oidCount = 0; + */ +#if PGDEBUG + puts("# 382: initdb done, oid base too low but OID range will be set because IsPostmasterEnvironment"); +#endif + } +} + +#if defined(__EMSCRIPTEN__) +EMSCRIPTEN_KEEPALIVE +#else +__attribute__((export_name("pgl_initdb"))) +#endif +int +pgl_initdb() { + PDEBUG("# 433: pg_initdb()"); + optind = 1; + pgl_idb_status |= IDB_FAILED; + + if (!chdir(PGDATA)){ + if (access("PG_VERSION", F_OK) == 0) { + chdir("/"); + + pgl_idb_status |= IDB_HASDB; + + /* assume auth success for now */ + pgl_idb_status |= IDB_HASUSER; +#if PGDEBUG + fprintf(stdout, "# 446: pg_initdb: db exists at : %s TODO: test for db name : %s \n", PGDATA, getenv("PGDATABASE")); +#endif // PGDEBUG + + async_restart = 0; + goto initdb_done; + } + chdir("/"); +#if PGDEBUG + fprintf(stderr, "# 454: pg_initdb no db found at : %s\n", PGDATA ); +#endif // PGDEBUG + } else { +#if PGDEBUG + fprintf(stderr, "# 458: pg_initdb db folder not found at : %s\n", PGDATA ); +#endif // PGDEBUG + } + + int initdb_rc = pgl_initdb_main(); + +#if PGDEBUG + fprintf(stderr, "\n\n# 465: " __FILE__ "pgl_initdb_main = %d\n", initdb_rc ); +#endif // PGDEBUG + PDEBUG("# 467:" __FILE__); + /* save stdin and use previous initdb output to feed boot mode */ + int saved_stdin = dup(STDIN_FILENO); + { + PDEBUG("# 471: restarting in boot mode for initdb"); + freopen(IDB_PIPE_BOOT, "r", stdin); + + char *boot_argv[] = { + g_argv[0], + "--boot", + "-D", PGDATA, + "-d","3", + WASM_PGOPTS, + "-X", "1048576", + NULL + }; + int boot_argc = sizeof(boot_argv) / sizeof(char*) - 1; + + set_pglocale_pgservice(boot_argv[0], PG_TEXTDOMAIN("initdb")); + + optind = 1; + BootstrapModeMain(boot_argc, boot_argv, false); + fclose(stdin); + remove(IDB_PIPE_BOOT); + stdin = fdopen(saved_stdin, "r"); + + PDEBUG("# 493: initdb faking shutdown to complete WAL/OID states"); + pg_proc_exit(66); + } + + /* use previous initdb output to feed single mode */ + + /* or resume a previous db */ + //IsPostmasterEnvironment = true; + if (TransamVariables->nextOid < ((Oid) FirstNormalObjectId)) { +#if PGDEBUG + puts("# 503: warning oid base too low, will need to set OID range after initdb(bootstrap/single)"); +#endif + } +/* + { +#if PGDEBUG + fprintf(stdout, "\n\n\n# 483: restarting in single mode for initdb with user '%s' instead of %s\n", getenv("PGUSER"), PGUSER); +#endif + char *single_argv[] = { + WASM_PREFIX "/bin/postgres", + "--single", + "-d", "1", "-B", "16", "-S", "512", "-f", "siobtnmh", + "-D", PGDATA, + "-F", "-O", "-j", + WASM_PGOPTS, + "template1", + NULL + }; + int single_argc = sizeof(single_argv) / sizeof(char*) - 1; + optind = 1; + RePostgresSingleUserMain(single_argc, single_argv, WASM_USERNAME); +PDEBUG("# 498: initdb faking shutdown to complete WAL/OID states in single mode"); + async_restart = 1; + } +*/ + async_restart = 1; +initdb_done:; + pgl_idb_status |= IDB_CALLED; + + if (optind>0) { + /* RESET getopt */ + optind = 1; + /* we did not fail, clear the default failed state */ + pgl_idb_status &= IDB_OK; + } else { + PDEBUG("# 511: exiting on initdb-single error"); + // TODO raise js exception + } + return pgl_idb_status; +} // pg_initdb + + + + + +int +main_repl() { + bool hadloop_error = false; + + whereToSendOutput = DestNone; + + if (!mkdir(PGDATA, 0700)) { + /* no db : run initdb now. */ +#if defined(PGDEBUG_STARTUP) + fprintf(stderr, "PGDATA=%s not found, running initdb with default=%s\n",PGDATA, WASM_PGDATA ); +#endif + #if defined(PG_INITDB_MAIN) + #warning "web build" + hadloop_error = pg_initdb() & IDB_FAILED; + #else + #warning "node build" + #if defined(__wasi__) + hadloop_error = pg_initdb() & IDB_FAILED; + #endif + #endif + + } else { + // download a db case ? + mkdirp(WASM_PGDATA); + + // db fixup because empty dirs may not be packaged in git case + mksub_dir(WASM_PGDATA, "/pg_wal"); + mksub_dir(WASM_PGDATA, "/pg_wal/archive_status"); + mksub_dir(WASM_PGDATA, "/pg_wal/summaries"); + + mksub_dir(WASM_PGDATA, "/pg_tblspc"); + mksub_dir(WASM_PGDATA, "/pg_snapshots"); + mksub_dir(WASM_PGDATA, "/pg_commit_ts"); + mksub_dir(WASM_PGDATA, "/pg_notify"); + mksub_dir(WASM_PGDATA, "/pg_replslot"); + mksub_dir(WASM_PGDATA, "/pg_twophase"); + + mksub_dir(WASM_PGDATA, "/pg_logical"); + mksub_dir(WASM_PGDATA, "/pg_logical/snapshots"); + mksub_dir(WASM_PGDATA, "/pg_logical/mappings"); + + } + + if (!hadloop_error) { + main_post(); + + /* + * Catch standard options before doing much else, in particular before we + * insist on not being root. + */ + if (g_argc > 1) { + if (strcmp(g_argv[1], "--help") == 0 || strcmp(g_argv[1], "-?") == 0) + { + help(progname); + exit(0); + } + if (strcmp(g_argv[1], "--version") == 0 || strcmp(g_argv[1], "-V") == 0) + { + fputs(PG_BACKEND_VERSIONSTR, stdout); + exit(0); + } + + } + + if (g_argc > 1 && strcmp(g_argv[1], "--check") == 0) { + BootstrapModeMain(g_argc, g_argv, true); + return 0; + } + + if (g_argc > 1 && strcmp(g_argv[1], "--boot") == 0) { + PDEBUG("# 1410: boot: " __FILE__ ); + BootstrapModeMain(g_argc, g_argv, false); + return 0; + } + + PDEBUG("# 570: single: " __FILE__ ); + AsyncPostgresSingleUserMain(g_argc, g_argv, PGUSER, 0); + } + return 0; +} + + + + + + + +/* + PGDATESTYLE + TZ + PG_SHMEM_ADDR + + PGCTLTIMEOUT + PG_TEST_USE_UNIX_SOCKETS + INITDB_TEMPLATE + PSQL_HISTORY + TMPDIR + PGOPTIONS +*/ + + + + +// ???? __attribute__((export_name("_main"))) +int +main(int argc, char **argv) +{ + int exit_code = 0; + main_pre(argc,argv); +#if PGDEBUG + printf("# 616: argv0 (%s) PGUSER=%s PGDATA=%s\n PGDATABASE=%s REPL=%s\n", + argv[0], PGUSER, PGDATA, getenv("PGDATABASE"), getenv("REPL") ); +#endif + progname = get_progname(argv[0]); + startup_hacks(progname); + g_argv = argv; + g_argc = argc; + + is_repl = strlen(getenv("REPL")) && getenv("REPL")[0]!='N'; + is_embed = true; + + if (!is_repl) { + PDEBUG("# 628: exit with live runtime (nodb)"); + return 0; + } +/* + main_post(); + + PDEBUG("# 634: repl"); + // so it is repl + main_repl(); + + if (is_node) { + PDEBUG("# 639: node repl"); + pg_repl_raf(); + } +*/ + emscripten_force_exit(exit_code); + return exit_code; +} + + + + + + + + + + + + + + + + diff --git a/pglite-REL_17_4_WASM/pg_proto.c b/pglite-REL_17_4_WASM/pg_proto.c new file mode 100644 index 0000000000000..34f4e506f3cc5 --- /dev/null +++ b/pglite-REL_17_4_WASM/pg_proto.c @@ -0,0 +1,272 @@ +/* + * this file is used by both interactive_file ( initdb boot/single ) + * and interactive_one() + * + */ + switch (firstchar) + { + case 'Q': /* simple query */ + { + const char *query_string; + + /* Set statement_timestamp() */ + SetCurrentStatementStartTimestamp(); + + query_string = pq_getmsgstring(&input_message); + pq_getmsgend(&input_message); + + if (am_walsender) + { + if (!exec_replication_command(query_string)) + exec_simple_query(query_string); + } + else + exec_simple_query(query_string); + + send_ready_for_query = true; + } + break; + + case 'P': /* parse */ + { + const char *stmt_name; + const char *query_string; + int numParams; + Oid *paramTypes = NULL; + + forbidden_in_wal_sender(firstchar); + + /* Set statement_timestamp() */ + SetCurrentStatementStartTimestamp(); + + stmt_name = pq_getmsgstring(&input_message); + query_string = pq_getmsgstring(&input_message); + numParams = pq_getmsgint(&input_message, 2); + if (numParams > 0) + { + paramTypes = palloc_array(Oid, numParams); + for (int i = 0; i < numParams; i++) + paramTypes[i] = pq_getmsgint(&input_message, 4); + } + pq_getmsgend(&input_message); + + exec_parse_message(query_string, stmt_name, + paramTypes, numParams); + + //valgrind_report_error_query(query_string); + } + break; + + case 'B': /* bind */ + forbidden_in_wal_sender(firstchar); + + /* Set statement_timestamp() */ + SetCurrentStatementStartTimestamp(); + + /* + * this message is complex enough that it seems best to put + * the field extraction out-of-line + */ + exec_bind_message(&input_message); + + /* exec_bind_message does valgrind_report_error_query */ + break; + + case 'E': /* execute */ + { + const char *portal_name; + int max_rows; + + forbidden_in_wal_sender(firstchar); + + /* Set statement_timestamp() */ + SetCurrentStatementStartTimestamp(); + + portal_name = pq_getmsgstring(&input_message); + max_rows = pq_getmsgint(&input_message, 4); + pq_getmsgend(&input_message); + + exec_execute_message(portal_name, max_rows); + + /* exec_execute_message does valgrind_report_error_query */ + } + break; + + case 'F': /* fastpath function call */ + forbidden_in_wal_sender(firstchar); + + /* Set statement_timestamp() */ + SetCurrentStatementStartTimestamp(); + + /* Report query to various monitoring facilities. */ + pgstat_report_activity(STATE_FASTPATH, NULL); + set_ps_display(""); + + /* start an xact for this function invocation */ + start_xact_command(); + + /* + * Note: we may at this point be inside an aborted + * transaction. We can't throw error for that until we've + * finished reading the function-call message, so + * HandleFunctionRequest() must check for it after doing so. + * Be careful not to do anything that assumes we're inside a + * valid transaction here. + */ + + /* switch back to message context */ + MemoryContextSwitchTo(MessageContext); + + HandleFunctionRequest(&input_message); + + /* commit the function-invocation transaction */ + finish_xact_command(); + + // valgrind_report_error_query("fastpath function call"); + + send_ready_for_query = true; + break; + + case 'C': /* close */ + { + int close_type; + const char *close_target; + + forbidden_in_wal_sender(firstchar); + + close_type = pq_getmsgbyte(&input_message); + close_target = pq_getmsgstring(&input_message); + pq_getmsgend(&input_message); + + switch (close_type) + { + case 'S': + if (close_target[0] != '\0') + DropPreparedStatement(close_target, false); + else + { + /* special-case the unnamed statement */ + drop_unnamed_stmt(); + } + break; + case 'P': + { + Portal portal; + + portal = GetPortalByName(close_target); + if (PortalIsValid(portal)) + PortalDrop(portal, false); + } + break; + default: + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid CLOSE message subtype %d", + close_type))); + break; + } + + if (whereToSendOutput == DestRemote) + pq_putemptymessage('3'); /* CloseComplete */ + + //valgrind_report_error_query("CLOSE message"); + } + break; + + case 'D': /* describe */ + { + int describe_type; + const char *describe_target; + + forbidden_in_wal_sender(firstchar); + + /* Set statement_timestamp() (needed for xact) */ + SetCurrentStatementStartTimestamp(); + + describe_type = pq_getmsgbyte(&input_message); + describe_target = pq_getmsgstring(&input_message); + pq_getmsgend(&input_message); + + switch (describe_type) + { + case 'S': + exec_describe_statement_message(describe_target); + break; + case 'P': + exec_describe_portal_message(describe_target); + break; + default: + ereport(ERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid DESCRIBE message subtype %d", + describe_type))); + break; + } + + // valgrind_report_error_query("DESCRIBE message"); + } + break; + + case 'H': /* flush */ + pq_getmsgend(&input_message); + if (whereToSendOutput == DestRemote) + pq_flush(); + break; + + case 'S': /* sync */ + pq_getmsgend(&input_message); + finish_xact_command(); + //valgrind_report_error_query("SYNC message"); + send_ready_for_query = true; + break; + + /* + * 'X' means that the frontend is closing down the socket. EOF + * means unexpected loss of frontend connection. Either way, + * perform normal shutdown. + */ + case EOF: + + /* for the cumulative statistics system */ + pgStatSessionEndCause = DISCONNECT_CLIENT_EOF; + + /* FALLTHROUGH */ + + case 'X': + + /* + * Reset whereToSendOutput to prevent ereport from attempting + * to send any more messages to client. + */ + if (whereToSendOutput == DestRemote) + whereToSendOutput = DestNone; + + /* + * NOTE: if you are tempted to add more code here, DON'T! + * Whatever you had in mind to do should be set up as an + * on_proc_exit or on_shmem_exit callback, instead. Otherwise + * it will fail to be called during other backend-shutdown + * scenarios. + */ +// puts("# 697:proc_exit/repl/skip"); //proc_exit(0); + is_repl = false; + return; + + case 'd': /* copy data */ + case 'c': /* copy done */ + case 'f': /* copy fail */ + + /* + * Accept but ignore these messages, per protocol spec; we + * probably got here because a COPY failed, and the frontend + * is still sending data. + */ + break; + + default: + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("invalid frontend message type %d", + firstchar))); + } // end switch + diff --git a/pglite-REL_17_4_WASM/pgl_initdb.c b/pglite-REL_17_4_WASM/pgl_initdb.c new file mode 100644 index 0000000000000..3fc19685ca549 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_initdb.c @@ -0,0 +1,58 @@ +#pragma once + +#include // FILE+fprintf +#ifndef PGL_INITDB_MAIN +#define PGL_INITDB_MAIN +#endif + +/* + * and now popen will return predefined slot from a file list + * as file handle in initdb.c + */ + + + +/* + * popen is routed via pg_popen to stderr or a IDB_PIPE_* file + * link a pclose replacement when we are in exec.c ( PG_EXEC defined ) + */ + +extern FILE * pgl_popen(const char *command, const char *type); +#define popen(command, mode) pgl_popen(command, mode) +// #define popen_check(command, mode) pgl_popen(command, mode) + +extern int pgl_pclose(FILE *stream); +#define pclose(stream) pgl_pclose(stream) +#define pclose_check(stream) pgl_pclose(stream) + + +int +pg_chmod(const char * path, int mode_t) { + return 0; +} + +#ifdef FRONTEND +#undef FRONTEND +#endif + +#define FRONTEND +# include "../postgresql/src/common/logging.c" +#undef FRONTEND + + +#include "../postgresql/src/interfaces/libpq/pqexpbuffer.c" + +#define sync_pgdata(...) +#define icu_language_tag(loc_str) icu_language_tag_idb(loc_str) +#define icu_validate_locale(loc_str) icu_validate_locale_idb(loc_str) + + +#include "../postgresql/src/bin/initdb/initdb.c" + +void use_socketfile(void) { + is_repl = true; + is_embed = false; +} + + + diff --git a/pglite-REL_17_4_WASM/pgl_mains.c b/pglite-REL_17_4_WASM/pgl_mains.c new file mode 100644 index 0000000000000..0f8305b48a214 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_mains.c @@ -0,0 +1,413 @@ +#include + +FILE * single_mode_feed = NULL; +volatile bool inloop = false; +volatile sigjmp_buf local_sigjmp_buf; +bool repl = false; + +__attribute__((export_name("pgl_shutdown"))) +void +pg_shutdown() { + PDEBUG("# 11:" __FILE__": pg_shutdown"); + proc_exit(66); +} + + +void +interactive_file() { + int firstchar = 0; + int c = 0; /* character read from getc() */ + StringInfoData input_message; + StringInfoData *inBuf; + FILE *stream ; + + /* + * At top of loop, reset extended-query-message flag, so that any + * errors encountered in "idle" state don't provoke skip. + */ + doing_extended_query_message = false; + + /* + * Release storage left over from prior query cycle, and create a new + * query input buffer in the cleared MessageContext. + */ + MemoryContextSwitchTo(MessageContext); + MemoryContextResetAndDeleteChildren(MessageContext); + + initStringInfo(&input_message); + inBuf = &input_message; + DoingCommandRead = true; + + stream = single_mode_feed; + + while (c!=EOF) { + resetStringInfo(inBuf); + while ((c = getc(stream)) != EOF) { + if (c == '\n') + { + if (UseSemiNewlineNewline) + { + /* + * In -j mode, semicolon followed by two newlines ends the + * command; otherwise treat newline as regular character. + */ + if (inBuf->len > 1 && + inBuf->data[inBuf->len - 1] == '\n' && + inBuf->data[inBuf->len - 2] == ';') + { + /* might as well drop the second newline */ + break; + } + } + else + { + /* + * In plain mode, newline ends the command unless preceded by + * backslash. + */ + if (inBuf->len > 0 && + inBuf->data[inBuf->len - 1] == '\\') + { + /* discard backslash from inBuf */ + inBuf->data[--inBuf->len] = '\0'; + /* discard newline too */ + continue; + } + else + { + /* keep the newline character, but end the command */ + appendStringInfoChar(inBuf, '\n'); + break; + } + } + } + + /* Not newline, or newline treated as regular character */ + appendStringInfoChar(inBuf, (char) c); + } + + + if (c == EOF && inBuf->len == 0) + return; + + /* Add '\0' to make it look the same as message case. */ + appendStringInfoChar(inBuf, (char) '\0'); + firstchar = 'Q'; +PDEBUG(inBuf->data); + +// ??? + if (ignore_till_sync && firstchar != EOF) + continue; + + #include "pg_proto.c" + } +} + +void +RePostgresSingleUserMain(int single_argc, char *single_argv[], const char *username) +{ +#if PGDEBUG +printf("# 123: RePostgresSingleUserMain progname=%s for %s feed=%s\n", progname, single_argv[0], IDB_PIPE_SINGLE); +#endif + single_mode_feed = fopen(IDB_PIPE_SINGLE, "r"); + + // should be template1. + const char *dbname = NULL; + + + /* Parse command-line options. */ + process_postgres_switches(single_argc, single_argv, PGC_POSTMASTER, &dbname); +#if PGDEBUG +printf("# 134: dbname=%s\n", dbname); +#endif + LocalProcessControlFile(false); + + process_shared_preload_libraries(); + +// InitializeMaxBackends(); + +// ? IgnoreSystemIndexes = true; +IgnoreSystemIndexes = false; + process_shmem_requests(); + + InitializeShmemGUCs(); + + InitializeWalConsistencyChecking(); + + PgStartTime = GetCurrentTimestamp(); + + SetProcessingMode(InitProcessing); +PDEBUG("# 153: Re-InitPostgres"); +if (am_walsender) + PDEBUG("# 155: am_walsender == true"); +// BaseInit(); + + InitPostgres(dbname, InvalidOid, /* database to connect to */ + username, InvalidOid, /* role to connect as */ + (!am_walsender) ? INIT_PG_LOAD_SESSION_LIBS : 0, + NULL); /* no out_dbname */ + +PDEBUG("# 164:" __FILE__); + + SetProcessingMode(NormalProcessing); + + BeginReportingGUCOptions(); + + if (IsUnderPostmaster && Log_disconnections) + on_proc_exit(log_disconnections, 0); + + pgstat_report_connect(MyDatabaseId); + + /* Perform initialization specific to a WAL sender process. */ + if (am_walsender) + InitWalSender(); + +#if PGDEBUG + whereToSendOutput = DestDebug; +#endif + + if (whereToSendOutput == DestDebug) + printf("\nPostgreSQL stand-alone backend %s\n", PG_VERSION); + + /* + * Create the memory context we will use in the main loop. + * + * MessageContext is reset once per iteration of the main loop, ie, upon + * completion of processing of each command message from the client. + */ + MessageContext = AllocSetContextCreate(TopMemoryContext, + "MessageContext", + ALLOCSET_DEFAULT_SIZES); + + /* + * Create memory context and buffer used for RowDescription messages. As + * SendRowDescriptionMessage(), via exec_describe_statement_message(), is + * frequently executed for ever single statement, we don't want to + * allocate a separate buffer every time. + */ + row_description_context = AllocSetContextCreate(TopMemoryContext, + "RowDescriptionContext", + ALLOCSET_DEFAULT_SIZES); + MemoryContextSwitchTo(row_description_context); + initStringInfo(&row_description_buf); + MemoryContextSwitchTo(TopMemoryContext); + +#if defined(__wasi__) + puts("# 210: sjlj exception handler off in initdb-wasi"); +#else +# define INITDB_SINGLE +# include "pgl_sjlj.c" +# undef INITDB_SINGLE +#endif // sjlj + + if (!ignore_till_sync) + send_ready_for_query = true; /* initially, or after error */ +/* + if (!inloop) { + inloop = true; + PDEBUG("# 335: REPL(initdb-single):Begin " __FILE__ ); + + while (repl) { interactive_file(); } + } else { + // signal error + optind = -1; + } +*/ + + interactive_file(); + fclose(single_mode_feed); + single_mode_feed = NULL; + +/* + while (repl) { interactive_file(); } + PDEBUG("# 232: REPL:End Raising a 'RuntimeError Exception' to halt program NOW"); + { + void (*npe)() = NULL; + npe(); + } + // unreachable. +*/ + + PDEBUG("# 240: no line-repl requested, exiting and keeping runtime alive"); +} + + + + +void +AsyncPostgresSingleUserMain(int argc, char *argv[], const char *username, int async_restart) +{ + const char *dbname = NULL; +PDEBUG("# 254:"__FILE__); + + /* Initialize startup process environment. */ + InitStandaloneProcess(argv[0]); +PDEBUG("# 254:"__FILE__); + /* Set default values for command-line options. */ + InitializeGUCOptions(); +PDEBUG("# 257:"__FILE__); + /* Parse command-line options. */ + process_postgres_switches(argc, argv, PGC_POSTMASTER, &dbname); +PDEBUG("# 260:"__FILE__); + /* Must have gotten a database name, or have a default (the username) */ + if (dbname == NULL) + { + dbname = username; + if (dbname == NULL) + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("%s: no database nor user name specified", + progname))); + } + +if (async_restart) goto async_db_change; +PDEBUG("# 273:SelectConfigFiles "__FILE__); + /* Acquire configuration parameters */ + if (!SelectConfigFiles(userDoption, progname)) { + proc_exit(1); + } +PDEBUG("# 278:SelectConfigFiles "__FILE__); + checkDataDir(); + ChangeToDataDir(); + + /* + * Create lockfile for data directory. + */ + CreateDataDirLockFile(false); + + /* read control file (error checking and contains config ) */ + LocalProcessControlFile(false); + + /* + * process any libraries that should be preloaded at postmaster start + */ + process_shared_preload_libraries(); + + /* Initialize MaxBackends */ + InitializeMaxBackends(); +PDEBUG("# 127"); /* on_shmem_exit stubs call start here */ + /* + * Give preloaded libraries a chance to request additional shared memory. + */ + process_shmem_requests(); + + /* + * Now that loadable modules have had their chance to request additional + * shared memory, determine the value of any runtime-computed GUCs that + * depend on the amount of shared memory required. + */ + InitializeShmemGUCs(); + + /* + * Now that modules have been loaded, we can process any custom resource + * managers specified in the wal_consistency_checking GUC. + */ + InitializeWalConsistencyChecking(); + + CreateSharedMemoryAndSemaphores(); + + /* + * Remember stand-alone backend startup time,roughly at the same point + * during startup that postmaster does so. + */ + PgStartTime = GetCurrentTimestamp(); + + /* + * Create a per-backend PGPROC struct in shared memory. We must do this + * before we can use LWLocks. + */ + InitProcess(); + +// main + SetProcessingMode(InitProcessing); + + /* Early initialization */ + BaseInit(); +async_db_change:; + +PDEBUG("# 167"); + /* + * General initialization. + * + * NOTE: if you are tempted to add code in this vicinity, consider putting + * it inside InitPostgres() instead. In particular, anything that + * involves database access should be there, not here. + */ + InitPostgres(dbname, InvalidOid, /* database to connect to */ + username, InvalidOid, /* role to connect as */ + (!am_walsender) ? INIT_PG_LOAD_SESSION_LIBS : 0, + NULL); /* no out_dbname */ + + /* + * If the PostmasterContext is still around, recycle the space; we don't + * need it anymore after InitPostgres completes. Note this does not trash + * *MyProcPort, because ConnCreate() allocated that space with malloc() + * ... else we'd need to copy the Port data first. Also, subsidiary data + * such as the username isn't lost either; see ProcessStartupPacket(). + */ + if (PostmasterContext) + { + MemoryContextDelete(PostmasterContext); + PostmasterContext = NULL; + } + + SetProcessingMode(NormalProcessing); + + /* + * Now all GUC states are fully set up. Report them to client if + * appropriate. + */ + BeginReportingGUCOptions(); + + /* + * Also set up handler to log session end; we have to wait till now to be + * sure Log_disconnections has its final value. + */ + if (IsUnderPostmaster && Log_disconnections) + on_proc_exit(log_disconnections, 0); + + pgstat_report_connect(MyDatabaseId); + + /* Perform initialization specific to a WAL sender process. */ + if (am_walsender) + InitWalSender(); + + /* + * Send this backend's cancellation info to the frontend. + */ + if (whereToSendOutput == DestRemote) + { + StringInfoData buf; + + pq_beginmessage(&buf, 'K'); + pq_sendint32(&buf, (int32) MyProcPid); + pq_sendint32(&buf, (int32) MyCancelKey); + pq_endmessage(&buf); + /* Need not flush since ReadyForQuery will do it. */ + } + + /* Welcome banner for standalone case */ + if (whereToSendOutput == DestDebug) + printf("\nPostgreSQL stand-alone backend %s\n", PG_VERSION); + + /* + * Create the memory context we will use in the main loop. + * + * MessageContext is reset once per iteration of the main loop, ie, upon + * completion of processing of each command message from the client. + */ + MessageContext = AllocSetContextCreate(TopMemoryContext, "MessageContext", ALLOCSET_DEFAULT_SIZES); + + /* + * Create memory context and buffer used for RowDescription messages. As + * SendRowDescriptionMessage(), via exec_describe_statement_message(), is + * frequently executed for ever single statement, we don't want to + * allocate a separate buffer every time. + */ + row_description_context = AllocSetContextCreate(TopMemoryContext, "RowDescriptionContext", ALLOCSET_DEFAULT_SIZES); + MemoryContextSwitchTo(row_description_context); + initStringInfo(&row_description_buf); + MemoryContextSwitchTo(TopMemoryContext); +} // AsyncPostgresSingleUserMain + + diff --git a/pglite-REL_17_4_WASM/pgl_os.h b/pglite-REL_17_4_WASM/pgl_os.h new file mode 100644 index 0000000000000..34e77afd7182e --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_os.h @@ -0,0 +1,61 @@ +#pragma once + +// to override chmod() +#include + +extern int pg_chmod(const char * path, int mode_t); +// initdb chmod is not supported by wasi, so just don't use it anywhere +// #if defined(__wasi__) +#define chmod(path, mode) pg_chmod(path, mode) +//#endif + + +#include // FILE + +FILE* IDB_PIPE_FP = NULL; +int IDB_STAGE = 0; + + +/* + * and now popen will return predefined slot from a file list + * as file handle in initdb.c + */ + +FILE *pgl_popen(const char *command, const char *type) { + if (IDB_STAGE>1) { + fprintf(stderr,"# popen[%s]\n", command); + return stderr; + } + + if (!IDB_STAGE) { + fprintf(stderr,"# popen[%s] (BOOT)\n", command); + IDB_PIPE_FP = fopen( IDB_PIPE_BOOT, "w"); + IDB_STAGE = 1; + } else { + fprintf(stderr,"# popen[%s] (SINGLE)\n", command); + IDB_PIPE_FP = fopen( IDB_PIPE_SINGLE, "w"); + IDB_STAGE = 2; + } + + return IDB_PIPE_FP; +} + +#define popen(command, mode) pgl_popen(command, mode) + +int +pgl_pclose(FILE *stream) { + if (IDB_STAGE==1) + fprintf(stderr,"# pg_pclose(%s) 133:" __FILE__ "\n" , IDB_PIPE_BOOT); + if (IDB_STAGE==2) + fprintf(stderr,"# pg_pclose(%s) 135:" __FILE__ "\n" , IDB_PIPE_SINGLE); + + if (IDB_PIPE_FP) { + fflush(IDB_PIPE_FP); + fclose(IDB_PIPE_FP); + IDB_PIPE_FP = NULL; + } + return 0; +} + + + diff --git a/pglite-REL_17_4_WASM/pgl_sjlj.c b/pglite-REL_17_4_WASM/pgl_sjlj.c new file mode 100644 index 0000000000000..e85acda6e8f22 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_sjlj.c @@ -0,0 +1,62 @@ +#if defined(__wasi__) + PDEBUG("# 2:" __FILE__ ": sjlj exception handler off"); +#else + if (sigsetjmp(local_sigjmp_buf, 1) != 0) + { + error_context_stack = NULL; + HOLD_INTERRUPTS(); + + disable_all_timeouts(false); /* do first to avoid race condition */ + QueryCancelPending = false; + idle_in_transaction_timeout_enabled = false; + idle_session_timeout_enabled = false; + DoingCommandRead = false; + + pq_comm_reset(); + EmitErrorReport(); + debug_query_string = NULL; + + AbortCurrentTransaction(); + + if (am_walsender) + WalSndErrorCleanup(); + + PortalErrorCleanup(); + if (MyReplicationSlot != NULL) + ReplicationSlotRelease(); + + ReplicationSlotCleanup(false); + + MemoryContextSwitchTo(TopMemoryContext); + FlushErrorState(); + + if (doing_extended_query_message) + ignore_till_sync = true; + + xact_started = false; + + if (pq_is_reading_msg()) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("terminating connection because protocol synchronization was lost"))); + + RESUME_INTERRUPTS(); +#if !defined(INITDB_SINGLE) + /* + * If we were handling an extended-query-protocol message, skip till next Sync. + * This also causes us not to issue ReadyForQuery (until we get Sync). + */ + + if (!ignore_till_sync) + send_ready_for_query = true; + + if (!is_wire) + pg_prompt(); + + goto wire_flush; +#endif + } + + PG_exception_stack = &local_sigjmp_buf; +#endif + diff --git a/pglite-REL_17_4_WASM/pgl_stubs.h b/pglite-REL_17_4_WASM/pgl_stubs.h new file mode 100644 index 0000000000000..c1812dc1dcfe0 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_stubs.h @@ -0,0 +1,139 @@ +#pragma once + +static void +init_locale(const char *categoryname, int category, const char *locale) +{ + if (pg_perm_setlocale(category, locale) == NULL && + pg_perm_setlocale(category, "C") == NULL) + elog(FATAL, "could not adopt \"%s\" locale nor C locale for %s", + locale, categoryname); +} + + +void +PostgresMain(const char *dbname, const char *username) { + // unused +} + + +void +startup_hacks(const char *progname) { +#ifdef PG16 + SpinLockInit(&dummy_spinlock); +#endif +} + + +void pg_repl_raf() { + puts("pg_repl_raf: STUB"); +} + + + +// embedded initdb requirements + +void +get_restricted_token(void) { + // stub +} + +void * +pg_malloc(size_t size) { + return malloc(size); +} + +void * +pg_malloc_extended(size_t size, int flags) { + return malloc(size); +} + +void * +pg_realloc(void *ptr, size_t size) { + return realloc(ptr, size); +} + +char * +pg_strdup(const char *in) { + char *tmp; + + if (!in) + { + fprintf(stderr, + _("cannot duplicate null pointer (internal error)\n")); + exit(EXIT_FAILURE); + } + tmp = strdup(in); + if (!tmp) + { + fprintf(stderr, _("out of memory\n")); + exit(EXIT_FAILURE); + } + return tmp; +} + + +char * +simple_prompt(const char *prompt, bool echo) { + return pg_strdup(""); +} + + +#ifndef PG16 +int +ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done) { + puts("# 89:"__FILE__" ProcessStartupPacket: STUB"); + return STATUS_OK; +} + +const char * +select_default_timezone(const char *share_path) { + fprintf(stderr, "# 95:" __FILE__ " select_default_timezone(%s): STUB\n", share_path); + return getenv("TZ"); +} + +#include "../src/interfaces/libpq/pqexpbuffer.h" +#include "../src/fe_utils/option_utils.c" + +bool +appendShellStringNoError(PQExpBuffer buf, const char *str) +{ + bool ok = true; + + const char *p; + + if (*str != '\0' && + strspn(str, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_./:") == strlen(str)) + { + appendPQExpBufferStr(buf, str); + return ok; + } + appendPQExpBufferChar(buf, '\''); + for (p = str; *p; p++) + { + if (*p == '\n' || *p == '\r') + { + ok = false; + continue; + } + + if (*p == '\'') + appendPQExpBufferStr(buf, "'\"'\"'"); + else + appendPQExpBufferChar(buf, *p); + } + appendPQExpBufferChar(buf, '\''); + return ok; +} + +void +appendShellString(PQExpBuffer buf, const char *str) +{ + if (!appendShellStringNoError(buf, str)) + { + fprintf(stderr, + _("shell command argument contains a newline or carriage return: \"%s\"\n"), + str); + exit(EXIT_FAILURE); + } +} +#endif diff --git a/pglite-REL_17_4_WASM/pgl_tools.h b/pglite-REL_17_4_WASM/pgl_tools.h new file mode 100644 index 0000000000000..bb531a47c644d --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_tools.h @@ -0,0 +1,72 @@ +#pragma once + + +#define STROPS_BUF 1024 + +char tmpstr[STROPS_BUF]; + +static +void mkdirp(const char *p) { + if (!mkdir(p, 0700)) { + fprintf(stderr, "# no '%s' directory, creating one ...\n", p); + } +} + + +void +strconcat(char*p, const char *head, const char *tail) { + int len; + + len = strnlen(head, STROPS_BUF ); + p = memcpy(p, head, len); + p += len; + + len = strnlen(tail, STROPS_BUF - len); + p = memcpy(p, tail, len); + p += len; + *p = '\0'; + +} + +char * +setdefault(const char* key, const char *value) { + setenv(key, value, 0); + return strdup(getenv(key)); +} + +char * +strcat_alloc(const char *head, const char *tail) { + char buf[STROPS_BUF]; + strconcat( &buf[0], head, tail); + return strdup((const char *)&buf[0]); +} + +void +mksub_dir(const char *dir,const char *sub) { + char buf[STROPS_BUF]; + strconcat(&buf[0], dir, sub); + mkdirp(&buf[0]); +} + + +#if PGDEBUG +static void +print_bits(size_t const size, void const * const ptr) +{ + unsigned char *b = (unsigned char*) ptr; + unsigned char byte; + int i, j; + + for (i = size-1; i >= 0; i--) { + for (j = 7; j >= 0; j--) { + byte = (b[i] >> j) & 1; + printf("%u", byte); + } + } + puts(""); +} +#endif // PGDEBUG + + + + diff --git a/pglite-REL_17_4_WASM/repl.html b/pglite-REL_17_4_WASM/repl.html new file mode 100644 index 0000000000000..6187ef7d30c70 --- /dev/null +++ b/pglite-REL_17_4_WASM/repl.html @@ -0,0 +1,987 @@ + + + + + + + PG SHELL TEST + + + + + + + + + + + + + + + + + +
+
emscripten
+
Downloading...
+
+ +
+ + + +
+ + +
+ +
+ +
+ + + +#if MODULARIZE + + +#endif + + + + + diff --git a/portable/Dockerfile b/portable/Dockerfile new file mode 100755 index 0000000000000..2f70f37e0d75d --- /dev/null +++ b/portable/Dockerfile @@ -0,0 +1,18 @@ +ENV SDKURL https://github.com/pygame-web/portable-sdk/releases/download/3.1.74.7bi/python3.13-wasm-sdk-alpine-3.21.tar.lz4 +ENV SDKROOT /tmp/sdk +ENV PIP_ROOT_USER_ACTION ignore +ENV PIP_NO_CACHE_DIR 1 + + +# nb: the python for build from sdk built with clang directly is used as SYS_PYTHON for emsdk +RUN apk add --no-cache --virtual .build-deps \ + tar file lz4 \ + git patch bison flex \ + findutils binutils coreutils \ + libffi curl perl nodejs \ + make autoconf automake libtool pkgconfig \ + ; + +WORKDIR /workspace + +RUN ./wasm-build.sh contrib extra diff --git a/portable/functions b/portable/functions new file mode 100644 index 0000000000000..49d8fda6e4931 --- /dev/null +++ b/portable/functions @@ -0,0 +1,32 @@ +#!/bin/bash + +FROM () { + echo "FROM $1 AS $2" +} + +ARG () { + echo "ARG $1" +} + +WORKDIR () { + echo "WORKDIR $1" + cd "$1" +} + +COPY () { + echo "COPY $*" +} + +ENV () { + if [[ -v $1 ]] + then + echo "$1 is set" + else + echo "$1 was unset" + export $1=$2 + fi +} + +RUN () { + "$@" +} diff --git a/portable/libx86_64.tar.bz2 b/portable/libx86_64.tar.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..802a4bfcb2322158485175a2001d57732bb00c7e GIT binary patch literal 2434597 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