Segment * CopySegment(Segment *src, MemoryContext cxt) { MemoryContext valid_cxt = cxt ? cxt : CurrentMemoryContext; Segment *dest = MemoryContextAllocZero(valid_cxt, sizeof(Segment)); memcpy(dest, src, sizeof(Segment)); /* memory leak risk! */ dest->hostname = MemoryContextStrdup(valid_cxt, src->hostname); dest->hostip = MemoryContextStrdup(valid_cxt, src->hostip); return dest; }
/* * sepgsql_avc_unlabeled * * Returns an alternative label to be applied when no label or an invalid * label would otherwise be assigned. */ static char * sepgsql_avc_unlabeled(void) { if (!avc_unlabeled) { security_context_t unlabeled; if (security_get_initial_context_raw("unlabeled", &unlabeled) < 0) ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR), errmsg("SELinux: failed to get initial security label: %m"))); PG_TRY(); { avc_unlabeled = MemoryContextStrdup(avc_mem_cxt, unlabeled); } PG_CATCH(); { freecon(unlabeled); PG_RE_THROW(); } PG_END_TRY(); freecon(unlabeled); } return avc_unlabeled; }
bool InstallHelper_isPLJavaFunction(Oid fn) { char *itsPath; char *pljPath; bool result = false; itsPath = pljavaFnOidToLibPath(fn); if ( NULL == itsPath ) return false; if ( NULL == pljavaLoadPath ) { pljPath = NULL; if ( InvalidOid != pljavaTrustedOid ) pljPath = pljavaFnOidToLibPath(pljavaTrustedOid); if ( NULL == pljPath && InvalidOid != pljavaUntrustedOid ) pljPath = pljavaFnOidToLibPath(pljavaUntrustedOid); if ( NULL == pljPath ) { elog(WARNING, "unable to determine PL/Java's load path"); goto finally; } pljavaLoadPath = (char const *)MemoryContextStrdup(TopMemoryContext, pljPath); pfree(pljPath); } result = 0 == strcmp(itsPath, pljavaLoadPath); finally: pfree(itsPath); return result; }
/* * Module initialize function: initialize info about Bloom relation options. * * Note: keep this in sync with makeDefaultBloomOptions(). */ void _PG_init(void) { int i; char buf[16]; bl_relopt_kind = add_reloption_kind(); /* Option for length of signature */ add_int_reloption(bl_relopt_kind, "length", "Length of signature in bits", DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH); bl_relopt_tab[0].optname = "length"; bl_relopt_tab[0].opttype = RELOPT_TYPE_INT; bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength); /* Number of bits for each possible index column: col1, col2, ... */ for (i = 0; i < INDEX_MAX_KEYS; i++) { snprintf(buf, sizeof(buf), "col%d", i + 1); add_int_reloption(bl_relopt_kind, buf, "Number of bits generated for each index column", DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS); bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext, buf); bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT; bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i; } }
/* * add_string_reloption * Add a new string reloption * * "validator" is an optional function pointer that can be used to test the * validity of the values. It must elog(ERROR) when the argument string is * not acceptable for the variable. Note that the default value must pass * the validation. */ void add_string_reloption(bits32 kinds, char *name, char *desc, char *default_val, validate_string_relopt validator) { relopt_string *newoption; /* make sure the validator/default combination is sane */ if (validator) (validator) (default_val); newoption = (relopt_string *) allocate_reloption(kinds, RELOPT_TYPE_STRING, name, desc); newoption->validate_cb = validator; if (default_val) { newoption->default_val = MemoryContextStrdup(TopMemoryContext, default_val); newoption->default_len = strlen(default_val); newoption->default_isnull = false; } else { newoption->default_val = ""; newoption->default_len = 0; newoption->default_isnull = true; } add_reloption((relopt_gen *) newoption); }
/* * sepgsql_xact_callback * * A callback routine of transaction commit/abort/prepare. Commit or abort * changes in the client_label_pending list. */ static void sepgsql_xact_callback(XactEvent event, void *arg) { if (event == XACT_EVENT_COMMIT) { if (client_label_pending != NIL) { pending_label *plabel = llast(client_label_pending); char *new_label; if (plabel->label) new_label = MemoryContextStrdup(TopMemoryContext, plabel->label); else new_label = NULL; if (client_label_committed) pfree(client_label_committed); client_label_committed = new_label; /* * XXX - Note that items of client_label_pending are allocated on * CurTransactionContext, thus, all acquired memory region shall * be released implicitly. */ client_label_pending = NIL; } } else if (event == XACT_EVENT_ABORT) client_label_pending = NIL; }
char *lookup_primary_key(char *schemaName, char *tableName, bool failOnMissing) { StringInfo sql = makeStringInfo(); char *keyname; SPI_connect(); appendStringInfo(sql, "SELECT column_name FROM information_schema.key_column_usage WHERE table_schema = '%s' AND table_name = '%s'", schemaName, tableName); SPI_execute(sql->data, true, 1); if (SPI_processed == 0) { if (failOnMissing) elog(ERROR, "Cannot find primary key column for: %s.%s", schemaName, tableName); else { SPI_finish(); return NULL; } } keyname = SPI_getvalue(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1); if (keyname == NULL) elog(ERROR, "Primary Key field is null for: %s.%s", schemaName, tableName); keyname = MemoryContextStrdup(TopTransactionContext, keyname); SPI_finish(); return keyname; }
static void parse_newstyle_args(PgqTriggerEvent *ev, TriggerData *tg) { int i; /* * parse args */ for (i = 1; i < tg->tg_trigger->tgnargs; i++) { const char *arg = tg->tg_trigger->tgargs[i]; if (strcmp(arg, "SKIP") == 0) ev->tgargs->skip = true; else if (strncmp(arg, "ignore=", 7) == 0) ev->tgargs->ignore_list = MemoryContextStrdup(tbl_cache_ctx, arg + 7); else if (strncmp(arg, "pkey=", 5) == 0) ev->tgargs->pkey_list = MemoryContextStrdup(tbl_cache_ctx, arg + 5); else if (strcmp(arg, "backup") == 0) ev->tgargs->backup = true; else if (strcmp(arg, "deny") == 0) ev->tgargs->deny = true; else if (strncmp(arg, "ev_extra4=", 10) == 0) make_query(ev, EV_EXTRA4, arg + 10); else if (strncmp(arg, "ev_extra3=", 10) == 0) make_query(ev, EV_EXTRA3, arg + 10); else if (strncmp(arg, "ev_extra2=", 10) == 0) make_query(ev, EV_EXTRA2, arg + 10); else if (strncmp(arg, "ev_extra1=", 10) == 0) make_query(ev, EV_EXTRA1, arg + 10); else if (strncmp(arg, "ev_data=", 8) == 0) make_query(ev, EV_DATA, arg + 8); else if (strncmp(arg, "ev_type=", 8) == 0) make_query(ev, EV_TYPE, arg + 8); else if (strncmp(arg, "when=", 5) == 0) make_query(ev, EV_WHEN, arg + 5); else elog(ERROR, "bad param to pgq trigger"); } if (ev->op_type == 'R') { if (ev->tgargs->ignore_list) elog(ERROR, "Column ignore does not make sense for truncate trigger"); if (ev->tgargs->pkey_list) elog(ERROR, "Custom pkey_list does not make sense for truncate trigger"); if (ev->tgargs->backup) elog(ERROR, "Backup does not make sense for truncate trigger"); } }
static PyObject * PLy_cursor_query(const char *query) { PLyCursorObject *cursor; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL) return NULL; cursor->portalname = NULL; cursor->closed = false; cursor->mcxt = AllocSetContextCreate(TopMemoryContext, "PL/Python cursor context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); PLy_typeinfo_init(&cursor->result, cursor->mcxt); oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); SPIPlanPtr plan; Portal portal; pg_verifymbstr(query, strlen(query), false); plan = SPI_prepare(query, 0, NULL); if (plan == NULL) elog(ERROR, "SPI_prepare failed: %s", SPI_result_code_string(SPI_result)); portal = SPI_cursor_open(NULL, plan, NULL, NULL, exec_ctx->curr_proc->fn_readonly); SPI_freeplan(plan); if (portal == NULL) elog(ERROR, "SPI_cursor_open() failed: %s", SPI_result_code_string(SPI_result)); cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); Assert(cursor->portalname != NULL); return (PyObject *) cursor; }
/* * Fill table information in hash table. */ static void fill_tbl_info(Relation rel, struct PgqTableInfo *info) { StringInfo pkeys; Datum values[1]; const char *name = find_table_name(rel); TupleDesc desc; HeapTuple row; bool isnull; int res, i, attno; /* allow reset ASAP, but ignore it in this call */ info->invalid = false; /* load pkeys */ values[0] = ObjectIdGetDatum(rel->rd_id); res = SPI_execute_plan(pkey_plan, values, NULL, false, 0); if (res != SPI_OK_SELECT) elog(ERROR, "pkey_plan exec failed"); /* * Fill info */ desc = SPI_tuptable->tupdesc; pkeys = makeStringInfo(); info->n_pkeys = SPI_processed; info->table_name = MemoryContextStrdup(tbl_cache_ctx, name); info->pkey_attno = MemoryContextAlloc(tbl_cache_ctx, info->n_pkeys * sizeof(int)); for (i = 0; i < SPI_processed; i++) { row = SPI_tuptable->vals[i]; attno = DatumGetInt16(SPI_getbinval(row, desc, 1, &isnull)); name = SPI_getvalue(row, desc, 2); info->pkey_attno[i] = attno; if (i > 0) appendStringInfoChar(pkeys, ','); appendStringInfoString(pkeys, name); } info->pkey_list = MemoryContextStrdup(tbl_cache_ctx, pkeys->data); info->tg_cache = NULL; }
/* * get_cache: get/set cached info */ static VariantCache * get_cache(FunctionCallInfo fcinfo, VariantInt vi, IOFuncSelector func) { VariantCache *cache = (VariantCache *) fcinfo->flinfo->fn_extra; /* IO type should always be the same, so assert that. But if we're not an assert build just force a reset. */ if (cache != NULL && cache->typid == vi->typid && cache->typmod == vi->typmod && cache->IOfunc != func) { Assert(false); cache->typid ^= vi->typid; } if (cache == NULL || cache->typid != vi->typid || cache->typmod != vi->typmod) { char typDelim; Oid typIoFunc; /* * We can get different OIDs in one call, so don't needlessly palloc */ if (cache == NULL) cache = (VariantCache *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(VariantCache)); cache->typid = vi->typid; cache->typmod = vi->typmod; cache->IOfunc = func; get_type_io_data(cache->typid, func, &cache->typlen, &cache->typbyval, &cache->typalign, &typDelim, &cache->typioparam, &typIoFunc); fmgr_info_cxt(typIoFunc, &cache->proc, fcinfo->flinfo->fn_mcxt); if (func == IOFunc_output || func == IOFunc_send) { cache->formatted_name = MemoryContextStrdup(fcinfo->flinfo->fn_mcxt, format_type_with_typemod(cache->typid, cache->typmod)); } else cache->formatted_name="\0"; fcinfo->flinfo->fn_extra = (void *) cache; } return cache; }
/* * Insert the procedure into the Python interpreter */ void PLy_procedure_compile(PLyProcedure *proc, const char *src) { PyObject *crv = NULL; char *msrc; proc->globals = PyDict_Copy(PLy_interp_globals); /* * SD is private preserved data between calls. GD is global data shared by * all functions */ proc->statics = PyDict_New(); if (!proc->statics) PLy_elog(ERROR, NULL); PyDict_SetItemString(proc->globals, "SD", proc->statics); /* * insert the function code into the interpreter */ msrc = PLy_procedure_munge_source(proc->pyname, src); /* Save the mangled source for later inclusion in tracebacks */ proc->src = MemoryContextStrdup(proc->mcxt, msrc); crv = PyRun_String(msrc, Py_file_input, proc->globals, NULL); pfree(msrc); if (crv != NULL) { int clen; char call[NAMEDATALEN + 256]; Py_DECREF(crv); /* * compile a call to the function */ clen = snprintf(call, sizeof(call), "%s()", proc->pyname); if (clen < 0 || clen >= sizeof(call)) elog(ERROR, "string would overflow buffer"); proc->code = Py_CompileString(call, "<string>", Py_eval_input); if (proc->code != NULL) return; } if (proc->proname) PLy_elog(ERROR, "could not compile PL/Python function \"%s\"", proc->proname); else PLy_elog(ERROR, "could not compile anonymous PL/Python code block"); }
/* * As for pljavaCheckExtension, livecheck == null when called from _PG_init * (when the real questions are whether PL/Java itself is being loaded, from * what path, and whether or not as an extension). When livecheck is not null, * PL/Java is already alive and the caller wants to know if an extension is * being created for some other reason. That wouldn't even involve this * function, except for the need to work around creating_extension visibility * on Windows. So if livecheck isn't null, this function only needs to proceed * as far as the CREATING_EXTENSION_HACK and then return. */ static void checkLoadPath( bool *livecheck) { List *l; Node *ut; LoadStmt *ls; #ifndef CREATING_EXTENSION_HACK if ( NULL != livecheck ) return; #endif if ( NULL == ActivePortal ) return; l = ActivePortal->stmts; if ( NULL == l ) return; if ( 1 < list_length( l) ) elog(DEBUG2, "ActivePortal lists %d statements", list_length( l)); ut = (Node *)linitial(l); if ( NULL == ut ) { elog(DEBUG2, "got null for first statement from ActivePortal"); return; } if ( T_LoadStmt != nodeTag(ut) ) #ifdef CREATING_EXTENSION_HACK if ( T_CreateExtensionStmt == nodeTag(ut) ) { if ( NULL != livecheck ) { *livecheck = true; return; } getExtensionLoadPath(); if ( NULL != pljavaLoadPath ) pljavaLoadingAsExtension = true; } #endif return; if ( NULL != livecheck ) return; ls = (LoadStmt *)ut; if ( NULL == ls->filename ) { elog(DEBUG2, "got null for a LOAD statement's filename"); return; } pljavaLoadPath = (char const *)MemoryContextStrdup(TopMemoryContext, ls->filename); }
Datum pgq_set_connection_context(PG_FUNCTION_ARGS) { char *ctx; if (current_context) pfree(current_context); current_context = NULL; if (PG_NARGS() > 0 && !PG_ARGISNULL(0)) { ctx = DatumGetCString(DirectFunctionCall1(textout, PG_GETARG_DATUM(0))); current_context = MemoryContextStrdup(TopMemoryContext, ctx); pfree(ctx); } PG_RETURN_VOID(); }
/* Subroutine for cache_locale_time(). */ static void cache_single_time(char **dst, const char *format, const struct tm * tm) { char buf[MAX_L10N_DATA]; char *ptr; /* * MAX_L10N_DATA is sufficient buffer space for every known locale, and * POSIX defines no strftime() errors. (Buffer space exhaustion is not an * error.) An implementation might report errors (e.g. ENOMEM) by * returning 0 (or, less plausibly, a negative value) and setting errno. * Report errno just in case the implementation did that, but clear it in * advance of the call so we don't emit a stale, unrelated errno. */ errno = 0; if (strftime(buf, MAX_L10N_DATA, format, tm) <= 0) elog(ERROR, "strftime(%s) failed: %m", format); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (*dst) pfree(*dst); *dst = ptr; }
/* * Update the lc_time localization cache variables if needed. */ void cache_locale_time(void) { char *save_lc_time; time_t timenow; struct tm *timeinfo; char buf[MAX_L10N_DATA]; char *ptr; int i; #ifdef WIN32 char *save_lc_ctype; #endif /* did we do this already? */ if (CurrentLCTimeValid) return; elog(DEBUG3, "cache_locale_time() executed; locale: \"%s\"", locale_time); /* save user's value of time locale */ save_lc_time = setlocale(LC_TIME, NULL); if (save_lc_time) save_lc_time = pstrdup(save_lc_time); #ifdef WIN32 /* * On WIN32, there is no way to get locale-specific time values in a * specified locale, like we do for monetary/numeric. We can only get * CP_ACP (see strftime_win32) or UTF16. Therefore, we get UTF16 and * convert it to the database locale. However, wcsftime() internally uses * LC_CTYPE, so we set it here. See the WIN32 comment near the top of * PGLC_localeconv(). */ /* save user's value of ctype locale */ save_lc_ctype = setlocale(LC_CTYPE, NULL); if (save_lc_ctype) save_lc_ctype = pstrdup(save_lc_ctype); /* use lc_time to set the ctype */ setlocale(LC_CTYPE, locale_time); #endif setlocale(LC_TIME, locale_time); timenow = time(NULL); timeinfo = localtime(&timenow); /* localized days */ for (i = 0; i < 7; i++) { timeinfo->tm_wday = i; strftime(buf, MAX_L10N_DATA, "%a", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_abbrev_days[i]) pfree(localized_abbrev_days[i]); localized_abbrev_days[i] = ptr; strftime(buf, MAX_L10N_DATA, "%A", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_full_days[i]) pfree(localized_full_days[i]); localized_full_days[i] = ptr; } /* localized months */ for (i = 0; i < 12; i++) { timeinfo->tm_mon = i; timeinfo->tm_mday = 1; /* make sure we don't have invalid date */ strftime(buf, MAX_L10N_DATA, "%b", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_abbrev_months[i]) pfree(localized_abbrev_months[i]); localized_abbrev_months[i] = ptr; strftime(buf, MAX_L10N_DATA, "%B", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_full_months[i]) pfree(localized_full_months[i]); localized_full_months[i] = ptr; } /* try to restore internal settings */ if (save_lc_time) { if (!setlocale(LC_TIME, save_lc_time)) elog(WARNING, "failed to restore old locale"); pfree(save_lc_time); } #ifdef WIN32 /* try to restore internal ctype settings */ if (save_lc_ctype) { if (!setlocale(LC_CTYPE, save_lc_ctype)) elog(WARNING, "failed to restore old locale"); pfree(save_lc_ctype); } #endif CurrentLCTimeValid = true; }
/* * Implements the 'EXECUTE' utility statement. * * Note: this is one of very few places in the code that needs to deal with * two query strings at once. The passed-in queryString is that of the * EXECUTE, which we might need for error reporting while processing the * parameter expressions. The query_string that we copy from the plan * source is that of the original PREPARE. */ void ExecuteQuery(ExecuteStmt *stmt, const char *queryString, ParamListInfo params, DestReceiver *dest, char *completionTag) { PreparedStatement *entry; List *stmt_list; MemoryContext qcontext; ParamListInfo paramLI = NULL; EState *estate = NULL; Portal portal; /* Look it up in the hash table */ entry = FetchPreparedStatement(stmt->name, true); qcontext = entry->context; /* Evaluate parameters, if any */ if (entry->argtype_list != NIL) { /* * Need an EState to evaluate parameters; must not delete it till end * of query, in case parameters are pass-by-reference. */ estate = CreateExecutorState(); estate->es_param_list_info = params; paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list); } /* Create a new portal to run the query in */ portal = CreateNewPortal(); /* Don't display the portal in pg_cursors, it is for internal use only */ portal->visible = false; /* Plan the query. If this is a CTAS, copy the "into" information into * the query so that we construct the plan correctly. Else the table * might not be created on the segments. (MPP-8135) */ { List *query_list = copyObject(entry->query_list); /* planner scribbles on query tree :( */ if ( stmt->into ) { Query *query = (Query*)linitial(query_list); Assert(IsA(query, Query) && query->intoClause == NULL); query->intoClause = copyObject(stmt->into); } stmt_list = pg_plan_queries(query_list, paramLI, false, QRL_ONCE); } /* * For CREATE TABLE / AS EXECUTE, make a copy of the stored query so that * we can modify its destination (yech, but this has always been ugly). * For regular EXECUTE we can just use the stored query where it sits, * since the executor is read-only. */ if (stmt->into) { MemoryContext oldContext; PlannedStmt *pstmt; if (list_length(stmt_list) != 1) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("prepared statement is not a SELECT"))); oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); stmt_list = copyObject(stmt_list); qcontext = PortalGetHeapMemory(portal); pstmt = (PlannedStmt *) linitial(stmt_list); pstmt->qdContext = qcontext; if (pstmt->commandType != CMD_SELECT) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("prepared statement is not a SELECT"), errOmitLocation(true))); pstmt->intoClause = copyObject(stmt->into); /* XXX Is it legitimate to assign a constant default policy without * even checking the relation? */ pstmt->intoPolicy = palloc0(sizeof(GpPolicy)- sizeof(pstmt->intoPolicy->attrs) + 255 * sizeof(pstmt->intoPolicy->attrs[0])); pstmt->intoPolicy->nattrs = 0; pstmt->intoPolicy->ptype = POLICYTYPE_PARTITIONED; pstmt->intoPolicy->bucketnum = GetRelOpt_bucket_num_fromRangeVar(stmt->into->rel, GetRandomDistPartitionNum()); MemoryContextSwitchTo(oldContext); } /* Copy the plan's saved query string into the portal's memory */ Assert(entry->query_string != NULL); char *query_string = MemoryContextStrdup(PortalGetHeapMemory(portal), entry->query_string); PortalDefineQuery(portal, NULL, query_string, entry->sourceTag, entry->commandTag, stmt_list, qcontext); create_filesystem_credentials(portal); /* * Run the portal to completion. */ PortalStart(portal, paramLI, ActiveSnapshot, savedSeqServerHost, savedSeqServerPort); (void) PortalRun(portal, FETCH_ALL, true, /* Effectively always top level. */ dest, dest, completionTag); PortalDrop(portal, false); if (estate) FreeExecutorState(estate); /* No need to pfree other memory, MemoryContext will be reset */ }
/* * There are a few ways to arrive in the initsequencer. * 1. From _PG_init (called exactly once when the library is loaded for ANY * reason). * 1a. Because of the command LOAD 'libraryname'; * This case can be distinguished because _PG_init will have found the * LOAD command and saved the 'libraryname' in pljavaLoadPath. * 1b. Because of a CREATE FUNCTION naming this library. pljavaLoadPath will * be NULL. * 1c. By the first actual use of a PL/Java function, causing this library * to be loaded. pljavaLoadPath will be NULL. The called function's Oid * will be available to the call handler once we return from _PG_init, * but it isn't (easily) available here. * 2. From the call handler, if initialization isn't complete yet. That can only * mean something failed in the earlier call to _PG_init, and whatever it was * is highly likely to fail again. That may lead to the untidyness of * duplicated diagnostic messages, but for now I like the belt-and-suspenders * approach of making sure the init sequence gets as many chances as possible * to succeed. * 3. From a GUC assign hook, if the user has updated a setting that might allow * initialization to succeed. It resumes from where it left off. * * In all cases, the sequence must progress as far as starting the VM and * initializing the PL/Java classes. In all cases except 1a, that's enough, * assuming the language handlers and schema have all been set up already (or, * in case 1b, the user is intent on setting them up explicitly). * * In case 1a, we can go ahead and test for, and create, the schema, functions, * and language entries as needed, using pljavaLoadPath as the library path * if creating the language handler functions. One-stop shopping. (The presence * of pljavaLoadPath in any of the other cases, such as resumption by an assign * hook, indicates it is really a continuation of case 1a.) */ static void initsequencer(enum initstage is, bool tolerant) { JVMOptList optList; Invocation ctx; jint JNIresult; char *greeting; switch (is) { case IS_FORMLESS_VOID: initstage = IS_GUCS_REGISTERED; case IS_GUCS_REGISTERED: libjvmlocation = strdup("libjvm.so"); initstage = IS_PLJAVA_ENABLED; case IS_PLJAVA_ENABLED: libjvm_handle = pg_dlopen(libjvmlocation); if ( NULL == libjvm_handle ) { ereport(ERROR, ( errmsg("Cannot load libjvm.so library, check that it is available in LD_LIBRARY_PATH"), errdetail("%s", (char *)pg_dlerror()))); goto check_tolerant; } initstage = IS_CAND_JVMOPENED; case IS_CAND_JVMOPENED: pljava_createvm = (jint (JNICALL *)(JavaVM **, void **, void *)) pg_dlsym(libjvm_handle, "JNI_CreateJavaVM"); if ( NULL == pljava_createvm ) { /* * If it hasn't got the symbol, it can't be the right * library, so close/unload it so another can be tried. * Format the dlerror string first: dlclose may clobber it. */ char *dle = MemoryContextStrdup(ErrorContext, pg_dlerror()); pg_dlclose(libjvm_handle); initstage = IS_CAND_JVMLOCATION; ereport(ERROR, ( errmsg("Cannot start Java VM"), errdetail("%s", dle), errhint("Check that libjvm.so is available in LD_LIBRARY_PATH"))); goto check_tolerant; } initstage = IS_CREATEVM_SYM_FOUND; case IS_CREATEVM_SYM_FOUND: s_javaLogLevel = INFO; checkIntTimeType(); HashMap_initialize(); /* creates things in TopMemoryContext */ #ifdef PLJAVA_DEBUG /* Hard setting for debug. Don't forget to recompile... */ pljava_debug = 1; #endif initstage = IS_MISC_ONCE_DONE; case IS_MISC_ONCE_DONE: JVMOptList_init(&optList); /* uses CurrentMemoryContext */ seenVisualVMName = false; addUserJVMOptions(&optList); if ( ! seenVisualVMName ) JVMOptList_addVisualVMName(&optList); JVMOptList_add(&optList, "vfprintf", (void*)my_vfprintf, true); #ifndef GCJ JVMOptList_add(&optList, "-Xrs", 0, true); #endif effectiveClassPath = getClassPath("-Djava.class.path="); if(effectiveClassPath != 0) { JVMOptList_add(&optList, effectiveClassPath, 0, true); } initstage = IS_JAVAVM_OPTLIST; case IS_JAVAVM_OPTLIST: JNIresult = initializeJavaVM(&optList); /* frees the optList */ if( JNI_OK != JNIresult ) { initstage = IS_MISC_ONCE_DONE; /* optList has been freed */ StaticAssertStmt(sizeof(jint) <= sizeof(long int), "jint wider than long int?!"); ereport(WARNING, (errmsg("failed to create Java virtual machine"), errdetail("JNI_CreateJavaVM returned an error code: %ld", (long int)JNIresult), jvmStartedAtLeastOnce ? errhint("Because an earlier attempt during this session " "did start a VM before failing, this probably means your " "Java runtime environment does not support more than one " "VM creation per session. You may need to exit this " "session and start a new one.") : 0)); goto check_tolerant; } jvmStartedAtLeastOnce = true; elog(DEBUG2, "successfully created Java virtual machine"); initstage = IS_JAVAVM_STARTED; case IS_JAVAVM_STARTED: #ifdef USE_PLJAVA_SIGHANDLERS pqsignal(SIGINT, pljavaStatementCancelHandler); pqsignal(SIGTERM, pljavaDieHandler); #endif /* Register an on_proc_exit handler that destroys the VM */ on_proc_exit(_destroyJavaVM, 0); initstage = IS_SIGHANDLERS; case IS_SIGHANDLERS: Invocation_pushBootContext(&ctx); PG_TRY(); { initPLJavaClasses(); initJavaSession(); Invocation_popBootContext(); initstage = IS_PLJAVA_FOUND; } PG_CATCH(); { MemoryContextSwitchTo(ctx.upperContext); /* leave ErrorContext */ Invocation_popBootContext(); initstage = IS_MISC_ONCE_DONE; /* We can't stay here... */ if ( tolerant ) reLogWithChangedLevel(WARNING); /* so xact is not aborted */ else { EmitErrorReport(); /* no more unwinding, just log it */ /* Seeing an ERROR emitted to the log, without leaving the * transaction aborted, would violate the principle of least * astonishment. But at check_tolerant below, another ERROR will * be thrown immediately, so the transaction effect will be as * expected and this ERROR will contribute information beyond * what is in the generic one thrown down there. */ FlushErrorState(); } } PG_END_TRY(); if ( IS_PLJAVA_FOUND != initstage ) { /* JVM initialization failed for some reason. Destroy * the VM if it exists. Perhaps the user will try * fixing the pljava.classpath and make a new attempt. */ ereport(WARNING, ( errmsg("failed to load initial PL/Java classes"), errhint("The most common reason is that \"pljava_classpath\" " "needs to be set, naming the proper \"pljava.jar\" file.") )); _destroyJavaVM(0, 0); goto check_tolerant; } case IS_PLJAVA_FOUND: greeting = InstallHelper_hello(); ereport(NULL != pljavaLoadPath ? NOTICE : DEBUG1, ( errmsg("PL/Java loaded"), errdetail("versions:\n%s", greeting))); pfree(greeting); if ( NULL != pljavaLoadPath ) InstallHelper_groundwork(); /* sqlj schema, language handlers, ...*/ initstage = IS_COMPLETE; case IS_COMPLETE: pljavaLoadingAsExtension = false; if ( alteredSettingsWereNeeded ) { /* Use this StringInfoData to conditionally construct part of the * hint string suggesting ALTER DATABASE ... SET ... FROM CURRENT * provided the server is >= 9.2 where that will actually work. * In 9.3, psprintf appeared, which would make this all simpler, * but if 9.3+ were all that had to be supported, this would all * be moot anyway. Doing the initStringInfo inside the ereport * ensures the string is allocated in ErrorContext and won't leak. * Don't remove the extra parens grouping * (initStringInfo, appendStringInfo, errhint) ... with the parens, * that's a comma expression, which is sequenced; without them, they * are just function parameters with evaluation order unknown. */ StringInfoData buf; #if PG_VERSION_NUM >= 90200 #define MOREHINT \ appendStringInfo(&buf, \ "using ALTER DATABASE %s SET ... FROM CURRENT or ", \ pljavaDbName()), #else #define MOREHINT #endif ereport(NOTICE, ( errmsg("PL/Java successfully started after adjusting settings"), (initStringInfo(&buf), MOREHINT errhint("The settings that worked should be saved (%s" "in the \"%s\" file). For a reminder of what has been set, " "try: SELECT name, setting FROM pg_settings WHERE name LIKE" " 'pljava.%%' AND source = 'session'", buf.data, superuser() ? PG_GETCONFIGOPTION("config_file") : "postgresql.conf")))); #undef MOREHINT if ( loadAsExtensionFailed ) { ereport(NOTICE, (errmsg( "PL/Java load successful after failed CREATE EXTENSION"), errdetail( "PL/Java is now installed, but not as an extension."), errhint( "To correct that, either COMMIT or ROLLBACK, make sure " "the working settings are saved, exit this session, and " "in a new session, either: " "1. if committed, run " "\"CREATE EXTENSION pljava FROM unpackaged\", or 2. " "if rolled back, simply \"CREATE EXTENSION pljava\" again." ))); } } return; default: ereport(ERROR, ( errmsg("cannot set up PL/Java"), errdetail( "An unexpected stage was reached in the startup sequence."), errhint( "Please report the circumstances to the PL/Java maintainers.") )); } check_tolerant: if ( pljavaLoadingAsExtension ) { tolerant = false; loadAsExtensionFailed = true; pljavaLoadingAsExtension = false; } if ( !tolerant ) { ereport(ERROR, ( errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg( "cannot use PL/Java before successfully completing its setup"), errhint( "Check the log for messages closely preceding this one, " "detailing what step of setup failed and what will be needed, " "probably setting one of the \"pljava.\" configuration " "variables, to complete the setup. If there is not enough " "help in the log, try again with different settings for " "\"log_min_messages\" or \"log_error_verbosity\"."))); } }
static PyObject * PLy_cursor_plan(PyObject *ob, PyObject *args) { PLyCursorObject *cursor; volatile int nargs; int i; PLyPlanObject *plan; volatile MemoryContext oldcontext; volatile ResourceOwner oldowner; if (args) { if (!PySequence_Check(args) || PyString_Check(args) || PyUnicode_Check(args)) { PLy_exception_set(PyExc_TypeError, "plpy.cursor takes a sequence as its second argument"); return NULL; } nargs = PySequence_Length(args); } else nargs = 0; plan = (PLyPlanObject *) ob; if (nargs != plan->nargs) { char *sv; PyObject *so = PyObject_Str(args); if (!so) PLy_elog(ERROR, "could not execute plan"); sv = PyString_AsString(so); PLy_exception_set_plural(PyExc_TypeError, "Expected sequence of %d argument, got %d: %s", "Expected sequence of %d arguments, got %d: %s", plan->nargs, plan->nargs, nargs, sv); Py_DECREF(so); return NULL; } if ((cursor = PyObject_New(PLyCursorObject, &PLy_CursorType)) == NULL) return NULL; cursor->portalname = NULL; cursor->closed = false; cursor->mcxt = AllocSetContextCreate(TopMemoryContext, "PL/Python cursor context", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); PLy_typeinfo_init(&cursor->result, cursor->mcxt); oldcontext = CurrentMemoryContext; oldowner = CurrentResourceOwner; PLy_spi_subtransaction_begin(oldcontext, oldowner); PG_TRY(); { PLyExecutionContext *exec_ctx = PLy_current_execution_context(); Portal portal; char *volatile nulls; volatile int j; if (nargs > 0) nulls = palloc(nargs * sizeof(char)); else nulls = NULL; for (j = 0; j < nargs; j++) { PyObject *elem; elem = PySequence_GetItem(args, j); if (elem != Py_None) { PG_TRY(); { plan->values[j] = plan->args[j].out.d.func(&(plan->args[j].out.d), -1, elem); } PG_CATCH(); { Py_DECREF(elem); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(elem); nulls[j] = ' '; } else { Py_DECREF(elem); plan->values[j] = InputFunctionCall(&(plan->args[j].out.d.typfunc), NULL, plan->args[j].out.d.typioparam, -1); nulls[j] = 'n'; } } portal = SPI_cursor_open(NULL, plan->plan, plan->values, nulls, exec_ctx->curr_proc->fn_readonly); if (portal == NULL) elog(ERROR, "SPI_cursor_open() failed: %s", SPI_result_code_string(SPI_result)); cursor->portalname = MemoryContextStrdup(cursor->mcxt, portal->name); PLy_spi_subtransaction_commit(oldcontext, oldowner); } PG_CATCH(); { int k; /* cleanup plan->values array */ for (k = 0; k < nargs; k++) { if (!plan->args[k].out.d.typbyval && (plan->values[k] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[k])); plan->values[k] = PointerGetDatum(NULL); } } Py_DECREF(cursor); PLy_spi_subtransaction_abort(oldcontext, oldowner); return NULL; } PG_END_TRY(); for (i = 0; i < nargs; i++) { if (!plan->args[i].out.d.typbyval && (plan->values[i] != PointerGetDatum(NULL))) { pfree(DatumGetPointer(plan->values[i])); plan->values[i] = PointerGetDatum(NULL); } } Assert(cursor->portalname != NULL); return (PyObject *) cursor; }
void AppendOnlyMirrorResyncEofs_Merge(RelFileNode *relFileNode, int32 segmentFileNum, int nestLevel, /* Transaction nesting level. */ char *relationName, ItemPointer persistentTid, int64 persistentSerialNum, bool mirrorCatchupRequired, MirrorDataLossTrackingState mirrorDataLossTrackingState, int64 mirrorDataLossTrackingSessionNum, int64 mirrorNewEof) { int64 previousMirrorNewEof = 0; AppendOnlyMirrorResyncEofsKey key; AppendOnlyMirrorResyncEofs *entry; bool found; if (AppendOnlyMirrorResyncEofsTable == NULL) AppendOnlyMirrorResyncEofs_HashTableInit(); AppendOnlyMirrorResyncEofs_InitKey( &key, relFileNode, segmentFileNum, nestLevel); entry = (AppendOnlyMirrorResyncEofs *) hash_search(AppendOnlyMirrorResyncEofsTable, (void *) &key, HASH_ENTER, &found); if (!found) { entry->relationName = MemoryContextStrdup(TopMemoryContext, relationName); entry->persistentSerialNum = persistentSerialNum; entry->persistentTid = *persistentTid; entry->didIncrementCommitCount = false; entry->isDistributedTransaction = false; entry->gid[0] = '\0'; entry->mirrorCatchupRequired = mirrorCatchupRequired; entry->mirrorDataLossTrackingState = mirrorDataLossTrackingState; entry->mirrorDataLossTrackingSessionNum = mirrorDataLossTrackingSessionNum; entry->mirrorNewEof = mirrorNewEof; } else { previousMirrorNewEof = entry->mirrorNewEof; /* * UNDONE: What is the purpose of this IF stmt? Shouldn't we always * set the new EOF? */ if (mirrorNewEof > entry->mirrorNewEof) entry->mirrorNewEof = mirrorNewEof; /* * We adopt the newer FileRep state because we accurately track the * state of mirror data. For example, the first write session might * have had loss because the mirror was down. But then the second * write session discovered we were in sync and copied both the first * and second write session to the mirror and flushed it. */ entry->mirrorCatchupRequired = mirrorCatchupRequired; entry->mirrorDataLossTrackingState = mirrorDataLossTrackingState; entry->mirrorDataLossTrackingSessionNum = mirrorDataLossTrackingSessionNum; } if (Debug_persistent_print || Debug_persistent_appendonly_commit_count_print) elog(Persistent_DebugPrintLevel(), "Storage Manager: %s Append-Only mirror resync eofs entry: %u/%u/%u, segment file #%d, relation name '%s' (transaction nest level %d, persistent TID %s, persistent serial number " INT64_FORMAT ", " "mirror data loss tracking (state '%s', session num " INT64_FORMAT "), " "previous mirror new EOF " INT64_FORMAT ", input mirror new EOF " INT64_FORMAT ", saved mirror new EOF " INT64_FORMAT ")", (found ? "Merge" : "New"), entry->key.relFileNode.spcNode, entry->key.relFileNode.dbNode, entry->key.relFileNode.relNode, entry->key.segmentFileNum, (entry->relationName == NULL ? "<null>" : entry->relationName), entry->key.nestLevel, ItemPointerToString(&entry->persistentTid), entry->persistentSerialNum, MirrorDataLossTrackingState_Name(mirrorDataLossTrackingState), mirrorDataLossTrackingSessionNum, previousMirrorNewEof, mirrorNewEof, entry->mirrorNewEof); }
/* * This is a mocked version of pstrdup to be used in ExecWorkFile_Create. * It asserts that it is executed in the TopMemoryContext. */ char *execWorkfile_pstrdup_mock(const char *string) { assert_int_equal(CurrentMemoryContext, TopMemoryContext); return MemoryContextStrdup(CurrentMemoryContext, string); }
/* * Update the lc_time localization cache variables if needed. */ void cache_locale_time(void) { char *save_lc_time; time_t timenow; struct tm *timeinfo; char buf[MAX_L10N_DATA]; char *ptr; int i; #ifdef WIN32 char *save_lc_ctype; #endif #ifdef WIN32 char *save_lc_ctype; #endif /* did we do this already? */ if (CurrentLCTimeValid) return; elog(DEBUG3, "cache_locale_time() executed; locale: \"%s\"", locale_time); #ifdef WIN32 /* set user's value of ctype locale */ save_lc_ctype = setlocale(LC_CTYPE, NULL); if (save_lc_ctype) save_lc_ctype = pstrdup(save_lc_ctype); setlocale(LC_CTYPE, locale_time); #endif /* set user's value of time locale */ save_lc_time = setlocale(LC_TIME, NULL); if (save_lc_time) save_lc_time = pstrdup(save_lc_time); setlocale(LC_TIME, locale_time); timenow = time(NULL); timeinfo = localtime(&timenow); /* localized days */ for (i = 0; i < 7; i++) { timeinfo->tm_wday = i; strftime(buf, MAX_L10N_DATA, "%a", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_abbrev_days[i]) pfree(localized_abbrev_days[i]); localized_abbrev_days[i] = ptr; strftime(buf, MAX_L10N_DATA, "%A", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_full_days[i]) pfree(localized_full_days[i]); localized_full_days[i] = ptr; } /* localized months */ for (i = 0; i < 12; i++) { timeinfo->tm_mon = i; timeinfo->tm_mday = 1; /* make sure we don't have invalid date */ strftime(buf, MAX_L10N_DATA, "%b", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_abbrev_months[i]) pfree(localized_abbrev_months[i]); localized_abbrev_months[i] = ptr; strftime(buf, MAX_L10N_DATA, "%B", timeinfo); ptr = MemoryContextStrdup(TopMemoryContext, buf); if (localized_full_months[i]) pfree(localized_full_months[i]); localized_full_months[i] = ptr; } /* try to restore internal settings */ if (save_lc_time) { setlocale(LC_TIME, save_lc_time); pfree(save_lc_time); } #ifdef WIN32 /* try to restore internal ctype settings */ if (save_lc_ctype) { setlocale(LC_CTYPE, save_lc_ctype); pfree(save_lc_ctype); } #endif CurrentLCTimeValid = true; }
char * pstrdup(const char *in) { return MemoryContextStrdup(CurrentMemoryContext, in); }
/* * StartPlacementListConnection returns a connection to a remote node suitable for * a placement accesses (SELECT, DML, DDL) or throws an error if no suitable * connection can be established if would cause a self-deadlock or consistency * violation. */ MultiConnection * StartPlacementListConnection(uint32 flags, List *placementAccessList, const char *userName) { char *freeUserName = NULL; ListCell *placementAccessCell = NULL; List *placementEntryList = NIL; ListCell *placementEntryCell = NULL; MultiConnection *chosenConnection = NULL; if (userName == NULL) { userName = freeUserName = CurrentUserName(); } chosenConnection = FindPlacementListConnection(flags, placementAccessList, userName, &placementEntryList); if (chosenConnection == NULL) { /* use the first placement from the list to extract nodename and nodeport */ ShardPlacementAccess *placementAccess = (ShardPlacementAccess *) linitial(placementAccessList); ShardPlacement *placement = placementAccess->placement; char *nodeName = placement->nodeName; int nodePort = placement->nodePort; /* * No suitable connection in the placement->connection mapping, get one from * the node->connection pool. */ chosenConnection = StartNodeUserDatabaseConnection(flags, nodeName, nodePort, userName, NULL); if (flags & CONNECTION_PER_PLACEMENT && ConnectionAccessedDifferentPlacement(chosenConnection, placement)) { /* * Cached connection accessed a non-co-located placement in the same * table or co-location group, while the caller asked for a connection * per placement. Open a new connection instead. * * We use this for situations in which we want to use a different * connection for every placement, such as COPY. If we blindly returned * a cached conection that already modified a different, non-co-located * placement B in the same table or in a table with the same co-location * ID as the current placement, then we'd no longer able to write to * placement B later in the COPY. */ chosenConnection = StartNodeUserDatabaseConnection(flags | FORCE_NEW_CONNECTION, nodeName, nodePort, userName, NULL); Assert(!ConnectionAccessedDifferentPlacement(chosenConnection, placement)); } } /* * Now that a connection has been chosen, initialise or update the connection * references for all placements. */ forboth(placementAccessCell, placementAccessList, placementEntryCell, placementEntryList) { ShardPlacementAccess *placementAccess = (ShardPlacementAccess *) lfirst(placementAccessCell); ShardPlacementAccessType accessType = placementAccess->accessType; ConnectionPlacementHashEntry *placementEntry = (ConnectionPlacementHashEntry *) lfirst(placementEntryCell); ConnectionReference *placementConnection = placementEntry->primaryConnection; if (placementConnection->connection == chosenConnection) { /* using the connection that was already assigned to the placement */ } else if (placementConnection->connection == NULL) { /* placement does not have a connection assigned yet */ placementConnection->connection = chosenConnection; placementConnection->hadDDL = false; placementConnection->hadDML = false; placementConnection->userName = MemoryContextStrdup(TopTransactionContext, userName); placementConnection->placementId = placementAccess->placement->placementId; /* record association with connection */ dlist_push_tail(&chosenConnection->referencedPlacements, &placementConnection->connectionNode); } else { /* using a different connection than the one assigned to the placement */ if (accessType != PLACEMENT_ACCESS_SELECT) { /* * We previously read from the placement, but now we're writing to * it (if we had written to the placement, we would have either chosen * the same connection, or errored out). Update the connection reference * to point to the connection used for writing. We don't need to remember * the existing connection since we won't be able to reuse it for * accessing the placement. However, we do register that it exists in * hasSecondaryConnections. */ placementConnection->connection = chosenConnection; placementConnection->userName = MemoryContextStrdup(TopTransactionContext, userName); Assert(!placementConnection->hadDDL); Assert(!placementConnection->hadDML); /* record association with connection */ dlist_push_tail(&chosenConnection->referencedPlacements, &placementConnection->connectionNode); } /* * There are now multiple connections that read from the placement * and DDL commands are forbidden. */ placementEntry->hasSecondaryConnections = true; if (placementEntry->colocatedEntry != NULL) { /* we also remember this for co-located placements */ placementEntry->colocatedEntry->hasSecondaryConnections = true; } } /* * Remember that we used the current connection for writes. */ if (accessType == PLACEMENT_ACCESS_DDL) { placementConnection->hadDDL = true; } if (accessType == PLACEMENT_ACCESS_DML) { placementConnection->hadDML = true; } }