static void makeRangeConstructors()

in src/backend/commands/typecmds.c [122:728]


static void makeRangeConstructors(const char *name, Oid namespace,
								  Oid rangeOid, Oid subtype);
static void makeMultirangeConstructors(const char *name, Oid namespace,
									   Oid multirangeOid, Oid rangeOid,
									   Oid rangeArrayOid, Oid *castFuncOid);
static Oid	findTypeInputFunction(List *procname, Oid typeOid);
static Oid	findTypeOutputFunction(List *procname, Oid typeOid);
static Oid	findTypeReceiveFunction(List *procname, Oid typeOid);
static Oid	findTypeSendFunction(List *procname, Oid typeOid);
static Oid	findTypeTypmodinFunction(List *procname);
static Oid	findTypeTypmodoutFunction(List *procname);
static Oid	findTypeAnalyzeFunction(List *procname, Oid typeOid);
static Oid	findTypeSubscriptingFunction(List *procname, Oid typeOid);
static Oid	findRangeSubOpclass(List *opcname, Oid subtype);
static Oid	findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid	findRangeSubtypeDiffFunction(List *procname, Oid subtype);
static void validateDomainConstraint(Oid domainoid, char *ccbin);
static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
static void checkEnumOwner(HeapTuple tup);
static char *domainAddConstraint(Oid domainOid, Oid domainNamespace,
								 Oid baseTypeOid,
								 int typMod, Constraint *constr,
								 const char *domainName, ObjectAddress *constrAddr);
static Node *replace_domain_constraint_value(ParseState *pstate,
											 ColumnRef *cref);

static void AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
							 HeapTuple tup, Relation catalog,
							 AlterTypeRecurseParams *atparams);


/*
 * DefineType
 *		Registers a new base type.
 */
ObjectAddress
DefineType(ParseState *pstate, List *names, List *parameters)
{
	char	   *typeName;
	Oid			typeNamespace;
	int16		internalLength = -1;	/* default: variable-length */
	List	   *inputName = NIL;
	List	   *outputName = NIL;
	List	   *receiveName = NIL;
	List	   *sendName = NIL;
	List	   *typmodinName = NIL;
	List	   *typmodoutName = NIL;
	List	   *analyzeName = NIL;
	List	   *subscriptName = NIL;
	char		category = TYPCATEGORY_USER;
	bool		preferred = false;
	char		delimiter = DEFAULT_TYPDELIM;
	Oid			elemType = InvalidOid;
	char	   *defaultValue = NULL;
	bool		byValue = false;
	char		alignment = TYPALIGN_INT;	/* default alignment */
	char		storage = TYPSTORAGE_PLAIN; /* default TOAST storage method */
	Oid			collation = InvalidOid;
	DefElem    *likeTypeEl = NULL;
	DefElem    *internalLengthEl = NULL;
	DefElem    *inputNameEl = NULL;
	DefElem    *outputNameEl = NULL;
	DefElem    *receiveNameEl = NULL;
	DefElem    *sendNameEl = NULL;
	DefElem    *typmodinNameEl = NULL;
	DefElem    *typmodoutNameEl = NULL;
	DefElem    *analyzeNameEl = NULL;
	DefElem    *subscriptNameEl = NULL;
	DefElem    *categoryEl = NULL;
	DefElem    *preferredEl = NULL;
	DefElem    *delimiterEl = NULL;
	DefElem    *elemTypeEl = NULL;
	DefElem    *defaultValueEl = NULL;
	DefElem    *byValueEl = NULL;
	DefElem    *alignmentEl = NULL;
	DefElem    *storageEl = NULL;
	DefElem    *collatableEl = NULL;
	Oid			inputOid;
	Oid			outputOid;
	Oid			receiveOid = InvalidOid;
	Oid			sendOid = InvalidOid;
	Oid			typmodinOid = InvalidOid;
	Oid			typmodoutOid = InvalidOid;
	Oid			analyzeOid = InvalidOid;
	Oid			subscriptOid = InvalidOid;
	char	   *array_type;
	Oid			array_oid;
	Oid			typoid;
	Datum		typoptions = 0;
	List	   *encoding = NIL;
	ListCell   *pl;
	ObjectAddress address;

	/*
	 * As of Postgres 8.4, we require superuser privilege to create a base
	 * type.  This is simple paranoia: there are too many ways to mess up the
	 * system with an incorrect type definition (for instance, representation
	 * parameters that don't match what the C code expects).  In practice it
	 * takes superuser privilege to create the I/O functions, and so the
	 * former requirement that you own the I/O functions pretty much forced
	 * superuserness anyway.  We're just making doubly sure here.
	 *
	 * XXX re-enable NOT_USED code sections below if you remove this test.
	 */
	if (!superuser())
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("must be superuser to create a base type")));

	/* Convert list of names to a name and namespace */
	typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName);

