diff --git a/.buildconfig b/.buildconfig new file mode 100644 index 0000000000000..a353815a4c62b --- /dev/null +++ b/.buildconfig @@ -0,0 +1,13 @@ +PG_VERSION=17.4 +PG_BRANCH=REL_17_4_WASM + +SDK_VERSION=3.1.74.6bi +WASI_SDK_VERSION=25.0.0 +SDKROOT=/tmp/sdk + +PG_DIST=/tmp/sdk/dist +PG_DIST_EXT=/tmp/sdk/dist/extensions-emsdk +CI=true + +GETZIC=false +ZIC=/usr/sbin/zic diff --git a/.gitignore b/.gitignore index 462591005e655..236e2672abb45 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,9 @@ lib*.pc /src/backend/commands/async.c /src/bin/initdb/initdb.c /src/bin/pg_verifybackup/pg_verifybackup.c +/build +/pglite/dist/** +/pglite/patches +/patches/ +/dist/pglite +/dist \ No newline at end of file diff --git a/configure b/configure index 055fd1b825bc7..7b46230fa077e 100755 --- a/configure +++ b/configure @@ -4328,7 +4328,7 @@ else 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 @@ if ac_fn_c_try_compile "$LINENO"; then : 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 @@ if test "$ac_test_CFLAGS" = set; then 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 @@ else 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 @@ if ac_fn_cxx_try_compile "$LINENO"; then : 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 @@ if test "$ac_test_CXXFLAGS" = set; then 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 "${CFLAGS+set}" = set; then 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 "${CXXFLAGS+set}" = set; then 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 @@ fi 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 @@ fi # 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 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext #ifdef __cplusplus extern "C" #endif -char uuid_export (); +extern char uuid_export (); int main () { @@ -13336,7 +13336,7 @@ $as_echo "$ac_cv_lib_uuid_uuid_export" >&6; } 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/extra/pg_ivm.sh b/extra/pg_ivm.sh new file mode 100755 index 0000000000000..df3a6606b0b83 --- /dev/null +++ b/extra/pg_ivm.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +. wasm-build/extension.sh + +pushd $PG_EXTRA + if [ -d pg_ivm ] + then + echo using local pgpg_ivm + else + wget https://github.com/sraoss/pg_ivm/archive/refs/tags/v1.11.tar.gz -O-|tar xfz - + mv pg_ivm-* pg_ivm + +# git clone --recursive --no-tags --depth 1 --single-branch --branch main https://github.com/sraoss/pg_ivm + + if $WASI + then + echo "no patching" + else + echo "PATCH?" + + fi + + fi +popd + +pushd $PG_EXTRA/pg_ivm + # 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 19 + + #cp sql/pg_ivm--1.10.sql ${PGROOT}/share/postgresql/extension/ + #rm -f ${PGROOT}/share/postgresql/extension/pg_ivm--?.?.?--?.?.?.sql ${PGROOT}/share/postgresql/extension/pg_ivm.sql + +popd + + diff --git a/extra/vector.sh b/extra/vector.sh new file mode 100755 index 0000000000000..4dd8666684c09 --- /dev/null +++ b/extra/vector.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +. wasm-build/extension.sh + +pushd $PG_EXTRA + # [ -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 + wget -c -q https://github.com/pgvector/pgvector/archive/refs/tags/v0.8.0.tar.gz -Opgvector.tar.gz + tar xvfz pgvector.tar.gz + mv pgvector-?.?.? vector + fi +popd + +pushd $PG_EXTRA/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 21 + + cp sql/vector--0.8.0.sql ${PGROOT}/share/postgresql/extension/ + rm ${PGROOT}/share/postgresql/extension/vector--?.?.?--?.?.?.sql +popd + + diff --git a/pglite-REL_17_4_WASM/build.sh b/pglite-REL_17_4_WASM/build.sh new file mode 100755 index 0000000000000..51fe1d99b46e8 --- /dev/null +++ b/pglite-REL_17_4_WASM/build.sh @@ -0,0 +1,318 @@ +export WASI=${WASI:-false} + +if ${WASI} +then + BUILD=wasi +else + BUILD=emscripten + WEBROOT=${PG_DIST}/web +fi + +echo "pglite/build-$BUILD: begin target BUILD_PATH=$BUILD_PATH" + +WORKSPACE=$(pwd) +PGROOT=/tmp/pglite + +if [ -d ${WORKSPACE}/src/fe_utils ] +then + PGSRC=${WORKSPACE} +else + PGSRC=${WORKSPACE}/postgresql-${PG_BRANCH} +fi + +LIBPGCORE=${BUILD_PATH}/libpgcore.a + + + +PGINC=" -I${BUILD_PATH}/src/include \ +-I${PGROOT}/include -I${PGROOT}/include/postgresql/server \ +-I${PGSRC}/src/include -I${PGSRC}/src/interfaces/libpq -I${PGSRC}/src" + + +GLOBAL_BASE_B=$(python3 -c "print(${CMA_MB}*1024*1024)") + + +if $WASI +then + + + echo " +_______________________ PG_BRANCH=${PG_BRANCH} _____________________ + +wasi : $(which wasi-c) $(wasi-c -v) +python : $(which python3) $(python3 -V) +wasmtime : $(which wasmtime) + +CC=${CC:-undefined} + +Linking to libpgcore static from $LIBPGCORE + +Folders : + source : $PGSRC + build : $BUILD_PATH + target : $PGROOT + retarget : ${PGL_DIST_C} + native : ${PGL_DIST_NATIVE} build $(arch) : ${NATIVE} + + CPOPTS : $COPTS + DEBUG : $DEBUG + LOPTS : $LOPTS + + CMA_MB : $CMA_MB +GLOBAL_BASE : $GLOBAL_BASE_B + + CC_PGLITE : $CC_PGLITE + + ICU i18n : $USE_ICU + +INCLUDES: $PGINC +________________________________________________________ + + +" + + + if ${CC} -ferror-limit=1 ${CC_PGLITE} \ + ${PGINC} \ + -DPOSTGRES_C=\"../postgresql/src/backend/tcop/postgres.c\" \ + -DPQEXPBUFFER_H=\"../postgresql/src/interfaces/libpq/pqexpbuffer.h\" \ + -DOPTION_UTILS_C=\"../postgresql/src/fe_utils/option_utils.c\" \ + -o ${BUILD_PATH}/pglite.o -c ${WORKSPACE}/pglite-wasm/pg_main.c \ + -Wno-incompatible-pointer-types-discards-qualifiers + then + if ${CC} -fpic -ferror-limit=1 ${CC_PGLITE} ${PGINC} \ + -o ${BUILD_PATH}/sdk_port-wasi.o \ + -c wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c \ + -Wno-incompatible-pointer-types + then + COPTS="$LOPTS" ${CC} ${CC_PGLITE} -ferror-limit=1 -Wl,--global-base=${GLOBAL_BASE_B} -o ${PG_DIST}/pglite.wasi \ + -nostartfiles ${PGINC} ${BUILD_PATH}/pglite.o \ + ${BUILD_PATH}/sdk_port-wasi.o \ + $LINKER $LIBPGCORE \ + $LINK_ICU \ + ${PG_BUILD}/${BUILD}/src/backend/snowball/libdict_snowball.a \ + ${PG_BUILD}/${BUILD}/src/pl/plpgsql/src/libplpgsql.a \ + -lxml2 -lz + else + echo "compilation of libpglite ${BUILD} support failed" + fi + + if [ -f ${PG_DIST}/pglite.wasi ] + then + echo "building minimal wasi FS" + cp ${PG_DIST}/pglite.wasi ${PGROOT}/bin/ + touch ${PGROOT}/bin/initdb ${PGROOT}/bin/postgres + tar -cvJ --files-from=${WORKSPACE}/wasmfs.txt > ${PG_DIST}/pglite-wasi.tar.xz + mkdir -p ${PGL_BUILD_NATIVE} + cat > ${PGL_BUILD_NATIVE}/pglite-native.sh < // access, unlink +#define PGL_LOOP +#if defined(__wasi__) +// volatile sigjmp_buf void*; +#else +volatile sigjmp_buf local_sigjmp_buf; +#endif + +// track back how many ex raised in steps of the loop until sucessfull clear_error +volatile int canary_ex = 0; + +// track back mode used for last reply <0 socketfiles , 0== repl , > 0 cma addr +volatile int channel = 0; + +/* TODO : prevent multiple write and write while reading ? */ +volatile int cma_wsize = 0; +volatile int cma_rsize = 0; // also defined in postgres.c for pqcomm +volatile bool sockfiles = false; // also defined in postgres.c for pqcomm + +__attribute__((export_name("get_buffer_size"))) +int +get_buffer_size(int fd) { + return (CMA_MB * 1024 * 1024) / CMA_FD; +} + +// TODO add query size +__attribute__((export_name("get_buffer_addr"))) +int +get_buffer_addr(int fd) { + return 1 + ( get_buffer_size(fd) *fd); +} + +__attribute__((export_name("get_channel"))) +int +get_channel() { + return channel; +} + + +__attribute__((export_name("interactive_read"))) +int +interactive_read() { + /* should cma_rsize should be reset here ? */ + return cma_wsize; +} + + +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("# 155: 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# 165: io_init --------- Ready for CLIENT ---------"); +} + + +volatile bool is_wire = true; +extern char * cma_port; +extern void pq_startmsgread(void); + +__attribute__((export_name("interactive_write"))) +void +interactive_write(int size) { + cma_rsize = size; + cma_wsize = 0; +} + +__attribute__((export_name("use_wire"))) +void +use_wire(int state) { +#if PGDEBUG + force_echo=true; +#endif + if (state>0) { +#if PGDEBUG + printf("\n\n# 194: PACKET START: wire mode, repl off, echo %d\n", force_echo); +#endif + is_wire = true; + is_repl = false; + } else { +#if PGDEBUG + printf("\n\n# 200: PACKET START: repl mode, no wire, echo %d\n", force_echo); +#endif + 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 discard_input(){ + if (!cma_rsize) + return; + pq_startmsgread(); + for (int i = 0; i < cma_rsize; i++) { + pq_getbyte(); + } + pq_endmsgread(); +} + +void +startup_auth() { + /* code is in handshake/auth domain so read whole msg now */ + send_ready_for_query = false; + + if (ProcessStartupPacket(MyProcPort, true, true) != STATUS_OK) { + PDEBUG("# 271: ProcessStartupPacket !OK"); + } else { + + sf_connected++; + PDEBUG("# 273: sending auth request"); + //ClientAuthentication(MyProcPort); + discard_input(); + +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); + PDEBUG("# 223: auth recv password: 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("# 310: auth skip"); + discard_input(); + } + 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("# 330: TODO: set a pgl started flag"); + send_ready_for_query = true; + ignore_till_sync = false; + volatile int sf_connected = 0; +} + +extern void pg_startcma(); + +__attribute__((export_name("interactive_one"))) 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 = NULL; + int packetlen; + + bool had_notification = notifyInterruptPending; + bool notified = false; + // send_ready_for_query = false; + + if (!MyProcPort) { + PDEBUG("# 353: client created"); + io_init(is_wire, false); + } + +#if PGDEBUG + puts("\n\n# 369: interactive_one"); + if (notifyInterruptPending) + PDEBUG("# 371: has notification !"); +#endif + + // 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("# 361: ERROR flush after frame"); + goto wire_flush; + } + + if (!cma_rsize) { + // no cma : reading from file. writing to file. + if (!SOCKET_FILE) { + SOCKET_FILE = fopen(PGS_OLOCK, "w") ; + MyProcPort->sock = fileno(SOCKET_FILE); + } + } else { + // prepare file reply queue, just in case of cma overflow + // if unused the file will be kept open till next query. + if (!SOCKET_FILE) { + SOCKET_FILE = fopen(PGS_OLOCK, "w") ; + } + } + + MemoryContextSwitchTo(MessageContext); + MemoryContextResetAndDeleteChildren(MessageContext); + + initStringInfo(&input_message); + + inBuf = &input_message; + + InvalidateCatalogSnapshotConditionally(); + + if (send_ready_for_query) { + + if (IsAbortedTransactionBlockState()) { + PDEBUG("@@@@ TODO 403: idle in transaction (aborted)"); + } + else if (IsTransactionOrTransactionBlock()) { + PDEBUG("@@@@ TODO 406: idle in transaction"); + } else { + if (notifyInterruptPending) { + ProcessNotifyInterrupt(false); + notified = true; + } + } + send_ready_for_query = false; + } +// postgres.c 4627 + DoingCommandRead = true; + +#if defined(EMUL_CMA) + // temp fix for -O0 but less efficient than literal + #define IO ((char *)(1+(int)cma_port)) +#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 the simpleset mode where stdin is just copied into input buffer (limited by CMA size). + * TODO: allow to redirect stdout for fully external repl. + */ + + peek = IO[0]; + packetlen = cma_rsize; + + if (cma_rsize) { + sockfiles = false; + if (!is_repl) { + whereToSendOutput = DestRemote; + if (!is_wire) + PDEBUG("# 439: repl message in cma buffer !"); + } else { + if (is_wire) + PDEBUG("# 442: wire message in cma buffer for REPL !"); + whereToSendOutput = DestDebug; + } + } else { + fp = fopen(PGS_IN, "r"); +PDEBUG("# 452:" PGS_IN "\n"); + // read file in socket buffer for SocketBackend to consumme. + if (fp) { + fseek(fp, 0L, SEEK_END); + packetlen = ftell(fp); + if (packetlen) { + // set to always true if no REPL. +// is_wire = true; + resetStringInfo(inBuf); + rewind(fp); + /* peek on first char */ + peek = getc(fp); + rewind(fp); + if (is_repl && !is_wire) { + // sql in buffer + for (int i=0; i true : %c\n", peek); + force_echo = true; + } + +#endif + firstchar = peek; + goto incoming; + } // wire msg +PDEBUG("# 507: NO DATA:" PGS_IN "\n"); + } // fp data read + + // is it REPL in cma ? + if (!peek) + goto return_early; + + 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; // wire blocks are not zero terminated + printf("\n# 524: fd=%d is_embed=%d is_repl=%d is_wire=%d fd %s,len=%d cma=%d peek=%d [%s]\n", MyProcPort->sock, is_embed, is_repl, is_wire, PGS_OLOCK, packetlen,cma_rsize, peek, IO); +#endif + + resetStringInfo(inBuf); + // when cma buffer is used to fake stdin, data is not read by socket/wire backend. + if (is_repl) { + for (int i=0; i0; +#if PGDEBUG + if (!pipelining) { + printf("# 556: end of wire, rfq=%d\n", send_ready_for_query); + } else { + printf("# 558: no end of wire -> pipelining, rfq=%d\n", send_ready_for_query); + } +#endif + } else { + /* nowire */ + // pipelining = false; + if (firstchar == EOF && inBuf->len == 0) { + firstchar = EOF; + } else { + appendStringInfoChar(inBuf, (char) '\0'); + firstchar = 'Q'; + } + } + DoingCommandRead = false; + +#if 0 // PGDEBUG + if (!pipelining) { + printf("# 573: wire=%d 1stchar=%c Q: %s\n", is_wire, firstchar, inBuf->data); + force_echo = false; + } else { + printf("# 576: PIPELINING [%c]!\n", firstchar); + } +#endif + + if (!ignore_till_sync) { + /* initially, or after error */ + // send_ready_for_query = true; + if (notifyInterruptPending) + ProcessClientReadInterrupt(true); + } else { + /* ignoring till sync will skip all pipeline */ + if (firstchar != EOF) { + if (firstchar != 'S') { + continue; + } + } + } + + #include "pg_proto.c" + + if (pipelining) { + pipelining = pq_buffer_remaining_data()>0; + if (pipelining && send_ready_for_query) { +puts("# 631: PIPELINING + rfq"); + ReadyForQuery(whereToSendOutput); + send_ready_for_query = false; + } + } + } + + if (!is_repl) { +wire_flush: + if (!ClientAuthInProgress) { + /* process notifications (SYNC) */ + if (notifyInterruptPending) + ProcessNotifyInterrupt(false); + + if (send_ready_for_query) { + PDEBUG("# 602: end packet - sending rfq\n"); + ReadyForQuery(DestRemote); + //done at postgres.c 4623 + send_ready_for_query = false; + } else { + PDEBUG("# 606: end packet - with no rfq\n"); + } + } else { + PDEBUG("# 609: end packet (ClientAuthInProgress - no rfq)\n"); + } + + if (SOCKET_DATA>0) { + if (sockfiles) { + channel = -1; + 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; + channel = cma_rsize + 2; + } + if (SOCKET_FILE) { + int outb = SOCKET_DATA; + fclose(SOCKET_FILE); + SOCKET_FILE = NULL; + SOCKET_DATA = 0; + + if (cma_wsize) { + PDEBUG("# 672: cma and sockfile ???\n"); + } + + if (sockfiles) { +#if PGDEBUG + printf("# 675: client:ready -> read(%d) " PGS_OLOCK "->" PGS_OUT"\n", outb); +#endif + rename(PGS_OLOCK, PGS_OUT); + } + } else { +#if PGDEBUG + printf("\n# 681: in[%d] out[%d] flushed\n", cma_rsize, cma_wsize); +#endif + SOCKET_DATA = 0; + } + + } else { + cma_wsize = 0; + PDEBUG("# 698: no data, send empty ?"); +// TODO: dedup 739 + if (sockfiles) { + fclose(SOCKET_FILE); + SOCKET_FILE = NULL; + rename(PGS_OLOCK, PGS_OUT); + } + } + } else { + pg_prompt(); +#if PGDEBUG + puts("# 683: repl output"); + if (SOCKET_DATA>0) { + puts("# 686: socket has data"); + if (sockfiles) + printf("# 688: socket file not flushed -> read(%d) " PGS_OLOCK "->" PGS_OUT"\n", SOCKET_DATA); + } else { +// TODO: dedup 723 + if (sockfiles) { + fclose(SOCKET_FILE); + SOCKET_FILE = NULL; + rename(PGS_OLOCK, PGS_OUT); + } + } + if (cma_wsize) + puts("ERROR: cma was not flushed before socketfile interface"); +#endif + } +return_early:; + /* always FD CLEANUP */ + if (fp) { + fclose(fp); + unlink(PGS_IN); + } + + + // always free kernel buffer !!! + cma_rsize = 0; + IO[0] = 0; + + #undef IO + + // reset EX counter + canary_ex = 0; +} + +#undef PGL_LOOP diff --git a/pglite-REL_17_4_WASM/native.sh b/pglite-REL_17_4_WASM/native.sh new file mode 100755 index 0000000000000..6b3777ae22391 --- /dev/null +++ b/pglite-REL_17_4_WASM/native.sh @@ -0,0 +1,189 @@ +#!/bin/bash + +[ -f /alpine ] && /sbin/apk add clang gcc + +mkdir -p ${SDKROOT}/src ${SDKROOT}/native +if [ -d ${SDKROOT}/src/w2c2 ] +then + echo " using local w2c2" +else + pushd ${SDKROOT}/src + git clone https://github.com/pygame-web/w2c2 + cp -R w2c2/w2c2/w2c2_base.h ${SDKROOT}/native/w2c2/ + cp -R w2c2/wasi ${SDKROOT}/native/ + popd +fi + +mv ${PGROOT}/bin/pglite.wasi pglite.wasi +${SDKROOT}/native/w2c2/w2c2 -f 0 -t 1 -p pglite.wasi pglite.c +mv pglite.wasi ${PGL_DIST_NATIVE}/ + +cat > tmp.py <0: + try: + bodies.write( makefunc(l) ) + except Exception as e: + print("?", l, e) + continue + +print("="*70) +with open('${WORKSPACE}/pglite-${PG_BRANCH}/pglite-modpython.c','r') as source: + with open('tmp.c','w') as out: + out.write( source.read().replace('\${WASM2C}','${WASM2C}') ) +END + +python3 tmp.py + +echo " building loader ..." + + +PYLD="-lm" +PYEXT="" + +if echo $CC|grep gcc +then + COPTS="-O0 -g0" + CCOPTS="-Wno-attributes" +else + COPTS="-O0 -g0" + CCOPTS="-fbracket-depth=4096 -Wno-unknown-attributes" +fi + +COMPILE="$CC -fPIC $PYINC $COPTS $CCOPTS -I${SDKROOT}/native -I${SDKROOT}/native/w2c2 -o ${WASM2C}$PYEXT tmp.c ${SDKROOT}/native/wasi/libw2c2wasi.a $PYLD -lc" +echo $COMPILE + +PYVER=$($PYTHON -V|cut -d' ' -f2|cut -d. -f1-2) +PYVER=${PYVER}$(${PYTHON}-config --abiflags) + +PYINC="-D__PYDK__=1 -shared $(${PYTHON}-config --includes)" +PYEXT=$(${PYTHON}-config --extension-suffix) +PYLD="-lpython$PYVER $(${PYTHON}-config --ldflags)" + +echo " +======================================================================== +WASM2C=$WASM2C +COPTS=$COPTS + +PYVER=$PYVER +PYEXT=$PYEXT + +PYINC=$PYINC +PYLD=$PYLD + +PGL_BUILD_NATIVE=${PGL_BUILD_NATIVE} +PGL_DIST_NATIVE=${PGL_DIST_NATIVE} +TARGET: ${WASM2C}$PYEXT +======================================================================== +" + +$COMPILE + + +[ -f ${WASM2C}$PYEXT ] && rm ${WASM2C}$PYEXT + +# -I${SDKROOT}/src/w2c2/w2c2 + +COMPILE="$CC -fPIC -Os -g0 $PYINC $CCOPTS -I${SDKROOT}/native -I${SDKROOT}/native/w2c2 -o ${WASM2C}$PYEXT tmp.c ${SDKROOT}/native/wasi/libw2c2wasi.a $PYLD -lc" +echo $COMPILE + +time $COMPILE + +if [ -f ${WASM2C} ] +then + du -hs ${WASM2C} + # ./${WASM2C} $@ +else + echo build native ${WASM2C} failed +fi + +PY=$(command -v python${PYMAJOR}.${PYMINOR}) + +echo "__________________________________________" +echo $PY +echo "__________________________________________" + + + +if [ -f ${WASM2C}$PYEXT ] +then + env -i $PY <env dump ==================\n"); + { + for (; i < argc; i++) { + const char *kv = argv[i]; + for (int sk = 0; sk < strlen(kv); sk++) { + if (sk > 255) { + 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; + } +// *INDENT-OFF* + 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; + }; + }); +// *INDENT-ON* +#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 now is no repl loop + setenv("REPL", "N", 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("# 280: 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("# 336: initdb must be called before starting/resuming backend"); + //abort(); + } + + if (async_restart) { +// old 487 + +#if PGDEBUG + fprintf(stdout, "\n\n\n\n" + "=========================== BACKEND ====================================\n" + "# 346: FIXME: restarting in single mode after initdb with user '%s' instead of %s\n", PGUSER, getenv("PGUSER")); + // main_post(); +#endif + setenv("PGUSER", PGUSER, 1); + 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); +// AsyncPostgresSingleUserMain(single_argc, single_argv, PGUSER, async_restart); + PDEBUG("# 365: 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# 387: 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("# 403: 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("# 412: 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, "# 427: 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, "# 435: pg_initdb no db found at : %s\n", PGDATA); +#endif // PGDEBUG + } else { +#if PGDEBUG + fprintf(stderr, "# 439: pg_initdb db folder not found at : %s\n", PGDATA); +#endif // PGDEBUG + } + + int initdb_rc = pgl_initdb_main(); + +#if PGDEBUG + fprintf(stderr, "\n\n# 444: " __FILE__ "pgl_initdb_main = %d\n", initdb_rc); +#endif // PGDEBUG + PDEBUG("# 448:" __FILE__); + /* save stdin and use previous initdb output to feed boot mode */ + int saved_stdin = dup(STDIN_FILENO); + { + PDEBUG("# 450: 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); +#if PGDEBUG + puts("BOOT FILE:"); + puts(IDB_PIPE_BOOT); +#else + remove(IDB_PIPE_BOOT); +#endif + stdin = fdopen(saved_stdin, "r"); + + PDEBUG("# 479: 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("# 482: 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("# 524: exiting on initdb-single error"); + // TODO raise js exception + } + return pgl_idb_status; +} // pgl_initdb + + + +/* + 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("# 550: 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("# 562: exit with live runtime (nodb)"); + return 0; + } +#if defined(__wasi__) + + +#else + /* + main_post(); + + PDEBUG("# 565: repl"); + // so it is repl + main_repl(); + + if (is_node) { + PDEBUG("# 570: node repl"); + pg_repl_raf(); + } + */ + emscripten_force_exit(exit_code); +#endif + 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..bc117ab2487ec --- /dev/null +++ b/pglite-REL_17_4_WASM/pg_proto.c @@ -0,0 +1,341 @@ +/* + * this file is used by both interactive_file ( initdb boot/single ) + * and interactive_one() + * + */ + switch (firstchar) + { + case 'Q': /* PqMsg_Query : 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); + + // valgrind_report_error_query(query_string); + + send_ready_for_query = true; + } + break; + + case 'P': /* PqMsg_Parse : exec_parse_message */ + { + 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); + } +#pragma warning "-------------------- TEST ------------------" +#if defined(PGL_LOOP) +send_ready_for_query = false; +#endif +#pragma warning "-------------------- /TEST ------------------" + + break; + + case 'B': /* PqMsg_Bind : exec_bind_message */ + { + 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 */ + } +#pragma warning "-------------------- TEST ------------------" +#if defined(PGL_LOOP) +if (send_ready_for_query) + send_ready_for_query = !pipelining; +#endif +#pragma warning "-------------------- /TEST ------------------" + + break; + + case 'E': /* PqMsg_Execute : execute */ + { + const char *portal_name; + int max_rows; +PDEBUG("# 82: exec_execute_message"); + 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 */ + } +#pragma warning "-------------------- TEST ------------------" +#if defined(PGL_LOOP) +if (send_ready_for_query) + send_ready_for_query = !pipelining; +#endif + +#pragma warning "-------------------- /TEST ------------------" + + break; + + case 'F': /* PqMsg_FunctionCall : 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': /* PqMsg_Close / PqMsg_CommandComplete : 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'); /* PqMsg_CloseComplete : CloseComplete */ + + //valgrind_report_error_query("CLOSE message"); + } +#pragma warning "-------------------- TEST ------------------" +#if defined(PGL_LOOP) +if (send_ready_for_query) + send_ready_for_query = !pipelining; +#endif + +#pragma warning "-------------------- /TEST ------------------" + break; + + case 'D': /* PqMsg_Describe : describe */ + { + int describe_type; + const char *describe_target; +PDEBUG("# 187: exec_describe_ statement/portal"); + 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"); + } +#pragma warning "-------------------- TEST ------------------" +#if defined(PGL_LOOP) +if (send_ready_for_query) + send_ready_for_query = !pipelining; +#endif +#pragma warning "-------------------- /TEST ------------------" + + break; + + case 'H': /* PqMsg_Flush : flush */ + { + pq_getmsgend(&input_message); + if (whereToSendOutput == DestRemote) + pq_flush(); + } + break; + + case 'S': /* PqMsg_Sync : sync */ + { + pq_getmsgend(&input_message); + /* + * If pipelining was used, we may be in an implicit + * transaction block. Close it before calling + * finish_xact_command. + */ +PDEBUG("# 259: EndImplicitTransactionBlock: pending"); + EndImplicitTransactionBlock(); + finish_xact_command(); + //valgrind_report_error_query("SYNC message"); + send_ready_for_query = true; + } +#if defined(PGL_LOOP) +if (notifyInterruptPending) + PDEBUG("# 267: EndImplicitTransactionBlock: NOTIFICATION"); +else + PDEBUG("# 269: EndImplicitTransactionBlock: ok"); +#endif + 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. + */ +if (sf_connected) + sf_connected--; +else + PDEBUG("ERROR: more exits than connections"); +PDEBUG("# 251:proc_exit/skip and repl stop"); //proc_exit(0); + is_repl = false; + ignore_till_sync = false; + send_ready_for_query = false; + +#if defined(PGL_LOOP) + pipelining = false ; + goto wire_flush; // take shortcut +#endif + break; + + 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..19bf7fd405457 --- /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 "common/logging.c" +#undef FRONTEND + + +#include "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 "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..e09f3e79738d0 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_mains.c @@ -0,0 +1,439 @@ +#include + +volatile int sf_connected = 0; +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); +} + +__attribute__((export_name("pgl_closed"))) +int +pgl_closed() { + if (sf_connected>0) + return 1; + return 0; +} + +#if FIXME +extern bool startswith(const char *str, const char *prefix); +#endif + +void +interactive_file() { + int firstchar = 0; + int c = 0; /* character read from getc() */ + StringInfoData input_message; + StringInfoData *inBuf; + FILE *stream ; + int sql_line=1; + bool sql_skip = false; + /* + * 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') + { + sql_line++; + 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'; +#if FIXME +#warning "FIXME: REVOKE ALL ON pg_largeobject FROM PUBLIC;" +#warning "FIXME: REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;" + sql_skip |= startswith(inBuf->data , "REVOKE ALL ON pg_largeobject FROM PUBLIC;"); + sql_skip |= startswith(inBuf->data , "REVOKE CREATE,TEMPORARY ON DATABASE template1 FROM public;"); + if (sql_skip) { + fprintf(stdout, "# 106: SKIPPED: %d: %s\n", sql_line, inBuf->data); + sql_skip = false; + continue; + } else { + // fprintf(stderr, "%d: %s\n", sql_line, inBuf->data); + } +#endif +// ??? + if (ignore_till_sync && firstchar != EOF) + continue; + + #include "pg_proto.c" + } + PDEBUG("# 115: interactive_file: end"); +} + +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("# 240: REPL:End Raising a 'RuntimeError Exception' to halt program NOW"); + { + void (*npe)() = NULL; + npe(); + } + // unreachable. +*/ + + PDEBUG("# 248: 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__); + +// if (!async_restart) /* Initialize startup process environment. */ + InitStandaloneProcess(argv[0]); +PDEBUG("# 254:"__FILE__); +// if (!async_restart) /* Set default values for command-line options. */ + InitializeGUCOptions(); +PDEBUG("# 257:"__FILE__); +// if (!async_restart) /* 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..fa7781f74a3e5 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_sjlj.c @@ -0,0 +1,68 @@ +#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 PGDEBUG + if (is_repl) + pg_prompt(); +#endif + + if (pq_buffer_remaining_data()>0) { + if (canary_ex++ > 8) + abort(); + goto incoming; + } else + 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..2ad262c7f0096 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_stubs.h @@ -0,0 +1,139 @@ +#pragma once + + +// wasi only stubs +#if defined(__wasi__) +# undef PQEXPBUFFER_H +# include "../src/interfaces/libpq/pqexpbuffer.h" + +#else +# include "../src/interfaces/libpq/pqexpbuffer.h" +#endif + + +// option_parse_int parse_sync_method +#include "../src/fe_utils/option_utils.c" + + + +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 +} + + +// 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) { + PDEBUG("# 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"); +} + + + +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..295586f60ef14 --- /dev/null +++ b/pglite-REL_17_4_WASM/pgl_tools.h @@ -0,0 +1,83 @@ +#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); + } +} + +#if FIXME +#warning "some FIXME are used" +bool startswith(const char *str, const char *prefix) { + // Check if the length of prefix is greater than the string + if (strlen(prefix) > strlen(str)) { + return false; + } + // Compare the beginning of the string with the prefix + return strncmp(str, prefix, strlen(prefix)) == 0; +} +#endif + +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/pglite-modpython.c b/pglite-REL_17_4_WASM/pglite-modpython.c new file mode 100644 index 0000000000000..8dc492eab99ad --- /dev/null +++ b/pglite-REL_17_4_WASM/pglite-modpython.c @@ -0,0 +1,218 @@ +#include +#include +#include + +#ifdef __wii__ +# include +# include +# include +#endif + +#include "w2c2/w2c2_base.h" +#include "wasi/wasi.h" +#include "${WASM2C}.h" + +void +trap(Trap trap) { + fprintf(stderr, "TRAP: %s\n", trapDescription(trap)); +#ifdef __wii__ + VIDEO_WaitVSync(); +#endif + abort(); +} + +U32 +wasi_snapshot_preview1__fd_renumber(void* ctx, U32 from, U32 to) { + return -1; +} + +wasmMemory* +wasiMemory(void* instance) { + return ${WASM2C}_memory((${WASM2C}Instance*)instance); +} + +extern char **environ; +volatile int skip_main = 0; +//char** __environ = NULL; +${WASM2C}Instance instance; + +#include "${WASM2C}.c" + +#define WASM_PREFIX "/tmp/pglite" +#define WASM_USERNAME "postgres" +#define PGDB WASM_PREFIX "/base" + + +char **g_argv; +int g_argc; + +char **copy_argv(int argc, char *argv[]) { + // calculate the contiguous argv buffer size + int length=0; + size_t ptr_args = argc + 1; + for (int i = 0; i < argc; i++) { + length += (strlen(argv[i]) + 1); + } + char** new_argv = (char**)malloc((ptr_args) * sizeof(char*) + length); + + // copy argv into the contiguous buffer + length = 0; + for (int i = 0; i < argc; i++) { + new_argv[i] = &(((char*)new_argv)[(ptr_args * sizeof(char*)) + length]); + strcpy(new_argv[i], argv[i]); + length += (strlen(argv[i]) + 1); + } + + // insert NULL terminating ptr at the end of the ptr array + new_argv[ptr_args-1] = NULL; + return (new_argv); +} + +int pre_main(int tmp_argc, char* tmp_argv[]) { + puts("# 84: pre_main"); + // __environ = environ; + + setenv("EMBED", "wasi", 1); + setenv("REPL", "N", 1); + + setenv("PGSYSCONFDIR", WASM_PREFIX, 1); + setenv("PGCLIENTENCODING", "UTF8", 1); + + setenv("TZ", "UTC", 1); + setenv("PGTZ", "UTC", 1); + setenv("PGUSER", WASM_USERNAME , 1); + setenv("PGDATA", PGDB , 1); + setenv("PGDATABASE", "template1" , 1); + setenv("PG_COLOR", "always", 0); + puts("# 87: global argc/argv"); + g_argv = copy_argv(tmp_argc, tmp_argv); + g_argc = sizeof(g_argv) / sizeof(char*) - 1; + + +# if defined(__MWERKS__) && defined(macintosh) + MaxApplZone(); + MoreMasters(); + MoreMasters(); + argc = ccommand(&argv); +# elif defined(__wii__) + /* TODO: get interactive console working */ + wiiInitVideo(); + fatInitDefault(); +# endif + + /* Initialize WASI */ + if (!wasiInit(g_argc, g_argv, environ)) { + fprintf(stderr, "failed to init WASI\n"); + return 1; + } + + if (!wasiFileDescriptorAdd(-1, "/", NULL)) { + fprintf(stderr, "failed to add preopen\n"); + return 1; + } + +# ifdef __MSL__ + SIOUXSetTitle("\p${WASM2C}"); +# endif + + ${WASM2C}Instantiate(&instance, NULL); + + return 0; +} + +// #define REACTOR + +#if defined(REACTOR) + #define STARTPROC(i) ${WASM2C}_setup(i) + // #define STARTPROC(i) ${WASM2C}_pg_initdb(i) +#else + #define STARTPROC(i) ${WASM2C}__start(&instance); +#endif + + +int main(int argc, char* argv[]); + +void do_main() { + puts("# 128: do_main Begin"); + STARTPROC(&instance); + puts("# 134: do_main End"); +} + +#if defined(__PYDK__) +# include "Python.h" + + static PyObject * ${WASM2C}_info(PyObject *self, PyObject *args, PyObject *kwds) + { + puts("${WASM2C} test function : return 42"); + return Py_BuildValue("i", 42); + } + + static PyObject * Begin(PyObject *self, PyObject *args, PyObject *kwds) { + puts("PyInit_${WASM2C}"); + + char *tmp_argv[] = { "/tmp/pglite/bin/postgres", "--single", "template1", NULL}; + int tmp_argc = sizeof(tmp_argv) / sizeof(char*) - 1; +puts("167"); + pre_main(tmp_argc, tmp_argv); +puts("169"); + +//${WASM2C}__start(&instance); + Py_RETURN_NONE; + } + + static PyObject * End(PyObject *self, PyObject *args, PyObject *kwds) { +puts("164: do main"); + do_main(); +puts("FREE"); + ${WASM2C}FreeInstance(&instance); + Py_RETURN_NONE; + } + +# include "${WASM2C}.pymod" + + static PyMethodDef mod_${WASM2C}_methods[] = { +#include "${WASM2C}.def" + {NULL, NULL, 0, NULL} + }; + + static struct PyModuleDef mod_${WASM2C} = { + PyModuleDef_HEAD_INIT, + "${WASM2C}", + NULL, + -1, + mod_${WASM2C}_methods, + NULL, // m_slots + NULL, // m_traverse + NULL, // m_clear + NULL, // m_free + }; + + + PyMODINIT_FUNC + PyInit_${WASM2C}(void) { + PyObject *${WASM2C}_mod = PyModule_Create(&mod_${WASM2C}); +# ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(${WASM2C}_mod, Py_MOD_GIL_NOT_USED); +# endif + return ${WASM2C}_mod; + } +#endif + + +int main(int argc, char* argv[]) { + if (!skip_main) { +puts("216"); + char *tmp_argv[] = { "/tmp/pglite/bin/postgres", "--single", "template1", NULL}; + int tmp_argc = sizeof(tmp_argv) / sizeof(char*) - 1; + skip_main = pre_main(tmp_argc, tmp_argv); +puts("220"); + if (!skip_main) { + do_main(); +puts("FREE"); + ${WASM2C}FreeInstance(&instance); + } + } else { + puts(" -- main skipped --"); + } + return skip_main; +} diff --git a/pglite-REL_17_4_WASM/repl.html b/pglite-REL_17_4_WASM/repl.html new file mode 100644 index 0000000000000..e87f0e3c1c817 --- /dev/null +++ b/pglite-REL_17_4_WASM/repl.html @@ -0,0 +1,1091 @@ + + + + + + + PG SHELL TEST + + + + + + + + + + + + + + + + + + +
+
emscripten
+
Downloading...
+
+ +
+ + + +
+ + +
+pglite.wasi: Download file
+
+pglite.wasi + filesystem: Download file
+

