/* * LookupOperName * Given a possibly-qualified operator name and exact input datatypes, * look up the operator. * * Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for * a postfix op. * * If the operator name is not schema-qualified, it is sought in the current * namespace search path. * * If the operator is not found, we return InvalidOid if noError is true, * else raise an error. pstate and location are used only to report the * error position; pass NULL/-1 if not available. */ Oid LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright, bool noError, int location) { Oid result; result = OpernameGetOprid(opername, oprleft, oprright); if (OidIsValid(result)) return result; /* we don't use op_error here because only an exact match is wanted */ if (!noError) { char oprkind; if (!OidIsValid(oprleft)) oprkind = 'l'; else if (!OidIsValid(oprright)) oprkind = 'r'; else oprkind = 'b'; ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator does not exist: %s", op_signature_string(opername, oprkind, oprleft, oprright)), errOmitLocation(true), parser_errposition(pstate, location))); } return InvalidOid; }
/* timestamptz_abstime() * Convert timestamp with time zone to abstime. */ Datum timestamptz_abstime(PG_FUNCTION_ARGS) { TimestampTz timestamp = PG_GETARG_TIMESTAMP(0); AbsoluteTime result; fsec_t fsec; struct pg_tm tt, *tm = &tt; if (TIMESTAMP_IS_NOBEGIN(timestamp)) result = NOSTART_ABSTIME; else if (TIMESTAMP_IS_NOEND(timestamp)) result = NOEND_ABSTIME; else if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) == 0) result = tm2abstime(tm, 0); else { ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"), errOmitLocation(true))); result = INVALID_ABSTIME; } PG_RETURN_ABSOLUTETIME(result); }
/* compatible_oper() * given an opname and input datatypes, find a compatible binary operator * * This is tighter than oper() because it will not return an operator that * requires coercion of the input datatypes (but binary-compatible operators * are accepted). Otherwise, the semantics are the same. */ Operator compatible_oper(ParseState *pstate, List *op, Oid arg1, Oid arg2, bool noError, int location) { Operator optup; Form_pg_operator opform; /* oper() will find the best available match */ optup = oper(pstate, op, arg1, arg2, noError, location); if (optup == (Operator) NULL) return (Operator) NULL; /* must be noError case */ /* but is it good enough? */ opform = (Form_pg_operator) GETSTRUCT(optup); if (IsBinaryCoercible(arg1, opform->oprleft) && IsBinaryCoercible(arg2, opform->oprright)) return optup; /* nope... */ ReleaseOperator(optup); if (!noError) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator requires run-time type coercion: %s", op_signature_string(op, 'b', arg1, arg2)), errOmitLocation(true), parser_errposition(pstate, location))); return (Operator) NULL; }
/* * reverse_ordering_oper - identify DESC sort operator (">") for a datatype * * On failure, return NULL if noError, else report a standard error */ Operator reverse_ordering_oper(Oid argtype, bool noError) { TypeCacheEntry *typentry; Oid oproid; Operator optup; /* * Look for a ">" operator for the datatype. We require it to be an exact * or binary-compatible match, since most callers are not prepared to cope * with adding any run-time type coercion steps. * * Note: the search algorithm used by typcache.c ensures that if a ">" * operator is returned, it will be consistent with the "=" operator * returned by equality_oper. This is critical for sorting and grouping * purposes. */ typentry = lookup_type_cache(argtype, TYPECACHE_GT_OPR); oproid = typentry->gt_opr; /* * If the datatype is an array, then we can use array_gt ... but only if * there is a suitable greater-than operator for the element type. (This * check is not in the raw typcache.c code ... should it be?) */ if (oproid == ARRAY_GT_OP) { Oid elem_type = get_element_type(argtype); if (OidIsValid(elem_type)) { optup = reverse_ordering_oper(elem_type, true); if (optup != NULL) ReleaseOperator(optup); else oproid = InvalidOid; /* element type has no ">" */ } else oproid = InvalidOid; /* bogus array type? */ } if (OidIsValid(oproid)) { optup = fetch_op_tup(oproid, true); return optup; } if (!noError) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify an ordering operator for type %s", format_type_be(argtype)), errhint("Use an explicit ordering operator or modify the query."), errOmitLocation(true))); return NULL; }
/* abstime_timestamptz() * Convert abstime to timestamp with time zone. */ Datum abstime_timestamptz(PG_FUNCTION_ARGS) { AbsoluteTime abstime = PG_GETARG_ABSOLUTETIME(0); TimestampTz result; struct pg_tm tt, *tm = &tt; int tz; char zone[MAXDATELEN + 1], *tzn = zone; switch (abstime) { case INVALID_ABSTIME: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert abstime \"invalid\" to timestamp"), errOmitLocation(true))); TIMESTAMP_NOBEGIN(result); break; case NOSTART_ABSTIME: TIMESTAMP_NOBEGIN(result); break; case NOEND_ABSTIME: TIMESTAMP_NOEND(result); break; default: abstime2tm(abstime, &tz, tm, &tzn); if (tm2timestamp(tm, 0, &tz, &result) != 0) ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"), errOmitLocation(true))); break; }; PG_RETURN_TIMESTAMP(result); }
Datum gp_persistent_set_relation_bufpool_kind_all(PG_FUNCTION_ARGS) { Relation pg_database; HeapScanDesc scan; HeapTuple tuple; if (!gp_upgrade_mode) ereport(ERROR, (errcode(ERRCODE_GP_FEATURE_NOT_SUPPORTED), errmsg("function is not supported"), errOmitLocation(true))); // UNDONE: Verify we are in some sort of single-user mode. /* * Special call here to scan the persistent meta-data structures so we are open for * business and then we can add information. */ PersistentFileSysObj_BuildInitScan(); pg_database = heap_open( DatabaseRelationId, AccessShareLock); scan = heap_beginscan(pg_database, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_database form_pg_database = (Form_pg_database)GETSTRUCT(tuple); Oid dbOid; Oid dattablespace; dbOid = HeapTupleGetOid(tuple); dattablespace = form_pg_database->dattablespace; if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "gp_persistent_set_relation_bufpool_kind_all: dbOid %u", dbOid); PersistentBuild_SetRelationBufpoolKind( dattablespace, dbOid); } heap_endscan(scan); heap_close(pg_database, AccessShareLock); PG_RETURN_INT32(1); }
static DemoUri *ParseDemoUri(const char *uri_str) { DemoUri *uri = (DemoUri *) palloc0(sizeof(DemoUri)); int protocol_len; uri->path = NULL; uri->protocol = NULL; /* * parse protocol */ char *post_protocol = strstr(uri_str, "://"); if(!post_protocol) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid demo prot URI \'%s\'", uri_str), errOmitLocation(true))); } protocol_len = post_protocol - uri_str; uri->protocol = (char *) palloc0 (protocol_len + 1); strncpy(uri->protocol, uri_str, protocol_len); /* make sure there is more to the uri string */ if (strlen(uri_str) <= protocol_len) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid demo prot URI \'%s\' : missing path", uri_str), errOmitLocation(true))); /* * parse path */ uri->path = pstrdup(uri_str + protocol_len + strlen("://")); return uri; }
/* * equality_oper - identify a suitable equality operator for a datatype * * On failure, return NULL if noError, else report a standard error */ Operator equality_oper(Oid argtype, bool noError) { TypeCacheEntry *typentry; Oid oproid; Operator optup; /* * Look for an "=" operator for the datatype. We require it to be an * exact or binary-compatible match, since most callers are not prepared * to cope with adding any run-time type coercion steps. */ typentry = lookup_type_cache(argtype, TYPECACHE_EQ_OPR); oproid = typentry->eq_opr; /* * If the datatype is an array, then we can use array_eq ... but only if * there is a suitable equality operator for the element type. (This check * is not in the raw typcache.c code ... should it be?) */ if (oproid == ARRAY_EQ_OP) { Oid elem_type = get_element_type(argtype); if (OidIsValid(elem_type)) { optup = equality_oper(elem_type, true); if (optup != NULL) ReleaseOperator(optup); else oproid = InvalidOid; /* element type has no "=" */ } else oproid = InvalidOid; /* bogus array type? */ } if (OidIsValid(oproid)) { optup = fetch_op_tup(oproid, true); return optup; } if (!noError) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not identify an equality operator for type %s", format_type_be(argtype)), errOmitLocation(true))); return NULL; }
/* * op_error - utility routine to complain about an unresolvable operator */ static void op_error(ParseState *pstate, List *op, char oprkind, Oid arg1, Oid arg2, FuncDetailCode fdresult, int location) { if (fdresult == FUNCDETAIL_MULTIPLE) ereport(ERROR, (errcode(ERRCODE_AMBIGUOUS_FUNCTION), errmsg("operator is not unique: %s", op_signature_string(op, oprkind, arg1, arg2)), errhint("Could not choose a best candidate operator. " "You may need to add explicit type casts."), errOmitLocation(true), parser_errposition(pstate, location))); else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("operator does not exist: %s", op_signature_string(op, oprkind, arg1, arg2)), errhint("No operator matches the given name and argument type(s). " "You may need to add explicit type casts."), errOmitLocation(true), parser_errposition(pstate, location))); }
Datum reltime_interval(PG_FUNCTION_ARGS) { RelativeTime reltime = PG_GETARG_RELATIVETIME(0); Interval *result; int year, month, day; result = (Interval *) palloc(sizeof(Interval)); switch (reltime) { case INVALID_RELTIME: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot convert reltime \"invalid\" to interval"), errOmitLocation(true))); result->time = 0; result->day = 0; result->month = 0; break; default: #ifdef HAVE_INT64_TIMESTAMP year = reltime / SECS_PER_YEAR; reltime -= year * SECS_PER_YEAR; month = reltime / (DAYS_PER_MONTH * SECS_PER_DAY); reltime -= month * (DAYS_PER_MONTH * SECS_PER_DAY); day = reltime / SECS_PER_DAY; reltime -= day * SECS_PER_DAY; result->time = (reltime * USECS_PER_SEC); #else TMODULO(reltime, year, SECS_PER_YEAR); TMODULO(reltime, month, DAYS_PER_MONTH * SECS_PER_DAY); TMODULO(reltime, day, SECS_PER_DAY); result->time = reltime; #endif result->month = MONTHS_PER_YEAR * year + month; result->day = day; break; } PG_RETURN_INTERVAL_P(result); }
/* * tintervalrecv - converts external binary format to tinterval */ Datum tintervalrecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); TimeInterval tinterval; tinterval = (TimeInterval) palloc(sizeof(TimeIntervalData)); tinterval->status = pq_getmsgint(buf, sizeof(tinterval->status)); if (!(tinterval->status == T_INTERVAL_INVAL || tinterval->status == T_INTERVAL_VALID)) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("invalid status in external \"tinterval\" value"), errOmitLocation(true))); tinterval->data[0] = pq_getmsgint(buf, sizeof(tinterval->data[0])); tinterval->data[1] = pq_getmsgint(buf, sizeof(tinterval->data[1])); PG_RETURN_TIMEINTERVAL(tinterval); }
void PersistentRelation_AddCreated( RelFileNode *relFileNode, ItemPointer persistentTid, bool flushToXLog) { WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; PersistentFileSysObjName fsObjName; ItemPointerData previousFreeTid; int64 persistentSerialNum; RelationDirEntry relationDirEntry; Datum values[Natts_gp_persistent_relation_node]; if (RelFileNode_IsEmpty(relFileNode)) { elog(ERROR, "Invalid RelFileNode (0,0,0)"); } MemSet(&previousFreeTid, 0, sizeof(ItemPointerData)); if (!Persistent_BeforePersistenceWork()) { elog(ERROR, "We can only add to persistent meta-data when special states"); } /* Verify PersistentFileSysObj_BuildInitScan has been called */ PersistentRelation_VerifyInitScan(); PersistentFileSysObjName_SetRelationDir( &fsObjName, relFileNode, is_tablespace_shared); WRITE_PERSISTENT_STATE_ORDERED_LOCK; relationDirEntry = PersistentRelation_CreateEntryUnderLock(relFileNode); if (relationDirEntry == NULL) { /* If out of shared memory, no need to promote to PANIC. */ WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of shared-memory for persistent relations"), errhint("You may need to increase the gp_max_relations value"), errOmitLocation(true))); } relationDirEntry->state = PersistentFileSysState_Created; GpPersistentRelationNode_SetDatumValues( values, relFileNode->spcNode, relFileNode->dbNode, relFileNode->relNode, PersistentFileSysState_Created, /* reserved */ 0, /* parentXid */ InvalidTransactionId, /* persistentSerialNum */ 0, &previousFreeTid, is_tablespace_shared(relFileNode->spcNode)); PersistentFileSysObj_AddTuple( PersistentFsObjType_RelationDir, values, flushToXLog, persistentTid, &persistentSerialNum); WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; if (Debug_persistent_print) { elog(Persistent_DebugPrintLevel(), "Persistent relation: Add '%s', in state 'Created', serial number " INT64_FORMAT " at TID %s", PersistentFileSysObjName_ObjectName(&fsObjName), persistentSerialNum, ItemPointerToString(persistentTid)); } }
void PersistentDatabase_MarkJustInTimeCreatePending( DbDirNode *dbDirNode, ItemPointer persistentTid, int64 *persistentSerialNum) { WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; PersistentFileSysObjName fsObjName; DatabaseDirEntry databaseDirEntry; SharedOidSearchAddResult addResult; if (Persistent_BeforePersistenceWork()) { if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "Skipping persistent database '%s' because we are before persistence work", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace)); return; // The initdb process will load the persistent table once we out of bootstrap mode. } PersistentDatabase_VerifyInitScan(); PersistentFileSysObjName_SetDatabaseDir(&fsObjName,dbDirNode->tablespace,dbDirNode->database,is_tablespace_shared); WRITE_PERSISTENT_STATE_ORDERED_LOCK; databaseDirEntry = (DatabaseDirEntry) SharedOidSearch_Find( &persistentDatabaseSharedData->databaseDirSearchTable, dbDirNode->database, dbDirNode->tablespace); if (databaseDirEntry != NULL) { /* * An existence check should have been done before calling this routine. */ elog(ERROR, "Persistent database entry '%s' already exists in state '%s'", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace), PersistentFileSysObjState_Name(databaseDirEntry->state)); } addResult = SharedOidSearch_Add( &persistentDatabaseSharedData->databaseDirSearchTable, dbDirNode->database, dbDirNode->tablespace, (SharedOidSearchObjHeader**)&databaseDirEntry); if (addResult == SharedOidSearchAddResult_NoMemory) { /* If out of shared memory, no need to promote to PANIC. */ WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of shared-memory for persistent databases"), errhint("You may need to increase the gp_max_databases and " "gp_max_tablespaces value"), errOmitLocation(true))); } else if (addResult == SharedOidSearchAddResult_Exists) elog(PANIC, "Persistent database entry '%s' already exists in state '%s'", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace), PersistentFileSysObjState_Name(databaseDirEntry->state)); else Assert(addResult == SharedOidSearchAddResult_Ok); databaseDirEntry->state = PersistentFileSysState_JustInTimeCreatePending; PersistentDatabase_AddTuple( databaseDirEntry, /* reserved */ 0, /* parentXid */ InvalidTransactionId, /* flushToXLog */ true); *persistentTid = databaseDirEntry->persistentTid; *persistentSerialNum = databaseDirEntry->persistentSerialNum; /* * This XLOG must be generated under the persistent write-lock. */ #ifdef MASTER_MIRROR_SYNC mmxlog_log_create_database(dbDirNode->tablespace, dbDirNode->database); #endif WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; }
static int send_snmp_inform_or_trap(const GpErrorData * errorData, const char * subject, const char * severity) { netsnmp_session session, *ss = NULL; netsnmp_pdu *pdu, *response; int status; char csysuptime[20]; static bool snmp_initialized = false; static char myhostname[255]; /* gethostname usually is limited to 65 chars out, but make this big to be safe */ char *rawstring = NULL; List *elemlist = NIL; ListCell *l = NULL; /* * "inform" messages get a positive acknowledgement response from the SNMP manager. * If it doesn't come, the message might be resent. * * "trap" messages are one-way, and we have no idea if the manager received it. * But, it's faster and cheaper, and no need to retry. So some people might prefer it. */ bool inform = strcmp(gp_snmp_use_inform_or_trap,"inform") == 0; if (gp_snmp_monitor_address == NULL || gp_snmp_monitor_address[0] == '\0') { static bool firsttime = 1; ereport(firsttime ? LOG : DEBUG1,(errmsg("SNMP inform/trap alerts are disabled"),errOmitLocation(true))); firsttime = false; return -1; } /* * SNMP managers are required to handle messages up to at least 484 bytes long, but I believe most existing * managers support messages up to one packet (ethernet frame) in size, 1472 bytes. * * But, should we take that chance? Or play it safe and limit the message to 484 bytes? */ elog(DEBUG2,"send_snmp_inform_or_trap"); if (!snmp_initialized) { snmp_enable_stderrlog(); if (gp_snmp_debug_log != NULL && gp_snmp_debug_log[0] != '\0') { snmp_enable_filelog(gp_snmp_debug_log, 1); //debug_register_tokens("ALL"); snmp_set_do_debugging(1); } /* * Initialize the SNMP library. This also reads the MIB database. */ /* Add GPDB-MIB to the list to be loaded */ putenv("MIBS=+GPDB-MIB:SNMP-FRAMEWORK-MIB:SNMPv2-CONF:SNMPv2-TC:SNMPv2-TC"); init_snmp("sendalert"); snmp_initialized = true; { char portnum[16]; myhostname[0] = '\0'; if (gethostname(myhostname, sizeof(myhostname)) == 0) { strcat(myhostname,":"); pg_ltoa(PostPortNumber,portnum); strcat(myhostname,portnum); } } } /* * Trap/Inform messages always start with the system up time. (SysUpTime.0) * * This presumably would be the uptime of GPDB, not the machine it is running on, I think. * * Use Postmaster's "MyStartTime" as a way to get that. */ sprintf(csysuptime, "%ld", (long)(time(NULL) - MyStartTime)); /* // ERRCODE_DISK_FULL could be reported vi rbmsMIB rdbmsTraps rdbmsOutOfSpace trap. // But it appears we never generate that error? // ERRCODE_ADMIN_SHUTDOWN means SysAdmin aborted somebody's request. Not interesting? // ERRCODE_CRASH_SHUTDOWN sounds interesting, but I don't see that we ever generate it. // ERRCODE_CANNOT_CONNECT_NOW means we are starting up, shutting down, in recovery, or Too many users are logged on. // abnormal database system shutdown */ /* * The gpdbAlertSeverity is a crude attempt to classify some of these messages based on severity, * where OK means everything is running normal, Down means everything is shut down, degraded would be * for times when some segments are down, but the system is up, The others are maybe useful in the future * * gpdbSevUnknown(0), * gpdbSevOk(1), * gpdbSevWarning(2), * gpdbSevError(3), * gpdbSevFatal(4), * gpdbSevPanic(5), * gpdbSevSystemDegraded(6), * gpdbSevSystemDown(7) */ char detail[MAX_ALERT_STRING+1]; snprintf(detail, MAX_ALERT_STRING, "%s", errorData->error_detail); detail[127] = '\0'; char sqlstmt[MAX_ALERT_STRING+1]; char * sqlstmtp = errorData->debug_query_string; if (sqlstmtp == NULL || sqlstmtp[0] == '\0') sqlstmtp = errorData->internal_query; if (sqlstmtp == NULL) sqlstmtp = ""; snprintf(sqlstmt, MAX_ALERT_STRING, "%s", sqlstmtp); sqlstmt[MAX_ALERT_STRING] = '\0'; /* Need a modifiable copy of To list */ rawstring = pstrdup(gp_snmp_monitor_address); /* Parse string into list of identifiers */ if (!SplitMailString(rawstring, ',', &elemlist)) { /* syntax error in list */ ereport(LOG, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid list syntax for \"gp_snmp_monitor_address\""))); return -1; } /* * This session is just a template, and doesn't need to be connected. * It is used by snmp_add(), which copies this info, opens the new session, and assigns the transport. */ snmp_sess_init( &session ); /* Initialize session to default values */ session.version = SNMP_VERSION_2c; session.timeout = SNMP_DEFAULT_TIMEOUT; session.retries = SNMP_DEFAULT_RETRIES; session.remote_port = 162; // I think this isn't used by net-snmp any more. /*if (strchr(session.peername,':')==NULL) strcat(session.peername,":162");*/ session.community = (u_char *)gp_snmp_community; session.community_len = strlen((const char *)session.community); // SNMP_DEFAULT_COMMUNITY_LEN means "public" session.callback_magic = NULL; foreach(l, elemlist) { char *cur_gp_snmp_monitor_address = (char *) lfirst(l); if (cur_gp_snmp_monitor_address == NULL || cur_gp_snmp_monitor_address[0] == '\0') continue; session.peername = cur_gp_snmp_monitor_address; /* * If we try to "snmp_open( &session )", net-snmp will set up a connection to that * endpoint on port 161, assuming we are the network monitor, and the other side is an agent. * * But we are pretending to be an agent, sending traps to the NM, so we don't need this. */ /*if (!snmp_open( &session )) // Don't open the session here! { const char *str; int xerr; xerr = snmp_errno; str = snmp_api_errstring(xerr); elog(LOG, "snmp_open: %s", str); return -1; }*/ /* * This call copies the info from "session" to "ss", assigns the transport, and opens the session. * We must specify "snmptrap" so the transport will know we want port 162 by default. */ ss = snmp_add(&session, netsnmp_transport_open_client("snmptrap", cur_gp_snmp_monitor_address), NULL, NULL); if (ss == NULL) { /* * diagnose netsnmp_transport_open_client and snmp_add errors with * the input netsnmp_session pointer */ { char *err; snmp_error(&session, NULL, NULL, &err); elog(LOG, "send_alert snmp_add of %s failed: %s", cur_gp_snmp_monitor_address, err); free(err); } return -1; } /* * We need to create the pdu each time, as it gets freed when we send a trap. */ pdu = snmp_pdu_create(inform ? SNMP_MSG_INFORM : SNMP_MSG_TRAP2); if (!pdu) { const char *str; int xerr; xerr = snmp_errno; str = snmp_api_errstring(xerr); elog(LOG, "Failed to create notification PDU: %s", str); return -1; } /* * Trap/Inform messages always start with the system up time. (SysUpTime.0) * We use Postmaster's "MyStartTime" as a way to get that. */ snmp_add_var(pdu, objid_sysuptime, sizeof(objid_sysuptime) / sizeof(oid), 't', (const char *)csysuptime); #if 0 /* * In the future, we might want to send RDBMS-MIB::rdbmsStateChange when the system becomes unavailable or * partially unavailable. This code, which is not currently used, shows how to build the pdu for * that trap. */ /* {iso(1) identified-organization(3) dod(6) internet(1) mgmt(2) mib-2(1) rdbmsMIB(39) rdbmsTraps(2) rdbmsStateChange(1)} */ snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o', "1.3.6.1.2.1.39.2.1"); // rdbmsStateChange snmp_add_var(pdu, objid_rdbmsrelstate, sizeof(objid_rdbmsrelstate) / sizeof(oid), 'i', "5"); // 4 = restricted, 5 = unavailable #endif /* {iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprises(1) gpdbMIB(31327) gpdbTraps(5) gpdbTrapsList(0) gpdbAlert(1)} */ /* * We could specify this trap oid by name, rather than numeric oid, but then if the GPDB-MIB wasn't * found, we'd get an error. Using the numeric oid means we can still work without the MIB loaded. */ snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o', "1.3.6.1.4.1.31327.5.0.1"); // gpdbAlert snmp_add_var(pdu, objid_gpdbAlertMsg, sizeof(objid_gpdbAlertMsg) / sizeof(oid), 's', subject); // SnmpAdminString = UTF-8 text snmp_add_var(pdu, objid_gpdbAlertSeverity, sizeof(objid_gpdbAlertSeverity) / sizeof(oid), 'i', (char *)severity); snmp_add_var(pdu, objid_gpdbAlertSqlstate, sizeof(objid_gpdbAlertSqlstate) / sizeof(oid), 's', errorData->sql_state); snmp_add_var(pdu, objid_gpdbAlertDetail, sizeof(objid_gpdbAlertDetail) / sizeof(oid), 's', detail); // SnmpAdminString = UTF-8 text snmp_add_var(pdu, objid_gpdbAlertSqlStmt, sizeof(objid_gpdbAlertSqlStmt) / sizeof(oid), 's', sqlstmt); // SnmpAdminString = UTF-8 text snmp_add_var(pdu, objid_gpdbAlertSystemName, sizeof(objid_gpdbAlertSystemName) / sizeof(oid), 's', myhostname); // SnmpAdminString = UTF-8 text elog(DEBUG2,"ready to send to %s",cur_gp_snmp_monitor_address); if (inform) status = snmp_synch_response(ss, pdu, &response); else status = snmp_send(ss, pdu) == 0; elog(DEBUG2,"send, status %d",status); if (status != STAT_SUCCESS) { /* Something went wrong */ if (ss) { char *err; snmp_error(ss, NULL, NULL, &err); elog(LOG, "sendalert failed to send %s: %s", inform ? "inform" : "trap", err); free(err); } else { elog(LOG, "sendalert failed to send %s: %s", inform ? "inform" : "trap", "Something went wrong"); } if (!inform) snmp_free_pdu(pdu); } else if (inform) snmp_free_pdu(response); snmp_close(ss); ss = NULL; }
void PersistentRelation_MarkCreatePending( RelFileNode *relFileNode, ItemPointer persistentTid, int64 *persistentSerialNum, bool flushToXLog) { WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; PersistentFileSysObjName fsObjName; RelationDirEntry relationDirEntry; ItemPointerData previousFreeTid; Datum values[Natts_gp_persistent_relation_node]; if (RelFileNode_IsEmpty(relFileNode)) { elog(ERROR, "Invalid RelFileNode (0,0,0)"); } MemSet(&previousFreeTid, 0, sizeof(ItemPointerData)); if (Persistent_BeforePersistenceWork()) { if (Debug_persistent_print) { elog(Persistent_DebugPrintLevel(), "Skipping persistent relation '%s' because we are before persistence work", relpath(*relFileNode)); } *persistentSerialNum = 0; /* * The initdb process will load the persistent table once we out * of bootstrap mode. */ return; } PersistentRelation_VerifyInitScan(); PersistentFileSysObjName_SetRelationDir( &fsObjName, relFileNode, is_tablespace_shared); WRITE_PERSISTENT_STATE_ORDERED_LOCK; relationDirEntry = PersistentRelation_CreateEntryUnderLock(relFileNode); if (relationDirEntry == NULL) { /* If out of shared memory, no need to promote to PANIC. */ WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of shared-memory for persistent relations"), errhint("You may need to increase the gp_max_relations value"), errOmitLocation(true))); } relationDirEntry->state = PersistentFileSysState_CreatePending; GpPersistentRelationNode_SetDatumValues( values, relFileNode->spcNode, relFileNode->dbNode, relFileNode->relNode, PersistentFileSysState_CreatePending, /* reserved */ 0, /* parentXid */ GetTopTransactionId(), /* persistentSerialNum */ 0, // This will be set by PersistentFileSysObj_AddTuple. &previousFreeTid, is_tablespace_shared(relFileNode->spcNode)); PersistentFileSysObj_AddTuple( PersistentFsObjType_RelationDir, values, flushToXLog, &relationDirEntry->persistentTid, &relationDirEntry->persistentSerialNum); *persistentTid = relationDirEntry->persistentTid; *persistentSerialNum = relationDirEntry->persistentSerialNum; /* * This XLOG must be generated under the persistent write-lock. */ #ifdef MASTER_MIRROR_SYNC mmxlog_log_create_relation( relFileNode->spcNode, relFileNode->dbNode, relFileNode->relNode, persistentTid, persistentSerialNum); #endif /* * MPP-18228 * To make adding 'Create Pending' entry to persistent table and * adding to the PendingDelete list atomic */ PendingDelete_AddCreatePendingEntryWrapper( &fsObjName, persistentTid, *persistentSerialNum); WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; }
/* * CheckMyDatabase -- fetch information from the pg_database entry for our DB */ static void CheckMyDatabase(const char *name, bool am_superuser) { HeapTuple tup; Form_pg_database dbform; /* Fetch our real pg_database row */ tup = SearchSysCache(DATABASEOID, ObjectIdGetDatum(MyDatabaseId), 0, 0, 0); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for database %u", MyDatabaseId); dbform = (Form_pg_database) GETSTRUCT(tup); /* This recheck is strictly paranoia */ if (strcmp(name, NameStr(dbform->datname)) != 0) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" has disappeared from pg_database", name), errdetail("Database OID %u now seems to belong to \"%s\".", MyDatabaseId, NameStr(dbform->datname)))); /* * Check permissions to connect to the database. * * These checks are not enforced when in standalone mode, so that there is * a way to recover from disabling all access to all databases, for * example "UPDATE pg_database SET datallowconn = false;". * * We do not enforce them for the autovacuum worker processes either. */ if (IsUnderPostmaster && !IsAutoVacuumProcess()) { /* * Check that the database is currently allowing connections. * (exception during upgrade_mode) */ if (gp_upgrade_mode && !dbform->datallowconn) elog(INFO, "Connecting to no-connection db in upgrade mode."); if (!dbform->datallowconn && !gp_upgrade_mode) ereport(FATAL, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("database \"%s\" is not currently accepting connections", name), errOmitLocation(true), errSendAlert(false))); /* * Check privilege to connect to the database. (The am_superuser test * is redundant, but since we have the flag, might as well check it * and save a few cycles.) */ if (!am_superuser && pg_database_aclcheck(MyDatabaseId, GetUserId(), ACL_CONNECT) != ACLCHECK_OK) ereport(FATAL, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied for database \"%s\"", name), errdetail("User does not have CONNECT privilege."), errOmitLocation(true), errSendAlert(false))); /* * Check connection limit for this database. * * There is a race condition here --- we create our PGPROC before * checking for other PGPROCs. If two backends did this at about the * same time, they might both think they were over the limit, while * ideally one should succeed and one fail. Getting that to work * exactly seems more trouble than it is worth, however; instead we * just document that the connection limit is approximate. */ if (dbform->datconnlimit >= 0 && !am_superuser && CountDBBackends(MyDatabaseId) > dbform->datconnlimit) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("too many connections for database \"%s\"", name), errOmitLocation(true), errSendAlert(false))); } /* * OK, we're golden. Next to-do item is to save the encoding info out of * the pg_database tuple. */ if (GpIdentity.segindex == UNINITIALIZED_GP_IDENTITY_VALUE || GpIdentity.segindex == MASTER_CONTENT_ID) SetDatabaseEncoding(dbform->encoding); else SetDatabaseEncoding(MyProcPort->encoding); /* Record it as a GUC internal option, too */ SetConfigOption("server_encoding", GetDatabaseEncodingName(), PGC_INTERNAL, PGC_S_OVERRIDE); /* If we have no other source of client_encoding, use server encoding */ SetConfigOption("client_encoding", GetDatabaseEncodingName(), PGC_BACKEND, PGC_S_DEFAULT); /* Use the right encoding in translated messages */ #ifdef ENABLE_NLS pg_bind_textdomain_codeset(textdomain(NULL)); #endif /* * Lastly, set up any database-specific configuration variables. */ if (IsUnderPostmaster) { Datum datum; bool isnull; datum = SysCacheGetAttr(DATABASEOID, tup, Anum_pg_database_datconfig, &isnull); if (!isnull) { ArrayType *a = DatumGetArrayTypeP(datum); ProcessGUCArray(a, PGC_S_DATABASE); } } ReleaseSysCache(tup); }
/* -------------------------------- * InitPostgres * Initialize POSTGRES. * * The database can be specified by name, using the in_dbname parameter, or by * OID, using the dboid parameter. In the latter case, the computed database * name is passed out to the caller as a palloc'ed string in out_dbname. * * In bootstrap mode no parameters are used. * * The return value indicates whether the userID is a superuser. (That * can only be tested inside a transaction, so we want to do it during * the startup transaction rather than doing a separate one in postgres.c.) * * As of PostgreSQL 8.2, we expect InitProcess() was already called, so we * already have a PGPROC struct ... but it's not filled in yet. * * Note: * Be very careful with the order of calls in the InitPostgres function. * -------------------------------- */ bool InitPostgres(const char *in_dbname, Oid dboid, const char *username, char **out_dbname) { bool bootstrap = IsBootstrapProcessingMode(); bool autovacuum = IsAutoVacuumProcess(); bool am_superuser; char *fullpath; char dbname[NAMEDATALEN]; /* * Set up the global variables holding database id and path. But note we * won't actually try to touch the database just yet. * * We take a shortcut in the bootstrap case, otherwise we have to look up * the db name in pg_database. */ if (bootstrap) { MyDatabaseId = TemplateDbOid; MyDatabaseTableSpace = DEFAULTTABLESPACE_OID; } else { /* * Find tablespace of the database we're about to open. Since we're * not yet up and running we have to use one of the hackish * FindMyDatabase variants, which look in the flat-file copy of * pg_database. * * If the in_dbname param is NULL, lookup database by OID. */ if (in_dbname == NULL) { if (!FindMyDatabaseByOid(dboid, dbname, &MyDatabaseTableSpace)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database %u does not exist", dboid), errSendAlert(false))); MyDatabaseId = dboid; /* pass the database name to the caller */ *out_dbname = pstrdup(dbname); } else { if (!FindMyDatabase(in_dbname, &MyDatabaseId, &MyDatabaseTableSpace)) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", in_dbname), errOmitLocation(true), errSendAlert(false))); /* our database name is gotten from the caller */ strlcpy(dbname, in_dbname, NAMEDATALEN); } } fullpath = GetDatabasePath(MyDatabaseId, MyDatabaseTableSpace); SetDatabasePath(fullpath); /* * Finish filling in the PGPROC struct, and add it to the ProcArray. (We * need to know MyDatabaseId before we can do this, since it's entered * into the PGPROC struct.) * * Once I have done this, I am visible to other backends! */ InitProcessPhase2(); // get temporary directory for QD if (!bootstrap && Gp_role == GP_ROLE_DISPATCH) { if (get_tmpdir_from_rm) { getLocalTmpDirFromMasterRM(); } else { getLocalTmpDirFromMasterConfig(gp_session_id); elog(LOG, "getLocalTmpDirFromMasterConfig session_id:%d tmpdir:%s", gp_session_id, LocalTempPath); } } /* Initialize SessionState entry */ SessionState_Init(); /* Initialize memory protection */ GPMemoryProtect_Init(); /* * Initialize my entry in the shared-invalidation manager's array of * per-backend data. * * Sets up MyBackendId, a unique backend identifier. */ MyBackendId = InvalidBackendId; SharedInvalBackendInit(false); if (MyBackendId > MaxBackends || MyBackendId <= 0) elog(FATAL, "bad backend id: %d", MyBackendId); /* Now that we have a BackendId, we can participate in ProcSignal */ ProcSignalInit(MyBackendId); /* * bufmgr needs another initialization call too */ InitBufferPoolBackend(); /* * Initialize local process's access to XLOG. In bootstrap case we may * skip this since StartupXLOG() was run instead. */ if (!bootstrap) InitXLOGAccess(); /* * Initialize the relation cache and the system catalog caches. Note that * no catalog access happens here; we only set up the hashtable structure. * We must do this before starting a transaction because transaction abort * would try to touch these hashtables. */ RelationCacheInitialize(); InitCatalogCache(); /* Initialize portal manager */ EnablePortalManager(); /* Initialize stats collection --- must happen before first xact */ if (!bootstrap) pgstat_initialize(); /* * Set up process-exit callback to do pre-shutdown cleanup. This has to * be after we've initialized all the low-level modules like the buffer * manager, because during shutdown this has to run before the low-level * modules start to close down. On the other hand, we want it in place * before we begin our first transaction --- if we fail during the * initialization transaction, as is entirely possible, we need the * AbortTransaction call to clean up. */ on_shmem_exit(ShutdownPostgres, 0); /* * Start a new transaction here before first access to db, and get a * snapshot. We don't have a use for the snapshot itself, but we're * interested in the secondary effect that it sets RecentGlobalXmin. */ if (!bootstrap) { StartTransactionCommand(); (void) GetTransactionSnapshot(); } /* * Now that we have a transaction, we can take locks. Take a writer's * lock on the database we are trying to connect to. If there is a * concurrently running DROP DATABASE on that database, this will block us * until it finishes (and has updated the flat file copy of pg_database). * * Note that the lock is not held long, only until the end of this startup * transaction. This is OK since we are already advertising our use of * the database in the PGPROC array; anyone trying a DROP DATABASE after * this point will see us there. * * Note: use of RowExclusiveLock here is reasonable because we envision * our session as being a concurrent writer of the database. If we had a * way of declaring a session as being guaranteed-read-only, we could use * AccessShareLock for such sessions and thereby not conflict against * CREATE DATABASE. */ if (!bootstrap) { if (MyDatabaseId == TemplateDbOid) LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, AccessShareLock); else LockSharedObject(DatabaseRelationId, MyDatabaseId, 0, RowExclusiveLock); } /* * Recheck the flat file copy of pg_database to make sure the target * database hasn't gone away. If there was a concurrent DROP DATABASE, * this ensures we will die cleanly without creating a mess. */ if (!bootstrap) { Oid dbid2; Oid tsid2; if (!FindMyDatabase(dbname, &dbid2, &tsid2) || dbid2 != MyDatabaseId || tsid2 != MyDatabaseTableSpace) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname), errdetail("It seems to have just been dropped or renamed."))); } /* * Now we should be able to access the database directory safely. Verify * it's there and looks reasonable. */ if (!bootstrap) { if (access(fullpath, F_OK) == -1) { if (errno == ENOENT) ereport(FATAL, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname), errdetail("The database subdirectory \"%s\" is missing.", fullpath))); else ereport(FATAL, (errcode_for_file_access(), errmsg("could not access directory \"%s\": %m", fullpath))); } ValidatePgVersion(fullpath); } /* * It's now possible to do real access to the system catalogs. * * Load relcache entries for the system catalogs. This must create at * least the minimum set of "nailed-in" cache entries. */ RelationCacheInitializePhase2(); /* * Figure out our postgres user id, and see if we are a superuser. * * In standalone mode and in the autovacuum process, we use a fixed id, * otherwise we figure it out from the authenticated user name. */ if (bootstrap || autovacuum) { InitializeSessionUserIdStandalone(); am_superuser = true; } else if (!IsUnderPostmaster) { InitializeSessionUserIdStandalone(); am_superuser = true; if (!ThereIsAtLeastOneRole()) ereport(WARNING, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no roles are defined in this database system"), errhint("You should immediately run CREATE USER \"%s\" CREATEUSER;.", username))); } else { /* normal multiuser case */ InitializeSessionUserId(username); am_superuser = superuser(); } /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); /* * Read the real pg_database row for our database, check permissions and * set up database-specific GUC settings. We can't do this until all the * database-access infrastructure is up. (Also, it wants to know if the * user is a superuser, so the above stuff has to happen first.) */ if (!bootstrap) CheckMyDatabase(dbname, am_superuser); /* * Check a normal user hasn't connected to a superuser reserved slot. */ if (!am_superuser && ReservedBackends > 0 && !HaveNFreeProcs(ReservedBackends)) ereport(FATAL, (errcode(ERRCODE_TOO_MANY_CONNECTIONS), errmsg("connection limit exceeded for non-superusers"), errOmitLocation(true), errSendAlert(true))); /* * Initialize various default states that can't be set up until we've * selected the active user and gotten the right GUC settings. */ /* set default namespace search path */ InitializeSearchPath(); /* initialize client encoding */ InitializeClientEncoding(); /* report this backend in the PgBackendStatus array */ if (!bootstrap) pgstat_bestart(); /* * MPP package setup * * Primary function is to establish connctions to the qExecs. * This is SKIPPED when the database is in bootstrap mode or * Is not UnderPostmaster. */ if (!bootstrap && IsUnderPostmaster) { cdb_setup(); on_proc_exit( cdb_cleanup, 0 ); } /* close the transaction we started above */ if (!bootstrap) CommitTransactionCommand(); return am_superuser; }
/* ---------------------------------------------------------------- * ExecInsert * * INSERTs have to add the tuple into * the base relation and insert appropriate tuples into the * index relations. * Insert can be part of an update operation when * there is a preceding SplitUpdate node. * ---------------------------------------------------------------- */ void ExecInsert(TupleTableSlot *slot, DestReceiver *dest, EState *estate, PlanGenerator planGen, bool isUpdate) { void *tuple = NULL; ResultRelInfo *resultRelInfo = NULL; Relation resultRelationDesc = NULL; Oid newId = InvalidOid; TupleTableSlot *partslot = NULL; AOTupleId aoTupleId = AOTUPLEID_INIT; bool rel_is_heap = false; bool rel_is_aorows = false; bool rel_is_external = false; bool rel_is_parquet = false; /* * get information on the (current) result relation */ if (estate->es_result_partitions) { resultRelInfo = slot_get_partition(slot, estate); estate->es_result_relation_info = resultRelInfo; if (NULL != resultRelInfo->ri_parquetSendBack) { /* * The Parquet part we are about to insert into * has sendBack information. This means we're inserting into the * part twice, which is not supported. Error out (GPSQL-2291) */ Assert(gp_parquet_insert_sort); ereport(ERROR, (errcode(ERRCODE_CDB_FEATURE_NOT_YET), errmsg("Cannot insert out-of-order tuples in parquet partitions"), errhint("Sort the data on the partitioning key(s) before inserting"), errOmitLocation(true))); } /* * Check if we need to close the last parquet partition we * inserted into (GPSQL-2291). */ Oid new_part_oid = resultRelInfo->ri_RelationDesc->rd_id; if (gp_parquet_insert_sort && PLANGEN_OPTIMIZER == planGen && InvalidOid != estate->es_last_parq_part && new_part_oid != estate->es_last_parq_part) { Assert(NULL != estate->es_partition_state->result_partition_hash); ResultPartHashEntry *entry = hash_search(estate->es_partition_state->result_partition_hash, &estate->es_last_parq_part, HASH_FIND, NULL /* found */); Assert(NULL != entry); Assert(entry->offset < estate->es_num_result_relations); ResultRelInfo *oldResultRelInfo = & estate->es_result_relations[entry->offset]; elog(DEBUG1, "Switching from old part oid=%d name=[%s] to new part oid=%d name=[%s]", estate->es_last_parq_part, oldResultRelInfo->ri_RelationDesc->rd_rel->relname.data, new_part_oid, resultRelInfo->ri_RelationDesc->rd_rel->relname.data); /* * We are opening a new partition, and the last partition we * inserted into was a Parquet part. Let's close the old * parquet insert descriptor to free the memory before * opening the new one. */ ParquetInsertDescData *oldInsertDesc = oldResultRelInfo->ri_parquetInsertDesc; /* * We need to preserve the "sendback" information that needs to be * sent back to the QD process from this part. * Compute it here, and store it for later use. */ QueryContextDispatchingSendBack sendback = CreateQueryContextDispatchingSendBack(1); sendback->relid = RelationGetRelid(oldResultRelInfo->ri_RelationDesc); oldInsertDesc->sendback = sendback; parquet_insert_finish(oldInsertDesc); /* Store the sendback information in the resultRelInfo for this part */ oldResultRelInfo->ri_parquetSendBack = sendback; /* Record in the resultRelInfo that we closed the parquet insert descriptor */ oldResultRelInfo->ri_parquetInsertDesc = NULL; /* Reset the last parquet part Oid, it's now closed */ estate->es_last_parq_part = InvalidOid; } } else { resultRelInfo = estate->es_result_relation_info; } Assert (!resultRelInfo->ri_projectReturning); resultRelationDesc = resultRelInfo->ri_RelationDesc; rel_is_heap = RelationIsHeap(resultRelationDesc); rel_is_aorows = RelationIsAoRows(resultRelationDesc); rel_is_external = RelationIsExternal(resultRelationDesc); rel_is_parquet = RelationIsParquet(resultRelationDesc); /* Validate that insert is not part of an non-allowed update operation. */ if (isUpdate && (rel_is_aorows || rel_is_parquet)) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Append-only tables are not updatable. Operation not permitted."), errOmitLocation(true))); } partslot = reconstructMatchingTupleSlot(slot, resultRelInfo); if (rel_is_heap || rel_is_external) { tuple = ExecFetchSlotHeapTuple(partslot); } else if (rel_is_aorows) { tuple = ExecFetchSlotMemTuple(partslot, false); } else if (rel_is_parquet) { tuple = NULL; } Assert( partslot != NULL ); Assert( rel_is_parquet || (tuple != NULL)); /* Execute triggers in Planner-generated plans */ if (planGen == PLANGEN_PLANNER) { /* BEFORE ROW INSERT Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_INSERT] > 0) { HeapTuple newtuple; /* NYI */ if(rel_is_parquet) elog(ERROR, "triggers are not supported on tables that use column-oriented storage"); newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); if (newtuple == NULL) /* "do nothing" */ { return; } if (newtuple != tuple) /* modified by Trigger(s) */ { /* * Put the modified tuple into a slot for convenience of routines * below. We assume the tuple was allocated in per-tuple memory * context, and therefore will go away by itself. The tuple table * slot should not try to clear it. */ TupleTableSlot *newslot = estate->es_trig_tuple_slot; if (newslot->tts_tupleDescriptor != partslot->tts_tupleDescriptor) ExecSetSlotDescriptor(newslot, partslot->tts_tupleDescriptor); ExecStoreGenericTuple(newtuple, newslot, false); newslot->tts_tableOid = partslot->tts_tableOid; /* for constraints */ tuple = newtuple; partslot = newslot; } } } /* * Check the constraints of the tuple */ if (resultRelationDesc->rd_att->constr && planGen == PLANGEN_PLANNER) { ExecConstraints(resultRelInfo, partslot, estate); } /* * insert the tuple * * Note: heap_insert returns the tid (location) of the new tuple in the * t_self field. * * NOTE: for append-only relations we use the append-only access methods. */ if (rel_is_aorows) { if (resultRelInfo->ri_aoInsertDesc == NULL) { ResultRelSegFileInfo *segfileinfo = NULL; /* Set the pre-assigned fileseg number to insert into */ ResultRelInfoSetSegFileInfo(resultRelInfo, estate->es_result_segfileinfos); segfileinfo = (ResultRelSegFileInfo *)list_nth(resultRelInfo->ri_aosegfileinfos, GetQEIndex()); resultRelInfo->ri_aoInsertDesc = appendonly_insert_init(resultRelationDesc, segfileinfo); } appendonly_insert(resultRelInfo->ri_aoInsertDesc, tuple, &newId, &aoTupleId); } else if (rel_is_external) { /* Writable external table */ if (resultRelInfo->ri_extInsertDesc == NULL) resultRelInfo->ri_extInsertDesc = external_insert_init( resultRelationDesc, 0); newId = external_insert(resultRelInfo->ri_extInsertDesc, tuple); } else if(rel_is_parquet) { /* If there is no parquet insert descriptor, create it now. */ if (resultRelInfo->ri_parquetInsertDesc == NULL) { ResultRelSegFileInfo *segfileinfo = NULL; ResultRelInfoSetSegFileInfo(resultRelInfo, estate->es_result_segfileinfos); segfileinfo = (ResultRelSegFileInfo *)list_nth(resultRelInfo->ri_aosegfileinfos, GetQEIndex()); resultRelInfo->ri_parquetInsertDesc = parquet_insert_init(resultRelationDesc, segfileinfo); /* * Just opened a new parquet partition for insert. Save the Oid * in estate, so that we can close it when switching to a * new partition (GPSQL-2291) */ elog(DEBUG1, "Saving es_last_parq_part. Old=%d, new=%d", estate->es_last_parq_part, resultRelationDesc->rd_id); estate->es_last_parq_part = resultRelationDesc->rd_id; } newId = parquet_insert(resultRelInfo->ri_parquetInsertDesc, partslot); } else { Insist(rel_is_heap); newId = heap_insert(resultRelationDesc, tuple, estate->es_snapshot->curcid, true, true, GetCurrentTransactionId()); } IncrAppended(); (estate->es_processed)++; (resultRelInfo->ri_aoprocessed)++; estate->es_lastoid = newId; partslot->tts_tableOid = RelationGetRelid(resultRelationDesc); if (rel_is_aorows || rel_is_parquet) { /* NOTE: Current version does not support index upon parquet table. */ /* * insert index entries for AO Row-Store tuple */ if (resultRelInfo->ri_NumIndices > 0 && !rel_is_parquet) ExecInsertIndexTuples(partslot, (ItemPointer)&aoTupleId, estate, false); } else { /* Use parttuple for index update in case this is an indexed heap table. */ TupleTableSlot *xslot = partslot; void *xtuple = tuple; setLastTid(&(((HeapTuple) xtuple)->t_self)); /* * insert index entries for tuple */ if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(xslot, &(((HeapTuple) xtuple)->t_self), estate, false); } if (planGen == PLANGEN_PLANNER) { /* AFTER ROW INSERT Triggers */ ExecARInsertTriggers(estate, resultRelInfo, tuple); } }
// ----------------------------------------------------------------------------- // Rebuild filespace persistent table 'gp_persistent_filespace_node' // ----------------------------------------------------------------------------- void PersistentFilespace_AddCreated( Oid filespaceOid, /* The filespace OID to be added. */ bool flushToXLog) /* When true, the XLOG record for this change will be flushed to disk. */ { WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; PersistentFileSysObjName fsObjName; ItemPointerData persistentTid; int64 persistentSerialNum; FilespaceDirEntry filespaceDirEntry; /*if (Persistent_BeforePersistenceWork()) { if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "Skipping persistent filespace %u because we are before persistence work", filespaceOid); return; // The initdb process will load the persistent table once we out of bootstrap mode. }*/ PersistentFilespace_VerifyInitScan(); PersistentFileSysObjName_SetFilespaceDir(&fsObjName,filespaceOid,is_filespace_shared); WRITE_PERSISTENT_STATE_ORDERED_LOCK; filespaceDirEntry = PersistentFilespace_CreateDirUnderLock(filespaceOid); if (filespaceDirEntry == NULL) { /* If out of shared memory, no need to promote to PANIC. */ WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of shared-memory for persistent filespaces"), errhint("You may need to increase the gp_max_filespaces value"), errOmitLocation(true))); } // if it is a new generated one, we need to set info from pg_filespace_entry if(filespaceDirEntry->persistentSerialNum==0 || strlen(filespaceDirEntry->locationBlankPadded1)==0) { Relation pg_fs_entry_rel; HeapScanDesc scandesc; HeapTuple tuple; ScanKeyData entry[1]; bool isNull; Datum locDatum; char *loc; /* Lookup the information for the current pg_filespace_entry */ pg_fs_entry_rel = heap_open(FileSpaceEntryRelationId, AccessShareLock); ScanKeyInit(&entry[0], Anum_pg_filespace_entry_fsefsoid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(filespaceOid)); scandesc = heap_beginscan(pg_fs_entry_rel, SnapshotNow, 1, entry); tuple = heap_getnext(scandesc, ForwardScanDirection); /* We assume that there can be at most one matching tuple */ if (!HeapTupleIsValid(tuple)) { elog(ERROR, "filespace %u could not be found in pg_filespace_entry", filespaceOid); } locDatum = heap_getattr(tuple, Anum_pg_filespace_entry_fselocation, pg_fs_entry_rel->rd_att, &isNull); loc = TextDatumGetCString(locDatum); //convert location with blank padded memset(filespaceDirEntry->locationBlankPadded1, ' ', FilespaceLocationBlankPaddedWithNullTermLen); filespaceDirEntry->locationBlankPadded1[FilespaceLocationBlankPaddedWithNullTermLen-1]='\0'; memcpy(filespaceDirEntry->locationBlankPadded1, loc, strlen(loc)); if(isNull) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("internal error: filespace '%u' has no name defined", filespaceOid))); heap_endscan(scandesc); heap_close(pg_fs_entry_rel, AccessShareLock); } filespaceDirEntry->state = PersistentFileSysState_Created; PersistentFilespace_AddTuple( filespaceDirEntry, /* createMirrorDataLossTrackingSessionNum */ 0, /* reserved */ 0, /* parentXid */ InvalidTransactionId, flushToXLog); persistentTid = filespaceDirEntry->persistentTid; persistentSerialNum = filespaceDirEntry->persistentSerialNum; WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "Persistent filespace directory: Add '%s' in state 'Created', serial number " INT64_FORMAT " at TID '%s' ", PersistentFileSysObjName_ObjectName(&fsObjName), persistentSerialNum, ItemPointerToString(&persistentTid)); }
void abstime2tm(AbsoluteTime _time, int *tzp, struct pg_tm * tm, char **tzn) { pg_time_t time = (pg_time_t) _time; struct pg_tm *tx; /* * If HasCTZSet is true then we have a brute force time zone specified. Go * ahead and rotate to the local time zone since we will later bypass any * calls which adjust the tm fields. */ if (HasCTZSet && (tzp != NULL)) time -= CTimeZone; if (!HasCTZSet && tzp != NULL) tx = pg_localtime(&time, session_timezone); else tx = pg_gmtime(&time); tm->tm_year = tx->tm_year + 1900; tm->tm_mon = tx->tm_mon + 1; tm->tm_mday = tx->tm_mday; tm->tm_hour = tx->tm_hour; tm->tm_min = tx->tm_min; tm->tm_sec = tx->tm_sec; tm->tm_isdst = tx->tm_isdst; tm->tm_gmtoff = tx->tm_gmtoff; tm->tm_zone = tx->tm_zone; if (tzp != NULL) { /* * We have a brute force time zone per SQL99? Then use it without * change since we have already rotated to the time zone. */ if (HasCTZSet) { *tzp = CTimeZone; tm->tm_gmtoff = CTimeZone; tm->tm_isdst = 0; tm->tm_zone = NULL; if (tzn != NULL) *tzn = NULL; } else { *tzp = -tm->tm_gmtoff; /* tm_gmtoff is Sun/DEC-ism */ /* * XXX FreeBSD man pages indicate that this should work - tgl * 97/04/23 */ if (tzn != NULL) { /* * Copy no more than MAXTZLEN bytes of timezone to tzn, in * case it contains an error message, which doesn't fit in the * buffer */ StrNCpy(*tzn, tm->tm_zone, MAXTZLEN + 1); if (strlen(tm->tm_zone) > MAXTZLEN) ereport(WARNING, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid time zone name: \"%s\"", tm->tm_zone), errOmitLocation(true))); } } } else tm->tm_isdst = -1; }
/* * Import data into GPDB. */ Datum demoprot_import(PG_FUNCTION_ARGS) { extprotocol_t *myData; char *data; int datlen; size_t nread = 0; /* Must be called via the external table format manager */ if (!CALLED_AS_EXTPROTOCOL(fcinfo)) elog(ERROR, "extprotocol_import: not called by external protocol manager"); /* Get our internal description of the protocol */ myData = (extprotocol_t *) EXTPROTOCOL_GET_USER_CTX(fcinfo); if(EXTPROTOCOL_IS_LAST_CALL(fcinfo)) { /* we're done receiving data. close our connection */ if(myData && myData->file) if(fclose(myData->file)) ereport(ERROR, (errcode_for_file_access(), errmsg("could not close file \"%s\": %m", myData->filename))); PG_RETURN_INT32(0); } if (myData == NULL) { /* first call. do any desired init */ const char *p_name = "demoprot"; DemoUri *parsed_url; char *url = EXTPROTOCOL_GET_URL(fcinfo); myData = palloc(sizeof(extprotocol_t)); myData->url = pstrdup(url); parsed_url = ParseDemoUri(myData->url); myData->filename = pstrdup(parsed_url->path); if(strcasecmp(parsed_url->protocol, p_name) != 0) elog(ERROR, "internal error: demoprot called with a different protocol (%s)", parsed_url->protocol); FreeDemoUri(parsed_url); /* open the destination file (or connect to remote server in other cases) */ myData->file = fopen(myData->filename, "r"); if (myData->file == NULL) ereport(ERROR, (errcode_for_file_access(), errmsg("demoprot_import: could not open file \"%s\" for reading: %m", myData->filename), errOmitLocation(true))); EXTPROTOCOL_SET_USER_CTX(fcinfo, myData); } /* ======================================================================= * DO THE IMPORT * ======================================================================= */ data = EXTPROTOCOL_GET_DATABUF(fcinfo); datlen = EXTPROTOCOL_GET_DATALEN(fcinfo); if(datlen > 0) { nread = fread(data, 1, datlen, myData->file); if (ferror(myData->file)) ereport(ERROR, (errcode_for_file_access(), errmsg("demoprot_import: could not write to file \"%s\": %m", myData->filename))); } PG_RETURN_INT32((int)nread); }
/* * Indicate we intend to create a filespace file as part of the current transaction. * * An XLOG IntentToCreate record is generated that will guard the subsequent file-system * create in case the transaction aborts. * * After 1 or more calls to this routine to mark intention about filespace files that are going * to be created, call ~_DoPendingCreates to do the actual file-system creates. (See its * note on XLOG flushing). */ void PersistentFilespace_MarkCreatePending( Oid filespaceOid, char *filespaceLocation, ItemPointer persistentTid, int64 *persistentSerialNum, bool flushToXLog) { WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; PersistentFileSysObjName fsObjName; FilespaceDirEntry filespaceDirEntry; if (Persistent_BeforePersistenceWork()) { if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "Skipping persistent filespace %u because we are before persistence work", filespaceOid); return; // The initdb process will load the persistent table once we out of bootstrap mode. } PersistentFilespace_VerifyInitScan(); PersistentFileSysObjName_SetFilespaceDir(&fsObjName,filespaceOid,is_filespace_shared); WRITE_PERSISTENT_STATE_ORDERED_LOCK; filespaceDirEntry = PersistentFilespace_CreateDirUnderLock(filespaceOid); if (filespaceDirEntry == NULL) { /* If out of shared memory, no need to promote to PANIC. */ WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of shared-memory for persistent filespaces"), errhint("You may need to increase the gp_max_filespaces value"), errOmitLocation(true))); } PersistentFilespace_BlankPadCopyLocation( filespaceDirEntry->locationBlankPadded1, filespaceLocation); filespaceDirEntry->state = PersistentFileSysState_CreatePending; PersistentFilespace_AddTuple( filespaceDirEntry, /* createMirrorDataLossTrackingSessionNum */ 0, /* reserved */ 0, /* parentXid */ GetTopTransactionId(), flushToXLog); *persistentTid = filespaceDirEntry->persistentTid; *persistentSerialNum = filespaceDirEntry->persistentSerialNum; /* * This XLOG must be generated under the persistent write-lock. */ #ifdef MASTER_MIRROR_SYNC mmxlog_log_create_filespace(filespaceOid); #endif #ifdef FAULT_INJECTOR FaultInjector_InjectFaultIfSet( FaultBeforePendingDeleteFilespaceEntry, DDLNotSpecified, "", // databaseName ""); // tableName #endif /* * MPP-18228 * To make adding 'Create Pending' entry to persistent table and adding * to the PendingDelete list atomic */ PendingDelete_AddCreatePendingEntryWrapper( &fsObjName, persistentTid, *persistentSerialNum); WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "Persistent filespace directory: Add '%s' in state 'Created', serial number " INT64_FORMAT " at TID %s", PersistentFileSysObjName_ObjectName(&fsObjName), *persistentSerialNum, ItemPointerToString(persistentTid)); }
/* * 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 */ }
/* * make_scalar_array_op() * Build expression tree for "scalar op ANY/ALL (array)" construct. */ Expr * make_scalar_array_op(ParseState *pstate, List *opname, bool useOr, Node *ltree, Node *rtree, int location) { Oid ltypeId, rtypeId, atypeId, res_atypeId; Operator tup; Form_pg_operator opform; Oid actual_arg_types[2]; Oid declared_arg_types[2]; List *args; Oid rettype; ScalarArrayOpExpr *result; ltypeId = exprType(ltree); atypeId = exprType(rtree); /* * The right-hand input of the operator will be the element type of the * array. However, if we currently have just an untyped literal on the * right, stay with that and hope we can resolve the operator. */ if (atypeId == UNKNOWNOID) rtypeId = UNKNOWNOID; else { rtypeId = get_element_type(atypeId); if (!OidIsValid(rtypeId)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires array on right side"), errOmitLocation(true), parser_errposition(pstate, location))); } /* Now resolve the operator */ tup = oper(pstate, opname, ltypeId, rtypeId, false, location); opform = (Form_pg_operator) GETSTRUCT(tup); args = list_make2(ltree, rtree); actual_arg_types[0] = ltypeId; actual_arg_types[1] = rtypeId; declared_arg_types[0] = opform->oprleft; declared_arg_types[1] = opform->oprright; /* * enforce consistency with ANYARRAY and ANYELEMENT argument and return * types, possibly adjusting return type or declared_arg_types (which will * be used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, 2, opform->oprresult); /* * Check that operator result is boolean */ if (rettype != BOOLOID) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator to yield boolean"), errOmitLocation(true), parser_errposition(pstate, location))); if (get_func_retset(opform->oprcode)) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator not to return a set"), errOmitLocation(true), parser_errposition(pstate, location))); /* * Now switch back to the array type on the right, arranging for any * needed cast to be applied. */ res_atypeId = get_array_type(declared_arg_types[1]); if (!OidIsValid(res_atypeId)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(declared_arg_types[1])), errOmitLocation(true), parser_errposition(pstate, location))); actual_arg_types[1] = atypeId; declared_arg_types[1] = res_atypeId; /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = makeNode(ScalarArrayOpExpr); result->opno = oprid(tup); result->opfuncid = InvalidOid; result->useOr = useOr; result->args = args; ReleaseOperator(tup); return (Expr *) result; }
/* * Indicate we intend to create a relation file as part of the current transaction. * * An XLOG IntentToCreate record is generated that will guard the subsequent file-system * create in case the transaction aborts. * * After 1 or more calls to this routine to mark intention about relation files that are going * to be created, call ~_DoPendingCreates to do the actual file-system creates. (See its * note on XLOG flushing). */ void PersistentDatabase_MarkCreatePending( DbDirNode *dbDirNode, ItemPointer persistentTid, int64 *persistentSerialNum, bool flushToXLog) { WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; DatabaseDirEntry databaseDirEntry; SharedOidSearchAddResult addResult; PersistentFileSysObjName fsObjName; if (Persistent_BeforePersistenceWork()) { if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "Skipping persistent database '%s' because we are before persistence work", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace)); /* * The initdb process will load the persistent table once we * out of bootstrap mode. */ return; } PersistentDatabase_VerifyInitScan(); PersistentFileSysObjName_SetDatabaseDir( &fsObjName, dbDirNode->tablespace, dbDirNode->database, is_tablespace_shared); WRITE_PERSISTENT_STATE_ORDERED_LOCK; databaseDirEntry = (DatabaseDirEntry) SharedOidSearch_Find( &persistentDatabaseSharedData->databaseDirSearchTable, dbDirNode->database, dbDirNode->tablespace); if (databaseDirEntry != NULL) elog(ERROR, "Persistent database entry '%s' already exists in state '%s'", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace), PersistentFileSysObjState_Name(databaseDirEntry->state)); addResult = SharedOidSearch_Add( &persistentDatabaseSharedData->databaseDirSearchTable, dbDirNode->database, dbDirNode->tablespace, (SharedOidSearchObjHeader**)&databaseDirEntry); if (addResult == SharedOidSearchAddResult_NoMemory) { /* If out of shared memory, no need to promote to PANIC. */ WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of shared-memory for persistent databases"), errhint("You may need to increase the gp_max_databases and " "gp_max_tablespaces value"), errOmitLocation(true))); } else if (addResult == SharedOidSearchAddResult_Exists) elog(PANIC, "Persistent database entry '%s' already exists in state '%s'", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace), PersistentFileSysObjState_Name(databaseDirEntry->state)); else Assert(addResult == SharedOidSearchAddResult_Ok); databaseDirEntry->state = PersistentFileSysState_CreatePending; databaseDirEntry->iteratorRefCount = 0; PersistentDatabase_AddTuple( databaseDirEntry, /* reserved */ 0, /* parentXid */ GetTopTransactionId(), flushToXLog); *persistentTid = databaseDirEntry->persistentTid; *persistentSerialNum = databaseDirEntry->persistentSerialNum; /* * This XLOG must be generated under the persistent write-lock. */ #ifdef MASTER_MIRROR_SYNC mmxlog_log_create_database(dbDirNode->tablespace, dbDirNode->database); #endif #ifdef FAULT_INJECTOR FaultInjector_InjectFaultIfSet( FaultBeforePendingDeleteDatabaseEntry, DDLNotSpecified, "", // databaseName ""); // tableName #endif /* * MPP-18228 * To make adding 'Create Pending' entry to persistent table and adding * to the PendingDelete list atomic */ PendingDelete_AddCreatePendingEntryWrapper( &fsObjName, persistentTid, *persistentSerialNum); WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; }
/* ---------------------------------------------------------------- * ExecUpdate * * note: we can't run UPDATE queries with transactions * off because UPDATEs are actually INSERTs and our * scan will mistakenly loop forever, updating the tuple * it just inserted.. This should be fixed but until it * is, we don't want to get stuck in an infinite loop * which corrupts your database.. * ---------------------------------------------------------------- */ void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid, TupleTableSlot *planSlot, DestReceiver *dest, EState *estate) { HeapTuple tuple; ResultRelInfo *resultRelInfo; Relation resultRelationDesc; HTSU_Result result; ItemPointerData update_ctid; TransactionId update_xmax; /* * abort the operation if not running transactions */ if (IsBootstrapProcessingMode()) elog(ERROR, "cannot UPDATE during bootstrap"); /* * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ tuple = ExecFetchSlotHeapTuple(slot); /* * get information on the (current) result relation */ resultRelInfo = estate->es_result_relation_info; resultRelationDesc = resultRelInfo->ri_RelationDesc; /* see if this update would move the tuple to a different partition */ if (estate->es_result_partitions) { AttrNumber max_attr; Datum *values; bool *nulls; Oid targetid; Assert(estate->es_partition_state != NULL && estate->es_partition_state->accessMethods != NULL); if (!estate->es_partition_state->accessMethods->part_cxt) estate->es_partition_state->accessMethods->part_cxt = GetPerTupleExprContext(estate)->ecxt_per_tuple_memory; Assert(PointerIsValid(estate->es_result_partitions)); max_attr = estate->es_partition_state->max_partition_attr; slot_getsomeattrs(slot, max_attr); values = slot_get_values(slot); nulls = slot_get_isnull(slot); targetid = selectPartition(estate->es_result_partitions, values, nulls, slot->tts_tupleDescriptor, estate->es_partition_state->accessMethods); if (!OidIsValid(targetid)) ereport(ERROR, (errcode(ERRCODE_NO_PARTITION_FOR_PARTITIONING_KEY), errmsg("no partition for partitioning key"))); if (RelationGetRelid(resultRelationDesc) != targetid) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("moving tuple from partition \"%s\" to " "partition \"%s\" not supported", get_rel_name(RelationGetRelid(resultRelationDesc)), get_rel_name(targetid)), errOmitLocation(true))); } } /* BEFORE ROW UPDATE Triggers */ if (resultRelInfo->ri_TrigDesc && resultRelInfo->ri_TrigDesc->n_before_row[TRIGGER_EVENT_UPDATE] > 0) { HeapTuple newtuple; newtuple = ExecBRUpdateTriggers(estate, resultRelInfo, tupleid, tuple, estate->es_snapshot->curcid); if (newtuple == NULL) /* "do nothing" */ return; if (newtuple != tuple) /* modified by Trigger(s) */ { /* * Put the modified tuple into a slot for convenience of routines * below. We assume the tuple was allocated in per-tuple memory * context, and therefore will go away by itself. The tuple table * slot should not try to clear it. */ TupleTableSlot *newslot = estate->es_trig_tuple_slot; if (newslot->tts_tupleDescriptor != slot->tts_tupleDescriptor) ExecSetSlotDescriptor(newslot, slot->tts_tupleDescriptor); ExecStoreGenericTuple(newtuple, newslot, false); newslot->tts_tableOid = slot->tts_tableOid; /* for constraints */ slot = newslot; tuple = newtuple; } } /* * Check the constraints of the tuple * * If we generate a new candidate tuple after EvalPlanQual testing, we * must loop back here and recheck constraints. (We don't need to redo * triggers, however. If there are any BEFORE triggers then trigger.c * will have done heap_lock_tuple to lock the correct tuple, so there's no * need to do them again.) */ lreplace:; if (resultRelationDesc->rd_att->constr) ExecConstraints(resultRelInfo, slot, estate); if (!GpPersistent_IsPersistentRelation(resultRelationDesc->rd_id)) { /* * Normal UPDATE path. */ /* * replace the heap tuple * * Note: if es_crosscheck_snapshot isn't InvalidSnapshot, we check that * the row to be updated is visible to that snapshot, and throw a can't- * serialize error if not. This is a special-case behavior needed for * referential integrity updates in serializable transactions. */ result = heap_update(resultRelationDesc, tupleid, tuple, &update_ctid, &update_xmax, estate->es_snapshot->curcid, estate->es_crosscheck_snapshot, true /* wait for commit */ ); switch (result) { case HeapTupleSelfUpdated: /* already deleted by self; nothing to do */ return; case HeapTupleMayBeUpdated: break; case HeapTupleUpdated: if (IsXactIsoLevelSerializable) ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); else if (!ItemPointerEquals(tupleid, &update_ctid)) { TupleTableSlot *epqslot; epqslot = EvalPlanQual(estate, resultRelInfo->ri_RangeTableIndex, &update_ctid, update_xmax, estate->es_snapshot->curcid); if (!TupIsNull(epqslot)) { *tupleid = update_ctid; slot = ExecFilterJunk(estate->es_junkFilter, epqslot); tuple = ExecFetchSlotHeapTuple(slot); goto lreplace; } } /* tuple already deleted; nothing to do */ return; default: elog(ERROR, "unrecognized heap_update status: %u", result); return; } } else { HeapTuple persistentTuple; /* * Persistent metadata path. */ persistentTuple = heap_copytuple(tuple); persistentTuple->t_self = *tupleid; frozen_heap_inplace_update(resultRelationDesc, persistentTuple); heap_freetuple(persistentTuple); } IncrReplaced(); (estate->es_processed)++; /* * Note: instead of having to update the old index tuples associated with * the heap tuple, all we do is form and insert new index tuples. This is * because UPDATEs are actually DELETEs and INSERTs, and index tuple * deletion is done later by VACUUM (see notes in ExecDelete). All we do * here is insert new index tuples. -cim 9/27/89 */ /* * insert index entries for tuple * * Note: heap_update returns the tid (location) of the new tuple in the * t_self field. */ if (resultRelInfo->ri_NumIndices > 0) ExecInsertIndexTuples(slot, &(tuple->t_self), estate, false); /* AFTER ROW UPDATE Triggers */ ExecARUpdateTriggers(estate, resultRelInfo, tupleid, tuple); }
void PersistentDatabase_AddCreated( DbDirNode *dbDirNode, /* The tablespace and database OIDs for the create. */ ItemPointer persistentTid, /* TID of the gp_persistent_rel_files tuple for the rel file */ bool flushToXLog) /* When true, the XLOG record for this change will be flushed to disk. */ { WRITE_PERSISTENT_STATE_ORDERED_LOCK_DECLARE; PersistentFileSysObjName fsObjName; DatabaseDirEntry databaseDirEntry; SharedOidSearchAddResult addResult; int64 persistentSerialNum; if (!Persistent_BeforePersistenceWork()) elog(ERROR, "We can only add to persistent meta-data when special states"); // Verify PersistentFileSysObj_BuildInitScan has been called. PersistentDatabase_VerifyInitScan(); PersistentFileSysObjName_SetDatabaseDir(&fsObjName,dbDirNode->tablespace,dbDirNode->database,is_tablespace_shared); WRITE_PERSISTENT_STATE_ORDERED_LOCK; /* * GpIdentity.segindex * In the initdb, GpIdentity.segindex is set to -10000. It will update this * value to the correct GpIdentity.segindex. */ databaseDirEntry = (DatabaseDirEntry) SharedOidSearch_Find( &persistentDatabaseSharedData->databaseDirSearchTable, dbDirNode->database, dbDirNode->tablespace); if (databaseDirEntry != NULL) elog(ERROR, "Persistent database entry '%s' already exists in state '%s'", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace), PersistentFileSysObjState_Name(databaseDirEntry->state)); addResult = SharedOidSearch_Add( &persistentDatabaseSharedData->databaseDirSearchTable, dbDirNode->database, dbDirNode->tablespace, (SharedOidSearchObjHeader**)&databaseDirEntry); if (addResult == SharedOidSearchAddResult_NoMemory) { /* If out of shared memory, no need to promote to PANIC. */ WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Out of shared-memory for persistent databases"), errhint("You may need to increase the gp_max_databases and " "gp_max_tablespaces value"), errOmitLocation(true))); } else if (addResult == SharedOidSearchAddResult_Exists) elog(PANIC, "Persistent database entry '%s' already exists in state '%s'", GetDatabasePath( dbDirNode->database, dbDirNode->tablespace), PersistentFileSysObjState_Name(databaseDirEntry->state)); else Assert(addResult == SharedOidSearchAddResult_Ok); databaseDirEntry->state = PersistentFileSysState_Created; databaseDirEntry->iteratorRefCount = 0; PersistentDatabase_AddTuple( databaseDirEntry, /* reserved */ 0, InvalidTransactionId, flushToXLog); *persistentTid = databaseDirEntry->persistentTid; persistentSerialNum = databaseDirEntry->persistentSerialNum; WRITE_PERSISTENT_STATE_ORDERED_UNLOCK; if (Debug_persistent_print) elog(Persistent_DebugPrintLevel(), "Persistent database directory: Add '%s' in state 'Created', serial number " INT64_FORMAT " at TID %s", PersistentFileSysObjName_ObjectName(&fsObjName), persistentSerialNum, ItemPointerToString(persistentTid)); }
/* ---------------------------------------------------------------- * cql1() * The underlying function for the cql() macro. * find the bind parameters in a caql string and build a key list * ---------------------------------------------------------------- */ cq_list * cql1(const char* caqlStr, const char* filename, int lineno, ...) { int maxkeys = 0; int badbind = 0; cq_list *pcql = (cq_list *) palloc0(sizeof(cq_list)); const char* pc = caqlStr; if ((NULL == caqlStr) || (0 == strlen(caqlStr))) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "Invalid or undefined CaQL string" ), errOmitLocation(true))); pcql->bGood = true; pcql->caqlStr = caqlStr; pcql->filename = filename; pcql->lineno = lineno; while (*pc && (maxkeys < 5)) { if (*pc != ':') { pc++; continue; } /* look for numeric bind parameter of the form ":1" to ":5" */ pc++; if (!*pc) break; switch (*pc) { case '1': if (maxkeys != 0) { badbind = 1; goto L_wrong_args; } maxkeys++; break; case '2': if (maxkeys != 1) { badbind = 2; goto L_wrong_args; } maxkeys++; break; case '3': if (maxkeys != 2) { badbind = 3; goto L_wrong_args; } maxkeys++; break; case '4': if (maxkeys != 3) { badbind = 4; goto L_wrong_args; } maxkeys++; break; case '5': if (maxkeys != 4) { badbind = 5; goto L_wrong_args; } maxkeys++; break; case '6': case '7': case '8': case '9': case '0': { badbind = 6; goto L_wrong_args; } default: break; } /* end switch */ } /* end while */ pcql->maxkeys = maxkeys; if (maxkeys) { va_list ap; va_start(ap, lineno); for (int ii = 0; ii < maxkeys; ii++) { pcql->cqlKeys[ii] = va_arg(ap, Datum); } va_end(ap); } return (pcql); L_wrong_args: if (badbind != 5) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "bind parameter out of range (1-5)" ), errOmitLocation(true))); else ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg( "missing bind parameter before :%d, or out of sequence", badbind ), errOmitLocation(true))); return NULL; } /* end cql1 */
/* * Implements the 'PREPARE' utility statement. */ void PrepareQuery(PrepareStmt *stmt, const char *queryString) { const char *commandTag = NULL; Query *query = NULL; List *query_list = NIL; List *plan_list = NIL; List *query_list_copy = NIL; NodeTag srctag; /* GPDB */ /* * Disallow empty-string statement name (conflicts with protocol-level * unnamed statement). */ if (!stmt->name || stmt->name[0] == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION), errmsg("invalid statement name: must not be empty"))); switch (stmt->query->commandType) { case CMD_SELECT: commandTag = "SELECT"; srctag = T_SelectStmt; break; case CMD_INSERT: commandTag = "INSERT"; srctag = T_InsertStmt; break; case CMD_UPDATE: commandTag = "UPDATE"; srctag = T_UpdateStmt; break; case CMD_DELETE: commandTag = "DELETE"; srctag = T_DeleteStmt; break; default: ereport(ERROR, (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION), errmsg("utility statements cannot be prepared"), errOmitLocation(true))); commandTag = NULL; /* keep compiler quiet */ srctag = T_Query; break; } /* * Parse analysis is already done, but we must still rewrite and plan the * query. */ /* * Because the planner is not cool about not scribbling on its input, we * make a preliminary copy of the source querytree. This prevents * problems in the case that the PREPARE is in a portal or plpgsql * function and is executed repeatedly. (See also the same hack in * DECLARE CURSOR and EXPLAIN.) XXX the planner really shouldn't modify * its input ... FIXME someday. */ query = copyObject(stmt->query); /* Rewrite the query. The result could be 0, 1, or many queries. */ AcquireRewriteLocks(query); query_list = QueryRewrite(query); query_list_copy = copyObject(query_list); /* planner scribbles on query tree */ /* Generate plans for queries. Snapshot is already set. */ plan_list = pg_plan_queries(query_list, NULL, false, QRL_NONE); /* * Save the results. We don't have the query string for this PREPARE, but * we do have the string we got from the client, so use that. */ StorePreparedStatement(stmt->name, queryString, /* WAS global debug_query_string, */ srctag, commandTag, query_list_copy, stmt->argtype_oids, true); }
/* * Wait for all QEs to finish, then report any errors from the given * CdbDispatchResults objects and free them. If not all QEs in the * associated gang(s) executed the command successfully, throws an * error and does not return. No-op if both CdbDispatchResults ptrs are NULL. * This is a convenience function; callers with unusual requirements may * instead call CdbCheckDispatchResult(), etc., directly. */ void cdbdisp_finishCommand(struct CdbDispatcherState *ds, void (*handle_results_callback) (CdbDispatchResults * primaryResults, void *ctx), void *ctx) { StringInfoData buf; int errorcode = 0; /* * If cdbdisp_dispatchToGang() wasn't called, don't wait. */ if (!ds || !ds->primaryResults) return; /* * If we are called in the dying sequence, don't touch QE connections. * Anything below could cause ERROR in which case we would miss a chance * to clean up shared memory as this is from AbortTransaction. * QE may stay a bit longer, but since we can consider QD as libpq * client to QE, they will notice that we as a client do not * appear anymore and will finish soon. Also ERROR report doesn't * go to the client anyway since we are in proc_exit. */ if (proc_exit_inprogress) return; /* * Wait for all QEs to finish. Don't cancel them. */ CdbCheckDispatchResult(ds, DISPATCH_WAIT_NONE); /* * If no errors, free the CdbDispatchResults objects and return. */ if (ds->primaryResults) errorcode = ds->primaryResults->errcode; if (!errorcode) { /* * Call the callback function to handle the results */ if (handle_results_callback != NULL) handle_results_callback(ds->primaryResults, ctx); cdbdisp_destroyDispatcherState(ds); return; } /* * Format error messages from the primary gang. */ initStringInfo(&buf); cdbdisp_dumpDispatchResults(ds->primaryResults, &buf); cdbdisp_destroyDispatcherState(ds); /* * Too bad, our gang got an error. */ PG_TRY(); { ereport(ERROR, (errcode(errorcode), errOmitLocation(true), errmsg("%s", buf.data))); } PG_CATCH(); { pfree(buf.data); PG_RE_THROW(); } PG_END_TRY(); }