/* * SetRelationRuleStatus * Set the value of the relation's relhasrules field in pg_class; * if the relation is becoming a view, also adjust its relkind. * * NOTE: caller must be holding an appropriate lock on the relation. * * NOTE: an important side-effect of this operation is that an SI invalidation * message is sent out to all backends --- including me --- causing relcache * entries to be flushed or updated with the new set of rules for the table. * This must happen even if we find that no change is needed in the pg_class * row. */ void SetRelationRuleStatus(oid_t relationId, bool relHasRules, bool relIsBecomingView) { struct relation* relationRelation; struct heap_tuple* tuple; Form_pg_class classForm; /* * Find the tuple to update in pg_class, using syscache for the lookup. */ relationRelation = heap_open(RelationRelationId, ROW_EXCL_LOCK); tuple = search_syscache_copy1(RELOID, OID_TO_D(relationId)); if (!HT_VALID(tuple)) elog(ERROR, "cache lookup failed for relation %u", relationId); classForm = (Form_pg_class) GET_STRUCT(tuple); if (classForm->relhasrules != relHasRules || (relIsBecomingView && classForm->relkind != RELKIND_VIEW)) { /* Do the update */ classForm->relhasrules = relHasRules; if (relIsBecomingView) classForm->relkind = RELKIND_VIEW; simple_heap_update(relationRelation, &tuple->t_self, tuple); /* Keep the catalog indexes up to date */ cat_update_indexes(relationRelation, tuple); } else { /* no need to change tuple, but force relcache rebuild anyway */ invalidate_relcache_by_tuple(tuple); } heap_free_tuple(tuple); heap_close(relationRelation, ROW_EXCL_LOCK); }
/* * transformArrayType() * Identify the types involved in a subscripting operation * * On entry, arrayType/arrayTypmod identify the type of the input value * to be subscripted (which could be a domain type). These are modified * if necessary to identify the actual array type and typmod, and the * array's element type is returned. An error is thrown if the input isn't * an array type. */ oid_t transformArrayType(oid_t* arrayType, int32* arrayTypmod) { oid_t origArrayType = *arrayType; oid_t elementType; struct heap_tuple* type_tuple_array; Form_pg_type type_struct_array; /* * If the input is a domain, smash to base type, and extract the actual * typmod to be applied to the base type. Subscripting a domain is an * operation that necessarily works on the base array type, not the domain * itself. (Note that we provide no method whereby the creator of a * domain over an array type could hide its ability to be subscripted.) */ *arrayType = get_basetyp_and_typmod(*arrayType, arrayTypmod); /* Get the type tuple for the array */ type_tuple_array = search_syscache1(TYPEOID, OID_TO_D(*arrayType)); if (!HT_VALID(type_tuple_array)) elog(ERROR, "cache lookup failed for type %u", *arrayType); type_struct_array = (Form_pg_type) GET_STRUCT(type_tuple_array); /* needn't check typisdefined since this will fail anyway */ elementType = type_struct_array->typelem; if (elementType == INVALID_OID) { ereport(ERROR, ( errcode(E_DATATYPE_MISMATCH), errmsg("cannot subscript type %s because it is not an array", format_type_be(origArrayType)))); } release_syscache(type_tuple_array); return elementType; }
/* compatible_opr() * 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_opr( parse_state_s* pstate, struct list* op, oid_t arg1, oid_t 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) GET_STRUCT(optup); if (IsBinaryCoercible(arg1, opform->oprleft) && IsBinaryCoercible(arg2, opform->oprright)) return optup; /* nope... */ release_syscache(optup); if (!noError) ereport(ERROR, ( errcode(E_UNDEFINED_FUNCTION), errmsg("operator requires run-time type coercion: %s", opr_signature_string(op, 'b', arg1, arg2)), parser_errpos(pstate, location))); return (Operator) NULL; }
/* given operator tuple, return the underlying function's OID */ oid_t opr_func_id(Operator op) { Form_pg_operator pgopform; pgopform = (Form_pg_operator) GET_STRUCT(op); return pgopform->oprcode; }
VALUE shoes_canvas_get_gutter_width(VALUE self) { int scrollwidth = 0; GET_STRUCT(canvas, canvas); scrollwidth = shoes_native_slot_gutter(canvas->slot); return INT2NUM(scrollwidth); }
VALUE shoes_app_method_missing(int argc, VALUE *argv, VALUE self) { VALUE cname, canvas; GET_STRUCT(app, app); cname = argv[0]; canvas = rb_ary_entry(app->nesting, RARRAY_LEN(app->nesting) - 1); if (!NIL_P(canvas) && rb_respond_to(canvas, SYM2ID(cname))) return ts_funcall2(canvas, SYM2ID(cname), argc - 1, argv + 1); return shoes_color_method_missing(argc, argv, self); }
Attack Ammo::getAttack( void ) { if (fired) { CombatValues c_val; GET_STRUCT(s_offence_cv, c_val); return Attack( c_val, getTarget(tg_controller), THIS, "shot" ); } else { return Item::getAttack(); } }
/* * Determine size of a large object * * NOTE: LOs can contain gaps, just like Unix files. We actually return * the offset of the last byte + 1. */ static uint32 inv_getsize(struct lobj *obj_desc) { uint32 lastbyte = 0; struct scankey skey[1]; struct sys_scan* sd; struct heap_tuple* tuple; ASSERT(PTR_VALID(obj_desc)); open_lo_relation(); scankey_init(&skey[0], Anum_pg_largeobject_loid, BT_EQ_STRAT_NR, F_OIDEQ, OID_TO_D(obj_desc->id)); sd = systable_beginscan_ordered(lo_heap_r, lo_index_r, obj_desc->snapshot, 1, skey); /* * Because the pg_largeobject index is on both loid and pageno, but we * constrain only loid, a backwards scan should visit all pages of the * large object in reverse pageno order. So, it's sufficient to examine * the first valid tuple (== last valid page). */ tuple = systable_getnext_ordered(sd, BACKWARD_SCANDIR); if (HT_VALID(tuple)) { Form_pg_largeobject data; bytea* datafield; bool pfreeit; if (HT_HAS_NULLS(tuple)) /* paranoia */ elog(ERROR, "null field found in pg_largeobject"); data = (Form_pg_largeobject) GET_STRUCT(tuple); datafield = &(data->data); /* see note at top of file */ pfreeit = false; if (VLA_EXTENDED(datafield)) { datafield = (bytea *) heap_tuple_untoast_attr((struct vla *) datafield); pfreeit = true; } lastbyte = data->pageno * LO_BLK_SIZE + getbytealen(datafield); if (pfreeit) pfree(datafield); } systable_endscan_ordered(sd); return lastbyte; }
/* * Find rule oid. * * If missing_ok is false, throw an error if rule name not found. If * true, just return INVALID_OID. */ oid_t get_rewrite_oid(oid_t relid, const char *rulename, bool missing_ok) { struct heap_tuple* tuple; oid_t ruleoid; /* Find the rule's pg_rewrite tuple, get its OID */ tuple = search_syscache2(RULERELNAME, OID_TO_D(relid), PTR_TO_D(rulename)); if (!HT_VALID(tuple)) { if (missing_ok) return INVALID_OID; ereport(ERROR, ( errcode(E_UNDEFINED_OBJECT), errmsg("rule \"%s\" for relation \"%s\" does not exist", rulename, get_rel_name(relid)))); } ASSERT(relid == ((Form_pg_rewrite) GET_STRUCT(tuple))->ev_class); ruleoid = HEAPTUP_OID(tuple); release_syscache(tuple); return ruleoid; }
/* * Find rule oid, given only a rule name but no rel OID. * * If there's more than one, it's an error. If there aren't any, that's an * error, too. In general, this should be avoided - it is provided to support * syntax that is compatible with pre-7.3 versions of PG, where rule names * were unique across the entire database. */ oid_t get_rewrite_oid_without_relid(const char *rulename, oid_t* reloid) { struct relation* RewriteRelation; struct heap_scan* scanDesc; struct scankey scanKeyData; struct heap_tuple* htup; oid_t ruleoid; /* Search pg_rewrite for such a rule */ scankey_init(&scanKeyData, Anum_pg_rewrite_rulename, BT_EQ_STRAT_NR, F_NAMEEQ, CSTRING_TO_D(rulename)); RewriteRelation = heap_open(RewriteRelationId, ACCESS_SHR_LOCK); scanDesc = heap_beginscan(RewriteRelation, snap_now, 1, &scanKeyData); htup = heap_getnext(scanDesc, FORWARD_SCANDIR); if (!HT_VALID(htup)) { ereport(ERROR, ( errcode(E_UNDEFINED_OBJECT), errmsg("rule \"%s\" does not exist", rulename))); } ruleoid = HEAPTUP_OID(htup); if (reloid != NULL) *reloid = ((Form_pg_rewrite) GET_STRUCT(htup))->ev_class; if (HT_VALID(htup = heap_getnext(scanDesc, FORWARD_SCANDIR))) { ereport(ERROR, ( errcode(E_DUPLICATE_OBJECT), errmsg("there are multiple rules named \"%s\"", rulename), errhint("Specify a relation name as well as a rule name."))); } heap_endscan(scanDesc); heap_close(RewriteRelation, ACCESS_SHR_LOCK); return ruleoid; }
/* * make_opr() * Operator expression construction. * * Transform operator expression ensuring type compatibility. * This is where some type conversion happens. * * As with coerce_type, pstate may be NULL if no special unknown-Param * processing is wanted. */ expr_n* make_opr(parse_state_s *pstate, struct list *opname, node_n *ltree, node_n *rtree, int location) { oid_t ltypeId; oid_t rtypeId; Operator tup; Form_pg_operator opform; oid_t actual_arg_types[2]; oid_t declared_arg_types[2]; int nargs; struct list* args; oid_t rettype; opr_xp* result; /* Select the operator */ if (rtree == NULL) { /* right operator */ ltypeId = expr_type(ltree); rtypeId = INVALID_OID; tup = right_opr(pstate, opname, ltypeId, false, location); } else if (ltree == NULL) { /* left operator */ rtypeId = expr_type(rtree); ltypeId = INVALID_OID; tup = left_opr(pstate, opname, rtypeId, false, location); } else { /* otherwise, binary operator */ ltypeId = expr_type(ltree); rtypeId = expr_type(rtree); tup = oper(pstate, opname, ltypeId, rtypeId, false, location); } opform = (Form_pg_operator) GET_STRUCT(tup); /* Check it's not a shell */ if (!REGPROC_VALID(opform->oprcode)) ereport(ERROR, ( errcode(E_UNDEFINED_FUNCTION), errmsg("operator is only a shell: %s", opr_signature_string(opname, opform->oprkind, opform->oprleft, opform->oprright)), parser_errpos(pstate, location))); /* Do typecasting and build the expression tree */ if (rtree == NULL) { /* right operator */ args = list_make1(ltree); actual_arg_types[0] = ltypeId; declared_arg_types[0] = opform->oprleft; nargs = 1; } else if (ltree == NULL) { /* left operator */ args = list_make1(rtree); actual_arg_types[0] = rtypeId; declared_arg_types[0] = opform->oprright; nargs = 1; } else { /* otherwise, binary operator */ 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; nargs = 2; } /* * enforce consistency with polymorphic 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, nargs, opform->oprresult, false); /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); /* and build the expression node */ result = MK_N(OpExpr,opr_xp); result->opno = opr_id(tup); result->opfuncid = opform->oprcode; result->opresulttype = rettype; result->opretset = get_func_retset(opform->oprcode); /* opcollid and inputcollid will be set by parse_collate.c */ result->args = args; result->location = location; release_syscache(tup); return (expr_n *) result; }
/* -------------------------------- * init_postgres * 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 actual database * name can be returned to the caller in out_dbname. If out_dbname isn't * NULL, it must point to a buffer of size NAMEDATALEN. * * In bootstrap mode no parameters are used. The autovacuum launcher process * doesn't use any parameters either, because it only goes far enough to be * able to read pg_database; it doesn't connect to any particular database. * In walsender mode only username is used. * * As of PostgreSQL 8.2, we expect init_process() was already called, so we * already have a struct proc struct ... but it's not completely filled in yet. * * Note: * Be very careful with the order of calls in the init_postgres function. * -------------------------------- */ void init_postgres(const char *in_dbname, oid_t dboid, const char *username, char *out_dbname) { bool bootstrap = BOOTSTRAP_MODE(); bool am_superuser; char *fullpath; char dbname[NAMEDATALEN]; elog(DEBUG3, "init_postgres"); /* * Add my struct proc struct to the ProcArray. * * Once I have done this, I am visible to other backends! */ init_proc_phase2(); /* * Initialize my entry in the shared-invalidation manager's array of * per-backend data. * * Sets up current_bid, a unique backend identifier. */ current_bid = INVALID_BKNID; sci_bkn_init(false); if (current_bid > MAX_NR_BACKENDS || current_bid <= 0) elog(FATAL, "bad backend ID: %d", current_bid); /* Now that we have a bid_t, we can participate in ProcSignal */ signal_init(current_bid); /* * bufmgr needs another initialization call too */ init_buffer_pool_bkn(); /* * Initialize local process's access to XLOG. */ if (child) { /* * The postmaster already started the XLOG machinery, but we need to * call init_xlog_access(), if the system isn't in hot-standby mode. * This is handled by calling recovery_in_progres and ignoring the * result. */ (void) recovery_in_progres(); } else { /* * We are either a bootstrap process or a standalone backend. Either * way, start up the XLOG machinery, and register to have it closed * down at exit. */ startup_xlog(); on_shmem_exit(shutdown_xlog, 0); } /* * 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. */ relcache_init_phase1(); init_catcache_phase1(); init_plan_cache(); /* Initialize portal manager */ start_portal(); /* Initialize stats collection --- must happen before first xact */ if (!bootstrap) stat_init(); /* * Load relcache entries for the shared system catalogs. This must * create at least entries for pg_database and catalogs used for * authentication. */ relcache_init_phase2(); /* * 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); /* The autovacuum launcher is done here */ if (is_avl_proc()) return; /* * 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 recent_global_xmin. (This * is critical for anything that reads heap pages, because HOT may decide * to prune them even if the process doesn't attempt to modify any * tuples.) */ if (!bootstrap) { /* statement_timestamp must be set for timeouts to work correctly */ set_current_stmt_start(); start_xact_cmd(); (void) get_xact_snap(); } /* * Perform client authentication if necessary, then figure out our * postgres user ID, and see if we are a superuser. * * In standalone mode and in autovacuum worker processes, we use a fixed * ID, otherwise we figure it out from the authenticated user name. */ if (bootstrap || is_avw_proc()) { init_session_uidStandalone(); am_superuser = true; } else if (!child) { init_session_uidStandalone(); am_superuser = true; if (!ThereIsAtLeastOneRole()) ereport(WARNING, ( errcode(E_UNDEFINED_OBJECT), errmsg("no roles are defined in this database system"), errhint("You should immediately run CREATE USER \"%s\" SUPERUSER;.", username))); } else { /* normal multiuser case */ ASSERT(proc_port != NULL); PerformAuthentication(proc_port); init_session_uid(username); am_superuser = superuser(); } /* * If we're trying to shut down, only superusers can connect, and new * replication connections are not allowed. */ if ((!am_superuser || am_walsender) && proc_port != NULL && proc_port->canAcceptConnections == CAC_WAITBACKUP) { if (am_walsender) ereport(FATAL, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("new replication connections are not allowed during database shutdown"))); else ereport(FATAL, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to connect during database shutdown"))); } /* * Binary upgrades only allowed super-user connections */ if (is_binary_upgrade && !am_superuser) { ereport(FATAL, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to connect in binary upgrade mode"))); } /* * The last few connections slots are reserved for superusers. Although * replication connections currently require superuser privileges, we * don't allow them to consume the reserved slots, which are intended for * interactive use. */ if ((!am_superuser || am_walsender) && reserved_bknds > 0 && !have_nfree_procs(reserved_bknds)) ereport(FATAL, ( errcode(E_TOO_MANY_CONNECTIONS), errmsg("remaining connection slots are reserved for non-replication superuser connections"))); /* * If walsender, we don't want to connect to any particular database. Just * finish the backend startup by processing any options from the startup * packet, and we're done. */ if (am_walsender) { ASSERT(!bootstrap); /* must have authenticated as a replication role */ if (!is_authenticated_user_replication_role()) ereport(FATAL, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("must be replication role to start walsender"))); /* process any options passed in the startup packet */ if (proc_port != NULL) process_startup_options(proc_port, am_superuser); /* Apply post_auth_delay as soon as we've read all options */ if (post_auth_delay > 0) pg_usleep(post_auth_delay * 1000000L); /* initialize client encoding */ init_client_encoding(); /* report this backend in the struct backend_status array */ stat_backend_start(); /* close the transaction we started above */ commit_xact_cmd(); return; } /* * Set up the global variables holding database id and default tablespace. * 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's entry in pg_database. */ if (bootstrap) { current_db_id = TemplateDbOid; current_tbs_id = DEFAULT_TBS_OID; } else if (in_dbname != NULL) { struct heap_tuple *tuple; Form_pg_database dbform; tuple = GetDatabaseTuple(in_dbname); if (!HT_VALID(tuple)) ereport(FATAL, ( errcode(E_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", in_dbname))); dbform = (Form_pg_database) GET_STRUCT(tuple); current_db_id = HEAPTUP_OID(tuple); current_tbs_id = dbform->dattablespace; /* take database name from the caller, just for paranoia */ strlcpy(dbname, in_dbname, sizeof(dbname)); } else { /* caller specified database by OID */ struct heap_tuple *tuple; Form_pg_database dbform; tuple = GetDatabaseTupleByOid(dboid); if (!HT_VALID(tuple)) { ereport(FATAL, ( errcode(E_UNDEFINED_DATABASE), errmsg("database %u does not exist", dboid))); } dbform = (Form_pg_database) GET_STRUCT(tuple); current_db_id = HEAPTUP_OID(tuple); current_tbs_id = dbform->dattablespace; ASSERT(current_db_id == dboid); strlcpy(dbname, NAME_TO_STR(dbform->datname), sizeof(dbname)); /* pass the database name back to the caller */ if (out_dbname) strcpy(out_dbname, dbname); } /* Now we can mark our struct proc entry with the database ID */ /* (We assume this is an atomic store so no lock is needed) */ current_proc->databaseId = current_db_id; /* * Now, 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 committed its update 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 struct proc array; anyone trying a DROP DATABASE after * this point will see us there. * * Note: use of ROW_EXCL_LOCK 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 * ACCESS_SHR_LOCK for such sessions and thereby not conflict against * CREATE DATABASE. */ if (!bootstrap) lock_sobj(DatabaseRelationId, current_db_id, 0, ROW_EXCL_LOCK); /* * Recheck 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) { struct heap_tuple *tuple; tuple = GetDatabaseTuple(dbname); if (!HT_VALID(tuple) || current_db_id != HEAPTUP_OID(tuple) || current_tbs_id != ((Form_pg_database) GET_STRUCT(tuple))->dattablespace) ereport(FATAL, ( errcode(E_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. */ fullpath = get_db_path(current_db_id, current_tbs_id); if (!bootstrap) { if (access(fullpath, F_OK) == -1) { if (errno == ENOENT) { ereport(FATAL, ( errcode(E_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", dbname), errdetail("The database subdirectory \"%s\" is missing.", fullpath))); } else { ereport(FATAL, ( errcode_file_access(), errmsg("could not access directory \"%s\": %m", fullpath))); } } check_version(fullpath); } set_db_path(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. */ relcache_init_phase3(); /* set up ACL framework (so CheckMyDatabase can check permissions) */ initialize_acl(); /* * Re-read the 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); /* * Now process any command-line switches and any additional GUC variable * settings passed in the startup packet. We couldn't do this before * because we didn't know if client is a superuser. */ if (proc_port != NULL) process_startup_options(proc_port, am_superuser); /* Process pg_db_role_setting options */ process_settings(current_db_id, get_session_uid()); /* Apply post_auth_delay as soon as we've read all options */ if (post_auth_delay > 0) pg_usleep(post_auth_delay * 1000000L); /* * 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 */ init_search_path(); /* initialize client encoding */ init_client_encoding(); /* report this backend in the struct backend_status array */ if (!bootstrap) stat_backend_start(); /* close the transaction we started above */ if (!bootstrap) commit_xact_cmd(); }
VALUE shoes_canvas_get_scroll_top(VALUE self) { GET_STRUCT(canvas, canvas); return INT2NUM(canvas->slot->scrolly); }
/* ---------------------------------------------------------------- * procedure_create * * Note: allParameterTypes, parameterModes, parameterNames, and proconfig * are either arrays of the proper types or NULL. We declare them Datum, * not "ArrayType *", to avoid importing array.h into pg_proc_fn.h. * ---------------------------------------------------------------- */ oid_t procedure_create( const char *procedureName, oid_t procNamespace, bool replace, bool returnsSet, oid_t returnType, oid_t languageObjectId, oid_t languageValidator, const char *prosrc, const char *probin, bool isAgg, bool isWindowFunc, bool security_definer, bool isStrict, char volatility, oid_vector_s *parameterTypes, datum_t allParameterTypes, datum_t parameterModes, datum_t parameterNames, struct list *parameterDefaults, datum_t proconfig, float4 procost, float4 prorows) { oid_t retval; int parameterCount; int allParamCount; oid_t* allParams; bool genericInParam = false; bool genericOutParam = false; bool internalInParam = false; bool internalOutParam = false; oid_t variadicType = INVALID_OID; oid_t proowner = get_uid(); acl_s* proacl = NULL; struct relation* rel; struct heap_tuple* tup; struct heap_tuple* oldtup; bool nulls[Natts_pg_proc]; datum_t values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; oid_t relid; struct name procname; struct tuple* tupDesc; bool is_update; struct objaddr myself; struct objaddr referenced; int i; /* * sanity checks */ ASSERT(PTR_VALID(prosrc)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) { ereport(ERROR, ( errcode(E_TOO_MANY_ARGUMENTS), errmsg_plural("functions cannot have more than %d argument", "functions cannot have more than %d arguments", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); } /* note: the above is correct, we do NOT count output arguments */ if (allParameterTypes != PTR_TO_D(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ array_s *allParamArray; allParamArray = (array_s*) D_TO_PTR(allParameterTypes); allParamCount = ARR_DIMS(allParamArray)[0]; if (ARR_NDIM(allParamArray) != 1 || allParamCount <= 0 || ARR_HASNULL(allParamArray) || ARR_ELEMTYPE(allParamArray) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D oid_t array"); allParams = (oid_t*) ARR_DATA_PTR(allParamArray); ASSERT(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } /* * Do not allow polymorphic return type unless at least one input argument * is polymorphic. Also, do not allow return type INTERNAL unless at * least one input argument is INTERNAL. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PTR_TO_D(NULL)) { for (i = 0; i < allParamCount; i++) { /* * We don't bother to distinguish input and output params here, so * if there is, say, just an input INTERNAL param then we will * still set internalOutParam. This is OK since we don't really * care. */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } if ((is_polymorphic_type(returnType) || genericOutParam) && !genericInParam) { ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning a polymorphic type must have" " at least one polymorphic argument."))); } if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) { ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at" " least one \"internal\" argument."))); } /* * don't allow functions of complex types that have the same name as * existing attributes of the type */ if (parameterCount == 1 && OID_VALID(parameterTypes->values[0]) && (relid = typeid_to_relid(parameterTypes->values[0])) != INVALID_OID && get_attnum(relid, procedureName) != INVALID_ATTR_NR) { ereport(ERROR, ( errcode(E_DUPLICATE_COLUMN), errmsg("\"%s\" is already an attribute of type %s", procedureName, format_type_be(parameterTypes->values[0])))); } if (parameterModes != PTR_TO_D(NULL)) { /* * We expect the array to be a 1-D CHAR array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of char values. */ array_s* modesArray; char* modes; modesArray = (array_s *) D_TO_PTR(parameterModes); if (ARR_NDIM(modesArray) != 1 || ARR_DIMS(modesArray)[0] != allParamCount || ARR_HASNULL(modesArray) || ARR_ELEMTYPE(modesArray) != CHAROID) elog(ERROR, "parameterModes is not a 1-D char array"); modes = (char*) ARR_DATA_PTR(modesArray); /* * Only the last input parameter can be variadic; if it is, save its * element type. Errors here are just elog since caller should have * checked this already. */ for (i = 0; i < allParamCount; i++) { switch (modes[i]) { case PROARGMODE_IN: case PROARGMODE_INOUT: if (OID_VALID(variadicType)) elog(ERROR, "variadic parameter must be last"); break; case PROARGMODE_OUT: case PROARGMODE_TABLE: /* okay */ break; case PROARGMODE_VARIADIC: if (OID_VALID(variadicType)) elog(ERROR, "variadic parameter must be last"); switch (allParams[i]) { case ANYOID: variadicType = ANYOID; break; case ANYARRAYOID: variadicType = ANYELEMENTOID; break; default: variadicType = get_element_type(allParams[i]); if (!OID_VALID(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; } break; default: elog(ERROR, "invalid parameter mode '%c'", modes[i]); break; } } } /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = false; values[i] = (datum_t) 0; replaces[i] = true; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NAME_TO_D(&procname); values[Anum_pg_proc_pronamespace - 1] = OID_TO_D(procNamespace); values[Anum_pg_proc_proowner - 1] = OID_TO_D(proowner); values[Anum_pg_proc_prolang - 1] = OID_TO_D(languageObjectId); values[Anum_pg_proc_procost - 1] = FLOAT4_TO_D(procost); values[Anum_pg_proc_prorows - 1] = FLOAT4_TO_D(prorows); values[Anum_pg_proc_provariadic - 1] = OID_TO_D(variadicType); values[Anum_pg_proc_proisagg - 1] = BOOL_TO_D(isAgg); values[Anum_pg_proc_proiswindow - 1] = BOOL_TO_D(isWindowFunc); values[Anum_pg_proc_prosecdef - 1] = BOOL_TO_D(security_definer); values[Anum_pg_proc_proisstrict - 1] = BOOL_TO_D(isStrict); values[Anum_pg_proc_proretset - 1] = BOOL_TO_D(returnsSet); values[Anum_pg_proc_provolatile - 1] = CHAR_TO_D(volatility); values[Anum_pg_proc_pronargs - 1] = UINT16_TO_D(parameterCount); values[Anum_pg_proc_pronargdefaults - 1] = UINT16_TO_D(list_length(parameterDefaults)); values[Anum_pg_proc_prorettype - 1] = OID_TO_D(returnType); values[Anum_pg_proc_proargtypes - 1] = PTR_TO_D(parameterTypes); if (allParameterTypes != PTR_TO_D(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = true; if (parameterModes != PTR_TO_D(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = true; if (parameterNames != PTR_TO_D(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = true; if (parameterDefaults != NIL) values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum( node_to_string(parameterDefaults)); else nulls[Anum_pg_proc_proargdefaults - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc); if (probin) values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin); else nulls[Anum_pg_proc_probin - 1] = true; if (proconfig != PTR_TO_D(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else nulls[Anum_pg_proc_proconfig - 1] = true; /* * proacl will be determined later */ rel = heap_open(ProcedureRelationId, ROW_EXCL_LOCK); tupDesc = REL_DESC(rel); /* Check for pre-existing definition */ oldtup = search_syscache3( PROCNAMEARGSNSP, PTR_TO_D(procedureName), PTR_TO_D(parameterTypes), OID_TO_D(procNamespace)); if (HT_VALID(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc; datum_t proargnames; bool isnull; oldproc = (Form_pg_proc) GET_STRUCT(oldtup); if (!replace) { ereport(ERROR, ( errcode(E_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); } if (!pg_proc_ownercheck(HEAPTUP_OID(oldtup), proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) { ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errhint("Use DROP FUNCTION first."))); } /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { struct tuple* olddesc; struct tuple* newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d( allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) { /* ok, both are runtime-defined RECORDs */ ; } else if (olddesc == NULL || newdesc == NULL || !tupdesc_equal(olddesc, newdesc)) { ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), errhint("Use DROP FUNCTION first."))); } } /* * If there were any named input parameters, check to make sure the * names have not been changed, as this could break existing calls. We * allow adding names to formerly unnamed parameters, though. */ proargnames = syscache_attr( PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargnames, &isnull); if (!isnull) { datum_t proargmodes; char** old_arg_names; char** new_arg_names; int n_old_arg_names; int n_new_arg_names; int j; proargmodes = syscache_attr( PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargmodes, &isnull); if (isnull) proargmodes = PTR_TO_D(NULL); /* just to be sure */ n_old_arg_names = get_func_input_arg_names( proargnames, proargmodes, &old_arg_names); n_new_arg_names = get_func_input_arg_names( parameterNames, parameterModes, &new_arg_names); for (j = 0; j < n_old_arg_names; j++) { if (old_arg_names[j] == NULL) continue; if (j >= n_new_arg_names || new_arg_names[j] == NULL || strcmp(old_arg_names[j], new_arg_names[j]) != 0) { ereport(ERROR,( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("cannot change name of input parameter \"%s\"", old_arg_names[j]), errhint("Use DROP FUNCTION first."))); } } } /* * If there are existing defaults, check compatibility: redefinition * must not remove any defaults nor change their types. (Removing a * default might cause a function to fail to satisfy an existing call. * Changing type would only be possible if the associated parameter is * polymorphic, and in such cases a change of default type might alter * the resolved output type of existing calls.) */ if (oldproc->pronargdefaults != 0) { datum_t proargdefaults; struct list* oldDefaults; struct list_cell* oldlc; struct list_cell* newlc; if (list_length(parameterDefaults) < oldproc->pronargdefaults) { ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("cannot remove parameter defaults from existing function"), errhint("Use DROP FUNCTION first."))); } proargdefaults = syscache_attr( PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargdefaults, &isnull); ASSERT(!isnull); oldDefaults = (struct list*) string_to_node( TextD_TO_CSTRING(proargdefaults)); ASSERT(IS_A(oldDefaults, List)); ASSERT(list_length(oldDefaults) == oldproc->pronargdefaults); /* new list can have more defaults than old, advance over 'em */ newlc = list_head(parameterDefaults); for (i = list_length(parameterDefaults) - oldproc->pronargdefaults; i > 0; i--) newlc = lnext(newlc); foreach(oldlc, oldDefaults) { node_n* oldDef; node_n* newDef; oldDef = (node_n*) lfirst(oldlc); newDef = (node_n*) lfirst(newlc); if (expr_type(oldDef) != expr_type(newDef)) { ereport(ERROR,( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("cannot change data type of existing" " parameter default value"), errhint("Use DROP FUNCTION first."))); } newlc = lnext(newlc); } }
// // shoes_basic routines // VALUE shoes_basic_remove(VALUE self) { GET_STRUCT(basic, self_t); shoes_canvas_remove_item(self_t->parent, self, 0, 0); shoes_canvas_repaint_all(self_t->parent); return self; }
/* * CheckMyDatabase -- fetch information from the pg_database entry for our DB */ static void CheckMyDatabase(const char *name, bool am_superuser) { struct heap_tuple *tup; Form_pg_database dbform; char *collate; char *ctype; /* Fetch our pg_database row normally, via syscache */ tup = search_syscache1(DATABASEOID, OID_TO_D(current_db_id)); if (!HT_VALID(tup)) elog(ERROR,"cache lookup failed for database %u", current_db_id); dbform = (Form_pg_database) GET_STRUCT(tup); /* This recheck is strictly paranoia */ if (strcmp(name, NAME_TO_STR(dbform->datname)) != 0) ereport(FATAL, ( errcode(E_UNDEFINED_DATABASE), errmsg("database \"%s\" has disappeared from pg_database", name), errdetail("Database OID %u now seems to belong to \"%s\".", current_db_id, NAME_TO_STR(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 autovacuum worker processes either. */ if (child && !is_avw_proc()) { /* * Check that the database is currently allowing connections. */ if (!dbform->datallowconn) ereport(FATAL, ( errcode(E_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("database \"%s\" is not currently accepting connections", name))); /* * 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 && db_acl_check(current_db_id, get_uid(), ACL_CONNECT) != ACLCHECK_OK) ereport(FATAL, ( errcode(E_INSUFFICIENT_PRIVILEGE), errmsg("permission denied for database \"%s\"", name), errdetail("User does not have CONNECT privilege."))); /* * Check connection limit for this database. * * There is a race condition here --- we create our struct proc 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 && count_db_bkns(current_db_id) > dbform->datconnlimit) ereport(FATAL, ( errcode(E_TOO_MANY_CONNECTIONS), errmsg("too many connections for database \"%s\"", name))); } /* * OK, we're golden. Next to-do item is to save the encoding info out of * the pg_database tuple. */ set_db_encoding(dbform->encoding); /* Record it as a GUC internal option, too */ set_option("server_encoding", get_db_encoding_name(), PGC_INTERNAL, PGC_S_OVERRIDE); /* If we have no other source of ClientEncoding, use server encoding */ set_option("ClientEncoding", get_db_encoding_name(), PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT); /* assign locale variables */ collate = NAME_TO_STR(dbform->datcollate); ctype = NAME_TO_STR(dbform->datctype); if (pg_perm_setlocale(LC_COLLATE, collate) == NULL) ereport(FATAL, ( errmsg("database locale is incompatible with operating system"), errdetail("The database was initialized with LC_COLLATE \"%s\", " " which is not recognized by setlocale().", collate), errhint("Recreate the database with another locale or install the missing " "locale."))); if (pg_perm_setlocale(LC_CTYPE, ctype) == NULL) ereport(FATAL, ( errmsg("database locale is incompatible with operating system"), errdetail("The database was initialized with LC_CTYPE \"%s\", " " which is not recognized by setlocale().", ctype), errhint("Recreate the database with another locale or install the missing " "locale."))); /* Make the locale settings visible as GUC variables, too */ set_option("lc_collate", collate, PGC_INTERNAL, PGC_S_OVERRIDE); set_option("lc_ctype", ctype, PGC_INTERNAL, PGC_S_OVERRIDE); /* Use the right encoding in translated messages */ #ifdef ENABLE_NLS pg_bind_textdomain_codeset(textdomain(NULL)); #endif release_syscache(tup); }
int inv_read(struct lobj *obj_desc, char *buf, int nbytes) { int nread = 0; int n; int off; int len; int32 pageno = (int32) (obj_desc->offset / LO_BLK_SIZE); uint32 pageoff; struct scankey skey[2]; struct sys_scan* sd; struct heap_tuple* tuple; ASSERT(PTR_VALID(obj_desc)); ASSERT(buf != NULL); if (nbytes <= 0) return 0; open_lo_relation(); scankey_init(&skey[0], Anum_pg_largeobject_loid, BT_EQ_STRAT_NR, F_OIDEQ, OID_TO_D(obj_desc->id)); scankey_init(&skey[1], Anum_pg_largeobject_pageno, BT_GE_STRAT_NR, F_INT4GE, INT32_TO_D(pageno)); sd = systable_beginscan_ordered(lo_heap_r, lo_index_r, obj_desc->snapshot, 2, skey); while ((tuple = systable_getnext_ordered(sd, FORWARD_SCANDIR)) != NULL) { Form_pg_largeobject data; bytea* datafield; bool pfreeit; if (HT_HAS_NULLS(tuple)) /* paranoia */ elog(ERROR, "null field found in pg_largeobject"); data = (Form_pg_largeobject) GET_STRUCT(tuple); /* * We expect the indexscan will deliver pages in order. However, * there may be missing pages if the LO contains unwritten "holes". We * want missing sections to read out as zeroes. */ pageoff = ((uint32) data->pageno) * LO_BLK_SIZE; if (pageoff > obj_desc->offset) { n = pageoff - obj_desc->offset; n = (n <= (nbytes - nread)) ? n : (nbytes - nread); pg_memset(buf + nread, 0, n); nread += n; obj_desc->offset += n; } if (nread < nbytes) { ASSERT(obj_desc->offset >= pageoff); off = (int) (obj_desc->offset - pageoff); ASSERT(off >= 0 && off < LO_BLK_SIZE); datafield = &(data->data); /* see note at top of file */ pfreeit = false; if (VLA_EXTENDED(datafield)) { datafield = (bytea *) heap_tuple_untoast_attr((struct vla *) datafield); pfreeit = true; } len = getbytealen(datafield); if (len > off) { n = len - off; n = (n <= (nbytes - nread)) ? n : (nbytes - nread); memcpy(buf + nread, VLA_DATA(datafield) + off, n); nread += n; obj_desc->offset += n; } if (pfreeit) pfree(datafield); } if (nread >= nbytes) break; } systable_endscan_ordered(sd); return nread; }
/* * Test whether an object exists. */ static bool object_exists(struct objaddr address) { int cache = -1; oid_t indexoid = INVALID_OID; struct relation* rel; struct scankey skey[1]; struct sys_scan * sd; bool found; /* Sub-objects require special treatment. */ if (address.objectSubId != 0) { struct heap_tuple* atttup; /* Currently, attributes are the only sub-objects. */ ASSERT(address.classId == RelationRelationId); atttup = search_syscache2( ATTNUM, OID_TO_D(address.objectId), INT16_TO_D(address.objectSubId)); if (!HT_VALID(atttup)) { found = false; } else { found = ((Form_pg_attribute) GET_STRUCT(atttup))->attisdropped; release_syscache(atttup); } return found; } /* * For object types that have a relevant syscache, we use it; for * everything else, we'll have to do an index-scan. This switch sets * either the cache to be used for the syscache lookup, or the index to be * used for the index scan. */ switch (address.classId) { case RelationRelationId: cache = RELOID; break; case RewriteRelationId: indexoid = RewriteOidIndexId; break; case TriggerRelationId: indexoid = TriggerOidIndexId; break; case ConstraintRelationId: cache = CONSTROID; break; case DatabaseRelationId: cache = DATABASEOID; break; case TableSpaceRelationId: cache = TBS_OID; break; case AuthIdRelationId: cache = AUTHOID; break; case NAMESPACE_RELATION_ID: cache = NAMESPACEOID; break; case LanguageRelationId: cache = LANGOID; break; case TypeRelationId: cache = TYPEOID; break; case ProcedureRelationId: cache = PROCOID; break; case OperatorRelationId: cache = OPEROID; break; case CollationRelationId: cache = COLLOID; break; case ConversionRelationId: cache = CONVOID; break; case OperatorClassRelationId: cache = CLAOID; break; case OperatorFamilyRelationId: cache = OPFAMILYOID; break; case LargeObjectRelationId: /* * Weird backward compatibility hack: struct objaddr notation uses * LargeObjectRelationId for large objects, but since PostgreSQL * 9.0, the relevant catalog is actually * LargeObjectMetadataRelationId. */ address.classId = LargeObjectMetadataRelationId; indexoid = LO_METADATA_OID_INDEX_ID; break; case CastRelationId: indexoid = CastOidIndexId; break; case ForeignDataWrapperRelationId: cache = FOREIGNDATAWRAPPEROID; break; case ForeignServerRelationId: cache = FOREIGNSERVEROID; break; case TSParserRelationId: cache = TSPARSEROID; break; case TSDictionaryRelationId: cache = TSDICTOID; break; case TSTemplateRelationId: cache = TSTEMPLATEOID; break; case TSConfigRelationId: cache = TSCONFIGOID; break; case ExtensionRelationId: indexoid = ExtensionOidIndexId; break; default: elog(ERROR, "unrecognized classid: %u", address.classId); } /* Found a syscache? */ if (cache != -1) return search_syscache_exists1(cache, OID_TO_D(address.objectId)); /* No syscache, so examine the table directly. */ ASSERT(OID_VALID(indexoid)); scankey_init( &skey[0], OID_ATTR_NR, BT_EQ_STRAT_NR, F_OIDEQ, OID_TO_D(address.objectId)); rel = heap_open(address.classId, ACCESS_SHR_LOCK); sd = systable_beginscan(rel, indexoid, true, snap_now, 1, skey); found = HT_VALID(systable_getnext(sd)); systable_endscan(sd); heap_close(rel, ACCESS_SHR_LOCK); return found; }
int inv_write(struct lobj *obj_desc, const char *buf, int nbytes) { int nwritten = 0; int n; int off; int len; int32 pageno = (int32) (obj_desc->offset / LO_BLK_SIZE); struct scankey skey[2]; struct sys_scan * sd; struct heap_tuple * oldtuple; Form_pg_largeobject olddata; bool neednextpage; bytea* datafield; bool pfreeit; struct { bytea hdr; char data[LO_BLK_SIZE]; /* make struct big enough */ int32 align_it; /* ensure struct is aligned well enough */ } workbuf; char* workb = VLA_DATA(&workbuf.hdr); struct heap_tuple* newtup; datum_t values[Natts_pg_largeobject]; bool nulls[Natts_pg_largeobject]; bool replace[Natts_pg_largeobject]; CatalogIndexState indstate; ASSERT(PTR_VALID(obj_desc)); ASSERT(buf != NULL); /* enforce writability because snapshot is probably wrong otherwise */ if ((obj_desc->flags & IFS_WRLOCK) == 0) ereport(ERROR, ( errcode(E_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("large object %u was not opened for writing", obj_desc->id))); /* check existence of the target largeobject */ if (!large_obj_exists(obj_desc->id)) ereport(ERROR, ( errcode(E_UNDEFINED_OBJECT), errmsg("large object %u was already dropped", obj_desc->id))); if (nbytes <= 0) return 0; open_lo_relation(); indstate = cat_open_indexes(lo_heap_r); scankey_init(&skey[0], Anum_pg_largeobject_loid, BT_EQ_STRAT_NR, F_OIDEQ, OID_TO_D(obj_desc->id)); scankey_init(&skey[1], Anum_pg_largeobject_pageno, BT_GE_STRAT_NR, F_INT4GE, INT32_TO_D(pageno)); sd = systable_beginscan_ordered(lo_heap_r, lo_index_r, obj_desc->snapshot, 2, skey); oldtuple = NULL; olddata = NULL; neednextpage = true; while (nwritten < nbytes) { /* * If possible, get next pre-existing page of the LO. We expect the * indexscan will deliver these in order --- but there may be holes. */ if (neednextpage) { if ((oldtuple = systable_getnext_ordered(sd, FORWARD_SCANDIR)) != NULL) { if (HT_HAS_NULLS(oldtuple)) /* paranoia */ elog(ERROR, "null field found in pg_largeobject"); olddata = (Form_pg_largeobject) GET_STRUCT(oldtuple); ASSERT(olddata->pageno >= pageno); } neednextpage = false; } /* * If we have a pre-existing page, see if it is the page we want to * write, or a later one. */ if (olddata != NULL && olddata->pageno == pageno) { /* * Update an existing page with fresh data. * * First, load old data into workbuf */ datafield = &(olddata->data); /* see note at top of file */ pfreeit = false; if (VLA_EXTENDED(datafield)) { datafield = (bytea *) heap_tuple_untoast_attr((struct vla *) datafield); pfreeit = true; } len = getbytealen(datafield); ASSERT(len <= LO_BLK_SIZE); memcpy(workb, VLA_DATA(datafield), len); if (pfreeit) pfree(datafield); /* * Fill any hole */ off = (int)(obj_desc->offset % LO_BLK_SIZE); if (off > len) pg_memset(workb + len, 0, off - len); /* * Insert appropriate portion of new data */ n = LO_BLK_SIZE - off; n = (n <= (nbytes - nwritten))? n : (nbytes - nwritten); memcpy(workb + off, buf + nwritten, n); nwritten += n; obj_desc->offset += n; off += n; /* compute valid length of new page */ len = (len >= off) ? len : off; VLA_SET_SZ_STND(&workbuf.hdr, len + VAR_HDR_SZ); /* * Form and insert updated tuple */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); memset(replace, false, sizeof(replace)); values[Anum_pg_largeobject_data - 1] = PTR_TO_D(&workbuf); replace[Anum_pg_largeobject_data - 1] = true; newtup = heap_modify_tuple(oldtuple, REL_DESC(lo_heap_r), values, nulls, replace); simple_heap_update(lo_heap_r, &newtup->t_self, newtup); cat_index_insert(indstate, newtup); heap_free_tuple(newtup); /* * We're done with this old page. */ oldtuple = NULL; olddata = NULL; neednextpage = true; } else { /* * Write a brand new page. * * First, fill any hole */ off = (int)(obj_desc->offset % LO_BLK_SIZE); if (off > 0) pg_memset(workb, 0, off); /* * Insert appropriate portion of new data */ n = LO_BLK_SIZE - off; n = (n <= (nbytes - nwritten))? n : (nbytes - nwritten); memcpy(workb + off, buf + nwritten, n); nwritten += n; obj_desc->offset += n; /* compute valid length of new page */ len = off + n; VLA_SET_SZ_STND(&workbuf.hdr, len + VAR_HDR_SZ); /* * Form and insert updated tuple */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); values[Anum_pg_largeobject_loid - 1] = OID_TO_D(obj_desc->id); values[Anum_pg_largeobject_pageno - 1] = INT32_TO_D(pageno); values[Anum_pg_largeobject_data - 1] = PTR_TO_D(&workbuf); newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); simple_heap_insert(lo_heap_r, newtup); cat_index_insert(indstate, newtup); heap_free_tuple(newtup); } pageno++; } systable_endscan_ordered(sd); cat_close_indexes(indstate); /* * Advance command counter so that my tuple updates will be seen by * later large-object operations in this transaction. */ cmd_count_incr(); return nwritten; }
void inv_truncate(struct lobj *obj_desc, int len) { int32 pageno = (int32) (len / LO_BLK_SIZE); int off; struct scankey skey[2]; struct sys_scan* sd; struct heap_tuple* oldtuple; Form_pg_largeobject olddata; struct { bytea hdr; char data[LO_BLK_SIZE]; /* make struct big enough */ int32 align_it;/* ensure struct is aligned well enough */ } workbuf; char* workb = VLA_DATA(&workbuf.hdr); struct heap_tuple* newtup; datum_t values[Natts_pg_largeobject]; bool nulls[Natts_pg_largeobject]; bool replace[Natts_pg_largeobject]; CatalogIndexState indstate; ASSERT(PTR_VALID(obj_desc)); /* enforce writability because snapshot is probably wrong otherwise */ if ((obj_desc->flags & IFS_WRLOCK) == 0) ereport(ERROR, ( errcode(E_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("large object %u was not opened for writing", obj_desc->id))); /* check existence of the target largeobject */ if (!large_obj_exists(obj_desc->id)) ereport(ERROR, ( errcode(E_UNDEFINED_OBJECT), errmsg("large object %u was already dropped", obj_desc->id))); open_lo_relation(); indstate = cat_open_indexes(lo_heap_r); /* * Set up to find all pages with desired loid and pageno >= target */ scankey_init(&skey[0], Anum_pg_largeobject_loid, BT_EQ_STRAT_NR, F_OIDEQ, OID_TO_D(obj_desc->id)); scankey_init(&skey[1], Anum_pg_largeobject_pageno, BT_GE_STRAT_NR, F_INT4GE, INT32_TO_D(pageno)); sd = systable_beginscan_ordered(lo_heap_r, lo_index_r, obj_desc->snapshot, 2, skey); /* * If possible, get the page the truncation point is in. The truncation * point may be beyond the end of the LO or in a hole. */ olddata = NULL; if ((oldtuple = systable_getnext_ordered(sd, FORWARD_SCANDIR)) != NULL) { if (HT_HAS_NULLS(oldtuple)) /* paranoia */ elog(ERROR, "null field found in pg_largeobject"); olddata = (Form_pg_largeobject) GET_STRUCT(oldtuple); ASSERT(olddata->pageno >= pageno); } /* * If we found the page of the truncation point we need to truncate the * data in it. Otherwise if we're in a hole, we need to create a page to * mark the end of data. */ if (olddata != NULL && olddata->pageno == pageno) { /* First, load old data into workbuf */ bytea* datafield = &(olddata->data); /* see note at top of file */ bool pfreeit = false; int pagelen; if (VLA_EXTENDED(datafield)) { datafield = (bytea *) heap_tuple_untoast_attr((struct vla *) datafield); pfreeit = true; } pagelen = getbytealen(datafield); ASSERT(pagelen <= LO_BLK_SIZE); memcpy(workb, VLA_DATA(datafield), pagelen); if (pfreeit) pfree(datafield); /* * Fill any hole */ off = len % LO_BLK_SIZE; if (off > pagelen) pg_memset(workb + pagelen, 0, off - pagelen); /* compute length of new page */ VLA_SET_SZ_STND(&workbuf.hdr, off + VAR_HDR_SZ); /* * Form and insert updated tuple */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); memset(replace, false, sizeof(replace)); values[Anum_pg_largeobject_data - 1] = PTR_TO_D(&workbuf); replace[Anum_pg_largeobject_data - 1] = true; newtup = heap_modify_tuple(oldtuple, REL_DESC(lo_heap_r), values, nulls, replace); simple_heap_update(lo_heap_r, &newtup->t_self, newtup); cat_index_insert(indstate, newtup); heap_free_tuple(newtup); } else { /* * If the first page we found was after the truncation point, we're in * a hole that we'll fill, but we need to delete the later page * because the loop below won't visit it again. */ if (olddata != NULL) { ASSERT(olddata->pageno > pageno); simple_heap_delete(lo_heap_r, &oldtuple->t_self); } /* * Write a brand new page. * * Fill the hole up to the truncation point */ off = len % LO_BLK_SIZE; if (off > 0) pg_memset(workb, 0, off); /* compute length of new page */ VLA_SET_SZ_STND(&workbuf.hdr, off + VAR_HDR_SZ); /* * Form and insert new tuple */ memset(values, 0, sizeof(values)); memset(nulls, false, sizeof(nulls)); values[Anum_pg_largeobject_loid - 1] = OID_TO_D(obj_desc->id); values[Anum_pg_largeobject_pageno - 1] = INT32_TO_D(pageno); values[Anum_pg_largeobject_data - 1] = PTR_TO_D(&workbuf); newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls); simple_heap_insert(lo_heap_r, newtup); cat_index_insert(indstate, newtup); heap_free_tuple(newtup); } /* * Delete any pages after the truncation point. If the initial search * didn't find a page, then of course there's nothing more to do. */ if (olddata != NULL) { while ((oldtuple = systable_getnext_ordered(sd, FORWARD_SCANDIR)) != NULL) { simple_heap_delete(lo_heap_r, &oldtuple->t_self); } } systable_endscan_ordered(sd); cat_close_indexes(indstate); /* * Advance command counter so that tuple updates will be seen by later * large-object operations in this transaction. */ cmd_count_incr(); }
static char *format_type_internal(oid_t type_oid, int32 typemod, bool typemod_given, bool allow_invalid) { bool with_typemod = typemod_given && (typemod >= 0); struct heap_tuple * tuple; Form_pg_type typeform; oid_t array_base_type; bool is_array; char *buf; if (type_oid == INVALID_OID && allow_invalid) return pstrdup("-"); tuple = search_syscache1(TYPEOID, OID_TO_D(type_oid)); if (!HT_VALID(tuple)) { if (allow_invalid) return pstrdup("???"); else elog(ERROR, "cache lookup failed for type %u", type_oid); } typeform = (Form_pg_type) GET_STRUCT(tuple); /* * Check if it's a regular (variable length) array type. Fixed-length * array types such as "name" shouldn't get deconstructed. As of * Postgres 8.1, rather than checking typlen we check the toast * property, and don't deconstruct "plain storage" array types --- this * is because we don't want to show oid_vector_s as oid[]. */ array_base_type = typeform->typelem; if (array_base_type != INVALID_OID && typeform->typstorage != 'p') { /* Switch our attention to the array element type */ release_syscache(tuple); tuple = search_syscache1(TYPEOID, OID_TO_D(array_base_type)); if (!HT_VALID(tuple)) { if (allow_invalid) return pstrdup("???[]"); else elog(ERROR, "cache lookup failed for type %u", type_oid); } typeform = (Form_pg_type) GET_STRUCT(tuple); type_oid = array_base_type; is_array = true; } else { is_array = false; } /* * See if we want to special-case the output for certain built-in types. * Note that these special cases should all correspond to special * productions in gram.y, to ensure that the type name will be taken as * a system type, not a user type of the same name. * * If we do not provide a special-case output here, the type name will * be handled the same way as a user type name --- in particular, it * will be double-quoted if it matches any lexer keyword. * * This behavior is essential for some cases, such as types "bit" and * "char". */ buf = NULL; /* flag for no special case */ switch (type_oid) { case BITOID: if (with_typemod) { buf = printTypmod("bit", typemod, typeform->typmodout); } else if (typemod_given) { /* * bit with typmod -1 is not the same as BIT, which * means BIT(1) per SQL spec. Report it as the quoted * typename so that parser will not assign a bogus * typmod. */ } else { buf = pstrdup("bit"); } break; case BOOLOID: buf = pstrdup("boolean"); break; case BPCHAROID: if (with_typemod) { buf = printTypmod("character", typemod, typeform->typmodout); } else if (typemod_given) { /* * bpchar with typmod -1 is not the same as CHARACTER, * which means CHARACTER(1) per SQL spec. Report it as * bpchar so that parser will not assign a bogus typmod. */ } else { buf = pstrdup("character"); } break; case FLOAT4OID: buf = pstrdup("real"); break; case FLOAT8OID: buf = pstrdup("double precision"); break; case INT2OID: buf = pstrdup("smallint"); break; case INT4OID: buf = pstrdup("integer"); break; case INT8OID: buf = pstrdup("bigint"); break; case NUMERICOID: if (with_typemod) buf = printTypmod("numeric", typemod, typeform->typmodout); else buf = pstrdup("numeric"); break; case INTERVALOID: if (with_typemod) buf = printTypmod("interval", typemod, typeform->typmodout); else buf = pstrdup("interval"); break; case TIMEOID: if (with_typemod) buf = printTypmod("time", typemod, typeform->typmodout); else buf = pstrdup("time without time zone"); break; case TIMETZOID: if (with_typemod) buf = printTypmod("time", typemod, typeform->typmodout); else buf = pstrdup("time with time zone"); break; case TIMESTAMPOID: if (with_typemod) buf = printTypmod("timestamp", typemod, typeform->typmodout); else buf = pstrdup("timestamp without time zone"); break; case TIMESTAMPTZOID: if (with_typemod) buf = printTypmod("timestamp", typemod, typeform->typmodout); else buf = pstrdup("timestamp with time zone"); break; case VARBITOID: if (with_typemod) buf = printTypmod("bit varying", typemod, typeform->typmodout); else buf = pstrdup("bit varying"); break; case VARCHAROID: if (with_typemod) buf = printTypmod("character varying", typemod, typeform->typmodout); else buf = pstrdup("character varying"); break; } if (buf == NULL) { /* * Default handling: report the name as it appears in the * catalog. Here, we must qualify the name if it is not visible * in the search path, and we must double-quote it if it's not * a standard identifier or if it matches any keyword. */ char *nspname; char *typname; if (type_is_visible(type_oid)) nspname = NULL; else nspname = get_ns_name(typeform->typnamespace); typname = NAME_TO_STR(typeform->typname); buf = quote_qualified_identifier(nspname, typname); if (with_typemod) buf = printTypmod(buf, typemod, typeform->typmodout); } if (is_array) buf = psnprintf(strlen(buf) + 3, "%s[]", buf); release_syscache(tuple); return buf; }
/* * make_scalar_array_op() * Build expression tree for "scalar op ANY/ALL (array)" construct. */ expr_n* make_scalar_array_op( parse_state_s* pstate, struct list* opname, bool useOr, node_n *ltree, node_n *rtree, int location) { oid_t ltypeId; oid_t rtypeId; oid_t atypeId; oid_t res_atypeId; Operator tup; Form_pg_operator opform; oid_t actual_arg_types[2]; oid_t declared_arg_types[2]; struct list* args; oid_t rettype; scalar_array_opr_xp *result; ltypeId = expr_type(ltree); atypeId = expr_type(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_base_element_type(atypeId); if (!OID_VALID(rtypeId)) ereport(ERROR, ( errcode(E_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires array on right side"), parser_errpos(pstate, location))); } /* Now resolve the operator */ tup = oper(pstate, opname, ltypeId, rtypeId, false, location); opform = (Form_pg_operator) GET_STRUCT(tup); /* Check it's not a shell */ if (!REGPROC_VALID(opform->oprcode)) { ereport(ERROR, ( errcode(E_UNDEFINED_FUNCTION), errmsg("operator is only a shell: %s", opr_signature_string(opname, opform->oprkind, opform->oprleft, opform->oprright)), parser_errpos(pstate, location))); } 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 polymorphic 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, false); /* * Check that operator result is boolean */ if (rettype != BOOLOID) ereport(ERROR, ( errcode(E_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator to yield boolean"), parser_errpos(pstate, location))); if (get_func_retset(opform->oprcode)) ereport(ERROR, ( errcode(E_WRONG_OBJECT_TYPE), errmsg("op ANY/ALL (array) requires operator not to return a set"), parser_errpos(pstate, location))); /* * Now switch back to the array type on the right, arranging for any * needed cast to be applied. Beware of polymorphic operators here; * enforce_generic_type_consistency may or may not have replaced a * polymorphic type with a real one. */ if (is_polymorphic_type(declared_arg_types[1])) { /* assume the actual array type is OK */ res_atypeId = atypeId; } else { res_atypeId = get_array_type(declared_arg_types[1]); if (!OID_VALID(res_atypeId)) ereport(ERROR, ( errcode(E_UNDEFINED_OBJECT), errmsg("could not find array type for data type %s", format_type_be(declared_arg_types[1])), parser_errpos(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 = MK_N(ScalarArrayOpExpr,scalar_array_opr_xp); result->opno = opr_id(tup); result->opfuncid = opform->oprcode; result->useOr = useOr; /* inputcollid will be set by parse_collate.c */ result->args = args; result->location = location; release_syscache(tup); return (expr_n *) result; }
/* * agg_create */ void agg_create( const char *aggName, oid_t aggNamespace, oid_t* aggArgTypes, int numArgs, struct list* aggtransfnName, struct list* aggfinalfnName, struct list* aggsortopName, oid_t aggTransType, const char* agginitval) { struct relation* aggdesc; struct heap_tuple* tup; bool nulls[Natts_pg_aggregate]; datum_t values[Natts_pg_aggregate]; Form_pg_proc proc; oid_t transfn; oid_t finalfn = INVALID_OID; /* can be omitted */ oid_t sortop = INVALID_OID; /* can be omitted */ bool hasPolyArg; bool hasInternalArg; oid_t rettype; oid_t finaltype; oid_t* fnArgs; int nargs_transfn; oid_t procOid; struct tuple* tupDesc; int i; struct objaddr myself; struct objaddr referenced; /* sanity checks (caller should have caught these) */ if (!aggName) elog(ERROR, "no aggregate name supplied"); if (!aggtransfnName) elog(ERROR, "aggregate must have a transition function"); /* check for polymorphic and INTERNAL arguments */ hasPolyArg = false; hasInternalArg = false; for (i = 0; i < numArgs; i++) { if (is_polymorphic_type(aggArgTypes[i])) hasPolyArg = true; else if (aggArgTypes[i] == INTERNALOID) hasInternalArg = true; } /* * If transtype is polymorphic, must have polymorphic argument also. * Else we will have no way to deduce the actual transtype. */ if (is_polymorphic_type(aggTransType) && !hasPolyArg) ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), errdetail("An aggregate using a polymorphic transition type must have at least" " one polymorphic argument."))); /* find the transfn */ nargs_transfn = numArgs + 1; fnArgs = (oid_t *) palloc(nargs_transfn * sizeof(oid_t)); fnArgs[0] = aggTransType; memcpy(fnArgs + 1, aggArgTypes, numArgs * sizeof(oid_t)); transfn = lookup_agg_func(aggtransfnName, nargs_transfn, fnArgs, &rettype); /* * Return type of transfn (possibly after refinement by * enforce_generic_type_consistency, if transtype isn't polymorphic) must * exactly match declared transtype. * * In the non-polymorphic-transtype case, it might be okay to allow a * rettype that's binary-coercible to transtype, but I'm not quite * convinced that it's either safe or useful. When transtype is * polymorphic we *must* demand exact equality. */ if (rettype != aggTransType) ereport(ERROR, ( errcode(E_DATATYPE_MISMATCH), errmsg("return type of transition function %s is not %s", nl_to_string(aggtransfnName), format_type_be(aggTransType)))); tup = search_syscache1(PROCOID, OID_TO_D(transfn)); if (!HT_VALID(tup)) elog(ERROR, "cache lookup failed for function %u", transfn); proc = (Form_pg_proc) GET_STRUCT(tup); /* * If the transfn is strict and the initval is NULL, make sure first input * type and transtype are the same (or at least binary-compatible), so * that it's OK to use the first input value as the initial transValue. */ if (proc->proisstrict && agginitval == NULL) { if (numArgs < 1 || !IsBinaryCoercible(aggArgTypes[0], aggTransType)) ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("must not omit initial value when transition function is strict" " and transition type is not compatible with input type"))); } release_syscache(tup); /* handle finalfn, if supplied */ if (aggfinalfnName) { fnArgs[0] = aggTransType; finalfn = lookup_agg_func(aggfinalfnName, 1, fnArgs, &finaltype); } else { /* * If no finalfn, aggregate result type is type of the state value */ finaltype = aggTransType; } ASSERT(OID_VALID(finaltype)); /* * If finaltype (i.e. aggregate return type) is polymorphic, inputs must * be polymorphic also, else parser will fail to deduce result type. * (Note: given the previous test on transtype and inputs, this cannot * happen, unless someone has snuck a finalfn definition into the catalogs * that itself violates the rule against polymorphic result with no * polymorphic input.) */ if (is_polymorphic_type(finaltype) && !hasPolyArg) { ereport(ERROR, ( errcode(E_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), errdetail("An aggregate returning a polymorphic type " "must have at least one polymorphic argument."))); } /* * Also, the return type can't be INTERNAL unless there's at least one * INTERNAL argument. This is the same type-safety restriction we enforce * for regular functions, but at the level of aggregates. We must test * this explicitly because we allow INTERNAL as the transtype. */ if (finaltype == INTERNALOID && !hasInternalArg) ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at" " least one \"internal\" argument."))); /* handle sortop, if supplied */ if (aggsortopName) { if (numArgs != 1) ereport(ERROR, ( errcode(E_INVALID_FUNCTION_DEFINITION), errmsg("sort operator can only be specified for" " single-argument aggregates"))); sortop = lookup_opr_name( NULL, aggsortopName, aggArgTypes[0], aggArgTypes[0], false, -1); } /* * Everything looks okay. Try to create the pg_proc entry for the * aggregate. (This could fail if there's already a conflicting entry.) */ procOid = procedure_create( aggName, aggNamespace, false, /* no replacement */ false, /* doesn't return a set */ finaltype, /* returnType */ INTERNALlanguageId, /* languageObjectId */ INVALID_OID, /* no validator */ "aggregate_dummy", /* placeholder proc */ NULL, /* probin */ true, /* isAgg */ false, /* isWindowFunc */ false, /* security invoker (currently not definable for agg) */ false, /* isStrict (not needed for agg) */ PROVOLATILE_IMMUTABLE, /* volatility (not needed for agg) */ buildoidvector(aggArgTypes, numArgs),/* paramTypes */ PTR_TO_D(NULL), /* allParamTypes */ PTR_TO_D(NULL), /* parameterModes */ PTR_TO_D(NULL), /* parameterNames */ NIL, /* parameterDefaults */ PTR_TO_D(NULL), /* proconfig */ 1, /* procost */ 0); /* prorows */ /* * Okay to create the pg_aggregate entry. */ /* initialize nulls and values */ for (i = 0; i < Natts_pg_aggregate; i++) { nulls[i] = false; values[i] = (datum_t) NULL; } values[Anum_pg_aggregate_aggfnoid - 1] = OID_TO_D(procOid); values[Anum_pg_aggregate_aggtransfn - 1] = OID_TO_D(transfn); values[Anum_pg_aggregate_aggfinalfn - 1] = OID_TO_D(finalfn); values[Anum_pg_aggregate_aggsortop - 1] = OID_TO_D(sortop); values[Anum_pg_aggregate_aggtranstype - 1] = OID_TO_D(aggTransType); if (agginitval) values[Anum_pg_aggregate_agginitval - 1] = CStringGetTextDatum(agginitval); else nulls[Anum_pg_aggregate_agginitval - 1] = true; aggdesc = heap_open(AggregateRelationId, ROW_EXCL_LOCK); tupDesc = aggdesc->rd_att; tup = heap_form_tuple(tupDesc, values, nulls); simple_heap_insert(aggdesc, tup); cat_update_indexes(aggdesc, tup); heap_close(aggdesc, ROW_EXCL_LOCK); /* * Create dependencies for the aggregate (above and beyond those already * made by procedure_create). Note: we don't need an explicit dependency * on aggTransType since we depend on it indirectly through transfn. */ myself.classId = ProcedureRelationId; myself.objectId = procOid; myself.objectSubId = 0; /* Depends on transition function */ referenced.classId = ProcedureRelationId; referenced.objectId = transfn; referenced.objectSubId = 0; record_dep_on(&myself, &referenced, DEP_NORMAL); /* Depends on final function, if any */ if (OID_VALID(finalfn)) { referenced.classId = ProcedureRelationId; referenced.objectId = finalfn; referenced.objectSubId = 0; record_dep_on(&myself, &referenced, DEP_NORMAL); } /* Depends on sort operator, if any */ if (OID_VALID(sortop)) { referenced.classId = OperatorRelationId; referenced.objectId = sortop; referenced.objectSubId = 0; record_dep_on(&myself, &referenced, DEP_NORMAL); } }