+ +
+ +
+ +
+ + + +#if MODULARIZE + + +#endif + + + + + diff --git a/portable/Dockerfile b/portable/Dockerfile new file mode 100755 index 0000000000000..c4b6f251867b2 --- /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 xz \ + git patch bison flex \ + findutils binutils coreutils \ + libffi tzdata-utils 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 0000000000000..802a4bfcb2322 Binary files /dev/null and b/portable/libx86_64.tar.bz2 differ diff --git a/portable/pack_extension.py b/portable/pack_extension.py new file mode 100644 index 0000000000000..7f95b55e57725 --- /dev/null +++ b/portable/pack_extension.py @@ -0,0 +1,142 @@ +# cibuild/pack_extension.py + +# TODO: use this file for merging symbols too. + +# use recorded file list in ${PGROOT}/pg.installed +# get other files into a tarball, find a .so and named everything after it + + +import asyncio +import tarfile +import os +import sys +from pathlib import Path + +class Error(Exception): + pass + +def gather(root: Path, *kw): + + for current, dirnames, filenames in os.walk(root): + rel = Path("/").joinpath(Path(current).relative_to(root)) + + # print(rel, len(dirnames), len(filenames)) + yield rel, filenames + + + +def is_extension(path:Path, fullpath:Path): + global EXTNAME, SYMBOLS, PGPATCH, PGROOT + asp = path.as_posix() + + # check .so + if asp.startswith('/lib/postgresql/'): + if path.suffix == ".so": + EXTNAME = path.stem + if os.environ.get('OBJDUMP',''): + os.system(f"wasm-objdump -x {fullpath} > {PGROOT}/dump.{EXTNAME}") + os.system(f"OBJDUMP={PGROOT}/dump.{EXTNAME} python3 cibuild/getsyms.py imports > {PGPATCH}/imports/{EXTNAME}") + with open(f"{PGPATCH}/imports/{EXTNAME}","r") as f: + SYMBOLS=f.readlines() + + return True + + # rpath + if asp.startswith('/lib/'): + return True + + if asp.startswith('/share/postgresql/extension'): + return True + + + + +async def archive(target_folder): + global INSTALLED, PACKLIST + + walked = [] + for folder, filenames in gather(target_folder): + walked.append([folder, filenames]) + + + for folder, filenames in walked: + for filename in filenames: + test = Path(folder) / Path(filename) + asp = test.as_posix() + if (PGROOT/test).is_symlink(): + print("SYMLINK:", test) + continue + if asp not in INSTALLED: + if asp.startswith('/sdk/'): + continue + + if asp.startswith('/base/'): + continue + + if asp.startswith('/dump.'): + continue + + fp = PGROOT / asp[1:] + if fp.is_symlink(): + continue + if is_extension(test, fp): + #print(f"{EXTNAME=}", test ) + PACKLIST.append( [fp, test] ) + else: + print("custom:", test) + + +PGROOT=Path(os.environ.get('PGROOT',"/tmp/pglite")) +PGPATCH=Path(os.environ.get('PGPATCH', PGROOT)) + +INSTALLED = [] + +EXTNAME = "" +PACKLIST = [] +SYMBOLS=[] + +PREINST = "/plpgsql" +IS_PREINST = PREINST in sys.argv +for line in open(PGROOT / "pg.installed" ).readlines(): + asp = Path(line[1:].strip()).as_posix() + if IS_PREINST: + if asp.find(PREINST)>0: + continue + INSTALLED.append( asp ) + + +print("="*80) +asyncio.run( archive(PGROOT) ) +print("="*80) + +if not EXTNAME: + print("MAYBE ERROR: no new installed extension found, is it builtin ?") + sys.exit(0) + +print(f""" +PG installed in : {PGROOT=} + + + {EXTNAME =} ({len(SYMBOLS)} imports) + + + +""") + +swd = os.getcwd() + +if (not IS_PREINST) and ("builtin" not in sys.argv): + if len(PACKLIST): + os.chdir(PGROOT) + with tarfile.open(PGROOT / "sdk" / f"{EXTNAME}.tar" , "w:") as tar: + for fp, fn in PACKLIST: + print(f"{EXTNAME} : {fp} => {fn}") + tar.add(fn.as_posix()[1:]) + #if "builtin" not in sys.argv: + os.remove(fp) + os.chdir(swd) + else: + print(f"Nothing found to pack for {EXTNAME}, did you 'make install' ?") +else: + print("Nothing to pack for builtin extension :", EXTNAME) + diff --git a/portable/portable.sh b/portable/portable.sh new file mode 100755 index 0000000000000..23ca54cd2f7a5 --- /dev/null +++ b/portable/portable.sh @@ -0,0 +1,525 @@ +#!/usr/bin/env bash +export PG_VERSION=${PG_VERSION:-17.4} +export PG_BRANCH=${PG_BRANCH:-REL_17_4_WASM} +export PORTABLE=$(realpath $(dirname $0)) +export ROOT=$(realpath $(pwd)) +export SDKROOT=${SDKROOT:-/tmp/sdk} +export WASI=${WASI:-false} + +echo " + +================================================================================================== +================================================================================================== + +PORTABLE=$PORTABLE +ROOT=$ROOT +PG_VERSION=$PG_VERSION +PG_BRANCH=$PG_BRANCH +SDKROOT=$SDKROOT +DEBUG=$DEBUG +USE_ICU=$USE_ICU +WASI=$WASI + +================================================================================================== +================================================================================================== + +" + + + +export PATH=$PORTABLE:$PATH +export WORKDIR=${ROOT} +export CONTAINER_PATH=${CONTAINER_PATH:-/tmp/fs} +export HOME=/tmp +export PROOT=${PORTABLE}/proot.$(arch) +export ALPINEPROOT_NO_PULSE=true + + +# git would remove empty dirs +mkdir -p ${WORKDIR}/dist-${PG_BRANCH} ${WORKDIR}/build-${PG_BRANCH} + +# -------------------------------------------------------- +# "docker emulation" +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 () { + echo "proot: skipping docker cmd : '$@'" +} + +# -------------------------------------------------------- + + + + + + +if mkdir -p $CONTAINER_PATH +then + pushd $CONTAINER_PATH + touch alpine + + # use host name resolver + mkdir -p etc + cp /etc/resolv.conf etc/ + + # add some apk and start build automatically + if [ -f ${ROOT}/Dockerfile ] + then + cat ${PORTABLE}/functions ${ROOT}/Dockerfile > initrc + else + cat ${PORTABLE}/functions ${PORTABLE}/Dockerfile > initrc + fi + + # set a nice name for release tarball + cat > etc/lsb-release <$CONTAINER_PATH/etc/resolv.conf + fi + + __start $@ +} + +__get_container_url() { + CURRENT_VERSION=`curl -fsSL https://alpinelinux.org/downloads/ | grep -oP '(?<=)[^<]+(?=)'` + [ "$?" != "0" ] && exit $? + + CURRENT_VERSION_ID=`echo $CURRENT_VERSION | grep -oE '^[0-9]+\.[0-9]+'` + + echo "The following Alpine Linux version will be installed:" + echo "" + echo "Current Version: ${CURRENT_VERSION}" + echo "Version ID: ${CURRENT_VERSION_ID}" + echo "" + + CONTAINER_DOWNLOAD_URL="https://dl-cdn.alpinelinux.org/alpine/v${CURRENT_VERSION_ID}/releases/$(uname -m)/alpine-minirootfs-${CURRENT_VERSION}-$(uname -m).tar.gz" +} + +__start() { + $PROOT -0 rm -rf $CONTAINER_PATH/proc + mkdir $CONTAINER_PATH/proc + + # Proceed make fake /proc/version + echo "Linux version ${ALPINEPROOT_KERNEL_RELEASE:-6.0.0+} (root@localhost) #1 SMP Fri Jul 23 12:00:00 PDT 2021" >$CONTAINER_PATH/proc/.version + + # Proceed make fake /proc/stat + [ ! -r /proc/stat ] && cat <<-EOM >$CONTAINER_PATH/proc/.stat + cpu 5742 0 3915 205916 1204 0 339 82 0 0 + cpu0 1428 0 904 51706 126 0 108 21 0 0 + cpu1 1455 0 846 51772 99 0 87 20 0 0 + cpu2 1235 0 864 52026 102 0 65 19 0 0 + cpu3 1622 0 1300 50410 876 0 77 21 0 0 + intr 1181516 235 9 0 0 1703 0 241 0 0 0 542 0 3 0 0 0 0 0 0 0 0 0 0 0 0 5 0 0 1 26315 0 641 571 691 623 674 581 961 599 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ctxt 199763 + btime 1629638266 + processes 1 + procs_running 1 + procs_blocked 0 + softirq 562902 0 237872 366 6862 26279 0 9 122617 0 168897 + EOM + + # Proceed make fake /proc/uptime + [ ! -r /proc/uptime ] && echo "1302.49 5018.43" >$CONTAINER_PATH/proc/.uptime + + # Proceed make fake /proc/loadavg + [ ! -r /proc/loadavg ] && echo "0.54 0.22 0.13 1/461 650" >$CONTAINER_PATH/proc/.loadavg + + # Proceed make fake /proc/vmstat + [ ! -r /proc/vmstat ] && cat <<-EOM >$CONTAINER_PATH/proc/.vmstat + nr_free_pages 3621122 + nr_zone_inactive_anon 13457 + nr_zone_active_anon 93331 + nr_zone_inactive_file 180805 + nr_zone_active_file 116706 + nr_zone_unevictable 0 + nr_zone_write_pending 273 + nr_mlock 0 + nr_page_table_pages 1560 + nr_kernel_stack 7424 + nr_bounce 0 + nr_zspages 1385 + nr_free_cma 0 + numa_hit 9934918 + numa_miss 0 + numa_foreign 0 + numa_interleave 2111 + numa_local 9934918 + numa_other 0 + nr_inactive_anon 13457 + nr_active_anon 93331 + nr_inactive_file 180805 + nr_active_file 116706 + nr_unevictable 0 + nr_slab_reclaimable 37930 + nr_slab_unreclaimable 15580 + nr_isolated_anon 0 + nr_isolated_file 0 + workingset_nodes 73 + workingset_refault 1285 + workingset_activate 460 + workingset_restore 423 + workingset_nodereclaim 0 + nr_anon_pages 112449 + nr_mapped 87383 + nr_file_pages 291852 + nr_dirty 273 + nr_writeback 0 + nr_writeback_temp 0 + nr_shmem 286 + nr_shmem_hugepages 0 + nr_shmem_pmdmapped 0 + nr_file_hugepages 0 + nr_file_pmdmapped 0 + nr_anon_transparent_hugepages 1 + nr_unstable 0 + nr_vmscan_write 4383 + nr_vmscan_immediate_reclaim 0 + nr_dirtied 45538 + nr_written 48611 + nr_kernel_misc_reclaimable 0 + nr_dirty_threshold 775081 + nr_dirty_background_threshold 387067 + pgpgin 1116233 + pgpgout 217429 + pswpin 1 + pswpout 4383 + pgalloc_dma 0 + pgalloc_dma32 0 + pgalloc_normal 10007308 + pgalloc_movable 0 + allocstall_dma 0 + allocstall_dma32 0 + allocstall_normal 0 + allocstall_movable 0 + pgskip_dma 0 + pgskip_dma32 0 + pgskip_normal 0 + pgskip_movable 0 + pgfree 13629652 + pgactivate 92685 + pgdeactivate 23439 + pglazyfree 8319 + pgfault 10640127 + pgmajfault 6033 + pglazyfreed 0 + pgrefill 25344 + pgsteal_kswapd 0 + pgsteal_direct 0 + pgscan_kswapd 0 + pgscan_direct 0 + pgscan_direct_throttle 0 + zone_reclaim_failed 0 + pginodesteal 819 + slabs_scanned 44091 + kswapd_inodesteal 0 + kswapd_low_wmark_hit_quickly 0 + kswapd_high_wmark_hit_quickly 0 + pageoutrun 0 + pgrotated 3 + drop_pagecache 0 + drop_slab 0 + oom_kill 0 + pgmigrate_success 0 + pgmigrate_fail 0 + compact_migrate_scanned 0 + compact_free_scanned 0 + compact_isolated 0 + compact_stall 0 + compact_fail 0 + compact_success 0 + compact_daemon_wake 0 + compact_daemon_migrate_scanned 0 + compact_daemon_free_scanned 0 + htlb_buddy_alloc_success 0 + htlb_buddy_alloc_fail 0 + unevictable_pgs_culled 0 + unevictable_pgs_scanned 0 + unevictable_pgs_rescued 0 + unevictable_pgs_mlocked 0 + unevictable_pgs_munlocked 0 + unevictable_pgs_cleared 0 + unevictable_pgs_stranded 0 + thp_fault_alloc 0 + thp_fault_fallback 0 + thp_collapse_alloc 1 + thp_collapse_alloc_failed 0 + thp_file_alloc 0 + thp_file_mapped 0 + thp_split_page 0 + thp_split_page_failed 0 + thp_deferred_split_page 0 + thp_split_pmd 0 + thp_split_pud 0 + thp_zero_page_alloc 0 + thp_zero_page_alloc_failed 0 + thp_swpout 0 + thp_swpout_fallback 0 + balloon_inflate 0 + balloon_deflate 0 + balloon_migrate 0 + swap_ra 0 + swap_ra_hit 0 + EOM + + if [ "$(uname -o)" = "Android" ] + then + unset LD_PRELOAD + fi + + COMMANDS=$PROOT +# COMMANDS+=" --link2symlink" + COMMANDS+=" --kill-on-exit" + COMMANDS+=" --kernel-release=\"${ALPINEPROOT_KERNEL_RELEASE:-5.18}\"" + COMMANDS+=" -b /dev -b /proc -b /sys" + COMMANDS+=" -b /proc/self/fd:/dev/fd" + COMMANDS+=" -b /proc/self/fd/0:/dev/stdin" + COMMANDS+=" -b /proc/self/fd/1:/dev/stdout" + COMMANDS+=" -b /proc/self/fd/2:/dev/stderr" + COMMANDS+=" -b ${WORKDIR}:/workspace" + COMMANDS+=" -b ${WORKDIR}/build-${PG_BRANCH}:/tmp/sdk/build" + COMMANDS+=" -b ${WORKDIR}/dist-${PG_BRANCH}:/tmp/sdk/dist" + for f in stat version loadavg vmstat uptime + do + [ -f "$CONTAINER_PATH/proc/.$f" ] && COMMANDS+=" -b $CONTAINER_PATH/proc/.$f:/proc/$f" + done +# --change-id=uid:gid + COMMANDS+=" -r $CONTAINER_PATH -0 -w /root" + COMMANDS+=" -b $CONTAINER_PATH/root:/dev/shm" + + # Detect whenever Pulseaudio is installed with POSIX support + if pulseaudio=$(command -v pulseaudio) && [ ! -S $PREFIX/var/run/pulse/native ]; then + if [ -z "$ALPINEPROOT_NO_PULSE" ] + then + ! $pulseaudio --check && $pulseaudio --start --exit-idle-time=-1 + + [ $? = 0 ] && [ -S "$(echo $TMPDIR/pulse-*/native)" ] && COMMANDS+=" -b $(echo $TMPDIR/pulse-*/native):/var/run/pulse/native" + fi + else + if [ -z "$ALPINEPROOT_NO_PULSE" ] + then + [ -S $PREFIX/var/run/pulse/native ] && COMMANDS+=" -b $PREFIX/var/run/pulse/native:/var/run/pulse/native" + fi + fi + + [ -n "$ALPINEPROOT_PROOT_OPTIONS" ] && COMMANDS+=" $ALPINEPROOT_PROOT_OPTIONS" + + # Detect whenever ALPINEPROOT_BIND_TMPDIR is available or no. + [ -n "$ALPINEPROOT_BIND_TMPDIR" ] && COMMANDS+=" -b $TMPDIR:/tmp" + + if [ "$#" = 0 ]; then + eval "exec $COMMANDS /bin/su -l" + else + eval "exec $COMMANDS /bin/su -l -c \"$@\"" + fi +} + + + + +# git checkout ${PG_BRANCH} +if cd ${WORKDIR}/postgresql-${PG_BRANCH} +then + if [ -f postgresql-${PG_BRANCH}.patched ] + then + echo tree already patched for emscripten/pglite + else + echo " + + Patching branch ${PG_BRANCH} with : + +$(find ${WORKDIR}/patches-${PG_BRANCH}/postgresql-*) + + + +" + + # these initially don't exist in a released postgres. + touch ./src/template/emscripten \ + ./src/include/port/emscripten.h \ + ./src/include/port/wasm_common.h \ + ./src/makefiles/Makefile.emscripten + + for patchdir in \ + postgresql-debug \ + postgresql-emscripten \ + postgresql-wasi \ + postgresql-pglite + do + if [ -d ${WORKDIR}/patches-${PG_BRANCH}/$patchdir ] + then + for one in ${WORKDIR}/patches-${PG_BRANCH}/$patchdir/*.diff + do + if cat $one | patch -p1 + then + echo applied $one + else + echo " + +Fatal: failed to apply patch : $one +" + exit 457 + fi + done + fi + done + touch postgresql-${PG_BRANCH}.patched + fi + + + if [ -d $CONTAINER_PATH/${SDKROOT} ] + then + echo " + * using cached sdk version from $CONTAINER_PATH/${SDKROOT} +" + else + # SDK_URL=https://github.com/pygame-web/portable-sdk/releases/download/3.1.74.7bi/python3.13-wasm-sdk-alpine-3.21.tar.lz4 + SDK_URL=https://github.com/pygame-web/portable-sdk/releases/download/3.1.61.8/python3.12-wasm-sdk-debian12-$(arch).tar.lz4 + echo " + + * Setting up emsdk+wasi sdk from $SDK_URL + +" + pushd $CONTAINER_PATH + mkdir -p tmp + tmpfile=tmp/python-wasm-sdk-alpine-3.21.tar.lz4 + # local cache + [ -f $PORTABLE/python-wasm-sdk-alpine-3.21.tar.lz4 ] && cp -f $PORTABLE/python-wasm-sdk-alpine-3.21.tar.lz4 $tmpfile + [ -f $tmpfile ] || wget -q $SDK_URL -O$tmpfile + cat $tmpfile | tar x --use-compress-program=lz4 + + # unpack wasi sdk (common) + tar xf $PORTABLE/wasi-sdk-25.tar.xz + # unpack wasi sdk ( binary ) + tar xf $PORTABLE/wasi-sdk-25.0-$(arch)-linux.tar.xz + + # install arch binaries to sdk wasi sdk root + mv tmp/sdk/wasisdk/wasi-sdk-25.0-$(arch)-linux/* tmp/sdk/wasisdk/upstream/ + + if [ -f $CONTAINER_PATH/usr/bin/python3 ] + then + echo "system python found" + else + if [ -L $CONTAINER_PATH/usr/bin/python3 ] + then + echo "linked python for build found" + else + echo "Setting python for build as system python3" + mkdir -p $CONTAINER_PATH/usr/bin + ln -s $SDKROOT/devices/$(arch)/usr/bin/python3 $CONTAINER_PATH/usr/bin/python3 + fi + fi + popd + fi + + + # prevent erasing + touch ${CONTAINER_PATH}${SDKROOT}/dev + + if ${CI:-false} + then + alpineproot "apk add bash;/bin/bash /initrc" + else + alpineproot "apk add bash;/bin/bash --init-file /initrc" + fi +else + echo "Error need PG_BRANCH=$PG_BRANCH set to a valid postgres-pglite WASM branch" +fi diff --git a/portable/proot b/portable/proot new file mode 100755 index 0000000000000..fedea371ee205 Binary files /dev/null and b/portable/proot differ diff --git a/postgresql-17.4.patched b/postgresql-17.4.patched new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/postgresql-REL_17_4_WASM-pglite.patched b/postgresql-REL_17_4_WASM-pglite.patched new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/postgresql-REL_17_4_WASM.patched b/postgresql-REL_17_4_WASM.patched new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/src/Makefile.shlib b/src/Makefile.shlib index fa81f6ffdd6d9..aed880df4b876 100644 --- a/src/Makefile.shlib +++ b/src/Makefile.shlib @@ -224,6 +224,33 @@ ifeq ($(SHLIB_EXPORTS),) 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 @@ all-lib: lib$(NAME).pc 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/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index f8233d225201f..ae9daac93479a 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -131,8 +131,15 @@ static char PqRecvBuffer_static[PQ_RECV_BUFFER_SIZE]; static char *PqRecvBuffer; static int PqRecvPointer; static int PqRecvLength; +volatile int querylen = 0; +volatile FILE* queryfp = NULL; #endif +/* pglite specific */ +extern int cma_rsize; +extern bool sockfiles; + + /* * Message status */ @@ -282,7 +289,7 @@ pq_init(ClientSocket *client_sock) (void) pq_settcpusertimeout(tcp_user_timeout, port); } #endif /* WASM */ -PDEBUG("# 285:" __FILE__); +PDEBUG("# 287:" __FILE__); /* initialize state variables */ PqSendBufferSize = PQ_SEND_BUFFER_SIZE; PqSendBuffer = MemoryContextAlloc(TopMemoryContext, PqSendBufferSize); @@ -320,9 +327,7 @@ PDEBUG("# 285:" __FILE__); AddWaitEventToSet(FeBeWaitSet, WL_POSTMASTER_DEATH, PGINVALID_SOCKET, NULL, NULL); #else /* WASM */ - PDEBUG("# 220: FIXME: socketfile"); - #pragma message "FIXME: socketfile" - /* because we fill before starting reading message */ + /* because we may fill before starting reading message */ PqRecvBuffer = &PqRecvBuffer_static[0]; #endif /* WASM */ /* @@ -923,6 +928,20 @@ pq_recvbuf(void) else PqRecvLength = PqRecvPointer = 0; } +#if defined(__EMSCRIPTEN__) || defined(__wasi__) + if (queryfp && querylen) { + int got = fread( PqRecvBuffer, 1, PQ_RECV_BUFFER_SIZE - PqRecvPointer, queryfp); + querylen -= got; + PqRecvLength += got; + if (querylen<=0) { + PDEBUG("# 931: could close fp early here " __FILE__); + queryfp = NULL; + } + if (got>0) + return 0; + } + return EOF; +#endif /* Ensure that we're in blocking mode */ socket_set_nonblocking(false); @@ -1025,7 +1044,9 @@ pq_getbyte_if_available(unsigned char *c) *c = PqRecvBuffer[PqRecvPointer++]; return 1; } - +#if defined(__EMSCRIPTEN__) || (__wasi__) +puts("# 1044: pq_getbyte_if_available N/I in " __FILE__ ); abort(); +#else /* Put the socket into non-blocking mode */ socket_set_nonblocking(true); @@ -1062,7 +1083,7 @@ pq_getbyte_if_available(unsigned char *c) /* EOF detected */ r = EOF; } - +#endif return r; } @@ -1129,6 +1150,7 @@ pq_discardbytes(size_t len) return 0; } + /* -------------------------------- * pq_buffer_remaining_data - return number of bytes in receive buffer * @@ -1150,19 +1172,25 @@ pq_buffer_remaining_data(void) * This must be called before any of the pq_get* functions. * -------------------------------- */ -#if defined(I_EMSCRIPTEN) || defined(I_WASI) +#if defined(__EMSCRIPTEN__) || defined(__wasi__) EMSCRIPTEN_KEEPALIVE void pq_recvbuf_fill(FILE* fp, int packetlen) { - fread( PqRecvBuffer, packetlen, 1, fp); + if (packetlen>PQ_RECV_BUFFER_SIZE) { + int got = fread( PqRecvBuffer, 1, PQ_RECV_BUFFER_SIZE, fp); + queryfp = fp; + querylen = packetlen - got; + PqRecvLength = got; + } else { + fread( PqRecvBuffer, packetlen, 1, fp); + PqRecvLength = packetlen; + queryfp = NULL; + querylen = 0; + } PqRecvPointer = 0; - PqRecvLength = packetlen; -#if PDEBUG - printf("# 1199: pq_recvbuf_fill cma_rsize=%d PqRecvLength=%d buf=%p reply=%p\n", cma_rsize, PqRecvLength, &PqRecvBuffer[0], &PqSendBuffer[0]); -#endif - } + #endif -extern int cma_rsize; + static char * PqSendBuffer_save; void pq_startmsgread(void) @@ -1175,9 +1203,14 @@ pq_startmsgread(void) ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("terminating connection because protocol synchronization was lost"))); -#if defined(I_EMSCRIPTEN) || defined(I_WASI) +#if defined(__EMSCRIPTEN__) || defined(__wasi__) if (!pq_buffer_remaining_data()) { - if (cma_rsize) { + if (sockfiles) { + PqRecvBuffer = &PqRecvBuffer_static[0]; + if (PqSendBuffer_save) + PqSendBuffer=PqSendBuffer_save; + PqSendBufferSize = PQ_SEND_BUFFER_SIZE; + } else { PqRecvPointer = 0; PqRecvLength = cma_rsize; PqRecvBuffer = (char*)0x1; @@ -1186,15 +1219,10 @@ pq_startmsgread(void) PqSendBuffer_save = PqSendBuffer; PqSendBuffer = 2 + (char*)(cma_rsize); PqSendBufferSize = (CMA_MB*1024*1024) - (int)(&PqSendBuffer[0]); - } else { - PqRecvBuffer = &PqRecvBuffer_static[0]; - if (PqSendBuffer_save) - PqSendBuffer=PqSendBuffer_save; - PqSendBufferSize = PQ_SEND_BUFFER_SIZE; } } #if PDEBUG - printf("# 1199: pq_startmsgread cma_rsize=%d PqRecvLength=%d buf=%p reply=%p\n", cma_rsize, PqRecvLength, &PqRecvBuffer[0], &PqSendBuffer[0]); + printf("# 1225: pq_startmsgread cma_rsize=%d PqRecvLength=%d buf=%p reply=%p\n", cma_rsize, PqRecvLength, &PqRecvBuffer[0], &PqSendBuffer[0]); #endif #endif @@ -1325,21 +1353,17 @@ extern FILE* SOCKET_FILE; extern int SOCKET_DATA; static int internal_putbytes(const char *s, size_t len) { - if (PqSendPointer >= PqSendBufferSize) { - fprintf(stderr, "# 1329: overflow %d >= %d cma_rsize=%d CMA=%d\n", PqSendPointer, PqSendBufferSize,cma_rsize, CMA_MB); - } - - if (!cma_rsize) { - int wc= fwrite(s, 1, len, SOCKET_FILE); - SOCKET_DATA+=wc; - } else { - size_t amount; + size_t amount; + if (!sockfiles) { while (len > 0) { - /* If buffer is full, then flush it out */ + /* If buffer is full, then flush it out from cma to file and continue from there */ if (PqSendPointer >= PqSendBufferSize) { - socket_set_nonblocking(false); - if (internal_flush()) - return EOF; + int redirected = fwrite(PqSendBuffer, 1, PqSendPointer, SOCKET_FILE); + sockfiles = true; +#if PGDEBUG + fprintf(stderr, "# 1364: overflow %zu >= %d redirect=%d cma_rsize=%d CMA_MB=%d \n", PqSendPointer, PqSendBufferSize, redirected, cma_rsize, CMA_MB); +#endif + break; } amount = PqSendBufferSize - PqSendPointer; if (amount > len) @@ -1351,6 +1375,11 @@ internal_putbytes(const char *s, size_t len) { SOCKET_DATA+=amount; } } + + if (sockfiles) { + int wc= fwrite(s, 1, len, SOCKET_FILE); + SOCKET_DATA+=wc; + } return 0; } @@ -1362,7 +1391,7 @@ socket_flush(void) { static int internal_flush(void) { /* no flush for raw wire */ - if (!cma_rsize) { + if (sockfiles) { PqSendStart = PqSendPointer = 0; } return 0; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 767cda0ceebcd..59e94bddaa79c 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -110,6 +110,7 @@ int restrict_nonsystem_relation_kind; #if (defined(__EMSCRIPTEN__) || defined(__wasi__)) #if !defined(PGL_MAIN) volatile int cma_rsize = 0; + volatile bool sockfiles = false; #endif // PGL_MAIN bool quote_all_identifiers = false; FILE* SOCKET_FILE = NULL; diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index f2c61bc56377c..c6ad884207836 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -171,7 +171,11 @@ static DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC; /* 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 @@ cleanup_directories_atexit(void) static char * get_id(void) { +#if !defined(__EMSCRIPTEN__) && !defined(__wasi__) const char *username; #ifndef WIN32 @@ -825,6 +830,10 @@ get_id(void) username = get_user_name_or_exit(progname); return pg_strdup(username); +#else + setenv("PGUSER", WASM_USERNAME, 0); + return pg_strdup(getenv("PGUSER")); +#endif /* wasm */ } static char * @@ -1070,6 +1079,9 @@ set_null_conf(void) 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 +1620,9 @@ bootstrap_template1(void) } PG_CMD_CLOSE(); - - termPQExpBuffer(&cmd); + termPQExpBuffer(&cmd); free(bki_lines); - +PDEBUG("# 1624: BOOT pipe complete"); check_ok(); } @@ -1711,16 +1722,16 @@ static void 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 +2647,13 @@ setup_bin_paths(const char *argv0) 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 +3112,7 @@ initialize_data_directory(void) 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 +3150,53 @@ initialize_data_directory(void) 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", + "--allow-group-access", "--no-sync", + "-E", "UTF8", + "--locale=C.UTF-8", "--locale-provider=libc", +// "--builtin-locale=en_US.UTF-8", "--locale-provider=builtin", +// "--locale-provider=icu", "--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 +3256,15 @@ main(int argc, char *argv[]) * 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 +3470,7 @@ main(int argc, char *argv[]) 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,13 +3506,13 @@ main(int argc, char *argv[]) get_restricted_token(); setup_pgdata(); - +PDEBUG("# 3493:pgl_initdb_main " __FILE__); setup_bin_paths(argv[0]); - +PDEBUG("# 3495:pgl_initdb_main " __FILE__); effective_user = get_id(); if (!username) username = effective_user; - +PDEBUG("# 3514:pgl_initdb_main " __FILE__); if (strncmp(username, "pg_", 3) == 0) pg_fatal("superuser name \"%s\" is disallowed; role names cannot begin with \"pg_\"", username); @@ -3479,7 +3540,7 @@ main(int argc, char *argv[]) get_su_pwd(); printf("\n"); - +puts("# 3527:" __FILE__); initialize_data_directory(); if (do_sync) @@ -3535,7 +3596,7 @@ main(int argc, char *argv[]) destroyPQExpBuffer(start_db_cmd); } - +PDEBUG("# 3583"); success = true; return 0; } diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index baa447a3e9d42..c606554d6cc06 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1295,7 +1295,7 @@ puts("# 1164 : setup_connection"); * Quote all identifiers, if requested. */ if (fe_utils_quote_all_identifiers) - ExecuteSqlStatement(AH, "SET fe_utils_quote_all_identifiers = true"); + ExecuteSqlStatement(AH, "SET quote_all_identifiers = true"); /* * Adjust row-security mode, if supported. diff --git a/src/port/pqsignal.c b/src/port/pqsignal.c index 8ceb097ac45df..5eb4d98840af6 100644 --- a/src/port/pqsignal.c +++ b/src/port/pqsignal.c @@ -170,7 +170,13 @@ pqsignal(int signo, pqsigfunc func) #endif } -/* sneak stubs into libpgport */ +/* sneak emsdk or wasi wasm port support into libpgport */ #if defined(SDK_PORT) -# include "sdk_port.c" +# if defined(__wasi__) +# include "sdk_port-wasi.c" +# endif +# if defined(__EMSCRIPTEN__) +// # include "sdk_port-emscripten.c" +# endif #endif + diff --git a/tmp/.gitignore b/tmp/.gitignore new file mode 100644 index 0000000000000..86d0cb2726c6c --- /dev/null +++ b/tmp/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore \ No newline at end of file diff --git a/wasm-build.sh b/wasm-build.sh new file mode 100755 index 0000000000000..c65f665840fe7 --- /dev/null +++ b/wasm-build.sh @@ -0,0 +1,561 @@ +#!/bin/bash +export PG_VERSION=${PG_VERSION:-17.4} + +#set -x; +#set -e; +export LC_ALL=C + +export CI=${CI:-false} +export PORTABLE=${PORTABLE:-$(pwd)/wasm-build} +export SDKROOT=${SDKROOT:-/tmp/sdk} + +export GETZIC=${GETZIC:-true} +# systems default may not be in path +export ZIC=${ZIC:-/usr/sbin/zic} + +# data transfer zone this is == (wire query size + result size ) + 2 +# expressed in EMSDK MB, max is 13MB on emsdk 3.1.74+ +export CMA_MB=${CMA_MB:-12} +export TOTAL_MEMORY=${TOTAL_MEMORY:-180MB} + + +export WORKSPACE=${GITHUB_WORKSPACE:-$(pwd)} +export PGROOT=${PGROOT:-/tmp/pglite} +export WEBROOT=${WEBROOT:-/tmp/web} + +export PG_BUILD=${BUILD:-/tmp/sdk/build} + export PG_BUILD_DUMPS=${PG_BUILD}/dumps + export PGL_BUILD_NATIVE=${PG_BUILD}/pglite-native + + + +export PG_DIST=${DIST:-/tmp/sdk/dist} + export PG_DIST_EXT=${PG_DIST}/extensions-emsdk + + export PGL_DIST_JS=${PG_DIST}/pglite-js + export PGL_DIST_LINK=${PG_DIST}/pglite-link + + export PGL_DIST_NATIVE=${PG_DIST}/pglite-native + export PGL_DIST_C=${PG_DIST}/pglite-native + export PGL_DIST_WEB=${PG_DIST}/pglite-web + + +export DEBUG=${DEBUG:-true} + +export USE_ICU=${USE_ICU:-false} +export PGUSER=${PGUSER:-postgres} + +[ -f /tmp/portable.opts ] && . /tmp/portable.opts +[ -f /tmp/portable.dev ] && . /tmp/portable.dev + +# can override from cmd line +export WASI=${WASI:-false} +export WASI_SDK=${WASI_SDK:-25.0} +export PYBUILD=${PYBUILD:-3.13} +export NATIVE=${NATIVE:-false} + + +if $WASI +then + BUILD=wasi + if $DEBUG + then + export COPTS=${COPTS:-"-O2 -g3"} + export LOPTS=${LOPTS:-"-O2 -g3"} + else + export COPTS=${COPTS:-"-Oz -g0"} + export LOPTS=${LOPTS:-"-Oz -g0"} + fi +else + BUILD=emscripten + if $DEBUG + then + # clang default to O0 but specifying -O0 may trigger align bug in emsdk + if [ -f /alpine ] + then + # dev debug + export COPTS="-O2 -g3 --no-wasm-opt" + export LOPTS=${LOPTS:-"-O2 -g3 --no-wasm-opt -sASSERTIONS=1"} + + else + # docker debug ( exepected to be ide friendly ) + export COPTS="-g3 --no-wasm-opt" + export LOPTS=${LOPTS:-"-g3 --no-wasm-opt -sASSERTIONS=1"} + fi + + export COPTS="-O2 -sDEMANGLE_SUPPORT=1 -frtti -g4 --no-wasm-opt" + export LOPTS=${LOPTS:-"-O1 -sDEMANGLE_SUPPORT=1 -frtti -g4 --no-wasm-opt -sASSERTIONS=1"} + + else + # DO NOT CHANGE COPTS - optimized wasm corruption fix + export COPTS="-O2 -g3 --no-wasm-opt" + export LOPTS=${LOPTS:-"-Os -g0 --closure=0 -sASSERTIONS=0"} + fi +fi + +export BUILD +export BUILD_PATH=${PG_BUILD}/${BUILD} + +export PG_EXTRA=${PG_BUILD}/extra-${BUILD} + + +# default to user writeable paths in /tmp/ . +DIST_ALL="${PGROOT}/bin ${PG_DIST} ${PG_DIST_EXT} ${PG_BUILD_DUMPS} ${PGL_DIST_JS} ${PGL_BUILD_NATIVE}" +DIST_ALL="$DIST_ALL ${PGL_DIST_LINK} ${PGL_DIST_NATIVE} ${PGL_DIST_WEB} ${PGL_DIST_C}" +DIST_ALL="$DIST_ALL ${PG_EXTRA}" + +if mkdir -p $DIST_ALL +then + echo "checking for valid prefix ${PGROOT} ${PG_DIST}" +else + sudo mkdir -p $DIST_ALL + sudo chown $(whoami) -R $DIST_ALL +fi + + + + + +export PGDATA=${PGROOT}/base + + +chmod +x ${PORTABLE}/*.sh +[ -d ${PORTABLE}/extra ] && ${PORTABLE}/extra/*.sh + + +# this was set to false on 16.4 to skip some harmless exceptions without messing with core code. +# exit on error +EOE=true + + +# TODO: also handle PGPASSFILE hostname:port:database:username:password +# https://www.postgresql.org/docs/devel/libpq-pgpass.html +export CRED="-U $PGUSER --pwfile=${PGROOT}/password" + +if [ -f ${PGROOT}/password ] +then + echo "not changing db password" + PGPASS=$(cat ${PGROOT}/password) +else + PGPASS=${PGPASS:-password} + echo ${PGPASS:-password} > ${PGROOT}/password +fi + +export PGPASS + + +export PG_DEBUG_HEADER="${PGROOT}/include/pg_debug.h" + + +echo " + +System node/pnpm ( may interfer) : + + node : $(which node) $(which node && $(which node) -v) + PNPM : $(which pnpm) + + +" + + + +# setup compiler+node. emsdk provides node 20, recent enough for bun. +# TODO: but may need to adjust $PATH with stock emsdk. + +pushd ${SDKROOT} + if ${WASI} + then + . wasisdk/wasisdk_env.sh + if ${PORTABLE}/sdk.sh + then + echo "$PORTABLE : sdk check passed (wasi)" + fi + else + if which emcc + then + echo "emcc found in PATH=$PATH" + else + . ${SDKROOT}/wasm32-bi-emscripten-shell.sh + fi + + if ${PORTABLE}/sdk.sh + then + echo "$PORTABLE : sdk check passed (emscripten)" + else + echo emsdk failed + exit 181 + fi + + + export PG_LINK=${PG_LINK:-$(which emcc)} + + echo " + + Using provided emsdk from $(which emcc) + Using PG_LINK=$PG_LINK as linker + + node : $(which node) $($(which node) -v) + PNPM : $(which pnpm) + + + " + fi +popd + +# used for not makefile (manual linking and pgl_main) +# pass the "kernel" contiguous memory zone size to the C compiler with CMA_MB which will be multiplied by 1024x1024 in +# preprocessed source. +# nb: wasi does not use -sGLOBAL_BASE +export CC_PGLITE="-DPYDK=1 -DPG_PREFIX=${PGROOT} -I${PGROOT}/include -DCMA_MB=${CMA_MB}" + + + +echo " + ---------------------------------------- +" +env|grep PG |grep -v BUILD +echo +env|grep BUILD|grep -v PG +echo +env|grep WA +echo +env|grep PY + +echo " + ---------------------------------------- +PATH=${PATH} +wasmtime=$(which wasmtime) + ---------------------------------------- +" + + + + +# ========================= symbol extractor ============================ +if [ -f $PGROOT/bin/wasm-objdump ] +then + echo "wasm-objdump found" +else + WRAPPER=$(which wasm-objdump) + WASIFILE=$(realpath ${WRAPPER}.wasi) + if $WRAPPER -h $WASIFILE | grep -q 'file format wasm 0x1' + then + mkdir -p $PGROOT/bin/ + if cp -f $WRAPPER $WASIFILE $PGROOT/bin/ + then + echo "wasm-objdump found and working, and copied to $PGROOT/bin/" + else + OBJDUMP=false + fi + else + echo " + WARNING: $(which wasm-objdump) not working properly, trying alternate syntax + +" + cat > $WRAPPER < /proc/self/fd/2 +$(which wasmtime) --env PYTHONDONTWRITEBYTECODE=1 --dir / \$WASM \$@ +END + chmod +x $WRAPPER + + if $WRAPPER -h $WASIFILE | grep -q 'file format wasm 0x1' + then + mkdir -p $PGROOT/bin/ + if cp -f $WRAPPER $WASIFILE $PGROOT/bin/ + then + echo "wasm-objdump fixed and working, copied to $PGROOT/bin/" + fi + else + echo " + ERROR: $(which wasm-objdump) not working properly ( is wasmtime ok ? ) + +" + exit 281 + fi + fi +fi + + +# ========================= pg core configuration ============================ + +# testing postgres.js file instead of ${PGROOT}/pgopts.sh because build should not have failed. +if [ -f ${WEBROOT}/postgres.js ] +then + echo using current from ${WEBROOT} + + . ${PGROOT}/pgopts.sh + +else + + # default to web/release size optim. + + mkdir -p ${PGROOT}/include + if $DEBUG + then + export PGDEBUG="" + cat > ${PG_DEBUG_HEADER} << END +#ifndef I_PGDEBUG +#define I_PGDEBUG +#define WASM_USERNAME "$PGUSER" +#define PGDEBUG 1 +#define PDEBUG(string) fputs(string, stderr) +#define JSDEBUG(string) {EM_ASM({ console.log(string); });} +#define ADEBUG(string) { PDEBUG(string); JSDEBUG(string) } +#endif +END + + else + export PGDEBUG="" + cat > ${PG_DEBUG_HEADER} << END +#ifndef I_PGDEBUG +#define I_PGDEBUG +#define WASM_USERNAME "$PGUSER" +#define PDEBUG(string) +#define JSDEBUG(string) +#define ADEBUG(string) +#define PGDEBUG 0 +#endif +END + fi + + mkdir -p ${PGROOT}/include/postgresql/server + for dest in ${PGROOT}/include ${PGROOT}/include/postgresql ${PGROOT}/include/postgresql/server + do + [ -f $dest/pg_debug.h ] || cp ${PG_DEBUG_HEADER} $dest/ + done + + # store all options that have impact on cmd line initdb/boot compile+link + cat > ${PGROOT}/pgopts.sh <> ${PGROOT}/pgopts.sh + echo "export PGLITE=${PGLITE}" >> ${PGROOT}/pgopts.sh + + [ -f /tmp/portable.opts ] && cat /tmp/portable.opts >> ${PGROOT}/pgopts.sh + [ -f /tmp/portable.dev ] && cat /tmp/portable.dev >> ${PGROOT}/pgopts.sh + + . ${PGROOT}/pgopts.sh + + # make sure no non-mvp feature gets in. + cat > ${PGROOT}/config.site < ${PGROOT}/etc/postgresql/locale < pglite-link.sh < pglite-link.sh <&1 >/dev/null + then + echo " + Building contrib extension : $ext : end + " + else + echo " + + Extension $ext from $extdir failed to build + + " + exit 216 + fi + popd + + python3 ${PORTABLE}/pack_extension.py 2>&1 >/dev/null + + fi + fi + done + +fi + + +echo " + + Extensions distribution folder : ${PG_DIST_EXT} + + +" + +# if $CI +# then +# #if [ -d $PREFIX/include/X11 ] +# if true +# then +# echo -n +# else +# # install EXTRA sdk +# . /etc/lsb-release +# DISTRIB="${DISTRIB_ID}-${DISTRIB_RELEASE}" +# CIVER=${CIVER:-$DISTRIB} +# SDK_URL=https://github.com/pygame-web/python-wasm-sdk-extra/releases/download/$SDK_VERSION/python3.13-emsdk-sdk-extra-${CIVER}.tar.lz4 +# echo "Installing extra lib from $SDK_URL" +# curl -sL --retry 5 $SDK_URL | tar xvP --use-compress-program=lz4 | pv -p -l -s 15000 >/dev/null +# chmod +x ./extra/*.sh +# fi +# fi + +if [ -f ${PG_BUILD_DUMPS}/dump.vector ] +then + echo " + + * NOT rebuilding extra extensions ( found ${PG_BUILD_DUMPS}/dump.vector ) + +" +else + + for extra_ext in ./extra/*.sh + do + LOG=$PG_DIST_EXT/$(basename ${extra_ext}).log + echo "==== ${extra_ext} : $LOG ====" + + ${extra_ext} > $LOG || exit 112 + + python3 wasm-build/pack_extension.py + done + + +fi diff --git a/wasm-build/build-pgcore.sh b/wasm-build/build-pgcore.sh new file mode 100755 index 0000000000000..f395478088a1d --- /dev/null +++ b/wasm-build/build-pgcore.sh @@ -0,0 +1,435 @@ +if [ -f postgresql-${PG_BRANCH}/postgresql-${PG_BRANCH}.patched ] +then + echo version ${PG_BRANCH} already selected and patch stage already done +else + if [ -f configure ] + then + echo "building in tree ( docker or proot )" + ln -s . postgresql-${PG_BRANCH} + else + git clone --no-tags --depth 1 --single-branch --branch ${PG_BRANCH} https://github.com/electric-sql/postgres-pglite postgresql-${PG_BRANCH} + fi + + if pushd postgresql-${PG_BRANCH} + then + echo + touch ./src/template/emscripten + touch ./src/include/port/emscripten.h + touch ./src/include/port/wasm_common.h + touch ./src/makefiles/Makefile.emscripten + for patchdir in \ + postgresql-debug \ + postgresql-emscripten \ + postgresql-pglite + do + if [ -d ../patches/$patchdir ] + then + for one in ../patches/$patchdir/*.diff + do + if cat $one | patch -p1 + then + echo applied $one + else + echo " + +Fatal: failed to apply patch : $one +" + exit 37 + fi + done + fi + done + touch postgresql-${PG_BRANCH}.patched + popd # postgresql-${PG_BRANCH} + fi + + # either a submodule dir or a symlink. + # currently release only use symlink + [ -f postgresql/configure ] && rm postgresql 2>/dev/null + + # do nothing if it is a submodule + [ -d postgresql ] || ln -s postgresql-${PG_BRANCH} postgresql + +fi + +export PGSRC=$(realpath postgresql-${PG_BRANCH}) + +echo " + +Building $ARCHIVE (patched) from $PGSRC WASI=$WASI + + +build-pgcore: begin($BUILD) +___________________________________________________ + +CC_PGLITE=$CC_PGLITE + + +" + +if [ -f ${PGROOT}/pg.${BUILD}.installed ] +then + echo " + * skipping pg build, using previous install from ${PGROOT} +" +else + + mkdir -p ${BUILD_PATH} + pushd ${BUILD_PATH} + + # create empty package.json to avoid emsdk node conflicts + # with root package.json of project + echo "{}" > package.json + + + if [ -f Makefile ] + then + echo "Cleaning up previous build ..." + make distclean 2>&1 > /dev/null + fi + + +# TODO: --with-libxml xml2 >= 2.6.23 +# TODO: --with-libxslt add to sdk +# --disable-atomics https://github.com/WebAssembly/threads/pull/147 "Allow atomic operations on unshared memories" + + + COMMON_CFLAGS="${CC_PGLITE} -fpic -Wno-declaration-after-statement -Wno-macro-redefined -Wno-unused-function -Wno-missing-prototypes -Wno-incompatible-pointer-types" + + # common to all wasm flavour + cp ${PGSRC}/src/include/port/wasm_common.h ${PGROOT}/include/wasm_common.h + + # wasm os implementation router + cp ${PORTABLE}/sdk_port.h ${PGROOT}/include/sdk_port.h + + # specific implementation for wasm os flavour + [ -d ${PORTABLE}/sdk_port-${BUILD} ] && cp ${PORTABLE}/sdk_port-${BUILD}/* ${PGROOT}/include/ + + if ${WASI} + then + echo "WASI BUILD: turning off xml/xslt support" + XML2="" + UUID="" + + # -lwasi-emulated-signal -D_WASI_EMULATED_SIGNAL -lwasi-emulated-getpid -D_WASI_EMULATED_GETPID + WASM_LDFLAGS="-lwasi-emulated-mman -lwasi-emulated-pthread -lwasi-emulated-process-clocks" + WASM_CFLAGS="-I${WASISDK}/hotfix -DSDK_PORT=${PREFIX}/include/sdk_port-wasi.c ${COMMON_CFLAGS} -D_WASI_EMULATED_PTHREAD -D_WASI_EMULATED_MMAN -D_WASI_EMULATED_PROCESS_CLOCKS" + export MAIN_MODULE="" + + else + # --with-libxml does not fit with --without-zlib + if $CI + then + # do not build obsolete ext xml2 on CI + XML2="--with-zlib --with-libxml" + else + XML2="--with-zlib --with-libxml --with-libxslt" + fi + UUID="--with-uuid=ossp" + WASM_CFLAGS="${COMMON_CFLAGS}" + WASM_LDFLAGS="-sERROR_ON_UNDEFINED_SYMBOLS" + export MAIN_MODULE="-sMAIN_MODULE=1" + fi + + export XML2_CONFIG=$PREFIX/bin/xml2-config + + if $USE_ICU + then + CNF_ICU="--with-icu" + else + CNF_ICU="--without-icu" + fi + + + [ -f ${PREFIX}/devices/emsdk/usr/lib/libossp-uuid.a ] && rm ${PREFIX}/devices/emsdk/usr/lib/libossp-uuid.a + [ -f ${PREFIX}/devices/emsdk/usr/lib/libouuid.a ] && rm ${PREFIX}/devices/emsdk/usr/lib/libuuid.a + + + CNF="${PGSRC}/configure --prefix=${PGROOT} --cache-file=${PGROOT}/config.cache.${BUILD} \ + --disable-spinlocks --disable-largefile --without-llvm \ + --without-pam --disable-largefile --with-openssl=no \ + --without-readline $CNF_ICU \ + ${UUID} ${XML2} ${PGDEBUG}" + + + mkdir -p bin + + GETZIC=${GETZIC:-true} + + + EMCC_NODE="-sEXIT_RUNTIME=1 -DEXIT_RUNTIME -sNODERAWFS -sENVIRONMENT=node" + + + if $WASI + then + + export EXT=wasi + cat > ${PGROOT}/config.site < bin/zic < ${PGROOT}/config.site < bin/zic < /tmp/disable-shared.log + + mkdir -p $PGROOT/bin + + cat > $PGROOT/bin/emsdk-shared <> /tmp/disable-shared.log +# shared build +COPTS="$LOPTS" \${PG_LINK:-emcc} -L${PREFIX}/lib -DPREFIX=${PGROOT} -shared -sSIDE_MODULE=1 \$@ -Wno-unused-function +END + ln -sf $PGROOT/bin/emsdk-shared bin/emsdk-shared + + + cat > $PGROOT/bin/wasi-shared <> /tmp/disable-shared.log +# shared build +echo =================================================================================== +wasi-c -L${PREFIX}/lib -DPREFIX=${PGROOT} -shared \$@ -Wno-unused-function +echo =================================================================================== +END + ln -sf $PGROOT/bin/wasi-shared bin/wasi-shared + + chmod +x bin/zic $PGROOT/bin/wasi-shared $PGROOT/bin/emsdk-shared + + # for zic and emsdk-shared/wasi-shared called from makefile + export PATH=$(pwd)/bin:$PATH + +> /tmp/build.log +# 2>&1 > /tmp/build.log + if $DEBUG + then + NCPU=1 + else + NCPU=$(nproc) + fi + + # Get rid of some build stages for now + + cat > src/test/Makefile < src/test/isolation/Makefile + + # Keep a shell script for fast rebuild with env -i from cmdline + + # same script handle emcc and wasi + + echo "#!/bin/bash +# /tmp/portable.opts + +" > pg-make.sh + cat /tmp/portable.opts >> pg-make.sh + cat >> pg-make.sh <> pg-make.sh + + cat >> pg-make.sh <&1 > /tmp/install.log + then + echo install ok + if $WASI + then + cp src/backend/postgres.wasi $PGROOT/bin/ || exit 365 + else + if $DEBUG + then + # built with EMCC_CFLAGS="-sEXIT_RUNTIME=1 -DEXIT_RUNTIME -sNODERAWFS -sENVIRONMENT=node" emmake make -C + cp src/bin/initdb/initdb.wasm $PGROOT/bin/ + cp src/backend/postgres.wasm $PGROOT/bin/ + + fi + + mv src/bin/pg_config/pg_config.wasm ${PGROOT}/bin/ + cat > ${PGROOT}/bin/pg_config < ${PGROOT}/pg.${BUILD}.installed + popd + + pushd ${WORKSPACE} + + python3 wasm-build/pack_extension.py builtin + + pushd ${PGROOT} + find . -type f > ${PGROOT}/pg.${BUILD}.installed + popd + + else + cat /tmp/install.log + echo "install failed" + exit 400 + fi + + python3 > ${PGROOT}/PGPASSFILE <= 0: + dbg(line, recording) + + if recording: + if line.startswith(END): + dbg("-" * 40) + recording = False + break + + exports.append(line) + if line[0] != "-": + dbg(line) + + else: + if line[0] == "-": + continue + + if line.startswith(BEGIN): + dbg("\n\n#", line) + recording = True + continue + dbg("skip", line) + +dbg(f"found {len(exports)} {SECTION}") + +if 1: + badchars = '"<> ' + VERBOSE = "-v" in sys.argv + REPORT = [] + for line in exports: + typ, header = line.split("] ", 1) + if typ.startswith("- func["): + typ = "def" + elif typ.startswith("- global["): + pass + typ = "var" + elif typ.startswith("- memory["): + pass + typ = "mem" + elif typ.startswith("- table["): + pass + typ = "tbl" + + if SNIFF: + if line.find(SNIFF) >= 0: + dbg( + f""" + +------------------------------------------------------------------------------------- + +{line=} +{typ=} + + +------------------------------------------------------------------------------------- + +""" + ) + + try: + if typ in ("def", "var"): + left, right = header.rsplit(WAY, 1) + left = left.strip(badchars) + right = right.strip(badchars) + # GOT.mem. GOT.func. env. + clean = False + for clean in (".", " <"): + if left.find(clean) >= 0: + _, left = left.rsplit(clean, 1) + _, right = right.rsplit(".", 1) + clean = True + break + if clean: + left = left.strip(badchars) + if right.find(".") > 0: + _, right = right.rsplit(".", 1) + right = right.strip(badchars) + + if left.find("=") > 0: + left = right + + if left.find("=") > 0: + left = "" + + if not left: + left = right + + if SNIFF: + if line.find(SNIFF) >= 0: + dbg(f"{left=} {right=} '{line}'") + + if left.find("=") > 0: + left = "" + elif left.find("::") > 0: + if VERBOSE: + raise Exception("bad export (c++)") + # continue + elif left.find(" ") > 0: + if VERBOSE: + raise Exception("bad export (space)") + continue + + if VERBOSE: + demangle = os.popen(f"c++filt {right}").read().strip() + if not left or demangle == left: + dbg(typ, right, "# right") + elif demangle != right: + dbg(typ, left, "#", demangle) + else: + dbg(typ, "LEFT=", left, "RIGHT=", demangle) + + if (right or left) not in REPORT: + REPORT.append(right or left) + except Exception as e: + dbg("ERROR", typ, header, e) + + dbg(len(REPORT), "unique") + NODUNDER = [] + + for rep in REPORT: + if rep in NODUNDER: + print(f"{rep}") + else: + print(f"_{rep}") diff --git a/wasm-build/linkexport.sh b/wasm-build/linkexport.sh new file mode 100755 index 0000000000000..50506fdf7b1a0 --- /dev/null +++ b/wasm-build/linkexport.sh @@ -0,0 +1,41 @@ +# this only runs when wasm-objdump is working and OBJDUMP not set to false + +mkdir -p ${PGL_DIST_LINK}/exports ${PGL_DIST_LINK}/imports ${PG_BUILD_DUMPS} + +echo "============= link export to ${PGL_DIST_LINK}/exports : begin ===============" + +echo "FULL:" > ${PGL_DIST_LINK}/sizes.log +du -hs ${PGL_DIST_JS}/pglite-js.* >> ${PGL_DIST_LINK}/sizes.log +echo >> ${PGL_DIST_LINK}/sizes.log + + +echo " + * getting wasm exports lists +" + +pushd $(realpath ${PGL_DIST_JS}) + wasmtime --dir / --dir $(pwd)::. -- $(which wasm-objdump).wasi -x pglite-js.wasm > ${PG_BUILD_DUMPS}/pgcore.wasm-objdump +popd + + +pushd ${PGL_DIST_LINK} + echo " + * getting postgres exports lists from ${BUILD_PATH} +" + cat $(find ${BUILD_PATH} -type f |grep /exports|grep -v /interfaces/libpq/) \ + | grep -v ^\ local \ + | grep -v ^{\ global \ + | sort | uniq > ${PGL_DIST_LINK}/exports/pgcore.exports + + echo " + * Merging wasm pg core symbols and postgres exports lists + into ${PGL_DIST_LINK}/exports/pgcore +" + + OBJDUMP=${PG_BUILD_DUMPS}/pgcore.wasm-objdump \ + PGDUMP=${PGL_DIST_LINK}/exports/pgcore.exports \ + python3 ${WORKSPACE}/wasm-build/getsyms.py exports > ${PGL_DIST_LINK}/exports/pgcore +popd + +echo "============= link export : end ===============" + diff --git a/wasm-build/linkimports.sh b/wasm-build/linkimports.sh new file mode 100755 index 0000000000000..4ab35b48d5a76 --- /dev/null +++ b/wasm-build/linkimports.sh @@ -0,0 +1,152 @@ +echo "============= link imports : begin ===============" + +# TODO : make a C-API list +# _main,_getenv,_setenv,_interactive_one,_interactive_write,_interactive_read,_pg_initdb,_pg_shutdown + + +# extract own pg lib requirements +mkdir -p ${PGL_DIST_LINK}/imports + +pushd ${WORKSPACE} + > ${PGL_DIST_LINK}/imports/pgcore + + for extra_pg_so in $(find $PGROOT/lib/postgresql/|grep \.so$) + do + SOBASE=${PG_BUILD_DUMPS}/$(basename $extra_pg_so .so) + wasm-objdump -x $(realpath $extra_pg_so) > $SOBASE.wasm-objdump + OBJDUMP=$SOBASE.wasm-objdump \ + PGDUMP=${PGL_DIST_LINK}/exports/pgcore.exports \ + python3 wasm-build/getsyms.py imports >> ${PGL_DIST_LINK}/imports/pgcore + done +popd + +#not yet + +#_emscripten_copy_from +#_emscripten_copy_to +#_emscripten_copy_to_end + +# copyFrom,copyTo,copyToEnd + cat ${PGL_DIST_LINK}/imports/* | sort | uniq > /tmp/symbols + + echo "Requesting $(wc -l /tmp/symbols) symbols from pg core for PGlite extensions" + + + + python3 < ${PGL_DIST_LINK}/exports/pglite + +import sys +import os + +def dbg(*argv, **kw): + kw.setdefault('file',sys.stderr) + return print(*argv,**kw) + +with open("${PGL_DIST_LINK}/exports/pgcore", "r") as file: + exports = set(map(str.strip, file.readlines())) + +with open("/tmp/symbols", "r") as file: + imports = set(map(str.strip, file.readlines())) + +matches = list( imports.intersection(exports) ) + + +# ? +for sym in """ + +_clear_error +_get_buffer_addr +_get_buffer_size +_get_channel +_interactive_one +_interactive_read +_interactive_write +_pgl_backend +_pgl_closed +_pgl_initdb +_pgl_shutdown +_use_wire + +_main + +_ErrorContext +_check_function_bodies +_clock_gettime +_CurrentMemoryContext +___cxa_throw +_error_context_stack +_getenv +_lowerstr + +_readstoplist +_searchstoplist +_setenv +_shmem_request_hook +_shmem_startup_hook +_stderr +_TopMemoryContext +""".splitlines(): + if sym and not sym in matches: + matches.append(sym) + +matches.sort() + +for sym in """ +_PQcancelCreate +_PQcancelErrorMessage +_PQcancelFinish +_PQcancelPoll +_PQcancelSocket +_PQcancelStart +_PQclear +_PQcmdStatus +_PQconnectPoll +_PQconnectStartParams +_PQconnectionUsedPassword +_PQconsumeInput +_PQerrorMessage +_PQfinish +_PQgetResult +_PQgetisnull +_PQgetvalue +_PQisBusy +_PQnfields +_PQntuples +_PQresultErrorField +_PQresultStatus +_PQsendQuery +_PQserverVersion +_PQsetSingleRowMode +_PQsocket +_PQstatus +_pgresStatus + +_PQbackendPID +_PQconninfo +_PQconninfoFree +_PQconninfoParse +_PQendcopy +_PQescapeIdentifier +""".splitlines(): + if sym and sym in matches: + dbg(f"\t* Removed symbol '{sym}'") + matches.remove(sym) + +# matches.append('') + +if not '_getTempRet0' in matches: + matches.append('_getTempRet0') +if not 'getTempRet0' in matches: + matches.append('getTempRet0') + +for sym in matches: + print(sym) + +dbg(f""" +exports {len(exports)} +imports {len(imports)} +Matches : {len(matches)} +""") +END + +echo "============= link imports : end ===============" diff --git a/wasm-build/pack_extension.py b/wasm-build/pack_extension.py new file mode 100644 index 0000000000000..2a8d89a5be673 --- /dev/null +++ b/wasm-build/pack_extension.py @@ -0,0 +1,154 @@ +# wasm-build/pack_extension.py + +# TODO: use this file for merging symbols too. + +# use recorded file list in ${PGROOT}/pg.installed +# get other files into a tarball, find a .so and named everything after it + + +import asyncio +import tarfile +import os +import sys +from pathlib import Path + +class Error(Exception): + pass + +def gather(root: Path, *kw): + + for current, dirnames, filenames in os.walk(root): + rel = Path("/").joinpath(Path(current).relative_to(root)) + + # print(rel, len(dirnames), len(filenames)) + yield rel, filenames + +DIRS=[] +def pushd(d): + global DIRS + DIRS.append(os.getcwd()) + os.chdir(d) + return 1 + +def popd(): + global DIRS + os.chdir( DIRS.pop() ) + + +def is_extension(path:Path, fullpath:Path): + global EXTNAME, SYMBOLS, PGL_DIST_LINK, PGROOT + asp = path.as_posix() + + # check .so + if asp.startswith('/lib/postgresql/'): + if path.suffix == ".so": + EXTNAME = path.stem + dumpcmd = f"{PGROOT}/bin/wasm-objdump -x {fullpath} > {PG_BUILD_DUMPS}/dump.{EXTNAME} 2>/dev/null " + os.system(dumpcmd) + + os.system(f"OBJDUMP={PG_BUILD_DUMPS}/dump.{EXTNAME} python3 wasm-build/getsyms.py imports > {PGL_DIST_LINK}/imports/{EXTNAME}") + with open(f"{PGL_DIST_LINK}/imports/{EXTNAME}","r") as f: + SYMBOLS=f.readlines() + + return True + + # rpath + if asp.startswith('/lib/'): + return True + + if asp.startswith('/share/postgresql/extension'): + return True + + + + +async def archive(target_folder): + global INSTALLED, PACKLIST + + walked = [] + for folder, filenames in gather(target_folder): + walked.append([folder, filenames]) + + + for folder, filenames in walked: + for filename in filenames: + test = Path(folder) / Path(filename) + asp = test.as_posix() + if (PGROOT/test).is_symlink(): + print("SYMLINK:", test) + continue + if asp not in INSTALLED: + if asp.startswith('/sdk/'): + continue + + if asp.startswith('/base/'): + continue + + fp = PGROOT / asp[1:] + if fp.is_symlink(): + print("SYMLINK:", fp) + continue + + if is_extension(test, fp): + print(f"{EXTNAME=}", test, fp) + PACKLIST.append( [fp, test] ) + else: + print("custom:", test) + + +DIST = Path(os.environ.get("PG_DIST_EXT", "/tmp/sdk/dist/extensions-emsdk")) +BUILD=os.environ.get('BUILD','emscripten') +PGROOT=Path(os.environ.get('PGROOT',"/tmp/pglite")) +PGL_DIST_LINK=Path(os.environ.get('PGL_DIST_LINK', "/tmp/sdk/dist/pglite-link")) +PG_BUILD_DUMPS=Path(os.environ.get('PG_BUILD_DUMPS', "/tmp/sdk/build/dumps")) + +INSTALLED = [] + +EXTNAME = "" +PACKLIST = [] +SYMBOLS=[] + +PREINST = "/plpgsql" +IS_PREINST = PREINST in sys.argv +for line in open(PGROOT / f"pg.{BUILD}.installed" ).readlines(): + asp = Path(line[1:].strip()).as_posix() + if IS_PREINST: + if asp.find(PREINST)>0: + continue + INSTALLED.append( asp ) + + +print("="*80) +asyncio.run( archive(PGROOT) ) +print("="*80) + +if not EXTNAME: + print("MAYBE ERROR: no new installed extension found, is it builtin ?") + sys.exit(0) + +print(f""" +PG installed in : {PGROOT=} + + + {EXTNAME =} ({len(SYMBOLS)} imports) + + + +""") + +swd = os.getcwd() + +if (not IS_PREINST) and ("builtin" not in sys.argv): + if len(PACKLIST): + if pushd(PGROOT): + with tarfile.open(DIST / f"{EXTNAME}.tar", "w:") as tar: + for fp, fn in PACKLIST: + print(f"{EXTNAME} : {fp} => {fn}") + tar.add(fn.as_posix()[1:]) + # if "builtin" not in sys.argv: + os.remove(fp) + popd() + else: + print(f"Nothing found to pack for {EXTNAME}, did you 'make install' ?") +else: + print("Nothing to pack for builtin extension :", EXTNAME) diff --git a/wasm-build/reqsym.py b/wasm-build/reqsym.py new file mode 100644 index 0000000000000..d59e1851d3014 --- /dev/null +++ b/wasm-build/reqsym.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +import sys +import os + +def dbg(*argv, **kw): + kw.setdefault('file',sys.stderr) + return print(*argv,**kw) + +if "${WORKSPACE}".endswith('{WORKSPACE}'): + with open("/data/git/pglite-16.x/patches/exports.pglite", "r") as file: + exports = set(map(str.strip, file.readlines())) +else: + with open("${WORKSPACE}/patches/exports.pglite", "r") as file: + exports = set(map(str.strip, file.readlines())) + +with open("/tmp/symbols", "r") as file: + imports = set(map(str.strip, file.readlines())) + +matches = list( imports.intersection(exports) ) +for sym in matches: + print(sym) + +dbg(f""" +exports {len(exports)} +imports {len(imports)} +Matches : {len(matches)} +""") diff --git a/wasm-build/sdk.sh b/wasm-build/sdk.sh new file mode 100755 index 0000000000000..f4c7c15f70908 --- /dev/null +++ b/wasm-build/sdk.sh @@ -0,0 +1,189 @@ +#!/bin/bash + +SDKROOT=${SDKROOT:-/tmp/sdk} +mkdir -p ${SDKROOT} + +if grep -q __emscripten_tempret_get ${SDKROOT}/emsdk/upstream/emscripten/src/library_dylink.js +then + echo -n +else + pushd ${SDKROOT}/emsdk + patch -p1 < { + resolved ||= resolveSymbol(prop); + if (!resolved) { ++ if (prop==='getTempRet0') ++ return __emscripten_tempret_get(...args); + throw new Error(\`Dynamic linking error: cannot resolve symbol \${prop}\`); + } + return resolved(...args); +END + popd +fi + + + +if ${NO_SDK_CHECK:-false} +then + exit 0 +fi + +# GLOBAL BASE probable bug here +# https://github.com/emscripten-core/emscripten/blob/ac676d5e437525d15df5fd46bc2c208ec6d376a3/tools/link.py#L1652-L1658 + + +if python3 -V 2>/dev/null +then + echo using installed python3 +else + echo will use python for build as system python. + ln -sf $SDKROOT/devices/$(arch)/usr/bin/python3 /usr/bin/python3 +fi + +if [ -f $SDKROOT/VERSION ] +then + echo "Using tested sdk from $SDKROOT" +else + if [ -d $SDKROOT/emsdk ] + then + echo "Using installed sdk from $SDKROOT" + else + echo "Installing sdk to $SDKROOT" + SDK_ARCHIVE=${SDK_ARCHIVE:-python3.13-wasm-sdk-Ubuntu-22.04.tar.lz4} + WASI_SDK_ARCHIVE=${WASI_SDK_ARCHIVE:-python3.13-wasi-sdk-Ubuntu-22.04.tar.lz4} + if $CI + then + echo "if sdk fails here, check .yml files and https://github.com/pygame-web/python-wasm-sdk releases" + fi + echo https://github.com/pygame-web/python-wasm-sdk/releases/download/$SDK_VERSION/$SDK_ARCHIVE + curl -sL --retry 5 https://github.com/pygame-web/python-wasm-sdk/releases/download/$SDK_VERSION/$SDK_ARCHIVE | tar xP --use-compress-program=lz4 + echo https://github.com/pygame-web/python-wasi-sdk/releases/download/$WASI_SDK_VERSION/$WASI_SDK_ARCHIVE + curl -sL --retry 5 https://github.com/pygame-web/python-wasi-sdk/releases/download/$WASI_SDK_VERSION/$WASI_SDK_ARCHIVE | tar xP --use-compress-program=lz4 + fi + + pushd /tmp/sdk + +#if false +#then +# ${SDKROOT}/emsdk/upstream/bin/wasm-opt --version > ${SDKROOT}/wasm-opt.version +# cat > ${SDKROOT}/emsdk/upstream/bin/wasm-opt <> /tmp/wasm.opt +# exit 0 +#fi +#END +# chmod +x ${SDKROOT}/emsdk/upstream/bin/wasm-opt +#fi + + ALL="-m32 \ +-D_FILE_OFFSET_BITS=64 \ +-sSUPPORT_LONGJMP=emscripten \ +-mno-bulk-memory \ +-mnontrapping-fptoint \ +-mno-reference-types \ +-mno-sign-ext \ +-mno-extended-const \ +-mno-atomics \ +-mno-tail-call \ +-mno-fp16 \ +-mno-multivalue \ +-mno-relaxed-simd \ +-mno-simd128 \ +-mno-multimemory \ +-mno-exception-handling" + + + rm hello_em.* + + cat > /tmp/sdk/hello_em.c < +#include +#if defined(__EMSCRIPTEN__) +#include "emscripten.h" +#endif + +#define IO ((char *)(1)) + +int main(int argc, char**arv){ +#if defined(__EMSCRIPTEN__) +# if defined(__PYDK__) + printf("pydk" " %d.%d.%d\n",__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); +# else + printf("emsdk" " %d.%d.%d\n",__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__); +# endif +#else + puts("native"); +#endif + { + int first = 0; + for (int i=0;i<256;i++) + if ( IO[i] ) { + if (!first) + first = i; + printf("%c", IO[i] ); + } else { + printf("."); + } + printf("\n\nBASE=%d\n", first); + assert(first==31); + } + return 0; +} +END + + EMCC_TRACE=true DEBUG_PATTERN=* ${SDKROOT}/emsdk/upstream/emscripten/emcc -sASSERTIONS=0 -sENVIRONMENT=node,web -sGLOBAL_BASE=32B -o hello_em.html /tmp/sdk/hello_em.c + $SDKROOT/emsdk/node/*.*.*64bit/bin/node hello_em.js + $SDKROOT/emsdk/node/*.*.*64bit/bin/node hello_em.js |grep ^pydk > $SDKROOT/VERSION || exit 80 + rm hello_em.js hello_em.wasm + + python3 -E ${SDKROOT}/emsdk/upstream/emscripten/emcc.py $COPTS -sENVIRONMENT=node,web -sGLOBAL_BASE=32B $ALL -o hello_em.js /tmp/sdk/hello_em.c + $SDKROOT/emsdk/node/*.*.*64bit/bin/node hello_em.js + $SDKROOT/emsdk/node/*.*.*64bit/bin/node hello_em.js |grep ^emsdk >> $SDKROOT/VERSION || exit 84 + + rm hello_em.* + + popd + +fi + +cat $SDKROOT/VERSION + + +if $WASI +then + # always install wasmtime because wasm-objdump needs it. + if [ -f ${SDKROOT}/devices/$(arch)/usr/bin/wasmtime ] + then + echo "keeping installed wasmtime and wasi binaries" + else +# TODO: window only has a zip archive, better use wasmtime-py instead. + + wget https://github.com/bytecodealliance/wasmtime/releases/download/v33.0.0/wasmtime-v33.0.0-$(arch)-${PLATFORM}.tar.xz \ + -O-|xzcat|tar xfv - + mv -vf $(find wasmtime*|grep /wasmtime$) ${SDKROOT}/devices/$(arch)/usr/bin + fi + + if [ -f ${WASI_SYSROOT}/extra ] + then + echo -n + else + pushd ${WASI_SYSROOT} + VMLABS="https://github.com/vmware-labs/webassembly-language-runtimes/releases/download" + wget -q "${VMLABS}/libs%2Flibpng%2F1.6.39%2B20230629-ccb4cb0/libpng-1.6.39-wasi-sdk-20.0.tar.gz" -O-| tar xfz - + wget -q "${VMLABS}/libs%2Fzlib%2F1.2.13%2B20230623-2993864/libz-1.2.13-wasi-sdk-20.0.tar.gz" -O-| tar xfz - + wget -q "${VMLABS}/libs%2Fsqlite%2F3.42.0%2B20230623-2993864/libsqlite-3.42.0-wasi-sdk-20.0.tar.gz" -O-| tar xfz - + wget -q "${VMLABS}/libs%2Flibxml2%2F2.11.4%2B20230623-2993864/libxml2-2.11.4-wasi-sdk-20.0.tar.gz" -O-| tar xfz - + wget -q "${VMLABS}/libs%2Fbzip2%2F1.0.8%2B20230623-2993864/libbzip2-1.0.8-wasi-sdk-20.0.tar.gz" -O-| tar xfz - + wget -q "${VMLABS}/libs%2Flibuuid%2F1.0.3%2B20230623-2993864/libuuid-1.0.3-wasi-sdk-20.0.tar.gz" -O-| tar xfz - + popd + touch ${WASI_SYSROOT}/extra + fi +fi + diff --git a/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c b/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c new file mode 100644 index 0000000000000..44dac9e5e3cd2 --- /dev/null +++ b/wasm-build/sdk_port-wasi/sdk_port-wasi-dlfcn.c @@ -0,0 +1,198 @@ +// dict helper for dlfcn tables +// TODO: use QSTR + +#include "postgres.h" + +#include "fmgr.h" + +typedef struct dict_entry_s { + const char *key; + int value; +} dict_entry_s; + +typedef struct dict_s { + int len; + int cap; + dict_entry_s *entry; +} dict_s, *dict_t; + +static int +dict_find_index(dict_t dict, const char *key) { + for (int i = 0; i < dict->len; i++) { + if (!strcmp(dict->entry[i].key, key)) { + return i; + } + } + return -1; +} + +static int +dict_find(dict_t dict, const char *key, int def) { + int idx = dict_find_index(dict, key); + return idx == -1 ? def : dict->entry[idx].value; +} + +static void +dict_add(dict_t dict, const char *key, int value) { + int idx = dict_find_index(dict, key); + if (idx != -1) { + dict->entry[idx].value = value; + return; + } + if (dict->len == dict->cap) { + dict->cap *= 2; + dict->entry = realloc(dict->entry, dict->cap * sizeof(dict_entry_s)); + } + dict->entry[dict->len].key = strdup(key); + dict->entry[dict->len].value = value; + dict->len++; +} + +static dict_t +dict_new(void) { + dict_s proto = {0, 10, malloc(10 * sizeof(dict_entry_s))}; + dict_t d = malloc(sizeof(dict_s)); + *d = proto; + return d; +} + +/* dlclose stub +static void +dict_free(dict_t dict) { + for (int i = 0; i < dict->len; i++) { + free(dict->entry[i].key); + } + free(dict->entry); + free(dict); +} +*/ + +static inline int +ends_with(const char *str, const char *suffix) +{ + if (!str || !suffix) + return 0; + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) + return 0; + return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; +} + +// dlfcn.h + +volatile dict_t* dltab[]; +volatile int dltab_index = 0; + +void * +sym_stub(void) { + puts("DLSYM STUB"); + return NULL; +} + +char * +dlerror(void) { + return (char *)dlerror; +} + +static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; + +const Pg_magic_struct * +STUB_Pg_magic_func(void) { + return &Pg_magic_data; +} + +void STUB__PG_init(void) { +} + +void +STUB__PG_fini(void) { +} + +#define PG_FUNCTION_ARGS FunctionCallInfo fcinfo + +extern Datum dsnowball_init(PG_FUNCTION_ARGS); +extern void pg_finfo_dsnowball_init(void); +extern void pg_finfo_dsnowball_lexize(void); + +extern void pg_finfo_plpgsql_call_handler(void); +extern void pg_finfo_plpgsql_inline_handler(void); +extern void pg_finfo_plpgsql_validator(void); + +// listed in .sql +extern void plpgsql_call_handler(void); +extern void plpgsql_inline_handler(void); +extern void plpgsql_validator(void); + +extern void _PG_init(void); + + +void * +dlopen(const char *filename, int flags) { + dict_t tab = NULL; + fprintf(stderr,"void *dlopen(const char *filename = %s, int flags=%d)\n", filename, flags); + for (int i=0; i< dltab_index; i++) { + if ( dict_find_index(dltab[i], filename) > 0 ) + return (void *)i; + } + printf("dlopen: new lib '%s'\n", filename ); + if ( ends_with(filename,"/plpgsql.so") ){ + puts(" ========= CALLING _PG_init ========="); + _PG_init(); + } + + tab = dict_new(); + dict_add(tab, filename, dltab_index++ ); + dltab[dltab_index] = tab; + + return (void *)dltab_index; +} + +void * +dlsym(void *__restrict handle, const char *__restrict symbol) { + void *sym = NULL; + if ( !strcmp(symbol, "Pg_magic_func") ) { + sym = &STUB_Pg_magic_func; + goto report; + } + + if ( !strcmp(symbol, "_PG_init") ) + // sym = &STUB__PG_init; + return &STUB__PG_init; + + if ( !strcmp(symbol, "dsnowball_init") ) + //sym = &dsnowball_init; + return &dsnowball_init; + + if ( !strcmp(symbol, "pg_finfo_dsnowball_init") ) + return &pg_finfo_dsnowball_init; + + if ( !strcmp(symbol, "pg_finfo_dsnowball_lexize") ) + return &pg_finfo_dsnowball_lexize; + + if ( !strcmp(symbol, "pg_finfo_plpgsql_call_handler") ) + return &pg_finfo_plpgsql_call_handler; + + if ( !strcmp(symbol, "pg_finfo_plpgsql_inline_handler") ) + return &pg_finfo_plpgsql_inline_handler; + + if ( !strcmp(symbol, "pg_finfo_plpgsql_validator") ) + return &pg_finfo_plpgsql_validator; + + if ( !strcmp(symbol, "plpgsql_call_handler") ) + return &plpgsql_call_handler; + + if ( !strcmp(symbol, "plpgsql_inline_handler") ) + return &plpgsql_inline_handler; + + if ( !strcmp(symbol, "plpgsql_validator") ) { + sym = &plpgsql_validator; + goto report; + } + +report:; + fprintf(stderr, "void *dlsym(void *handle = %p, const char *symbol = %s) => %p\n", handle, symbol, sym); + return sym; +} + + diff --git a/wasm-build/sdk_port-wasi/sdk_port-wasi.c b/wasm-build/sdk_port-wasi/sdk_port-wasi.c new file mode 100644 index 0000000000000..bf0d75b402e21 --- /dev/null +++ b/wasm-build/sdk_port-wasi/sdk_port-wasi.c @@ -0,0 +1,595 @@ + +// WIP: signal here +// ================================================================== +#include + +/* Convenience type when working with signal handlers. */ +typedef void (*sa_handler_t) (int); + +/* Return the handler of a signal, as a sa_handler_t value regardless + of its true type. The resulting function can be compared to + special values like SIG_IGN but it is not portable to call it. */ +// static inline sa_handler_t; + +/* +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; +#ifdef SA_RESTORER + __sigrestore_t sa_restorer; +#endif + sigset_t sa_mask; +}; +*/ + +#ifdef SIGABRT_COMPAT +#define SIGABRT_COMPAT_MASK (1U << SIGABRT_COMPAT) +#else +#define SIGABRT_COMPAT_MASK 0 +#endif + + +/* Present to allow compilation, but unsupported by gnulib. */ +#if 0 +union sigval +{ + int sival_int; + void *sival_ptr; +}; + + +struct siginfo_t +{ + int si_signo; + int si_code; + int si_errno; + pid_t si_pid; + uid_t si_uid; + void *si_addr; + int si_status; + long si_band; + union sigval si_value; +}; + +typedef struct siginfo_t siginfo_t; + +struct sigaction_x +{ + union + { + void (*_sa_handler) (int); + /* Present to allow compilation, but unsupported by gnulib. POSIX + says that implementations may, but not must, make sa_sigaction + overlap with sa_handler, but we know of no implementation where + they do not overlap. */ + void (*_sa_sigaction) (int, siginfo_t *, void *); + } _sa_func; + sigset_t sa_mask; + /* Not all POSIX flags are supported. */ + int sa_flags; +}; +#endif // 0 + +#define sa_handler _sa_func._sa_handler +#define sa_sigaction _sa_func._sa_sigaction + +/* Unsupported flags are not present. */ +#define SA_RESETHAND 1 +#define SA_NODEFER 2 +#define SA_RESTART 4 + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 + + + +/* Set of current actions. If sa_handler for an entry is NULL, then + that signal is not currently handled by the sigaction handler. */ +struct sigaction volatile action_array[NSIG] /* = 0 */; + +// typedef void (*__sighandler_t) (int); + +# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) + + +/* Set of currently blocked signals. */ +volatile sigset_t blocked_set /* = 0 */; + +/* Set of currently blocked and pending signals. */ +volatile sig_atomic_t pending_array[NSIG] /* = { 0 } */; + +/* The previous signal handlers. + Only the array elements corresponding to blocked signals are relevant. */ +volatile handler_t old_handlers[NSIG]; + + +int +sigemptyset(sigset_t *set) { + *set = 0; + return 0; +} + +int +sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { + puts("# 96: sigaction STUB"); + return 0; +} + +int +sigfillset (sigset_t *set) { + *set = ((2U << (NSIG - 1)) - 1) & ~ SIGABRT_COMPAT_MASK; + return 0; +} + +int +sigaddset (sigset_t *set, int sig) { + if (sig >= 0 && sig < NSIG) + { + #ifdef SIGABRT_COMPAT + if (sig == SIGABRT_COMPAT) + sig = SIGABRT; + #endif + + *set |= 1U << sig; + return 0; + } + else + { + errno = EINVAL; + return -1; + } +} + +int +sigdelset (sigset_t *set, int sig) { + if (sig >= 0 && sig < NSIG) + { + #ifdef SIGABRT_COMPAT + if (sig == SIGABRT_COMPAT) + sig = SIGABRT; + #endif + + *set &= ~(1U << sig); + return 0; + } + else + { + errno = EINVAL; + return -1; + } +} + +/* Signal handler that is installed for blocked signals. */ +void +blocked_handler (int sig) +{ + /* Reinstall the handler, in case the signal occurs multiple times + while blocked. There is an inherent race where an asynchronous + signal in between when the kernel uninstalled the handler and + when we reinstall it will trigger the default handler; oh + well. */ + signal (sig, blocked_handler); + if (sig >= 0 && sig < NSIG) + pending_array[sig] = 1; +} + +int +sigprocmask (int operation, const sigset_t *set, sigset_t *old_set) { + if (old_set != NULL) + *old_set = blocked_set; + + if (set != NULL) + { + sigset_t new_blocked_set; + sigset_t to_unblock; + sigset_t to_block; + + switch (operation) + { + case SIG_BLOCK: + new_blocked_set = blocked_set | *set; + break; + case SIG_SETMASK: + new_blocked_set = *set; + break; + case SIG_UNBLOCK: + new_blocked_set = blocked_set & ~*set; + break; + default: + errno = EINVAL; + return -1; + } + to_unblock = blocked_set & ~new_blocked_set; + to_block = new_blocked_set & ~blocked_set; + + if (to_block != 0) + { + int sig; + + for (sig = 0; sig < NSIG; sig++) + if ((to_block >> sig) & 1) + { + pending_array[sig] = 0; + if ((old_handlers[sig] = signal (sig, blocked_handler)) != SIG_ERR) + blocked_set |= 1U << sig; + } + } + + if (to_unblock != 0) + { + sig_atomic_t received[NSIG]; + int sig; + + for (sig = 0; sig < NSIG; sig++) + if ((to_unblock >> sig) & 1) + { + if (signal (sig, old_handlers[sig]) != blocked_handler) + /* The application changed a signal handler while the signal + was blocked, bypassing our rpl_signal replacement. + We don't support this. */ + abort (); + received[sig] = pending_array[sig]; + blocked_set &= ~(1U << sig); + pending_array[sig] = 0; + } + else + received[sig] = 0; + + for (sig = 0; sig < NSIG; sig++) + if (received[sig]) + raise (sig); + } + } + return 0; +} + + +// STUBS +int sigismember(const sigset_t *set, int signum) { + return -1; +} + +int +pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset) { + return 0; +} + +int sigpending(sigset_t *set) { + return -1; +} + +int sigwait(const sigset_t *restrict set, int *restrict sig) { + return 0; +} + +unsigned int alarm(unsigned int seconds) { + return 0; +} + + +// WIP : shm +// ======================================================================================== +volatile int shm_index = 0; + +#include +void get_shm_path(char *tmpnam, const char *name) { + const char *shm = getenv("SHM"); + if (shm) { + printf("# 281 SHM=%s.%d", shm, shm_index); + snprintf(tmpnam, 128, "%s.%d", shm, shm_index++); + } else { + snprintf(tmpnam, 128, "/tmp%s", name); + } +} + +int shm_open(const char *name, int oflag, mode_t mode) { + char tmpnam[128]; + int fd; + get_shm_path(&tmpnam, name); + fd=fileno(fopen(tmpnam, "w+")); + fprintf(stderr, "# 287: shm_open(%s) => %d\n", tmpnam, fd); + return fd; +} + +int shm_unlink(const char *name) { + char tmpnam[128]; + if (getenv("SHM")) { + fprintf(stderr, "# 294: shm_unlink(%s) STUB\n", name); + return 0; + } + get_shm_path(&tmpnam, name); + return remove(tmpnam); +} + + +// popen +// ======================================================================================== + + +#include // FILE+fprintf +extern FILE* IDB_PIPE_FP; +extern FILE* SOCKET_FILE; +extern int SOCKET_DATA; +extern int IDB_STAGE; + + +static inline int +ends_with(const char *str, const char *suffix) +{ + if (!str || !suffix) + return 0; + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) + return 0; + return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; +} + +int +system_wasi(const char *command) { + fprintf(stderr, "# 164: system('%s')\n", command); + return -1; +} + +// pthread.h +// ======================================================================================== + + +/* +int pthread_create(pthread_t *restrict thread, + const pthread_attr_t *restrict attr, + void *(*start_routine)(void *), + void *restrict arg) { + puts("# 327: pthread_create STUB"); + return 0; +} + +int pthread_join(pthread_t thread, void **retval) { + return 0; +} + + + +int sdk_pthread_mutex_lock(void *mutex) { + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) { + return 0; +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) { + return 0; +} + +*/ + + +void wait(); + +// present in share/wasi-sysroot/lib/wasm32-wasi/libwasi-emulated-signal.a(signal.o) +// void __SIG_IGN(int param) { } + + +FILE *tmpfile(void) { + return fopen(mktemp("/tmp/tmpfile"),"w"); +} + + + + + +// unix socket via file emulation using sched_yiedl for event pump. +// ================================================================================================= + +#include + +volatile int stage = 0; +volatile int fd_queue = 0; +volatile int fd_out=2; +volatile FILE *fd_FILE = NULL; + +// default fd is stderr +int socket(int domain, int type, int protocol) { +#if 0 + printf("# 404 : domain =%d type=%d proto=%d -> FORCE FD to 3 \n", domain , type, protocol); + return 3; +#else + printf("# 408 : domain =%d type=%d proto=%d\n", domain , type, protocol); +#endif + if (domain|AF_UNIX) { + fd_FILE = fopen(PGS_ILOCK, "w"); + if (fd_FILE) { + fd_out = fileno(fd_FILE); + printf("# 414: AF_UNIX sock=%d (fd_sock write) FILE=%s\n", fd_out, PGS_ILOCK); + } else { + printf("# 416: AF_UNIX ERROR OPEN (w/w+) FILE=%s\n", PGS_ILOCK); + abort(); + } + } + return fd_out; +} + +int connect(int socket, void *address, socklen_t address_len) { +#if 1 + puts("# 425: connect STUB"); + //fd_out = 3; + return 0; +#else + puts("# 429: connect EINPROGRESS"); + errno = EINPROGRESS; + return -1; +#endif +} + +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, void *dest_addr, socklen_t addrlen) { + int sent = write( fd_out, buf, len); + + printf("# 438: send/sendto(%d ?= %ld )/%zu sockfd=%d fno=%d fd_out=%d)\n", sent, ftell(fd_FILE), len, sockfd, fileno(fd_FILE), fd_out); + fd_queue+=sent; + return sent; +} + +ssize_t sdk_send(int sockfd, const void *buf, size_t len, int flags) { + return sendto(sockfd, buf, len, flags, NULL, 0); +} + +volatile bool web_warned = false; + +void sock_flush() { + if (fd_queue) { + printf(" -- 451 sockflush : AIO YIELD, expecting %s filled on return --\n", PGS_OUT); + if (!fd_FILE) { + if (!web_warned) { + puts("# 454: WARNING: fd_FILE not set but queue not empty, assuming web"); + web_warned = true; + } + + } else { + printf("# 459: SENT=%ld/%d fd_out=%d fno=%d\n", ftell(fd_FILE), fd_queue, fd_out, fileno(fd_FILE)); + fclose(fd_FILE); + rename(PGS_ILOCK, PGS_IN); +//freopen(PGS_ILOCK, "w+", fd_FILE); + fd_FILE = fopen(PGS_ILOCK, "w"); + fd_out = fileno(fd_FILE); + printf("# 465: fd_out=%d fno=%d\n", fd_out, fileno(fd_FILE)); + } + fd_queue = 0; + sched_yield(); + return; + } + + printf(" -- 472 sockflush[%d] : NO YIELD --\n",stage); + + // limit inf loops + if (stage++ > 1024) { + puts("# 476 sock_flush : busy looping ?"); + abort(); + } +} + + +volatile int fd_current_pos = 0; +volatile int fd_filesize = 0; + + +ssize_t recvfrom_bc(int socket, void *buffer, size_t length, int flags, void *address, socklen_t *address_len) { +// int busy = 0; + int rcv = -1; + sock_flush(); +/* + while (access(PGS_OUT, F_OK) != 0) { + if (!(++busy % 555111)) { + printf("# 471: FIXME: busy wait (%d) for input stream %s\n", busy, PGS_OUT); + } + if (busy>1665334) { + errno = EINTR; + return -1; + } + } +*/ + FILE *sock_in = fopen(PGS_OUT,"r"); + if (sock_in) { + if (!fd_filesize) { + fseek(sock_in, 0L, SEEK_END); + fd_filesize = ftell(sock_in); + } + fseek(sock_in, fd_current_pos, SEEK_SET); + + char *buf = buffer; + buf[0] = 0; + rcv = fread(buf, 1, length, sock_in); + + if (rcv1665334) { + errno = EINTR; + return -1; + } + } +*/ + FILE *sock_in = fopen(PGS_OUT,"r"); + if (sock_in) { + if (!fd_filesize) { + fseek(sock_in, 0L, SEEK_END); + fd_filesize = ftell(sock_in); + } + fseek(sock_in, fd_current_pos, SEEK_SET); + + char *buf = buffer; + buf[0] = 0; + rcv = fread(buf, 1, length, sock_in); + + if (rcv + +#elif defined(__wasi__) + + +#ifndef I_WASI +#define I_WASI + +#undef HAVE_PTHREAD + +#if defined(HAVE_SETSID) +#undef HAVE_SETSID +#endif + +#if defined(HAVE_GETRLIMIT) +#undef HAVE_GETRLIMIT +#endif + + +#define PLATFORM_DEFAULT_SYNC_METHOD SYNC_METHOD_FDATASYNC + +#define EMSCRIPTEN_KEEPALIVE __attribute__((used)) +#define __declspec( dllimport ) __attribute__((used)) + +#define em_callback_func void +#define emscripten_set_main_loop(...) +#define emscripten_force_exit(...) +#define EM_JS(...) + +#include "/tmp/pglite/include/wasm_common.h" + + +static pid_t +fork(void) { + puts("# 31: fork -1"); + return -1; +} + +// ======== signal ======================== +#define SA_RESTART 4 +#define SIG_SETMASK 2 + +#define SIG_BLOCK 0 +#define SIG_UNBLOCK 1 + +/* A signal handler. */ +typedef void (*handler_t) (int signal); +typedef unsigned char sigset_t; +typedef void (*__sighandler_t) (int); + +struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; +#ifdef SA_RESTORER + __sigrestore_t sa_restorer; +#endif + sigset_t sa_mask; +}; +extern int sigemptyset(sigset_t *set); +extern int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); +extern int sigdelset (sigset_t *set, int sig); +extern int sigfillset (sigset_t *set); +extern int sigprocmask (int operation, const sigset_t *set, sigset_t *old_set); +extern int sigaddset (sigset_t *set, int sig); + +// STUBS +extern int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); +extern int sigismember(const sigset_t *set, int signum); +extern int sigpending(sigset_t *set); +extern int sigwait(const sigset_t *restrict set, int *restrict sig); + +// ==================================================== +// unistd +extern unsigned int alarm(unsigned int seconds); + +// ==================================================== + + +#include + +static int +sigsetjmp(sigjmp_buf env, int savesigs) { +// puts("# 120: sigsetjmp"); + return 0; +} + +static void +siglongjmp(sigjmp_buf env, int val) { + puts("# 120: siglongjmp"); +} + + + +// WIP : + +#include +static uid_t +getuid(void) { + return 1000; +} + +static int +dup(int fd) { + puts("# 128: dup"); + return fd; +} +static int +dup2(int old, int new) { + puts("# 140: dup2"); + return -1; +} +static int +pipe(int fd[2]) { + puts("# 145: pipe"); + abort(); + return -1; +} + +#include +#define RLIMIT_NOFILE 7 +#define RLIMIT_STACK 3 +#define RLIM_INFINITY ((unsigned long int)(~0UL)) + +struct rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; +static int +getrlimit(int resource, struct rlimit *rlim) { + return -1; +} + + +static const char *gai_strerror_msg = "# 165: gai_strerror_msg"; +static const char * +gai_strerror(int errcode) { + return gai_strerror_msg; +} + + +static int +getrusage(int who, struct rusage *usage) { + return -1; +} + +// WIP: semaphores here +// ================================================================== +#include + +static int +semctl(int semid, int semnum, int cmd, ...) { + return 0; // -1; +} + +static int +semget(key_t key, int nsems, int semflg) { +#if 0 // PGDEBUG + printf("# 213: semget(key_t key = %d, int nsems=%d, int semflg=%d)\n", key, nsems, semflg); +#endif + return 1; +} + +static int +semop(int semid, struct sembuf *sops, size_t nsops) { + return 0; // -1; +} + + + +#include +#if defined(PYDK) +extern int shm_open(const char *name, int oflag, mode_t mode); +extern int shm_unlink(const char *name); +#else +static int +shm_open(const char *name, int oflag, mode_t mode) { + char tmpnam[128]; + int fd; + snprintf(tmpnam, 128, "/tmp%s", name); + fd=fileno(fopen(tmpnam, "w+")); + fprintf(stderr, "# 212: shm_open(%s) => %d\n", tmpnam, fd); + return fd; +} + +static int +shm_unlink(const char *name) { + char tmpnam[128]; + snprintf(tmpnam, 128, "/tmp%s", name); + fprintf(stderr, "# 220: shm_unlink(%s) STUB\n", tmpnam); + return remove(tmpnam); // -1 +} + +#endif + +// initdb chmod +#if defined(__wasi__) + #define chmod(...) 0 +#endif + + + +#define system(command) system_wasi(command) +extern int system_wasi(const char *command); + + + +// time.h + +static void +tzset(void) { + puts("# 241: tzset(void) STUB"); +} + +#if defined(PG_INITDB) || defined(FE_UTILS_PRINT) || defined(PG_DUMP_PARALLEL) +static void +__SIG_IGN(int param) { +} +#endif + + +extern void sock_flush(); + + +// TODO: socket here +// ================================================================== + +#include + +extern ssize_t sdk_recv(int sockfd, void *buf, size_t len, int flags); +extern ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, void *address, socklen_t *address_len); + +extern ssize_t sdk_send(int sockfd, const void *buf, size_t len, int flags); +extern ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, void *dest_addr, socklen_t addrlen); + +#define recv(sockfd, buf, len, flags) sdk_recv(sockfd, buf, len, flags) + + + + +static int +listen(int sockfd, int backlog) { + return 0; +} + +static struct group *_Nullable +getgrnam(const char *_Nullable name) { + return NULL; +} + +static int +getsockname(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict addrlen) { + return -1; +} + +static int +getaddrinfo(const char *restrict node, + const char *restrict service, + void *restrict hints, + void **restrict res) { + puts("# 60: getaddrinfo"); + return -1; +} +static void +freeaddrinfo(void *res) { + puts("# 65: freeaddrinfo"); +} + +extern ssize_t recvfrom_bc(int socket, void *buffer, size_t length, int flags, void *address, socklen_t *address_len); + + +#define getpid sdk_getpid +extern pid_t sdk_getpid(void); + + +//#define pthread_mutex_lock(mut) sdk_pthread_mutex_lock(mut) +//extern int sdk_pthread_mutex_lock(void *mutex); + +/* + int pthread_mutex_lock(pthread_mutex_t *mutex); + int pthread_mutex_trylock(pthread_mutex_t *mutex); + int pthread_mutex_unlock(pthread_mutex_t *mutex); +*/ + + +#endif // I_WASI + +#else + #error "unknown port mode should be __EMSCRIPTEN__ or __wasi__" +#endif // __EMSCRIPTEN__