/* * quote_literal_cstr - * returns a properly quoted literal */ char * quote_literal_cstr(const char *rawstr) { char *result; int len; int newlen; len = strlen(rawstr); /* We make a worst-case result area; wasting a little space is OK */ result = palloc(len * 2 + 3); newlen = quote_literal_internal(result, rawstr, len); result[newlen] = '\0'; return result; }
/* * quote_literal - * returns a properly quoted literal */ Datum quote_literal(PG_FUNCTION_ARGS) { text *t = PG_GETARG_TEXT_P(0); text *result; char *cp1; char *cp2; int len; len = VARSIZE(t) - VARHDRSZ; /* We make a worst-case result area; wasting a little space is OK */ result = (text *) palloc(len * 2 + 3 + VARHDRSZ); cp1 = VARDATA(t); cp2 = VARDATA(result); SET_VARSIZE(result, VARHDRSZ + quote_literal_internal(cp2, cp1, len)); PG_RETURN_TEXT_P(result); }
/* * Delete error log of the specified relation. This returns true from master * iif all segments and master find the relation. */ Datum gp_truncate_error_log(PG_FUNCTION_ARGS) { text *relname; char *relname_str; RangeVar *relrv; Oid relid; bool allResults = true; relname = PG_GETARG_TEXT_P(0); relname_str = text_to_cstring(relname); if (strcmp(relname_str, "*.*") == 0) { /* * Only superuser is allowed to delete log files across database. */ if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to delete all error log files")))); ErrorLogDelete(InvalidOid, InvalidOid); } else if (strcmp(relname_str, "*") == 0) { /* * Database owner can delete error log files. */ if (!pg_database_ownercheck(MyDatabaseId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_DATABASE, get_database_name(MyDatabaseId)); ErrorLogDelete(MyDatabaseId, InvalidOid); } else { AclResult aclresult; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); relid = RangeVarGetRelid(relrv, true); /* Return false if the relation does not exist. */ if (!OidIsValid(relid)) PG_RETURN_BOOL(false); /* * Allow only the table owner to truncate error log. */ aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, relrv->relname); /* We don't care if this fails or not. */ ErrorLogDelete(MyDatabaseId, relid); } /* * Dispatch the work to segments. */ if (Gp_role == GP_ROLE_DISPATCH) { int i = 0; StringInfoData sql; CdbPgResults cdb_pgresults = {NULL, 0}; initStringInfo(&sql); appendStringInfo(&sql, "SELECT pg_catalog.gp_truncate_error_log(%s)", quote_literal_internal(text_to_cstring(relname))); CdbDispatchCommand(sql.data, DF_WITH_SNAPSHOT, &cdb_pgresults); for (i = 0; i < cdb_pgresults.numResults; i++) { Datum value; bool isnull; struct pg_result *pgresult = cdb_pgresults.pg_results[i]; if (PQresultStatus(pgresult) != PGRES_TUPLES_OK) { cdbdisp_clearCdbPgResults(&cdb_pgresults); ereport(ERROR, (errmsg("unexpected result from segment: %d", PQresultStatus(pgresult)))); } value = ResultToDatum(pgresult, 0, 0, boolin, &isnull); allResults &= (!isnull && DatumGetBool(value)); } cdbdisp_clearCdbPgResults(&cdb_pgresults); pfree(sql.data); } /* Return true iif all segments return true. */ PG_RETURN_BOOL(allResults); }
/* * gp_read_error_log * * Returns set of error log tuples. */ Datum gp_read_error_log(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; ReadErrorLogContext *context; HeapTuple tuple; Datum result; /* * First call setup */ if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; FILE *fp; text *relname; funcctx = SRF_FIRSTCALL_INIT(); relname = PG_GETARG_TEXT_P(0); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); context = palloc0(sizeof(ReadErrorLogContext)); funcctx->user_fctx = (void *) context; funcctx->tuple_desc = BlessTupleDesc(GetErrorTupleDesc()); /* * Though this function is usually executed on segment, we dispatch * the execution if it happens to be on QD, and combine the results * into one set. */ if (Gp_role == GP_ROLE_DISPATCH) { struct CdbPgResults cdb_pgresults = {NULL, 0}; StringInfoData sql; int i; initStringInfo(&sql); /* * construct SQL */ appendStringInfo(&sql, "SELECT * FROM pg_catalog.gp_read_error_log(%s) ", quote_literal_internal(text_to_cstring(relname))); CdbDispatchCommand(sql.data, DF_WITH_SNAPSHOT, &cdb_pgresults); for (i = 0; i < cdb_pgresults.numResults; i++) { if (PQresultStatus(cdb_pgresults.pg_results[i]) != PGRES_TUPLES_OK) { cdbdisp_clearCdbPgResults(&cdb_pgresults); elog(ERROR, "unexpected result from segment: %d", PQresultStatus(cdb_pgresults.pg_results[i])); } context->numTuples += PQntuples(cdb_pgresults.pg_results[i]); } pfree(sql.data); context->segResults = cdb_pgresults.pg_results; context->numSegResults = cdb_pgresults.numResults; } else { /* * In QE, read the error log. */ RangeVar *relrv; Oid relid; relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname)); relid = RangeVarGetRelid(relrv, true); /* * If the relation has gone, silently return no tuples. */ if (OidIsValid(relid)) { AclResult aclresult; /* * Requires SELECT priv to read error log. */ aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_CLASS, relrv->relname); ErrorLogFileName(context->filename, MyDatabaseId, relid); fp = AllocateFile(context->filename, "r"); context->fp = fp; } } MemoryContextSwitchTo(oldcontext); if (Gp_role != GP_ROLE_DISPATCH && !context->fp) { pfree(context); SRF_RETURN_DONE(funcctx); } } funcctx = SRF_PERCALL_SETUP(); context = (ReadErrorLogContext *) funcctx->user_fctx; /* * Read error log, probably on segments. We don't check Gp_role, however, * in case master also wants to read the file. */ if (context->fp) { pg_crc32 crc, written_crc; tuple = ErrorLogRead(context->fp, &written_crc); /* * CRC check. */ if (HeapTupleIsValid(tuple)) { INIT_CRC32C(crc); COMP_CRC32C(crc, tuple->t_data, tuple->t_len); FIN_CRC32C(crc); if (!EQ_CRC32C(crc, written_crc)) { elog(LOG, "incorrect checksum in error log %s", context->filename); tuple = NULL; } } /* * If we found a valid tuple, return it. Otherwise, fall through * in the DONE routine. */ if (HeapTupleIsValid(tuple)) { /* * We need to set typmod for the executor to understand * its type we just blessed. */ HeapTupleHeaderSetTypMod(tuple->t_data, funcctx->tuple_desc->tdtypmod); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } } /* * If we got results from dispatch, return all the tuples. */ while (context->currentResult < context->numSegResults) { Datum values[NUM_ERRORTABLE_ATTR]; bool isnull[NUM_ERRORTABLE_ATTR]; PGresult *segres = context->segResults[context->currentResult]; int row = context->currentRow; if (row >= PQntuples(segres)) { context->currentRow = 0; context->currentResult++; continue; } context->currentRow++; MemSet(isnull, false, sizeof(isnull)); values[0] = ResultToDatum(segres, row, 0, timestamptz_in, &isnull[0]); values[1] = ResultToDatum(segres, row, 1, textin, &isnull[1]); values[2] = ResultToDatum(segres, row, 2, textin, &isnull[2]); values[3] = ResultToDatum(segres, row, 3, int4in, &isnull[3]); values[4] = ResultToDatum(segres, row, 4, int4in, &isnull[4]); values[5] = ResultToDatum(segres, row, 5, textin, &isnull[5]); values[6] = ResultToDatum(segres, row, 6, textin, &isnull[6]); values[7] = ResultToDatum(segres, row, 7, byteain, &isnull[7]); tuple = heap_form_tuple(funcctx->tuple_desc, values, isnull); result = HeapTupleGetDatum(tuple); SRF_RETURN_NEXT(funcctx, result); } if (context->segResults != NULL) { int i; for (i = 0; i < context->numSegResults; i++) PQclear(context->segResults[i]); /* XXX: better to copy to palloc'ed area */ free(context->segResults); } /* * Close the file, if we have opened it. */ if (context->fp != NULL) { FreeFile(context->fp); context->fp = NULL; } SRF_RETURN_DONE(funcctx); }