#ifdef NOT_USED
	/* XXX this is unnecessary given the superuser check above */
	/* Check we have creation rights in target namespace */
	aclresult = pg_namespace_aclcheck(typeNamespace, GetUserId(), ACL_CREATE);
	if (aclresult != ACLCHECK_OK)
		aclcheck_error(aclresult, OBJECT_SCHEMA,
					   get_namespace_name(typeNamespace));
#endif

	/*
	 * Look to see if type already exists.
	 */
	typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
							 CStringGetDatum(typeName),
							 ObjectIdGetDatum(typeNamespace));

	/*
	 * If it's not a shell, see if it's an autogenerated array type, and if so
	 * rename it out of the way.
	 */
	if (OidIsValid(typoid) && get_typisdefined(typoid))
	{
		if (moveArrayTypeName(typoid, typeName, typeNamespace))
			typoid = InvalidOid;
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("type \"%s\" already exists", typeName)));
	}

	/*
	 * If this command is a parameterless CREATE TYPE, then we're just here to
	 * make a shell type, so do that (or fail if there already is a shell).
	 */
	if (!OidIsValid(typoid))
	{
		address = TypeShellMake(typeName, typeNamespace, GetUserId());
		typoid = address.objectId;
		/* Make new shell type visible for modification below */
		CommandCounterIncrement();

		/*
		 * If the command was a parameterless CREATE TYPE, we're done ---
		 * creating the shell type was all we're supposed to do.
		 */
		if (parameters == NIL)
		{
			/* Must dispatch shell type creation */
			if (Gp_role == GP_ROLE_DISPATCH)
			{
				DefineStmt * stmt = makeNode(DefineStmt);
				stmt->kind = OBJECT_TYPE;
				stmt->oldstyle = false; /*?*/
				stmt->defnames = names;
				stmt->args = NIL;
				stmt->definition = NIL;
				CdbDispatchUtilityStatement((Node *) stmt,
											DF_CANCEL_ON_ERROR|
											DF_WITH_SNAPSHOT|
											DF_NEED_TWO_PHASE,
											GetAssignedOidsForDispatch(),
											NULL);
			}
			return address;
		}
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("type \"%s\" does not exist", typeName),
					 errhint("Create the type as a shell type, then create its I/O functions, then do a full CREATE TYPE.")));
	}
	else
	{
		if (parameters == NIL)
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("type \"%s\" already exists", typeName)));
	}

	/* Extract the parameters from the parameter list */
	foreach(pl, parameters)
	{
		DefElem    *defel = (DefElem *) lfirst(pl);
		DefElem   **defelp;

		if (strcmp(defel->defname, "like") == 0)
			defelp = &likeTypeEl;
		else if (strcmp(defel->defname, "internallength") == 0)
			defelp = &internalLengthEl;
		else if (strcmp(defel->defname, "input") == 0)
			defelp = &inputNameEl;
		else if (strcmp(defel->defname, "output") == 0)
			defelp = &outputNameEl;
		else if (strcmp(defel->defname, "receive") == 0)
			defelp = &receiveNameEl;
		else if (strcmp(defel->defname, "send") == 0)
			defelp = &sendNameEl;
		else if (strcmp(defel->defname, "typmod_in") == 0)
			defelp = &typmodinNameEl;
		else if (strcmp(defel->defname, "typmod_out") == 0)
			defelp = &typmodoutNameEl;
		else if (strcmp(defel->defname, "analyze") == 0 ||
				 strcmp(defel->defname, "analyse") == 0)
			defelp = &analyzeNameEl;
		else if (strcmp(defel->defname, "subscript") == 0)
			defelp = &subscriptNameEl;
		else if (strcmp(defel->defname, "category") == 0)
			defelp = &categoryEl;
		else if (strcmp(defel->defname, "preferred") == 0)
			defelp = &preferredEl;
		else if (strcmp(defel->defname, "delimiter") == 0)
			defelp = &delimiterEl;
		else if (strcmp(defel->defname, "element") == 0)
			defelp = &elemTypeEl;
		else if (strcmp(defel->defname, "default") == 0)
			defelp = &defaultValueEl;
		else if (strcmp(defel->defname, "passedbyvalue") == 0)
			defelp = &byValueEl;
		else if (strcmp(defel->defname, "alignment") == 0)
			defelp = &alignmentEl;
		else if (strcmp(defel->defname, "storage") == 0)
			defelp = &storageEl;
		else if (strcmp(defel->defname, "collatable") == 0)
			defelp = &collatableEl;
		else if (is_storage_encoding_directive(defel->defname))
		{
			/* 
			 * This is to define default block size, compress type, and
			 * compress level. When this type is used in an append only column
			 * oriented table, the column's encoding will be defaulted to these
			 * values.
			 * We have to do this instead of having a list of encoding clauses as 
			 * in ALTER TYPE because the way the encoding clauses are consumed 
			 * in the parser for CREATE TYPE.
			 */
			encoding = lappend(encoding, defel);
			continue;
		}
		else
		{
			/* WARNING, not ERROR, for historical backwards-compatibility */
			ereport(WARNING,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("type attribute \"%s\" not recognized",
							defel->defname),
					 parser_errposition(pstate, defel->location)));
			continue;
		}
		if (*defelp != NULL)
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("conflicting or redundant options"),
					 parser_errposition(pstate, defel->location)));
		*defelp = defel;
	}

	/*
	 * Now interpret the options; we do this separately so that LIKE can be
	 * overridden by other options regardless of the ordering in the parameter
	 * list.
	 */
	if (likeTypeEl)
	{
		Type		likeType;
		Form_pg_type likeForm;

		likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
		likeForm = (Form_pg_type) GETSTRUCT(likeType);
		internalLength = likeForm->typlen;
		byValue = likeForm->typbyval;
		alignment = likeForm->typalign;
		storage = likeForm->typstorage;
		ReleaseSysCache(likeType);
	}
	if (internalLengthEl)
		internalLength = defGetTypeLength(internalLengthEl);
	if (inputNameEl)
		inputName = defGetQualifiedName(inputNameEl);
	if (outputNameEl)
		outputName = defGetQualifiedName(outputNameEl);
	if (receiveNameEl)
		receiveName = defGetQualifiedName(receiveNameEl);
	if (sendNameEl)
		sendName = defGetQualifiedName(sendNameEl);
	if (typmodinNameEl)
		typmodinName = defGetQualifiedName(typmodinNameEl);
	if (typmodoutNameEl)
		typmodoutName = defGetQualifiedName(typmodoutNameEl);
	if (analyzeNameEl)
		analyzeName = defGetQualifiedName(analyzeNameEl);
	if (subscriptNameEl)
		subscriptName = defGetQualifiedName(subscriptNameEl);
	if (categoryEl)
	{
		char	   *p = defGetString(categoryEl);

		category = p[0];
		/* restrict to non-control ASCII */
		if (category < 32 || category > 126)
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("invalid type category \"%s\": must be simple ASCII",
							p)));
	}
	if (preferredEl)
		preferred = defGetBoolean(preferredEl);
	if (delimiterEl)
	{
		char	   *p = defGetString(delimiterEl);

		delimiter = p[0];
		/* XXX shouldn't we restrict the delimiter? */
	}
	if (elemTypeEl)
	{
		elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl));
		/* disallow arrays of pseudotypes */
		if (get_typtype(elemType) == TYPTYPE_PSEUDO)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("array element type cannot be %s",
							format_type_be(elemType))));
	}
	if (defaultValueEl)
		defaultValue = defGetString(defaultValueEl);
	if (byValueEl)
		byValue = defGetBoolean(byValueEl);
	if (alignmentEl)
	{
		char	   *a = defGetString(alignmentEl);

		/*
		 * Note: if argument was an unquoted identifier, parser will have
		 * applied translations to it, so be prepared to recognize translated
		 * type names as well as the nominal form.
		 */
		if (pg_strcasecmp(a, "double") == 0 ||
			pg_strcasecmp(a, "float8") == 0 ||
			pg_strcasecmp(a, "pg_catalog.float8") == 0)
			alignment = TYPALIGN_DOUBLE;
		else if (pg_strcasecmp(a, "int4") == 0 ||
				 pg_strcasecmp(a, "pg_catalog.int4") == 0)
			alignment = TYPALIGN_INT;
		else if (pg_strcasecmp(a, "int2") == 0 ||
				 pg_strcasecmp(a, "pg_catalog.int2") == 0)
			alignment = TYPALIGN_SHORT;
		else if (pg_strcasecmp(a, "char") == 0 ||
				 pg_strcasecmp(a, "pg_catalog.bpchar") == 0)
			alignment = TYPALIGN_CHAR;
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("alignment \"%s\" not recognized", a)));
	}
	if (storageEl)
	{
		char	   *a = defGetString(storageEl);

		if (pg_strcasecmp(a, "plain") == 0)
			storage = TYPSTORAGE_PLAIN;
		else if (pg_strcasecmp(a, "external") == 0)
			storage = TYPSTORAGE_EXTERNAL;
		else if (pg_strcasecmp(a, "extended") == 0)
			storage = TYPSTORAGE_EXTENDED;
		else if (pg_strcasecmp(a, "main") == 0)
			storage = TYPSTORAGE_MAIN;
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("storage \"%s\" not recognized", a)));
	}
	if (collatableEl)
		collation = defGetBoolean(collatableEl) ? DEFAULT_COLLATION_OID : InvalidOid;

	/* transform the encoding clause and then get the typoptions */
	if (encoding)
	{
		encoding = transformStorageEncodingClause(encoding, true);
		typoptions = transformRelOptions((Datum) 0, encoding, NULL, NULL, true, false);
	}

	/*
	 * make sure we have our required definitions
	 */
	if (inputName == NIL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("type input function must be specified")));
	if (outputName == NIL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("type output function must be specified")));

	if (typmodinName == NIL && typmodoutName != NIL)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("type modifier output function is useless without a type modifier input function")));

	/*
	 * Convert I/O proc names to OIDs
	 */
	inputOid = findTypeInputFunction(inputName, typoid);
	outputOid = findTypeOutputFunction(outputName, typoid);
	if (receiveName)
		receiveOid = findTypeReceiveFunction(receiveName, typoid);
	if (sendName)
		sendOid = findTypeSendFunction(sendName, typoid);

	/*
	 * Convert typmodin/out function proc names to OIDs.
	 */
	if (typmodinName)
		typmodinOid = findTypeTypmodinFunction(typmodinName);
	if (typmodoutName)
		typmodoutOid = findTypeTypmodoutFunction(typmodoutName);

	/*
	 * Convert analysis function proc name to an OID. If no analysis function
	 * is specified, we'll use zero to select the built-in default algorithm.
	 */
	if (analyzeName)
		analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);

	/*
	 * Likewise look up the subscripting function if any.  If it is not
	 * specified, but a typelem is specified, allow that if
	 * raw_array_subscript_handler can be used.  (This is for backwards
	 * compatibility; maybe someday we should throw an error instead.)
	 */
	if (subscriptName)
		subscriptOid = findTypeSubscriptingFunction(subscriptName, typoid);
	else if (OidIsValid(elemType))
	{
		if (internalLength > 0 && !byValue && get_typlen(elemType) > 0)
			subscriptOid = F_RAW_ARRAY_SUBSCRIPT_HANDLER;
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("element type cannot be specified without a subscripting function")));
	}

	/*
	 * Check permissions on functions.  We choose to require the creator/owner
	 * of a type to also own the underlying functions.  Since creating a type
	 * is tantamount to granting public execute access on the functions, the
	 * minimum sane check would be for execute-with-grant-option.  But we
	 * don't have a way to make the type go away if the grant option is
	 * revoked, so ownership seems better.
	 *
	 * XXX For now, this is all unnecessary given the superuser check above.
	 * If we ever relax that, these calls likely should be moved into
	 * findTypeInputFunction et al, where they could be shared by AlterType.
	 */
