Beispiel #1
0
/*
 * 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);
}
Beispiel #2
0
/*
 * 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;
}
Beispiel #3
0
/* 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;
}
Beispiel #4
0
/* 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;
}
Beispiel #5
0
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);
}
Beispiel #6
0
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);
}
Beispiel #7
0
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();
    }
}
Beispiel #8
0
/*
 * 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;
}
Beispiel #9
0
/*
 * 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;
}
Beispiel #10
0
/*
 * 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;
}
Beispiel #11
0
/*
 * 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;
}
Beispiel #12
0
/* --------------------------------
 * 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();
}
Beispiel #13
0
VALUE
shoes_canvas_get_scroll_top(VALUE self)
{
  GET_STRUCT(canvas, canvas);
  return INT2NUM(canvas->slot->scrolly);
}
Beispiel #14
0
/* ----------------------------------------------------------------
 *		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);
			}
		}
Beispiel #15
0
//
// 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;
}
Beispiel #16
0
/*
 * 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);
}
Beispiel #17
0
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;
}
Beispiel #18
0
/*
 * 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;
}
Beispiel #19
0
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;
}
Beispiel #20
0
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();
}
Beispiel #21
0
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;
}
Beispiel #22
0
/*
 * 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;
}
Beispiel #23
0
/*
 * 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);
	}
}