/*
15.5/EBF 17340 SMP/P/x86_64/Enterprise Linux/ase155/2391/64-bit/OPT/Mon Nov  9 14:15:35 2009

Confidential property of Sybase, Inc.
Copyright 1987, 2009
Sybase, Inc.  All rights reserved.
Unpublished rights reserved under U.S. copyright laws.

This software contains confidential and trade secret information of Sybase,
Inc.   Use,  duplication or disclosure of the software and documentation by
the  U.S.  Government  is  subject  to  restrictions set forth in a license
agreement  between  the  Government  and  Sybase,  Inc.  or  other  written
agreement  specifying  the  Government's rights to use the software and any
applicable FAR provisions, for example, FAR 52.227-19.
Sybase, Inc. One Sybase Drive, Dublin, CA 94568, USA
*/

declare @retval int
exec @retval = sp_version 'installdbextend', NULL, '15.5/EBF 17340 SMP/P/x86_64/Enterprise Linux/ase155/2391/64-bit/OPT/Mon Nov  9 14:15:35 2009', 'start'
if (@retval != 0) select syb_quit()
go

declare @script_versnum int
select @script_versnum = 15500
if (@@version_number < @script_versnum)
begin
	print "'installdbextend' is being run on an older ASE installation. ASE version '%1!', install scripts version '%2!'.",
		@@version_number, @script_versnum
	select syb_quit()
end
go

declare @do_quit int
select @do_quit = 0
if (check_db_upgrade('master') = 0)
begin
	print "'installdbextend' cannot continue, because 'master' database is not upgraded to correct version."
	select @do_quit = 1
end
if (check_db_upgrade('tempdb') = 0)
begin
	print "'installdbextend' cannot continue, because 'tempdb' database is not upgraded to correct version."
	select @do_quit = 1
end
if (check_db_upgrade('model') = 0)
begin
	print "'installdbextend' cannot continue, because 'model' database is not upgraded to correct version."
	select @do_quit = 1
end
if (check_db_upgrade('sybsystemprocs') = 0)
begin
	print "'installdbextend' cannot continue, because 'sybsystemprocs' database is not upgraded to correct version."
	select @do_quit = 1
end
if (@do_quit = 1)
	select syb_quit()
go


use master
go

sp_configure "allow updates", 1
go

set proc_return_status off
go

set nocount on
go

use master
go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:06 2003 
*/
/*
** raiserror Messages for dbxt_utils [Total 5]
**
** 19205, "%1!Internal Error. %2!."
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19233, "%1! Internal error. Temp table '%2!' already contains %3! rows for database '%4!' inserted by procedure '%5!'."
** 19528, "The '%1!' database does not exist. Terminating the installation."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_utils [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_utils ..."
go

if exists (select * from master.dbo.sysdatabases
		where name = "sybsystemprocs")
begin
	use sybsystemprocs
end
else
begin
	/*
	** 19528, "The '%1!' database does not exist. Terminating the installation."
	*/
	raiserror 19528, "sybsystemprocs"
	select syb_quit()
end
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

/*
{
**	*** Utility procedures used under db expansion sprocs ***
*/
exec sp_dbxt_recreate_proc sp_dbxt_lpages_2_MB
go

/*
** ***************************************************************************
** sp_dbxt_lpages_2_MB
**
** Convert # of logical pages to a string in Mb.
**
** Parameters:
**	@nlpages	- # of logical pages.
**
** Output Parameters:
**	@nlpages_MB	- String giving # of logical pages in MB.
{
*/
create procedure sp_dbxt_lpages_2_MB(
			  @nlpages	int
			, @nlpages_MB	float output
) as
begin
	declare @megabytes	int
	      , @nlpages_per_MB	int

	-- Build conversion from # of logical pages to MB.
	-- 
	select @megabytes = (1024 * 1024)
	select @nlpages_per_MB = (@megabytes / @@maxpagesize)

	select @nlpages_MB = (convert(float, @nlpages) / @nlpages_per_MB)
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_gen_set_date
go

create procedure sp_dbxt_gen_set_date(
			  @cmd		varchar(10)
			, @getdate 	varchar(30)
			, @setdate	varchar(30)	output
)
as
begin
	-- Comes out as:
	--	set='date output'
	--	mod='date output'
	--
	select @setdate = @cmd + "='" + convert(varchar(20), @getdate) + "'"
end
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_build_name_list
**
** Given a @start, and @end number values, and other information identifying
** rows in spt_values for this feature, build a comma-separated list of
** option names and return that to the caller.
**
** This is useful when reporting the list of valid names for error conditions.
**
** This is an internal sproc, meant (mostly) to be called from
** sp_dbxt_build_cmd_list. External callers should call that one with the
** right directory entry number to do the lookup.
**
** Parameters:
**	@type	- Type code for this feature.
**	@start	- Start number of option we are searching for.
**	@end	- End number of option we are searching for.
**
** Returns:
**	Comma-separated char value of list of options.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_build_name_list
go

create procedure sp_dbxt_build_name_list(
			  @type		varchar(2)
			, @start	int
			, @end		int
			, @separator	varchar(4)
			, @list		varchar(256) output
) as
begin
	declare @value	varchar(30)	-- spt_values.name column
	      , @ictr	int

	select    @list = NULL
		, @ictr = @start

	-- Build a comma-separated list of type names from spt_values,
	-- processing through rows of @type, and in (@start .. @end).
	-- Generate the list in sorted order.
	--
	declare spt_values_cur cursor for
	select name
	from master.dbo.spt_values
	where type = @type
	  and number between @start and @end
	order by name asc

	open spt_values_cur

	fetch spt_values_cur into @value

	while (@@sqlstatus = 0)
	begin
		-- First item in the list does need separator.
		select @list = @list
				+ case when @ictr = @start
				       then NULL
				       else @separator
				  end
				+ "'" + @value + "'"

		fetch spt_values_cur into @value
		select @ictr = @ictr + 1
	end

	close spt_values_cur

	deallocate cursor spt_values_cur
end
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_build_cmd_list
**
** Retrieve the known list of commands from spt_values and return that to
** the caller. This is a special-instance of the general lookup, searching
** for only 'commands'.
**
** Parameters:
**	@type		- Type code for this feature.
**	@direntry	- Ordinal number for class of items.
**
** Output Parameters:
**	@cmdlist	- Comma-separated list of 'commands'
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_build_cmd_list
go

create procedure sp_dbxt_build_cmd_list(
				  @type		varchar(2)
				, @direntry	int
				, @cmdlist	varchar(256) output
) as
begin
	declare	@low	int
	      , @high	int

	-- First find range of numbers for 'commands'
	--
	select @low = low, @high = high
	from master.dbo.spt_values
	where type = @type
	  and number = @direntry

	exec sp_dbxt_build_name_list @type, @low, @high, ", "
					, @cmdlist output
end
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_build_args_list
**
** Given a name of a class of things, build an output list of the types
** of such 'things' that can exist. For instance if we are searching for
** 'objects', find the list of names defined for 'objects' and return them
** as a string, separated by '|'.
**
** This is used in error messages and usage information when reporting to
** the user the valid set of arguments for a particular command.
**
** Example:
**	For the 'set' command, the list of 'objects' is:
**		'database', 'device', 'threshold' etc.
**
**	Build a string like:
**		 "'database' | 'device' | 'threshold'"
**
** 	and return that.
**
** Note: This is quite similar to sp_dbxt_build_cmd_list with the difference
**	 that produces ','-separated lists, and this produces '|'-separated
**	 lists.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_build_args_list
go

create procedure sp_dbxt_build_args_list (
				  @type		varchar(2)
				, @direntry	int
				, @cmdlist	varchar(256) output
) as
begin
	declare	@low	int
	      , @high	int

	-- First find range of numbers for 'commands'
	--
	select @low = low, @high = high
	from master.dbo.spt_values
	where type = @type
	  and number = @direntry

	exec sp_dbxt_build_name_list @type, @low, @high, " | "
					, @cmdlist output
end
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_name_to_ordnum
**
** Utility procedure that takes an argument from the user for a known class
** of lists (e.g. commands, object types etc.), and converts it to an
** ordinal number. Here is where we support unique prefixes and case-ignorant
** lookups of names in a known list from spt_values.
**
** Parameters:
**	@type		- This feature's type code in spt_values.
**	@direntry	- Type's class number.
**	@class		- Class of item; 'commands', 'objects' etc.
**
** Output Parameters:
**	@arg		- User-provided argument that we are trying to
**			  match on unique prefix, and validate. This is also
**			  used to return the full arg name given a prefix.
**
** Returns:
**	Ordinal number starting from 1 .. <n> for argument.
**	Negative numbers to indicate an error status.
**	-1	: Arg does not exist.
**	-2	: Arg exists, but is not a unique prefix.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_name_to_ordnum
go

create procedure sp_dbxt_name_to_ordnum(
			  @type		varchar(2)
			, @direntry	int
			, @class	varchar(30)
			, @arg		varchar(30) output
) as
begin
	declare	@low	int
	      , @high	int
	      , @retval	int

	-- Conver to lower-case, to avoid case mismatch errors.
	--
	select @arg = lower(@arg)

	-- First, find range of entries for object type: 'objects'.
	select @low = low, @high = high
	from master.dbo.spt_values
	where type = @type		-- feature's type code; e.g. 'XT'
	  and name = @class		-- class of option; e.g. 'objects'
	  and number = @direntry	-- dir entry for 'objects'

	-- See if the passed-in type even exists in the known object types.
	-- If not, return 0 so that the caller can flag an error.
	--
	if ((select count(*)
	     from master.dbo.spt_values
	     where type = @type
	       and number between @low and @high
	       and name like @arg + "%") = 0)
	begin
		return -1
	end

	-- See if a complete matching name was provided. If so, return
	-- that argument's ordinal #. (E.g. 'list', 'listfull'. If user
	-- types in 'list', use that first match.)
	--
	select @retval = (number - @low) + 1
	from master.dbo.spt_values
	where type = @type
	  and number between @low and @high
	  and name = @arg

	if (@retval is NOT NULL)
		return @retval

	-- Now see if the passed-in value is a duplicate by checking
	-- it against the existing object type values in the above range.
	--
	if ((select count(*)
	     from master.dbo.spt_values
	     where type = @type
	       and number between @low and @high
	       and name like @arg + "%") != 1)
	begin
		return -2
	end

	/*
	** Now that we know that we have a unique-prefix, find that
	** entry, and get its number. Subtract that from the low
	** of the range, to return an ordinal number describing the
	** object type argument.
	*/
	select    @retval = (number - @low) + 1
		, @arg = name
	from master.dbo.spt_values
	where type = @type
	  and number between @low and @high
	  and name like @arg + "%"

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_get_direntry
**
** Given a command, lookup the spt_values list for this feature, and retrieve
** the directory entry number. This is the 'number' column for the top-level
** list of 'items' for which we maintain lists in that table. This directory
** entry is then used to fetch the low/high range for the other entries in
** this class of items.
**
** Parameters:
**	@command	- Name of top-level 'class' of interest.
**			  E.g. 'command', 'objects' etc.
**
** Returns:
**	Directory entry for class if found. -1 Otherwise.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_get_direntry
go

create procedure sp_dbxt_get_direntry (@command	varchar(100)
) as
begin
	declare @retval	int

	select @retval = number
	from master.dbo.spt_values
	where type = 'XT'
	  and name = @command
	  and low is NOT NULL	-- These columns define the directory entry.
	  and high is NOT NULL

	-- return (case when @retval is NULL then -1 else @retval end)
	return (isnull(@retval, -1))
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_gen_command_tag
go

/*
** sp_dbxt_gen_command_tag
**
** Simple parser to take a sproc name (in canonical format) and break that
** up into individual pieces. Join the pieces back in to return back to the
** caller an output string describing the command that was issued by the
** user.
**
** This relies on a consistent naming convention being followed in the dbxt*
** family of procedures. A sproc that implements command 'foo', on object
** 'bar' must be named as one of:
**
**	sp_dbxt_foo_bar
**	sp_dbxt_foo_bar_chk
**
** The '_chk' version implements the security checks for each sub-command.
**
** . Parse this out to separate out the <command> (foo) and the <object> (bar).
** . Strip out the '_chk' suffix if it is found.
** . Reconstruct that using the top-level sp_dbextend sproc's name to
**   return the following string:
**
**	sp_dbextend 'foo', 'bar'
**
** Example: Calling this on 
**	sp_dbxt_clear_database
** or	sp_dbxt_clear_database_chk
**
** will yield: "sp_dbextend 'clear', 'database'"
**
** Parameters:
**	@whoami_cp	- Caller Sproc's name
**	@objname	- Object's name (if any). Could be NULL.
**	@outstr		- Output string formatted as described above.
*/
create procedure sp_dbxt_gen_command_tag(
				  @whoami_cp	varchar(30)
				, @objname	varchar(30)
				, @outstr	varchar(100) output
) as
begin
	declare @command	varchar(30)
	      , @object		varchar(30)
	      , @uscore_loc	int

	-- First strip out the 'sp_dbxt_' prefix.
	select @outstr = str_replace(@whoami_cp, 'sp_dbxt_', NULL)

	-- Locate the '_' char in the remaining text.
	select @uscore_loc = charindex('_', @outstr)

	-- Pull out the 1st <command> and 2nd <object> field.
	--
	select @command = substring(@outstr, 1, (@uscore_loc - 1))
	     , @object  = substring(@outstr, @uscore_loc + 1,
	     			    (datalength(@outstr) - @uscore_loc + 1) )

	-- Strip out '_chk' from @object, if it exists.
	--
	select @object = str_replace(@object, '_chk', NULL)

	-- Put the pieces back together, with top-level sproc's name.
	select @outstr = "sp_dbextend"
				 + " '" + @command + "'"

				 + case @object
				     when NULL then ""
				     else ", '" + @object + "'"
				   end

				 + case @objname
				     when NULL then ""
				     else ", '" + @objname + "'"
				   end
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_format_msg
go

/*
** ***************************************************************************
** sp_dbxt_format_msg
**
** Utility procedure to build a format string given a message to be printed.
** This is commonly used to banner-line messages for start of chunks of 
** output.
**
** This does some pretty-printing formatting for messages that will hold
** one argument for int type. Just assume that the int value will be no more
** than 3 chars.
**
** Parameters:
**	@msgid		- Message number to print.
**
** Output Parameters:
**	@fmtstr		- Format string, modified to accomodate the print
**			  specifiers for banner lines ('====' etc.)
**	@dashedline	- String of '=====...' long enough to cover message.
**
{
*/
create procedure sp_dbxt_format_msg(
			  @msgid	int
			, @fmtstr	varchar(256)	output
			, @dashedline	varchar(256)	output
) as
begin
	declare	@nfmt_specs	int	-- # of arg specifiers in message.
	      , @widthint_arg	int	-- # of bytes for int argument.

	select    @nfmt_specs 	= 1
		, @widthint_arg	= 3

	-- First get the message requested.
	exec sp_getmessage @msgid, @fmtstr output

	-- Build the banner line with '='s of exactly the right length 
	-- below the banner heading line.
	--
	select @dashedline = char(10)	-- line feed
				+ replicate('=',
				   (datalength(@fmtstr) 
				    - (@nfmt_specs * 3)	-- format specs
				    + @widthint_arg	-- extra padding
				   ))
				+ char(10)

	-- Add banner line printing print-specifiers to
	-- the @fmstr, so that we get a nicely separated
	-- banner.
	--
	select @fmtstr = char(10) + @fmtstr + " %2!"
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_validate_db
go

/*
** ***************************************************************************
** sp_dbxt_validate_db
**
** Validate that a given dbname is for a valid db that still exists.
**
** Parameters:
**	@dbname	- DB name that we want to check whether it still exists.
**
** Returns:
**	0	- DB does not exist.
**	<dbid>	- Valid dbname; return its dbid.
{
*/
create procedure sp_dbxt_validate_db(
			  @dbname		varchar(256)
			, @callerID		varchar(256)
) as
begin
	declare @whoami	varchar(30)
	      , @msg	varchar(100)
	      , @dbid	int

	select @whoami = object_name(@@procid, db_id('sybsystemprocs')) + ": "

	if (@dbname IS NULL)
	begin
		select @msg = @callerID + " supplied NULL @dbname argument."
		raiserror 19205 , @whoami , @msg
		return 0
	end

	select @dbid = dbid from master.dbo.sysdatabases
	where name = @dbname

	-- return (case when @dbid IS NULL then 0 else @dbid end)
	return (isnull(@dbid,0))
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_segid
go

/*
** ***************************************************************************
** sp_dbxt_segid
**
** Lookup segmentname in syssegments and return the segment's ID.
{
*/
create procedure sp_dbxt_segid(
			  @segmentname	varchar(256)
) as
begin
	declare @segid	int

	select @segid = segment
	from dbo.syssegments
	where name = @segmentname

	return @segid
end
go

if (@@error != 0) select syb_quit()
go

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as dbname
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_list_syssegments
go

/*
** ***************************************************************************
** sp_dbxt_list_syssegments
**
** List the contents of #syssegments table with auto-formatting.
{
*/
create procedure sp_dbxt_list_syssegments(
				@trace int	= 0
) as
begin
	declare @retval	int

	exec @retval = sp_autoformat
				  @fulltabname = "#syssegments"
				, @selectlist = "dbname, segmentname = name, segment"
	if (@retval != 0)
		return 1

	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_ins_db_seginfo
go

/*
** ***************************************************************************
** sp_dbxt_ins_db_seginfo
**
** Simple procedure to cache the contents of syssegments into a #temp table
** so that we can use that without having to be in the database that is
** being worked on.
**
** Parameters:
**	@dbname	- DB whose syssgements data we want to cache.
**	@trace	- Tracing level.
**
** Returns:
**	0	- If the insert succeeded
**	1	- Otherwise.
{
*/
create procedure sp_dbxt_ins_db_seginfo(
				  @dbname	varchar(256)
				, @callerID	varchar(30)
				, @trace 	int	= 0
) as
begin
	declare @sqlstr		varchar(256)
	      , @retval		int
	      , @whoami		varchar(32)
	      , @thisdb		varchar(256)
	      , @numrows	int

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))

	select @thisdb = db_name()
	if (@thisdb != 'sybsystemprocs')
	begin
		raiserror 19232, @whoami, 'sybsystemprocs', @thisdb
		return 1
	end

	/*
	if (@trace = 1)
	begin
		print "%1!: Cache data for '%2!'", @whoami , @dbname
	end
	*/

	-- We should only be calling this sproc once in the entire execution.
	-- If we find rows for this db, someone has already cached the data.
	-- This implies a logic error in the sprocs. Fail this operation.
	--
	select @numrows = count(*) from #syssegments where dbname = @dbname
	if (@numrows != 0)
	begin
		-- Re-set input arg as we are erroring out anyway.
		--
		select @callerID = inserted_by
		from #syssegments
		where dbname = @dbname

		raiserror 19233
			, @whoami , '#syssegments', @numrows
			, @dbname, @callerID
		return 1
	end

	select @sqlstr =  "insert #syssegments(dbname, inserted_by"
					+ ", segment, name, status"
					+ ")"
			+ " select "
				+ "'" + @dbname + "'"
				+  ",'" + @callerID + "'"
				+ ", segment, name, status"
				+ " from "
				+ @dbname + ".dbo.syssegments"

	exec @retval = sp_exec_SQL @sqlstr, @whoami
	return @retval
end
go

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, free_space, status, proc_name, suid, currauth
	into #systhresholds lock allpages
	from systhresholds
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_list_systhresholds
go

/*
** ***************************************************************************
** sp_dbxt_list_systhresholds
**
** List the contents of #systhresholds table with auto-formatting.
{
*/
create procedure sp_dbxt_list_systhresholds(
				@trace int	= 0
) as
begin
	declare @retval int

	exec @retval = sp_autoformat
				  @fulltabname = "#systhresholds"
				, @selectlist = "dbname, segment, free_space"
	if (@retval != 0)
		return 1
	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_ins_db_thresholds
go

/*
** ***************************************************************************
** sp_dbxt_ins_db_thresholds
**
** Simple procedure to cache the contents of systhresholds into a #temp table
** so that we can use that without having to be in the database that is
** being worked on.
**
** Parameters:
**	@dbname	- DB whose syssgements data we want to cache.
**	@trace	- Tracing level.
**
** Returns:
**	0	- If the insert succeeded
**	1	- Otherwise.
{
*/
create procedure sp_dbxt_ins_db_thresholds(
				  @dbname	varchar(256)
				, @callerID	varchar(30)
				, @trace 	int	= 0
) as
begin
	declare @sqlstr		varchar(512)
	      , @retval		int
	      , @whoami		varchar(32)
	      , @thisdb		varchar(256)
	      , @numrows	int

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))

	select @thisdb = db_name()
	if (@thisdb != 'sybsystemprocs')
	begin
		raiserror 19232, @whoami, 'sybsystemprocs', @thisdb
		return 1
	end

	/*
	if (@trace = 1)
	begin
		print "%1!: Cache data for '%2!'", @whoami , @dbname
	end
	*/

	-- We should only be calling this sproc once in the entire execution.
	-- If we find rows for this db, someone has already cached the data.
	-- This implies a logic error in the sprocs. Fail this operation.
	--
	select @numrows = count(*) from #systhresholds where dbname = @dbname
	if (@numrows != 0)
	begin
		-- Re-set input arg as we are erroring out anyway.
		--
		select @callerID = inserted_by
		from #systhresholds
		where dbname = @dbname

		raiserror 19233
			, @whoami , '#systhresholds', @numrows
			, @dbname, @callerID
		return 1
	end

	select @sqlstr =  "insert #systhresholds(dbname, inserted_by"
					+ ", segment, free_space, status"
					+ ", proc_name, suid, currauth"
					+ ")"
			+ " select"
				+ " '" + @dbname + "'"
				+  ",'" + @callerID + "'"
				+ ", segment, free_space, status"
				+ ", proc_name, suid, currauth"
				+ " from "
				+ @dbname + ".dbo.systhresholds"

	exec @retval = sp_exec_SQL @sqlstr, @whoami
	return @retval
end
go

if (@@error != 0) select syb_quit()
go

drop table #systhresholds
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_ins_all_seginfo
go

/*
** ***************************************************************************
** sp_dbxt_ins_all_seginfo
**
** Simple procedure to cache the contents of syssegments and systhresholds 
** into #temp tables for all databases of interest that have some rules
** available for them in master.dbo.sysattributes.
**
** This sproc is also used to cache info from these catalogs as part of the
** 'set' operation. In that case, if we were setting the rules for a db the
** very first time, no data will be available in sysattributes. Hence, the
** caller passes in the name of the db they are setting for, so that we can
** also consider that database and cache its contents.
**
** Similarly, if you list threshold for a db/segment and no rules were set
** before for this db in sysattributes, we have to explicitly ask to cache
** the catalog info for this @dbname, via the @setdbname argument.
**
** Parameters:
**	@dbname		- DB name of interest, could be a LIKE pattern.
**	@callerID	- Name of sproc that is calling us.
**	@trace		- Tracing level
**	@setdbname	- DB name for which the 'set' command is being used.
**
** Returns:
**	0	- If all the insert(s) succeeded
**	1	- Otherwise.
{
*/
create procedure sp_dbxt_ins_all_seginfo(
			  @dbname	varchar(256)
			, @callerID	varchar(30)
			, @trace	int		= 0
			, @setdbname	varchar(256) = NULL
)as
begin
	declare	@lcl_dbname	varchar(256)
	      , @class		int
	      , @retval		int
	      , @whoami_cp	varchar(30)
	      , @procname	varchar(256)
	      , @dbexists	int
	      , @sybprocs	varchar(14)

	/*
	if (@trace = 1)
	begin
		print "sp_dbxt_ins_all_seginfo: Cache data for '%1!'",
			@dbname
	end
	*/

	select    @class = 19		-- AUTODBXT_CLASS
		, @retval = 0
		, @sybprocs = 'sybsystemprocs'
		, @lcl_dbname = db_name()

	select    @whoami_cp = object_name(@@procid, db_id(@sybprocs))

	if (@lcl_dbname != @sybprocs)
	begin
		raiserror 19232, @whoami_cp, @sybprocs, @lcl_dbname
		return 1
	end

	-- We only want to cache the segment info for a db once.
	-- Otherwise, duplicate rows appear in the #syssegments table,
	-- leading to subqueries on that table returning multiple rows.
	--
	declare curdbname cursor for
	select distinct object_cinfo
	from master.dbo.sysattributes
	where class = @class
	  and object_type = 'DB'
	  and (@dbname IS NULL or object_cinfo LIKE @dbname)

	  -- Eliminate 'default' rules as there is no such db, and there
	  -- is no need to gather any segment info for such dbs.
	  --
	  and object_cinfo != 'default'

	  -- Filter out rows describing the directory entries as they just
	  -- contain tags for the object_cinfo, which is not a real dbname.
	  --
	  and attribute != 0

	UNION

	-- Add in the db for the 'set' command (per description above).

	select @setdbname where @setdbname IS NOT NULL

	open curdbname

	fetch curdbname into @lcl_dbname

	-- Process all dbs that have rules for them, caching the contents
	-- of each db's syssegments in the #syssegments temp table.
	--
	while ((@retval = 0) and (@@sqlstatus = 0))
	begin
		-- Check to see if the db exists first before trying to 
		-- get its segment info.
		--
		exec @dbexists = sp_dbxt_validate_db
					  @lcl_dbname
					, 'sp_dbxt_ins_all_seginfo'
		if (@dbexists != 0)
		begin
			-- Cache data into #syssegments for this @dbname.
			--
			exec @retval = sybsystemprocs.dbo.sp_dbxt_ins_db_seginfo
						  @lcl_dbname
						, @callerID
						, @trace
			if (@retval != 0)
				break

			-- Cache data into #systhresholds for this @dbname.
			--
			exec @retval = sybsystemprocs.dbo.sp_dbxt_ins_db_thresholds
						  @lcl_dbname
						, @callerID
						, @trace
			if (@retval != 0)
				break

		end
	
		fetch curdbname into @lcl_dbname
	end

	close curdbname

	deallocate cursor curdbname

	/*
	if (@trace = 1)
	begin
		exec sp_dbxt_list_syssegments
		exec sp_dbxt_list_systhresholds
	end
	*/
	return @retval
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_segment_to_segbit
go

/*
** ***************************************************************************
** sp_dbxt_segment_to_segbit
**
** Convert a segment ID to a bit position. Copied from sp_helpsegment.
{
*/
create procedure sp_dbxt_segment_to_segbit(
				@segment	int
) as
begin
	declare @segbit	int

	-- Convert segment ID to a bit position, accounting for
	-- potential overflow for last segment.
	--
	if (@segment < 31)
		select @segbit = power(2, @segment)
	else
	begin
		select @segbit = low
		from master.dbo.spt_values
		where type = "E"
		  and number = 2
	end
	return @segbit
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_create_control_table
go

/*
** ***************************************************************************
** sp_dbxt_create_control_table
**
** Returns:
**	0 	- If control table was created and initialized ok.
**	1	- Otherwise.
{
*/
create procedure sp_dbxt_create_control_table(
			  @recreate	int	= 1
) as
begin
	declare	@create_str	varchar(11)
	      , @dbname		varchar(30)
	      , @retval		int
	      , @sqlstr		varchar(256)

	select    @create_str = "Creating"
		, @dbname = db_name()

	-- If the table does exist in this db, then drop and recreate it.
	-- Also do this for tempdb. In case user re-runs the install
	-- script multiple times in an active server that has auto
	-- expansion going on, it will be difficult to re-install these
	-- sprocs, as one or more be already in use. So the expectation
	-- is that users will do a clean re-install.
	--
	if (object_id('syb_auto_db_extend_control') is NOT NULL)
	begin
		select @create_str = "Re-creating"
		drop table syb_auto_db_extend_control
	end

	-- If asked to only drop, then drop and bail out.
	if (@recreate = 0)
	begin
		print "Drop table %1!.dbo.syb_auto_db_extend_control"
			, @dbname
		return 0
	end

	create table syb_auto_db_extend_control(
			  dbname		varchar(30)
			, segmentname		varchar(30)
			, devicename		varchar(30) NULL
			, spid			int
			-- , kpid		int
			, token			numeric(38,0) identity

			-- at which threshold was initially fired.
			, freespace		int NOT NULL

			-- current free space in this db/segment pair.
			, curr_freespace	int NOT NULL
	) lock datarows
	if (@@error != 0)
		return 1

	print "%1! control table in %2! database."
		, @create_str, @dbname

	return 0
end
go	-- }

-- Create the control table once at the start of the install process,
-- so that all sub-procs that need it find it in tempdb, and find the
-- latest version.
--
exec tempdb.dbo.sp_dbxt_create_control_table
go

go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:05 2003 
*/
/*
** raiserror Messages for dbxt_parse [Total 2]
**
** 18476, "'%1!' is not a valid value for '%2!'."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_parse [Total 3]
**
** 19190, "freespace"
** 19191, "growby"
** 19192, "maxsize"
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_parse ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

exec sp_dbxt_recreate_proc sp_dbxt_parse_growby
go

/*
** sp_dbxt_parse_growby
**
** Given a string argument, parse it for the growby specifier.
**
** Returns:
**  . If @growby was specified as a %age, @growby_pct contains the value
**    after stripping out the '%' sign. @growby_m returned as -1.
**  . If @growby was specified as a value, @growby_m returned for the growth
**    rate in Mb. @growby_pct returned as -1 to indicate that value specifier
**    was being used.
{
*/
create procedure sp_dbxt_parse_growby (
		        @growby		varchar(30)
		      , @growby_m	float	output -- @growby in megabytes
		      , @growby_pct	float 	output -- %age value
) as
begin
	declare @pct_loc	int		-- '%' char locator
	      , @pct_str	varchar(10)	-- %age value as string
	      , @size_k		int		-- growby in Kb
	      , @msg		varchar(10)	-- 'growby' string
	      , @getsize_rv	int

	if (@growby is NULL)
		return 0

	-- Validate @growby specifier, and store either in mb or %age.
	-- If there is a '%' char anywhere, we will try to parse it as a
	-- %age specifier.
	--
	select @pct_loc = patindex("%[%]%", @growby)

	-- It is not in 'nnn%' form. Parse it like a size specifier.
	--
	if (@pct_loc = 0)
	begin
		exec @getsize_rv = sp_aux_getsize @growby, @size_k output
		if (@getsize_rv = 0)
		begin
			exec sp_getmessage 19191, @msg output
			raiserror 18476, @growby, @msg
			return 1
		end

		-- Return growby increment as Mb.
		--
		select @growby_m = (convert(float, @size_k) / 1024)

		-- Value is not input as a %age. Return the pct as -1.
		select @growby_pct = -1
	end
	else
	begin
		-- DBXT_FUTURE: The parsing for %age values is weak.
		-- We only want to see [0-9]*[.][0-9]*% regular expressions.
		-- If we find any other char other than these, raise an
		-- error, and fail. Can't find an easy and fast way to
		-- implement this using T-SQL built-ins.
		--
		select	  @pct_str = substring(@growby, 1, (@pct_loc - 1))

		-- convert() might fail if the input is bogus, e.g.: '3xxf%'
		-- But if it does fail, conversion routines will bail all
		-- the way out, and procedure execution is aborted.
		-- No need to specially check for @@error.
		--
		select	  @growby_pct = convert(float, @pct_str)
			, @growby_m = -1

		-- We just don't allow shrinking databases. So fail a
		-- negative %age growth rate.
		--
		if (@growby_pct < 0)
		begin
			exec sp_getmessage 19191, @msg output
			raiserror 18476, @growby, @msg
			return 1
		end
	end

	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_parse_maxsize
go

/*
** sp_dbxt_parse_maxsize
**
** Procedure to parse a string value, and validate if it is a valid
** max-size specifier. Parse out any unit-specifiers, and return the
** maxsize in Mb units via @maxsize_m. If the @maxsize value has any
** '%' symbols, it is flagged as an invalid value. This small test defends
** against an accidentally use of '5%' for @maxsize, when it was actually
** meant for @growby argument in the 'set' command's interface.
**
** Parameters:
**	@maxsize	- User provided maxsize value.
**	@maxsize_m	- Converted maxsize in Mb units.
**
** Returns:
**	0	- All is OK.
**	1	- Some error(s) were found in the input string.
{
*/
create procedure sp_dbxt_parse_maxsize (
				@maxsize	varchar(30)	= NULL
			      , @maxsize_m	float output
) as
begin
	declare	@size_k		int
	      , @getsize_rv	int
	      , @pct_loc	int
	      , @msg		varchar(10)

	if (@maxsize is NULL)
		return 0

	select @pct_loc = patindex("%[%]", @maxsize)
	if (@pct_loc != 0)
	begin
		exec sp_getmessage 19192, @msg output
		raiserror 18476, @maxsize, @msg
		return 1
	end

	-- Convert the @maxsize to standard denominations.
	-- First convert it to 'k', then conver to 'm', as the rest of the
	-- sproc uses sizes in megabytes.
	--
	exec @getsize_rv = sp_aux_getsize @maxsize, @size_k output

	if (@getsize_rv = 0)
	begin
		exec sp_getmessage 19192, @msg output
		raiserror 18476, @maxsize, @msg
		return 1
	end
	else
		select @maxsize_m = (convert(float, @size_k) / 1024.0)

	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_parse_itemtype
go

/*
** sp_dbxt_parse_itemtype
**
** Generic procedure to validate that a user-argument is a valid argument
** for the current 'itemclass' of arguments to consider. Then, see if the
** argument ('itemtype') is a valid, unique prefix. If these rules are
** satisfied, retrieve the argument's number from spt_vaules, and return
** an ordinal number representing that argument, starting from 1..n.
**
** Parameters:
**	@itemclass	- String giving class name of item.
**			  E.g. 'command', 'objects' etc.
**	@itemtype	- User-speciifed argument for one item in the list
**			  of items in this class.
**			  E.g. For 'command', this could be 'set' etc.
**			  E.g. For 'objects', this could be 'threshold' etc.
**	@msgnum		- Error message that should be raised if the
**			  'itemtype' was not found in 'itemclass' 's list
** 			  of values.
**
** Returns:
**	-1	- Error. (raiserror is done in some cases.)
**	0	- Argument 'itemtype' was NULL. (Some callers want to know
**		  the difference, so that they can allow NULL args.)
**	1..n	- Ordinal number of arg from spt_values.
**		- @itemtype is returned as the unique string for the item.
{
*/
create procedure sp_dbxt_parse_itemtype(
			  @itemclass	varchar(30)
			, @itemtype	varchar(30)	output
			, @msgnum	int
) as
begin
	declare	@retval		int
	      , @direntry	int
	      , @cmd		varchar(30)

	-- If user passed-in a NULL entry, skip any further checking.
	-- Caller will have to decide if NULL arg is ok, or not.
	--
	if (@itemtype is NULL)
		return 0

	-- Fetch the directory entry for the current class.
	-- (This should generally never happen. If this happens, it means
	--  the caller has used an invalid major 'class' name. That would
	--  be an internal error.)
	--
	exec @direntry = sp_dbxt_get_direntry @itemclass
	if (@direntry = -1)
		return @direntry

	-- Try to do name matching, look for unique prefixes etc.
	-- Returned value will be an ordinal # mapping to input argument.
	--
	exec @retval = sp_dbxt_name_to_ordnum 'XT', @direntry, @itemclass,
						@itemtype output
	if (@retval <= 0)
	begin
		declare @typenames	varchar(256)

		exec sp_dbxt_build_cmd_list 'XT', @direntry, @typenames output

		raiserror @msgnum, @itemtype, @typenames
		return -1
	end

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

/*
** sp_dbxt_parse_freespace
**
** Parse the string representing the 'free space' argument to be used for
** sp_addthreshold/sp_dropthreshold. Convert that string which might contain
** space unit-specifiers, and return # of logical pages to the caller. The
** threshold management sprocs deal with 'free space' in # of logical pages.
**
** Parameters:
**	@freespace	- String representing free space.
**
** Output parameters:
**	@nlpages 	- # of logical pages that 'free space' translates to.
**
** Returns:
**	0	- No errors.
**	1	- Some error occured during parsing.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_parse_freespace
go

create procedure sp_dbxt_parse_freespace (
			  @freespace	varchar(30)
			, @nlpages	int	output
) as
begin
	declare	@size_k	int	-- freespace in 'kb' units.
	      , @retval	int
	      , @msg	varchar(10)

	-- Convert the space_left to standard denominations.
	exec @retval = sp_aux_getsize @freespace, @size_k output

	if (@retval = 0)
	begin
		exec sp_getmessage 19190, @msg output
		raiserror 18476, @freespace, @msg
		return 1
	end

	select @nlpages = @size_k * (convert(float, 1024) / @@maxpagesize)
	return 0
end
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:05 2003 
*/
/*
** raiserror Messages for dbxt_permissions [Total 1]
**
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_permissions [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_permissions ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

exec sp_dbxt_recreate_proc sp_dbxt_is_dbo
go

/*
** **************************************************************************
** sp_dbxt_is_dbo()
{
*/
create procedure sp_dbxt_is_dbo(
			  @currdbname	varchar(256)
) as
begin
	declare	@dbo	int
	      -- , @suserid	int
	      -- , @userid		int

	-- Initialize as non-DBO to handle all exit conditions.
	select @dbo = 0

	-- select @suserid = suser_id(), @userid = user_id()
	-- print "%1! %2!", @suserid, @userid

	-- Check for caller's error. We should always call this sproc
	-- using @currdbname.dbo.<sprocname> so that we are looking up the
	-- correct sysusers. In case of an error in caller's calling
	-- convention, flag that by returning that user is not DBO in db.
	--
	if (@currdbname != db_name())
	begin
		select @dbo = 0
	end

	-- Is this login the dbo of the current db?
	-- If a dbo has 'setuser' to some other user, we do not recognize
	-- him/her as DBO any longer. This is a minor restriction that we
	-- only allow a real DBO to perform any command, and not under setuser.
	--
	else if exists (select 1 from sysusers
			where uid = 1
			  and suid = suser_id())
		and (user_id() = 1)
	begin
		select @dbo = 1
	end
	else
	begin
		-- Is this login the 'dbo' alias of the db, but w/o sa_role?
		--
		if exists (select 1 from sysalternates
			   where suid = suser_id()
			     and altsuid = (select suid from sysusers
			     		    where uid = 1) )
		begin
			select @dbo = 1
		end
	end

	return @dbo
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_check_dbo
go

/*
** **************************************************************************
** sp_dbxt_check_dbo()
{
*/
create procedure sp_dbxt_check_dbo(
			  @dbname	varchar(256)
			, @dbo		int output
) as
begin
	declare @sqlstr		varchar(100)
	      , @retval		int
	      , @whoami		varchar(32)
	      , @procname	varchar(100)

	select   @whoami = object_name(@@procid, db_id('sybsystemprocs'))
				+ ": "
	select @procname = @dbname + ".dbo.sp_dbxt_is_dbo"
	exec @dbo = @procname @dbname, @dbo output

	-- If user is not even a valid user in the @dbname db, then we
	-- will fail even before executing the sproc. Trap that condition
	-- here and return @dbo as 0. (User will see the system error from
	-- sproc execution and then, maybe, also a sproc-specicic error. That
	-- is ok, as we still get our job done.)
	--
	if (@@error != 0)
		select @dbo = 0

	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_sa_dbo
go

/*
** **************************************************************************
** sp_dbxt_sa_dbo
**
** Common procedure to determine if user running the sproc has 'sa_role'
** or is dbo of the db from which (or on which) the command is operating.
** Return these status back to the caller as booleans.
**
** Code modularized from sp_addthreshold logic, which is mostly what we
** want to implement also.
**
** Parameters:
**	@currdbname	- DBname from where the command is being executed.
**
** Output Parameters:
**	@sa_role	- Whether user has sa_role.
**			  1: sa_role granted directly
**			  2: sa_role granted indirectly via another user-	**			     defined role that has sa_role granted to it.
**
**	@dbo		- Whether user is dbo of @dbname
{
*/
create procedure sp_dbxt_sa_dbo (
			  @currdbname	varchar(256)
			, @sa_role	int	output
			, @dbo		int	output
) as
begin
	declare @lcl_sarole	int
	      , @whoami		varchar(32)

	select	  @sa_role = 0
		, @dbo = 0
	  	, @whoami = object_name(@@procid, db_id('sybsystemprocs'))
				+ ": "

	-- If caller passes NULL for currdbname, it means that caller is
	-- not interested in whether user is dbo in any db. We are only
	-- looking for sa_role privilege. Just skip the dbo check.
	--
	if (@currdbname IS NOT NULL)
	begin
		exec sybsystemprocs.dbo.sp_dbxt_check_dbo
						  @currdbname
						, @dbo output
		if (@dbo = 1)
			return 0
	end

	-- Check to see if the user has sa_role granted directly to him.
	--
	-- if (charindex("sa_role", show_role()) > 0)
	begin
		select @lcl_sarole = proc_role("sa_role")

		-- Permit those with direct grants as 'sa_role', or
		-- indirectly granted as 'sa_role'.
		--
		-- This is done in a way similar to what is being done
		-- for addthreshold, due to open CR 131764. As we already
		-- have this restriction that thresholds can only be added
		-- by login who was directly granted 'sa_role', we continue
		-- that model when users run 'set' 'threshold'. For all
		-- operations, we allow indirectly granted sa_role to be
		-- equivalent to directly granted sa_role.
		--
		select @sa_role = case @lcl_sarole
					when 1	then 1
					when 2 	then 2
					else 0
				  end
	end

	return 0
end
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:06 2003 
*/
/*
** raiserror Messages for dbxt_trace [Total 3]
**
** 18524, "%1!: Permission denied. This operation requires System Administrator (sa_role) role."
** 19172, "Usage: sp_dbextend 'trace', {'on' | 'off' }"
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_trace [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_trace ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

exec sp_dbxt_recreate_proc sp_dbxt_trace
go

/*
** Simple sproc interface to turn tracing ON/OFF on behalf of the user.
** This reduces the need to specially document the use of a particular
** traceflag (which might change over the years) to allow the 'sa' to trace
** the execution of the threshold sprocs.
**
** DBXT_FUTURE: Tracing level should move to sysattributes as well.
*/
create procedure sp_dbxt_trace (
			@cmd	varchar(10)
) as
begin
	declare @retval		int
	      , @sa_role	int
	      , @dbo		int
	      , @direntry	int

	exec @direntry = sp_dbxt_get_direntry 'tracelevel'
	exec @retval = sp_dbxt_parse_itemtype 'trace', @cmd , 19194

	-- NULL arg, or wrong argument is an error.
	if ((@retval = 0) or (@retval = -1))
	begin
		raiserror 19172
		return 1
	end

	/*
	** Only allow 'sa_role' to turn ON/OFF tracing server-wide.
	** This could be extended in the future to allow DBO to turn on/off
	** tracing for their particular DB.
	*/
	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  NULL
					, @sa_role output
					, @dbo output
	if (@sa_role = 0)
	begin
		raiserror 18524, "sp_dbextend 'trace'"
		return 1
	end

	-- Change the tracing level in the control table.
	update master.dbo.spt_values
	set low = (case @cmd when 'on'  then 1	-- trace ON
			     when 'off' then 0	-- trace OFF
			     else  0		-- defensive
		   end)
	where type = 'XT'
	  and number = @direntry
		
	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_get_tracelevel
go

create procedure sp_dbxt_get_tracelevel
as
begin
	declare @tracelevel	int
	      , @direntry	int

	exec @direntry = sp_dbxt_get_direntry 'tracelevel'
	select @tracelevel = low
	from master.dbo.spt_values
	where type = 'XT'
	  and number = @direntry

	return @tracelevel
end
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Wed Aug 13 17:45:24 2003 
*/
/*
** raiserror Messages for dbxt_get [Total 2]
**
** 19205, "%1!Internal Error. %2!."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_get [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_get ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

exec sp_dbxt_recreate_proc sp_dbxt_exists_validate_args
go

/*
** ***************************************************************************
** Common procedures that perform the 'get' operation from the rows in
** master.dbo.sysattributes. These procedures know how the 'set' operation
** has created these rows, and decode them to return back to the caller
** data like 'how much to grow a database by', 'how much to grow a device
** by' etc.
**
** These procedures also provide ways for other command(s) like 'set',
** 'modify' to test if a row already exists for a given combination and
** either error out, or do further processing.
{
*/
create procedure sp_dbxt_exists_validate_args (
			  @callerID	varchar(30)
			, @dbname	varchar(256)
			, @segid	int
			, @db_dev_code	char(2)
) as
begin
	declare @error_msg	varchar(80)

	select @error_msg = NULL

	if (@db_dev_code = 'DB')
	begin
		if (    (@dbname = 'default') and (@segid is NOT NULL)
		     or (@dbname != 'default') and (@segid is NULL)
		)
		begin
			select @error_msg =
				case @dbname
				  when 'default' then
					"@dbname is 'default' but @segid is NOT NULL"
				  else
					"@dbname is NOT 'default' but @segid is NULL"
				end
		end
	end
	else if (@db_dev_code = 'DV')
	begin
		if (@segid is NOT NULL)
		begin
			select @error_msg = "For devices, @segid is NOT NULL"
		end
	end

	if (@error_msg is NOT NULL)
	begin
		-- Internal error. Report above string, and caller's fn name.
		raiserror 19205, @callerID, @error_msg
		return 1
	end
	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_get_enabled
go

/*
** ***************************************************************************
** sp_dbxt_get_enabled
**
** Top-level check to see whether the entire auto-extension feature is
** enabled/disabled server-wide. Then, see if it is enabled/disabled for
** the db/segment of interest.
**
** DBXT_FUTURE: We should extend this (no pun intended) to get enabled status
** for an individual db/segment. Then, at the top-level we can query that status
** and exit if expansion is disabled, and print a nice message.
**
** Parameters:
**
** Returns:
**	1	- Auto expansion is enabled server-wide.
**	0	- Auto expansion is disabled server-wide.
**
{
*/
create procedure sp_dbxt_get_enabled
as
begin
	declare @retval	int

	-- First find out if the feature is enabled server-wide.
	--
	select @retval = a.object_info2
	from master.dbo.sysattributes a
	where a.class = 19
	  and a.object_type = 'XT'
	  and a.object_info1 IN (1)
	  and a.object_cinfo = 'server-wide'
	  and a.attribute in (select a2.object_info1
	  		      from master.dbo.sysattributes a2
			      where a2.class = 19
			        and a2.attribute = 0	-- DBXT_DIRECTORY
			      	and a2.object_type = 'XT')

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_exists_growby
go

/*
** ***************************************************************************
** sp_dbxt_exists_growby
**
** Given a (@dbname, @segmentname), find out if there exists a row
** for its growby property, and return that information back, as given
** below.
**
** The @dbname doubles as a way for this procedure to determine whether
** the user is interested in 'user-specified' property, or the
** 'system-default' property. If @dbname is 'default', we look for the
** 'system-default' property; otherwise for 'user-specified' property.
**
** Parameters:
**	@dbname		- Db name of interest.
**	@segid		- Segment ID of interest.
**	@db_dev_type	- Code for whether to look for database ('DB')
**			  or for a device ('DV').
**	@trace		- Trace level for debugging
**
** Output Parameters:
**	@growby_str	- Growby string, if row was found.
**	@out_segid	- Segment ID for this string. (This is the same as
**			  for @segid, but is useful for the 'default'
**			  db. In that case, this is the min size that the
**			  growby is allowed in Mb.)
**
** Returns:
**	1 : Row was found. Output parameters are valid.
**	0 : Row was not found. Output parameters are not valid.
{
*/
create procedure sp_dbxt_exists_growby (
			  @dbname	varchar(256)
			, @segid	int
			, @db_dev_code	char(2)
			, @growby_str	varchar(30)	output
			, @out_segid	int		output
			, @trace	int	= 0
) as
begin
	declare @class		smallint
	      , @enabled	int
	      , @growby_code	int
	      , @growbypct_code	int

	      , @user_sys_attr_id
	      			int	-- attribute ID

	      , @whoami		varchar(32)
	      , @retval		int

	select @class	= 19		-- AUTODBXT_CLASS

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
		, @growby_code		= 3
		, @growbypct_code	= 4

		-- Init to NULLs, so as to get dependable behaviour from
		-- this sproc in all cases.
		--
		, @growby_str	= NULL
		, @out_segid	= NULL

		-- Retrieve the attribute for the class requested based
		-- on the @dbname, and whether it is a db/dev we are
		-- looking for. This will return either one of:
		--
		-- 	DBXT_DEFAULT_DB  , DBXT_USER_SPEC_DB for databases.
		--	DBXT_DEFAULT_DEV , DBXT_USER_SPEC_DEV for devices.
		--
		, @user_sys_attr_id
				= (select a.object_info1
				   from master.dbo.sysattributes a
				   where a.class = @class

				     and a.attribute = 0 -- DBXT_DIRECTORY
				     and a.object_type = @db_dev_code
				     and a.object_cinfo
				     		= case @dbname
							when 'default'
							then 'system-default'
							else 'user-specified'
						  end
				  )

	/*
	exec @retval = sp_dbxt_exists_validate_args @whoami
					, @dbname, @segid
					, @db_dev_code
	if (@retval = 1)
		return @retval
	*/

	/*
	if (@trace = 1)
	begin
		print "%1!class: '%2!' user_sys_attr_id: '%3!' db_dev_code: '%4!' dbname: '%5!', segid: '%6!', growby_code: '%7!', growbypct_code: '%8!'",
			@whoami, @class, @user_sys_attr_id, @db_dev_code,
			@dbname, @segid, @growby_code, @growbypct_code
	end
	*/

	-- Find a row matching for the @dbname/@segid pair. Account for
	-- the fact that for system-default, caller will pass-in NULL
	-- for @segid. So, if it is NULL, don't use that for a match.
	--
	select    @growby_str 	= char_value
		, @out_segid 	= object
		, @enabled	= object_info2
	from master.dbo.sysattributes
	where class = @class
	  and attribute = @user_sys_attr_id
	  and object_type = @db_dev_code
	  and object_cinfo = @dbname

	  -- Do not use this construct. This is causing the SELECT to fail,
	  -- although it appears to be a valid construct.
	  --
	  /* **** and isnull(object, 0) = isnull(@segid, 0) */
	  
	  and (@segid IS NULL or object = @segid)

	  and object_info1 in (@growby_code, @growbypct_code)

	-- growby policy not found for this db/segment.
	if (@growby_str is NULL)
		return 0

	-- growby policy found, but it has been disabled. Not an error.
	else if (@enabled = 0)
	begin
		-- Even though a policy exists, it has been disabled. So
		-- pretend that we did not find it by sending a NULL back,
		-- and return 1. Return 1 will appear as if the policy
		-- exists, so we won't try to find the default policy.
		--
		select @growby_str = NULL
		if (@trace = 1)
		begin
			print "%1!growby for %2! is currently disabled."
				, @whoami, @dbname
		end
		return 1
	end

	-- We found a currently enabled growby policy.
	else
		return 1
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_exists_maxsize
go

/*
** ***************************************************************************
** sp_dbxt_exists_maxsize
**
** Given a (@dbname, @segmentname), find out if there exists a row
** for its maxsize property, and return that information back, as given
** below.
**
** The @dbname doubles as a way for this procedure to determine whether
** the user is interested in 'user-specified' property, or the
** 'system-default' property. If @dbname is 'default', we look for the
** 'system-default' property; otherwise for 'user-specified' property.
**
** Parameters:
**	@dbname		- Db name of interest.
**	@segid		- Segment ID of interest.
**	@db_dev_type	- Code for whether to look for database ('DB')
**			  or for a device ('DV').
**	@trace		- Trace level for debugging	
**
** Output Parameters:
**	@maxsize_str	- Maxsize string, if row was found.
**
** Returns:
**	1 : Row was found. Output parameters are valid.
**	0 : Row was not found. Output parameters are not valid.
{
*/
create procedure sp_dbxt_exists_maxsize (
			  @dbname	varchar(256)
			, @segid	int
			, @db_dev_code	char(2)
			, @maxsize_str	varchar(30)	output
			, @trace	int	= 0
) as
begin
	declare @class		smallint
	      , @maxsize_code	int

	      , @user_sys_attr_id
	      			int	-- attribute ID

	      , @whoami		varchar(32)
	      , @retval		int

	select @class	= 19		-- AUTODBXT_CLASS

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
		, @maxsize_code		= 5

		-- Init to NULLs, so as to get dependable behaviour from
		-- this sproc in all cases.
		--
		, @maxsize_str	= NULL

		-- Retrieve the attribute for the class requested based
		-- on the @dbname, and whether it is a db/dev we are
		-- looking for. This will return either one of:
		--
		-- 	DBXT_DEFAULT_DB  , DBXT_USER_SPEC_DB for databases.
		--	DBXT_DEFAULT_DEV , DBXT_USER_SPEC_DEV for devices.
		--
		--
		, @user_sys_attr_id
				= (select a.object_info1
				   from master.dbo.sysattributes a
				   where a.class = @class

				     and a.attribute = 0 -- DBXT_DIRECTORY
				     and a.object_type = @db_dev_code
				     and a.object_cinfo
				     		= case @dbname
							when 'default'
							then 'system-default'
							else 'user-specified'
						  end
				  )

	/*
	DBXT_RESOLVE: Why is this call failing w/an internal error?
	exec @retval = sp_dbxt_exists_validate_args @whoami
					, @dbname, @segid
					, @db_dev_code
	if (@retval = 1)
		return @retval
	*/

	-- Find a row matching for the @dbname/@segid pair. Account for
	-- the fact that for system-default, caller will pass-in NULL
	-- for @segid. So, if it is NULL, don't use that for a match.
	--
	select @maxsize_str = char_value
	from master.dbo.sysattributes
	where class = @class
	  and attribute = @user_sys_attr_id
	  and object_type = @db_dev_code
	  and object_cinfo = @dbname

	  -- Do not use this construct. This is causing the SELECT to fail,
	  -- although it appears to be a valid construct.
	  -- and isnull(object, 0) = isnull(@segid, 0)
	  --
	  and (@segid IS NULL or object = @segid)

	  and object_info1 in (@maxsize_code)

	return (case when @maxsize_str is NULL then 0 else 1 end)
end
go	-- }

if (@@error != 0) select syb_quit()
go
exec sp_dbxt_recreate_proc sp_dbxt_get_db_growby
go

/*
** ***************************************************************************
** sp_dbxt_get_db_growby
**
** Get the size by which a given (db, segment) can grow. This is defined as
** follows:
**
**   . IF the (db, segment) has a site-specific policy, identify that and
**     extract out the 'growby' value; either a value or a %age.
**
**   	. If the growby is a %age, apply that to the passed-in current size of
**	  the segment. This gives the amount we could 'growby'.
**
**   . IF there are no site-specific policies, identify the row in
**     sysattributes that describes our default 'growby' size based on the
**     current size of the segment.
**
**   . If the (db, segment) has a 'maxsize' specifier, identify that.
**     Add the growby value to the current size, and see if it would exceed
**     the 'maxsize' value.
**
**	. Return the value that we can 'growby' so that we don't exceed the
**	  'maxsize' specified for this (db, segment).
**
** Parameters:
**	@dbname		- Name of the database where threshold fired.
**	@segid		- Segment ID where threshold fired.
**	@totalsize_MB	- Of the segment, in Mb.
**
** Output Parameters:
**	@growby_MB_rv	- Size, in MB, by which the db should be grown by.
**
** Returns:
**	0	- If a valid (+ve) growby value was found.
**	1	- Invalid growby value found. (growby returned as 0.)
{
*/
create procedure sp_dbxt_get_db_growby(
				@dbname		varchar(256)
			      , @segid		int
			      , @totalsize_MB	float
			      , @growby_MB_rv	float	output
			      , @trace		int = 0
)
as
begin
	declare @retval 	int
	      , @out_segid	int
	      , @min_db_alter	int	-- Min size to alter db by.
	      , @growby_str	varchar(30)
	      , @maxsize_str	varchar(30)
	      , @growby_m	float	-- @growby in megabytes
	      , @growby_pct	float 	-- %age value for @growby arg
	      , @maxsize_m	float	-- maxsize value in Mb.
	      , @whoami		varchar(32)
	      , @use_defaults	tinyint

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end

		, @min_db_alter 	= 0
		, @use_defaults 	= 0

		-- Initialize to 0 to handle any exits due to errors.
		, @growby_MB_rv = 0

	-- Is auto-expansion enabled server-wide?
	--
	exec @retval = sp_dbxt_get_enabled
	if (@retval = 0)
	begin
		return 0
	end

	-- See if a growby defn row exists, and return its value(s).
	exec @retval = sp_dbxt_exists_growby @dbname, @segid, 'DB'
					, @growby_str output
					, @out_segid output
					, @trace

	-- Scan sysattributes to find the 'default' growby value for all dbs.
	--
	if (@retval = 0)
	begin
		exec @retval = sp_dbxt_exists_growby 'default', NULL, 'DB'
						, @growby_str output
						, @min_db_alter output
						, @trace

		-- Note that we used the 'default' rule from sysattributes.
		select @use_defaults = 1

		if (@trace = 1)
			print "%1!default growby found in sysattributes: '%2!'",
				@whoami, @growby_str
	end
	else
	begin
		if (@trace = 1)
			print "%1!User-specified growby found in sysattributes: '%2!'",
				@whoami, @growby_str
	end

	if (@growby_str is NULL)
	begin
		return 0
	end

	-- Validate @growby specifier, and store either in mb or %age.
	--
	exec @retval = sp_dbxt_parse_growby @growby_str,
					     @growby_m output,
					     @growby_pct output
	if (@retval != 0)
	begin
		return 1	-- Some error; bail out.
	end

	-- If the growby is a %age, multiply that to the current
	-- total size to get the increment. Otherwise, the growby
	-- is a value, already converted to 'Mb'. Use that.
	--
	select @growby_MB_rv =
			case when @growby_pct = -1 then @growby_m
			     else (@totalsize_MB * @growby_pct) / 100.0
			end

	-- If there were no site-specific policies, then if the growby value
	-- so far (computed from system defaults) is less then the minimum
	-- defined by the system, bump up the growby value to the minimum
	-- value. (If the user did specify a site-specific policy, then we
	-- don't do this bump-up, as we expect the user to have defined
	-- something meanginful.)
	--
	if ((@use_defaults = 1) and (@growby_MB_rv < @min_db_alter))
		select @growby_MB_rv = @min_db_alter

	if (@growby_MB_rv <= 0)
	begin
		select @retval = (case when @growby_MB_rv = 0
				       then 0
				       else 1
				  end)
		select @growby_MB_rv = 0
		return @retval
	end

	-- Extract out any max size specified for this db/segment
	-- as specified by the user's policy. (There is currently
	-- no support for system-default max size.)
	--
	exec @retval = sp_dbxt_exists_maxsize @dbname, @segid, 'DB'
						 , @maxsize_str output
						 , @trace
	if (@maxsize_str is not NULL)
	begin
		exec @retval = sp_dbxt_parse_maxsize @maxsize_str,
						@maxsize_m output
		if (@retval != 0)
		begin
			select @growby_MB_rv = 0
			return @retval
		end

		-- Validate growby against max size specifier
		--
		if (@totalsize_MB + @growby_MB_rv > @maxsize_m)
		begin
			select @growby_MB_rv = (@maxsize_m - @totalsize_MB)
		end
	end

	-- Do a final check for negative values.
	if (@growby_MB_rv < 0)
	begin
		select @growby_MB_rv = 0
		return 1
	end
	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_get_dev_growby
go

/*
** ***************************************************************************
** sp_dbxt_get_dev_growby
**
** Get the size by which a given device can grow. This is defined as
** follows:
**
**   . IF the device has a site-specific policy, identify that and
**     extract out the 'growby' value; either a value or a %age.
**
**   	. If the growby is a %age, apply that to the passed-in current size of
**	  the device. This gives the amount we could 'growby'.
**
**   . IF there are no site-specific policies, identify the row in
**     sysattributes that describes our default 'growby' size for all
**     devices.
**
**   . If the device has a 'maxsize' specifier, identify that.
**     Add the growby value to the current size, and see if it would exceed
**     the 'maxsize' value.
**
**	. Return the value that we can 'growby' so that we don't exceed the
**	  'maxsize' specified for this device.
**
** Parameters:
**	@devicename	- Name of the candidate device to be resized.
**	@growybp	- Segment ID where threshold fired.
**	@totalsize_MB	- Of the segment, in Mb.
**
** Output Parameters:
**	@growby_MB_rv	- Size, in Mb, to grow device by.
**
** Returns:
**	0	- 'get' operation was successful.
**	1	- Some errors. @growby_MB_rv returned as 0.
{
*/
create procedure sp_dbxt_get_dev_growby (
				@devicename	varchar(256)	= NULL
			      , @totalsize_MB	float
			      , @growby_MB_rv	float		output
			      , @trace		int 		= 0
) as
begin
	declare @retval 	int
	      , @out_segid	int
	      , @growby_str	varchar(30)
	      , @maxsize_str	varchar(30)
	      , @growby_m	float	-- @growby in megabytes
	      , @growby_pct	float 	-- %age value for @growby arg
	      , @maxsize_m	float	-- maxsize value in Mb.
	      , @min_dev_resize	float	-- min device resize in Mb
	      , @use_defaults	int	-- sysattributes defaults are used.
	      , @whoami		varchar(32)

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
		, @min_dev_resize 	= 0
		, @use_defaults 	= 0

		-- Initialize to 0 to handle any exits due to errors.
		, @growby_MB_rv = 0

	if (@trace = 1)
	begin
		print "%1!Device: '%2!' Total size requested (Mb): %3!"
			, @whoami, @devicename, @totalsize_MB
	end

	-- See if a growby defn row for device-specific policy exists.
	-- If so, returns its values.
	--
	exec @retval = sp_dbxt_exists_growby @devicename, NULL, 'DV'
					, @growby_str output
					, @out_segid output
					, @trace

	-- Scan sysattributes to find the default growby value for devices.
	--
	if (@retval = 0)
	begin
		exec @retval = sp_dbxt_exists_growby 'default', NULL, 'DV'
					, @growby_str output
					, @min_dev_resize output
					, @trace

		-- Note that we used the 'default' rule from sysattributes.
		select @use_defaults = 1

		if (@trace = 1)
		  	print "%1!Using default growby for devices: '%2!'",
				@whoami, @growby_str
	end
	else if (@trace = 1)
	begin
		print "%1!User-specified growby found in sysattributes: '%2!'",
			@whoami, @growby_str
	end

	if (@growby_str is NULL)
	begin
		return 0
	end

	-- Validate @growby specifier, and store either in mb or %age.
	--
	exec @retval = sp_dbxt_parse_growby @growby_str,
					     @growby_m output,
					     @growby_pct output
	if (@retval != 0)
	begin
		return 1	-- Some error; bail out.
	end

	-- If the growby is a %age, multiply that to the current
	-- total size to get the increment. Otherwise, the growby
	-- is a value, already converted to 'Mb'. Use that.
	--
	select @growby_MB_rv =
			case when @growby_pct = -1 then @growby_m
			     else (@totalsize_MB * @growby_pct) / 100.0
			end

	-- If there were no site-specific policies, then if the growby value
	-- so far (computed from system defaults) is less then the minimum
	-- defined by the system, bump up the growby value to the minimum
	-- value. (If the user did specify a site-specific policy, then we
	-- don't do this bump-up, as we expect the user to have defined
	-- something meanginful.)
	--
	if ((@use_defaults = 1) and (@growby_MB_rv < @min_dev_resize))
		select @growby_MB_rv = @min_dev_resize

	-- Check if user has asked to not grow this device, and also check
	-- for some error conditions.
	--
	if (@growby_MB_rv <= 0)
	begin
		select @retval = (case when @growby_MB_rv = 0
				       then 0
				       else 1
				  end)
		select @growby_MB_rv = 0
		return 0
	end

	-- Extract out any max size specified for this db/segment.
	--
	exec @retval = sp_dbxt_exists_maxsize
					  @devicename , NULL , 'DV'
					, @maxsize_str output
					, @trace

	if (@maxsize_str is not NULL)
	begin
		exec @retval = sp_dbxt_parse_maxsize @maxsize_str,
						@maxsize_m output
		if (@retval != 0)
		begin
			select @growby_MB_rv = 0
			return 1
		end

		-- Validate growby against max size specifier
		--
		if (@totalsize_MB + @growby_MB_rv > @maxsize_m)
		begin
			select @growby_MB_rv = (@maxsize_m - @totalsize_MB)
		end
	end

	-- Do a final check for negative values.
	if (@growby_MB_rv < 0)
	begin
		select @growby_MB_rv = 0
		return 1
	end
	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go
go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:03 2003 
*/
/*
** raiserror Messages for dbxt_drop [Total 2]
**
** 17260, "Can't run %1! from within a transaction."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_drop [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_drop ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go


-- ***************************************************************************
	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, free_space, status, proc_name, suid, currauth
	into #systhresholds lock allpages
	from systhresholds
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_drop_db_seg
go

/*
** ***************************************************************************
** sp_dbxt_drop_db_seg
**
{
*/
create procedure sp_dbxt_drop_db_seg(
			  @dbname	varchar(256)
			, @segmentname	varchar(256)
			, @freespace	varchar(30)	= NULL
) as
begin
	declare @nlpages		int	-- freespace; # logical pages.
	      , @retval			int
	      , @freespace_nlpgs	int
	      , @segment_Name		varchar(256)
	      , @sqlstr			varchar(512)
	      , @whoami			varchar(30)

	select    @retval = 0
		, @whoami = object_name(@@procid, db_id('sybsystemprocs'))

	if (@@trancount > 0)
	begin
		raiserror 17260, @whoami
		return 1
	end
	-- IF freespace was provided, convert that to # of logical pages.
	if (@freespace is NOT NULL)
	begin
		exec @retval = sp_dbxt_parse_freespace @freespace,
						@nlpages output
		if (@retval != 0)
		begin
			return 1
		end
	end

	-- Cache the segment/thresholds info for the db that we are working on.
	--
	exec @retval = sp_dbxt_ins_all_seginfo @dbname , @whoami, 0, @dbname

	-- Iterate through all auto-expansion thresholds that might
	-- exist on this (db, segment) pair, and drop each one of
	-- them. We cannot directly declare a cursor on the join as a
	-- subsequent delete of a row from systhresholds results in an
	-- 582 error. Hence, cache the data in a #temp table, and iterate
	-- through that.
	--
	-- Just create a template table, first.
	--
	select s.name, t.free_space
	into #seg_thresholds lock allpages
	from 	  #systhresholds t
		, #syssegments s
	where t.proc_name = 'sp_dbxt_extend_db'
	  and t.segment = s.segment
	  and s.name like @segmentname
	  and 1 = 0

	-- Build the basic SQL fragment to insert into #temp table.
	-- This collects up all those threshold definitions that use
	-- the auto-expansion procedure sp_dbxt_extend_db as the
	-- threshold action procedure, and which are defined on all
	-- segments matching the passed-in segmentname.

	select @sqlstr = "insert into #seg_thresholds(name, free_space)"
			+ "select s.name, t.free_space"
			+ " from #systhresholds t, #syssegments s"
			+ " where t.proc_name = 'sp_dbxt_extend_db'"
			+ " and t.segment = s.segment"

			-- IF user gave a segmentname, search on that,
			-- otherwise, pick all segments that might exist
			-- with the auto-expansion sproc.
			--
			+ case @segmentname
				when NULL then ""
				else " and s.name like '" + @segmentname + "'"
			  end

			+ " and t.dbname = '" + @dbname + "'"
			+ " and s.dbname = '" + @dbname + "'"

	-- Add filtering clause if we have a user-input freespace value.
	--
	if (@freespace IS NOT NULL)
		select @sqlstr = @sqlstr + " and t.free_space = "
					 + convert(varchar, @nlpages)
					 
	exec @retval = sp_exec_SQL @sqlstr, @whoami
	if (@retval != 0)
		return 1

	-- select * From #seg_thresholds

	declare curth cursor for
	select name, free_space
	from #seg_thresholds

	open curth

	fetch curth into @segment_Name, @freespace_nlpgs

	while (@@sqlstatus = 0)
	begin
		-- Drop the thresholds.
		select @sqlstr = @dbname + ".dbo.sp_dropthreshold"
		exec @retval = @sqlstr    @dbname
					, @segment_Name
					, @freespace_nlpgs

		if (@retval != 0)
			break

		-- Iterate through next threshold (if any)
		fetch curth into @segment_Name, @freespace_nlpgs
	end

	close curth

	deallocate cursor curth

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_drop
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

drop table #systhresholds
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Wed Aug 13 17:45:25 2003 
*/
/*
** raiserror Messages for dbxt_clear [Total 8]
**
** 18524, "%1!: Permission denied. This operation requires System Administrator (sa_role) role."
** 19155, "Usage: sp_dbextend 'clear', { %1! } {, arguments ...}. Use: sp_dbextend 'help', 'clear' for more information."
** 19199, "You must be in database '%1!' to run this command. Issue: 'USE %2!', then run this procedure again."
** 19201, "%1!: Permission denied. This operation requires System Administrator (sa_role) role, or DBO of database '%2!'."
** 19207, "%1!: Pattern specifiers are not allowed for the argument '%2!' to this command."
** 19224, "Segment name cannot be specified when database name is '%1!'. %2!"
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_clear [Total 2]
**
** 19225, "Rules for 'default' databases apply to all segments."
** 19226, "Segment name should be NULL when clearing rules for databases that no longer exist."
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_clear ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

-- ***************************************************************************

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_clear_db_privileges
go

/*
** ***************************************************************************
** sp_dbxt_clear_db_privileges
**
** Common routine that implements privilege checking for the 'clear' command.
** This code is shared by the 'enable', 'disable' commands as they need the
** same sorts of privileges.
**
{
*/
create procedure sp_dbxt_clear_db_privileges(
				@dbname		varchar(256)	output
			      , @segmentname	varchar(256)	= NULL
			      , @currdbname	varchar(256)	= NULL
			      , @command	varchar(10)	= 'clear'
			      , @valid_db	int	output
) as
begin

	declare @retval 	int
	      , @sa_role	int
	      , @dbo		int
	      , @oper_dbo	int	-- DBO of db operating on.
	      , @whoami		varchar(30)
	      , @error_msg	varchar(100)
	      , @reason		varchar(80)
	      , @class		smallint
	      , @objtype	varchar(2)
	      , @permcheck_ok	tinyint

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))

		-- Initially assume that @dbname is non-NULL and is for a
		-- valid database. Later, we might find that it is NULL, or
		-- for a db that does not exist.
		--
		, @valid_db = 1
		, @dbo = 0
		, @sa_role = 0
		, @permcheck_ok = 0
		, @error_msg = "sp_dbextend '" + @command + "', 'database', "
				+ case @dbname
				    when NULL then "NULL"
				    else @dbname
				  end

	/*
	** *******************************************************************
	** Check Privileges on the command.
	** *******************************************************************
	*/
	if (@dbname is NULL)
	begin	-- {

		/*
		** NOTE: It might appear that we want to check for NOT NULL
		**	 @segmentname, but we don't do that so as to allow
		**	 dbo of db to issue:
		**
		**	pubs2> sp_dbextend 'clear', 'da', NULL, 'dataseg%'
		**
		**	 and clear rules for named segments in the current db.
		**
		if (@segmentname IS NOT NULL)
		begin
			raiserror 19224, 'NULL', NULL

			return 1
		end
		*/

		-- Get current privileges, using user's current db where
		-- sproc was being executed from. In order for user to run
		-- with NULL @dbname, he should either be DBO of current db,
		-- or have sa_role.
		--
		exec sybsystemprocs.dbo.sp_dbxt_sa_dbo 
						  @currdbname
						, @sa_role output
						, @dbo output

		-- If we are already the DBO in the current db, allow the
		-- user to clear all rules for all segments in this db.
		-- This is a convenience for DBO to be able to do:
		--
		--	pubs2> sp_dbextend 'clear', 'da'
		--	pubs2> sp_dbextend 'enable', 'da'
		--	pubs2> sp_dbextend 'disable', 'da'
		--
		-- without having to list each segment in this db, pubs2.
		--
		-- Command for the non-DBO login surely needs sa_role.
		--
		if ((@dbo = 0) and (@sa_role = 0))
		begin
			-- User did not give a @dbname, or even 'default'.
			-- This will clear info for current database, and needs
			-- dbo of current db or sa_role permission.
			--
			raiserror 19201, @error_msg, @currdbname
			return 1
		end

		-- Don't special case anything for master/tempdb.
		-- If sa_role runs clear from either of these dbs,
		-- @dbo will be 1, but there probably are no rules
		-- for master/tempdb. NULL @dbname should just work
		-- for the current db, always.
		--
		select	  @dbname = @currdbname
			, @permcheck_ok = 1

	end	-- } dbname is NULL

	else if (@dbname = 'default')
	begin
		-- Just find out if user is sa_role; NULL dbname.
		exec sybsystemprocs.dbo.sp_dbxt_sa_dbo 
						  NULL
						, @sa_role output
						, @dbo output
		if (@sa_role = 0)
		begin
			-- User gave a name of 'default'. This will clear
			-- info for system-specified defaults. This needs
			-- sa_role permission.
			--
			raiserror 18524, @error_msg
			return 1
		end

		-- But 'default' databases don't have any segment specified
		-- for it. So raise an error if user specifies a segment
		-- name also in this case.
		--
		else if (@segmentname IS NOT NULL)
		begin
			exec sp_getmessage 19225, @reason output
			raiserror 19224
				, 'default'
				, @reason
			return 1
		end

		select @permcheck_ok = 1
	end

	-- Allow the 'sa_role' to run with @dbname like 'pubs2%', as they
	-- don't need dbo permissions checking. Non-sa_role folks will have
	-- to issue on one db at a time.
	--
	else if (@dbname != @currdbname)
	begin	-- {

		-- We cannot support %pubs2% here as we need to know if the
		-- login is the DBO (firstly), and then we need to match on
		-- the segid stored v/s syssegments. We don't want to do too
		-- much work for non-sa_role users. So disallow pattern
		-- specifiers if sa_role is missing.
		--
		if (patindex("%[%]%", @dbname) != 0)
		begin
			-- Just find out if user is sa_role; NULL dbname.
			exec sybsystemprocs.dbo.sp_dbxt_sa_dbo 
							  NULL
							, @sa_role output
							, @dbo output
			if (@sa_role = 0)
			begin
				raiserror 19207, @error_msg, "@dbname"
				raiserror 18524, @error_msg
				return 1
			end
			select @permcheck_ok = 1
		end
		else
		begin	-- {

			-- Support a special-case when user tries to clear
			-- rules for a db that no longer exists. In this case,
			-- we want the user to have sa_role. First check if it
			-- is the case that @dbname does not exist.
			--
			exec @valid_db = sp_dbxt_validate_db @dbname, @whoami

			if (@valid_db = 0)	-- @dbname db does not exist
			begin
				-- Just find out if user is sa_role;
				exec sybsystemprocs.dbo.sp_dbxt_sa_dbo 
							  NULL
							, @sa_role output
							, @dbo output
				if (@sa_role = 0)
				begin
					-- User gave a name of db that does not
					-- exist. This needs sa_role permission.
					--
					raiserror 18524, @error_msg
					return 1
				end

				else if (@segmentname IS NOT NULL)
				begin
					-- segmentname can't be non-NULL for a
					-- dbname that does not exit.
					--
					exec sp_getmessage 19226
							, @reason output
					raiserror 19224
						, @dbname
						, @reason
					return 1
				end
				select @permcheck_ok = 1
			end

		end	-- }

	end	-- }	else if (@dbname != @currdbname)

	-- We got a valid db, but we have not yet checked for privileges
	-- for this command on that db. User should either be dbo or sa_role.
	--
	if (@permcheck_ok = 0)
	begin
		exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  @dbname
					, @sa_role output
					, @dbo output

		if ((@sa_role = 0) and (@dbo = 0))
		begin
			-- We are operating on a db, but don't have sa/dbo
			raiserror 19201, @error_msg, @dbname
			return 1
		end
	end

	/*
	** NOTE: We could have a check as follows, but we don't do this.
	** 	 Allowing @segmentname to be NULL allows a DBO to clear all
	**	 the rules for his owning DB, which is a convenience in
	**	 some cases.
	**
	else if (@segmentname is NULL)
	begin
		raiserror 19199, @dbname, @dbname
		return 1
	end
	*/

	-- All permissions checks are ok.
	return 0

end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_clear_database
go

/*
** ***************************************************************************
** sp_dbxt_clear_database
**
** Implement the permission checks for 'clear' 'database' command.
** Perform the 'clear' operation when all permission checks pass.
**
** With appropriate privileges, the following are valid:
**
** . Clear rules for one db, one segment:
**	sp_dbextend 'clear', 'da', pubs2, logsegment
**
** . Clear rules for one db, multiple segment:
**	sp_dbextend 'clear', 'da', pubs2, 'dataseg%'
**
** . Clear rules for multiple dbs, multiple segment:
**	sp_dbextend 'clear', 'da', "%pubs2", 'dataseg%'
**
** Parameters:
**	@dbname		- DB name arg for 'clear' command.
**	@segmentname	- Segment for which to clear rules.
**	@currdbname	- DB from which the sproc was executed.
**
** Returns:
**	0	- If all permission checks passed.
**	1	- Otherwise. (Some error messages might be raised.)
{
*/
create procedure sp_dbxt_clear_database(
				@dbname		varchar(256)	= NULL
			      , @segmentname	varchar(256)	= NULL
			      , @currdbname	varchar(256)	= NULL
) as
begin
	declare @retval 	int
	      , @whoami		varchar(30)
	      , @valid_db	int
	      , @class		smallint
	      , @objtype	varchar(2)

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))

		-- Initially assume that @dbname is non-NULL and is for a
		-- valid database. Later, we might find that it is NULL, or
		-- for a db that does not exist.
		--
		, @valid_db = 1

	/*
	** *******************************************************************
	** Check Privileges on the command.
	** *******************************************************************
	*/
	exec @retval = sp_dbxt_clear_db_privileges @dbname output
						 , @segmentname
						 , @currdbname
						 , 'clear'
						 , @valid_db output
	if (@retval != 0)
		return 1

	/*
	** By now, we have determined that the @dbname that this command is
	** operting on is either valid, or NULL. Appropriately, cache the
	** segment info for this db. (@dbname might be NULL, or a pattern
	** specifier.)
	*/
	if (@valid_db != 0)	-- Could be dbid of @dbname
	begin
		exec @retval = sp_dbxt_ins_all_seginfo @dbname, @whoami
		if (@retval != 0)
			return 1
	end

	/*
	** *******************************************************************
	** Perform the 'clear' command.
	** *******************************************************************
	*/
	select    @class = 19		-- AUTODBXT_CLASS
		, @objtype = 'DB'

	-- Delete all rows from master.dbo.sysattributes that match the two
	-- arguments (@dbname, @segmentname), if either was provided.
	-- Just work with a single-statement transaction to avoid a 3917
	-- error if this were done under a begin-end tran bracket, and the
	-- command was issued from 'tempdb'.
	--
	delete master.dbo.sysattributes
	where class = @class

	  -- Returns either one of DBXT_DEFAULT_DB or DBXT_USER_SPEC_DB
	  -- attribute ID from sysattributes.
	  --
	  and attribute = (select a2.object_info1
	  		   from master.dbo.sysattributes a2
			   where class = @class
			     and attribute = 0	-- DBXT_DIRECTORY
			     and object_type = @objtype
			     and object_cinfo = 
			     		case @dbname
					  when 'default'
					  then 'system-default'
					  else 'user-specified'
					end
			  )

	  and object_type = @objtype
	  and (@dbname is NULL OR object_cinfo like @dbname)
	  and (    @segmentname is NULL
	        OR object IN (select si.segment
			      from #syssegments si
			      where si.name like @segmentname
			        and si.dbname like @dbname
			     )
	      )

	return (case when (@@error != 0) then 1 else 0 end)
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_clear_device
go

/*
** ***************************************************************************
{
*/
create procedure sp_dbxt_clear_device (
				  @devicename	varchar(256)	= NULL
) as
begin
	declare @class		smallint
	      , @sa_role	int
	      , @dbo		int
	      , @objtype	varchar(2)

	select    @class   = 19		-- AUTODBXT_CLASS
		, @objtype = 'DV'

	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  NULL
					, @sa_role output
					, @dbo output
	if ((@sa_role = 0) )
	begin
		-- Need 'sa_role' to clear info for devices.
		raiserror 18524, "sp_dbextend 'clear', 'device'"
		return 1
	end

	-- Delete all rows from master.dbo.sysattributes that match the
	-- requested @devicename. Work with single-statement transaction
	-- here. We have ensured at the top-level that we are not in an
	-- outer transaction. If we try to do this under a begin-end tran
	-- bracket, and the 'clear' is issued from tempdb, then we will fail
	-- with a 3917 error. Avoid that, as a single-statement transaction
	-- is good enough.
	--
	delete master.dbo.sysattributes
	where class = @class

	  -- Returns either one of DBXT_DEFAULT_DB or DBXT_USER_SPEC_DB
	  -- attribute ID from sysattributes.
	  --
	  and attribute = (select a2.object_info1
	  		   from master.dbo.sysattributes a2
			   where class = @class
			     and attribute = 0	-- DBXT_DIRECTORY
			     and object_type = @objtype
			     and object_cinfo = case @devicename
			     			  when 'default'
						  then 'system-default'
						  else 'user-specified'
						end
			  )

	  and object_type = @objtype
	  and (@devicename is NULL OR object_cinfo like @devicename)

	return (case when (@@error != 0) then 1 else 0 end)
end
go

if (@@error != 0) select syb_quit()
go


exec sp_dbxt_recreate_proc sp_dbxt_clear_threshold
go

/*
** ***************************************************************************
{
*/
create procedure sp_dbxt_clear_threshold (
			  @dbname	varchar(256)	= NULL
			, @segmentname	varchar(256)	= NULL
			, @freespace	varchar(256)	= NULL
			, @currdbname	varchar(256)
) as
begin
	declare	@retval		int
	      , @sa_role	int
	      , @dbo		int
	      , @whoami		varchar(30)
	      , @error_msg	varchar(100)

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))

	if (@dbname is NOT NULL)
	begin
		if (@dbname != @currdbname)
		begin
			-- Need to be in the @dbname for drop threshold to work.
			raiserror 19199, @dbname, @dbname
			return 1
		end
	end
	else
	begin
		select @dbname = @currdbname
	end

	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  @dbname
					, @sa_role output
					, @dbo output

	if ((@sa_role = 0) and (@dbo = 0))
	begin
		exec sp_dbxt_gen_command_tag @whoami, @dbname,
					     @error_msg output

		-- We are in the right db, but don't have DBO/sa permissions.
		raiserror 19201, @error_msg, @dbname
		return 1
	end

	exec @retval = sybsystemprocs.dbo.sp_dbxt_drop_db_seg
					  @dbname
					, @segmentname
					, @freespace
	
	return @retval
end
go

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_clear
go

/*
** ***************************************************************************
** sp_dbxt_clear
**
** Top-level sproc that implements the 'clear' command.
** Executed from sybsystemprocs.
**
** Parameters:
**	@objtype	- Object type; see below.
**	@db_devname	- Db or device name.
**	@arg1		- Sub-command/object-specfic argument.
**	@arg2		- Sub-command/object-specfic argument.
**	@currdbname	- Name of db where command was invoked from.
**
** Returns:
**	0	- If 'clear' was executed without any errors.
**	1	- Otherwise.
{
*/
create procedure sp_dbxt_clear (
			@objtype 	varchar(30)	= NULL
		      , @db_devname	varchar(256)	= NULL
		      , @arg1		varchar(256)	= NULL
		      , @arg2		varchar(256)	= NULL
		      , @currdbname	varchar(256)
) as
begin
	declare @msg		varchar(100)
	      , @objtype_val	int
	      , @retval		int
	      , @retval2	int
	      , @direntry	tinyint
	      , @objtype_names	varchar(256)
	      , @whoami_cp	varchar(30)

	select @msg = 'sybsystemprocs'

	select    @retval 	= 1
		, @whoami_cp	= object_name(@@procid, db_id(@msg)) + ": "

		-- Reuse variable for current db name
		, @objtype_names= db_name()

	-- Verify that we are executing form sybsystemprocs.
	--
	if (@objtype_names != @msg)	-- db_name() != sybsystemprocs
	begin
		raiserror 19232, @whoami_cp, @msg, @objtype_names
		return 1
	end

	exec @objtype_val = sp_dbxt_parse_itemtype 'objects'
					, @objtype output
					, 19194

	-- error condition.
	if ((@objtype_val = -1) OR (@objtype_val = 0))
	begin
		if (@objtype_val = 0)
		begin
			exec @direntry = sp_dbxt_get_direntry 'objects'
			exec sp_dbxt_build_args_list 'XT', @direntry,
						@objtype_names output

			raiserror 19155, @objtype_names
		end
		return 1
	end

	-- Drop entries for a 'database'.
	if (@objtype = 'database')
	begin
		exec @retval = sp_dbxt_clear_database
						  @db_devname
						, @arg1
						, @currdbname
	end

	-- Drop entries for a 'device'.
	else if (@objtype = 'device')
	begin
		exec @retval = sp_dbxt_clear_device @db_devname
	end

	-- Drop a threshold at the specified value(s).
	--
	else if (@objtype = 'threshold')
	begin
		exec @retval = sp_dbxt_clear_threshold
					  @db_devname
					, @arg1
					, @arg2
					, @currdbname
	end

	return (case when @retval != 0 then 1 else 0 end)
end
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Wed Aug 13 17:45:24 2003 
*/
/*
** raiserror Messages for dbxt_enable [Total 4]
**
** 19213, "Invalid argument or unsupported command: %1!."
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19243, "Usage: sp_dbextend { 'enable' | 'disable' }, 'database' [, @dbname [, @segmentname ] ]"
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_enable [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_enable ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

-- ***************************************************************************

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_do_enable_database
go

/*
** sp_dbxt_do_enable_database
**
** Lower level routine that performs the update to sysattributes to
** enable/disable a policy. This routine can be used to change the status
** either for a given db/segment, or server-wide.
**
** Returns:
**	0	- The policy was successfully changed.
**	1	- There were errors changing it.
**     -1	- Policy was not found; so could not change it.
{
*/
create procedure sp_dbxt_do_enable_database(
				  @dbname	varchar(256)	= NULL
				, @segmentname	varchar(256)	= NULL
				, @enable	int
) as
begin
	declare @whoami		varchar(30)
	      , @class		smallint
	      , @objtype	varchar(2)
	      , @err1           int
	      , @rcount1        int
	/*
	** *******************************************************************
	** Perform the 'enable' , 'disable' command.
	** *******************************************************************
	*/
	select    @class = 19		-- AUTODBXT_CLASS
		, @objtype = 'DB'

    if (@dbname = 'server-wide')
    begin
	-- Make the server-wide policy disabled.
	--
	update master.dbo.sysattributes
	set object_info2 = @enable
	where class = @class
	  and object_type = 'XT'
	  and object_info1 IN (1)
	  and attribute in (select a2.object_info1
			      from master.dbo.sysattributes a2
			      where a2.class = 19
				and a2.attribute = 0  -- DBXT_DIRECTORY
				and a2.object_type = 'XT')
    end

    else
    begin
	-- Update all rows from master.dbo.sysattributes that match the two
	-- arguments (@dbname, @segmentname), if either was provided.
	-- Just work with a single-statement transaction to avoid a 3917
	-- error if this were done under a begin-end tran bracket, and the
	-- command was issued from 'tempdb'.
	--
	update master.dbo.sysattributes
	set object_info2 = @enable
	where class = @class

	  -- Returns either one of DBXT_DEFAULT_DB or DBXT_USER_SPEC_DB
	  -- attribute ID from sysattributes.
	  --
	  and attribute = (select a2.object_info1
	  		   from master.dbo.sysattributes a2
			   where class = @class
			     and attribute = 0	-- DBXT_DIRECTORY
			     and object_type = @objtype
			     and object_cinfo = 
			     		case @dbname
					  when 'default'
					  then 'system-default'
					  else 'user-specified'
					end
			  )

	  and object_type = @objtype
	  and (@dbname is NULL OR object_cinfo like @dbname)
	  and (    @segmentname is NULL
	        OR object IN (select si.segment
			      from #syssegments si
			      where si.name like @segmentname
			        and si.dbname like @dbname
			     )
	      )
    end

	select @err1 = @@error, @rcount1 = @@rowcount

	-- Return back error status and if any rows were updated.
	--
	if (@err1 != 0)
		return 1
	else if (@rcount1 != 0)
		return 0
	else 
		return -1
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_enable_database
go

/*
** ***************************************************************************
** sp_dbxt_enable_database
**
** Parameters:
**	@dbname		- DB name arg for 'enable', 'disable' command.
**	@segmentname	- Segment for which to clear rules.
**	@enable		- Boolean: Whether it is enable/disable.
**	@currdbname	- DB from which the sproc was executed.
**
** Returns:
**	0	- If auto expansion was correctly turned on/off.
**	1	- Otherwise. (Some error messages might be raised.)
{
*/
create procedure sp_dbxt_enable_database(
				@dbname		varchar(256)	= NULL
			      , @segmentname	varchar(256)	= NULL
			      , @enable		int
			      , @currdbname	varchar(256)
) as
begin
	declare @retval 	int
	      , @whoami		varchar(30)
	      , @command	varchar(10)
	      , @valid_db	int

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))

		-- Initially assume that @dbname is non-NULL and is for a
		-- valid database. Later, we might find that it is NULL, or
		-- for a db that does not exist.
		--
		, @valid_db = 1
		, @command = case @enable
				when 1 then 'enable'
				else 'disable'
			     end

	/*
	** *******************************************************************
	** Check Privileges on the command.
	** *******************************************************************
	*/
	exec @retval = sp_dbxt_clear_db_privileges @dbname output
						 , @segmentname
						 , @currdbname
						 , @command
						 , @valid_db output

	-- If we failed privilege checks, abort operation.
	if (@retval != 0)
		return 1

	/*
	** By now, we have determined that the @dbname that this command is
	** operting on is either valid, or NULL. Appropriately, cache the
	** segment info for this db. (@dbname might be NULL, or a pattern
	** specifier.)
	*/
	if (@valid_db != 0)	-- Could be dbid of @dbname
	begin
		exec @retval = sp_dbxt_ins_all_seginfo @dbname, @whoami
		if (@retval != 0)
			return 1
	end

	-- Perform the actual change, now that permissions are ok.
	exec @retval = sp_dbxt_do_enable_database @dbname, @segmentname
						, @enable

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_enable
go

/*
** ***************************************************************************
** sp_dbxt_enable
**
** Top-level sproc that implements the 'enable', 'disable' commands.
** Executed from sybsystemprocs.
**
** Parameters:
**	@objtype	- Object type; see below.
**	@db_devname	- Db , or device name.
**	@arg1		- Sub-command specific argument.
**	@enable		- Boolean: Whether it is enable/disable.
**	@currdbname	- Name of db where command was invoked from.
**
** Returns:
**	0	- If command executed without any errors.
**	1	- Otherwise.
{
*/
create procedure sp_dbxt_enable (
			@objtype 	varchar(30)	= NULL
		      , @db_devname	varchar(256)	= NULL
		      , @arg1		varchar(256)	= NULL
		      , @enable		int
		      , @currdbname	varchar(256)
) as
begin
	declare @msg		varchar(100)
	      , @objtype_val	int
	      , @retval		int
	      , @retval2	int
	      , @direntry	tinyint
	      , @objtype_names	varchar(256)
	      , @whoami_cp	varchar(30)

	select @msg = 'sybsystemprocs'

	select    @retval 	= 1
		, @whoami_cp	= object_name(@@procid, db_id(@msg)) + ": "

		-- Reuse variable for current db name
		, @objtype_names= db_name()

	-- Verify that we are executing form sybsystemprocs.
	--
	if (@objtype_names != @msg)	-- db_name() != sybsystemprocs
	begin
		raiserror 19232, @whoami_cp, @msg, @objtype_names
		return 1
	end

	exec @objtype_val = sp_dbxt_parse_itemtype 'objects'
					, @objtype output
					, 19194

	-- error condition.
	if ((@objtype_val = -1) OR (@objtype_val = 0))
	begin
		if (@objtype_val = 0)
		begin
			exec @direntry = sp_dbxt_get_direntry 'objects'
			exec sp_dbxt_build_args_list 'XT', @direntry,
						@objtype_names output

			raiserror 19243, @objtype_names
		end
		return 1
	end

	-- Disable/Enable auto expansion for a db/segment.
	if (@objtype = 'database')
	begin
		exec @retval = sp_dbxt_enable_database
						  @db_devname
						, @arg1
						, @enable
						, @currdbname
	end

	-- Enable/disable expansion for a device.
	else if (@objtype IN ('device', 'threshold'))
	begin
		raiserror 19213, @objtype
		return 1
	end

	return (case when @retval != 0 then 1 else 0 end)
end
go	-- }

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Wed Aug 13 17:45:25 2003 
*/
/*
** raiserror Messages for dbxt_sprocs [Total 2]
**
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_sprocs [Total 3]
**
** 19216, "%1!Device %2! of size %3!M resized by %4!M to a total size of %5!M."
** 19241, "Automatic expansion policy disabled for database '%1!' segment '%2!' due to error %3!. User with appropriate privileges can re-enable the policy using the 'ENABLE' command."
** 19242, "Automatic expansion policy disabled server-wide due to fatal error %1! in the master database encountered when expanding database '%2!' for segment '%3!'. Please resolve these errors and re-enable automatic expansion using the ENABLE sub-command."
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_sprocs ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

/*
** ===========================================================================
** Create the #temp table that will be used in various stored procedures.
** This #temp table stores global data about the device allocations, and
** available sizes. It is used heavily in dry-run mode to demonstrate the
** results of the algorithms being implemented here.
*/
/*
** Use the exact same SQL that will be used at run-time to create the
** #temp table now. Just keep it empty using WHERE 1 = 0 predicate.
** This will get dropped after the procedures are all installed.
**
** ===============================
** NOTE: Code maintenance issue:
** ===============================
**
**	This SQL statement is duplicated in the sp_dbxt_extend_db which creates
**	the table at run-time. MAKE SURE that if this definition ever changes,
**	that sproc is also updated.
**
*/
	select 	 
		  -- OR in sysusages.segmap from all rows
		  convert(int not null, 0) as segmaps

		, d.name as devicename

		, d.high, d.low, d.vdevno
		, (d.high - d.low + 1) as devsize_vpgs

		, (convert(float, (d.high - d.low + 1)) * 2048 
				/ @@maxpagesize) as devsize_lpgs

		-- Compute sum used pages on this device, in # logical pages.
		, (select sum(convert(float, u2.size))
		   from master.dbo.sysusages u2
		   where u2.vdevno = d.vdevno) 
		   as devusedsize_lpgs

		-- (Total device size) - (sum of used size by all dbs)
		, ( ( ( convert(float, (d.high - d.low + 1) ) * 2048)
				/ @@maxpagesize)

		       -- Sum of used sizes from this device for all dbs.
		    - (select sum(u3.size)
		       from master.dbo.sysusages u3
		       where u3.vdevno = d.vdevno) )

		as devfreesize_lpgs

	into	#segdev lock allpages

	from 	  master.dbo.sysdevices d
		, master.dbo.sysusages u
	where d.status & 2 = 2		-- only consider physical devices
	  and u.dbid = db_id('tempdb')	-- for this database
	  and u.vdevno = d.vdevno
	  and 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_segsize_simulate
go

/*
** ===========================================================================
** sp_dbxt_segsize_simulate()
**
** Sub-proc to get the segment's size in simulate mode. This exists as a
** separate proc so that users/QA can call sp_dbxt_segsize() without getting
** an error about #segsize not being found.
**
** Parameters:
**	@dbname		- Db for which we are searching for 'segment'
**	@segid		- Segment ID we are interested in.
**
** Returns:
**	@totalsize	- Total size in the segment in # of logical pages.
{
*/
create procedure sp_dbxt_segsize_simulate (
			  @dbname	varchar(256)
			, @segbit	int
) as
begin
	declare @totalsize 	int

	select @totalsize = sum(sd.devusedsize_lpgs)
	from #segdev sd
	where (sd.segmaps & @segbit) = @segbit

	return (@totalsize)
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_segsize
go

/*
** ===========================================================================
** sp_dbxt_segsize()
**
** Simple procedure to calculate the size of a given segment, and return
** the total segment size in number of logical pages.
**
** This just returns the current total size of the segment for the given
** (@dbname, @segid) pair, obtained either from catalogs directly, or from.
** the cached #segdev table tracking simulated state of space available.
**
** Parameters:
**	@dbname		- Db for which we are searching for 'segment'
**	@segid		- Segment ID we are interested in.
**	@simulate	- Whether simulation or real execution.
**
** Returns:
**	@totalsize	- Total size in the segment in # of logical pages.
{
*/
create procedure sp_dbxt_segsize (
			  @dbname		varchar(256)
			, @segid		int
			, @simulate	int	= 0
) as
begin
	declare @segbit		int		-- bitmask for segment id
	      , @totalsize	int

	exec @segbit = sp_dbxt_segment_to_segbit @segid

	if (@simulate != 1)	-- Threshold execution, or 'execute' mode.
	begin
		select @totalsize = sum(size)
		from master.dbo.sysusages
		where dbid = db_id(@dbname)
		  and (segmap & @segbit) = @segbit
	end
	else
	begin
		exec @totalsize = sp_dbxt_segsize_simulate @dbname, @segbit
	end

	return @totalsize
end
go	-- }

exec sp_dbxt_recreate_proc sp_dbxt_get_alterdb_size
go

/* ===========================================================================
** sp_dbxt_get_alterdb_size()
**
** Here is where we could implement a site-specific policy to determine by
** how much to alter a particular database, for a given segment on a given
** disk that seems to have some room left on it. For now, we dummy this one
** up and always return the default of 2 allocation units for the current
** logical page size.
**
** sp_dbxt_get_alterdb_size() is provided as a hook for installing the 
** procedures. This will be dropped at the end of the installation, so that
** then the user can go back and install a site-specific version, if needed.
** If this procedure does not exist, then the size by which the db will
** be expanded is the size at which the threshold action procedure was
** installed.
**
** Parameters:
**	@dbname		- DB which needs to be altered.
**	@segmentname	- Segment that ran out of space.
**	@devname	- Device being considered for the increase.
**	@simulate	- Type code for execution mode. (See caller.)
**
** Output Parameters:
**	@incr_size_MB	- Size to growby this db by in MB.
**
** Returns:
**	Nothing.
**
{
*/
create procedure sp_dbxt_get_alterdb_size(
				  @dbname	varchar(256)
				, @segmentname	varchar(256)
				, @incr_size_MB	float	output
				, @simulate	int	= 1
				, @trace	int	= 0
) as
begin
	declare @segid		int
	      , @totalsize	int	-- # of logical pages.
	      , @totalsize_MB	float
	      , @whoami		varchar(256)
	      , @retval		int
	      , @procname	varchar(300)

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))
	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
	-- Find out the segment's ID for joining w/sysattributes. Ensure
	-- that you get the segment ID of the db that we are operating on,
	-- and not of the db that this proc lives in.
	--
	select @procname = @dbname + ".dbo.sp_dbxt_segid"
	exec @segid = @procname @segmentname

	exec @totalsize = sp_dbxt_segsize @dbname, @segid, @simulate
	select @totalsize_MB = (convert(float, @totalsize) * @@maxpagesize)/ (1024 * 1024)

	-- Compute the appropriate db expansion size given the current size
	-- of the (db, segment), and the rules as found in sysattributes.
	--
	exec @retval = sp_dbxt_get_db_growby @dbname
					   , @segid
					   , @totalsize_MB
					   , @incr_size_MB output
					   , @trace

	if (@trace = 1)
	begin
		print "%1!: Db '%2!' segment: '%3!' will be expanded by '%4!M'",
			@whoami, @dbname, @segmentname, @incr_size_MB
	end

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_show_segmaps
go

/* ===========================================================================
** sp_dbxt_show_segmaps()
**
** Generate a SELECT statement to output data from #segdev table. This sproc
** does auto-formatting of the result rows, for readability. This procedure
** is only (*should* only be) called when tracing is on. To avoid caller
** errors, we provide a default @trace argument set to 1.
**
** Parameters:
**	@header		- User-supplied header tag-line to print.
**	@devicename	- Name of device to select using LIKE pattern.
**	@order_By	- User-specified ORDER BY clause.
**	@trace		- Trace level. Default to 1.
**
** Returns:
**	0	- If print was ok.
**	1	- Some errors; usually from execute immediate.
{
*/
create procedure sp_dbxt_show_segmaps(
			  @header	varchar(256)
			, @devicename	varchar(256)	= NULL
			, @order_By	varchar(50)	= NULL
			, @trace	int		= 1
) as
begin
	declare @numrows	int
	      , @whoami		varchar(32)
	      , @retval		int
	      , @where_Clause	varchar(100)

	select @numrows = count(*) from #segdev

	select    @whoami = case @trace
				when 0 then NULL
				else object_name(@@procid, db_id('sybsystemprocs'))
						+ ": "
			    end
		, @order_By = case when @order_By IS NULL
				then " order by devicename"
				else " " + @order_By
			     end
		, @where_Clause = case when @devicename IS NULL
				      then NULL
				      else "WHERE devicename like '"
				      		+ @devicename + "'"
				  end

	-- Prefix user's header with the fn name for tagging.
	select @header = @whoami + @header
				 + " ("
				 + @order_By
				 + "; "
				 + convert(varchar, @numrows)
				 + " rows)"
	print @header

	if (@numrows = 0)
		return 0

	exec @retval = sp_autoformat
				  @fulltabname = "#segdev"
			  	, @selectlist = "segmaps, devicename,  devsize_vpgs, devsize_lpgs, devusedsize_lpgs, devfreesize_lpgs"
				, @whereclause = @where_Clause
				, @orderby = @order_By
	if (@retval != 0)
		return 1

	print ""
	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

/*
** DBXT_FUTURE: There are ways to identify tasks that hog up the log and
** 	   trip the last-chance threshold on syslogs. It is simple enough
**	   to setup a penultimate last-chance threshold that does a
**	   DUMP TRAN (like above), and a real last-chance threshold that
**	   also kills the task that is hogging up the log. Have not built
**	   the code for this, and that needs testing.
*/

/* ===========================================================================
** sp_dbxt_devsize_vpgs()
** sp_dbxt_devsize_lpgs()
** sp_dbxt_usedsize_lpgs()

** Given a device's logical name, return its size in number of logical pages
** This is useful to decide whether there is sufficient space on this disk
** to alter the database on.
**
** These three procedures are the key size-providers that help with dry-run
** mode of execution. When called, they just lookup the value from the
** #temp table, but while at it, also validate that the cached value of
** these sizes in the #temp table are still current. For e.g, it might be
** that when the #temp table was created a particular device had 50M of space
** free, but by the time we got around to running this sproc, the space was
** used up by 25M, and we are down to only 25M of free space on this device.
** To account for such 'race' conditions (possible but unlikely), we also
** recompute the value from sysdevices. If the new value is less than the
** cached value, the #temp table is updated. 
**
** Parameters:
** 	@devname	- Name of device where we want to search for space.
**	@simulate	- Type code for execution mode. (See caller.)
**
** Returns:
**	Actual virtual /free/used pages available on the device.
*/
exec sp_dbxt_recreate_proc sp_dbxt_devsize_vpgs
go

/* --------------------------------------------------------------------------- 
{
*/
create procedure sp_dbxt_devsize_vpgs (@devname 	varchar(256)
				      , @simulate	int = 1
				      , @trace		int = 0
) as
begin
	declare @cached_size_virtpages		int
	      , @size_virtpages			int

	if (@simulate != 1)
	begin
		select @size_virtpages = (d.high - d.low + 1)
		from master.dbo.sysdevices d
		where d.name = @devname
		  and (d.status & 2) = 2
	end
	else
	begin
		-- Disk can only resize up, so the new size can only be
		-- more than the cached total size value.
		--
		select @size_virtpages = devsize_vpgs
		from #segdev
		where devicename = @devname
	end

	return @size_virtpages
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_devsize_lpgs
go

/* --------------------------------------------------------------------------- 
{
*/
create procedure sp_dbxt_devsize_lpgs (@devname 	varchar(256)
				      , @simulate	int = 1
				      , @trace		int = 0
) as
begin
	declare @cached_size_logicalpages	int
	      , @size_logicalpages		int

	/*
	-- Only dump when we are simulating and tracing.
	if ((@simulate = 1) and (@trace = 1))
		exec sp_dbxt_show_segmaps "In sp_dbxt_devsize_lpgs:", @devname
	*/
	if (@simulate != 1)
	begin
		-- First get # of virtual pages.
		exec @size_logicalpages = sp_dbxt_devsize_vpgs @devname 
								, @simulate
								, @trace

		-- Then convert that to # of logical pages.
		select @size_logicalpages = ((convert(float,@size_logicalpages) 
						/ @@maxpagesize)
							* @@pagesize)
	end
	else
	begin
		-- Get cached size in # logical pages from #temp table.
		-- Disk can only resize up, so the new size can only be more
		-- than the cached total size value.
		--
		select @size_logicalpages = devsize_lpgs
		from #segdev
		where devicename = @devname
	end
	return @size_logicalpages
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_usedsize_lpgs
go

/* --------------------------------------------------------------------------- 
** Return the size used on this device by *all* databases.
{
*/
create procedure sp_dbxt_usedsize_lpgs (@devname	varchar(256)
				      , @simulate 	int = 1
				      , @trace		int = 0
) as
begin
	declare @cached_used_size_lpgs	int
	      , @used_size_lpgs		int
	      , @whoami			varchar(30) 

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
	/*
	-- Only dump when we are simulating and tracing.
	if ((@simulate = 1) and (@trace = 1))
		exec sp_dbxt_show_segmaps "In sp_dbxt_usedsize_lpgs:", @devname
	*/

	select @used_size_lpgs = sum(u.size)
	from  	  master.dbo.sysusages u
		, master.dbo.sysdevices d
	where u.vdevno = d.vdevno
	  and d.cntrltype = 0
	  and d.name = @devname
	  and (d.status & 2) = 2

	if (@simulate = 1)
	begin
		select @used_size_lpgs = devusedsize_lpgs
		from #segdev
		where devicename = @devname

		/*
		** DBXT_FUTURE: This is probabaly all unnecessary. The
		** simulation will happen very fast, so we don't have to
		** worry about intermediate create/drop dbs. 
		**
		select @cached_used_size_lpgs = devusedsize_lpgs
		from #segdev
		where devicename = @devname

		-- Current usage can be lower than the cached value in case
		-- some other db that was on this device was dropped in the
		-- meantime. On the other hand, as we run through the dry-run
		-- mode, each iteration of the simulation will reduce the
		-- @cached_used_size_lpgs reflecting the increased usage of
		-- space on this device.
		--
		if (@used_size_lpgs > @cached_used_size_lpgs)
		begin
			if (@trace = 1)
			begin
				print "%1!Updating #segdev from %2! to %3!",
					@whoami,
					@cached_used_size_lpgs,
					@used_size_lpgs
			end

			select @cached_used_size_lpgs = @used_size_lpgs

			update #segdev
			set devusedsize_lpgs = @cached_used_size_lpgs
			where devicename = @devname
		end
		else
			select @used_size_lpgs = @cached_used_size_lpgs
		*/
	end

	return @used_size_lpgs
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_aux_get_resize_by
go

/* ===========================================================================
** sp_aux_get_resize_by()
**
** Here is where we implement another site-specific policy control. This
** procedure is a way for the customer to specify by how much to resize a
** particular Sybase device by. Customers can choose to replace the Sybase
** provided default procedure by one that is tailor made for each site.
**
** The arbitrarily picked default is to resize each disk by 10% of its
** current size, subject to a (random) minimum of 4Mb.
**
** Parameters:
**	@devname	- Device name which is a candidate for resize operation
**	@simulate	- Type code for execution mode. (See caller.)
**	@trace		- Debugging hook of int type.
**			  0: (default) No trace output printed.
**			  1: Print tracing output of return value.
**
** Output Parameters:
**	@resize_min_MB	- Min size, in MB, to resize device by.
**	Size in MB to resize device by. This is obtained either from
**	site-specific policy, or the default resizing scheme installed by
**	this machinery.
**
** Returns:
**	0	- Size was appropriate; i.e. +ve.
{
*/
/*
** Probably don't need this any more, as the default rules are
** coded in sysattributes, and sp_dbxt_get_device_resize will exist. We
** can consider retaining this in case user accidentally drops that sproc
** while in the process of updating that for more rules.
*/
create procedure sp_aux_get_resize_by(
				  @devname		varchar(256)
				, @resize_min_MB	float	 output
				, @simulate		int = 1
				, @trace		int = 0
) as
begin
	declare @devsize_vpgs	int	-- Device size in # of virtual pages
	      , @devsize_MB	float

	exec @devsize_vpgs = sp_dbxt_devsize_vpgs @devname, @simulate, @trace

	select @resize_min_MB = 4	-- 4 MB of minimum resize

	-- 4Mb min resize increment is 10% of 20K virtual pages. (Each
	-- virtual page is 2K, so 20K virtual pages is 40M. 10% of that
	-- is 4Mb.) We only need to figure out the %age increment if the
	-- # of virtual pages in the disk is currently > 20K.
	--
	if (@devsize_vpgs > ( 20 * 1024 ) )
	begin
		select @devsize_MB = (convert(float, @devsize_vpgs) * 2) / 1024
		select @resize_min_MB = (0.1 * @devsize_MB)
	end

	if (@trace = 1)
	begin
		declare @whoami 	varchar(30)
		select @whoami = object_name(@@procid, db_id('sybsystemprocs'))
	
		print "%1!: Device '%2!' will be resized by '%3!M'",
			@whoami, @devname, @resize_min_MB
	end
	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

/* ===========================================================================
** sp_dbxt_get_device_resize
**
** Create a placeholder sproc that is necessary to install the other
** threshold procedures. Customer has the choice to provide one such
** procedure that can embed the policy rules for how each device should
** be resized. This dummy sproc will be dropped at the end of the install
** process.
**
** Parameters:
**	@devname	- Logical name of candidate device to resize.
**	@simulate	- Whether this is a dry-run mode.
**	@trace		- Debugging level.
**
** Output Parameters:
**	@resize_min_MB	- Min size, in MB, to resize device by.
**	Size in MB to resize device by. This is obtained either from
**	site-specific policy, or the default resizing scheme installed by
**	this machinery.
**
** Returns:
**	0	- If size was appropriate; i.e. +ve.
**	1	- Size was incorrect; but was fixed up to be 0 to avoid errors.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_get_device_resize
go

create procedure sp_dbxt_get_device_resize(
				  @devname		varchar(256)
				, @resize_min_MB	float	output
				, @simulate		int = 1
				, @trace		int = 0
) as
begin
	declare @devsize_vpgs	int	-- Device size in # of virtual pages
	      , @devsize_MB	float	-- Device size in Mb
	      , @retval		int

	exec @devsize_vpgs = sp_dbxt_devsize_vpgs @devname, @simulate, @trace
	select @devsize_MB = (convert(float, @devsize_vpgs) * 2) / 1024

	exec @retval = sp_dbxt_get_dev_growby @devname
					    , @devsize_MB
					    , @resize_min_MB output
					    , @trace
	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

/* ===========================================================================
** sp_dbxt_upd_segmaps()
**
** Update the segment maps in the #temp table for all database pieces
** that reside on a given device. This segment map is an OR of all existing
** segments that use this device. The idea here is that if a given segment
** already uses a given device, then that device is a candidate for future
** re-use when altering the db for that segment.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_upd_segmaps
go

create procedure sp_dbxt_upd_segmaps(
				  @dbname 	varchar(256)
				, @segmentname	varchar(256)
				, @simulate	int = 1
				, @trace	int = 0
) as
begin
	declare @segmap		int
	      , @usage_segmap	int	-- segmap from sysusages
	      , @devicename	varchar(256)
	      , @vdevno		int

	-- Loop around each device in the #temp table.
	declare devcur cursor for
	select devicename, vdevno
	from #segdev

	open devcur

	fetch devcur into @devicename, @vdevno

	while (@@sqlstatus = 0)
	begin	-- {

		select @segmap = 0

		-- Loop around each row from sysusages for the db of
		-- interest, looking for device usages on the current
		-- device being scanned in the outer cursor.
		--
		declare usagecur cursor for
		select u.segmap
		from master.dbo.sysusages u
		where u.dbid = db_id(@dbname)
		  and u.vdevno = @vdevno

		open usagecur

		fetch usagecur into @usage_segmap

		while (@@sqlstatus = 0)
		begin
			-- OR the various segmaps into the accumulator
			select @segmap = (@segmap | @usage_segmap)

			fetch usagecur into @usage_segmap
		end

		close usagecur

		deallocate cursor usagecur

		-- Update the segmap in the #temp table to reflect all
		-- the segments that are on this device.
		--
		update #segdev set segmaps = @segmap
		where devicename = @devicename

		fetch devcur into @devicename, @vdevno
	end	-- }

	close devcur

	deallocate cursor devcur

	if ((@simulate = 1) and (@trace = 1) )
	begin
		exec sp_dbxt_show_segmaps
			"Contents after updating segmaps."
	end
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_handle_error
go

/* ===========================================================================
** sp_dbxt_handle_error()
**
** Command-specific error handling routine. This is invoked right after an
** execute immediate that performs the auto expansion step; either the ALTER
** DATABASE or a DISK RESIZE. If those commands fail, we proactively take
** corrective steps, one of which might mean turning off the auto expansion
** policy for the db/segment on which it was being last attempted.
**
** Parameters:
**	@error		- @@error code from previous command.
**	@dbname		- DB that the command was operating for / on.
**	@segmentname	- Segment in db that command was operating for/on.
**	@currdbname	- DB that this sproc was called from.
**	@trace		- Trace level.
**
** Returns:
**	0	- Nothing was done to change the policy. The error is
**		  'acceptable'. Returned for errors that are known to us.
**	1	- The expansion policy was disabled. This needs to be
**		  percolated back by the caller to turn off auto expansion
**		  in this iteration, and for future runs.
**     -1	- Other internal errors in this procedure. Policy remains
**		  unchanged, but we return state back to caller in case it
**		  wants to handle it.
{
*/
create procedure sp_dbxt_handle_error(
			  @error	int
			, @dbname	varchar(256)
			, @segmentname	varchar(256)
			, @currdbname	varchar(256)
			, @trace	int		= 0
) as
begin
	declare @retval		int
	      , @change_policy	tinyint
	      , @whoami		varchar(30)
	      , @msg		varchar(256)

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))

		-- don't expect to have to turn off expansion policies.
		, @change_policy = 0

	if (@trace = 1)
	begin
		print "%1!: Handle error %2! for db: '%3!' segment: '%4!'"
			, @whoami, @error, @dbname, @segmentname
	end

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0

	-- Store the syssegments info in a temp table, for later use.
	exec @retval = sybsystemprocs.dbo.sp_dbxt_ins_db_seginfo
						@dbname, @whoami, @trace
	if (@retval != 0)
		return -1

	-- ******************************************************************
	-- Disable auto expansion for this db/segment, as we got some
	-- fatal errors that we don't know about, and dont' know how to
	-- handle them. Handle errors affecting 'master' db in a strong
	-- manner by turning off the whole auto expansion feature. Otherwise,
	-- master could get corrupt, and the whole server can crash.
	-- NOTE: QA pointed out that we can never get 1105 in master as
	-- tasks will be put to LOG SUSPEND state. We cannot set the db
	-- option in 'master' to 'abort tran on log full' directly.
	--
	if ((@currdbname in ('master')) and (@error IN (1105, 5029)))
	begin
		select @change_policy = 1
		exec @retval = sp_dbxt_do_enable_database
					  'server-wide'
					, NULL
					, 0
	end

	-- ****************************************************************
	-- Known 'errors' that are permissible:
	-- 1205	- Deadlock, usually involving sysusages.
	-- 2601 - Multiple alter db violate unique index on sysusages.
	-- 1816	- Race condition when 2 tasks try to use the same db piece
	--	  for create db.
	-- These should have been resolved due to hard-synchronization
	-- logic, but check for them in any case.
	--
	else if (@error NOT IN (1205, 2601, 1816))
	begin
		select @change_policy = 1
		exec @retval = sp_dbxt_do_enable_database
						  @dbname
						, @segmentname
						, 0
	end

	-- If an attempt was made to change the policy:
	--
	if (@change_policy = 1)
	begin
		if (@retval = 0)	-- was successfully disabled.
		begin
			if (@dbname in ('master'))
			begin
				-- Turned off auto expansion server-wide due to
				-- fatal errors in master.
				--
				exec sp_getmessage 19242, @msg output
				print @msg, @error, @dbname, @segmentname
					  , @error
			end
			else
			begin
				-- Turned off auto-expansion for the db/segment.
				exec sp_getmessage 19241, @msg output
				print @msg, @dbname, @segmentname, @error
			end

			return 1
		end
		else if (@trace = 1)
		begin
			print "%1!: Could not disable auto expansion policy for db: '%2!' segment: '%3!'. Return value: %4!"
				, @whoami , @dbname, @segmentname, @retval

			return -1
		end
	end

	return 0
end
go	-- }

exec sp_dbxt_recreate_proc sp_dbxt_synchronize_expansion
go

/*
** ===========================================================================
** sp_dbxt_synchronize_expansion()
**
** Synchronize multiple tasks trying to perform automatic expansion the very
** first time they come in to alter a database.
**
** Parameters:
**	@dbname		- DB name on which threshold proc fired.
**	@segmentname	- Segment which is being expanded right now.
**	@operation	- Command name that caused expansion. (For messages.)
**	@devicename	- Device name (if any) that command is working on.
**	@free_space	- At which threshold was initially fired.
**	@simulate	- Type code for execution mode. (See caller.)
**	@trace		- Trace level.
**
** Output Parameters:
**	@token		- Token received to identify this task in the
**			  control table. 
**
** Returns:
**	0	- If we were able to lock ourselves in.
**	1	- Otherwise, some synchronization errors.
{
*/
create procedure sp_dbxt_synchronize_expansion(
				 @dbname	varchar(256)
			       , @segmentname	varchar(256)
			       , @operation	varchar(30)
			       , @devicename	varchar(256)
			       , @free_space	int
			       , @token		numeric(38, 0)	output
			       , @simulate	int	= 1
			       , @trace		int	= 0
)as
begin
	declare @whoami		varchar(32)
	      , @whoami_fn	varchar(32)
	      , @dbxt_appln	varchar(30)
	      , @max_token	numeric(38,0)
	      , @min_token	numeric(38,0)
	      , @min_spid	int	-- spid of task owning min_token
	      , @max_spid	int	-- spid of task owning max_token
	      , @in_queue	int
	      , @token_str	varchar(30)
	      , @soft_sync_low	int	-- Start/end of commands that we
	      , @soft_sync_high	int	-- should soft-sync on.
	      , @printed	tinyint

	select   @whoami_fn = object_name(@@procid, db_id('sybsystemprocs'))
	       , @dbxt_appln = "Automatic Database Expansion"
	       , @printed = 0

	select @whoami = case @trace when 0 then NULL else @whoami_fn + ": " end

	-- Register ourselves in the control table, but only do it on
	-- the very first visit to this action.
	--
	if (@token = 0)
	begin
		insert tempdb.dbo.syb_auto_db_extend_control (
			dbname, segmentname, devicename, spid, freespace, curr_freespace
		) values
			( @dbname, @segmentname, @devicename, @@spid
			, @free_space
			, @free_space
			)
		if (@@error != 0)
		begin
			return 1
		end

		select    @token = @@identity
			, @token_str = convert(varchar, @@identity)

		-- ******************************************************
		-- 'Save' the operation's token # in sysprocesses, so that
		-- we can quickly identify all the tasks that are running
		-- with valid tokens. In case of errors, it will then be
		-- simple to delete all those rows from the control table
		-- that do not appear in sysprocesses.clientname
		--
		SET clientname @token_str

		if (@trace = 1)
		begin
			print "%1!Received new token %2! for spid %3!. %4! DB: '%5!' Segment: '%6!' Device: '%7!'"
				, @whoami, @token, @@spid
				, @operation
				, @dbname, @segmentname
				, @devicename
		end
	end

	-- ***********************************************************
	-- If this is the only fellow in the control table, we are done.
	--
	if ((select count(*) from tempdb.dbo.syb_auto_db_extend_control) = 1)
	begin
		goto soft_sync
	end

	-- ***********************************************************
	-- Wait till it is your turn. Wait for both user-invoked
	-- alter and for threshold task. We wait for user-invoked case
	-- also in case this is being done in a run-time system to
	-- force an alter operation, in which case we have to still
	-- synchronize with other background tasks.
	--
	while (1 = 1)
	begin	-- {

		select @min_token = isnull(min(token), 0)
		from tempdb.dbo.syb_auto_db_extend_control

		-- If we are the first (or new) one in, we are done.
		if (@min_token IN (0, @token))
			break

		else if (@min_token < @token)
		begin 	-- {

			-- ************************************************
			-- Cleanup entries from the queue left by this task
			-- itself from prior runs.
			--
			delete tempdb.dbo.syb_auto_db_extend_control
			where spid = @@spid
			  and token < @token

			if (@@error != 0)
			begin
				print "%1!Error: Could not remove entry for auto-extension operation ID: %2!. DB: %3! Segment: %4! Spid: %5! (%6! rows deleted, expected %7!.)"
					, @whoami , @token
					, @dbname, @segmentname, @@spid
					, @@rowcount, 1

				return 1
			end

			-- Find the min token owner's spid.
			--
			select @min_spid = isnull(spid, 0)
			from tempdb.dbo.syb_auto_db_extend_control
			where token = @min_token

			-- ************************************************
			-- Some body is ahead of us. Check whether
			-- they are really there in sysprocesses. Here
			-- we handle the case where some task was
			-- improperly killed and did not get a chance
			-- to clean up its token before exiting.
			-- If the task is still around with the same
			-- spid, then we are hosed. (Sa will have to
			-- do something.) Otherwise, handle it
			-- ourselves and remove his entry from the 
			-- control table, so that at least others can
			-- move on.
			--
			if ((@min_spid > 0) and

			     -- See if the other guy still exists. Ignore
			     -- ourselves, in case we were the ones to have
			     -- left this from the last run, and forgot to
			     -- clean it up before exiting.
			     --
			     ((select count(*)
			       from master.dbo.sysprocesses
			       where clientapplname = @dbxt_appln
			 	 and spid = @min_spid
				 and spid != @@spid)
				 	= 0)
			   )
			begin
				delete tempdb.dbo.syb_auto_db_extend_control
				where spid	= @min_spid
				  and token	= @min_token

				-- We should deleted atmost 1 row w/o error.
				--
				if ((@@error != 0) or (@@rowcount > 1))
				begin
					print "%1!Error: Could not remove entry for auto-extension operation ID: %2!. DB: %3! Segment: %4! Spid: %5! (%6! rows deleted, expected %7!.)"
						, @whoami , @token
						, @dbname, @segmentname, @@spid
						, @@rowcount, 1

					return 1
				end
				else
				begin
					-- Go back waiting for the min.
					continue
				end
			end

			if ((@trace = 1) and (@printed = 0))
			begin
				select @printed = 1

				-- Get a quick max value; no locking.
				select    @min_token = isnull(min(token), 0)
					, @max_token = max(token)
				from tempdb.dbo.syb_auto_db_extend_control
				at isolation 0

				select @in_queue = count(*)
				from tempdb.dbo.syb_auto_db_extend_control
				where token < @token

				select @max_spid = isnull(spid, 0)
				from tempdb.dbo.syb_auto_db_extend_control
				where token = @max_token

				print "%1!Waiting for token %2!. Min: %3! (spid: %4!) Max: %5! (spid: %6!), In Queue: %7!. %8! DB: '%9!' Segment: '%10!' Device: '%11!'"
					, @whoami, @token
					, @min_token, @min_spid
					, @max_token, @max_spid
					, @in_queue
					, @operation
					, @dbname, @segmentname
					, @devicename
			end

			waitfor delay "0:0:5"
		end	-- }
	end	-- }

	print "%1!Received control for token %2!. (spid: %3!). %4! DB: '%5!' Segment: '%6!' Device: '%7!'"
		, @whoami, @token, @@spid
		, @operation
		, @dbname, @segmentname
		, @devicename
soft_sync:

	-- Testing hook to cause sprocs to hang, so that we can interleave
	-- other user tasks to start other operations in between.
	--
	/*
	while ((select count(*) from tempdb.dbo.dbxt_sync) = 0)
	begin
		print "Insert a row in tempdb.dbo.dbxt_sync to move on..."
		waitfor delay "0:0:5"
	end
	*/

	-- Get range of commands to soft-synchronize with.
	--
	select @soft_sync_low = low , @soft_sync_high = high
	from master.dbo.spt_values
	where type = 'XT'
	  and name = 'soft-sync commands'

	-- Soft-synchronization between other tasks that might be
	-- affecting sysdevices, sysusages, but are not part of the
	-- auto expansion process. Here is where we work with other
	-- user's operating on databases, and wait a bit to proceed
	-- further.
	--
	while ((select count(*) from master.dbo.sysprocesses p
	 	where p.cmd IN (select v.name
				from master.dbo.spt_values v
				where type = 'XT'
				 and number between @soft_sync_low
				 		and @soft_sync_high)
		) != 0)
	begin
		if (@trace = 1)
		begin
			print "%1!Soft-synchronization with concurrent activity for %2! command."
				, @whoami , @operation
		end

		waitfor delay "0:0:5"
	end

	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go


/* ===========================================================================
** sp_dbxt_do_resize_dev()
**
** Common procedure to resize a single disk, subject to site-specific
** rules for resizing disks. The site-specific policy is implemented as
** rules in master.dbo.sysattributes. If that exists, we retrieve the size to
** resize this disk by from there. Otherwise, we dummy this policy by resizing
** on each call by a randomly chosen 4 Mb.
**
** Once we start supporting hooks for customers to do a DISK INIT and return
** that disk's name to us, we might have situations where a non-existent
** disk is returned. Here is where we defend ourselves against user errors.
**
** Parameters:
**	@devname	- Logical Name of Sybase device to resize.
**	@dbname		- Name of db for which db is being resized.
**	@segmentname	- Name of segment in db on which alter started.
**	@free_space	- At which threshold initially fired.
**	@simulate	- Type code for execution mode. (See caller.)
**	@trace		- Trace level.
**
** Output Parameters:
**	@resize_MB	- Size, in MB, by which this device was resized.
**	@token		- Token received to identify this task in the
**			  control table. 
**
** Returns:
**	0	- If resize was done successfully.
**	1	- In case of errors. (E.g. permissions, disk not found etc.)
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_do_resize_dev
go

create procedure sp_dbxt_do_resize_dev(
			  @devname	varchar(256)
			, @resize_MB	float	output
			, @dbname	varchar(256)
			, @segmentname	varchar(256)
			, @free_space	int
			, @token 	numeric(38,0) output
			, @simulate	int = 1
			, @trace	int = 0
) as
begin
	declare @sqlstr		varchar(256)
	      , @whoami_fn	varchar(32)
	      , @whoami		varchar(32)
	      , @resize_cmd	varchar(11)
	      , @resize_lpgs	int	-- resized in # of logical pages
	      , @resize_vpgs	int	-- resized in # of virtual pages
	      , @before_size_MB	float
	      , @after_size_MB	float
	      , @retval		int
	      , @currdbname	varchar(30)

	-- Used also for exec immediate, so generate name w/o @trace check.
	--
	select @whoami_fn = object_name(@@procid, db_id('sybsystemprocs'))
	
	select    @whoami = case @trace when 0 then NULL
					       else @whoami_fn + ": "
			     end

		-- To handle error conditions, init to 0.
		, @resize_MB = 0
		, @resize_cmd = "DISK RESIZE"

		-- This should always be executed from 'master'.
		, @currdbname = db_name()


	-- Error checking for non-existent disks.
	if not exists (select 1 from master.dbo.sysdevices
		        where name = @devname)
	begin
		if (@trace = 1)
		begin
			print "%1!Could not find requested device '%2!'",
				@whoami, @devname
		end
		return 1
	end

	-- Get device's size in virtual pages, and convert that to MB.
	--
	exec @before_size_MB = sp_dbxt_devsize_vpgs @devname, @simulate, @trace
	select @before_size_MB = (convert(float, @before_size_MB) * 2) / 1024

	/*
	** Implement site-specific device-expansion logic here.
	** Search for the existence of a user-supplied sproc that can
	** serve up the size by which this device should get resized.
	** If such a procedure is not found in either master or sybsystemprocs,
	** we default to using a small value for the resize.
	*/
	if exists (select 1 from sybsystemprocs.dbo.sysobjects
		   where id = object_id('sybsystemprocs.dbo.sp_dbxt_get_device_resize') )
	begin
		exec @retval = sp_dbxt_get_device_resize @devname
						, @resize_MB output
						, @simulate, @trace
	end
	else
	begin
		exec @retval = sp_aux_get_resize_by @devname
						, @resize_MB output
						, @simulate, @trace
	end

	if (@retval != 0)
	begin
		-- Callee has guaranteed that @resize_MB == 0.
		return 1
	end

	if (@resize_MB <= 0)
	begin
		if (@trace = 1)
		begin
			print "%1!Skipping Request to resize device '%2!' by '%3!M'."
				, @whoami, @devname, @resize_MB
		end

		-- It is not an error to receive resize_MB as 0, as user might
		-- have turned off disk resizing explicitly for some disks.
		-- It *is* an error (probably, internal) to receive a < 0 value.
		--
		select @retval = case when @resize_MB < 0 then 1 else 0 end

		select @resize_MB = 0

		return @retval
	end

	select @resize_MB = ceiling(@resize_MB)

	select @sqlstr = @resize_cmd + " name = '"
			+ @devname
			+ "', size = '"
			+ convert(varchar, @resize_MB)
			+ "M'"

	print "%1! -- Db: %2! Segment: %3!"
		, @sqlstr, @dbname, @segmentname

	if (@simulate != 1)
	begin
		-- Leave tags, so users can find this step in sysprocesses.
		--
		SET clienthostname @devname

		-- ***********************************************************
		-- Register yourself in the control table and receive a token.

		exec @retval = sp_dbxt_synchronize_expansion
					  @dbname
					, @segmentname
					, @resize_cmd
					, @devname
					, @free_space
					, @token output
					, @simulate
					, @trace

		exec @retval = sp_exec_SQL @sqlstr, @whoami_fn

		-- Reset these synchronization tags before exiting.
		SET clienthostname NULL

		-- If there were some errors in executing the statement,
		-- pretend that the resize failed, and return 0 for the
		-- size by which the resize was done.
		--
		if (@retval != 0)
		begin
			select @resize_MB = 0

			-- Handle command-specific error conditions.
			--
			exec @retval = sp_dbxt_handle_error @retval
						, @dbname
						, @segmentname
						, @currdbname
						, @trace

			-- We have to return a non-zero return status as
			-- there was some error. For 'handled' errors, return
			-- 1 to indicate that caller can continue searching
			-- for more places to expand on. For 'unhandled'
			-- errors (usually hard errors), return 2 so that
			-- caller can abort the expansion process right away.
			--
			if (@retval = 0)
				return 1
			else if (@retval = 1)
				return 2
			else return 1
		end


		-- Get device's size in virtual pages, and convert that to MB.
		--
		exec @after_size_MB = sp_dbxt_devsize_vpgs @devname, @simulate
							 , @trace
		select @after_size_MB = (convert(float, @after_size_MB) * 2)
						/ 1024

		select @resize_MB = @after_size_MB - @before_size_MB
	end
	else
	begin
		-- Pretend that the resize worked. After size will increase.
		--
		select @after_size_MB = @before_size_MB + @resize_MB

		select    @resize_lpgs = @resize_MB * 
						((1024 * 1024) / @@maxpagesize)
			, @resize_vpgs = @resize_MB *
						((1024 * 1024) / @@pagesize)

		-- Update the disk space stats to reflect the addition of this
		-- new space. As we created more space, all page sizes go up.
		--
		update #segdev
		set 	  devsize_lpgs = devsize_lpgs + @resize_lpgs
			, devsize_vpgs = devsize_vpgs + @resize_vpgs
			, devfreesize_lpgs = devfreesize_lpgs + @resize_lpgs
		where devicename = @devname
	end

	-- Print a message here as DISK RESIZE is a silent command,
	-- unlike ALTER DATABASE. In real-execution mode, it is useful
	-- to trace through output(s) showing the size by which the
	-- device was resized. (Reuse @sqlstr to save space.)
	--
	exec sp_getmessage 19216, @sqlstr output

	print @sqlstr
		, @whoami, @devname
		, @before_size_MB , @resize_MB , @after_size_MB

	-- Return the (actual) size by which disk was resized (@resize_MB).
	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

/* ===========================================================================
** sp_dbxt_do_extend_db_on_dev()
**
** Extend a given database on a given already-existing Sybase device for
** a given segment, by a given size (in MB).
*
** Parameters:
**	@dbname		- Database name
**	@segmentname	- Segment name in this database.
**	@segid		- ID of the above segment.
**	@free_space	- Free space level at which threshold fired.
**	@devname	- Name of candidate device to alter db on.
**	@altersize_MB	- Size by which to try and alter the db on this dev.
**	@simulate	- Type code for execution mode. (See caller.)
**	@trace		- Debugging level.
**
** Output Parameters:
**	@altersize_MB	- Size by which the db was altered on this device.
**
**		The actual size by which the db was altered (in MB). This
**		value is computed by doing a diff of the before/after sizes
**		of the db for this segment. In the common case, only one
**		instance of this sproc would be executing. So there would be
**		very little chance for a race condition here that might cause
**		two alter dbs to execute at the same time so that the 'after'
**		size value is incorrect.
**
**	@token		- Token received to identify this task in the
**			  control table. 
**
**		This is actually an I/O parameter. The first time we attempt an
**		alter operation on any device, we put ourselves in a queue.
**		This is our token for further synchronization. Every one else
**		has to wait till we are done with our entire operation. So,
**		if we try to subsequently alter the db (in this operation),
**		we don't have to wait, our token lets us through.
**
**		This is percolated all the way back to the top-level caller,
**		so that it can delete the row after the work was done.
**
** Returns:
**	0	- If alter db was successful. (@altersize_MB is non-zero.)
**	1	- Alter db was not possible as the requested increment size
**		  is below the min alter db size for current page size. This
**		  return code is a stopping point for caller sprocs, w/o trying
**		  to further do DISK RESIZE for small increments like 0.5M.
**		  (@altersize_MB is zero.)
**	2	- Internal sproc error. Alter db failed. (@altersize_MB = zero.)
** 	3	- Alter db failed, due to a hard error, and subsequently
**		  auto expansion policy was disabled by error handler.
{
*/
sp_dbxt_recreate_proc sp_dbxt_do_extend_db_on_dev
go

create procedure sp_dbxt_do_extend_db_on_dev (
					 @dbname	varchar(256)
				       , @segmentname	varchar(256)
				       , @segid		int
				       , @free_space	int
				       , @devname	varchar(256)
				       , @altersize_MB	float output
				       , @token		numeric(38,0) output
				       , @simulate	int = 1
				       , @trace		int = 0
) as
begin
	declare @whoami		varchar(30)
	      , @sybprocdb	varchar(14)
	      , @whoami_fn	varchar(32)
	      , @sqlstr		varchar(256)
	      , @currdbname	varchar(30)
	      , @procname	varchar(300)
	      , @alterdb_cmd	varchar(14)
	      , @altersize_lpgs	int
	      , @before_size_MB	float
	      , @new_altersize_MB	float
	      , @after_size_MB	float
	      , @lpgs_per_MB	int	-- # of logical pages / MB
	      , @retval		int
	      , @minalter_MB	float
	      , @old_lct	int	-- LCT for logsegment before alter db
	      , @new_lct	int	-- LCT for logsegment after alter db
	      , @old_lct_free_pages
	      			int	-- logsegment free pages before alter db
	      , @new_lct_free_pages
	      			int	-- logsegment free pages after alter db
	      , @dbxt_appln	varchar(30)

		-- All size in number of logical pages
	      , @disk_size_lpgs		int
	      , @used_size_lpgs		int
	      , @free_size_lpgs		int

		-- All sizes in Megabytes
	      , @free_size_MB		float

	select    @lpgs_per_MB = (1024 * 1024) / @@maxpagesize
		, @retval = 0
		, @alterdb_cmd = "ALTER DATABASE"
		, @dbxt_appln = "Automatic Database Expansion"
		, @sybprocdb = "sybsystemprocs"

		-- Find out the minimum size by which an alter db can be
		-- done.
		, @minalter_MB = case @@maxpagesize
				   when 2048	then 1.0
				   else (256 * @@maxpagesize)
				   		/ (1024 * 1024)
				 end

		-- This should always be executed from 'master'.
		, @currdbname = db_name()

	select 	  @whoami_fn = object_name(@@procid, db_id(@sybprocdb))
		, @altersize_lpgs = (@altersize_MB * @lpgs_per_MB)

	select @whoami = case @trace when 0 then NULL else @whoami_fn + ": " end
	
	/*
	** Ensure that the sproc is executed from sybsystemprocs.
	** We cannot make this check here, as this procedure has to run
	** from master, as it performs an alter database.
	**
	** *** Do not delete this comment block. Keep for future reference ***
	**
	select @sqlstr = db_name()
	if (@sqlstr != @sybprocdb)
	begin
		raiserror 19232, @whoami_fn, @sybprocdb, @sqlstr
		select @altersize_MB = 0
		return 0
	end
	*/

	if (@trace = 1)
	begin
		print "%1!Request to alter database: '%2!' on disk: '%3!' for segment: '%4!' by '%5!M' (%6! logical pages) (@simulate: %7!)"
			, @whoami
			, @dbname, @devname, @segmentname
			, @altersize_MB , @altersize_lpgs
			, @simulate
	end

	-- If we are dealing with very small #s, the remnant piece that we
	-- were requested for will be very small; e.g. '.10000000000000009M'
	-- If the requested size is less than the min size by which alter db
	-- will succeed, then bail out, doing nothing.
	--
	if (@altersize_MB < @minalter_MB)
	begin
		-- Report back that we did no alters.
		if (@trace = 1)
		begin
			print "%1!Skip altering db '%2!' segment '%3!' on device '%4!' as increment requested '%5!M' is less than min alter db size '%6!M' for this page size %7! bytes."
				, @whoami , @dbname , @segmentname, @devname
				, @altersize_MB, @minalter_MB
				, @@maxpagesize
		end
		select @altersize_MB = 0
		--
		return 1
	end

	-- Deal with floats that appear like: '1.1000000000000001M'
	-- For now, just round up to the next integer.
	-- IF we are already at the min, cool. Otherwise, round-up.
	--
	if (@altersize_MB != @minalter_MB)
	begin
		select @altersize_MB = ceiling(@altersize_MB)
	end

	-- Acquire the 'before' size of the segment, and convert to MB units.
	exec @before_size_MB = sp_dbxt_segsize @dbname, @segid, @simulate
	select @before_size_MB = @before_size_MB / @lpgs_per_MB

	-- If you are altering the logsegment, use the LOG ON clause.
	-- Otherwise, simply use the ON clause to alter the data segment.
	--
	select @sqlstr = @alterdb_cmd + " "
			+ @dbname
			+ case @segmentname
			   when "logsegment" then " log on "
			   else			  " on "
			  end
			+ @devname
			+ " = '"
			+ convert(varchar, @altersize_MB)
			+"M'"

	print "%1! -- Segment: %2!" , @sqlstr, @segmentname -- , @simulate

	if (@simulate != 1)	-- The real thing at run-time.
	begin	-- {

		-- Collect lct/free page info before the alter db.
		if (@segmentname = 'logsegment')
		begin
			select @procname = @dbname + ".dbo.sp_dbxt_get_lct"
			exec @old_lct = @procname 'reserve'
			exec @old_lct_free_pages
					= @procname 'logsegment_freepages'
		end

		-- ***********************************************************
		-- The actual operation starts here.

		-- Tag the segment we are working on as the clienthostname,
		-- so that we can see it in sysprocesses.
		--
		SET clienthostname @segmentname

		-- ***********************************************************
		-- Register yourself in the control table and receive a token.

		exec @retval = sp_dbxt_synchronize_expansion
					  @dbname
					, @segmentname
					, @alterdb_cmd
					, NULL
					, @free_space
					, @token output
					, @simulate
					, @trace

		-- Usually should not fail. But check error code anyway.
		if (@retval != 0)
		begin
			SET clienthostname NULL
			select @altersize_MB = 0
			return 2
		end

		-- **********************************************************
		-- The space available on this device might have changed
		-- since the time we checked it in our caller as we just
		-- had to wait for access to run this command. Check the
		-- space available again, and if it is insufficient, bail
		-- out. Skip this device, or we will get an 1816 error.

		exec @disk_size_lpgs = sp_dbxt_devsize_lpgs @devname
							, @simulate, @trace
		exec @used_size_lpgs = sp_dbxt_usedsize_lpgs @devname
							, @simulate, @trace

		select @free_size_lpgs = (@disk_size_lpgs - @used_size_lpgs)

		-- DBXT_FUTURE: Use sp_dbxt_lpages_2_MB routine.
		select @free_size_MB = (convert(float, @free_size_lpgs)
							/ @lpgs_per_MB)

		-- If there is no more room on this device, skip this one.
		--
		if ( @free_size_MB >= @altersize_MB )
		begin
			exec @retval = sp_exec_SQL @sqlstr, @whoami_fn
		end
		else
		begin
			SET clienthostname NULL

			-- Did not run the alter db command. Bail out.
			select @altersize_MB = 0
			return 0
		end

		-- Reset these synchronization tags before exiting.
		SET clienthostname NULL

		-- If there were some errors in executing the statement,
		-- pretend that the alter failed, and return 0 for the
		-- size by which the alter db was done.
		--
		if (@retval != 0)
		begin
			select @altersize_MB = 0

			-- Handle command-specific error conditions.
			--
			exec @retval = sp_dbxt_handle_error @retval
						, @dbname
						, @segmentname
						, @currdbname
						, @trace

			-- We have to return a non-zero return status as
			-- there was some error. For 'handled' errors, return
			-- 1 to indicate that caller can continue searching
			-- for more places to expand on. For 'unhandled'
			-- errors (usually hard errors), return 2 so that
			-- caller can abort the expansion process right away.
			--
			return (case @retval
				  when 0 then  0	-- all ok
				  when 1 then  3	-- unhandled hard errors
				  when -1 then 2	-- internal errors
				  	 else  2	-- unknown errors
				end)
		end

		-- Acquire the 'after' size of the segment in # logical pages,
		-- and then convert to MB units.
		--
		exec @after_size_MB = sp_dbxt_segsize @dbname, @segid, @simulate
		select @after_size_MB = @after_size_MB / @lpgs_per_MB

		-- The size we actually were successfully able to alter
		-- the database, and hence, this segment by.
		--
		select @altersize_MB = @after_size_MB - @before_size_MB
		select @altersize_lpgs = (@altersize_MB * @lpgs_per_MB)

		-- Check what the old and new LCT values were after this
		-- alter on the log segment. Only do this if it is the real
		-- background threshold procedure. Otherwise, in user-driven
		-- execution mode, the LCT will not have really changed, so
		-- we will raise unnecessary / inapplicable errors.
		--
		if ((@segmentname = 'logsegment') and (@simulate = 0))
		begin	-- {

			select @procname = @dbname + ".dbo.sp_dbxt_get_lct"
			exec @new_lct = @procname 'reserve'
			exec @new_lct_free_pages
					= @procname 'logsegment_freepages'

			print "%1!'%2!' logsegment LCT moves from %3! to %4! logical pages. Free pages move from %5! to %6! logical pages."
				, @whoami , @dbname
				, @old_lct, @new_lct
				, @old_lct_free_pages, @new_lct_free_pages

			-- **************************************************
			-- IF the new LCT is within hysteresis range of the
			-- threshold level which caused this operation to fire
			-- then this threshold would have been disabled.
			-- Cope with that by moving up the free space value
			-- for this threshold. To be sure, check the space
			-- in relation to the lct, and also the internal
			-- status in the threshold cache. (See helpthreshold.)
			--
			if ((@free_space <= @new_lct + 2 * @@thresh_hysteresis)
			and ((lct_admin('status_in_cache', @segid,
					@free_space) & 9) != 8)
			   )
			begin
				select @procname
					= @dbname
					  + ".dbo.sp_dbxt_modify_threshold"

				exec @procname
						  @dbname
						, @segmentname
						, @free_space
						, @old_lct
						, @new_lct
						, @altersize_lpgs
						, @simulate
						, @trace
			end

		end	-- }
	end	-- }

	else	-- Dry-run mode. Only simulation going on here.

	begin	-- {
		-- Reset these synchronization tags before exiting.
		set clientname NULL
		set clienthostname NULL

		-- Pretend that the alter did succeed. So the after size
		-- would be 'before' size plus the alter size.
		--
		select @after_size_MB = @before_size_MB + @altersize_MB

		select  @altersize_lpgs = (@altersize_MB * @lpgs_per_MB)

		-- Now that we have allocated some space for this db from
		-- this device, update the available space counter in the
		-- #temp table
		--
		update #segdev
		set 	  devfreesize_lpgs = devfreesize_lpgs - @altersize_lpgs
			, devusedsize_lpgs = devusedsize_lpgs + @altersize_lpgs
		where devicename = @devname
		if (@@error != 0)
		begin
			select @altersize_MB = 0
			return  2
		end
	end	-- }

	if (@trace = 1)
	begin
		print "%1!Db '%2!' altered by '%3!M' (%4! logical pages)",
			@whoami, @dbname, @altersize_MB, @altersize_lpgs
	end

	-- Return what we actually extended db by.
	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

/* ===========================================================================
** sp_dbxt_resize_devices_fordb()
**
** Driver procedure that will try to figure out the devices that are already
** allocated to a given (db, segment) and will first try to resize each
** device, and then will try to alter the db on that resized device. We
** work through all devices allocated to the current (db, segment) till
** we are able to fulfill the request increment size to alter the db by.
**
** Parameters:
**	@dbname		- Database name for which we wish to do the alter db.
**	@segmentname	- Segment that needs more space. (== @segid))
**	@segid		- Segment ID for which the threshold has fired.
**	@free_space	- Free space level at which threshold fired.
**	@incr_size_MB	- Total amount of increment that we want to provide.
**	@simulate	- Type code for execution mode. (See caller.)
**	@trace		- Debug flag. 0: No trace info. 1: Print trace info
**
** Output Parameters:
**	@incr_size_MB	- Amount left over to alter db by.
**	Left-over increment size (MB) that we were unable to account for by
**	just doing alter db of the db on devices that are already allocated
**	to this db. If we found enough room on newly-resized devices, then
**	the return size will be 0.
**
** Returns:
**	0	- Disk resize/alter db were successful.
**	1	- Some failures either in disk resize or alter db.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_resize_devices_fordb
go

create procedure sp_dbxt_resize_devices_fordb (
				  @dbname	varchar(256)
				, @segmentname	varchar(256)
				, @segid	int
				, @free_space	int
				, @incr_size_MB	float output
				, @token 	numeric(38,0) output
				, @simulate	int = 1
				, @trace	int = 0
) as
begin
	declare @whoami			varchar(30)
	      , @devname		varchar(256)
	      , @disk_resize_MB		float
	      , @incr_size_MB_dev	float	-- size incremented on one dev
	      , @segbit			int	-- bitmask for segment id
	      , @retval			int
	      , @fn_retval		int	-- from sproc.

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
	if (@trace = 1)
	begin
		print "%1!Resize devices for db: '%2!', segment: '%3!' by total size: '%4!M'",
			@whoami, @dbname, @segmentname, @incr_size_MB
	end

	exec @segbit = sp_dbxt_segment_to_segbit @segid
	select    @fn_retval 	= 0
		, @retval	= 0

	-- Find out each disk piece that belongs to this segment.
	--
	-- Order the output by increasing order of TOTAL SIZE of each disk
	-- so that if the thresholds fire repeatedly, on every new
	-- attempt to resize disks, we pick the smallest one(s) first
	-- and resize them. Initially, if disks were all of the same
	-- size, then this scheme will just cycle through disks till
	-- each is resized. If initially disks were not of the same
	-- sizes, this scheme will start sizing up the smallest ones
	-- till it becomes larger than the initially-larger disk. Then
	-- that disk will be resized, and we move lock-step through the
	-- series of disks. This scheme has some form of stability when
	-- we don't know what layout of disks or sizes we might be
	-- confronted with.
	--
	if (@simulate = 1)	-- Process cached contents from #temp table
	begin
		if (@trace = 1)
		begin
			exec sp_dbxt_show_segmaps
				@whoami , NULL , "order by devsize_lpgs asc"
		end

		declare segdevcur cursor for
		select sd.devicename
		from #segdev sd
		where (sd.segmaps & @segbit) = @segbit
		order by devsize_lpgs asc
	end
	else
	begin
		-- Cache the required data into a #temp table and cursor
		-- through it. Cache at isolevel 0 to avoid causing any
		-- blocking to concurrent activity. This information does
		-- not change very rapidly, so there is little risk of it
		-- getting stale.
		--
		-- First cache the names of all devices that this db spans
		-- on.
		--
		select    d.name
			, d.high
			, d.low
		into #devices_used_info
		from 	  master.dbo.sysdevices d
			, master.dbo.sysusages u
		where u.vdevno = d.vdevno
		  and d.status & 2 = 2		-- only get physical devices
		  and (u.segmap & @segbit) = @segbit
		  and u.dbid = db_id(@dbname)

		-- Then, declare a cursor to get unique device names, so
		-- that we process each device only once.
		--
		declare segdevcur cursor for
		select distinct d.name
		from #devices_used_info d
		order by (d.high - d.low) asc
	end

	open segdevcur

	fetch segdevcur into @devname

	-- ****************************************************************
	-- While there are more candidate disks that we can alter this db
	-- on, and if we have still have some more increments to go:
	--
	-- NOTE:	We just make one pass through sysdevices for all
	--		disks that are already allocated to this segment.
	-- ****************************************************************
	--
	-- Even if we get an error on one device in this loop, just skip
	-- that one and move on with other devices. Don't worry about aborting
	-- the whole opertion because of one bad apple. On the other hand, if
	-- some callee has turned off auto expansion, they report that back
	-- as 2, in which case, bail out of this exercise.
	--
	while ((@retval < 2) and (@@sqlstatus = 0) and (@incr_size_MB > 0) )
	begin	-- {

		-- Resize this particular disk to what it can grow to,
		-- per the site-specific rules for resizing disk.
		--
		exec @retval = master.dbo.sp_dbxt_do_resize_dev
						  @devname
						, @disk_resize_MB output
						, @dbname
						, @segmentname
						, @free_space
						, @token output
						, @simulate
						, @trace

		-- If we succeeded in resizing this device, and the device
		-- was resized by some non-zero amount...
		--
		if ((@retval = 0) and (@disk_resize_MB > 0))
		begin 	-- {

			if ((@simulate = 1) and (@trace = 1))
			begin
				exec sp_dbxt_show_segmaps
					"After sp_dbxt_do_resize_dev:"
					, @devname
			end

			-- We might have resized device by 5M whereas the
			-- initial request to alter this db was a total of
			-- 50M. In that case, only ask to alter this db on
			-- this device by the size that the device was resized.
			-- Otherwise (at run-time and mainly in simulate mode)
			-- we will mistakenly 'alter' the db on this device
			-- by a very large size, bringing the free size
			-- counter in #segdev to a negative value.
			--
			select @incr_size_MB_dev =
				case
				  when (@disk_resize_MB < @incr_size_MB)
				  then @disk_resize_MB
				  else @incr_size_MB
				end

			-- Sproc returns the actual size by which an extension
			-- of this db was done on this device.
			--
			exec @retval = master.dbo.sp_dbxt_do_extend_db_on_dev
						  @dbname
						, @segmentname
						, @segid
						, @free_space
						, @devname
						, @incr_size_MB_dev output
						, @token output
						, @simulate
						, @trace

			if ((@simulate = 1) and (@trace = 1))
			begin
				exec sp_dbxt_show_segmaps
					  "After sp_dbxt_do_extend_db_on_dev:"
					, @devname
			end

			-- Reduce the amount left only if we succeeded.
			if (@retval = 0)
			begin
				select @incr_size_MB = @incr_size_MB -
							@incr_size_MB_dev
			end

		end	-- }

		select @fn_retval = @fn_retval + @retval
		fetch segdevcur into @devname

	end	-- } while()

	close segdevcur

	deallocate cursor segdevcur

	-- Return remaining size to increment (if any).
	return @fn_retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

/* ===========================================================================
** sp_dbxt_extend_db_on_this_dev()
**
** Extend the database on the selected Sybase device. This procedure
** implements the basic work of figuring out disk size (used/free) and
** if there is sufficient space on this disk, it will call the work-horse
** sproc to do the actual 'alter database' command.
**
** Parameters:
**	@dbname		- Database name for which we wish to do the alter db.
**	@segmentname	- Segment that needs more space.
**	@segid		- Segment ID for above segment.
**	@free_space	- Free space level at which threshold fired.
**	@incr_size_MB	- Total amount of increment that we want to provide.
**	@devname	- Name of device on which to consider an alter db.
**	@simulate	- Type code for execution mode. (See caller.)
**	@trace		- Debug flag. 0: No trace info. 1: Print trace info
**
** Output Parameters:
**	@incr_size_MB	- Total increment still left to be done.
**	Amount of increment size (in Mb) that we were unable to handle by
**	doing the alter database on _this_ (one) device.
**	If we found enough room on this device, then the return size will
**	for @incr_size_MB be 0.
**
**	@token		- Token received to identify this task in the
**			  control table. 
** Returns:
**	0	- If the extension attempt was successful.
**	1	- Some errors encountered during extension logic.
**	2	- (Hard?) Errors caused auto expansion to be disabled.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_extend_db_on_this_dev
go

create procedure sp_dbxt_extend_db_on_this_dev(
				     @dbname		varchar(256)
				   , @segmentname	varchar(256)
				   , @segid		int
				   , @free_space	int
				   , @devname		varchar(256)
				   , @incr_size_MB	float output
				   , @token		numeric(38,0) output
				   , @simulate		int = 1
				   , @trace		int = 0
) as
begin
	declare @whoami			varchar(256)

		-- All size in number of logical pages
	      , @disk_size_lpgs		int
	      , @used_size_lpgs		int
	      , @free_size_lpgs		int

		-- All sizes in Megabytes
	      , @disk_size_MB		float
	      , @used_size_MB		float
	      , @free_size_MB		float
	      , @incr_size_MB_dev	float	-- on a particular disk

		-- Min size by which an alter db can be done on a single
		-- disk piece
	      , @min_altsize_MB		float

	      , @incr_size_lpgs	int	-- back from MB to # logical pages
	      , @megabytes	int	-- # of bytes in a MB
	      , @nlpgsper_MB	int	-- # of logical pages per MB
	      , @retval		int


	-- Build conversion from # of logical pages to MB.
	-- 
	select @megabytes = (1024 * 1024)
	select @nlpgsper_MB = (@megabytes / @@maxpagesize)

	-- Minimum size by which an alter db can be performed is 
	-- min( 1 AU, or 1 Mb).
	--
	select @min_altsize_MB = case @@maxpagesize
				   when 2048 then 1
				   else (256 * @@maxpagesize) / @megabytes
				end

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
	if (@trace = 1)
	begin
		print "%1!Db: '%2!' Segment: '%3!' Device: '%4!' incr_size_MB '%5!M'",
			@whoami,
			@dbname, @segmentname,
			@devname, @incr_size_MB
	end

	exec @disk_size_lpgs = sp_dbxt_devsize_lpgs @devname, @simulate, @trace
	exec @used_size_lpgs = sp_dbxt_usedsize_lpgs @devname, @simulate, @trace

	select @free_size_lpgs = (@disk_size_lpgs - @used_size_lpgs)

	-- DBXT_FUTURE: Use sp_dbxt_lpages_2_MB routine.
	select    @disk_size_MB = (convert(float, @disk_size_lpgs)
						/ @nlpgsper_MB)
		, @used_size_MB = (convert(float, @used_size_lpgs)
						/ @nlpgsper_MB)
		, @free_size_MB = (convert(float, @free_size_lpgs)
						/ @nlpgsper_MB)

	-- If there is no more room on this device, skip this one.
	--
	if ( @free_size_MB < @min_altsize_MB )
	begin
		if (@trace = 1)
		begin
			print "%1!Device '%2!' has insufficient free size (%3!M) to alter db by minimum size of %4!M",
				@whoami,
				@devname, @free_size_MB, @min_altsize_MB
		end

		-- The whole amount is still left to be allocated.
		return 0
	end

	-- There is room on this disk, consider it for an alter.
	--
	if (@trace = 1)
	begin
		print "%1!Segment: '%2!' Device '%3!' has total size (in logical pages): '%4!' ('%5!M'), used size: '%6!' ('%7!M'), free size: '%8!' ('%9!M')",
			@whoami,
			@segmentname, @devname,
			@disk_size_lpgs, @disk_size_MB,
			@used_size_lpgs, @used_size_MB,
			@free_size_lpgs, @free_size_MB
	end

	-- If this disk has enough free space for the total
	-- increment, use that for the size to alter db by.
	-- Otherwise, only alter up to as much free space
	-- as there is on this disk.
	--
	select @incr_size_MB_dev
			= case
			     when (@free_size_MB >= @incr_size_MB)
			     then @incr_size_MB
			     else @free_size_MB
			   end

	-- Do the actual alter db on the chosen disk.
	exec @retval = master.dbo.sp_dbxt_do_extend_db_on_dev
					  @dbname
					, @segmentname
					, @segid
					, @free_space
					, @devname
					, @incr_size_MB_dev output
					, @token output
					, @simulate
					, @trace

	-- Reduce size-left-to-increment by what we extended.
	-- Above sproc returns the actual size of the increment that was done.
	--
	select @incr_size_MB = (@incr_size_MB - @incr_size_MB_dev)

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

/* ===========================================================================
** sp_dbxt_extend_db_all_devices()
**
** Driver procedure that will try to figure out the devices that are already
** allocated to a given (db, segment) and will first try to alter the db
** on those disks to satisfy the increment size that we need to provide for
** this database.
**
** Parameters:
**	@dbname		- Database name for which we wish to do the alter db.
**	@segmentname	- Segment that needs more space. (== @segid))
**	@segid		- Segment ID for which the threshold has fired.
**			  (output parameter also)
**	@free_space	- Free space level at which threshold fired.
**	@incr_size_MB	- Total amount of increment that we want to provide.
**	@simulate	- Type code for execution mode. (See caller.)
**	@trace		- Debug flag. 0: No trace info. 1: Print trace info
**
** Output Parameters:
**	@incr_size_MB	- Total increment still left to be done.
**	Left-over increment size that we were unable to account for by just
**	doing alter db of the db on devices that are already allocated to this
**	db. If we found enough room on already-allocated devices, then the
**	return size will be 0.
**
** Returns:
**	0	- If the extension attempt was successful.
**	1	- Some errors encountered during extension logic. Errors could
**		  also imply certain conditions (e.g. increment size < min
**		  alter db size) occured that make it unnecessary to continue
**		  with the expansion process.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_extend_db_all_devices
go

create procedure sp_dbxt_extend_db_all_devices (
				  @dbname	varchar(256)
				, @segmentname	varchar(256)
				, @segid	int
				, @free_space	int
				, @incr_size_MB	float output
				, @token 	numeric(38,0) output
				, @simulate	int = 1
				, @trace	int = 0
) as
begin
	declare @whoami		varchar(256)
	      , @devname	varchar(256)
	      , @segbit		int		-- bitmask for segment id
	      , @retval		int
	      , @fn_retval	int		-- from function.
	      , @alterdb_cmd	varchar(14)
	      , @dbxt_appln	varchar(30)


	exec @segbit = sp_dbxt_segment_to_segbit @segid
	select	  @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
		, @alterdb_cmd = "ALTER DATABASE"
		, @dbxt_appln = "Automatic Database Expansion"
	
	if (@trace = 1)
	begin
		print "%1!Extend db:'%2!' on segment '%3!' (%4!) by '%5!M'",
			@whoami,
			@dbname, @segmentname, @segid,
			@incr_size_MB
	end

	-- Find out each device piece that belongs to this segment.
	-- Use 'distinct' to eliminate duplicate logical devices when
	-- the same db gets altered on the same logical device multiple
	-- times. All we want to get here is just one logical device,
	-- and then we will chase that down to see if it has room for
	-- this db to be altered on to it.
	--
	-- DBXT_FUTURE: We need to implement some smart search algorithms
	--		here to implement some form of best-fit or first-fit
	--		search strategies. This can help quickly identify the
	--		those devices that already have sufficient space on
	--		them for the current request.
	--
	if (@simulate = 1)
	begin
		if (@trace = 1)
		begin
			exec sp_dbxt_show_segmaps
				@whoami, NULL, "order by devfreesize_lpgs desc"
		end

		declare segdevcur cursor for
		select sd.devicename
		from #segdev sd
		where (sd.segmaps & @segbit) = @segbit
		order by devfreesize_lpgs desc
	end
	else
	begin
		-- Cache the required data into a #temp table and cursor
		-- through it. Cache at isolevel 0 to avoid causing any
		-- blocking to concurrent activity. This information does
		-- not change very rapidly, so there is little risk of it
		-- getting stale.
		--
		-- First cache the names of all devices that this db spans
		-- on.
		--
		select    d.name
			, d.high
			, d.low
		into #devices_used_by_db
		from 	  master.dbo.sysdevices d
			, master.dbo.sysusages u
		where u.vdevno = d.vdevno
		  and d.status & 2 = 2		-- only get physical devices
		  and (u.segmap & @segbit) = @segbit
		  and u.dbid = db_id(@dbname)

		-- DBXT_FUTURE: Not the same clause as for dry-run. OK for now.
		-- We would like to ORDER BY the amount of free space on each
		-- device, but that is a computed value. It gets a bit tricky
		-- to run this in one query. We would have to save the value(s)
		-- to another #temp table, and then ORDER BY on that to get
		-- exactly what would happen during the simulation. For now,
		-- it is probably ok to just ORDER BY the total size descending
		-- in the hope that larger devices probably have more free
		-- space.
		--
		-- Then, declare a cursor to get unique device names, so
		-- that we process each device only once.
		--
		declare segdevcur cursor for
		select d.name
		from #devices_used_by_db d
		order by (d.high - d.low)
	end

	open segdevcur

	fetch segdevcur into @devname

	select @fn_retval = 0, @retval = 0

	-- While there are more candidate disks that we can alter this db
	-- on, and if we have still have some more increments to go, alter
	-- the db on all candidate devices. Even if the alter process were
	-- to fail on one device, don't abort. Continue and try the other
	-- ones, except in case of very hard errors when auto expansion.
	-- is disabled by error handler.
	--
	while ((@retval <= 2) and (@@sqlstatus = 0) and (@incr_size_MB > 0) )
	begin
		if ((@simulate = 1) and (@trace = 1))
		begin
			exec sp_dbxt_show_segmaps
				"Before sp_dbxt_extend_db_on_this_dev"
				, @devname
		end

		-- Each call reduces @incr_size_MB by amount of extension.
		--
		exec @retval = sp_dbxt_extend_db_on_this_dev
							  @dbname
							, @segmentname
							, @segid
							, @free_space
							, @devname
							, @incr_size_MB output
							, @token output
							, @simulate
							, @trace

		if ((@simulate = 1) and (@trace = 1))
		begin
			exec sp_dbxt_show_segmaps
				  "After sp_dbxt_extend_db_on_this_dev:"
				, @devname
		end

		select @fn_retval = @fn_retval + @retval
		fetch segdevcur into @devname
	end

	close segdevcur

	deallocate cursor segdevcur

	return (case @fn_retval when 0 then 0 else 1 end)
end
go	-- }

if (@@error != 0) select syb_quit()
go

/*
** sp_dbxt_do_extend_db()
**
** Driver procedure that performs extend operation by first checking
** whether candidate devices have space on them. Alter the db on those
** first. Then, attempt to re-size candidate devices which already have
** database fragments on it. And, follow that by trying to alter the
** database on those devices.
**
** Parameters:
**	@dbname		Name of the database where threshold fired
**	@segmentname	Segment where space was running low
**	@segid		ID of the above segment from syssegments.
**	@space_left_lpgs Space left in this segment in # of logical pages
**	@status 	0/1: Whether it is a last-chance threshold
**	@simulate	Type code for execution mode. (See caller.)
**	@trace		0/1: Debugging hook.
**
** Output Parameters:
**	@incr_size_MB	Size, in MB, by which the db/segment was extended.
**
** Returns:
**	0	- If extension was successful w/o errors.
**	1	- Some errors occured during extension process.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_do_extend_db
go

create procedure sp_dbxt_do_extend_db (
			  @dbname		varchar(256)
			, @segmentname		varchar(256)
			, @segid		int
			, @space_left_lpgs	int = 0
			, @status 		int = 0		-- lct or not?
			, @incr_size_MB		float	output
			, @simulate		int = 1
			, @trace		int = 0
) as 
begin
	declare @whoami		varchar(30)	-- Proc name tag for tracing
	      , @user_name 	varchar(256)
	      , @retval		int
	      , @user_id 	int
	      , @suser_id 	int
	      , @suser_name	varchar(256)	-- login name
	      , @in_dbid	int		-- Proc fired in this db
	      , @in_dbname	varchar(256)	-- With this dbname
	      , @space_left_MB	float		-- Space left in Mb
	      , @incr_size_MB_orig
	      			float
	      , @token		numeric(38,0)

	select    @space_left_MB = ((convert(float, @space_left_lpgs)
					* @@maxpagesize) /
						(1024 * 1024) )
		, @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
		, @token = 0	-- Initialize to illegal value.
	
	if (@trace = 1)
	begin
		-- Get the identity of the user/login that is inherited when
		-- this thresholdaction sproc is fired.
		--
		select    @user_name 	= user_name()
			, @user_id 	= user_id()
			, @suser_id 	= suser_id()
			, @suser_name	= suser_name()
			, @in_dbid	= db_id()
			, @in_dbname	= db_name()

		print "%1!Thresholdaction proc fired in db: %2! ('%3!') for db: '%4!', segment: '%5!' to extend db. Space left: %6! logical pages ('%7!M')",
			@whoami,
			@in_dbid, @in_dbname,
			@dbname, @segmentname, @space_left_lpgs, @space_left_MB

	 	print "%1!Thresholdaction proc fired by user: '%2!' with user_id: %3! and suser_id: %4! ('%5!'). Simulate: %6!"
	 		, @whoami, @user_name
			, @user_id, @suser_id, @suser_name
			, @simulate
	end

	/*
	** ******************************************************************
	** Here is where the site-specific policy comes into play.
	** Once we know that a particular (db, segment) is running out of
	** room, each site can lookup the size by which the db should be
	** extended for this particular segment. This could be done via a
	** lookup in some policy table, perhaps, in master db.
	** For now, we pretend that there is a policy somewhere, and look
	** it up via a call to a sproc that will eventually be implemented
	** as the policy at the customer's site.
	** ******************************************************************
	*/
	if exists (select 1 from sysobjects
		   where id = object_id('dbo.sp_dbxt_get_alterdb_size') )
	or exists (select 1 from sybsystemprocs.dbo.sysobjects
		   where id = object_id('sybsystemprocs.dbo.sp_dbxt_get_alterdb_size') )
	begin
		exec @retval = sp_dbxt_get_alterdb_size
						  @dbname
						, @segmentname
						, @incr_size_MB output
						, @simulate
						, @trace
	end
	else
	begin
		if (@trace = 1)
		begin
			print "%1!Using default values for aux_get_alterdb_size",
				@whoami
		end

		select @incr_size_MB = @space_left_MB
	end

	if (@retval != 0)
	begin
		-- Callee has guaranteed that @incr_size_MB == 0
		return 1
	end

	-- ******************************************************************
	-- Bail out right at the top if a requested increment is negative.
	-- This might happen if:
	--
	--  . the user has explicitly growby to NULL or 0.
	--  . server-wide auto-expansion is disabled.
	--  . auto expansion is disabled for this db/segment.
	--
	-- This allows the other sprocs to work with valid values.
	--
	if (@incr_size_MB <= 0)
	begin
		if (@trace = 1)
		begin
			print "%1!Ignore invalid increment size %2! received.",
				@whoami, @incr_size_MB
		end
		select @incr_size_MB = 0
		return 0
	end

	-- Save value for final reporting if we failed eventually.
	select @incr_size_MB_orig = @incr_size_MB

	-- Try to extend the database on all the existing devices it is
	-- already using. This process will return the balance of the
	-- increment after doing whatever 'alter database' is possible.
	--
	exec @retval = sp_dbxt_extend_db_all_devices
					  @dbname
					, @segmentname
					, @segid
					, @space_left_lpgs
					, @incr_size_MB output
					, @token output
					, @simulate
					, @trace

	  -- If we still have an increment left-over, it means that all
	  -- the existing devices on which the (database, segment) lives
	  -- do not have sufficient room to satisfy the initial increment
	  -- request. Try to satisfy remaining request by resizing
	  -- existing devices. (We DO NOT allow @retval to be non-zero from
	  -- above, which means the alter db stage found one or more problems.
	  -- Abort the expansion process right now, to avoid more errors or
	  -- doing unnecesary DISK RESIZE (e.g.) when the alter db increment
	  -- is too small.
	  --
	  if ( (@retval = 0) and (@incr_size_MB > 0))
	  begin
		exec @retval = sp_dbxt_resize_devices_fordb
						  @dbname
						, @segmentname
						, @segid
						, @space_left_lpgs
						, @incr_size_MB output
						, @token output
						, @simulate
						, @trace
	end

	-- ***************************************************************
	-- Now that we are done with this operation, delete our entry from
	-- the control table, so we let others proceed ahead. Add a defensive
	-- check in case of any error(s) in percolating output back. We don't
	-- want to mistakenly conclude that the delete failed.
	--
	if (@token != 0)
	begin
		delete tempdb.dbo.syb_auto_db_extend_control
		where dbname 		= @dbname
		  and segmentname 	= @segmentname
		  and spid		= @@spid
		  and token		= @token

		if ((@@error != 0) or (@@rowcount != 1))
		begin
			print "%1!Error: Could not remove entry for auto-extension operation ID: %2!. DB: %3! Segment: %4! Spid: %5! (%6! rows deleted, expected %7!.)"
				, @whoami , @token
				, @dbname, @segmentname, @@spid
				, @@rowcount, 1
		end
	end

	-- If there is still some increment left-over, report to user.
	-- We might have acquired some amount(s) already, which might
	-- allow the application to continue. Here all we are doing is
	-- reporting that the site-specific request could not be met
	-- fully. (Probable causes are, for instance, the OS-disk ran
	-- out of room to accomodate all disk resizes.)
	--
	if ( (@incr_size_MB > 0) and (@trace = 1) )
	begin
		print "%1!Db: '%2!' Segment: '%3!': Failed to acquire total of '%4!M' by '%5!M'",
			@whoami,
			@dbname, @segmentname,
			@incr_size_MB_orig, @incr_size_MB
	end

	-- Return back to caller the actual amount by which extension was done.
	--
	select @incr_size_MB = @incr_size_MB_orig - @incr_size_MB

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

drop table #segdev
go

go
/*
** Generated by spgenmsgs.pl on Wed Aug 13 17:45:25 2003 
*/
/*
** raiserror Messages for dbxt_check [Total 8]
**
** 17871, "There is no segment named '%1!'."
** 19199, "You must be in database '%1!' to run this command. Issue: 'USE %2!', then run this procedure again."
** 19219, "Database '%1!', segment '%2!' is configured for auto expansion at a free space of %3! logical pages ('%4!M') which is less than or within the hysteresis region of the current last-chance threshold at %5! logical pages ('%6!M')."
** 19220, "Auto-expansion threshold procedure for database '%1!', segment '%2!' already exists at a free space of %3! pages ('%4!M'). Use the CLEAR sub-command to clear it first. Or, use the %5! procedure to drop the existing threshold procedure."
** 19221, "Usage: sp_dbextend 'check', 'database' [, @dbname [, @segmentname ] ]"
** 19223, "Segments '%1!' and '%2!' in database '%3!' are mapped to the same set of devices, and segment '%4!' is already set for auto-expansion. This makes it unnecessary to set the '%5!' segment for auto-expansion."
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19253, "Free space accounting is currently disabled for database '%1!'. This makes it impossible to trigger auto expansion thresholds for segment '%2!'. Use sp_dboption to set '%3!' to FALSE for this database."
*/
/*
** sp_getmessage Messages for dbxt_check [Total 5]
**
** 19219, "Database '%1!', segment '%2!' is configured for auto expansion at a free space of %3! logical pages ('%4!M') which is less than or within the hysteresis region of the current last-chance threshold at %5! logical pages ('%6!M')."
** 19220, "Auto-expansion threshold procedure for database '%1!', segment '%2!' already exists at a free space of %3! pages ('%4!M'). Use the CLEAR sub-command to clear it first. Or, use the %5! procedure to drop the existing threshold procedure."
** 19223, "Segments '%1!' and '%2!' in database '%3!' are mapped to the same set of devices, and segment '%4!' is already set for auto-expansion. This makes it unnecessary to set the '%5!' segment for auto-expansion."
** 19252, "Consider installing the auto expansion procedure at a free space value sufficiently above that of the current last-chance threshold, separated from it by more than %1! pages."
** 19253, "Free space accounting is currently disabled for database '%1!'. This makes it impossible to trigger auto expansion thresholds for segment '%2!'. Use sp_dboption to set '%3!' to FALSE for this database."
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_check ..."
go

use sybsystemprocs
go


	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, free_space, status, proc_name, suid, currauth
	into #systhresholds lock allpages
	from systhresholds
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_check_multiple_thprocs
go

/*
** ***************************************************************************
** sp_dbxt_check_multiple_thprocs
**
{
*/
create procedure sp_dbxt_check_multiple_thprocs(
				  @dbname		varchar(256)
				, @segmentname		varchar(256)
				, @raiserror		int
				, @freespace_nlpgs	int
				, @trace		int = 0
) as
begin
	declare	@th_at_free_space	int
	      , @th_at_free_space_MB	float
	      , @freespace_nlpgs_MB	float
	      , @thprocname		varchar(30)
	      , @sqlmsg			varchar(256)

	select @thprocname = 'sp_dbxt_extend_db'

	-- *************************************************************
	-- Restrict the user from installing more than one threshold
	-- proc on the same (db, segment) pair. This is just a first-cut
	-- simplification. Not clear if there is great value in allowing
	-- multiple threshold procs for auto-expansion to be permitted.
	--
	select @th_at_free_space = t.free_space
	from #systhresholds t
	where t.proc_name = @thprocname
	  and t.dbname = @dbname
	  and t.segment = (select s.segment
	  		   from #syssegments s
			   where s.name = @segmentname
			     and s.dbname = @dbname
			  )

	-- Raise an error if we found another space expansion threshold
	-- at a different free space value.
	--
	if (@th_at_free_space IS NOT NULL)
	 and (@th_at_free_space != @freespace_nlpgs)
	begin
		exec sp_dbxt_lpages_2_MB  @th_at_free_space
					, @th_at_free_space_MB output

		if (@raiserror = 1)
		begin
			raiserror 19220 , @dbname
					, @segmentname
					, @th_at_free_space
					, @th_at_free_space_MB
					, "sp_dropthreshold"
		end
		else
		begin
			exec sp_getmessage 19220, @sqlmsg output
			print @sqlmsg   , @dbname
					, @segmentname
					, @th_at_free_space
					, @th_at_free_space_MB
					, "sp_dropthreshold"
			print " "
		end
		return 1
	end
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_get_lct
go

/*
** ***************************************************************************
** sp_dbxt_get_lct
**
** Retrieve the current LCT for the log-segment in the db that this sproc
** is executing.
**
** Parameters:
**	@lct_param	- LCT parameters to get free space etc.
{
*/
create procedure sp_dbxt_get_lct (@lct_param	varchar(30)
) as
begin
	declare @retval	int

	select @retval = case @lct_param
			  when 'reserve'
			  	then lct_admin(@lct_param, 0)
			  when 'logsegment_freepages'
			  	then lct_admin(@lct_param, db_id())
			  else  0
			end

	return @retval
end
go	/* } */

exec sp_dbxt_recreate_proc sp_dbxt_check_logsegment_lct
go

/*
** ***************************************************************************
** sp_dbxt_check_logsegment_lct
**
** Check that the freespace value at which the auto-expansion threshold
** proc is installed for the logsegment is kosher with the current LCT
** value for the log segemnt.
**
** Parameters:
**	@freespace_nlpgs	- Free space (# logical pages) at which the
**				  threshold proc was installed.
**	@curr_lct_lpgs		- Current LCT in # logical pages.
**	@raiserror		- Boolean: 1 - raiserror, 0 - warning.
**	@trace			- Boolean: 0 - No tracing; 1 - Trace
**
** Returns:
**	0	- If threshold position is kosher with LCT.
**	1	- Prints a warning message and returns 1.
{
*/
create procedure sp_dbxt_check_logsegment_lct(
				  @freespace_nlpgs	int
				, @curr_lct_lpgs	int
				, @raiserror		int	= 0
				, @trace		int	= 0
) as
begin
	declare @freespace_nlpgs_MB	float
	      , @curr_lct_lpgs_MB	float
	      , @sqlmsg			varchar(512)
	      , @currdbname		varchar(256)
	      , @lct_upper_limit	float
	      , @hyst_region		int

	select    @currdbname = db_name()
		, @hyst_region = 2 * @@thresh_hysteresis

	if (@trace = 1)
	begin
		select @lct_upper_limit = @curr_lct_lpgs + @hyst_region
		print "@curr_lct_lpgs: %1! @@thresh_hysteresis: %2! (upper limit: %3!) @freespace_nlpgs: %4!"
				, @curr_lct_lpgs
				, @@thresh_hysteresis
				, @lct_upper_limit
				, @freespace_nlpgs
	end

	-- If the user-threshold is below the LCT for the log, or is
	-- within a range of threshold hysteresis, then it would have been
	-- disabled. This is internally done by the threshold manager when
	-- there are dynamic LCTs (in mixed log-and-data) so as to avoid
	-- a race between the LCT and the user-threshold. In some situations,
	-- it might happen that the user-threshold is *not* firing, and
	-- users wonder why. Implement that check here, and report it to
	-- the user as a warning.
	--
	if (@freespace_nlpgs <= @curr_lct_lpgs + @hyst_region)
	begin
		exec sp_dbxt_lpages_2_MB  @freespace_nlpgs
					, @freespace_nlpgs_MB output

		exec sp_dbxt_lpages_2_MB  @curr_lct_lpgs
					, @curr_lct_lpgs_MB output

		if (@raiserror = 1)
		begin
			raiserror 19219
				, @currdbname
				, 'logsegment'
				, @freespace_nlpgs
				, @freespace_nlpgs_MB
				, @curr_lct_lpgs
				, @curr_lct_lpgs_MB
		end
		else
		begin
			exec sp_getmessage 19219, @sqlmsg output
			print @sqlmsg
				, @currdbname
				, 'logsegment'
				, @freespace_nlpgs
				, @freespace_nlpgs_MB
				, @curr_lct_lpgs
				, @curr_lct_lpgs_MB

			exec sp_getmessage 19252, @sqlmsg output
				, @hyst_region
			print " "
		end

		return 1
	end
	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_check_segment_disks
go

	-- *****************************************************************
	-- Create a #temp table to store the <segmentname> -> (<list of
	-- device names>) mapping.
	--
	select    db_name() as 'dbname'
		, s.name as 'segmentname', d.name as 'devicename'
	into	#segdevinfo lock allpages
	from 	  dbo.syssegments s
		, master.dbo.sysdevices d
	where 1 = 0
go

/*
** ***************************************************************************
** sp_dbxt_check_segment_disks
**
** Driver proc that validates whether a given segment spans on devices
** that are also using exactly the same set of devices. This only works
** on all segments of *one* database at a time.
**
** Parameters:
**	@dbname		- Db that we are checking
**	@segmentname	- Segment name that we want to validate.
**	@raiserror	- Boolean: 1 - raiserror, 0 - warning.
**	@trace		- Boolean: 0 - No tracing; 1 - Trace
**
** Returns:
**	0	- No device overlap.
**	<n>	- # of segments with exactly overlapping devices.
{
*/
create procedure sp_dbxt_check_segment_disks(
			    @dbname		varchar(256)
			  , @segmentname	varchar(256)
			  , @raiserror		int	= 0
			  , @trace		int	= 0
) as
begin
	declare @ndevs_thisseg	int
	      , @ndevs_thatseg	int
	      , @that_segment	varchar(256)
	      , @retval		int
	      , @whoami		varchar(32)
	      , @sqlmsg		varchar(256)

	select    @retval = 0
		, @whoami = object_name(@@procid, db_id('sybsystemprocs'))
				+ ": "

	if (@trace = 1)
	begin
		print "%1!Validating db: %2! segment: '%3!'"
			, @whoami, @dbname , @segmentname
	end

	-- First, find out how many devices this segment spans on.
	--
	select @ndevs_thisseg = count(*)
	from #segdevinfo
	where segmentname = @segmentname

	declare segcur cursor for
	select distinct segmentname
	from #segdevinfo

	-- Eliminate the one segment we are checking against.
	where segmentname != @segmentname
	order by segmentname

	open segcur

	fetch segcur into @that_segment

	while (@@sqlstatus = 0)
	begin
		-- Find out how many devices that segment spans on.
		--
		select @ndevs_thatseg = count(*)
		from #segdevinfo
		where segmentname = @that_segment

		-- We have work to do only if the # of devices are the same.
		--
		if (@ndevs_thisseg = @ndevs_thatseg)
		begin
			if (@trace = 1)
			begin
				print "%1!Segment: '%2!' has %3! devices. Check '%4!'"
					, @whoami
					, @segmentname
					, @ndevs_thisseg
					, @that_segment
			end

			-- Check whether each device for @segmentname is
			-- also the same device for @that_segment; and
			-- vice-versa.
			-- The NOT EXISTS subquery establishes the count
			-- of devices for one segment that don't appear as
			-- the devices for the other segment. If both counts
			-- are 0, it means that both segments share the same
			-- set of devices.
			--
			if ((select count(*)
			     from #segdevinfo s1
			     where s1.segmentname = @segmentname
			       and not exists (select s2.devicename
			       		       from #segdevinfo s2
					       where s2.segmentname
					        	= @that_segment
						 and s2.devicename
						 	= s1.devicename))
				= 0)
			OR ((select count(*)
			     from #segdevinfo s1
			     where s1.segmentname = @that_segment
			       and not exists (select s2.devicename
			       		       from #segdevinfo s2
					       where s2.segmentname
					        	= @segmentname
						 and s2.devicename
						 	= s1.devicename))
				= 0)
			begin
				if (@raiserror = 0)
				begin
					exec sp_getmessage 19223, @sqlmsg output
					print @sqlmsg
						, @segmentname
						, @that_segment
						, @dbname
						, @that_segment
						, @segmentname
					print " "
				end
				else
				begin
					raiserror 19223
						, @segmentname
						, @that_segment
						, @dbname
						, @that_segment
						, @segmentname
				end

				select @retval = @retval + 1
			end
		end

		fetch segcur into @that_segment
	end

	close segcur
	deallocate cursor segcur

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_check_load_segdevinfo
go

/*
** ***************************************************************************
** sp_dbxt_check_load_segdevinfo
**
** Parameters:
**	@thprocname	- Name of auto-expansion threshold procedure.
**	@dbname		- DB whose segments we want to check.
**	@segmentname	- Name of segment to valid.
**	@segid		- Segment ID of above segment.
**	@raiserror	- 0: raise warning; 1: raiserror
**	@trace		- Trace level.
{
*/
create procedure sp_dbxt_check_load_segdevinfo(
				  @thprocname	varchar(30)
				, @dbname	varchar(256)
				, @segmentname	varchar(256)	= NULL
				, @segid	int		= NULL
				, @raiserror	int		= 0
) as
begin
	declare @retval 	int
	      , @lcl_segname	varchar(256)
	      , @segment	int
	      , @segbit		int

	-- *****************************************************************
	-- Load the #segdevinfo table with the denormalized data describing
	-- the devices that each segment maps on. (This is exactly what
	-- you would get from sp_helpsegment.) Do this for all those segements
	-- that already have the auto-expansion proc installed on them.
	--
	declare cur_thproc cursor for
	select s.name , s.segment
	from 	  #systhresholds t
		, #syssegments s
	where t.proc_name = @thprocname
	  and t.segment = s.segment
	  and t.dbname = @dbname
	  and s.dbname = @dbname

	  -- NOTE: DO *NOT* include the following clause, which seems like
	  --	   the right thing to do. But it is *NOT*. This part of
	  -- 	   the UNION wants to select all exisiting rows from the
	  --	   two catalogs, and union them with the newly-added segment
	  --	   (in case of the 'set' scenario). So, pick up all rows.
	  --
	  -- and (@segmentname is NULL or s.name like @segmentname)

	UNION

	-- This appears here to take care of the caller from set threshold
	-- in which case @raiserror is 1. @raiserror is surely 0 when 'check'
	-- command is being run.
	--
	select @segmentname, @segid where @raiserror = 1

	order by s.name

	open cur_thproc

	fetch cur_thproc into @lcl_segname, @segment

	while (@@sqlstatus = 0)
	begin

		-- Convert segment ID to a bit position.
		exec @segbit = sybsystemprocs.dbo.sp_dbxt_segment_to_segbit
							@segment

		-- Cache the data from sysdevices into #temp table.
		--
		insert #segdevinfo(dbname, segmentname, devicename)
		select distinct @dbname, @lcl_segname, d.name
		from	  master.dbo.sysusages u
			, master.dbo.sysdevices d
		where (u.segmap & @segbit) = @segbit
		  and u.dbid = db_id(@dbname)
		  and d.status & 2 = 2		-- only physical devices.
		  and u.vdevno = d.vdevno
		
		if (@@error != 0)
			return 1

		fetch cur_thproc into @lcl_segname, @segment
	end

	close cur_thproc
	deallocate cursor cur_thproc

	return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_check_segments
go

/*
** ***************************************************************************
** sp_dbxt_check_segments
**
** Parameters:
**	@thprocname	- Name of auto-expansion threshold procedure.
**	@dbname		- DB whose segments we want to check.
**	@segmentname	- Name of segment to valid.
**	@raiserror	- 0: raise warning; 1: raiserror
**	@nlpages_free	- # of free pages at which threshold is being set.
**	@trace		- Trace level.
{
*/
create procedure sp_dbxt_check_segments(
				  @thprocname	varchar(30)
				, @dbname	varchar(256)
				, @segmentname	varchar(256)	= NULL
				, @raiserror	int		= 0
				, @nlpages_free	int
				, @trace	int		= 0
) as
begin
	declare @retval		int
	      , @this_retval	int
	      , @lcl_segname	varchar(256)
	      , @freespace_lpgs	int
	      , @curr_lct_lpgs	int
	      , @procname	varchar(300)
	      , @nofreespaceopt	varchar(20)

	select	  @retval = 0
		, @nofreespaceopt	= "no free space acctg"

	-- There is a slight problem that this query will cause some
	-- segments to be checked twice. The reason is that we need the
	-- free_space value so that it can be used for the 'logsegment'
	-- scenario. This means if there were two instances of the thproc
	-- for non-logsegments, they will not be eliminated. Not a huge
	-- problem as this checker routine is to flag down user errors
	-- in the first place; so more output is ok here.
	--
	declare cur_thproc cursor for
	select distinct s.name , t.free_space
	from 	  #systhresholds t
		, #syssegments s
	where t.proc_name = @thprocname
	  and t.dbname = @dbname
	  and s.dbname = @dbname
	  and t.segment = s.segment
	  and (@segmentname is NULL or s.name like @segmentname)

	UNION

	-- This appears here to take care of the caller from set threshold
	-- in which case @raiserror is 1. @raiserror is surely 0 when 'check'
	-- command is being run.
	--
	select @segmentname, @nlpages_free where @raiserror = 1

	order by s.name

	open cur_thproc

	fetch cur_thproc into @lcl_segname , @freespace_lpgs

	-- Process all segments of interest, and implement various checks
	-- on each.
	--
	while (@@sqlstatus = 0)
	begin
		-- Special check for logsegment v/s LCT.
		if (@lcl_segname = 'logsegment')
		begin
			select @procname = @dbname + ".dbo.sp_dbxt_get_lct"

			-- **************************************************
			-- Get the current LCT value. If user is installing
			-- an auto-expansion threshold at or below this value
			-- for logsegments, report an error, and fail the
			-- operation. We want to avoid unforeseen situations
			-- where execution of this threshold proc interferes
			-- with LCT management.
			--
			exec @curr_lct_lpgs = @procname 'reserve'
			exec @this_retval =
				sybsystemprocs.dbo.sp_dbxt_check_logsegment_lct
							  @freespace_lpgs
							, @curr_lct_lpgs
							, @raiserror
							, @trace

			select @retval = @retval + @this_retval
		end
		else
		begin
			-- ***************************************************
			-- Catch the situation when user is trying to (or
			-- already has) set an expansion threshold on a
			-- non-logsegment, and the db currently has free space
			-- accounting disabled. This will never fire the
			-- threshold, so it is useless for the user.
			-- (d.status2: 0x0002 is DBT2_NOACCOUNT)
			--
			if ((select d.status2
			     from master.dbo.sysdatabases d
			     where d.name = @dbname) & 2 = 2)
			begin
				if (@raiserror = 1)
					raiserror 19253, @dbname, @lcl_segname
						, @nofreespaceopt
				else
				begin
					-- Reuse variable for message.
					--
					exec sp_getmessage 19253
							, @procname output
					print @procname, @dbname, @lcl_segname
						, @nofreespaceopt
					print " "
				end

				-- Register this error condition.
				select @retval = @retval + 1
			end
		end

		-- Validate all segments for disk overlapping
		--
		exec @this_retval = sp_dbxt_check_segment_disks
						  @dbname
						, @lcl_segname
						, @raiserror
						, @trace
		select @retval = @retval + @this_retval
		
		-- Validate that there are no multiple auto-expansion
		-- thresholds on the same segment.
		--
		exec @this_retval = sp_dbxt_check_multiple_thprocs
						  @dbname
						, @lcl_segname
						, @raiserror
						, @freespace_lpgs
						, @trace
		select @retval = @retval + @this_retval

		fetch cur_thproc into @lcl_segname, @freespace_lpgs
	end

	close cur_thproc

	deallocate cursor cur_thproc

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

drop table #segdevinfo
go

if (@@error != 0) select syb_quit()
go

drop table #systhresholds
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_check
go

/*
** ***************************************************************************
** sp_dbxt_check
**
** Implement the 'check' command to perform some basic checking of the
** various segments, devices, rules (etc.) and inform the user with
** advisory messages.
**
** Currently, we only support 'check', 'database', and do not provide
** further granularity to check thresholds or devices.
**
** This procedure doubles as the way to check one or more segments statically
** using the 'check' command. Also, when set threshold is done, we call this
** procedure to check whether there is a segment already setup with the
** auto-expansion proc that shares the same set of devices with the segment
** on which a new threshold proc is being set. Hence, some of the SQL you
** see here is funny looking, as it has to take care of both callers.
**
** Parameters:
**	@objtype	- Object type; currently only 'database'.
**	@dbname		- Database name that we want to check.
**	@segmentname	- Segment name in @dbname db.
**	@raiserror	- Boolean: 1 - raiserror, 0 - warning.
**	@currdbname	- DB name where main sproc was exec'ed from.
**	@trace		- Trace level.
**
** Returns:
**	0	- No errors found.
**	<n>	- # of errors found.
{
*/
create procedure sp_dbxt_check(
			  @objtype	varchar(256)	= NULL
			, @dbname	varchar(256)	= NULL
			, @segmentname	varchar(256)	= NULL
			, @raiserror	int		= 0
			, @currdbname	varchar(256)
			, @nlpages_free	int		= 0
			, @trace	int		= NULL
) as
begin
	declare @retval 	int
	      , @lcl_segname	varchar(256)
	      , @whoami_cp	varchar(30)
	      , @sybprocs	varchar(14)
	      , @segid		int
	      , @segment	int
	      , @segbit		int
	      , @thprocname	varchar(30)
	      , @procname	varchar(300)

		-- Reuse variable for current dbname.
	select    @procname = db_name()
		, @sybprocs = 'sybsystemprocs'

	select	  @whoami_cp = object_name(@@procid, db_id(@sybprocs))

	if (@procname != @sybprocs)
	begin
		raiserror 19232, @whoami_cp, @sybprocs, @procname
		return 1
	end

	exec @retval = dbo.sp_dbxt_parse_itemtype 'objects'
					     , @objtype output
					     , 19194

	if ((@retval = -1) or (@retval = 0) or (@objtype != 'database'))
	begin
		raiserror 19221
		return 1
	end

	-- Require user to be in the db that they are checking. Fair enough.
	--
	if (@dbname is NOT NULL)
	begin
		if (@dbname != @currdbname)
		begin
			raiserror 19199, @dbname, @dbname
			return 1
		end
	end
	else
	begin
		select @dbname = @currdbname
	end

	-- *****************************************************************
	-- Cache the contents of key catalogs in #temp tables.
	-- Only do this if (@raiserror = 0) here, which means we are running
	-- the 'check' command. Otherwise, this routine is being called
	-- under the 'set' 'threshold' command, in which case
	-- sp_dbxt_set_extend_db has already cached the data.
	--
	if (@raiserror = 0)
	begin
		exec @retval = sp_dbxt_ins_all_seginfo @dbname, @whoami_cp, 0
							, @dbname
		if (@retval != 0)
			return 1
	end

	-- *****************************************************************
	-- Validate that the segment exists. Handle both forms of callers:
	--
	-- . 'check' will have @raiserror as 0, and @segmentname can be a
	--   LIKE pattern in that case.
	-- . Otherwise, @raiserror is 1, and @segmentname should be a valid
	--   segment.
	--
	if (    (@segmentname is NOT NULL)
	    and (patindex("%[%]%", @segmentname) = 0) )
	begin
		select @segid = segment
		from #syssegments
		where dbname = @dbname
		  and name = @segmentname

		if (@segid IS NULL)
		begin
			raiserror 17871, @segmentname
			return 1
		end
	end

	-- As we are only checking, don't raise any errors.
	--
	select    @retval = 0		-- Assume success.
		, @thprocname = "sp_dbxt_extend_db"

	if (@trace IS NULL)
	begin
		exec @trace = sp_dbxt_get_tracelevel
	end

	-- *****************************************************************
	-- Create a #temp table to store the <segmentname> -> (<list of
	-- device names>) mapping.
	--
	select    db_name() as 'dbname'
		, s.name as 'segmentname', d.name as 'devicename'
	into	#segdevinfo lock allpages

		-- OK to use this as we are just creating the table.
	from 	  dbo.syssegments s
		, master.dbo.sysdevices d
	where 1 = 0

	-- *****************************************************************
	-- Load the #segdevinfo table with the denormalized data describing
	-- the devices that each segment maps on. (This is exactly what
	-- you would get from sp_helpsegment.) Do this for all those segements
	-- that already have the auto-expansion proc installed on them.
	--
	exec @retval = sybsystemprocs.dbo.sp_dbxt_check_load_segdevinfo
				  @thprocname
				, @dbname
				, @segmentname
				, @segid
				, @raiserror

	if (@trace = 1)
	begin
		select * from #segdevinfo order by segmentname, devicename
	end

	-- *****************************************************************
	-- Now go through all segments in this db, looking for any threshold
	-- proc for auto-expansion, and validate each one.
	--
	exec @retval = sybsystemprocs.dbo.sp_dbxt_check_segments
				  @thprocname
				, @dbname
				, @segmentname
				, @raiserror
				, @nlpages_free
				, @trace

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:05 2003 
*/
/*
** raiserror Messages for dbxt_set_extend_db [Total 3]
**
** 19199, "You must be in database '%1!' to run this command. Issue: 'USE %2!', then run this procedure again."
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_set_extend_db [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_set_extend_db ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

-- ***************************************************************************

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as dbname, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, segment, free_space, status, proc_name, suid, currauth
	into #systhresholds lock allpages
	from systhresholds
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_set_extend_db()
**
** User-api procedure to define the thresholds at which database extension
** should be considered. 
**
** The invocation specifies that when the space on the specified segment
** in the input database is down to @space_left amount of space, then the
** user would like the default threshold procedures to fire and perform
** the appropriate database expansion.
**
** This internally calls sp_dropthreshold to drop any existing thresholds
** at the same @space_left level. And, a new threshold will be added at
** that level. Any error-handling by the underlying threshold procedures
** is simply conveyed back to the caller, without further intervention.
**
** The value-add with this one is that it allows users to enter the
** @space_left argument with unit-specifiers, and we will internally
** convert it to the right number of logical pages for the database in
** question. This way, the user-interface becomes simple.
**
** There are some extra rules/restrictions/warnings implemented by this proc.
** Here is a short-list; see below for details:
**
** 	. Fail if there already exists an auto-expansion proc.
**	. Warn user if setting an expansion proc <= current LCT value.
**
** Unit-specifiers:
** 	@space_left can be specified in one of many ways, either with or
**	without unit-specifiers. There should be no space between the
**	numeric value and the unit-specifer.
**
** 	No units default to kilobytes.
**	'k', 'K'	- Kilo bytes
**	'm', 'M'	- Mega bytes
**	'g', 'G'	- Giga bytes
**	'p', 'P'	- Server's logical page(s) (not petabytes)
**
** Examples:
**	'105'	=> 105 K
**	'3M'	=> 3 megabytes
**	'1.4g'	=> 1.4 gigabytes
**	etc.
**
** Parameters:
**	@dbname		- Name of database that should be extended.
**	@segmentname	- Segment on which threshold will be installed.
**	@space_left	- Amount of space left when threshold is fired.
**			  Can be specified using units like 'k','K',
**			  'm', 'M', 'g', 'G'. Without the unit-specifier
**			  the number will be treated as number of logical
**			  pages.
**	@lct		- Whether this is a last-chance threshold.
**	DBXT_FUTURE - Not clear whether we need to support @lct. Add later.
**	@currdbname	- Db from which top-level sproc was invoked.
**
** Returns:
**	0	: If the new threshold was installed properly.
**	!= 0	: Otherwise, in case of any errors.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_set_extend_db
go

create procedure sp_dbxt_set_extend_db (
			  @dbname	varchar(256)
			, @segmentname	varchar(256)
			, @space_left	varchar(255)
			, @lct		int	= 0
			, @currdbname	varchar(256)
) as
begin
	declare @msg		varchar(100)
	      , @retval		int
	      , @sybprocs	varchar(14)
	      , @thprocname	varchar(30)
	      , @procname	varchar(300)
	      , @size_k		int	-- space_left in 'K' units
	      , @nlpages	int	-- # of logical pages for thmgr.

	      , @curr_lct_lpgs	int	-- Current LCT value.
	      , @whoami_cp	varchar(30)
	      , @tracelevel	int

	select    @procname = db_name()	-- reuse variable
		, @sybprocs = 'sybsystemprocs'

	select 	  @whoami_cp = object_name(@@procid, db_id(@sybprocs))

	if (@procname != @sybprocs)
	begin
		raiserror 19232, @whoami_cp, @sybprocs, @procname
		return 1
	end

        if (@dbname != @currdbname)
	begin
		raiserror 19199, @dbname, @dbname
		return 1
	end

	exec @retval = sp_dbxt_parse_freespace @space_left, @nlpages output
	if (@retval != 0)
		return 1

	select @thprocname = "sp_dbxt_extend_db"
	
	exec @tracelevel = sp_dbxt_get_tracelevel

	-- *************************************************************
	-- Cache the contents of key catalogs in #temp tables.
	--
	exec @retval = sp_dbxt_ins_all_seginfo
					  @dbname
					, @whoami_cp
					, @tracelevel
					, @dbname
	if (@retval != 0)
		return 1

	-- *************************************************************
	-- Check whether there is already a segment mapped to the same
	-- devices as this one and which has a threshold proc set on it.
	-- ... and other checks that develop over time.
	--
	exec @retval = sybsystemprocs.dbo.sp_dbxt_check 'database'
				    , @dbname
				    , @segmentname
				    , 1		-- raiserror
				    , @currdbname
				    , @nlpages
				    , @tracelevel
	if (@retval != 0)
		return 1

	-- *************************************************************
	-- Check if an earlier auto-expansion threshold exists at the same
	-- space_left threshold. If so, drop that one, and install a new
	-- one using the system-provided default threshold action procedure
	-- to extend the database. (We don't drop other threshold procs
	-- that might exist at the same free_space value. We just let the
	-- sp_addthreshold complain about that, and have the user decide
	-- what they want to do.)
	--
	if exists (select 1 from #systhresholds t
		   where t.segment = (select s.segment
		   		      from #syssegments s
		   		      where s.name = @segmentname
				        and s.dbname = @dbname
				     )
		     and t.free_space = @nlpages
		     and t.proc_name = @thprocname
		     and t.dbname = @dbname
		  )
	begin
		select @procname = @dbname + ".dbo.sp_dropthreshold"
		exec @retval = @procname
					  @dbname
					, @segmentname
					, @nlpages

		if (@retval != 0)
			return @retval
	end

	select @procname = @dbname + ".dbo.sp_addthreshold"
	exec @retval = @procname  @dbname
				, @segmentname
				, @nlpages
				, @thprocname
	return @retval
end
go

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

drop table #systhresholds
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Fri Jan 23 12:42:20 2004 
*/
/*
** raiserror Messages for dbxt_list [Total 4]
**
** 18524, "%1!: Permission denied. This operation requires System Administrator (sa_role) role."
** 19199, "You must be in database '%1!' to run this command. Issue: 'USE %2!', then run this procedure again."
** 19207, "%1!: Pattern specifiers are not allowed for the argument '%2!' to this command."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_list [Total 11]
**
** 17105, "default"
** 19188, "database"
** 19189, "device"
** 19191, "growby"
** 19192, "maxsize"
** 19200, "No policy information exists in master.dbo.sysattributes for the requested arguments."
** 19245, "disabled"
** 19246, "disabled by LCT"
** 19247, "enabled"
** 19248, "last-chance"
** 19249, "Log segment free space currently is %1! logical pages (%2!K)."
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_list ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

/*
** ***************************************************************************
** Drop and recreate the #temp table that is used within these procedures.
*/
if ((select object_id("#dbxt_list")) IS NOT NULL)
	drop table #dbxt_list
go

if (@@error != 0) select syb_quit()
go

	create table #dbxt_list (
			  typecode	tinyint		
			, type		varchar(10)	NULL
			, name		varchar(256)	NULL
			, segment	varchar(256)	NULL
			, item		varchar(10)	NULL
			, value		varchar(30)	NULL
			, status	varchar(10)	NULL
			, comment	varchar(100)	NULL
	) lock allpages
go

exec sp_dbxt_recreate_proc sp_dbxt_listraw
go

/*
** ***************************************************************************
** sp_dbxt_listraw
**
** Easy-to-use interface to display raw data from master.dbo.sysattributes.
** @attr_class is provided as a hook to dump rows relating to other features
** just to examine what the contents look like for other features.
**
** Parameters:
**	@rawdump	- Code to specify what to dump:
**	@attr_class	- Attribute class; e.g. other than this feature's.
**
**	0	- Dump from master.dbo.sysattributes, abridged char_value.
**	1	- Dump from master.dbo.sysattributes, w/full char_value.
**	2	- Dump from master.dbo.spt_values
**	NULL	- Dump all.
**
** Returns:
**	0	- the list was successful.
**	1	- Some errors.
{
*/
create procedure sp_dbxt_listraw (
				  @rawdump_arg	varchar(30) = NULL
				, @attr_class	varchar(30) = NULL
) as
begin
	declare @sa_role	int
	      , @dbo		int
	      , @class		smallint
	      , @whoami_fn	varchar(32)
	      , @tabname	varchar(30)
	      , @where_clause	varchar(30)
	      , @rawdump	int
	      , @retval		int

	select    @class 	= case when @attr_class IS NOT NULL
					then convert(int, @attr_class)
					else 19	-- AUTODBXT_CLASS
				  end

		, @rawdump	= convert(int, @rawdump_arg)

		, @whoami_fn = object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "

	select   @where_clause = "where class = " + convert(varchar, @class)

	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  NULL
					, @sa_role output
					, @dbo output
	
	if (@sa_role = 0)
	begin
		raiserror 18524, "sp_dbextend 'listraw'"
		return 1
	end

	if ((@rawdump is NULL) or (@rawdump = 0) or (@rawdump = 1))
	begin
		if ((@rawdump is NULL) or (@rawdump = 0))
		begin
			-- Retrieve abridged char_value into a #temp table,
			-- for readability.
			--
			select    class, attribute, object_type, object_cinfo
				, object, object_info1, object_info2
				, convert(varchar(10), char_value) as char_value
			into #sysattributes
			from master.dbo.sysattributes
			where class = @class

			select @tabname = "#sysattributes"

		end
		else	-- @rawdump is 1
		begin
			select @tabname = "master.dbo.sysattributes"
		end

		-- Generate the formatted output from one of the 2 tables.
		--
		exec @retval = sp_autoformat
				  @fulltabname = @tabname
				, @selectlist = "class, attr=attribute, object_type, object_cinfo, object, object_info1, object_info2,char_value"
				, @whereclause = @where_clause
	end

	if ((@rawdump is NULL) or (@rawdump = 2))
	begin
		exec @retval = sp_autoformat
				  @fulltabname = "master.dbo.spt_values"
				, @selectlist = "name, number, type, ansi_w, low, high, msgnum"
				, @whereclause = "where type = 'XT'"

	end
	if (@retval != 0)
		return 1

	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

-- ***************************************************************************

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, free_space, status, proc_name, suid, currauth
	into #systhresholds lock allpages
	from systhresholds
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_list_feature
go

/*
** ***************************************************************************
** sp_dbxt_list_feature
**
{
*/
create procedure sp_dbxt_list_feature
as
begin
	declare @retval	int

	-- Insert the summary data describing policies for the feature
	-- server-wide.
	--
	insert #dbxt_list (typecode
			 , type, name, segment, item, value, status, comment)
	select	1
		, 'feature'
		, a.object_cinfo

		-- feature-wide setting is irrelevant to segments.
		, '(n/a)'
		, '(n/a)'
		, '(n/a)'
		, case a.object_info2 when 1 then 'enabled' else 'disabled' end
		, case charindex(' [', a.comments)
			when 0 then a.comments
			else substring(a.comments, 1,
					(charindex(' [', a.comments) - 1) )
		  end

	from master.dbo.sysattributes a
	where a.class = 19
	  and a.object_type = 'XT'
	  and a.object_info1 IN (1)
	  and a.object_cinfo = 'server-wide'
	  and a.attribute in (select a2.object_info1
	  		      from master.dbo.sysattributes a2
			      where a2.class = 19
			        and a2.attribute = 0	-- DBXT_DIRECTORY
			      	and a2.object_type = 'XT')

	select @retval = (case when @@error != 0 then 1 else 0 end)

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_list_database
go

/*
** ***************************************************************************
** sp_dbxt_list_database
**
** List the rules found in sysattributes, filtering on user provided
** @dbname, or @segmentname.
**
** With appropriate privileges, the following are valid:
**
** . List rules for one db, all segments in that db:
**	sp_dbextend 'list', 'da', pubs2
**
** . List rules for one db, one segments:
**	sp_dbextend 'list', 'da', pubs2, logsegment
**
** . List rules for one db, multiple segments in that db:
**	sp_dbextend 'list', 'da', pubs2, 'dataseg%'
**
** . List rules for multiple db, multiple segments in that db:
**	sp_dbextend 'list', 'da', "%pubs2", "dataseg%"
**
{
*/
create procedure sp_dbxt_list_database (
				@dbname		varchar(256)	= NULL
			      , @segmentname	varchar(256)	= NULL
			      , @currdbname	varchar(256)
			      , @seginfo_done	tinyint		= 0 output
			      , @trace		int		= 0
) as
begin
	declare @sqlstr			varchar(1024)
	      , @sa_role		int
	      , @dbo			int
	      , @class			smallint
	      , @max_dbname		int
	      , @max_segname		int
	      , @max_charvalue		int
	      , @max_comment		int
	      , @rowct_before		int
	      , @rowct_after		int
	      , @object_name		varchar(10)
	      , @growby_str		varchar(10)
	      , @maxsize_str		varchar(10)
	      , @default_str		varchar(10)
	      , @retval			int
	      , @whoami_cp		varchar(30)	-- this proc's name
	      , @whoami			varchar(32)

	select @whoami_cp = object_name(@@procid, db_id('sybsystemprocs'))

	select @whoami = case @trace when 0 then NULL else @whoami_cp + ":" end
	
	/*
	** Do not run this here, before knowing whether @dbname is NULL or
	** not. Otherwise, even if user is dbo of @dbname, but not dbo of
	** @currdbname and not sa_role, they will get an error from proc_role().
	**
	** COMMENTED OUT FOR POSTERITY!
	**
	** exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  -- @currdbname
					  NULL
					, @sa_role output
					, @dbo output
	*/

	/*
	** Complicated logic for figuring out who is allowed to run 'list'.
	** Most list commands are simply allowed, except for the use of
	** pattern specifiers in the dbname.
	**
	** Here are the rules:
	**
	**  o The system-default rules for all db/segments will always be
	**    listed. This helps in case the requested dbname (e.g. pubs2)
	**    does not have any specific rules for itself, but will be
	**    affected by the system-default rules.
	**
	**  o list w/no arguments or list 'database', with NULL dbname will
	**    be treated as list rules for the current db, along with the rules
	**    for system-wide db policy. No permissions are required.
	**
	**  o list 'database', @dbname will list the rules for that db, @dbname.
	**    alone. No permissions checks are needed here again.
	**    
	**  o list 'database', @dbname no longer requires that this command
	**    be run from the @dbname db. It can be run from any database.
	**
	**  o list with pattern-specifiers in dbname (e.g. pubs2%, "%")
	**    is only allowed to login with sa_role. 
	**    This restriction is being imposed as we have
	**    to process the segment names based on the segment ID that is
	**    available in sysattributes. Generating the data in #syssegments
	**    might take time for large #s of dbs that have rules defined.
	**    So, restrict this to sa_role. (Non-sa_role users can always
	**    get the same info by providing the db name of interest.)
	*/
	if (@dbname is NULL)
	begin
		-- If we are already the dbo of the current db, interpret:
		--
		--	pubs2> sp_dbextend 'list', 'da'
		--
		-- to mean list the info for the current db.
		--
		select 	  @dbname = @currdbname
	end

	else	-- some dbname was provided
	begin
		-- We don't want to allow non-sa_role users to run with
		-- %pubs2% for the reason given above.
		--
		if (patindex("%[%]%", @dbname) != 0)
		begin
			-- Check whether user is dbo/sa_role.
			exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
							  NULL
							, @sa_role output
							, @dbo output
			if (@sa_role = 0)
			begin
				exec sp_dbxt_gen_command_tag @whoami_cp
						, @dbname
						, @sqlstr output

				raiserror 19207, @sqlstr, "@dbname"
				return 1
			end
		end
	end

	-- Cache syssegments and systhresholds info for db of interest.
	--
	if (@seginfo_done = 0)
	begin
		exec @retval = sp_dbxt_ins_all_seginfo @dbname, @whoami_cp
						, @trace
		if (@retval != 0)
			return 1

		-- Inform caller that #seginfo table was loaded here
		select @seginfo_done = 1
	end

	exec sp_getmessage 19188, @object_name out
	exec sp_getmessage 19191, @growby_str out
	exec sp_getmessage 19192, @maxsize_str out

	exec sp_getmessage 17105, @default_str out

	select @class	= 19	-- AUTODBXT_CLASS

	if (@trace = 1)
	begin
		select @rowct_before = count(*) from #dbxt_list
		-- exec sp_dbxt_list_syssegments
	end

	-- Insert the summary data describing policies for databases into
	-- the #temp table created by the caller. If a @segmentname is given
	-- we extract all rows for that (db, segment) pair. Otherwise, we
	-- extract all rows for all segments in that database.
	-- Both dbname and segment names support LIKE patterns.
	--
	insert #dbxt_list (typecode
			 , type, name, segment, item, value, status, comment)
	select	2
		, @object_name
		, a.object_cinfo

		-- Try to map the segment ID back to its name by looking up
		-- syssegments.
		--
		--  o For 'default' rules, segment ID actually gives the min
		--    size for alter-db extensions. The system-default policy
		--    actually applies to all segments. Hence, generate 'all'.
		--
		--  o In other cases, (i.e. segment name is NULL because
		--    segment ID was not found in syssegments), retain NULL
		--    so that we can flag that in the output.
		
		, case when a.object_cinfo = 'default'
		       then "(all)"
		       else (select si.name
		       	     from #syssegments si
		       	     where si.segment = a.object
			       and si.dbname = a.object_cinfo
			     )
		  end

		  -- Decode object_info1 into the field it stands for.
		, case a.object_info1
			when 1	then 'enable'
			when 2	then 'trace'
			when 3	then @growby_str
			when 4	then @growby_str
			when 5	then @maxsize_str
			else	'unknown'
		  end

		, a.char_value
		, case a.object_info2 when 1 then 'enabled' else 'disabled' end
		, case charindex(' [', a.comments)
			when 0 then a.comments
			else substring(a.comments, 1,
					(charindex(' [', a.comments) - 1) )
		  end

	from master.dbo.sysattributes a
	where a.class = @class
	  and a.object_type = 'DB'

	  -- For now, as we don't have ability to enable/disable or
	  -- to turn tracing on/off at db/device level, just ignore
	  -- those attribute rows. (Confuses the user otherwise.)
	  --
	  and a.object_info1 IN (3, 4, 5)

	  -- Extract all rows for system defaults and user-specified
	  -- relating to databases.
	  -- The subquery returns the attribute values:
	  --
	  --	DBXT_DEFAULT_DB
	  --	DBXT_USER_SPEC_DB
	  --
	  and a.attribute in (select a2.object_info1
	  		      from master.dbo.sysattributes a2
			      where a2.class = @class
			        and a2.attribute = 0	-- DBXT_DIRECTORY
			      	and a2.object_type = 'DB')

	  -- But eliminate directory entry rows.
	  and a.attribute != 0

	  and ((a.object_cinfo = 'default') OR (a.object_cinfo like @dbname) )
	  and (   @segmentname is NULL
	       OR a.object IN (select si.segment
	       			from #syssegments si
				where (   @segmentname is NULL
			   	        OR (   si.name like @segmentname
					    AND si.dbname = a.object_cinfo
					   )
				      )
			      )
	      )

	select @retval = (case when @@error != 0 then 1 else 0 end)

	if (@trace = 1)
	begin
		select @rowct_after = count(*) from #dbxt_list

		print "%1!In #dbxt_list: rowct_before: %2! rowct_after: %3!"
			, @whoami, @rowct_before, @rowct_after
	end

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_list_device
go

/*
** ***************************************************************************
** sp_dbxt_list_device
{
*/
create procedure sp_dbxt_list_device (
				@devicename	varchar(256)	= NULL
			      , @trace		int		= 0
) as
begin
	declare @sqlstr			varchar(1024)
	      , @sa_role		int
	      , @dbo			int
	      , @class			smallint
	      , @max_devname		int
	      , @max_segname_unused	int
	      , @max_charvalue		int
	      , @max_comment		int
	      , @object_name		varchar(10)
	      , @growby_str		varchar(10)
	      , @maxsize_str		varchar(10)
	      , @default_str		varchar(10)
	      , @retval			int
	      , @rowct_before		int
	      , @rowct_after		int
	      , @whoami			varchar(30)

	select @whoami = case @trace
			   when 0 then NULL
			   else object_name(@@procid, db_id('sybsystemprocs'))
				  + ": "
			 end
	
	/*
	** We don't bother about permissions for viewing the rules for
	** devices. Even though these are server-wide resources, it does
	** no harm if a normal user were to get this information. They
	** should be able to do nothing with that.
	*/
	exec sp_getmessage 19189, @object_name out
	exec sp_getmessage 19191, @growby_str out
	exec sp_getmessage 19192, @maxsize_str out

	exec sp_getmessage 17105, @default_str out

	select @class		= 19	-- AUTODBXT_CLASS
	if (@trace = 1)
	begin
		select @rowct_before = count(*) from #dbxt_list
	end

	-- Insert the summary data describing policies for databases into
	-- the #temp table created by the caller.
	--
	insert #dbxt_list (typecode
			 , type, name, segment, item, value, status, comment)
	select	3
		, @object_name
		, a.object_cinfo

		-- No 'segment' info is needed for devices. So, generate
		-- a '(n/a)' string to reflect that fact in the output.
		--
		, '(n/a)'

		  -- Decode object_info1 into the field it stands for.
		, case a.object_info1
			when 1	then 'enable'
			when 2	then 'trace'
			when 3	then @growby_str
			when 4	then @growby_str
			when 5	then @maxsize_str
			else	'unknown'
		  end

		, a.char_value
		, case a.object_info2 when 1 then 'enabled' else 'disabled' end
		, case charindex(' [', a.comments)
			when 0 then a.comments
			else substring(a.comments, 1,
					(charindex(' [', a.comments) - 1) )
		  end

	from master.dbo.sysattributes a
	where a.class = @class
	  and a.object_type = 'DV'

	  -- For now, as we don't have ability to enable/disable or
	  -- to turn tracing on/off at db/device level, just ignore
	  -- those attribute rows. (Confuses the user otherwise.)
	  --
	  and a.object_info1 IN (3, 4, 5)

	  -- Extract all rows for system defaults and user-specified
	  -- relating to databases.
	  -- The subquery returns the attribute values:
	  --
	  --	DBXT_DEFAULT_DEV
	  --	DBXT_USER_SPEC_DEV
	  --
	  and a.attribute in (select a2.object_info1
	  		      from master.dbo.sysattributes a2
			      where a2.class = @class
			        and a2.attribute = 0	-- DBXT_DIRECTORY
			      	and a2.object_type = 'DV')

	  -- But eliminate directory entry rows.
	  and a.attribute != 0

	  and (@devicename is NULL OR a.object_cinfo like @devicename)

	select @retval = (case when @@error != 0 then 1 else 0 end)

	if (@trace = 1)
	begin
		select @rowct_after = count(*) from #dbxt_list

		print "%1!In #dbxt_list: rowct_before: %2! rowct_after: %3!"
			, @whoami, @rowct_before, @rowct_after
	end

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

drop table #dbxt_list
go

exec sp_dbxt_recreate_proc sp_dbxt_help_threshold
go

/*
** ***************************************************************************
** sp_dbxt_help_threshold: Temp till we can rework sp_helpthreshold.
{
*/
create procedure sp_dbxt_help_threshold (
			  @segmentname	varchar(256)
) as
begin
	declare @retval			int
	      , @lct			int
	      , @log_freespace		int
	      , @log_freespace_KB	int
	      , @logfreespace_msg	varchar(80)
	      , @disabled_msg		varchar(15)
	      , @disabled_by_lct_msg	varchar(15)
	      , @enabled_msg		varchar(15)
	      , @last_chance_msg	varchar(15)

/* Code pulled out from sp_helpthreshold, hence the bad indentation */

select @lct = lct_admin('reserve', 0)

/*
** Show all thresholds for all segments requested. Store the data in a #temp
** table first. First, just store the current in-memory status of each
** threshold as received from lct_admin().

*/
select	  segment_name = s.name

	, case when t.status = 1 then @lct else t.free_space end as 'free_pages'

	, ((case when t.status = 1 then @lct else t.free_space end)
			* (@@maxpagesize / 1024) ) as 'free_pages_KB'

	, last_chance = t.status
	, procname = t.proc_name

	, th_status = lct_admin('status_in_cache', s.segment, t.free_space)
	, status = convert(varchar(45), ' ')
into #thresholdinfo
from syssegments s, systhresholds t
where t.segment = s.segment
  and (@segmentname IS NULL or s.name like @segmentname)

/* Retrieve the various status string names added as messages. */
exec sp_getmessage 19245, @disabled_msg output
exec sp_getmessage 19246, @disabled_by_lct_msg output
exec sp_getmessage 19247, @enabled_msg output
exec sp_getmessage 19248, @last_chance_msg output

/*
** Go back to the #temp table, and decipher the current state of the
** threshold as found in the in-memory cache, and interpret it as a string
** to be useful to the user. Update the 'status' column with the deciphered
** meaning, so that it can be generated later on.
**
** Here we decode the in-memory status value from the threshold
** cache, and provide human-readable status for each threshold. The status
** tags mean the following:
**
**  last-chance	- This is the last-chance threshold.
**  enabled	- This threshold is currently enabled to fire next.
**  <no status>	- Threshold is active, but waiting in line to be enabled
**		  the next time the free space goes above that threshold.
**  disabled	- Threshold is at free space lower than the current LCT.
**  disabled by LCT
**		- Threshold is within the hystereis range around the LCT.
**		  So it has been disabled.
*/

	update #thresholdinfo
	set status = 

	-- Retrieve the in-memory status of the threshold which determines
	-- whether the threshold is enabled (will fire) or disable (will not).
	--
	--	0x1	- TH_LASTCHANCE
	--	0x8	- TH_ENABLED.
	--
	-- We are only interested in these status for now.
	--
	-- First handle the case when lct_admin() has returned a value of -1
	-- for thresholds that are not found in the cache. This can happen if
	-- the LCT came too close another threshold. Such thresholds are
	-- reported as 'disabled by LCT'.
	--
	case th_status
	-- {

	    when -1 then @disabled_by_lct_msg
	    else
		case (th_status & 9)
		-- {

		   when 9 then @last_chance_msg + "," + @enabled_msg
		   when 8 then @enabled_msg
		   when 1 then @last_chance_msg

		   	-- Cope with the case of threshold being less than LCT
			-- or around the hysteresis value of LCT. This only
			-- applies to thresholds on the logsegment. For all
			-- other segments, if the threshold is not 'enabled'
			-- then it is just waiting in line ('NULL').
			--
		   else case t.segment_name
		   	    when 'logsegment' then
			        (case 
			             when (t.free_pages <= @lct)
			      			then @disabled_msg
			             when (t.free_pages 
			      		    <= @lct + 2 * @@thresh_hysteresis)
						then @disabled_by_lct_msg
			             else " "
			        end)
		   	    else " "
		        end
		-- }
		end

	-- }
	end

	from #thresholdinfo t

-- Report back from the #temp table, auto-formatting while at it.
--
exec @retval = sp_autoformat
		  @fulltabname = #thresholdinfo
		, @selectlist = "'segment name' = segment_name, 'free pages' = free_pages, 'free pages (KB)' = free_pages_KB, 'threshold procedure' = procname, status"
		, @orderby = "order by segment_name, free_pages"

-- Report back the logsegment's current free space value, in case user
-- was running this for the logsegment, or for all segments.
--
if ((@segmentname IS NULL) or (@segmentname = 'logsegment'))
begin
	print " "
	select @log_freespace = lct_admin('logsegment_freepages', db_id())
	select @log_freespace_KB = (@log_freespace * @@maxpagesize) / 1024

	exec sp_getmessage 19249, @logfreespace_msg output
	print @logfreespace_msg
		, @log_freespace
		, @log_freespace_KB
end
return @retval
end
go	-- }

exec sp_dbxt_recreate_proc sp_dbxt_list_threshold
go

/*
** ***************************************************************************
** sp_dbxt_list_threshold
**
** Wrapper proc to call sp_helpthreshold to list the thresholds on various
** segments on this db.
**
** This does some more work of validating that we are in the current db,
** and that we are the DBO of the requested DB and / or sa_role.
**
** Parameters:
**	@dbname		- DB whose thresholds we want to list.
**	@segmentname	- Segment in @dbname db.
**	@currdbname	- Db were sproc was executed from.
** 	@seginfo_done	- Whether caller has already populated seginfo table
**	@trace		- Tracing level.
{
*/
create procedure sp_dbxt_list_threshold (
				  @dbname	varchar(256) = NULL
				, @segmentname	varchar(256) = NULL
				, @currdbname	varchar(256)
				, @seginfo_done	tinyint = 0 output
			        , @trace	int	= 0
) as
begin
	declare @sa_role	int
	      , @dbo		int
	      , @whoami_cp	varchar(30)
	      , @procname	varchar(300)
	      , @retval		int
	
	-- Need to be in the db of interest as we look up system catalogs.
	--
	if (@dbname != @currdbname)
	begin
		raiserror 19199, @dbname, @dbname
		return 1
	end

	select	  @whoami_cp = object_name(@@procid, db_id('sybsystemprocs'))
		, @retval = 0

	-- Cache syssegments and systhresholds info for db of interest.
	--
	if (@seginfo_done = 0)
	begin
		exec @retval = sp_dbxt_ins_all_seginfo
						  @dbname
						, @whoami_cp
						, @trace
						, @dbname
		if (@retval != 0)
			return 1

		select @seginfo_done = 1
	end

	-- DBXT_FUTURE: As we are in the db, can use the system catalogs
	-- directly here.
	--
	if exists (select 1 from #systhresholds t
		   where t.proc_name = 'sp_dbxt_extend_db'
		     and t.dbname = @dbname
		     and (@segmentname is NULL 
		           or t.segment IN (select si.segment
			   		    from #syssegments si
					    where si.name like @segmentname
					      and si.dbname = @dbname
					   )
			 )
		  )
	begin
		/*
		** DBXT_FUTURE: Leave this here for the day when we can
		** properly integrate this with sp_helpthreshold. Until
		** then, inline that code here to show the list outputs.
		*/
		-- Pass that through, and if it is a wild-card, the
		-- sp_helpthreshold sproc will know to report only on
		-- those segments.
		--
		select @procname = @currdbname + ".dbo.sp_dbxt_help_threshold"
		exec @retval = @procname @segmentname
	end

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

drop table #systhresholds
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_list_contents
go


	-- Create a #temp table that will then be used in the called
	-- procedures to populate information from master.dbo.sysattributes.
	-- (This gets recreated in the caller sp_dbxt_list. This is being
	--  created here only to allow creation of sp_dbxt_list_contents.)

	create table #dbxt_list (
			  type		varchar(10)	NULL
			, name		varchar(256)	NULL
			, segment	varchar(256)	NULL
			, item		varchar(10)	NULL
			, value		varchar(30)	NULL
			, comment	varchar(100)	NULL
	) lock allpages
go

/* 
** ***************************************************************************
** sp_dbxt_list_contents
**
{
*/
create procedure sp_dbxt_list_contents (
				  @objtype_val	int
				, @objtype_db	int
				, @objtype_dev 	int
				, @objtype_none	int
				, @arg1		varchar(256)	= NULL
				, @arg2		varchar(256)	= NULL
				, @verbose	int
)as
begin
	declare @whoami_fn	varchar(30)
	      , @orderbyclause	varchar(256)
	      , @select_list	varchar(100)
	      , @retval		int

	select	  @whoami_fn = object_name(@@procid, db_id('sybsystemprocs'))
					+ ": "
	
	-- Build an ORDER BY clause using one if the user has
	-- provided one, or else, use the default ordering.
	--
	select	  @orderbyclause =
			case 
			  when (@objtype_val in ( @objtype_none, @objtype_db)
			  	    and @arg2 is NOT NULL)
			  then	@arg2

			  when  (@objtype_val in (@objtype_none, @objtype_dev)
				    and @arg1 is NOT NULL)
			  then @arg1

			  else " ORDER BY typecode, type, name "
				     + case when @objtype_val in (@objtype_none,
						     		  @objtype_dev)
					   then ", segment"
					   else ""
			  	       end
			end

		-- If asking for all objects, include 'type' column in the
		-- select list. Otherwise, skip it.
		--
		, @select_list = 
			case when @objtype_val = @objtype_none
				then "type, "
				else ""
			  end
			+ "name, segment, item, value, status"

			-- Include the comment field if requested.
			+ case @verbose
			    when 1 then ", comment"
			    else ""
			  end

	exec @retval = sp_autoformat
			  @fulltabname = #dbxt_list
			, @selectlist = @select_list
			, @whereclause = NULL
			, @orderby = @orderbyclause

	return (case when (@retval != 0) then 1 else 0 end)
end
go	-- }

if (@@error != 0) select syb_quit()
go

drop table #dbxt_list
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_list
**
** Common interface to list stuff stored in sysattributes for users.
**
** Serves as the place to also do 'listfull' and 'listraw'. Executed from
** sybsystemprocs.
**
** Parameters:
**	@objtype	- Object type; datbase, device, threshold etc.
**	@db_devname	- Type of object to list on.
**	@arg1		- Object name (filter)
**	@verbose	- Verbosity flag:
**			  0	- list
**			  1	- listfull
**			  2	- listraw	*internal* use
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_list
go

create procedure sp_dbxt_list (
			@objtype 	varchar(30)	= NULL
		      , @db_devname	varchar(256)	= NULL
		      , @arg1		varchar(256)	= NULL
		      , @arg2		varchar(256)	= NULL
		      , @verbose	int		= 0
		      , @currdbname	varchar(256)
) as
begin
	declare	@objtype_val	int
	      , @objtype_none	int	-- No object type given
	      , @objtype_db	int
	      , @objtype_dev	int
	      , @objtype_th	int
	      , @retval		int
	      , @class		smallint
	      , @trace		int
	      , @seginfo_done	tinyint
	      , @msg		varchar(100)

	select    @retval = 0
		, @objtype_none	= 0	-- NULL => process all types of objs.
		, @objtype_db	= 1	-- 'database'
		, @objtype_dev	= 2	-- 'device'
		, @objtype_th	= 3	-- 'threshold'

					-- AUTODBXT_CLASS
		, @class	= 19	-- feature's class in sysattributes
		, @seginfo_done	= 0

	-- list raw mode the data in sysattributes, and spt_values.
	--
	if (@verbose = 2)
	begin
		-- 1st arg is a number that gives what raw-dump to perform.
		-- 2nd arg gives attribute class to dump on and is only used
		-- when 1st arg is '0'.
		--
		exec @retval = sp_dbxt_listraw @objtype, @db_devname
		return @retval
	end

	-- Convert the arg to 'list' command to an ordinal number.
	--
	exec @objtype_val = sp_dbxt_parse_itemtype 'objects'
					, @objtype output
					, 19194
	if (@objtype_val = -1)
	begin
		return 1
	end

	-- ******************************************************************
	-- Create a #temp table that will then be used in the called
	-- procedures to populate information from master.dbo.sysattributes.
	-- typecode is a tag to control how the final listing comes out.
	-- We would like to report stuff as feature, databas, device and this
	-- controls that ordering.
	--
	create table #dbxt_list (
			  typecode	tinyint		
			, type		varchar(10)	NULL
			, name		varchar(256)	NULL
			, segment	varchar(256)	NULL
			, item		varchar(10)	NULL
			, value		varchar(30)	NULL
			, status	varchar(10)	NULL
			, comment	varchar(100)	NULL
	) lock allpages

	exec @trace = sp_dbxt_get_tracelevel

	-- First, generate the current state of the policy for the entire
	-- feature. This is done for both databases and devices, as the
	-- actual expansion for databases is effected by whether the feature
	-- is enabled/disabled server-wide.
	--
	if ((@objtype_val = @objtype_none) OR
	    (@objtype IN ('database','device')) )
	begin
		exec @retval = sp_dbxt_list_feature
	end

	-- List policies for all 'database's.
	--
	if (@objtype_val = @objtype_none) OR (@objtype = 'database')
	begin
		exec @retval = sp_dbxt_list_database @db_devname
						   , @arg1
						   , @currdbname
						   , @seginfo_done output
						   , @trace
		if (@retval != 0)
			return @retval
	end

	-- List policies for all 'devices's.
	--
	if (@objtype_val = @objtype_none) OR (@objtype = 'device')
	begin
		exec @retval = sp_dbxt_list_device @db_devname, @trace
		if (@retval != 0)
			return @retval
	end

	-- if (@trace = 1) select * from #dbxt_list

	-- If we were asked to list 'database', or 'device' info, and 
	-- then if no data was gathered, report that there was nothing
	-- to display. (Otherwise, for list,  'th' we get this annoying
	-- warning that there was no data to report.)
	--
	if (@objtype_val in (@objtype_none, @objtype_db, @objtype_dev) )
	begin
		if ((select count(*) from #dbxt_list) = 0)
		begin
			-- No policy info was found. Not an error.
			-- Just nothing to report here.
			exec sp_getmessage 19200, @msg output print @msg
		end
		else
		begin
			-- We have some data to display. Show that.
			exec @retval = sp_dbxt_list_contents
						  @objtype_val
						, @objtype_db
						, @objtype_dev
						, @objtype_none
						, @arg1	
						, @arg2
						, @verbose
			if (@retval != 0)
				return 0
		end
	end
	
	/*
	** Help the user somewhat. If the user issued this for 'database',
	** and provided the current db's name (common case), then also
	** show the thresholds for the segments specified (if any). This way,
	** when the user does:
	**
	** 	sp_dbextend 'list', 'database', mypubs2, logsegment
	**
	** they get more complete information in one pass.
	*/
	if ((@objtype = 'database') and (@db_devname = @currdbname))
	begin
		-- Reuse @msg instead of adding a @procname
		--
		select @msg = @currdbname + ".dbo.sp_dbxt_help_threshold"
		exec @msg @arg1
	end

	-- List any thresholds on this db. This has to appear here, after
	-- we've tried to select from #temp table. If the #temp table had
	-- any data, we would like to show that first, and follow it with
	-- some details on thresholds after that.
	--
	if (@objtype_val = @objtype_none) OR (@objtype = 'threshold')
	begin
		-- First check if the db is a valid db, when running simple
		-- list command. For 'list', 'th', don't worry about valid
		-- db, as sub-proc will catch that error (if any).
		--
		if (@objtype_val = @objtype_none)
		begin
			if (@db_devname is NOT NULL)
			begin
				exec @retval = sp_dbxt_validate_db @db_devname
							, 'sp_dbxt_list'
			end
			else
			begin
				-- No need to run list_threshold as we
				-- did not get any valid dbname to check for.
				--
				select @retval = 0
			end
		end
		else
		begin
			-- Use current db, if @dbname is NULL
			--
			if (@db_devname IS NULL)
			begin
				select @db_devname = @currdbname
			end
			select @retval = 1
		end
		
		/*
		print "@db_devname: %1! @currdbname: %2!"
			, @db_devname , @currdbname
		*/

		if (@retval != 0)
		begin
			-- Reuse @msg instead of adding a @procname
			--
			exec @retval = dbo.sp_dbxt_list_threshold
					      @db_devname
					    , @arg1
					    , @currdbname
					    , @seginfo_done output
					    , @trace
			if (@retval != 0)
				return @retval
		end
	end

	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:05 2003 
*/
/*
** raiserror Messages for dbxt_set [Total 12]
**
** 17871, "There is no segment named '%1!'."
** 18524, "%1!: Permission denied. This operation requires System Administrator (sa_role) role."
** 19151, "Usage: sp_dbextend 'set', { %1! } {, arguments ...}. Use: sp_dbextend 'help','set' for more information."
** 19152, "Usage: sp_dbextend 'set', 'threshold', @dbname, @segmentname, @freespace"
** 19153, "Usage: sp_dbextend 'set', 'database', @dbname, @segmentname { [, @growby ] [, @maxsize ] }"
** 19154, "Usage: sp_dbextend 'set', 'device', @devicename { [, @growby ] [, @maxsize ] }"
** 19196, "Space expansion policy already exists for object '%1!'. Use the MODIFY command to change the policy. Or, CLEAR this policy first, and then reissue the SET command. Use the LIST command to view the current policy."
** 19199, "You must be in database '%1!' to run this command. Issue: 'USE %2!', then run this procedure again."
** 19201, "%1!: Permission denied. This operation requires System Administrator (sa_role) role, or DBO of database '%2!'."
** 19208, "The 'set' operation for 'default' objects is not permitted. Users authorized with 'sa_role' should run the 'reload [defaults]' commands, instead."
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_set [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_set ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

/*
** This is just a dummy creation of this procedure as it is being called
** from here. This dummy sproc will be dropped soon after this procedure
** is created. The real procedure will be created much later in the
** install sequence.
*/
exec sp_dbxt_recreate_proc sp_dbextend
go

create procedure sp_dbextend as select 1
go

/*
** ***************************************************************************
** sp_dbxt_set_threshold
**
** DBXT_FUTURE: We have to consider cases where multiple segments on
** the same db have thrsholds set at the same # say 1000. If data is being
** loaded into all segments at the same time, it will be another reason for
** alter db to be serialized on the same db, and this could be avoided by
** providing helpful messages to the user to keep these segment-thresholds
** also far apart.
**
** Parameters:
**	@dbname		- DB on which to set the threshold.
**	@segmentname	- Segment in @dbname to set threshold on.
**	@freespace	- Free space value for threshold.
**	@currdbname	- DB fromwhich top-level sproc was executed.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_set_threshold
go

create procedure sp_dbxt_set_threshold (
				@dbname		varchar(256)	= NULL
			      , @segmentname	varchar(256)	= NULL
			      , @freespace	varchar(30) 	= NULL
			      , @currdbname	varchar(256)
)
as
begin
	declare @retval 	int

	select	 @retval = 1		-- expect things to fail initially

	-- Error checking for required arguments.
	if ((@dbname is NULL) OR (@segmentname is NULL) OR (@freespace is NULL))
	begin
		raiserror 19152
		return 1
	end

	if (@dbname != @currdbname)
	begin
		raiserror 19199, @dbname, @dbname
		return 1
	end

	-- Install the threshold procedure at requested freespace level.
	--
	exec @retval = sybsystemprocs.dbo.sp_dbxt_set_extend_db
						  @dbname
						, @segmentname
						, @freespace
						, 0	-- @lct
						, @currdbname

	return (case when @retval != 0 then @retval else 0 end)
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_ins_sysattributes
go

/*
** ***************************************************************************
** sp_dbxt_ins_sysattributes
**
** Wrapper procedure that actually does the insert into master.dbo.
** sysattributes taking a bunch of arguments from the caller. This
** serves several purposes:
**
**  1. Common code between 'database', 'device', both of which want
**     to do the same set of inserts.
**
**  2. Single-point begin-commit/rollback tran block.
**
**  3. Most important reason. If the 'set' operation is performed on
**     tempdb (for any segment), if this code was in-lined in the
**     respective callers, then we will fail with a 3917 error, reporting
**     that we cannot start a multi-db xact from tempdb, and then insert
**     into master db. To avoid this sitiation, this sproc exists in one
**     chunk, so that the caller can call this using master.dbo. notation.
**
** Returns:
**	0 - Insert xact committed successfully.
**	1 - Some errors. Xact will be rolled back.
{
*/
create procedure sp_dbxt_ins_sysattributes(
				  @dbname	varchar(256)
				, @segid	int
				, @db_dev_tag	char(2)
			        , @growby	varchar(30)
			        , @maxsize	varchar(30)
				, @growby_pct	int
				, @setdate	varchar(30)
) as
begin
	declare @whoami_cp	varchar(30)
	      , @dbxt_class	smallint
	      , @user_db_attr	smallint	-- user-specified DB attribute
	      , @growby_code	int
	      , @growbypct_code	int
	      , @maxsize_code	int

	-- DBXT_FUTURE: Would be good to push this to dbxt_attrmgr sproc
	-- module that can hide these hard-coded numbers inside of that.
	--
	-- Hard-coded values from dbxt_defaults.
	select    @dbxt_class 		= 19	-- AUTODBXT_CLASS
		, @growby_code		= 3
		, @growbypct_code	= 4
		, @maxsize_code		= 5
		, @whoami_cp		=
			   	  object_name(@@procid, db_id('sybsystemprocs'))

	-- Retrieve the attribute for user-specifie database props.
	-- Returns the value DBXT_USER_SPEC_DB or DBXT_USER_SPEC_DEV
	--
	select @user_db_attr = (select a.object_info1
				   from master.dbo.sysattributes a
				   where class = @dbxt_class
				     and attribute = 0	-- DBXT_DIRECTORY
				     and object_type = @db_dev_tag
				     and object_cinfo = 'user-specified'
				  )

	begin tran @whoami_cp

	if (@growby is NOT NULL)
	begin
		insert master.dbo.sysattributes (class, attribute, object_type,
						 object_cinfo, object,
						 object_info1, object_info2,
						 char_value, comments)

		values(@dbxt_class, @user_db_attr, @db_dev_tag, @dbname, @segid

			, case when @growby_pct = -1
			       then @growby_code
			       else @growbypct_code
			  end
			, 1	-- once SET, growby is enabled, by default.
			, @growby
			, @setdate
		)
		if (@@error != 0)
		begin
			rollback tran @whoami_cp
			return 1
		end
	end

	if (@maxsize is not NULL)
	begin
		insert master.dbo.sysattributes (class, attribute, object_type,
					      object_cinfo, object,
					      object_info1, object_info2,
					      char_value, comments)

		values(@dbxt_class, @user_db_attr, @db_dev_tag, @dbname, @segid
			, @maxsize_code
			, 1	-- once SET, maxsize is enabled, by default.
			, @maxsize
			, @setdate
		)
		if (@@error != 0)
		begin
			rollback tran @whoami_cp
			return 1
		end
	end

	commit tran @whoami_cp
	return 0
end
go

if (@@error != 0) select syb_quit()
go

-- ***************************************************************************

	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as dbname, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

exec sp_dbxt_recreate_proc sp_dbxt_set_database
go

/*
** ***************************************************************************
** sp_dbxt_set_database
**
** Set the properties for a database, and insert them in sysattributes.
**
** Parameters:
**	@dbname		- Name of db for which we want to set the rules.
**	@segmentname	- Segment name in @dbname
**	@growby		- Grow by value; possibly NULL.
**	@maxsize	- Max size value; possibly NULL.
**	@currdbname	- Name of db where 'set' was executed from.
{
*/
create procedure sp_dbxt_set_database (
				@dbname		varchar(256)	= NULL
			      , @segmentname	varchar(256)	= NULL
			      , @growby		varchar(30) 	= NULL
			      , @maxsize	varchar(30) 	= NULL
			      , @currdbname	varchar(256)
)
as
begin
	declare @retval 	int
	      , @sa_role	int
	      , @dbo		int
	      , @growby_m	float	-- @growby in megabytes
	      , @growby_pct	float 	-- %age value for @growby arg
	      , @maxsize_m	float	-- maxsize value in Mb.
	      , @segid		tinyint	-- segment ID
	      , @whoami_cp	varchar(30)
	      , @growby_str	varchar(30)
	      , @maxsize_str	varchar(30)
	      , @db_dev_code	char(2)
	      , @out_segid	int
	      , @msg		varchar(200)
	      , @getdate	varchar(20)
	      , @setdate	varchar(30)
	      , @growby_exists	int
	      , @maxsize_exists	int

	select	  @retval = 1		-- expect things to fail initially
		, @whoami_cp = object_name(@@procid, db_id('sybsystemprocs'))

	-- Error checking for required arguments.
	if (  (@dbname is NULL) OR (@segmentname is NULL)
	 OR  ((@growby is NULL) and (@maxsize is NULL) ) )
	begin
		raiserror 19153
		return 1
	end

	/*
	** It would have been nice to allow segmentname as NULL, and @dbname
	** is not the current db. This would allow a dbo of @dbname to be
	** in some other db, and still set the db's properties. But, then
	** we can't do permission checks to see if the user is the dbo of
	** @dbname or not. Hence, require that this command be run from the
	** db that @dbname is. (This is not that much of a restriction.)
	*/
	if (@dbname = 'default')
	begin
		-- 'set' is not permitted for 'default' objects. Have to use
		-- 'modify' instead.
		--
		raiserror 19208
		return 1
	end

	-- See if user has privileges on db that set is operating on.
	-- User could be doing cross-db set operation, and that is now being
	-- allowed.
	--
	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  @dbname
					, @sa_role output
					, @dbo output
	if ((@sa_role = 0) and (@dbo = 0))
	begin
		raiserror 19201, "sp_dbextend 'set', 'database'", @dbname
		return 1
	end

	-- Cache the segment info for the db that we are operating on.
	-- (Reuse @msg variable to avoid data bloat.)
	--
	exec @retval = sp_dbxt_ins_db_seginfo
				  @dbname
				, @whoami_cp
	if (@retval != 0)
		return 1

	-- Validate @growby specifier, and store either in mb or %age.
	--
	if (@growby is not NULL)
	begin
		exec @retval = sp_dbxt_parse_growby @growby, @growby_m out,
							@growby_pct out
		if (@retval != 0)
			return @retval
	end

	if (@maxsize is not NULL)
	begin
		exec @retval = sp_dbxt_parse_maxsize @maxsize,
						@maxsize_m out
		if (@retval != 0)
			return @retval
	end

	if (@segmentname is NOT NULL)
	begin
		select @segid = segment
		from #syssegments
		where name = @segmentname

		if (@segid IS NULL)
		begin
			raiserror 17871, @segmentname
			return 1
		end
	end

	select    @getdate 	= getdate()
		, @db_dev_code 	= 'DB'

	-- Error checking. Site-specific policy rules appear in sysattributes
	-- using (dbname, segmentid) pairs. See if there is one already for
	-- this pair. If so, raise an error, and instruct the user to either
	-- do DROP first, and then SET again, or use the MODIFY command.
	--
	if (@growby is NOT NULL)
	begin
		exec @growby_exists = sp_dbxt_exists_growby
							  @dbname
							, @segid
							, @db_dev_code
							, @growby_str output
							, @out_segid output
	end
	if (@maxsize is not NULL)
	begin
		exec @maxsize_exists = sp_dbxt_exists_maxsize
							  @dbname
							, @segid
							, @db_dev_code
							, @maxsize_str output
	end

	-- If either one of these rules already exist, report an error.
	--
	if ((@growby_exists = 1) or (@maxsize_exists = 1))
	begin
		-- DBXT_FUTURE: When object names grow in size, this code
		-- will have to change. For now, work with small sizes which
		-- are known to be sufficient.
		--
		select @msg = @dbname + ", " + @segmentname

		raiserror 19196, @msg

		/*
		** It would be very useful to list the output to help the
		** user here, but we run into a stack overflow issue due to
		** the fact that call to sp_autoformat adds 2 procedure
		** nesting levels. This is unavoidable. Skip this step.
		exec sp_dbextend 'list', 'database', @dbname, @segmentname
		*/
		return 1
	end

	exec sp_dbxt_gen_set_date "set", @getdate, @setdate out

	exec @retval = master.dbo.sp_dbxt_ins_sysattributes
				  @dbname
				, @segid
				, @db_dev_code
			        , @growby
			        , @maxsize
				, @growby_pct
				, @setdate

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_set_device
go

/*
** ***************************************************************************
{
*/
create procedure sp_dbxt_set_device (
				@devicename	varchar(256)	= NULL
			      , @growby		varchar(30)	= NULL
			      , @maxsize	varchar(30)	= NULL
) as
begin
	declare @retval 	int
	      , @sa_role	int
	      , @dbo		int
	      , @growby_m	float	-- @growby in megabytes
	      , @pct_loc	int	-- '%' char locator
	      , @growby_pct	int 	-- %age value for @growby arg
	      , @maxsize_m	float	-- maxsize value in Mb.
	      , @db_dev_code	char(2)
	      , @dbxt_class	tinyint	-- feature's attribute class
	      , @user_dev_attr	tinyint	-- user-specified DB attribute
	      , @growby_code	tinyint
	      , @growbypct_code	tinyint
	      , @maxsize_code	tinyint
	      , @msg		varchar(100)
	      , @getdate	varchar(20)
	      , @setdate	varchar(30)
	      , @growby_str	varchar(30)
	      , @maxsize_str	varchar(30)
	      , @growby_exists	int
	      , @maxsize_exists	int
	      , @out_segid	int

	-- Error checking for required arguments.
	if (    (@devicename is NULL)
	     OR ((@growby is NULL) and (@maxsize is NULL) ) )
	begin
		raiserror 19154
		return 1
	end

	if (@devicename = 'default')
	begin
		-- Only sa_role should run 'reload' command instead to 'set'
		-- 'default' attributes.
		--
		raiserror 19208
		return 1
	end

	-- Check on permissions; only interested in sa_role privilege.
	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  NULL
					, @sa_role output
					, @dbo output
	if (@sa_role = 0)
	begin
		raiserror 18524, "sp_dbextend 'set', 'device'"
		return 1
	end

	-- Validate @growby specifier, and store either in mb or %age.
	--
	exec @retval = sp_dbxt_parse_growby @growby, @growby_m out,
							@growby_pct out
	if (@retval != 0)
		return @retval

	if (@maxsize is not NULL)
	begin
		exec @retval = sp_dbxt_parse_maxsize @maxsize,
						@maxsize_m out
		if (@retval != 0)
			return @retval
	end

	select    @getdate = getdate()
		, @db_dev_code		= 'DV'

	exec sp_dbxt_gen_set_date "set", @getdate, @setdate out
	-- Error checking. IF there is already a site-specific policy for this
	-- device name, error out. Instruct user to either do a DROP first,
	-- followed by a SET again, or use the MODIFY command.
	--
	if (@growby is NOT NULL)
	begin
		exec @growby_exists = sp_dbxt_exists_growby
							  @devicename
							, NULL
							, @db_dev_code
							, @growby_str output
							, @out_segid output
	end
	if (@maxsize is not NULL)
	begin
		exec @maxsize_exists = sp_dbxt_exists_maxsize
							  @devicename
							, NULL
							, @db_dev_code
							, @maxsize_str output
	end

	if ((@growby_exists = 1) or (@maxsize_exists = 1))
	begin
		raiserror 19196, @devicename
		exec sp_dbextend 'list', 'device', @devicename
		return 1
	end

	exec @retval = master.dbo.sp_dbxt_ins_sysattributes
				  @devicename
				, NULL
				, @db_dev_code
			        , @growby
			        , @maxsize
				, @growby_pct
				, @setdate
	return 0
end
go

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
** sp_dbxt_set
**
** Top-level sproc that implements the 'set' command.
**
** Parameters:
**	@objtype	- Object type; see below.
**	@db_devname	- Db or device name.
**	@arg1		- Sub-command/object-specfic argument.
**	@arg2		- Sub-command/object-specfic argument.
**	@arg3		- Sub-command/object-specfic argument.
**	@currdbname	- Name of db where command was invoked from.
**
** Returns:
**	0	- If 'set' was executed without any errors.
**	1	- Otherwise.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_set
go

create procedure sp_dbxt_set (
			@objtype 	varchar(30)	= NULL
		      , @db_devname	varchar(256)	= NULL
		      , @arg1		varchar(256)	= NULL
		      , @arg2		varchar(30)	= NULL
		      , @arg3		varchar(30)	= NULL
		      , @currdbname	varchar(256)
)
as
begin
	declare @msg		varchar(100)
	      , @retval		int
	      , @direntry	tinyint
	      , @objtype_names	varchar(256)
	      , @whoami_cp	varchar(100)

	select  @msg = 'sybsystemprocs'		-- reuse variable

	select    @whoami_cp = object_name(@@procid, db_id(@msg)) + ": "

		-- current db of execution; reuse variable
		, @objtype_names = db_name()

	-- Verify that we are executing form sybsystemprocs.
	--
	if (@objtype_names != @msg)	-- db_name() != sybsystemprocs
	begin
		raiserror 19232, @whoami_cp, @msg, @objtype_names
		return 1
	end

	exec @retval = sp_dbxt_parse_itemtype 'objects'
					     , @objtype output
					     , 19194

	if ((@retval = -1) or (@retval = 0))
	begin
		if (@retval = 0)
		begin
			exec @direntry = sp_dbxt_get_direntry 'objects'
			exec sp_dbxt_build_args_list 'XT', @direntry,
						@objtype_names output

			raiserror 19151, @objtype_names
		end
		return 1
	end

	if (@objtype = 'database')
	begin
		exec @retval = dbo.sp_dbxt_set_database @db_devname
						  , @arg1
						  , @arg2
						  , @arg3
						  , @currdbname
	end
	else if (@objtype = 'device')
	begin
		-- Invoke using master.dbo. so that the xact is started in
		-- 'master', rather than the current db. If this command
		-- were to be issued from 'tempdb', we would get an
		-- 3917 (Can't start multi-db tran update in master with
		-- begin tran in tempdb... ) error.
		--
		exec @retval = dbo.sp_dbxt_set_device @db_devname,
						     @arg1,
						     @arg2
	end
	else if (@objtype = 'threshold')
	begin
		exec @retval = dbo.sp_dbxt_set_threshold
						  @db_devname
						, @arg1
						, @arg2
						, @currdbname
	end

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbextend
go
go
/*
** Generated by spgenmsgs.pl on Wed Aug 13 17:45:26 2003 
*/
/*
** raiserror Messages for dbxt_modify [Total 12]
**
** 18524, "%1!: Permission denied. This operation requires System Administrator (sa_role) role."
** 19159, "Usage: sp_dbextend 'modify', { 'database' | 'device' } {, arguments ...}. See: sp_dbextend 'help', 'modify' for more information."
** 19160, "Usage: sp_dbextend 'modify', 'database', @dbname, @segmentname, { 'growby' | 'maxsize' }, @newvalue"
** 19161, "Usage: sp_dbextend 'modify', 'device', @devicename, { 'growby' | 'maxsize' }, @newvalue"
** 19201, "%1!: Permission denied. This operation requires System Administrator (sa_role) role, or DBO of database '%2!'."
** 19203, "No policy found for database '%1!', segment name '%2!' for property '%3!'."
** 19204, "No policy found for device '%1!' for property '%2!'."
** 19207, "%1!: Pattern specifiers are not allowed for the argument '%2!' to this command."
** 19212, "The 'modify' operation for 'threshold' is not supported. Use the sp_modifythreshold procedure to perform the desired modification."
** 19227, "Cannot modify rules for database '%1!' as it does not exist. Only operation possible on nonexistent database(s) is: %2! '%3!'"
** 19232, "%1! Internal error. This procedure should be executed in database '%2!' but was instead executed in database '%3!'."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_modify [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_modify ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

-- ***************************************************************************
	/*
	** DUPLICATE CODE WARNING: This #temp table is created in the top-level
	** sproc sp_dbextend. Update this if the table's schema changes there.
	*/
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_modify_value
go

/*
** ***************************************************************************
** sp_dbxt_modify_value
**
** 'modify' the properties for an object ('growby' or 'maxsize'). This
** interfaces allows dbos to modify the properties for a given (db, segment).
** or for a given device. This also allows 'sa_role' to modify the properties
** for the 'default' (i.e. all) database or device.
**
** This proc does *NO* permissions checks. It assumes that the caller has
** filtered out non-authorized users from performing this modification.
**
** As part of the update of the value, the 'comments' column in sysattributes
** is also updated to capture the datetime when the modify operation was
** last performed. The format is as follows:
**
**	Initially comments would contain one of:
**
**	a)	set='<set datetime>' [comment for system-default rows]
**	b)	set='<set datetime>'
**	c)	mod='<last modify datetime> set='<set datetime>'
**
**	These will be changed as follows:
**
**	a)	mod='<last modify datetime> set='<set datetime>' [comment for system-default rows]
**	b)	mod='<last modify datetime> set='<set datetime>'
**	c)	mod='<new modify datetime> set='<set datetime>'
**
** Parameters:
**	@objname	- Name of the object (database or device)
**	@segementname	- Name of the segment in db @dbname.
**	@item		- String naming item to modify.
**	@newvalue	- String with new value for item.
**
** Returns:
**     -1 - Modify was not attempted as row was not found.
**	0 - Modify was successful, tran was committed.
**	1 - Modify was unsuccessful due to some errors. Tran rolled back.
{
*/
create procedure sp_dbxt_modify_value (
				@objname	varchar(256)	= NULL
			      , @segmentname	varchar(256)	= NULL
			      , @item		varchar(30)
			      , @db_dev_code	char(2)
			      , @newvalue	varchar(30)	= NULL
)
as
begin
	declare @retval 	int
	      , @class		smallint
	      , @objtype	varchar(2)
	      , @sa_role	int
	      , @dbo		int
	      , @item_val	smallint
	      , @attr_id	smallint
	      , @getdate	varchar(30)
	      , @item_list	varchar(256)
	      , @mod_date	varchar(100)
	      , @growby_m	float
	      , @growby_pct	float
	      , @maxsize_m	float
	      , @growby_code	int
	      , @growbypct_code	int
	      , @maxsize_code	int
	      , @whoami		varchar(30)

	select @whoami = object_name(@@procid, db_id('sybsystemprocs'))
				+ ": "

	exec @item_val = sp_dbxt_parse_itemtype 'modify'
					, @item output
					, 19194
	if ((@item_val = -1) or (@item_val = 0))
	begin
		return 1
	end

	select    @class 		= 19	-- AUTODBXT_CLASS
		, @objtype 		= @db_dev_code
		, @getdate 		= getdate()
		, @growby_code		= 3
		, @growbypct_code	= 4
		, @maxsize_code		= 5

	-- Find the attribute ID depending on the db being modified.
	-- It will either be the user-specified or system-default one.
	--
	select @attr_id = (select a.object_info1
			   from master.dbo.sysattributes a
			   where class = @class
			     and attribute = 0  -- DBXT_DIRECTORY
			     and object_type = @objtype
			     and object_cinfo =
			     		(case @objname
						when 'default'
						then 'system-default'
						else 'user-specified'
					 end
					)
			)
						
	-- Before performing the update, first check that there already
	-- a row for the arguments requested. If not, it would be an user
	-- error. We should at least inform the user that no update was
	-- being performed. (Benign error.)
	--
	if not exists (select 1
		       from master.dbo.sysattributes a
		       where class = @class
		         and attribute = @attr_id
			 and object_type = @db_dev_code
			 and object_cinfo = @objname
			 and (@segmentname is NULL
			      or object IN (select si.segment
			      		    from #syssegments si
					    where si.name like @segmentname
					      and si.dbname like @objname)
			     )

			 -- Identify exactly those rows that describe the
			 -- input item (growby/maxsize) being modified.
			 --
			 and (     (     @item_val = 1
			 	     and object_info1 IN
				     		(  @growby_code
						 , @growbypct_code)
				   )
			        or (     @item_val = 2
			 	     and object_info1 IN (@maxsize_code)
				   )
			      )
			)
	begin
		return -1
	end

	-- Parse the newvalue to see that it is in the proper format.
	-- (We don't care what it is, as long as it is in the proper
	--  format, we will just stick it in the catalog.)
	-- DBXT_FUTURE: There is probably some scope to do better
	-- validation here and in 'set' for the growby/maxsize values.
	-- And combine the code that parses these input values.
	--
	if (@newvalue is NOT NULL)
	begin
		if (@item = 'growby')
		begin
			exec @retval = sp_dbxt_parse_growby @newvalue
							, @growby_m out
							, @growby_pct out
			if (@retval != 0)
				return 1
		end
		else if (@item = 'maxsize')
		begin
			exec @retval = sp_dbxt_parse_maxsize @newvalue
							, @maxsize_m out
			if (@retval != 0)
				return 1
		end
	end

	exec sp_dbxt_gen_set_date "mod", @getdate, @mod_date out

	if (@item = 'growby')
	begin
		-- growby is funny. There can be a value that is defined
		-- as 'value', and the newvalue can be a %age. Or the other
		-- way around. Cope with that change by explicitly setting
		-- the growby-code (i.e. object_info1 column) depending
		-- on the way the new value was specified.
		--
		update master.dbo.sysattributes
		set	  char_value = @newvalue
			, object_info1 = case when @growby_pct = -1
					      then @growby_code
					      else @growbypct_code
					 end
			, comments = case charindex('mod', comments)
					when 0 then @mod_date
						    + " " + comments
					else stuff(comments, 1,
						   datalength(@mod_date),
						   @mod_date)
				     end
					
	        where class = @class
		 and attribute = @attr_id
		 and object_type = @db_dev_code
		 and object_cinfo = @objname
		 and (    @segmentname is NULL
		       or object IN (select si.segment
		       		     from #syssegments si
				     where si.name like @segmentname
				       and si.dbname like @objname
				    )
		     )
		 and object_info1 IN (@growby_code, @growbypct_code)
	end
	else if (@item = 'maxsize')
	begin
		update master.dbo.sysattributes
		set 	  char_value = @newvalue
			, comments = case charindex('mod', comments)
					when 0 then @mod_date
						    + " " + comments
					else stuff(comments, 1,
						   datalength(@mod_date),
						   @mod_date)
				     end
					
	        where class = @class
		 and attribute = @attr_id
		 and object_type = @db_dev_code
		 and object_cinfo = @objname
		 and (    @segmentname is NULL
		       or object IN (select si.segment
		       		     from #syssegments si
				     where si.name like @segmentname
				       and si.dbname like @objname
				    )
		     )
		 and object_info1 IN (@maxsize_code)
	end

	-- We do support like patterns for segmentname, so multi-row
	-- update should be possible here.
	--
	if (@@error != 0)
	begin
		return 1
	end

	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

drop table #syssegments
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_modify_database
go

/*
** ***************************************************************************
** sp_dbxt_modify_database
**
** 'modify' the properties for a database ('growby' or 'maxsize'). This
** interfaces allows dbos to modify the properties for a given (db, segment).
** This also allows 'sa_role' to modify the properties for the 'default'
** database.
**
** With appropriate privileges, the following are valid:
**
** . Modify one value for one segment in a single db:
**	sp_dbextend 'mod', 'da', pubs2, logsegment, 'growby', '4m'
**
** . Modify one value for multiple segments in a single db:
**	sp_dbextend 'mod', 'da', pubs2, "data%", 'growby', '4m'
**
** Parameters:
**	@dbname		- Name of the database.
**	@segementname	- Name of the segment in db @dbname.
**	@item		- String naming item to modify.
**	@newvalue	- String with new value for item.
**	@currdbname	- Current db where sproc was executed from.
**
** Returns:
**	0 - Modify was successful, tran was committed.
**	1 - Modify was unsuccessful due to some errors.
{
*/
create procedure sp_dbxt_modify_database (
				@dbname		varchar(256)	= NULL
			      , @segmentname	varchar(256)	= NULL
			      , @item		varchar(30)	= NULL
			      , @newvalue	varchar(30)	= NULL
			      , @currdbname	varchar(256)
)
as
begin
	declare @retval 	int
	      , @sa_role	int
	      , @dbo		int
	      , @oper_dbo	int	-- DBO of db operating on.
	      , @valid_db	int
	      , @item_val	int
	      , @permcheck_ok	tinyint
	      , @whoami_cp	varchar(30)
	      , @error_msg	varchar(100)

	select @whoami_cp = object_name(@@procid, db_id('sybsystemprocs'))
	     , @permcheck_ok = 0

	-- Allow segmentname to be NULL for the 'default' db, i.e. when
	-- modifyng the default db properties. There is no segment associated
	-- for such rules, and no point in asking user to enter anything.
	--
	if (    (@dbname is NULL)
	     or ((@dbname != 'default') and (@segmentname is NULL))
	     or ((@dbname = 'default') and (@segmentname is NOT NULL))
	     or (@item is NULL)
	)
	begin
		raiserror 19160
		return 1
	end

	-- We need to be in the db @dbname in order to process modify as
	-- we have to lookup the segment name for validation.
	-- We exempt this for the @dbname of 'default' as there will be
	-- no such db. This is just a tag for the default property of
	-- growby/maxrole for all defaults that are installed by the system.
	--
	if (@dbname = 'default')
	begin
		-- modify for the 'default' db surely needs sa_role.
		-- Check that.
		--
		exec sybsystemprocs.dbo.sp_dbxt_sa_dbo NULL
						     , @sa_role output
						     , @dbo output
		if (@sa_role = 0)
		begin
			raiserror 18524,
				"sp_dbextend 'modify', 'database','default'"
			return 1
		end
		select @permcheck_ok = 1
	end

	else if (@dbname != @currdbname)
	begin	-- {

		-- We do not support %pubs2% here as we need to know if the
		-- login is the DBO (firstly), and then we need to match on
		-- the segid stored v/s syssegments. (This is an artificial
		-- restriction that could be raised for sa_role.)
		--
		if (patindex("%[%]%", @dbname) != 0)
		begin
			exec sp_dbxt_gen_command_tag @whoami_cp, @dbname,
					@error_msg output
			raiserror 19207, @error_msg, "@dbname"
			return 1
		end

		else
		begin	-- {

			-- See if the @dbname is a valid one. Raise an error
			-- otherwise, as there is no segment info we can get
			-- if the db does not exist. (Basically, we restrict
			-- user from doing any modify on dbs that don't exist.
			-- They have just clear the rules for such dbs.)
			--
			exec @valid_db = sp_dbxt_validate_db @dbname
						, @whoami_cp
			if (@valid_db = 0)
			begin
				select @error_msg = "sp_dbextend 'clear', 'database', "
				-- Can't modify for non-existent dbs; Use clear.
				raiserror 19227
					, @dbname
					, @error_msg
					, @dbname

				return 1
			end

		end	-- }

	end	-- } else if (@dbname != @currdbname)

	-- If we are in the current db, or if we have not validated permissions,
	-- yet, do so now.
	--
	if (@permcheck_ok = 0)
	begin
		exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
						  @dbname
						, @sa_role output
						, @dbo output
		if ((@sa_role = 0) and (@dbo = 0))
		begin
			exec sp_dbxt_gen_command_tag @whoami_cp, @dbname,
						@error_msg output

			-- User is in the right db, but is not a DBO. Flag that.
			raiserror 19201, @error_msg, @dbname
			return 1
		end
	end

	exec @item_val = sp_dbxt_parse_itemtype 'modify'
					, @item output
					, 19194
	if ((@item_val = -1) or (@item_val = 0))
	begin
		return 1
	end

	-- Cache the syssegments info into #syssegments table.
	--
	exec @retval = sp_dbxt_ins_all_seginfo @dbname, @whoami_cp
	if (@retval != 0)
		return 1

	exec @retval = sp_dbxt_modify_value @dbname, @segmentname, @item,
					    'DB', @newvalue
	if (@retval = -1)
	begin
		-- Modification failed as no row matching the input args
		-- was found. Probably a user error.
		--
		raiserror 19203, @dbname, @segmentname, @item
		return 1
	end
	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

/*
** ***************************************************************************
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_modify_device
go

create procedure sp_dbxt_modify_device (
				@devicename	varchar(256)	= NULL
			      , @item		varchar(30)	= NULL
			      , @newvalue	varchar(30)	= NULL
) as
begin
	declare @class		smallint
	      , @objtype	varchar(2)
	      , @sa_role	int
	      , @dbo		int
	      , @item_val	int
	      , @retval		int
	      , @whoami_cp	varchar(30)
	      , @error_msg	varchar(100)

	select @whoami_cp = object_name(@@procid, db_id('sybsystemprocs'))

	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  NULL
					, @sa_role output
					, @dbo output

	-- To change any device's characterisitics, must have sa_role.
	--
	if (@sa_role = 0)
	begin
		exec sp_dbxt_gen_command_tag @whoami_cp, @devicename,
					@error_msg output
		raiserror 18524, @error_msg
		return 1
	end

	if ((@devicename is NULL) or (@item is NULL))
	begin
		-- Show usage information. Some non-NULL args were NULL.
		raiserror 19161
		return 1
	end

	exec @item_val = sp_dbxt_parse_itemtype 'modify', @item, 19194
	if ((@item_val = -1) or (@item_val = 0))
	begin
		return 1
	end

	exec @retval = sp_dbxt_modify_value @devicename, NULL, @item,
					    'DV', @newvalue
	if (@retval = -1)
	begin
		-- Modification failed as no row matching the input args
		-- was found. Probably a user error.
		--
		raiserror 19204, @devicename, @item
		return 1
	end

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_modify_threshold
go

/*
** ***************************************************************************
** sp_dbxt_modify_threshold:
**
** Right now this is not being called from the user interface. We do not
** provide an interface via 'modify' to modify the threshold. User can run 
** sp_modifythreshold to do that (already). Maybe we should raise a
** warning here directing the user to use that interface.
**
** This is currently called internally by the threshold firing mechanism
** to adjust the threshold's free space value so that it is not too close
** to the new LCT value for the logsegment.
**
** What we do is to keep moving up (and away) from the current LCT till
** we find a region of free space that is not already occupied by any other
** threshold. If we find such a region, we park this threshold at that free
** space value. Otherwise, we just return, leaving the threshold unchanged,
** and possibly at a 'disabled' state.
**
** Here is an attempt at explaining this pictorially. Before this threshold
** fired, say the layout was:
**
** LCT: Last chance threshold. XT: Xpansion threshold; this one.
**
**                                    | LCT (200)
**                                    v
** |------------------------------------------|
** |                         ^                |
** |                         | XT (400)
**
** Say the db was expanded by 4000 pages, so it now looks like:
**
**                                                                | LCT (300)
**                                                                |
**                                    | LCT (200)                 |
**                                    v                           v
** |------------------------------------------|- - - - - - - - - - - - - |
** |                         ^                |                ^         |
** |                         | XT (400)       |                |         |
**                           A                B                C         D
**                                                             XT (400)
**
** . As the threshold fired at A, the region A -> B is free space. (Or was.)
** . The alter db has expanded the database in the region of B -> D, so this
**   is also free space.
** . LCT has moved to value of 300 pages, and old XT remains at 400 pages,
**   shown at position C above. So it is clobbered by LCT's hysteresis region.
** . We have to move XT up in the region of C -> A searching for a region of
**   space that is not already occupied by some other threshold.
**
** . It is a reasonably safe assumption that the region B -> D would probably
**   not have any new thresholds on them, yet, as that would be too much of a
**   race. (We don't have thresholds define'able by %ages, yet.)
**
** . It is quite possible that there are some non-space expansion thresholds
**   in the region of old LCT (200) through B (old end of log segment). These
**   are still disabled, and might come in our way. We can't touch them or
**   remove them, as we don't know what they are.
**
** The search for the new region to move the XT is as follows:
**
**	- Find a threshold atleast a distance (4 * @@thresh_hysteresis) away
**	  from the new LCT region. (2 * @@thresh_hysteresis) above the LCT
**	  is dead because of the new LCT. (2 * @@thresh_hysteresis) below
**	  the new threshold (if any) is dead because of that one.
**
**	- If such a threshold exists, then park the new XT threshold mid-way
**	  between (that threshold + 2 * @@thresh_hysteresis) and
**		  (LCT - 2 * @@thresh_hysteresis). 
**	  This guarantees that the new location is not shadowed by any other
**	  threshold.
**
**	- If such a threshold were not found, move the XT threshold the
**	  same distance out side the shadow of the new LCT's region by
**	  which the LCT moved from its old to new location. What this achieves
**	  is that the new location is moved correspondingly to the growth
**	  of the log segment. The growth of the logsegment reflects in the
** 	  changed position of the LCT, so we reflect the same growth in the
**	  distance by which this XT moves.
**
**	- Validate that the new position selected is not within the shadow
**	  region of any other existing threshold.
**
**	- If all the rules succeed, apply the modify threshold on this XT,
**	  and return success. Otherwise, leave it as it is, and return failure.
**	  The space expansion threshold will remain disabled until someone
**	  manually figures out the right location for it.
**
** Parameters:
**	@dbname		- DB name where threshold lives.
**	@segmentname	- Segment in @dbname where threshold is set.
**	@freespace	- Free space (#logical pages) for threshold
**	@old_lct	- Old LCT for logsegment before the alter db operation.
**	@new_lct	- New LCT for logsegment after the alter db operation.
**	@alter_lpages	- # of logical pages that segment was altered by.
**	@simulate	- 0/1: Whether this is a dry-run.
**	@trace		- Trace level.
{
*/
create procedure sp_dbxt_modify_threshold (
			  @dbname	varchar(256)
			, @segmentname	varchar(256)
			, @freespace	int
			, @old_lct	int
			, @new_lct	int
			, @alter_lpages	int
			, @simulate	int 	= 1
			, @trace	int	= 0
) as
begin
	declare	@retval		int
	      , @sybprocdb	varchar(14)
	      , @thisdbname	varchar(256)
	      , @candidate	int	-- candidate threshold's free space.
	      , @prev_candidate	int
	      , @new_free_space	int	-- new location of problem threshold.
	      , @found_one	tinyint
	      , @dead_region	int	-- 2 ** hysteresis is dead region.
	      , @lct_delta	int	-- Distance by which LCT moved.
	      , @whoami		varchar(32)
	      , @whoami_fn	varchar(30)
	      , @num_candidates	smallint
	      , @new_lct_end	int

	-- Ensure that this sproc is being run from sybsystemprocs.
	--
	select @sybprocdb = 'sybsystemprocs'

	select    @whoami_fn 	= object_name(@@procid, db_id(@sybprocdb))
		, @thisdbname 	= db_name()
		, @dead_region 	= 2 * @@thresh_hysteresis

		, @retval	= 0
		, @found_one 	= 0
		, @candidate 	= 0	-- did not find any candidate.
		, @num_candidates	= 0

	-- ******************************************************************
	-- Although we are passed-in the @freespace value, it is really the
	-- free space at which this entire extension processed initially.
	-- In the meantime, this threshold might have been modified due to
	-- a move of the LCT. So, here find the actual current freespace
	-- value for this threshold. We have a restriction that there is only
	-- one auto expansion threshold per segment, so it is ok to just look
	-- for the only (first) one.
	-- DBXT_FUTURE: It would be nice if this sproc can percolate the new
	-- free space value to its caller, so that the caller can pass that
	-- value down on repeated calls in the same threshold execution. This
	-- will become necessary once we start allowing multiple auto expansion
	-- thresholds on the same segment at diff free space values.
	--
	select @freespace = t.free_space
	from dbo.systhresholds t, dbo.syssegments s
	where t.proc_name = 'sp_dbxt_extend_db'
	  and t.segment = s.segment
	  and s.name = @segmentname

		-- This is one past the dead region above the new LCT.
		-- The new threshold location can start anywhere from
		-- this point on.
		--
	select	  @new_lct_end = (@new_lct + @dead_region + 1)
		, @lct_delta	= (@new_lct - @old_lct)

	select @whoami = case @trace when 0 then NULL else @whoami_fn + ": " end

		-- In the search for candidate threshold such that there is
		-- enough gap below that, the lower end of the spectrum is
		-- this point above the new lct, from which the search starts.
		--
		, @prev_candidate = @new_lct_end

	if (@trace = 1)
	begin
		print "%1!Find new position for threshold at %2! when LCT moves from %3! to %4!"
			, @whoami, @freespace, @old_lct, @new_lct
	end

	/*
	if (@thisdbname != @sybprocdb)
	begin
		raiserror 19232, @whoami, @sybprocdb, @thisdbname
		return 1
	end
	*/

	-- Temp message; should never get raised as this is an internal sproc.
	if ((@dbname is NULL) OR (@segmentname is NULL)
	 OR (@segmentname != 'logsegment'))
	begin
		raiserror 19159
		return 1
	end
	
	-- See if there is a candidate threshold far enough from the upper
	-- region of the LCT. Use that as a marker to figure out where to place
	-- this threshold now.
	--
	declare curthresh cursor for
	select t.free_space
	from systhresholds t
	where t.segment = (select s.segment
			   from dbo.syssegments s
			   where s.name = @segmentname)
	  and t.free_space >= @new_lct_end
	order by free_space asc

	open curthresh

	fetch curthresh into @candidate

	while (@@sqlstatus = 0)
	begin
		select @num_candidates = @num_candidates + 1

		-- See if there is a clear region below this candidate
		-- where we can place the problem one. There should be
		-- no other thresholds within 2 regions of this candidate.
		--
		if ((select count(*)
		     from systhresholds t
		     where t.segment = (select s.segment
		     			from dbo.syssegments s
					where s.name = @segmentname)

		       and @candidate != t.free_space
		       and (@candidate > t.free_space)
		       and ((@candidate - 2 * @dead_region - 1)
		       		< t.free_space)
		    ) = 0
		  )
		  begin
			if (@trace = 1)
			begin
				print "%1!Found a candidate at %2!, prev candidate: %3!."
					, @whoami, @candidate
					, @prev_candidate
			end
		  	select @found_one = 1
		  	break
		end

		select @prev_candidate = @candidate
		fetch curthresh into @candidate
	end

	close curthresh

	deallocate cursor curthresh

	if (@found_one = 1)
	begin
		if (@prev_candidate != 0)
		begin
			select @new_free_space = @candidate
					- ((@candidate - @prev_candidate) / 2)
		end
		else
		begin
			select @new_free_space = @candidate - @dead_region - 1
		end
	end
	else if (@candidate > 0)
	begin
		if (@trace = 1)
		begin
			print "%1!Processed %2! candidate thresholds; Max one is: %3!"
				, @whoami , @num_candidates , @candidate
		end

		-- We did not find a single interleaving region which
		-- has no other thresholds with space between them for
		-- this new one. On exiting, @candidate will point to
		-- the highest threshold that exists on this segment.
		-- Move this problem one above that, by the same space
		-- that the LCT was moved by the alter db.
		--
		select @new_free_space = (@candidate 
					   + @dead_region + @lct_delta + 1)
	end
	else
	begin
		-- The region is wide open above this threshold. Move
		-- it past the LCT by sufficiently large dead regions.
		--
		if (@freespace >= @new_lct)
			select @new_free_space = (@freespace + 2 * @dead_region
							+ 1)
		else
			select @new_free_space = (@new_lct + 2 * @dead_region
							+ 1)
	end

	-- Validate our work... just in case.
	if (@new_free_space < 0)
	begin
		select @new_free_space = 0
			, @retval = 1
	end

	exec @retval = sp_modifythreshold @dbname, @segmentname
				, @freespace , 'sp_dbxt_extend_db'
				, @new_free_space
	if (@retval = 0)
	begin
		print "%1!Threshold for database '%2!' segment '%3!' moved from '%4!' to '%5!' logical pages."
			, @whoami, @dbname , @segmentname
			, @freespace, @new_free_space
	end
	else
	begin
		print "***** Potentially Fatal Error! Attempt to modify threshold for dbname '%1!' segment '%2!' at free space of '%3!' failed."
			, @dbname, @segmentname, @freespace
	end

	return @retval
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_modify
go

/*
** ***************************************************************************
** sp_dbxt_modify
**
** Top-level sproc that implements the 'modify' command.
**
** Parameters:
**	@objtype	- Object type; see below.
**	@db_devname	- Db or device name.
**	@arg1		- Sub-command/object-specfic argument.
**	@arg2		- Sub-command/object-specfic argument.
**	@arg3		- Sub-command/object-specfic argument.
**	@currdbname	- Name of db where command was invoked from.
**
** Returns:
**	0	- If 'modify' was executed without any errors.
**	1	- Otherwise.
{
*/
create procedure sp_dbxt_modify (
			@objtype 	varchar(30)	= NULL
		      , @db_devname	varchar(256)	= NULL
		      , @arg1		varchar(256)	= NULL
		      , @arg2		varchar(256)	= NULL
		      , @arg3		varchar(256)	= NULL
		      , @currdbname	varchar(256)	= NULL
) as
begin
	declare @sybprocs	varchar(14)
	      , @objtype_val	int
	      , @retval		int
	      , @retval2	int
	      , @objtype_both	tinyint
	      , @direntry	tinyint
	      , @objtype_names	varchar(256)
	      , @whoami_cp	varchar(30)

	select    @retval 	= 1
		, @objtype_both	= 0	-- NULL
		, @sybprocs	= 'sybsystemprocs'

	select	  @whoami_cp = object_name(@@procid, db_id(@sybprocs)) + ": "
		, @objtype_names = db_name()	-- reuse variable

	-- Verify that we are executing form sybsystemprocs.
	--
	if (@objtype_names != @sybprocs)	-- db_name() != sybsystemprocs
	begin
		raiserror 19232, @whoami_cp, @sybprocs, @objtype_names
		return 1
	end

	exec @objtype_val = sp_dbxt_parse_itemtype 'objects'
						  , @objtype output
						  , 19194

	-- error condition.
	if ((@objtype_val = -1) OR (@objtype_val = 0))
	begin
		if (@objtype_val = 0)
		begin
			raiserror 19159
		end
		return 1
	end

	-- Modify entries for a 'database'.
	if (@objtype = 'database')
	begin
		exec @retval = sp_dbxt_modify_database @db_devname
						     , @arg1
						     , @arg2
						     , @arg3
						     , @currdbname
	end

	-- Modify entries for a 'device'.
	else if (@objtype = 'device')
	begin
		exec @retval = sp_dbxt_modify_device @db_devname, @arg1,
							@arg2
	end

	-- Modify is not supported for thresholds; currently.
	else if (@objtype = 'threshold')
	begin
		raiserror 19159
		raiserror 19212
	end

	return (case when @retval != 0 then 1 else 0 end)
end
go	-- }

if (@@error != 0) select syb_quit()
go
go
/*
** Generated by spgenmsgs.pl on Fri Jan 23 12:42:20 2004 
*/
/*
** raiserror Messages for dbxt_help [Total 2]
**
** 19193, "Command '%1!' is either invalid, or non-unique. Valid command types are: %2!"
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_help [Total 43]
**
** 19149, "Usage: sp_dbextend [ arguments ... ]"
** 19150, "Usage: sp_dbextend help [, <command type> ]"
** 19152, "Usage: sp_dbextend 'set', 'threshold', @dbname, @segmentname, @freespace"
** 19153, "Usage: sp_dbextend 'set', 'database', @dbname, @segmentname { [, @growby ] [, @maxsize ] }"
** 19154, "Usage: sp_dbextend 'set', 'device', @devicename { [, @growby ] [, @maxsize ] }"
** 19156, "Usage: sp_dbextend 'clear', 'threshold', @dbname, @segmentname [, @freespace ]"
** 19157, "Usage: sp_dbextend 'clear', 'database' [, @dbname [, @segmentname ] ]"
** 19158, "Usage: sp_dbextend 'clear', 'device' [, @devicename ]"
** 19160, "Usage: sp_dbextend 'modify', 'database', @dbname, @segmentname, { 'growby' | 'maxsize' }, @newvalue"
** 19161, "Usage: sp_dbextend 'modify', 'device', @devicename, { 'growby' | 'maxsize' }, @newvalue"
** 19162, "Usage: sp_dbextend { 'list' | 'listfull' } [, 'database' [, @dbname [, @segmentname [, @ORDER_BY_clause ] ] ] ]"
** 19163, "Examples: sp_dbextend 'list'"
** 19164, "          sp_dbextend 'list', 'dat', mypubs2"
** 19165, "          sp_dbextend 'list', 'dat', mypubs2, logsegment"
** 19166, "          sp_dbextend 'list', 'dat', mypubs2, logsegment, 'order by segment, item'"
** 19167, "Usage: sp_dbextend { 'list' | 'listfull' } [, 'device' [, @devicename [, @ORDER_BY_clause ] ] ]"
** 19168, "Examples: sp_dbextend 'list', 'dev', pubs2_datadev1"
** 19169, "          sp_dbextend 'list', 'dev', NULL, 'order by name, item'"
** 19170, "Usage: sp_dbextend %1!, @dbname, @segmentname [, @iterations ]"
** 19171, "Valid command types are: %1!"
** 19172, "Usage: sp_dbextend 'trace', {'on' | 'off' }"
** 19173, "Usage: sp_dbextend 'reload [defaults]'"
** 19174, "Set space expansion threshold and rules for a database, segment."
** 19175, "Set space expansion rules for a device."
** 19176, "Size specifiers must be >= 0. Examples for size specifiers:"
** 19177, "  @freespace can be : '100', '200m', '0.5g'"
** 19178, "  @growby can be : '100', '200m', '0.5g', '10%%', '0', '0%%'"
** 19179, "  @maxsize can be : '100', '200m', '0.5g'"
** 19180, "Use 'default' as the @dbname, @devicename argument for server-wide default properties."
** 19181, "Clear space expansion threshold for a database."
** 19182, "Clear space expansion rules for a database or device."
** 19183, "List any existing space expansion rules for a database or device."
** 19186, "Execute database / device expansion thresholds *NOW* %1! time(s)."
** 19187, "Reload system-defaults for auto-expansion, leaving user-specified values unchanged."
** 19195, "Modify space expansion rules for a database or device."
** 19211, "Simulate execution of space expansion thresholds <n> times."
** 19221, "Usage: sp_dbextend 'check', 'database' [, @dbname [, @segmentname ] ]"
** 19222, "Check existing auto-expansion rules for a database, segment pair."
** 19228, "Usage: sp_dbextend { 'list' | 'listfull' }, [ 'threshold' [ , @dbname [, @segmentname ] ] ]"
** 19243, "Usage: sp_dbextend { 'enable' | 'disable' }, 'database' [, @dbname [, @segmentname ] ]"
** 19244, "Enable, or disable, automatic expansion policy for a database, segment pair, or server-wide."
** 19250, "Usage: sp_dbextend 'who' [, '<spid>' | 'block' | 'all' ]"
** 19251, "Show who is running the automatic database expansion currently; on what database/segment, or device. Argument 'block' shows only those tasks that are currently blocked in this process, if any."
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_help ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

exec sp_dbxt_recreate_proc sp_dbxt_help_cmd
go

/*
** sp_dbxt_help_cmd
**
** Work-horse routine that generates help for each command. This takes
** a command's ordinal # as the tag. Ordinal # '0' is reserved as a way
** to run through the help messages for all commands.
{
*/
create procedure sp_dbxt_help_cmd(
			  @command	varchar(30)
			, @cmdcode 	int
) as
begin
	declare @msg		varchar(250)
	      , @cmds_list	varchar(256)

	if (@cmdcode = 0) or (@command = 'set')		-- 'set'
	begin
		print " "
		exec sp_getmessage 19174, @msg output print @msg
		exec sp_getmessage 19152, @msg output print @msg
		exec sp_getmessage 19153, @msg output print @msg
		print ""

		exec sp_getmessage 19175, @msg output print @msg
		exec sp_getmessage 19154, @msg output print @msg
		print ""

		exec sp_getmessage 19180, @msg output print @msg
		exec sp_getmessage 19176, @msg output print @msg
		exec sp_getmessage 19177, @msg output print @msg
		exec sp_getmessage 19178, @msg output print @msg
		exec sp_getmessage 19179, @msg output print @msg
	end

	if (@cmdcode = 0) or (@command = 'clear')
	begin
		print " "
		exec sp_getmessage 19181, @msg output print @msg
		exec sp_getmessage 19156, @msg output print @msg
		print ""

		exec sp_getmessage 19182, @msg output print @msg
		exec sp_getmessage 19157, @msg output print @msg
		exec sp_getmessage 19158, @msg output print @msg
		print ""
		exec sp_getmessage 19180, @msg output print @msg
	end

	/*
	if (@cmdcode = 0) or (@command = 'listraw')
	begin
		print "Undocumented command; no help info available."
	end
	*/

	if (@cmdcode = 0) or (@command = 'modify')
	begin
		print " "
		exec sp_getmessage 19195, @msg output print @msg
		exec sp_getmessage 19160, @msg output print @msg
		exec sp_getmessage 19161, @msg output print @msg
		print ""
		exec sp_getmessage 19180, @msg output print @msg
		exec sp_getmessage 19176, @msg output print @msg
		exec sp_getmessage 19178, @msg output print @msg
		exec sp_getmessage 19179, @msg output print @msg
	end

	if (@cmdcode = 0) or (@command in ('list', 'listfull') )
	begin
		print " "
		exec sp_getmessage 19183, @msg output print @msg
		print " "
		exec sp_getmessage 19162, @msg output print @msg
		exec sp_getmessage 19163, @msg output print @msg
		exec sp_getmessage 19164, @msg output print @msg
		exec sp_getmessage 19165, @msg output print @msg
		exec sp_getmessage 19166, @msg output print @msg
		print " "
		exec sp_getmessage 19167, @msg output print @msg
		exec sp_getmessage 19168, @msg output print @msg
		exec sp_getmessage 19169, @msg output print @msg
		print " "
		exec sp_getmessage 19228, @msg output print @msg
	end

	if (@cmdcode = 0) or (@command = 'simulate')
	begin
		print " "
		exec sp_getmessage 19211, @msg output print @msg
		exec sp_getmessage 19170, @msg output print @msg, "'simulate'"
	end

	if (@cmdcode = 0) or (@command = 'execute')
	begin
		print " "
		exec sp_getmessage 19186, @msg output print @msg, "<n>"
		exec sp_getmessage 19170, @msg output print @msg, "'execute'"
	end

	-- Command type is 'help' or Unknown command type. Report usage info.
	if (@cmdcode = 0) or (@command = 'help')
	begin
		print " "
		exec sp_getmessage 19150, @msg output print @msg

		exec sp_dbxt_build_cmd_list 'XT', 0, @cmds_list output

		-- Add special command for help.
		select @cmds_list = "'all', " + @cmds_list

		exec sp_getmessage 19171, @msg output

		print @msg, @cmds_list
	end

	if (@cmdcode = 0) or (@command = 'trace')
	begin
		print " "
		exec sp_getmessage 19172, @msg output print @msg
	end

	if (@cmdcode = 0) or (@command = 'reload defaults')
	begin
		print " "
		exec sp_getmessage 19187, @msg output print @msg
		exec sp_getmessage 19173, @msg output print @msg
	end

	if (@cmdcode = 0) or (@command = 'check')
	begin
		print " "
		exec sp_getmessage 19222, @msg output print @msg
		exec sp_getmessage 19221, @msg output print @msg
	end
	if (@cmdcode = 0) or (@command IN ('enable', 'disable'))
	begin
		print " "
		exec sp_getmessage 19244, @msg output print @msg
		exec sp_getmessage 19243, @msg output print @msg
	end

	if (@cmdcode = 0) or (@command IN ('who'))
	begin
		print " "
		exec sp_getmessage 19251, @msg output print @msg
		print " "
		exec sp_getmessage 19250, @msg output print @msg
	end
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_help
go

/*
**	*** Help procedures ***
{
*/
create procedure sp_dbxt_help(@cmdtype varchar(30)	= NULL
) as
begin
	declare @msg		varchar(250)
	      , @cmds_list	varchar(256)
	      , @cmdcode	int

	if (@cmdtype is NULL)
	begin
		exec sp_getmessage 19149, @msg output print @msg
		exec sp_getmessage 19150, @msg output print @msg
		print " "

		/* set */
		exec sp_getmessage 19152, @msg output print @msg
		exec sp_getmessage 19153, @msg output print @msg
		exec sp_getmessage 19154, @msg output print @msg
		print " "

		/* clear */
		exec sp_getmessage 19156, @msg output print @msg
		exec sp_getmessage 19157, @msg output print @msg
		exec sp_getmessage 19158, @msg output print @msg
		print " "

		/* modify */
		exec sp_getmessage 19160, @msg output print @msg
		exec sp_getmessage 19161, @msg output print @msg
		print " "

		/* list */
		exec sp_getmessage 19162, @msg output print @msg
		exec sp_getmessage 19167, @msg output print @msg
		print " "

		/* check */
		exec sp_getmessage 19221, @msg output print @msg

		/* dryrun */
		exec sp_getmessage 19170, @msg output
		print @msg, "{ 'simulate' | 'execute' }"

		/* trace */
		exec sp_getmessage 19172, @msg output print @msg

		/* reload defaults */
		exec sp_getmessage 19173, @msg output print @msg

		/* enable/disable policies */
		exec sp_getmessage 19243, @msg output print @msg

		/* who command */
		exec sp_getmessage 19250, @msg output print @msg

		return 0
	end

	-- Special case check for: help, 'all'. This is an easy way for
	-- the user (and testing) to run through all help message(s) and
	-- get a complete syntax/help.
	--
	if (@cmdtype = 'all')
	begin
		-- 0 will print help for all commands.
		exec sp_dbxt_help_cmd @cmdtype, 0
		return 0
	end

	-- Convert command to an ordinal #, checking for uniquness/validity.
	--
	exec @cmdcode = sp_dbxt_name_to_ordnum 'XT', 0, 'commands'
						, @cmdtype output
	if (@cmdcode <= 0)
	begin
		exec sp_dbxt_build_cmd_list 'XT', 0, @cmds_list output

		-- Special-case command is prefixed here additionally.
		--
		select @cmds_list = "'all', " + @cmds_list

		raiserror 19193, @cmdtype, @cmds_list
		return 1
	end

	-- Run the help for an individual command.
	exec sp_dbxt_help_cmd @cmdtype, @cmdcode
	return 0
end
go	-- }

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Wed Aug 13 17:45:24 2003 
*/
/*
** raiserror Messages for dbxt_extend_db [Total 7]
**
** 17260, "Can't run %1! from within a transaction."
** 17871, "There is no segment named '%1!'."
** 18031, "This procedure can only affect thresholds in the current database.  Say 'USE %1!', then run this procedure again."
** 19197, "Usage: %1! @dbname, @segname [, @space_left_lpages [, @status ] ]."
** 19201, "%1!: Permission denied. This operation requires System Administrator (sa_role) role, or DBO of database '%2!'."
** 19254, "Automatic database expansion is currently disabled server-wide. Skip expansion of database '%1!' segment '%2!'. User with '%3!' role can use the 'ENABLE' command to re-enable database expansion server-wide."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_extend_db [Total 4]
**
** 19214, "Threshold action procedure '%1!' fired in db '%2!' on segment '%3!'. Space left: %4! logical pages ('%5!M')."
** 19218, "Database '%1!', segment '%2!' would be altered from an initial size of '%3!M' by '%4!M' for a resultant total size of '%5!M'."
** 19235, "Database '%1!' was altered by total size '%2!M' for segment '%3!'."
** 19254, "Automatic database expansion is currently disabled server-wide. Skip expansion of database '%1!' segment '%2!'. User with '%3!' role can use the 'ENABLE' command to re-enable database expansion server-wide."
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_extend_db ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

/* ===========================================================================
** sp_dbxt_extend_db()
**
** Threshold proc that gets fired as part of the threshold action for a given
** database/segment, and does the actual work of extending the database
** based on some internal and user-specified rules. User-specified rules
** are defined via sp_dbextend interface, and are stored in
** master.dbo.sysattributes.
**
** Parameters:
**	@dbname		- DB name for which this is the threshold sproc.
**	@segmentname	- Segment on which the threshold fired.
**	@space_left_lpgs- # of logical pages at which the threshold was set.
**	@status		- Threshold sproc status information (LCT or not).
**
** Choice of defaults:
**
** 1.	@space_left_lpgs is chosen as -1 as a convenience for the user to
**	simply issue:
**
**		isql> sp_dbextend 'execute', mypubs2, 'logsegment'
**
**	In this case, the sproc will figure out at what # of space left
**	pages value this threshold proc was already installed on the
**	provided segment, and will run a simulation for that value of
**	@space_left_lpgs.
**
**	  o If this sproc is installed as the threshold proc at multiple
**	    free space levels, choose the min one. (DBXT_FUTURE.)
**	  o If this threshold is not installed on the provide segment,
**	    provide a default of 256 logical pages (1 AU) to run the
**	    simulation.
**
** 2.	@status is chosen as 2, again as a convenience for above scenario.
**	We run the 'simulation' one time by default.
**
** 3. @simulate is no longer a boolean. It is a type code as follows:
**	0 - Background threshold procedure execution.
**	1 - User-driven simulation via 'simulate' command.
**	2 - User-driven execution via 'execute' command.
**
**	The reason this is a type code is that we need to distinguish between
**	the cases of user-driven execution (2) v/s threshold execution (0).
**	In case of the latter, the logsegment's LCT will most likely move
**	around, and might disable this threshold proc. So we cope with that
**	occurence in sp_dbxt_do_extend_db_on_dev. For user-driven execution,
**	most likely the LCT is not going to move, and we might even be firing
**	a threshold that is actually 'disabled'. In that case we dont' want
**	to move this sproc's threshold level.
**
** Returns:
**	Nothing.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_extend_db
go

create procedure sp_dbxt_extend_db (
			  @dbname		varchar(256) = NULL
			, @segmentname		varchar(256) = NULL
			, @space_left_lpgs	int = -1	-- simulation
			, @status 		int = 2		-- dry-run
) as 
begin
	declare @whoami		varchar(30)	-- Proc name tag for tracing
	      , @sa_role	int
	      , @dbo		int
	      , @segid		int
	      , @segbit		int
	      ,	@simulate	int		-- Dry-run for testing
	      , @user_invoked	int		-- Boolean; see below
	      , @trace		int
	      , @colon_loc	int		-- Position of ':' separator
	      , @iter_str	varchar(30)
	      , @iterations	int		-- # of iterations to simulate
	      , @ictr		int
	      , @space_left_MB	float		-- Space left in Mb
	      , @dbo_suid	int
	      , @retval		int
	      , @msg		varchar(256)
	      , @fmtstr		varchar(255)

	      , @segsize_initial_MB	float
	      , @segsize_final_MB	float
	      , @segincr_MB		float
	      , @segincr_total_MB	float

	-- Use plain-name, as it is used to lookup systhresholds also.
	select 	  @whoami = object_name(@@procid, db_id('sybsystemprocs'))

	-- Set this at the top-level, so that any one who does sp_who will
	-- see this as the tagname for this feature in sysprocesses.
	--
	SET clientapplname "Automatic Database Expansion"

	-- Disallow this sproc when run from within a transaction as the
	-- DDL commands it does cannot be run from inside a transaction.
	--
	if (@@trancount > 0)
	begin
		raiserror 17260, @whoami
		return 1
	end

	set transaction isolation level 1
	set chained off

	-- Print help info and exist if @dbname and @segname are NULL.
	--
	if ((@dbname is NULL) or (@segmentname is NULL))
	begin
		raiserror 19197, @whoami
		return 1
	end

	 /* Make sure we are in the right database */
	 if (@dbname != db_name())
	 begin
		-- In the wrong db. Need to run 'use db' first.
		raiserror 18031, @dbname
		return 1
	end

	-- Enforce permissions on who can run this procedure.
	-- Only dbo, 'sa', or 'sa_role' can run this procedure.
	--
	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
					  @dbname
					, @sa_role output
					, @dbo output

	if ( (@sa_role = 0) and (@dbo = 0) )
	begin
		-- Need sa_role or DBO of dbname to run this command.
		raiserror 19201, @whoami, @dbname
		return 1
	end

	-- Extract dry-run (simulation) semantics from @status bit.
	select @simulate = case when @status = 2 then 1 else 0 end

		-- Loop counters for iteration in case of simulate/execute
		, @ictr = 1
		, @iterations = 1

		-- Assume that this is ASE-invoked threshold proc being fired.
		, @user_invoked = 0

	-- *******************************************************************
	-- For user-invoked simulate/execute case, see if user passed-in a
	-- # of iterations to simulate/execute for. IF so, caller sproc has
	-- tacked that on to the segment name using <segmentname>:<number>
	-- notation. Extract the # of iterations out.
	--
	select @colon_loc = charindex(':', @segmentname)
	if (@colon_loc != 0)
	begin
		-- Handle case of:
		--	<segmentname>:<number>
		-- E.g:	logsegment:30
		-- We assume that when this happens, it is really the user
		-- driving the threshold execution. (We also assume that
		-- there will almost never be a segment named '<seg>:4',
		-- for instance. Otherwise, if we come here with that name,
		-- we will mistakenly conclude that this is a user-driven
		-- execution.)
		--
		select @iter_str = substring(@segmentname,
					     @colon_loc + 1,
					     (datalength(@segmentname)
					      - @colon_loc))

		select @segmentname = substring(@segmentname,
						1, @colon_loc - 1)

		select    @iterations = convert(int, @iter_str)

			-- This is user invocation using 'simulate' or
			-- 'execute' commands.
			--
			, @user_invoked = 1

			-- If we are really in simulate mode, then leave it
			-- as that. Otherwise, now that we know that this is
			-- a user-driven execution, change 'simulate' type
			-- to value. 2 => user-invoked execution.
			--
			, @simulate = case @simulate when 0 then 2 else 1 end
	end

	-- *******************************************************************
	-- Check to see if auto expansion is currently enabled server-wide.
	-- Report that as an error if user is running 'execute'. If we are
	-- being fired as a background task under threshold manager, or are
	-- doing a simulation, just do a print.
	--
	exec @retval = sp_dbxt_get_enabled
	if (@retval = 0)
	begin
		if (@simulate IN (0, 1))	-- 'simulate' or threshold mode
		begin
			exec sp_getmessage 19254, @msg output
			print @msg , @dbname, @segmentname , "sa_role"

			-- If we are really running as a background task,
			-- then there is nothing more to do. Otherwise, just
			-- do the simulation as user requested, to give them
			-- an idea of what would happen.
			--
			if (@simulate = 0)
				return 0
		end
		else	-- 'execute' mode
		begin
			raiserror 19254, @dbname, @segmentname, "sa_role"

			-- It is an error to try to execute the threshold
			-- when it is disabled server-wide.
			--
			return 1
		end
	end

	select @segid = segment from syssegments where name = @segmentname
	if (@segid is NULL)
	begin
		-- Report that the Segment does not exist.
		raiserror 17871, @segmentname
		return 1
	end

	-- Retrieve current tracing level from spt_values.
	--
	exec @trace = sybsystemprocs.dbo.sp_dbxt_get_tracelevel

	if (@trace = 1)
	begin
		print "%1! @simulate: %2! @user_invoked: %3! @iterations: %4!"
			, @whoami, @simulate , @user_invoked , @iterations

	end

	-- *******************************************************************
	select 	  distinct
		  -- OR in u.segmap from all rows
		  0 as segmaps			
		, d.name as devicename

		, d.high, d.low, d.vdevno
		, (d.high - d.low + 1) as devsize_vpgs

		, (convert(float, (d.high - d.low + 1)) * 2048 
				/ @@maxpagesize) as devsize_lpgs

		-- Compute sum used pages on this device, in
		-- number of logical pages.
		--
		, (select sum(convert(float, u2.size))
		   from master.dbo.sysusages u2
		   where u2.vdevno = d.vdevno) 
		   as devusedsize_lpgs

		-- (Total device size) - (sum of used size by all dbs)
		, ( ( ( convert(float, (d.high - d.low + 1) ) * 2048)
				/ @@maxpagesize)

		       -- Sum of used sizes from this device for all
		       -- dbs.
		    - (select sum(u3.size)
		       from master.dbo.sysusages u3
		       where u3.vdevno = d.vdevno) ) 

		as devfreesize_lpgs

	into	#segdev lock allpages

	from 	  master.dbo.sysdevices d
		, master.dbo.sysusages u
	where d.status & 2 = 2		-- only physical devices
	  and u.dbid = db_id(@dbname)	-- for this database
	  and u.vdevno = d.vdevno

	  -- Only in case of simulate (user_invoked) do we want to
	  -- to load any data to the #temp table. For the ASE-invoked
	  -- threshold procedure, all we want to do is to create the #temp
	  -- table, so that the sub-procs can find it. But we don't want to
	  -- load any data, as that data will never be used. The WHERE 1 = 0
	  -- clause will force the table to be empty, thereby saving on space
	  -- in tempdb. Additionally, if there is an error in the sproc logic
	  -- whereby in background threshold proc execution an attempt is made
	  -- to read data from this #segdev table, the presence of the empty
	  -- table will quickly alert us about such logic errors.
	  --
	  and (@simulate = 1 or 1 = 0)

	-- *******************************************************************
	-- Now go back and accumulate all the segmaps that exist on
	-- various disk pieces from each device that are allocated to
	-- the current database. Update that in the #temp table.
	-- (@trace: will force dump of #temp table, so we can examine the
	--  state for debugging.)
	--
	if (@simulate = 1)
	begin
		exec sybsystemprocs.dbo.sp_dbxt_upd_segmaps
						  @dbname
						, @segmentname
						, @simulate
						, @trace

		exec @segbit = sp_dbxt_segment_to_segbit @segid

		-- ***********************************************************
		-- Store the sizes of various devices at start of simulation
		-- in a #temp table, so that we can update the final values
		-- and report that back to the user..
		--
		select devicename
			, convert(varchar(30),
					(convert(float, devsize_vpgs) * 2)
						/ 1024)
				+ 'M'
				as initial_size
			, convert(varchar(30), '0M') as final_size

		into #segdev_summary

		from #segdev sd
		where (sd.segmaps & @segbit) = @segbit
	end

	-- Store the initial size of this db/segment; convert to MB
	exec @segsize_initial_MB = sybsystemprocs.dbo.sp_dbxt_segsize
							  @dbname
							, @segid
							, @simulate
	select @segsize_initial_MB =
			( (@segsize_initial_MB / (1024 * 1024))
				* @@maxpagesize )

		, @segincr_total_MB = 0

	-- *******************************************************************
	-- Help the user to run a simulation using existing space left.
	--
	if (@space_left_lpgs = -1)	-- User is running w/default value
	begin
		-- Simulate using min value of free_space of threshold 
		-- Find this auto expansion threshold that might have been
		-- installed by the 'set' command for this db/segment.
		-- There should be only one if the 'set' command was used
		-- always. If the user also used the sp_addthreshold interface
		-- to add this sproc as the threshold (at another free space)
		-- level), use the one with the lowest free space.
		--
		select @space_left_lpgs = min(free_space)
		from systhresholds
		where segment = @segid
		  and proc_name = @whoami

		if (@space_left_lpgs is NULL)
			select @space_left_lpgs = 256
	end

	select @space_left_MB = ((convert(float, @space_left_lpgs)
					    * @@maxpagesize) /
							(1024 * 1024) )

		, @retval = 0

	-- *******************************************************************
	-- Create a control table for this feature in tempdb. This is used
	-- to implement serialization of tasks that might alter databases,
	-- or resize disks.
	--
	-- Setup a loop so that in simulate/execute mode
	while ((@retval = 0) and (@ictr <= @iterations))
	begin
		if (@user_invoked = 1)
		begin
			-- Print start of each iteration message.
			exec sybsystemprocs.dbo.sp_dbxt_format_msg
						  19198
						, @fmtstr output
						, @msg output

			print @fmtstr, @ictr, @msg
		end

		exec sp_getmessage 19214, @msg output
		print @msg, @whoami, @dbname, @segmentname,
				@space_left_lpgs, @space_left_MB

		-- Execute the work-horse sproc that actually performs
		-- the database expansion. This is a self-contained proc,
		-- that figures out the work to be done and does it.
		-- It returns the size by which the extension was done.
		--
		exec @retval = sybsystemprocs.dbo.sp_dbxt_do_extend_db
						   @dbname
						 , @segmentname
						 , @segid
						 , @space_left_lpgs
						 , @status 
						 , @segincr_MB output
						 , @simulate
						 , @trace

		exec sp_getmessage 19235, @msg output
		print @msg , @dbname, @segincr_MB , @segmentname

		-- Update the final sizes in the summary table, and then
		select    @ictr = @ictr + 1
			, @segincr_total_MB = @segincr_total_MB
						+ @segincr_MB
	end

	-- ******************************************************
	-- Clean up after our selves, the tags we left behind to
	-- control who gets ordered to run first. See the proc
	-- sp_dbxt_synchronize_alterdbs where clientname is left
	-- behind as the token # for the current operation.
	--
	SET clientapplname NULL
	SET clientname NULL

	-- Report final 'state' of devices and databases after simulation.
	-- This helps user figure out what the final device sizes would
	-- be after a series of expansions.
	--
	if (@simulate = 1)
	begin
		if (@trace = 1)
		begin
			exec sybsystemprocs.dbo.sp_dbxt_show_segmaps @whoami
		end

		-- present it back to the user.
		--
		update #segdev_summary
		set final_size = convert(varchar,
					   (convert(float, sd.devsize_vpgs) * 2)
						/ 1024)
					+ 'M'
		from #segdev_summary summ
		   , #segdev sd
		where (sd.segmaps & @segbit) = @segbit
		  and summ.devicename = sd.devicename

		-- Print start of each iteration message.
		exec sybsystemprocs.dbo.sp_dbxt_format_msg
						  19217
						, @fmtstr output
						, @msg output

		print @fmtstr, @iterations, @msg
		print " "

		exec @retval = sp_autoformat
				  @fulltabname = "#segdev_summary"
				, @selectlist = "devicename, 'initial size' = initial_size, 'final size' = final_size"

		print " "
		exec sp_getmessage 19218 , @msg output

		select @segsize_final_MB =   @segsize_initial_MB
					   + @segincr_total_MB

		print @msg, @dbname , @segmentname , @segsize_initial_MB
			  , @segincr_total_MB
			  , @segsize_final_MB
	end
end
go	/* } */

if (@@error != 0) select syb_quit()
go

go
exec sp_procxmode 'sp_dbxt_extend_db', 'anymode'
go
grant execute on sp_dbxt_extend_db to public
go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 04:27:26 2003 
*/
/*
** raiserror Messages for dbxt_simulate [Total 4]
**
** 19170, "Usage: sp_dbextend %1!, @dbname, @segmentname [, @iterations ]"
** 19199, "You must be in database '%1!' to run this command. Issue: 'USE %2!', then run this procedure again."
** 19201, "%1!: Permission denied. This operation requires System Administrator (sa_role) role, or DBO of database '%2!'."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_simulate [Total 4]
**
** 19184, "NO REAL WORK WILL BE DONE. Simulate database / device expansion in a dry-run mode %1! time(s)."
** 19185, "These are the series of database/device expansions that would have happened if the threshold on database '%1!', segment '%2!' were to fire %3! time(s)."
** 19202, "Warning! The command %1! may fail at runtime as it requires the following roles that you do not possess: %2!."
** 19215, "To actually expand the database manually for this threshold, issue: %1! '%2!', '%3!', '%4!', '%5!'"
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_dryrun ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

/*
** dbxt_dryrun:
**
**	Top-level wrapper to build the arguments required to call
**	sp_dbxt_extend_db in 'simulate' (dry-run) or 'execute' (real-run)
**	mode.
**
** Parameters:
**	@dbname		- Database name.
**	@segmentname	- Segment name on which to simulate the firing of
**			  the threshold.
**	@numiters	- Number of iterations of firing to simulate.
**	@simulate	- 0/1: Whether simulate or execute mode
**
** Usage:
**	sp_dbextend 'dryrun', mypubs2, logsegment
**	sp_dbextend 'dryrun', mypubs2, logsegment, '<n>'
**
** This procedure also doubles as a way for DBAs to test out how the
** threshold machinery will work when it is fired at run-time.
** sa_role user can invoke this procedure using the 'simulate' or 'execute'
** commands for <n> # of iterations of the auto expansion machinery.
**
** Examples for dry-run execution of this procedure:
**
**	isql> sp_dbextend 'simulate', mypubs2, 'logsegment'
**
**	Simulate firing of the threshold proc at the set auto expansion
**	threshold and go through the process once.
**
**	isql> sp_dbextend 'simulate', mypubs2, 'logsegment', '5'
**
**	Simulate firing of the threshold proc and go through the process
**	5 times in a loop, accounting for space consumed on each iteration.
**
**    What happens is that we go through the built-in logic of figuring out
**    which device to alter on, which device to resize etc, and the same
**    behaviour is obtained as if the threshold were actually fired.
**
**    This is a hook for the DBA to work-around instances where we have
**    a situation where the DB expansion still did not occur properly even
**    after the threshold was fired.
**
** Returns:
**	0	- All is ok. No errors.
**	1	- Some errors in arguments, or execution of simulator.
{
*/
exec sp_dbxt_recreate_proc sp_dbxt_dryrun
go

create procedure sp_dbxt_dryrun (
			  @dbname	varchar(256)	= NULL
			, @segmentname	varchar(256)	= NULL
			, @numiters	varchar(10)	= NULL
			, @simulate	int		= 1
) as
begin
	declare @msg		varchar(200)
	      , @sa_role	int
	      , @dbo		int
	      , @iterations	int
	      , @ictr		int
	      , @retval		int
	      , @banner		varchar(255)
	      , @fmtstr 	varchar(255)
	      , @whoami		varchar(100)

	      			-- 'segmentname>:<iterations>' string
	      , @segnameIters	varchar(300)
	      , @trace		int
	      , @status		int	-- pass to sp_dbxt_extend_db

	if ((@dbname is NULL) or (@segmentname is NULL))
	begin
		raiserror 19170, "{ 'simulate' | 'execute' }"
		return 1
	end

	if (@dbname != db_name())
	begin
		raiserror 19199, @dbname, @dbname
		return 1
	end

	select @whoami = '"'
			 + "sp_dbextend "
			 + case @simulate
				when 1
				then "'simulate'"
				else "'execute'"
			   end
			 + ", "
			 + @dbname
			 + '"'

		, @dbo = 0, @sa_role = 0

	/*
	** Only sa_role or dbo of current db is allowed to run 'simulate'
	** or 'execute' modes. Others just should not be allowed to do
	** this.
	*/
	exec sybsystemprocs.dbo.sp_dbxt_check_dbo
					  @dbname
					, @dbo output

	if (@dbo = 0)
	begin
		-- User is not dbo, at least does he have sa_role?
		exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
						  NULL
						, @sa_role output
						, @dbo output
		if (@sa_role = 0)
		begin
			-- Really, need to be DBO/sa_role to run this command.
			raiserror 19201, @whoami, @dbname
			return 1
		end
	end

	-- It is DBO, but running 'execute'. Did we already know that they
	-- have sa_role?
	--
	if ((@sa_role = 0) and (@simulate != 1))
	begin
		-- User might be dbo in this db, but does he also have
		-- sa_role. Disk resize needs sa_role to succeed. Warn
		-- user about that. Inline this check here rather than
		-- calling sp_dbxt_sa_dbo as that routine will raise 567
		-- error if user is non-sa_role. In this case, we just
		-- want to raise a warning, so that error will be annoying.
		--
		if (charindex("sa_role", show_role()) > 0)
		begin
			select @sa_role = proc_role("sa_role")
		end

			if (@sa_role = 0)
			begin
				-- Issue a warning that in 'execute' mode there
				-- is the risk that disk resize will fail if
				-- attempted as this fellow does not have
				-- sa_role.
				--
				exec sp_getmessage 19202, @msg output
				print @msg, @whoami, "'sa_role'"
		end
	end

	-- Use of convert() will ( *should* ) trap illegal values.
	select @iterations = case when @numiters is NULL then 1
				  else convert(int, @numiters)
			     end
	       , @ictr = 1

	exec @trace = sp_dbxt_get_tracelevel

	if (@simulate = 1)
	begin
		-- Banner msg about dry-run work. NO real work will be done.
		exec sp_getmessage 19184, @msg out
		print @msg, @iterations

		-- More intro info for user about what is going on here.
		exec sp_getmessage 19185, @msg out
		print @msg, @dbname, @segmentname, @iterations
	end

	-- 'status' word conveys to sp_dbxt_extend_db whether it is
	-- a simulation or the real execution.
	--
	select @status = (case @simulate when 1 then 2 else 0 end)

	-- Perform the simulation using the installed value for freespace
	-- when the 'set' command was last executed. For the purpose of this
	-- simulation, we don't really care what the @freespace setting is.
	-- We just want to pretend that we are out of space, and run a
	-- simulation.
	--
	-- Build the ':'-separated string after the segmentname to
	-- pass down the # of times to perform the simulation.
	--
	select @segnameIters = @segmentname
				+ ":"
				+ convert(varchar, @iterations)

	/*
	** Pass arguments by name, so that we can allow the sproc
	** to choose its default values for those arguments that
	** we don't want to worry about. @status = 2 tells the
	** the threshld procedure to run in dry-run mode.
	*/
	exec @retval = sp_dbxt_extend_db  @dbname = @dbname
					, @segmentname = @segnameIters
					, @status = @status

	-- Inform user w/message on command to run to actually extend db.
	-- And only if the above call did not fail.
	--
	if ((@retval = 0) and (@simulate = 1))
	begin
		exec sp_getmessage 19215, @fmtstr output

		print @fmtstr, 'sp_dbextend', 'execute', @dbname, @segmentname,
			       @iterations
	end

	return @retval
end
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 04:27:26 2003 
*/
/*
** raiserror Messages for dbxt_reload_defaults [Total 4]
**
** 17231, "No login with the specified name exists."
** 18524, "%1!: Permission denied. This operation requires System Administrator (sa_role) role."
** 19199, "You must be in database '%1!' to run this command. Issue: 'USE %2!', then run this procedure again."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_reload_defaults [Total 1]
**
** 19209, "Deleted %1! rows and inserted %2! new rows for system defaults."
*/
/*
** End spgenmsgs.pl output.
*/

print "Installing procedures from dbxt_defaults ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

set flushmessage on
go

exec sp_dbxt_recreate_proc sp_dbxt_reload_defaults
go

/*
** **************************************************************************
** sp_dbxt_reload_defaults
**
** Load default entries describing the default ASE-supplied behaviour
** for expanding databases (for each segment) and devices.
**
** This script will first delete any pre-existing defaults in master.dbo.
** sysattributes and will then re-load the new defaults as described in
** this script. The delete-insert allows us to ship updated defaults in
** an later release, or even for the customer to tweak these defaults
** manually.
**
** NOTE: Customers are not expected to tweak these defaults, but if there
**	 ever arises such a need, we should instruct customers to tweak it
**	 so that they leave behind a trail in the 'comments' column of
**	 master.dbo.sysattributes documenting what was changed, when, why.
**	 This will help support trace through any issues.
**
** Parameters:
**	@currdbname	- Db name where sproc is executed from.
**	@action_code	- Action-code;
**			  1 - update version string and exit. (When updating
**			      version string, @currdbname contains a date-time
**			      stamp that is passed-in by buildinstall.)
**			  2 - Full reload, deleting all rows and not saving
**			      the version string row. This is employed when
**			      running the installdbextend script and is
**			      followed by a call to this sproc with an
**			      action_code of 1.
{
*/
create procedure sp_dbxt_reload_defaults(
			    @currdbname		varchar(256)
			  , @action_code	int	= 0
) as
begin
	declare @getdate		varchar(30)
	      , @setdate		varchar(30)
	      , @sa_role		int
	      , @dbo			int
	      , @attr_dir		int
	      , @feature_code		char(2)
	      , @objtype_db		char(2)
	      , @objtype_dev		char(2)
	      , @default		varchar(10)
	      , @server_wide		varchar(20)
	      , @system_default		varchar(20)
	      , @user_specified		varchar(20)
	      , @class			int
	      , @attribute		int
	      , @skip_some_attrs	int
	      , @start_attr_num		int
	      , @maxmb_lo		int	-- lower limit of range
	      , @maxmb_hi		int	-- upper limit of range
	      , @growby_pct		varchar(10)
	      , @min_db_alter		int
	      , @min_dev_resize		int
	      , @comment_string		varchar(80)
	      , @version_string		varchar(256)
	      
	      , @enabled_code		int	-- feature/policy is enabled

	      , @enable_code		tinyint
	      , @trace_code		tinyint
	      , @growby_code		tinyint
	      , @growbypct_code		tinyint
	      , @maxsize_code		tinyint
	      , @delrowcount		int
	      , @insrowcount		int
	      , @version_string_exists	int

	-- Initialize commonly used local variables.
	--
	select	  @getdate		= getdate()
		, @class		= 19	-- AUTODBXT_CLASS
		, @attr_dir		= 0	-- DBXT_DIRECTORY
		, @feature_code		= 'XT'
		, @objtype_db		= 'DB'
		, @objtype_dev		= 'DV'
		, @server_wide		= 'server-wide'
		, @default		= 'default'
		, @system_default	= 'system-default'
		, @user_specified	= 'user-specified'
		, @growby_pct 		= '10%'

		-- When feature's install script is run, all default
		-- policies are enabled.
		--
		, @enabled_code		= 1

		-- Code for object_info1 column for various types of data.
		-- (See dbxt_list where this is decoded neatly.)
		--
		, @enable_code		= 1
		, @trace_code		= 2
		, @growby_code		= 3
		, @growbypct_code	= 4
		, @maxsize_code		= 5

		-- Leave aside some attr #s for future use.
		, @start_attr_num	= 31

		-- Skip some attribute values between ranges of values.
		, @skip_some_attrs	= 20

		, @comment_string = "System-supplied default percentage expansion for each "

		-- Version string for this feature. Insert an initial portion.
		-- buildinstall, will tack on the current date when the
		-- installdbextend is built.
		--
		, @version_string = "Adaptive Server Automatic Database Expansion/1.0/"

	-- If we are only updating the version string (only happens from
	-- buildinstall phase), do that and exit. Normal users, or even sa
	-- should never have to run this command directly. They will only
	-- get this interface via installdbextend script.
	--
	if (@action_code = 1)
	begin
		-- We only want 'sa_role' fellow to run installdbextend script,
		-- which is the only one that should issue with this option.
		-- Check for that.
		--
		exec sybsystemprocs.dbo.sp_dbxt_sa_dbo
						  'master'
						, @sa_role output
						, @dbo output

		if ((@dbo = 0) and (@sa_role = 0))
		begin
			raiserror 18524, "sp_dbextend 'reload'"
			return 1
		end
		update master.dbo.sysattributes
		set char_value = char_value + @currdbname
		where class 		= @class
		  and attribute 	= @attr_dir
		  and object_type 	= @feature_code
		  and object_cinfo 	= @server_wide
		return 0
	end

	-- Avoid running into unnecessary 3917 errors if 'sa' were to run
	-- this from 'tempdb'. Tell them that they need to be in master first.

	if (@currdbname != 'master')
	begin
		raiserror 19199, 'master', 'master'
		return 1
	end

	exec sybsystemprocs.dbo.sp_dbxt_sa_dbo 
					  @currdbname
					, @sa_role output
					, @dbo output

	if ((@dbo = 0) and (@sa_role = 0))
	begin
		raiserror 18524, "sp_dbextend 'reload'"
		return 1
	end

	-- *******************************************************************
	-- Remember whether there were previously loaded rows by running
	-- installdbextend. If so, we want to save the row with the version
	-- string as that captures the date when the sprocs were built first.
	--
	select @version_string_exists = count(*)
	from master.dbo.sysattributes
	where class 		= @class
	  and attribute 	= @attr_dir
	  and object_type 	= @feature_code
	  and object_cinfo 	= @server_wide

	if ((@version_string_exists = 1) and (@action_code = 0))
	begin
		-- Save the version string row from a prior install.
		--
		select @version_string = char_value
		from master.dbo.sysattributes
		where class 		= @class
		  and attribute 	= @attr_dir
		  and object_type 	= @feature_code
		  and object_cinfo 	= @server_wide
	end

	/*
	** ================================================================
	** DELETE OLD ENTRIES FROM A PRIOR RUN.
	** ================================================================
	*/
    begin tran dbxt_defaults

	delete master.dbo.sysattributes
	where class = @class

	  -- This gets the directory entries for system-default rules
          and (attribute = @attr_dir	-- DBXT_DIRECTORY

	   	-- This gets the actual rows for system-defaults.
		-- These are the rows defined with attribute IDs:
		--
		--	DBXT_DEFAULT_DB DBXT_DEFAULT_DEV
		--
	   	or attribute IN (select object_info1
				    from master.dbo.sysattributes
				    where class = @class

				      	-- DBXT_DIRECTORY
				      and attribute = @attr_dir
				      and object_cinfo 
						IN (  @system_default
						    , @server_wide))
	     )

	select    @delrowcount = @@rowcount
		, @insrowcount = 0

	exec sp_dbxt_gen_set_date "set", @getdate, @setdate out

	/*
	** ================================================================
	** LOAD DIRECTORY ENTRIES WITH ATTRIBUTE = 0 (DBXT_DIRECTORY)
	** ================================================================
	*/

	-- exec sp_dbxt_list '0', NULL, NULL, NULL, 2, @currdbname

	/*
	** FIRST Load the directory entries for feature-wide policy.
	** Remember not to do it if we are re-running reload command.
	*/
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object_info1
					-- , object_info2
					, char_value
					, comments)
	values (  @class		-- class
		, @attr_dir		-- attribute
		, @feature_code		-- object_type
		, @server_wide		-- object_cinfo
		, @start_attr_num	-- object_info1

		-- , @enabled_code		-- object_info2
		, @version_string	-- char_value
		, @setdate		-- comments
			+ " ["
			+ "Feature-wide attributes effective server-wide."
			+ "]"
	)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	/*
	** Load the directory entries for system-default rows.
	** This generates attribute ID DBXT_DEFAULT_DB for object_info1.
	*/
	select @start_attr_num = @start_attr_num + @skip_some_attrs

	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object_info1,
					comments)
	values (  @class		-- class
		, @attr_dir		-- attribute
		, @objtype_db		-- object_type
		, @system_default	-- object_cinfo
		, @start_attr_num	-- object_info1
		, @setdate		-- comments
		+ " ["
		+ "Attribute for system-specified default policy for databases"
		+ "]"
	)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- This generates attribute ID DBXT_DEFAULT_DEV for object_info1.
	select @start_attr_num = @start_attr_num + @skip_some_attrs

	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object_info1,
					comments)
	values (  @class		-- class
		, @attr_dir		-- attribute
		, @objtype_dev		-- object_type
		, @system_default	-- object_cinfo
		, @start_attr_num	-- object_info1
		, @setdate		-- comments
		+ " ["
		+ "Attribute for system-specified default policy for devices"
		+ "]"
	)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- This generates attribute ID DBXT_USER_SPEC_DB for object_info1.
	select @start_attr_num = @start_attr_num + @skip_some_attrs

	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object_info1,
					comments)
	values (  @class		-- class
		, @attr_dir		-- attribute
		, @objtype_db		-- object_type
		, @user_specified	-- object_cinfo
		, @start_attr_num	-- object_info1
		, @setdate		-- comments
		+ " ["
		+ "Attribute for user-specified policy for databases"
		+ "]"
	)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- This generates attribute ID DBXT_USER_SPEC_DEV for object_info1.
	select @start_attr_num = @start_attr_num + @skip_some_attrs

	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object_info1,
					comments)
	values (  @class		-- class
		, @attr_dir		-- attribute
		, @objtype_dev		-- object_type
		, @user_specified	-- object_cinfo
		, @start_attr_num	-- object_info1
		, @setdate		-- comments
		+ " ["
		+ "Attribute for user-specified policy for devices"
		+ "]"
	)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	/*
	** ================================================================
	** LOAD DIRECTORY ENTRIES WITH FOR 'server-wide' behaviours.
	** ================================================================
	*/
	select    @attribute = (select object_info1
				from master.dbo.sysattributes
				where class = @class
				  and attribute = @attr_dir
				  and object_type = @feature_code
				  and object_cinfo = @server_wide)

	-- ---------------------------------------------------------
	-- Feature-wide auto-growth for databases / devices is ON (enabled).
	-- Row w/attr ID DBXT_SERVER_WIDE inserted.
	--
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					object_info2,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @feature_code		-- object_type
		, @server_wide		-- object_cinfo
		, NULL			-- object
		, @enable_code		-- object_info1
		, @enabled_code		-- object_info2
		, 'on'			-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + "Enable default expansion policy."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- ---------------------------------------------------------
	-- Feature-wide Tracing for database / devices  is OFF.
	-- Row w/attr ID DBXT_SERVER_WIDE inserted.
	--
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @feature_code		-- object_type
		, @server_wide		-- object_cinfo
		, NULL			-- object
		, @trace_code		-- object_info1
		, 'off'			-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + "Tracing of default expansion policy."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	/*
	** ================================================================
	** LOAD DIRECTORY ENTRIES WITH FOR 'default' database behaviours.
	** ================================================================
	*/
	/*
	** These insert statements load default rules for expansion for
	** any (database,segment) pair, and for any device.
	**
	** For databases, *always* use a default 10% growby for all sizes.
	** For devices, *always* use a default 10% growby for all sizes.
	*/

	/*
	** Default expansion rule for databases is 10%, subject to a
	** 'minimum' of (1 AU, or 1MB) for each 'alter database' command.
	*/
	select    @attribute = (select object_info1
				from master.dbo.sysattributes
				where class = @class
				  and attribute = @attr_dir
				  and object_type = @objtype_db
				  and object_cinfo = @system_default)

		-- Min size to alter db by is min(1Mb, size of 1 AU)
		--
		, @min_db_alter = (case @@maxpagesize
					when 2048	then 1
					else (256 * @@maxpagesize) / 
						(1024 * 1024)
				   end)
					
	-- ---------------------------------------------------------
	-- Auto-growth for 'default' databases is ON (enabled).
	-- Row w/attr ID DBXT_DEFAULT_DB inserted.
	--
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @objtype_db		-- object_type
		, @default		-- object_cinfo
		, NULL			-- object
		, @enable_code		-- object_info1
		, 'on'			-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + "Enable default expansion policy."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- ---------------------------------------------------------
	-- Tracing for 'default' database is OFF.
	-- Row w/attr ID DBXT_DEFAULT_DB inserted.
	--
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @objtype_db		-- object_type
		, @default		-- object_cinfo
		, NULL			-- object
		, @trace_code		-- object_info1
		, 'off'			-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + "Tracing of default expansion policy."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- ---------------------------------------------------------
	-- Default growth for db/segment is 10%
	-- Row w/attr ID DBXT_DEFAULT_DB inserted.
	--
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					object_info2,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @objtype_db		-- object_type
		, @default		-- object_cinfo
		, @min_db_alter		-- object
		, @growbypct_code	-- object_info1
		, @enabled_code		-- object_info2
		, @growby_pct		-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + @comment_string
			 + "database."
			 + " Minimum of "
			 + convert(varchar, @min_db_alter)
			 + "M for each alter database."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- ******************************************************
	-- NOTE THERE IS NO DEFAULT MAXSIZE FOR DB/SEGMENT.
	-- ******************************************************

	/*
	** ================================================================
	** LOAD DIRECTORY ENTRIES WITH FOR 'default' device behaviours.
	** ================================================================
	*/
	/*
	-- ---------------------------------------------------------
	** Default expansion rule for devices is 10%, subject to a
	** 'minimum' of 4M per device. The value of 4M is chosen so that
	** it can fit at least one allocation unit on the largest page
	** size; 16K.
	*/
	select    @attribute = (select object_info1
				from master.dbo.sysattributes
				where class = @class
				  and attribute = @attr_dir
				  and object_type = @objtype_dev
				  and object_cinfo = @system_default)

		-- Min size to alter db by is min(1Mb, size of 1 AU)
		--
		, @min_dev_resize = 4

	-- ---------------------------------------------------------
	-- Auto-growth for 'default' devices is ON (enabled).
	-- Row w/attr ID DBXT_DEFAULT_DEV inserted.
	--
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @objtype_dev		-- object_type
		, @default		-- object_cinfo
		, NULL			-- object
		, @enable_code		-- object_info1
		, 'on'			-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + "Enable default device expansion policy."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- ---------------------------------------------------------
	-- Tracing for 'default' devices is OFF.
	-- Row w/attr ID DBXT_DEFAULT_DEV inserted.
	--
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @objtype_dev		-- object_type
		, @default		-- object_cinfo
		, NULL			-- object
		, @trace_code		-- object_info1
		, 'off'			-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + "Tracing of default device expansion policy."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- Row w/attr ID DBXT_DEFAULT_DEV inserted.
	insert master.dbo.sysattributes(class, attribute, object_type,
					object_cinfo, object, object_info1,
					object_info2,
					char_value, comments)
	values (  @class		-- class
		, @attribute		-- attribute
		, @objtype_dev		-- object_type
		, @default		-- object_cinfo
		, @min_dev_resize	-- object
		, @growbypct_code	-- object_info1
		, @enabled_code		-- object_info2
		, @growby_pct		-- char_value
		, @setdate + 		-- comments
			 + " ["
			 + @comment_string
			 + "device."
			 + " Minimum of "
			 + convert(varchar, @min_dev_resize)
			 + "M for each disk resize."
			 + "]"
		)
	if (@@error != 0)
	begin
		rollback tran dbxt_defaults
		select syb_quit()
	end
	select @insrowcount = @insrowcount + 1

	-- ******************************************************
	-- NOTE THERE IS NO DEFAULT MAXSIZE FOR DEVICES.
	-- ******************************************************

    commit tran dbxt_defaults

    exec sp_getmessage 19209, @comment_string output
    print @comment_string, @delrowcount, @insrowcount
    	
    return 0
end
go

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_reset
go

/*
** **************************************************************************
** sp_dbxt_reset
**
** Special-case command to reset the synchronization machinery when it were
** to go awry. All tasks register themselves in a control table, using a token.
** This token value is 'displayed' by the expansion processes from the time
** they start the expansion process till they are done in the sysprocesses.
** clientname column.
**
** If certain tasks leave behind control locks in the control table, and the
** tasks go away, this procedure cleans up the control locks.
**
{
*/
create procedure sp_dbxt_reset
as
begin
	declare @retval	int

	select c.token, c.spid, c.dbname, c.segmentname
	from tempdb.dbo.syb_auto_db_extend_control c
	where c.token NOT IN (select convert(int, p.clientname)
			      from master.dbo.sysprocesses p
			      where p.clientapplname = "Automatic Database Expansion"
			     )
end
go	-- }

if (@@error != 0) select syb_quit()
go

exec sp_dbxt_recreate_proc sp_dbxt_who
go

/*
** **************************************************************************
** sp_dbxt_who
**
** Special sp_who-like interface to help detect blocked situations.
**
** Usage: sp_dbextend 'who' [, argument ] will eventually come down here as:
**
**	sp_dbxt_who	- Will generate the lists of all tasks that are of
**			  *this* application from sysprocessess and also from
**			  the control table.
**
**	sp_dbxt_who 'block'
**			- Will generate info about only those spids that are
**			  involved in the blocking.
**
**	sp_dbxt_who '<spid#>'
**			- Will generate info about a spid that is currently
**			  running, if any.
**
**	sp_dbxt_who 'all'
**			- Will generate info about all active spids in the
**			  server in the format that the who output comes from
**			  this interface. Presented as a convenience to the
**			  user to quickly see who all is running in the server
**			  in a consistent output format.
{
*/
create procedure sp_dbxt_who(
			@loginame	varchar(30)	= NULL
) as
begin

	declare @low		int
	      , @high		int
	      , @all_tasks	tinyint
	      , @spidlow	int
	      , @spidhigh	int
	      , @spid_mintoken	int	-- Spid holding smallest token currently
	      , @spid_blocker	int	-- blocking spid
	      , @where_clause	varchar(50)

	set nocount on

	-- loginame NULL should only generate a short listing of auto
	-- expansion tasks.
	-- Provide for 'all' interface to generate listing for all tasks,
	-- just as a convenience to show the same output that sp_who does.
	--
	if ((@loginame is NULL) or (@loginame = 'all'))
	begin
		select    @low = @@minsuid, @high = @@maxsuid
			, @spidlow = @@minspid, @spidhigh = @@maxspid
			, @where_clause = NULL

		select @all_tasks = case @loginame when NULL then 0 else 1 end
	end

	-- ****************************************************************
	-- 'loginame' arg doubles as a convenience to do different things.
	--
	--	Pass in spid of interest to track through two tables.
	--	Pass in 'block' to only dump the blocking spid's info.
	--
	else if ((@loginame is not NULL) and (@loginame != 'block'))
	begin
		select    @low = suser_id(@loginame)
			, @high = suser_id(@loginame)
			, @all_tasks = 0

		if @low is NULL
		begin
			if @loginame like "[0-9]%"
			begin
				select @spidlow = convert(int, @loginame),
				     @spidhigh = convert(int, @loginame),
				     @low = 0, @high = @@maxsuid

				-- Setup the where clause also to be
				-- used to extract rows from the
				-- control table.
				--
				select	@where_clause = "WHERE spid BETWEEN "
							+ convert(varchar,
								  @spidlow)
							+ " AND "
							+ convert(varchar,
								  @spidhigh)
			end
			else
			begin
				raiserror 17231
				return (1)
			end
		end
	end

	-- Generate the info from sysprocesses for all the threshold tasks,
	-- that are running in the background, for this application. Order
	-- them by token #, so that we can detect which task is holding the
	-- token and is not releasing it.
	--
	select p.spid
		-- , p.clientapplname
		, token = p.clientname
		, blk_spid = p.blocked

		-- Token held, if any, by blocking spid as found in the
		-- control table.
		--
		, blk_token = (select convert(int, c.token)
			       from tempdb.dbo.syb_auto_db_extend_control c
			       where c.spid = p.blocked)

		, object = p.clienthostname
		-- , p.status
		, cmd = rtrim(p.cmd)
		-- , p.program_name
		, dbname = db_name(dbid)
		, usr = user_name(uid)
		, 'procname' = isnull(object_name(p.id, p.dbid),
			 object_name(p.id, db_id('sybsystemprocs')))
		-- , p.linenum
		-- , p.stmtnum
	into #sysprocesses
	from master.dbo.sysprocesses p

	-- Here is where we implement the 'all' argument. If user gave
	-- nothing, then only show auto expansion processes. If user gave
	-- 'all', show all processes in the server.
	--
	where ((@all_tasks != 0)
		  or (      p.clientapplname = "Automatic Database Expansion"
	  	  	and p.program_name = "<threshold>"
	  	  	and p.status = 'background'
		     )
	      )
	  and p.suid between @low and @high
	  and p.spid between @spidlow and @spidhigh

	-- *****************************************************************
	-- Start displaying output.
	-- *****************************************************************
	--
	if (@all_tasks = 1)	-- Generating who for all tasks.
	begin
		-- Show all the auto-expansion processes from sysprocesses.
		--
		exec sp_autoformat
			  @fulltabname = #sysprocesses
			, @orderby = 'order by spid, token'

		-- Also generate the info from the control table that might be
		-- causing some blocking.
		--
		exec sp_autoformat
			  @fulltabname = 'tempdb.dbo.syb_auto_db_extend_control'
			, @selectlist = 'dbname, segmentname, devicename, spid, token'
			, @whereclause = @where_clause
			, @orderby = 'order by token'

	end
	else	-- User asked for summary info only.
	begin
		-- Having generated the full lists, go back and extract out the
		-- summary info for the first spid in the control table that
		-- is blocking the whole processing. Show info about that task,
		-- and the spid that is blocking him (if any) as you find it
		-- in #sysprocesses.
		--
		select @spid_mintoken
				= isnull((select c.spid
				   from tempdb.dbo.syb_auto_db_extend_control c
				   where c.token = 
				   		(select min(c2.token)
						 from tempdb.dbo.syb_auto_db_extend_control c2)
				  ), 0)

		-- Find the spid of the blocking task on the spid that is now
		-- holding the token.
		--
		select @spid_blocker = blk_spid
		from #sysprocesses
		where spid = @spid_mintoken
		if (@spid_blocker IS NULL)
			select @spid_blocker = 0

		select @where_clause = "WHERE spid IN ("
					+ convert(varchar, @spid_mintoken)
					+ ","
					+ convert(varchar, @spid_blocker)
					+ ")"

		/*
		print "@spid_mintoken: '%1!' @spid_blocker: '%2!' %3!"
			, @spid_mintoken , @spid_blocker, @where_clause
		*/

		exec sp_autoformat
			  @fulltabname = #sysprocesses
			, @whereclause = @where_clause

		-- When extracting info from the control table, we only deal
		-- with the 'spid' column. No blk_spid exists in that table.
		--
		exec sp_autoformat
			  @fulltabname = 'tempdb.dbo.syb_auto_db_extend_control'
			, @selectlist = 'dbname, segmentname, devicename, spid, token'
			, @whereclause = @where_clause
			, @orderby = 'order by token'
	end

	set nocount off
end
go

if (@@error != 0) select syb_quit()
go

go
/*
** Generated by spgenmsgs.pl on Sun Aug 10 03:54:03 2003 
*/
/*
** raiserror Messages for dbxt_dbextend [Total 5]
**
** 17260, "Can't run %1! from within a transaction."
** 19193, "Command '%1!' is either invalid, or non-unique. Valid command types are: %2!"
** 19210, "Syntax error. Extra arguments passed to '%1!' command. Run: %2! 'help', '%3!' for usage information."
** 19213, "Invalid argument or unsupported command: %1!."
** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
*/
/*
** sp_getmessage Messages for dbxt_dbextend [Total 0]
*/
/*
** End spgenmsgs.pl output.
*/
print "Installing procedures from dbxt_dbextend ..."
go

use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go

exec sp_dbxt_recreate_proc sp_dbextend
go

/*
** ***************************************************************************
** sp_dbextend
**
**	Top-level user-interface procedure for auto-db expansion feature.
**
**	This procedure is the command-control for the entire interface. It
**	receives the command from the user, parses it, and then calls other
**	sub-procs to execute on each command.
**
** Returns:
**	0	- All checks are ok. Execution is ok.
**	1	- Some errors (arguments, usage) or permission(s) issues.
{
*/
create procedure sp_dbextend (
			@command	varchar(30)	= NULL
		      , @objtype	varchar(256)	= NULL
		      , @db_devname	varchar(256)	= NULL
		      , @arg1		varchar(256)	= NULL
		      , @arg2		varchar(30)	= NULL
		      , @arg3		varchar(30)	= NULL
)
as
begin
	declare @retval		int	-- From called sub-procedure.
	      , @cmdcode	int	-- Ordinal # for command
	      , @whoami		varchar(30)
	      , @currdbname	varchar(256)
	      , @procname	varchar(100)
	      , @listmode	int
	      , @spid		int
	      , @cmd_flag	int	-- Boolean; on/off etc.

	select @retval = 1	-- Expect failure.

		-- Simple name, as it's used for error msgs etc.
		, @whoami = object_name(@@procid, db_id('sybsystemprocs'))

	if (@@trancount > 0)
	begin
		-- Cannot run this sproc from a transaction.
		raiserror 17260, @whoami
		return 1
	end

	set transaction isolation level 1
	set chained off
	set nocount on

	-- Save the user's current db, as we need that to determine whether
	-- user has DBO privileges. Most sprocs are executed using 
	-- sybsystemprocs.dbo. prefix to ensure that we are running the
	-- right version of the sproc that this feature ships with.
	--
	select @currdbname = db_name()

	-- Allow non-sa users (or even non-dbo users) to run without any
	-- arguments, at least so that the dbo can get some help info before
	-- working on a particular command. (It is difficult to further restrict
	-- this to regular users when we have absolutely no arguments.)
	-- Remaining permissions checks will be done shortly hereafter.
	--
	if (@command IS NULL)
	begin
		exec @retval = sybsystemprocs.dbo.sp_dbxt_help @objtype
		return 0
	end

	-- Convert command to an ordinal #, checking for uniquness/validity.
	--
	exec @cmdcode = sybsystemprocs.dbo.sp_dbxt_name_to_ordnum
						  'XT'
						, 0
						, 'commands'
						, @command output
	if (@cmdcode <= 0)
	begin
		declare @cmds_list	varchar(256)
		exec sybsystemprocs.dbo.sp_dbxt_build_cmd_list
						  'XT'
						, 0
						, @cmds_list output

		raiserror 19193, @command, @cmds_list
		return 1
	end

	-- ****************************************************************
	-- Create a temp table that will be populated later by one of
	-- the sub-procs. This caches the segment id/name info for the
	-- db that the sub-command is operating on.
	-- The inserted_by column tracks the 1st sproc that stashed the
	-- data here and is used for reporting its name in case of an
	-- error when another sproc (in another code-path) is also trying
	-- to re-stash the data. (We should only maintain one copy of
	-- all data in both tables.)
	--
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, name, status
	into #syssegments lock allpages
	from syssegments
	where 1 = 0

	-- ****************************************************************
	-- Create a temp table to cache the info from systhresholds. This
	-- will be populated by one of the sub-procs.
	--
	select db_name() as 'dbname'
		, convert(varchar(30), 'xx') as 'inserted_by'
		, segment, free_space, status, proc_name, suid, currauth
	into #systhresholds lock allpages
	from systhresholds
	where 1 = 0

	/*
	** ****************************************************************
	** No top-level permissions checks are being done in this procedure
	** as the permissions vary depending on the command being run and
	** granularity of the operation. Permission checking will be
	** implemented by each sub-command's procedure.
	** ****************************************************************
	*/
	if (@command = 'set')
	begin
		exec @retval = sybsystemprocs.dbo.sp_dbxt_set
						  @objtype
						, @db_devname
						, @arg1
						, @arg2
						, @arg3
						, @currdbname
	end
	else if (@command = 'clear')
	begin
		if (@arg3 is NOT NULL)
		begin
			-- Report on extra arguments, and fail.
			raiserror 19210, @command, @whoami, @command
			return 1
		end

		exec @retval = sybsystemprocs.dbo.sp_dbxt_clear
						  @objtype
						, @db_devname
						, @arg1
						, @arg2
						, @currdbname
	end
	else if (@command = 'modify')	-- 'modify'
	begin

		exec @retval = sybsystemprocs.dbo.sp_dbxt_modify
						  @objtype
						, @db_devname
						, @arg1
						, @arg2
						, @arg3
						, @currdbname
	end
	else if (@command IN ('list', 'listfull', 'listraw')) 
	begin
		if (@command IN ('list', 'listfull'))
		begin
			if (@arg3 is NOT NULL)
			begin
				-- Report on extra arguments, and fail.
				raiserror 19210, @command, @whoami, @command
				return 1
			end
		end
		else if (@command IN ('listraw'))
		begin
			if  (@arg1 is NOT NULL)
			 or (@arg2 is NOT NULL)
			 or (@arg3 is NOT NULL)
			begin
				-- Report on extra arguments, and fail.
				raiserror 19210, @command, @whoami, @command
				return 1
			end
		end
		select @listmode = case @command
					when 'list'	then 0
					when 'listfull'	then 1
					when 'listraw'	then 2
				   end

		exec @retval = sybsystemprocs.dbo.sp_dbxt_list
						  @objtype
						, @db_devname
						, @arg1
						, @arg2
						, @listmode
						, @currdbname
	end
	else if (@command IN ('simulate', 'execute'))
	begin
		if (@arg2 is NOT NULL) or (@arg3 is NOT NULL)
		begin
			-- Report on extra arguments, and fail.
			raiserror 19210, @command, @whoami, @command
			return 1
		end
		select @cmd_flag = case @command
					when 'simulate' then 1
					else 0
				   end
		exec @retval = dbo.sp_dbxt_dryrun
						  @objtype
						, @db_devname
						, @arg1
						, @cmd_flag
	end

	else if (@command = 'help')
	begin
		exec @retval = sybsystemprocs.dbo.sp_dbxt_help @objtype
	end
	else if (@command = 'trace')
	begin
		if (@arg1 is NOT NULL) or (@arg2 is NOT NULL)
		 or (@arg3 is NOT NULL)
		begin
			-- Report on extra arguments, and fail.
			raiserror 19210, @command, @whoami, @command
			return 1
		end
		exec @retval = sybsystemprocs.dbo.sp_dbxt_trace @objtype
	end
	else if (@command = 'reload defaults')
	begin
		if (@arg1 is NOT NULL) or (@arg2 is NOT NULL)
		 or (@arg3 is NOT NULL)
		begin
			-- Report on extra arguments, and fail.
			raiserror 19210, @command, @whoami, @command
			return 1
		end
		exec @retval = sybsystemprocs.dbo.sp_dbxt_reload_defaults
						  @currdbname
	end

	else if (@command = 'check')
	begin
		if (@arg2 is NOT NULL) or (@arg3 is NOT NULL)
		begin
			-- Report on extra arguments, and fail.
			raiserror 19210, @command, @whoami, @command
			return 1
		end
		exec @retval = sybsystemprocs.dbo.sp_dbxt_check
						  @objtype
						, @db_devname
						, @arg1
						, 0	-- no errors
						, @currdbname
	end

	else if (@command = 'version')
	begin
		-- Extract version string of feature. (Reuse variable.)
		select @currdbname = NULL
		select @currdbname = char_value
		from master.dbo.sysattributes
		where class = 19
		  and attribute = 0
		  and object_type = 'XT'
		  and object_cinfo = 'server-wide'

		if (@currdbname IS NOT NULL)
		begin
			print "%1!", @currdbname
			select @retval = 0
		end
	end

	else if (@command = 'who')
	begin
		exec @retval = sybsystemprocs.dbo.sp_dbxt_who @objtype
	end

	else if (@command IN ('enable', 'disable'))
	begin
		if (@arg3 is NOT NULL)
		begin
			-- Report on extra arguments, and fail.
			raiserror 19210, @command, @whoami, @command
			return 1
		end

		select @cmd_flag = case @command
					when 'enable' then 1
					else 0
				   end
		exec @retval = sybsystemprocs.dbo.sp_dbxt_enable
						  @objtype
						, @db_devname
						, @arg1
						, @cmd_flag
						, @currdbname
	end
	else
	begin
		raiserror 19213, @command
		return 1
	end
	set nocount off
	return @retval
end
go	/* } */

if (@@error != 0) select syb_quit()
go

use master
go

set flushmessage on
go

/*
** ***************************************************************************
** Having created this procedure, load the default rows in master.dbo.
** sysattributes, so that when the user runs installdbextend, they are all
** ready to go.
*/
declare @retval int
      , @dbname varchar(30)

select @dbname = db_name()

-- Do a hard-reload, deleting every existing of system-rows, and re-install.
--
exec @retval = sp_dbxt_reload_defaults @dbname, 2
if ((@retval != 0) or (@@error != 0))
begin
	print "Error running sp_dbextend: @retval=%1! @@error=%2!"
		, @retval , @@error
	select syb_quit()
end
go

/*
** ***************************************************************************
** Create a control table as part of the installation of this application.
** The control table resides in tempdb, owned by 'sa'.
*/
use tempdb
go

-- The control table should have been created at the top of the install
-- process.
--
grant SELECT on syb_auto_db_extend_control to public
grant INSERT on syb_auto_db_extend_control to public
grant DELETE on syb_auto_db_extend_control to public
go

/*
** ***************************************************************************
** Create one in 'model' db also so that on every reboot this is populated
** in the system tempdb. (Each user created tempdb will also gain this copy,
** but that should not be too much of a problem.)
*/
use model
go

sp_dbxt_create_control_table
go

grant SELECT on syb_auto_db_extend_control to public
grant INSERT on syb_auto_db_extend_control to public
grant DELETE on syb_auto_db_extend_control to public
go

set flushmessage off
go

-- Reset, so that (subsequent) grant execute on public works ok.
use sybsystemprocs
go

if (db_name() != "sybsystemprocs")
begin
	/*
	** 19529, "Cannot open database '%1!'. Check the availability of this database and retry the installation."
	*/
	raiserror 19529, "sybsystemprocs"
	select syb_quit()
end
go
go
exec sp_procxmode 'sp_dbextend', 'anymode'
go
grant execute on sp_dbextend to public
go

use master
go

exec sp_dbxt_reload_defaults '15.5/EBF 17340 SMP/P/x86_64/Enterprise Linux/ase155/2391/64-bit/OPT/Mon Nov  9 14:15:35 2009', 1
go

sp_configure "allow updates", 0
go

set proc_return_status ON
go

set nocount off
go

print "Loading of auto-expansion threshold procedures is complete."
go


declare @retval int
exec @retval = sp_version 'installdbextend', NULL, '15.5/EBF 17340 SMP/P/x86_64/Enterprise Linux/ase155/2391/64-bit/OPT/Mon Nov  9 14:15:35 2009', 'end'
if (@retval != 0) select syb_quit()
go