#ifdef NOT_USED
	if (inputOid && !pg_proc_ownercheck(inputOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(inputName));
	if (outputOid && !pg_proc_ownercheck(outputOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(outputName));
	if (receiveOid && !pg_proc_ownercheck(receiveOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(receiveName));
	if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(sendName));
	if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(typmodinName));
	if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(typmodoutName));
	if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(analyzeName));
	if (subscriptOid && !pg_proc_ownercheck(subscriptOid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION,
					   NameListToString(subscriptName));
#endif

	/*
	 * OK, we're done checking, time to make the type.  We must assign the
	 * array type OID ahead of calling TypeCreate, since the base type and
	 * array type each refer to the other.
	 */
	array_type = makeArrayTypeName(typeName, typeNamespace);
	array_oid = AssignTypeArrayOid(array_type, typeNamespace);

	/*
	 * now have TypeCreate do all the real work.
	 *
	 * Note: the pg_type.oid is stored in user tables as array elements (base
	 * types) in ArrayType and in composite types in DatumTupleFields.  This
	 * oid must be preserved by binary upgrades.
	 */
	address =
		TypeCreate(InvalidOid,	/* no predetermined type OID */
				   typeName,	/* type name */
				   typeNamespace,	/* namespace */
				   InvalidOid,	/* relation oid (n/a here) */
				   0,			/* relation kind (ditto) */
				   GetUserId(), /* owner's ID */
				   internalLength,	/* internal size */
				   TYPTYPE_BASE,	/* type-type (base type) */
				   category,	/* type-category */
				   preferred,	/* is it a preferred type? */
				   delimiter,	/* array element delimiter */
				   inputOid,	/* input procedure */
				   outputOid,	/* output procedure */
				   receiveOid,	/* receive procedure */
				   sendOid,		/* send procedure */
				   typmodinOid, /* typmodin procedure */
				   typmodoutOid,	/* typmodout procedure */
				   analyzeOid,	/* analyze procedure */
				   subscriptOid,	/* subscript procedure */
				   elemType,	/* element type ID */
				   false,		/* this is not an implicit array type */
				   array_oid,	/* array type we are about to create */
				   InvalidOid,	/* base type ID (only for domains) */
				   defaultValue,	/* default type value */
				   NULL,		/* no binary form available */
				   byValue,		/* passed by value */
				   alignment,	/* required alignment */
				   storage,		/* TOAST strategy */
				   -1,			/* typMod (Domains only) */
				   0,			/* Array Dimensions of typbasetype */
				   false,		/* Type NOT NULL */
				   collation);	/* type's collation */
	Assert(typoid == address.objectId);

	/* now pg_type_encoding */
	if (DatumGetPointer(typoptions) != NULL)
		add_type_encoding(address.objectId, typoptions);

	/*
	 * Create the array type that goes with it.
	 */

	/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for arrays */
	alignment = (alignment == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;

	TypeCreate(array_oid,		/* force assignment of this type OID */
			   array_type,		/* type name */
			   typeNamespace,	/* namespace */
			   InvalidOid,		/* relation oid (n/a here) */
			   0,				/* relation kind (ditto) */
			   GetUserId(),		/* owner's ID */
			   -1,				/* internal size (always varlena) */
			   TYPTYPE_BASE,	/* type-type (base type) */
			   TYPCATEGORY_ARRAY,	/* type-category (array) */
			   false,			/* array types are never preferred */
			   delimiter,		/* array element delimiter */
			   F_ARRAY_IN,		/* input procedure */
			   F_ARRAY_OUT,		/* output procedure */
			   F_ARRAY_RECV,	/* receive procedure */
			   F_ARRAY_SEND,	/* send procedure */
			   typmodinOid,		/* typmodin procedure */
			   typmodoutOid,	/* typmodout procedure */
			   F_ARRAY_TYPANALYZE,	/* analyze procedure */
			   F_ARRAY_SUBSCRIPT_HANDLER,	/* array subscript procedure */
			   typoid,			/* element type ID */
			   true,			/* yes this is an array type */
			   InvalidOid,		/* no further array type */
			   InvalidOid,		/* base type ID */
			   NULL,			/* never a default type value */
			   NULL,			/* binary default isn't sent either */
			   false,			/* never passed by value */
			   alignment,		/* see above */
			   TYPSTORAGE_EXTENDED, /* ARRAY is always toastable */
			   -1,				/* typMod (Domains only) */
			   0,				/* Array dimensions of typbasetype */
			   false,			/* Type NOT NULL */
			   collation);		/* type's collation */

	pfree(array_type);

	if (Gp_role == GP_ROLE_DISPATCH)
	{
		DefineStmt * stmt = makeNode(DefineStmt);
		stmt->kind = OBJECT_TYPE;
		stmt->oldstyle = false; /*?*/
		stmt->defnames = names;
		stmt->args = NIL;
		stmt->definition = parameters;

		CdbDispatchUtilityStatement((Node *) stmt,
									DF_CANCEL_ON_ERROR|
									DF_WITH_SNAPSHOT|
									DF_NEED_TWO_PHASE,
									GetAssignedOidsForDispatch(),
									NULL);
	}

	return address;
}